From c387c15eb07a22761a3eaf44ef90e626b6641822 Mon Sep 17 00:00:00 2001 From: Christophe Dumez Date: Sat, 13 Nov 2010 19:36:46 +0000 Subject: [PATCH] Fix possible rule saving issue Matching articles are now previewable in the rss downloader dialog Make sure Rss downloading is enabled before checking for matching rules --- src/GUI.cpp | 9 +---- src/headlessloader.h | 4 +- src/qtlibtorrent/qbtsession.cpp | 16 ++++++++ src/qtlibtorrent/qbtsession.h | 8 +++- src/rss/automatedrssdownloader.cpp | 63 +++++++++++++++++++++++++++++- src/rss/automatedrssdownloader.h | 2 + src/rss/automatedrssdownloader.ui | 11 ++++-- src/rss/rss_imp.cpp | 4 +- src/rss/rssarticle.cpp | 8 ---- src/rss/rssarticle.h | 4 +- src/rss/rssdownloadrule.cpp | 12 ++++++ src/rss/rssdownloadrule.h | 3 ++ src/rss/rssdownloadrulelist.cpp | 3 ++ src/rss/rssfeed.cpp | 32 ++++++++------- src/rss/rssfolder.cpp | 19 +++++++-- src/rss/rssfolder.h | 1 + src/rss/rssmanager.cpp | 17 +++++++- src/rss/rssmanager.h | 6 ++- 18 files changed, 174 insertions(+), 48 deletions(-) diff --git a/src/GUI.cpp b/src/GUI.cpp index 1f240984c..7aaa6c719 100644 --- a/src/GUI.cpp +++ b/src/GUI.cpp @@ -131,7 +131,7 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for // Fix Tool bar layout toolBar->layout()->setSpacing(7); // Creating Bittorrent session - BTSession = new QBtSession(); + BTSession = QBtSession::instance(); connect(BTSession, SIGNAL(fullDiskError(QTorrentHandle&, QString)), this, SLOT(fullDiskError(QTorrentHandle&, QString))); connect(BTSession, SIGNAL(finishedTorrent(QTorrentHandle&)), this, SLOT(finishedTorrent(QTorrentHandle&))); connect(BTSession, SIGNAL(trackerAuthenticationRequired(QTorrentHandle&)), this, SLOT(trackerAuthenticationRequired(QTorrentHandle&))); @@ -277,11 +277,6 @@ GUI::~GUI() { // Workaround to avoid bug http://bugreports.qt.nokia.com/browse/QTBUG-7305 setUnifiedTitleAndToolBarOnMac(false); #endif - // Async deletion of Bittorrent session as early as possible - // in order to speed up exit - session_proxy sp; - if(BTSession) - sp = BTSession->asyncDeletion(); // Some saving properties->saveSettings(); disconnect(tabs, SIGNAL(currentChanged(int)), this, SLOT(tab_changed(int))); @@ -328,7 +323,7 @@ GUI::~GUI() { delete switchRSSShortcut; // Delete BTSession objects qDebug("Deleting BTSession"); - delete BTSession; + QBtSession::drop(); // May freeze for a few seconds after the next line // because the Bittorrent session proxy will // actually be deleted now and destruction diff --git a/src/headlessloader.h b/src/headlessloader.h index fd4edab32..ccd6b215d 100644 --- a/src/headlessloader.h +++ b/src/headlessloader.h @@ -44,7 +44,7 @@ public: // Enable Web UI Preferences::setWebUiEnabled(true); // Instanciate Bittorrent Object - BTSession = new QBtSession(); + BTSession = QBtSession::instance(); connect(BTSession, SIGNAL(newConsoleMessage(QString)), this, SLOT(displayConsoleMessage(QString))); // Resume unfinished torrents BTSession->startUpTorrents(); @@ -61,7 +61,7 @@ public: } ~HeadlessLoader() { - delete BTSession; + QBtSession::drop(); } public slots: diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp index 3d254802b..a03767b83 100644 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/qtlibtorrent/qbtsession.cpp @@ -68,6 +68,8 @@ #include #include +QBtSession* QBtSession::m_instance = 0; + const int MAX_TRACKER_ERRORS = 2; const float MAX_RATIO = 100.; enum VersionType { NORMAL,ALPHA,BETA,RELEASE_CANDIDATE,DEVEL }; @@ -2628,3 +2630,17 @@ void QBtSession::startUpTorrents() { settings.setValue("ported_to_new_savepath_system", true); qDebug("Unfinished torrents resumed"); } + +QBtSession * QBtSession::instance() +{ + if(!m_instance) { + m_instance = new QBtSession; + } + return m_instance; +} + +void QBtSession::drop() +{ + if(m_instance) + delete m_instance; +} diff --git a/src/qtlibtorrent/qbtsession.h b/src/qtlibtorrent/qbtsession.h index f97258a21..76f3161d2 100644 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/qtlibtorrent/qbtsession.h @@ -66,9 +66,13 @@ class QBtSession : public QObject { Q_OBJECT Q_DISABLE_COPY(QBtSession) +private: + explicit QBtSession(); + static QBtSession* m_instance; + public: - // Constructor / Destructor - QBtSession(); + static QBtSession* instance(); + static void drop(); ~QBtSession(); QTorrentHandle getTorrentHandle(QString hash) const; std::vector getTorrents() const; diff --git a/src/rss/automatedrssdownloader.cpp b/src/rss/automatedrssdownloader.cpp index f8dd4b050..5df23a07f 100644 --- a/src/rss/automatedrssdownloader.cpp +++ b/src/rss/automatedrssdownloader.cpp @@ -41,6 +41,8 @@ #include "rssdownloadrulelist.h" #include "preferences.h" #include "qinisettings.h" +#include "rssmanager.h" +#include "rssfeed.h" AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent) : QDialog(parent), @@ -49,6 +51,8 @@ AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent) : ui->setupUi(this); ui->listRules->setSortingEnabled(true); ui->listRules->setSelectionMode(QAbstractItemView::ExtendedSelection); + ui->treeMatchingArticles->setSortingEnabled(true); + connect(ui->listRules, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayRulesListMenu(const QPoint&))); m_ruleList = RssDownloadRuleList::instance(); initLabelCombobox(); @@ -58,6 +62,9 @@ AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent) : connect(ui->listRules, SIGNAL(itemSelectionChanged()), SLOT(updateRuleDefinitionBox())); connect(ui->listRules, SIGNAL(itemSelectionChanged()), SLOT(updateFeedList())); connect(ui->listFeeds, SIGNAL(itemChanged(QListWidgetItem*)), SLOT(handleFeedCheckStateChange(QListWidgetItem*))); + // Update matching articles when necessary + connect(ui->lineContains, SIGNAL(textEdited(QString)), SLOT(updateMatchingArticles())); + connect(ui->lineNotContains, SIGNAL(textEdited(QString)), SLOT(updateMatchingArticles())); updateRuleDefinitionBox(); updateFeedList(); } @@ -149,6 +156,7 @@ void AutomatedRssDownloader::updateFeedList() } ui->listFeeds->setEnabled(!ui->listRules->selectedItems().isEmpty()); connect(ui->listFeeds, SIGNAL(itemChanged(QListWidgetItem*)), SLOT(handleFeedCheckStateChange(QListWidgetItem*))); + updateMatchingArticles(); } bool AutomatedRssDownloader::isRssDownloaderEnabled() const @@ -224,7 +232,11 @@ void AutomatedRssDownloader::saveCurrentRule(QListWidgetItem * item) { qDebug() << Q_FUNC_INFO << item; if(!item) return; - if(!ui->listRules->findItems(item->text(), Qt::MatchExactly).isEmpty()) return; + if(ui->listRules->findItems(item->text(), Qt::MatchExactly).isEmpty()) { + qDebug() << "Could not find rule" << item->text() << "in the UI list"; + qDebug() << "Probably removed the item, no need to save it"; + return; + } RssDownloadRule rule = m_ruleList->getRule(item->text()); if(!rule.isValid()) { rule.setName(item->text()); @@ -263,6 +275,7 @@ void AutomatedRssDownloader::on_addRuleBtn_clicked() QListWidgetItem * item = new QListWidgetItem(rule, ui->listRules); item->setFlags(item->flags()|Qt::ItemIsUserCheckable); item->setCheckState(Qt::Checked); // Enable as a default + ui->listRules->clearSelection(); ui->listRules->setCurrentItem(item); } @@ -402,6 +415,54 @@ void AutomatedRssDownloader::handleFeedCheckStateChange(QListWidgetItem *feed_it m_ruleList->saveRule(rule); } } + // Update Matching articles + updateMatchingArticles(); +} + +void AutomatedRssDownloader::updateMatchingArticles() +{ + ui->treeMatchingArticles->clear(); + if(ui->ruleDefBox->isEnabled()) { + saveCurrentRule(ui->listRules->currentItem()); + } + const QHash all_feeds = RssManager::instance()->getAllFeedsAsHash(); + + foreach(const QListWidgetItem *rule_item, ui->listRules->selectedItems()) { + RssDownloadRule rule = m_ruleList->getRule(rule_item->text()); + if(!rule.isValid()) continue; + foreach(const QString &feed_url, rule.rssFeeds()) { + Q_ASSERT(all_feeds.contains(feed_url)); + const RssFeed *feed = all_feeds.value(feed_url); + const QStringList matching_articles = rule.findMatchingArticles(feed); + if(!matching_articles.isEmpty()) + addFeedArticlesToTree(feed, matching_articles); + } + } +} + +void AutomatedRssDownloader::addFeedArticlesToTree(const RssFeed *feed, const QStringList &articles) +{ + // Check if this feed is already in the tree + QTreeWidgetItem *treeFeedItem = 0; + for(int i=0; itreeMatchingArticles->topLevelItemCount(); ++i) { + QTreeWidgetItem *item = ui->treeMatchingArticles->topLevelItem(i); + if(item->data(0, Qt::UserRole).toString() == feed->getUrl()) { + treeFeedItem = item; + break; + } + } + // If there is none, create it + if(!treeFeedItem) { + treeFeedItem = new QTreeWidgetItem(QStringList() << feed->getName()); + treeFeedItem->setData(0, Qt::UserRole, feed->getUrl()); + ui->treeMatchingArticles->addTopLevelItem(treeFeedItem); + } + // Insert the articles + foreach(const QString &art, articles) { + QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << art); + treeFeedItem->addChild(item); + } + ui->treeMatchingArticles->expandItem(treeFeedItem); } diff --git a/src/rss/automatedrssdownloader.h b/src/rss/automatedrssdownloader.h index aa48a0b87..6b876d155 100644 --- a/src/rss/automatedrssdownloader.h +++ b/src/rss/automatedrssdownloader.h @@ -70,10 +70,12 @@ private slots: void on_exportBtn_clicked(); void on_importBtn_clicked(); void renameSelectedRule(); + void updateMatchingArticles(); private: RssDownloadRule getCurrentRule() const; void initLabelCombobox(); + void addFeedArticlesToTree(const RssFeed *feed, const QStringList& articles); private: Ui::AutomatedRssDownloader *ui; diff --git a/src/rss/automatedrssdownloader.ui b/src/rss/automatedrssdownloader.ui index 7bcbecfd0..5aceba1eb 100644 --- a/src/rss/automatedrssdownloader.ui +++ b/src/rss/automatedrssdownloader.ui @@ -266,10 +266,15 @@ - - + + false - + + + + 1 + + diff --git a/src/rss/rss_imp.cpp b/src/rss/rss_imp.cpp index 763497c26..43871cc7c 100644 --- a/src/rss/rss_imp.cpp +++ b/src/rss/rss_imp.cpp @@ -580,7 +580,7 @@ void RSSImp::updateRefreshInterval(unsigned int val) { RSSImp::RSSImp(QBtSession *BTSession) : QWidget(), BTSession(BTSession){ setupUi(this); - rssmanager = new RssManager(BTSession); + rssmanager = RssManager::instance(); listStreams = new FeedListWidget(splitter_h, rssmanager); splitter_h->insertWidget(0, listStreams); @@ -634,7 +634,7 @@ RSSImp::~RSSImp(){ qDebug("Deleting RSSImp..."); saveFoldersOpenState(); delete listStreams; - delete rssmanager; + RssManager::drop(); qDebug("RSSImp deleted"); } diff --git a/src/rss/rssarticle.cpp b/src/rss/rssarticle.cpp index 0d06e9f4f..a06cee8b0 100644 --- a/src/rss/rssarticle.cpp +++ b/src/rss/rssarticle.cpp @@ -252,10 +252,6 @@ bool RssArticle::has_attachment() const { return !torrent_url.isEmpty(); } -QString RssArticle::getId() const { - return id; -} - QHash RssArticle::toHash() const { QHash item; item["title"] = title; @@ -282,10 +278,6 @@ bool RssArticle::isValid() const { return is_valid; } -QString RssArticle::getTitle() const{ - return title; -} - QString RssArticle::getAuthor() const { return author; } diff --git a/src/rss/rssarticle.h b/src/rss/rssarticle.h index 78c903d41..6a5bdbc09 100644 --- a/src/rss/rssarticle.h +++ b/src/rss/rssarticle.h @@ -48,12 +48,12 @@ public: QDateTime _date, QString _author, bool _read); ~RssArticle(); bool has_attachment() const; - QString getId() const; + inline QString getId() const { return id; } QHash toHash() const; static RssArticle* fromHash(RssFeed* parent, const QHash &h); RssFeed* getParent() const; bool isValid() const; - QString getTitle() const; + inline QString getTitle() const { return title; } QString getAuthor() const; QString getTorrentUrl() const; QString getLink() const; diff --git a/src/rss/rssdownloadrule.cpp b/src/rss/rssdownloadrule.cpp index 85973df81..39748cf05 100644 --- a/src/rss/rssdownloadrule.cpp +++ b/src/rss/rssdownloadrule.cpp @@ -34,6 +34,8 @@ #include "rssdownloadrule.h" #include "preferences.h" #include "qinisettings.h" +#include "rssfeed.h" +#include "rssarticle.h" RssDownloadRule::RssDownloadRule() { @@ -123,3 +125,13 @@ void RssDownloadRule::setSavePath(const QString &save_path) else m_savePath = QString(); } + +QStringList RssDownloadRule::findMatchingArticles(const RssFeed *feed) const +{ + QStringList ret; + foreach(const RssArticle *art, feed->values()) { + if(matches(art->getTitle())) + ret << art->getTitle(); + } + return ret; +} diff --git a/src/rss/rssdownloadrule.h b/src/rss/rssdownloadrule.h index 3dab0116f..c9ac46f05 100644 --- a/src/rss/rssdownloadrule.h +++ b/src/rss/rssdownloadrule.h @@ -34,6 +34,8 @@ #include #include +class RssFeed; + class RssDownloadRule { @@ -58,6 +60,7 @@ public: inline bool isValid() const { return !m_name.isEmpty(); } inline QString mustContain() const { return m_mustContain.join(" "); } inline QString mustNotContain() const { return m_mustNotContain.join(" "); } + QStringList findMatchingArticles(const RssFeed* feed) const; // Operators bool operator==(const RssDownloadRule &other); diff --git a/src/rss/rssdownloadrulelist.cpp b/src/rss/rssdownloadrulelist.cpp index 78919ae74..f50638c7f 100644 --- a/src/rss/rssdownloadrulelist.cpp +++ b/src/rss/rssdownloadrulelist.cpp @@ -33,6 +33,7 @@ #include #include "rssdownloadrulelist.h" +#include "rsssettings.h" #include "qinisettings.h" RssDownloadRuleList* RssDownloadRuleList::m_instance = 0; @@ -56,6 +57,7 @@ void RssDownloadRuleList::drop() RssDownloadRule RssDownloadRuleList::findMatchingRule(const QString &feed_url, const QString &article_title) const { + Q_ASSERT(RssSettings::isRssDownloadingEnabled()); QStringList rule_names = feedRules(feed_url); foreach(const QString &rule_name, rule_names) { RssDownloadRule rule = m_rules[rule_name]; @@ -127,6 +129,7 @@ void RssDownloadRuleList::loadRulesFromVariantHash(const QVariantHash &h) void RssDownloadRuleList::saveRule(const RssDownloadRule &rule) { + qDebug() << Q_FUNC_INFO << rule.name(); Q_ASSERT(rule.isValid()); if(m_rules.contains(rule.name())) { removeRule(rule.name()); diff --git a/src/rss/rssfeed.cpp b/src/rss/rssfeed.cpp index 86c21db0b..843836fa7 100644 --- a/src/rss/rssfeed.cpp +++ b/src/rss/rssfeed.cpp @@ -290,21 +290,23 @@ short RssFeed::readDoc(QIODevice* device) { resizeList(); // RSS Feed Downloader - foreach(RssArticle* item, values()) { - if(item->isRead()) continue; - QString torrent_url; - if(item->has_attachment()) - torrent_url = item->getTorrentUrl(); - else - torrent_url = item->getLink(); - // Check if the item should be automatically downloaded - const RssDownloadRule matching_rule = RssDownloadRuleList::instance()->findMatchingRule(url, item->getTitle()); - if(matching_rule.isValid()) { - // Download the torrent - BTSession->addConsoleMessage(tr("Automatically downloading %1 torrent from %2 RSS feed...").arg(item->getTitle()).arg(getName())); - BTSession->downloadUrlAndSkipDialog(torrent_url, matching_rule.savePath(), matching_rule.label()); - // Item was downloaded, consider it as Read - item->setRead(); + if(RssSettings::isRssDownloadingEnabled()) { + foreach(RssArticle* item, values()) { + if(item->isRead()) continue; + QString torrent_url; + if(item->has_attachment()) + torrent_url = item->getTorrentUrl(); + else + torrent_url = item->getLink(); + // Check if the item should be automatically downloaded + const RssDownloadRule matching_rule = RssDownloadRuleList::instance()->findMatchingRule(url, item->getTitle()); + if(matching_rule.isValid()) { + // Download the torrent + BTSession->addConsoleMessage(tr("Automatically downloading %1 torrent from %2 RSS feed...").arg(item->getTitle()).arg(getName())); + BTSession->downloadUrlAndSkipDialog(torrent_url, matching_rule.savePath(), matching_rule.label()); + // Item was downloaded, consider it as Read + item->setRead(); + } } } return 0; diff --git a/src/rss/rssfolder.cpp b/src/rss/rssfolder.cpp index 11ab61387..9c163009c 100644 --- a/src/rss/rssfolder.cpp +++ b/src/rss/rssfolder.cpp @@ -256,16 +256,27 @@ QList RssFolder::getAllFeeds() const { QList streams; foreach(RssFile *item, this->values()) { if(item->getType() == RssFile::FEED) { - streams << ((RssFeed*)item); + streams << static_cast(item); } else { - foreach(RssFeed* stream, ((RssFolder*)item)->getAllFeeds()) { - streams << stream; - } + streams << static_cast(item)->getAllFeeds(); } } return streams; } +QHash RssFolder::getAllFeedsAsHash() const { + QHash ret; + foreach(RssFile *item, this->values()) { + if(item->getType() == RssFile::FEED) { + RssFeed* feed = static_cast(item); + ret[feed->getUrl()] = feed; + } else { + ret.unite(static_cast(item)->getAllFeedsAsHash()); + } + } + return ret; +} + void RssFolder::addFile(RssFile * item) { if(item->getType() == RssFile::FEED) { Q_ASSERT(!this->contains(((RssFeed*)item)->getUrl())); diff --git a/src/rss/rssfolder.h b/src/rss/rssfolder.h index 825e24e77..9f09361a3 100644 --- a/src/rss/rssfolder.h +++ b/src/rss/rssfolder.h @@ -57,6 +57,7 @@ public: unsigned int getNbFeeds() const; QList getContent() const; QList getAllFeeds() const; + QHash getAllFeedsAsHash() const; QString getName() const; QString getID() const; bool hasChild(QString ID); diff --git a/src/rss/rssmanager.cpp b/src/rss/rssmanager.cpp index 8e8049b5e..647b4038a 100644 --- a/src/rss/rssmanager.cpp +++ b/src/rss/rssmanager.cpp @@ -35,7 +35,9 @@ #include "rssarticle.h" #include "rssdownloadrulelist.h" -RssManager::RssManager(QBtSession *BTSession): RssFolder(0, this, BTSession, QString::null) { +RssManager* RssManager::m_instance = 0; + +RssManager::RssManager(): RssFolder(0, this, QBtSession::instance(), QString::null) { loadStreamList(); connect(&newsRefresher, SIGNAL(timeout()), this, SLOT(refreshAll())); refreshInterval = RssSettings::getRSSRefreshInterval(); @@ -137,3 +139,16 @@ QList RssManager::sortNewsList(const QList& news_list) } return new_list; } + +RssManager * RssManager::instance() +{ + if(!m_instance) + m_instance = new RssManager; + return m_instance; +} + +void RssManager::drop() +{ + if(m_instance) + delete m_instance; +} diff --git a/src/rss/rssmanager.h b/src/rss/rssmanager.h index 490fa3e13..f457b6c2d 100644 --- a/src/rss/rssmanager.h +++ b/src/rss/rssmanager.h @@ -37,9 +37,13 @@ class RssManager: public RssFolder { Q_OBJECT +private: + explicit RssManager(); + static RssManager* m_instance; public: - RssManager(QBtSession *BTSession); + static RssManager* instance(); + static void drop(); ~RssManager(); static void insertSortElem(QList &list, RssArticle *item); static QList sortNewsList(const QList& news_list);