From 28ed981082e2ef548da85f27b7f71cd4aa7f9c54 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Sat, 17 Oct 2015 18:59:04 +0300 Subject: [PATCH] Redesign RSS base classes. --- src/base/base.pri | 4 +- src/base/rss/{ => private}/rssparser.cpp | 34 ++++---- src/base/rss/{ => private}/rssparser.h | 66 ++++++++------- src/base/rss/rssarticle.cpp | 10 +-- src/base/rss/rssarticle.h | 1 - src/base/rss/rssfeed.cpp | 101 +++++++++-------------- src/base/rss/rssfeed.h | 19 ++--- src/base/rss/rssfile.cpp | 9 +- src/base/rss/rssfile.h | 16 ++-- src/base/rss/rssfolder.cpp | 78 ++++------------- src/base/rss/rssfolder.h | 27 ++---- src/base/rss/rssmanager.cpp | 53 ++++++++---- src/base/rss/rssmanager.h | 28 +++++-- src/gui/rss/automatedrssdownloader.cpp | 3 +- src/gui/rss/feedlistwidget.cpp | 7 +- src/gui/rss/rss_imp.cpp | 36 ++++---- 16 files changed, 229 insertions(+), 263 deletions(-) rename src/base/rss/{ => private}/rssparser.cpp (96%) rename src/base/rss/{ => private}/rssparser.h (51%) diff --git a/src/base/base.pri b/src/base/base.pri index ccea8efe1..b38caa601 100644 --- a/src/base/base.pri +++ b/src/base/base.pri @@ -43,7 +43,7 @@ HEADERS += \ $$PWD/rss/rssarticle.h \ $$PWD/rss/rssdownloadrule.h \ $$PWD/rss/rssdownloadrulelist.h \ - $$PWD/rss/rssparser.h \ + $$PWD/rss/private/rssparser.h \ $$PWD/utils/fs.h \ $$PWD/utils/gzip.h \ $$PWD/utils/misc.h \ @@ -94,7 +94,7 @@ SOURCES += \ $$PWD/rss/rssdownloadrule.cpp \ $$PWD/rss/rssdownloadrulelist.cpp \ $$PWD/rss/rssfile.cpp \ - $$PWD/rss/rssparser.cpp \ + $$PWD/rss/private/rssparser.cpp \ $$PWD/utils/fs.cpp \ $$PWD/utils/gzip.cpp \ $$PWD/utils/misc.cpp \ diff --git a/src/base/rss/rssparser.cpp b/src/base/rss/private/rssparser.cpp similarity index 96% rename from src/base/rss/rssparser.cpp rename to src/base/rss/private/rssparser.cpp index 18b138c41..eecfe1af1 100644 --- a/src/base/rss/rssparser.cpp +++ b/src/base/rss/private/rssparser.cpp @@ -29,21 +29,26 @@ */ #include +#include #include #include #include #include +#include #include "base/utils/fs.h" #include "rssparser.h" namespace Rss { - struct ParsingJob + namespace Private { - QString feedUrl; - QString filePath; - }; + struct ParsingJob + { + QString feedUrl; + QByteArray feedData; + }; + } } static const char shortDay[][4] = { @@ -64,7 +69,7 @@ static const char shortMonth[][4] = { "Sep", "Oct", "Nov", "Dec" }; -using namespace Rss; +using namespace Rss::Private; Parser::Parser(QObject *parent) : QThread(parent) @@ -227,11 +232,11 @@ QDateTime Parser::parseDate(const QString &string) return result; } -void Parser::parseRssFile(const QString &feedUrl, const QString &filePath) +void Parser::parseFeedData(const QString &feedUrl, const QByteArray &feedData) { - qDebug() << Q_FUNC_INFO << feedUrl << filePath; + qDebug() << Q_FUNC_INFO << feedUrl; m_mutex.lock(); - ParsingJob job = { feedUrl, Utils::Fs::fromNativePath(filePath) }; + ParsingJob job = { feedUrl, feedData }; m_queue.enqueue(job); // Wake up thread. if (m_queue.count() == 1) { @@ -487,14 +492,9 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl) // read and create items from a rss document void Parser::parseFeed(const ParsingJob &job) { - qDebug() << Q_FUNC_INFO << job.feedUrl << job.filePath; - QFile fileRss(job.filePath); - if (!fileRss.open(QIODevice::ReadOnly | QIODevice::Text)) { - reportFailure(job, tr("Failed to open downloaded RSS file.")); - return; - } + qDebug() << Q_FUNC_INFO << job.feedUrl; - QXmlStreamReader xml(&fileRss); + QXmlStreamReader xml(job.feedData); bool foundChannel = false; while (xml.readNextStartElement()) { if (xml.name() == "rss") { @@ -533,14 +533,10 @@ void Parser::parseFeed(const ParsingJob &job) return; } - // Clean up - fileRss.close(); emit feedParsingFinished(job.feedUrl, QString()); - Utils::Fs::forceRemove(job.filePath); } void Parser::reportFailure(const ParsingJob &job, const QString &error) { emit feedParsingFinished(job.feedUrl, error); - Utils::Fs::forceRemove(job.filePath); } diff --git a/src/base/rss/rssparser.h b/src/base/rss/private/rssparser.h similarity index 51% rename from src/base/rss/rssparser.h rename to src/base/rss/private/rssparser.h index 0a02ca773..89893feaa 100644 --- a/src/base/rss/rssparser.h +++ b/src/base/rss/private/rssparser.h @@ -31,52 +31,56 @@ #ifndef RSSPARSER_H #define RSSPARSER_H +#include #include #include #include +#include #include -#include "rssarticle.h" +class QXmlStreamReader; namespace Rss { - struct ParsingJob; - - class Parser: public QThread + namespace Private { - Q_OBJECT + struct ParsingJob; + + class Parser: public QThread + { + Q_OBJECT + + public: + explicit Parser(QObject *parent = 0); + ~Parser(); - public: - explicit Parser(QObject *parent = 0); - virtual ~Parser(); + void parseFeedData(const QString &feedUrl, const QByteArray &feedData); + void clearFeedData(const QString &feedUrl); - signals: - void newArticle(const QString &feedUrl, const QVariantHash &rssArticle); - void feedTitle(const QString &feedUrl, const QString &title); - void feedParsingFinished(const QString &feedUrl, const QString &error); + signals: + void newArticle(const QString &feedUrl, const QVariantHash &rssArticle); + void feedTitle(const QString &feedUrl, const QString &title); + void feedParsingFinished(const QString &feedUrl, const QString &error); - public slots: - void parseRssFile(const QString &feedUrl, const QString &filePath); - void clearFeedData(const QString &feedUrl); + private: + void run() override; - protected: - virtual void run(); + static QDateTime parseDate(const QString &string); - private: - static QDateTime parseDate(const QString &string); - void parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl); - void parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl); - void parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl); - void parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl); - void parseFeed(const ParsingJob &job); - void reportFailure(const ParsingJob &job, const QString &error); + void parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl); + void parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl); + void parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl); + void parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl); + void parseFeed(const ParsingJob &job); + void reportFailure(const ParsingJob &job, const QString &error); - bool m_running; - QMutex m_mutex; - QQueue m_queue; - QWaitCondition m_waitCondition; - QHash m_lastBuildDates; // Optimization - }; + bool m_running; + QMutex m_mutex; + QQueue m_queue; + QWaitCondition m_waitCondition; + QHash m_lastBuildDates; // Optimization + }; + } } #endif // RSSPARSER_H diff --git a/src/base/rss/rssarticle.cpp b/src/base/rss/rssarticle.cpp index 582575b64..1357d09c9 100644 --- a/src/base/rss/rssarticle.cpp +++ b/src/base/rss/rssarticle.cpp @@ -120,12 +120,10 @@ bool Article::isRead() const void Article::markAsRead() { - if (m_read) return; - - m_read = true; - m_parent->decrementUnreadCount(); - m_parent->markAsDirty(); - emit articleWasRead(); + if (!m_read) { + m_read = true; + emit articleWasRead(); + } } const QString &Article::guid() const diff --git a/src/base/rss/rssarticle.h b/src/base/rss/rssarticle.h index 88f8b4202..e81a10f6a 100644 --- a/src/base/rss/rssarticle.h +++ b/src/base/rss/rssarticle.h @@ -32,7 +32,6 @@ #ifndef RSSARTICLE_H #define RSSARTICLE_H -#include #include #include #include diff --git a/src/base/rss/rssfeed.cpp b/src/base/rss/rssfeed.cpp index 457851676..76acfaf4b 100644 --- a/src/base/rss/rssfeed.cpp +++ b/src/base/rss/rssfeed.cpp @@ -40,9 +40,9 @@ #include "base/utils/fs.h" #include "base/net/downloadmanager.h" #include "base/net/downloadhandler.h" +#include "private/rssparser.h" #include "rssdownloadrulelist.h" #include "rssarticle.h" -#include "rssparser.h" #include "rssfolder.h" #include "rssmanager.h" #include "rssfeed.h" @@ -57,9 +57,8 @@ namespace Rss using namespace Rss; -Feed::Feed(Manager *manager, Folder *parent, const QString &url) +Feed::Feed(const QString &url, Manager *manager) : m_manager(manager) - , m_parent(parent) , m_url (QUrl::fromEncoded(url.toUtf8()).toString()) , m_icon(":/icons/oxygen/application-rss+xml.png") , m_unreadCount(0) @@ -69,34 +68,26 @@ Feed::Feed(Manager *manager, Folder *parent, const QString &url) { qDebug() << Q_FUNC_INFO << m_url; // Listen for new RSS downloads - connect(manager->rssParser(), SIGNAL(feedTitle(QString,QString)), SLOT(handleFeedTitle(QString,QString))); - connect(manager->rssParser(), SIGNAL(newArticle(QString,QVariantHash)), SLOT(handleNewArticle(QString,QVariantHash))); - connect(manager->rssParser(), SIGNAL(feedParsingFinished(QString,QString)), SLOT(handleFeedParsingFinished(QString,QString))); + Private::Parser *const parser = m_manager->rssParser(); + connect(parser, SIGNAL(feedTitle(QString,QString)), SLOT(handleFeedTitle(QString,QString))); + connect(parser, SIGNAL(newArticle(QString,QVariantHash)), SLOT(handleNewArticle(QString,QVariantHash))); + connect(parser, SIGNAL(feedParsingFinished(QString,QString)), SLOT(handleParsingFinished(QString,QString))); // Download the RSS Feed icon Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(iconUrl(), true); - connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString))); - connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); - m_iconUrl = handler->url(); + connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleIconDownloadFinished(QString, QString))); // Load old RSS articles loadItemsFromDisk(); + + refresh(); } Feed::~Feed() { if (!m_icon.startsWith(":/") && QFile::exists(m_icon)) Utils::Fs::forceRemove(m_icon); -} - -Folder *Feed::parent() const -{ - return m_parent; -} - -void Feed::setParent(Folder *parent) -{ - m_parent = parent; + m_manager->rssParser()->clearFeedData(m_url); } void Feed::saveItemsToDisk() @@ -104,7 +95,7 @@ void Feed::saveItemsToDisk() qDebug() << Q_FUNC_INFO << m_url; if (!m_dirty) return; - markAsDirty(false); + m_dirty = false; QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); QVariantList oldItems; @@ -140,13 +131,15 @@ void Feed::addArticle(const ArticlePtr &article) int maxArticles = Preferences::instance()->getRSSMaxArticlesPerFeed(); if (!m_articles.contains(article->guid())) { - markAsDirty(); + m_dirty = true; // Update unreadCount if (!article->isRead()) ++m_unreadCount; // Insert in hash table m_articles[article->guid()] = article; + if (!article->isRead()) // Optimization + connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleRead()), Qt::UniqueConnection); // Insertion sort ArticleList::Iterator lowerBound = qLowerBound(m_articlesByDate.begin(), m_articlesByDate.end(), article, articleDateRecentThan); m_articlesByDate.insert(lowerBound, article); @@ -162,7 +155,7 @@ void Feed::addArticle(const ArticlePtr &article) // Check if article was inserted at the end of the list and will break max_articles limit if (Preferences::instance()->isRssDownloadingEnabled()) { if ((lbIndex < maxArticles) && !article->isRead()) - downloadArticleTorrentIfMatching(m_manager->downloadRules(), article); + downloadArticleTorrentIfMatching(article); } } else { @@ -172,7 +165,7 @@ void Feed::addArticle(const ArticlePtr &article) ArticlePtr skipped = m_articles.value(article->guid(), ArticlePtr()); if (skipped) { if (!skipped->isRead()) - downloadArticleTorrentIfMatching(m_manager->downloadRules(), skipped); + downloadArticleTorrentIfMatching(skipped); } } } @@ -186,10 +179,9 @@ bool Feed::refresh() } m_loading = true; // Download the RSS again - Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(m_url, true); - connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString))); - connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); - m_url = handler->url(); // sync URL encoding + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(m_url); + connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), this, SLOT(handleRssDownloadFinished(QString, QByteArray))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleRssDownloadFailed(QString, QString))); return true; } @@ -290,11 +282,6 @@ void Feed::markAsRead() m_manager->forwardFeedInfosChanged(m_url, displayName(), 0); } -void Feed::markAsDirty(bool dirty) -{ - m_dirty = dirty; -} - uint Feed::unreadCount() const { return m_unreadCount; @@ -327,28 +314,27 @@ ArticleList Feed::unreadArticleListByDateDesc() const QString Feed::iconUrl() const { // XXX: This works for most sites but it is not perfect - return QString("http://") + QUrl(m_url).host() + QString("/favicon.ico"); + return QString("http://%1/favicon.ico").arg(QUrl(m_url).host()); } -// read and store the downloaded rss' informations -void Feed::handleFinishedDownload(const QString &url, const QString &filePath) +void Feed::handleIconDownloadFinished(const QString &url, const QString &filePath) { - if (url == m_url) { - qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << url; - // Parse the download RSS - m_manager->rssParser()->parseRssFile(m_url, filePath); - } - else if (url == m_iconUrl) { - m_icon = filePath; - qDebug() << Q_FUNC_INFO << "icon path:" << m_icon; - m_manager->forwardFeedIconChanged(m_url, m_icon); - } + Q_UNUSED(url); + + m_icon = filePath; + qDebug() << Q_FUNC_INFO << "icon path:" << m_icon; + m_manager->forwardFeedIconChanged(m_url, m_icon); } -void Feed::handleDownloadFailure(const QString &url, const QString &error) +void Feed::handleRssDownloadFinished(const QString &url, const QByteArray &data) { - if (url != m_url) return; + qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << url; + // Parse the download RSS + m_manager->rssParser()->parseFeedData(m_url, data); +} +void Feed::handleRssDownloadFailed(const QString &url, const QString &error) +{ m_inErrorState = true; m_loading = false; m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); @@ -368,9 +354,10 @@ void Feed::handleFeedTitle(const QString &feedUrl, const QString &title) m_manager->forwardFeedInfosChanged(feedUrl, title, m_unreadCount); } -void Feed::downloadArticleTorrentIfMatching(DownloadRuleList *rules, const ArticlePtr &article) +void Feed::downloadArticleTorrentIfMatching(const ArticlePtr &article) { Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); + DownloadRuleList *rules = m_manager->downloadRules(); DownloadRulePtr matchingRule = rules->findMatchingRule(m_url, article->title()); if (!matchingRule) return; @@ -378,7 +365,6 @@ void Feed::downloadArticleTorrentIfMatching(DownloadRuleList *rules, const Artic QDateTime lastMatch = matchingRule->lastMatch(); if (lastMatch.isValid()) { if (QDateTime::currentDateTime() < lastMatch.addDays(matchingRule->ignoreDays())) { - connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleStateChanged()), Qt::UniqueConnection); article->markAsRead(); return; } @@ -396,8 +382,7 @@ void Feed::downloadArticleTorrentIfMatching(DownloadRuleList *rules, const Artic } Logger::instance()->addMessage(tr("Automatically downloading '%1' torrent from '%2' RSS feed...").arg(article->title()).arg(displayName())); - connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString &)), Qt::UniqueConnection); - if (BitTorrent::MagnetUri(torrent_url).isValid()) + if (BitTorrent::MagnetUri(torrentUrl).isValid()) article->markAsRead(); else connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString&)), Qt::UniqueConnection); @@ -415,10 +400,9 @@ void Feed::downloadArticleTorrentIfMatching(DownloadRuleList *rules, const Artic void Feed::recheckRssItemsForDownload() { Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); - DownloadRuleList *rules = m_manager->downloadRules(); foreach (const ArticlePtr &article, m_articlesByDate) { if (!article->isRead()) - downloadArticleTorrentIfMatching(rules, article); + downloadArticleTorrentIfMatching(article); } } @@ -440,7 +424,7 @@ void Feed::handleNewArticle(const QString &feedUrl, const QVariantHash &articleD //m_manager->forwardFeedContentChanged(m_url); } -void Feed::handleFeedParsingFinished(const QString &feedUrl, const QString &error) +void Feed::handleParsingFinished(const QString &feedUrl, const QString &error) { if (feedUrl != m_url) return; @@ -459,12 +443,9 @@ void Feed::handleFeedParsingFinished(const QString &feedUrl, const QString &erro saveItemsToDisk(); } -void Feed::handleArticleStateChanged() -{ - m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); -} - -void Feed::decrementUnreadCount() +void Feed::handleArticleRead() { --m_unreadCount; + m_dirty = true; + m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); } diff --git a/src/base/rss/rssfeed.h b/src/base/rss/rssfeed.h index 30fd45830..5198234bd 100644 --- a/src/base/rss/rssfeed.h +++ b/src/base/rss/rssfeed.h @@ -58,11 +58,9 @@ namespace Rss Q_OBJECT public: - Feed(Manager *manager, Folder *parent, const QString &url); + Feed(const QString &url, Manager *manager); ~Feed(); - Folder *parent() const; - void setParent(Folder *parent); bool refresh(); QString id() const; void removeAllSettings(); @@ -78,38 +76,35 @@ namespace Rss ArticlePtr getItem(const QString &guid) const; uint count() const; void markAsRead(); - void markAsDirty(bool dirty = true); uint unreadCount() const; ArticleList articleListByDateDesc() const; const ArticleHash &articleHash() const; ArticleList unreadArticleListByDateDesc() const; - void decrementUnreadCount(); void recheckRssItemsForDownload(); private slots: - void handleFinishedDownload(const QString &url, const QString &filePath); - void handleDownloadFailure(const QString &url, const QString &error); + void handleIconDownloadFinished(const QString &url, const QString &filePath); + void handleRssDownloadFinished(const QString &url, const QByteArray &data); + void handleRssDownloadFailed(const QString &url, const QString &error); void handleFeedTitle(const QString &feedUrl, const QString &title); void handleNewArticle(const QString &feedUrl, const QVariantHash &article); - void handleFeedParsingFinished(const QString &feedUrl, const QString &error); - void handleArticleStateChanged(); + void handleParsingFinished(const QString &feedUrl, const QString &error); + void handleArticleRead(); private: QString iconUrl() const; void loadItemsFromDisk(); void addArticle(const ArticlePtr &article); - void downloadArticleTorrentIfMatching(DownloadRuleList *rules, const ArticlePtr &article); + void downloadArticleTorrentIfMatching(const ArticlePtr &article); private: Manager *m_manager; ArticleHash m_articles; ArticleList m_articlesByDate; // Articles sorted by date (more recent first) - Folder *m_parent; QString m_title; QString m_url; QString m_alias; QString m_icon; - QString m_iconUrl; uint m_unreadCount; bool m_dirty; bool m_inErrorState; diff --git a/src/base/rss/rssfile.cpp b/src/base/rss/rssfile.cpp index 1f911fdda..394bd56f0 100644 --- a/src/base/rss/rssfile.cpp +++ b/src/base/rss/rssfile.cpp @@ -36,11 +36,16 @@ using namespace Rss; File::~File() {} +Folder *File::parentFolder() const +{ + return m_parent; +} + QStringList File::pathHierarchy() const { QStringList path; - if (parent()) - path << parent()->pathHierarchy(); + if (m_parent) + path << m_parent->pathHierarchy(); path << id(); return path; } diff --git a/src/base/rss/rssfile.h b/src/base/rss/rssfile.h index 0b5452624..285da93f6 100644 --- a/src/base/rss/rssfile.h +++ b/src/base/rss/rssfile.h @@ -55,25 +55,27 @@ namespace Rss public: virtual ~File(); - virtual uint unreadCount() const = 0; - virtual QString displayName() const = 0; virtual QString id() const = 0; + virtual QString displayName() const = 0; + virtual uint unreadCount() const = 0; virtual QString iconPath() const = 0; + virtual ArticleList articleListByDateDesc() const = 0; + virtual ArticleList unreadArticleListByDateDesc() const = 0; + virtual void rename(const QString &newName) = 0; virtual void markAsRead() = 0; - virtual Folder *parent() const = 0; - virtual void setParent(Folder *parent) = 0; virtual bool refresh() = 0; - virtual ArticleList articleListByDateDesc() const = 0; - virtual ArticleList unreadArticleListByDateDesc() const = 0; virtual void removeAllSettings() = 0; virtual void saveItemsToDisk() = 0; virtual void recheckRssItemsForDownload() = 0; + Folder *parentFolder() const; QStringList pathHierarchy() const; protected: - uint m_unreadCount; + friend class Folder; + + Folder *m_parent = nullptr; }; } diff --git a/src/base/rss/rssfolder.cpp b/src/base/rss/rssfolder.cpp index 0a054c9b8..e17afecb9 100644 --- a/src/base/rss/rssfolder.cpp +++ b/src/base/rss/rssfolder.cpp @@ -40,24 +40,11 @@ using namespace Rss; -Folder::Folder(Folder *parent, const QString &name) - : m_parent(parent) - , m_name(name) +Folder::Folder(const QString &name) + : m_name(name) { } -Folder::~Folder() {} - -Folder *Folder::parent() const -{ - return m_parent; -} - -void Folder::setParent(Folder *parent) -{ - m_parent = parent; -} - uint Folder::unreadCount() const { uint nbUnread = 0; @@ -78,31 +65,6 @@ void Folder::removeChild(const QString &childId) } } -FolderPtr Folder::addFolder(const QString &name) -{ - FolderPtr subfolder; - if (!m_children.contains(name)) { - subfolder = FolderPtr(new Folder(this, name)); - m_children[name] = subfolder; - } - else { - subfolder = qSharedPointerDynamicCast(m_children.value(name)); - } - return subfolder; -} - -FeedPtr Folder::addStream(Manager *manager, const QString &url) -{ - qDebug() << Q_FUNC_INFO << manager << url; - FeedPtr stream(new Feed(manager, this, url)); - Q_ASSERT(stream); - qDebug() << "Stream URL is " << stream->url(); - Q_ASSERT(!m_children.contains(stream->url())); - m_children[stream->url()] = stream; - stream->refresh(); - return stream; -} - // Refresh All Children bool Folder::refresh() { @@ -176,7 +138,8 @@ void Folder::rename(const QString &newName) Q_ASSERT(!m_parent->hasChild(newName)); if (!m_parent->hasChild(newName)) { // Update parent - m_parent->renameChildFolder(m_name, newName); + FilePtr folder = m_parent->m_children.take(m_name); + m_parent->m_children[newName] = folder; // Actually rename m_name = newName; } @@ -224,20 +187,17 @@ QHash Folder::getAllFeedsAsHash() const return ret; } -void Folder::addFile(const FilePtr &item) +bool Folder::addFile(const FilePtr &item) { - if (FeedPtr feed = qSharedPointerDynamicCast(item)) { - Q_ASSERT(!m_children.contains(feed->url())); - m_children[feed->url()] = item; - qDebug("Added feed %s to folder ./%s", qPrintable(feed->url()), qPrintable(m_name)); - } - else if (FolderPtr folder = qSharedPointerDynamicCast(item)) { - Q_ASSERT(!m_children.contains(folder->displayName())); - m_children[folder->displayName()] = item; - qDebug("Added folder %s to folder ./%s", qPrintable(folder->displayName()), qPrintable(m_name)); + Q_ASSERT(!m_children.contains(item->id())); + if (!m_children.contains(item->id())) { + m_children[item->id()] = item; + // Update parent + item->m_parent = this; + return true; } - // Update parent - item->setParent(this); + + return false; } void Folder::removeAllItems() @@ -245,6 +205,11 @@ void Folder::removeAllItems() m_children.clear(); } +FilePtr Folder::child(const QString &childId) +{ + return m_children.value(childId); +} + void Folder::removeAllSettings() { FileHash::ConstIterator it = m_children.begin(); @@ -274,13 +239,6 @@ bool Folder::hasChild(const QString &childId) return m_children.contains(childId); } -void Folder::renameChildFolder(const QString &oldName, const QString &newName) -{ - Q_ASSERT(m_children.contains(oldName)); - FilePtr folder = m_children.take(oldName); - m_children[newName] = folder; -} - FilePtr Folder::takeChild(const QString &childId) { return m_children.take(childId); diff --git a/src/base/rss/rssfolder.h b/src/base/rss/rssfolder.h index 78ff21401..7adefb423 100644 --- a/src/base/rss/rssfolder.h +++ b/src/base/rss/rssfolder.h @@ -48,19 +48,12 @@ namespace Rss typedef QSharedPointer FolderPtr; typedef QList FeedList; - class Folder: public QObject, public File + class Folder: public File { - Q_OBJECT - public: - explicit Folder(Folder *parent = 0, const QString &name = QString()); - ~Folder(); + explicit Folder(const QString &name = QString()); - Folder *parent() const; - void setParent(Folder *parent); uint unreadCount() const; - FeedPtr addStream(Manager *manager, const QString &url); - FolderPtr addFolder(const QString &name); uint getNbFeeds() const; FileList getContent() const; FeedList getAllFeeds() const; @@ -71,22 +64,20 @@ namespace Rss bool hasChild(const QString &childId); ArticleList articleListByDateDesc() const; ArticleList unreadArticleListByDateDesc() const; + + void rename(const QString &newName); + void markAsRead(); + bool refresh(); void removeAllSettings(); void saveItemsToDisk(); + void recheckRssItemsForDownload(); void removeAllItems(); - void renameChildFolder(const QString &oldName, const QString &newName); + FilePtr child(const QString &childId); FilePtr takeChild(const QString &childId); - void recheckRssItemsForDownload(); - - public slots: - bool refresh(); - void addFile(const FilePtr &item); + bool addFile(const FilePtr &item); void removeChild(const QString &childId); - void rename(const QString &newName); - void markAsRead(); private: - Folder *m_parent; QString m_name; FileHash m_children; }; diff --git a/src/base/rss/rssmanager.cpp b/src/base/rss/rssmanager.cpp index 17e665661..a7bd087d1 100644 --- a/src/base/rss/rssmanager.cpp +++ b/src/base/rss/rssmanager.cpp @@ -33,19 +33,23 @@ #include "base/logger.h" #include "base/preferences.h" +#include "private/rssparser.h" +#include "rssfolder.h" #include "rssfeed.h" #include "rssarticle.h" #include "rssdownloadrulelist.h" -#include "rssparser.h" #include "rssmanager.h" static const int MSECS_PER_MIN = 60000; using namespace Rss; +using namespace Rss::Private; -Manager::Manager() - : m_downloadRules(new DownloadRuleList) +Manager::Manager(QObject *parent) + : QObject(parent) + , m_downloadRules(new DownloadRuleList) , m_rssParser(new Parser(this)) + , m_rootFolder(new Folder) { connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(refresh())); m_refreshInterval = Preferences::instance()->getRSSRefreshInterval(); @@ -57,16 +61,12 @@ Manager::~Manager() qDebug("Deleting RSSManager..."); delete m_downloadRules; delete m_rssParser; - saveItemsToDisk(); + m_rootFolder->saveItemsToDisk(); saveStreamList(); + m_rootFolder.clear(); qDebug("RSSManager deleted"); } -Parser *Manager::rssParser() const -{ - return m_rssParser; -} - void Manager::updateRefreshInterval(uint val) { if (m_refreshInterval != val) { @@ -95,14 +95,22 @@ void Manager::loadStreamList() const QString feedUrl = path.takeLast(); qDebug() << "Feed URL:" << feedUrl; // Create feed path (if it does not exists) - Folder *feedParent = this; + FolderPtr feedParent = m_rootFolder; foreach (const QString &folderName, path) { - qDebug() << "Adding parent folder:" << folderName; - feedParent = feedParent->addFolder(folderName).data(); + if (!feedParent->hasChild(folderName)) { + qDebug() << "Adding parent folder:" << folderName; + FolderPtr folder(new Folder(folderName)); + feedParent->addFile(folder); + feedParent = folder; + } + else { + feedParent = qSharedPointerDynamicCast(feedParent->child(folderName)); + } } // Create feed qDebug() << "Adding feed to parent folder"; - FeedPtr stream = feedParent->addStream(this, feedUrl); + FeedPtr stream(new Feed(feedUrl, this)); + feedParent->addFile(stream); const QString &alias = aliases[i]; if (!alias.isEmpty()) stream->rename(alias); @@ -128,7 +136,7 @@ void Manager::forwardFeedIconChanged(const QString &url, const QString &iconPath void Manager::moveFile(const FilePtr &file, const FolderPtr &destinationFolder) { - Folder *srcFolder = file->parent(); + Folder *srcFolder = file->parentFolder(); if (destinationFolder != srcFolder) { // Remove reference in old folder srcFolder->takeChild(file->id()); @@ -144,7 +152,7 @@ void Manager::saveStreamList() const { QStringList streamsUrl; QStringList aliases; - FeedList streams = getAllFeeds(); + FeedList streams = m_rootFolder->getAllFeeds(); foreach (const FeedPtr &stream, streams) { // This backslash has nothing to do with path handling QString streamPath = stream->pathHierarchy().join("\\"); @@ -164,3 +172,18 @@ DownloadRuleList *Manager::downloadRules() const Q_ASSERT(m_downloadRules); return m_downloadRules; } + +FolderPtr Manager::rootFolder() const +{ + return m_rootFolder; +} + +Parser *Manager::rssParser() const +{ + return m_rssParser; +} + +void Manager::refresh() +{ + m_rootFolder->refresh(); +} diff --git a/src/base/rss/rssmanager.h b/src/base/rss/rssmanager.h index 515d45a64..2321ce5a2 100644 --- a/src/base/rss/rssmanager.h +++ b/src/base/rss/rssmanager.h @@ -32,31 +32,44 @@ #ifndef RSSMANAGER_H #define RSSMANAGER_H +#include #include #include -#include "rssfolder.h" - namespace Rss { class DownloadRuleList; - class Parser; + class File; + class Folder; + class Feed; class Manager; + typedef QSharedPointer FilePtr; + typedef QSharedPointer FolderPtr; + typedef QSharedPointer FeedPtr; + + namespace Private + { + class Parser; + } + typedef QSharedPointer ManagerPtr; - class Manager: public Folder + class Manager: public QObject { Q_OBJECT public: - Manager(); + explicit Manager(QObject *parent = 0); ~Manager(); - Parser *rssParser() const; DownloadRuleList *downloadRules() const; + FolderPtr rootFolder() const; + + Private::Parser *rssParser() const; public slots: + void refresh(); void loadStreamList(); void saveStreamList() const; void forwardFeedContentChanged(const QString &url); @@ -74,7 +87,8 @@ namespace Rss QTimer m_refreshTimer; uint m_refreshInterval; DownloadRuleList *m_downloadRules; - Parser *m_rssParser; + Private::Parser *m_rssParser; + FolderPtr m_rootFolder; }; } diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index 98ef89088..5b5493763 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -39,6 +39,7 @@ #include "base/rss/rssdownloadrulelist.h" #include "base/preferences.h" #include "base/rss/rssmanager.h" +#include "base/rss/rssfolder.h" #include "base/rss/rssfeed.h" #include "guiiconprovider.h" #include "autoexpandabledialog.h" @@ -524,7 +525,7 @@ void AutomatedRssDownloader::updateMatchingArticles() Rss::ManagerPtr manager = m_manager.toStrongRef(); if (!manager) return; - const QHash all_feeds = manager->getAllFeedsAsHash(); + const QHash all_feeds = manager->rootFolder()->getAllFeedsAsHash(); saveEditedRule(); foreach (const QListWidgetItem *rule_item, ui->listRules->selectedItems()) { diff --git a/src/gui/rss/feedlistwidget.cpp b/src/gui/rss/feedlistwidget.cpp index ebdd325d6..6f00ffcbd 100644 --- a/src/gui/rss/feedlistwidget.cpp +++ b/src/gui/rss/feedlistwidget.cpp @@ -29,6 +29,7 @@ */ #include "base/rss/rssmanager.h" +#include "base/rss/rssfolder.h" #include "base/rss/rssfeed.h" #include "guiiconprovider.h" #include "feedlistwidget.h" @@ -40,9 +41,9 @@ FeedListWidget::FeedListWidget(QWidget *parent, const Rss::ManagerPtr& rssmanage setColumnCount(1); headerItem()->setText(0, tr("RSS feeds")); m_unreadStickyItem = new QTreeWidgetItem(this); - m_unreadStickyItem->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->unreadCount())+ QString(")")); + m_unreadStickyItem->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->rootFolder()->unreadCount()) + QString(")")); m_unreadStickyItem->setData(0,Qt::DecorationRole, GuiIconProvider::instance()->getIcon("mail-folder-inbox")); - itemAdded(m_unreadStickyItem, rssmanager); + itemAdded(m_unreadStickyItem, rssmanager->rootFolder()); connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(updateCurrentFeed(QTreeWidgetItem*))); setCurrentItem(m_unreadStickyItem); } @@ -202,7 +203,7 @@ void FeedListWidget::dropEvent(QDropEvent *event) { dest_folder = qSharedPointerCast(getRSSItem(dest_folder_item)); folders_altered << dest_folder_item; } else { - dest_folder = m_rssManager; + dest_folder = m_rssManager->rootFolder(); } QList src_items = selectedItems(); // Check if there is not going to overwrite another file diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index a37dac89c..853204f1b 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -47,7 +47,6 @@ #include "base/rss/rssmanager.h" #include "base/rss/rssfolder.h" #include "base/rss/rssarticle.h" -#include "base/rss/rssparser.h" #include "base/rss/rssfeed.h" #include "automatedrssdownloader.h" #include "guiiconprovider.h" @@ -79,7 +78,7 @@ void RSSImp::displayRSSListMenu(const QPoint& pos) myRSSListMenu.addAction(actionMark_items_read); myRSSListMenu.addSeparator(); if (selectedItems.size() == 1) { - if (m_feedList->getRSSItem(selectedItems.first()) != m_rssManager) { + if (m_feedList->getRSSItem(selectedItems.first()) != m_rssManager->rootFolder()) { myRSSListMenu.addAction(actionRename); myRSSListMenu.addAction(actionDelete); myRSSListMenu.addSeparator(); @@ -167,14 +166,15 @@ void RSSImp::askNewFolder() Q_ASSERT(rss_parent); } else { - rss_parent = m_rssManager; + rss_parent = m_rssManager->rootFolder(); } bool ok; QString new_name = AutoExpandableDialog::getText(this, tr("Please choose a folder name"), tr("Folder name:"), QLineEdit::Normal, tr("New folder"), &ok); - if (!ok) + if (!ok || rss_parent->hasChild(new_name)) return; - Rss::FolderPtr newFolder = rss_parent->addFolder(new_name); + Rss::FolderPtr newFolder(new Rss::Folder(new_name)); + rss_parent->addFile(newFolder); QTreeWidgetItem* folderItem = createFolderListItem(newFolder); if (parent_item) parent_item->addChild(folderItem); @@ -207,7 +207,7 @@ void RSSImp::on_newFeedButton_clicked() if (parent_item) rss_parent = qSharedPointerCast(m_feedList->getRSSItem(parent_item)); else - rss_parent = m_rssManager; + rss_parent = m_rssManager->rootFolder(); // Ask for feed URL bool ok; QString clip_txt = qApp->clipboard()->text(); @@ -229,7 +229,9 @@ void RSSImp::on_newFeedButton_clicked() QMessageBox::Ok); return; } - Rss::FeedPtr stream = rss_parent->addStream(m_rssManager.data(), newUrl); + + Rss::FeedPtr stream(new Rss::Feed(newUrl, m_rssManager.data())); + rss_parent->addFile(stream); // Create TreeWidget item QTreeWidgetItem* item = createFolderListItem(stream); if (parent_item) @@ -265,17 +267,13 @@ void RSSImp::deleteSelectedItems() // Notify TreeWidget m_feedList->itemAboutToBeRemoved(item); // Actually delete the item - rss_item->parent()->removeChild(rss_item->id()); + rss_item->parentFolder()->removeChild(rss_item->id()); delete item; // Update parents count - while (parent && parent != m_feedList->invisibleRootItem()) { - updateItemInfos (parent); + while (parent && (parent != m_feedList->invisibleRootItem())) { + updateItemInfos(parent); parent = parent->parent(); } - // Clear feed data from RSS parser (possible caching). - Rss::Feed* rssFeed = dynamic_cast(rss_item.data()); - if (rssFeed) - m_rssManager->rssParser()->clearFeedData(rssFeed->url()); } m_rssManager->saveStreamList(); // Update Unread items @@ -406,7 +404,7 @@ void RSSImp::renameSelectedRssFile() newName = AutoExpandableDialog::getText(this, tr("Please choose a new name for this RSS feed"), tr("New feed name:"), QLineEdit::Normal, m_feedList->getRSSItem(item)->displayName(), &ok); // Check if name is already taken if (ok) { - if (rss_item->parent()->hasChild(newName)) { + if (rss_item->parentFolder()->hasChild(newName)) { QMessageBox::warning(0, tr("Name already in use"), tr("This name is already used by another item, please choose another one.")); ok = false; } @@ -489,7 +487,7 @@ void RSSImp::fillFeedsList(QTreeWidgetItem* parent, const Rss::FolderPtr& rss_pa if (parent) children = rss_parent->getContent(); else - children = m_rssManager->getContent(); + children = m_rssManager->rootFolder()->getContent(); foreach (const Rss::FilePtr& rssFile, children) { QTreeWidgetItem* item = createFolderListItem(rssFile); Q_ASSERT(item); @@ -546,7 +544,7 @@ void RSSImp::populateArticleList(QTreeWidgetItem* item) qDebug("Getting the list of news"); Rss::ArticleList articles; - if (rss_item == m_rssManager) + if (rss_item == m_rssManager->rootFolder()) articles = rss_item->unreadArticleListByDateDesc(); else articles = rss_item->articleListByDateDesc(); @@ -655,7 +653,7 @@ void RSSImp::updateItemInfos(QTreeWidgetItem *item) return; QString name; - if (rss_item == m_rssManager) { + if (rss_item == m_rssManager->rootFolder()) { name = tr("Unread"); emit updateRSSCount(rss_item->unreadCount()); } @@ -799,7 +797,7 @@ void RSSImp::on_rssDownloaderBtn_clicked() AutomatedRssDownloader dlg(m_rssManager, this); dlg.exec(); if (dlg.isRssDownloaderEnabled()) { - m_rssManager->recheckRssItemsForDownload(); + m_rssManager->rootFolder()->recheckRssItemsForDownload(); refreshAllFeeds(); } }