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
This commit is contained in:
Christophe Dumez 2010-11-13 19:36:46 +00:00
parent 345222cf42
commit c387c15eb0
18 changed files with 174 additions and 48 deletions

View file

@ -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

View file

@ -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:

View file

@ -68,6 +68,8 @@
#include <boost/filesystem/exception.hpp>
#include <queue>
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;
}

View file

@ -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<torrent_handle> getTorrents() const;

View file

@ -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<QString, RssFeed*> 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; i<ui->treeMatchingArticles->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);
}

View file

@ -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;

View file

@ -266,10 +266,15 @@
</widget>
</item>
<item>
<widget class="QListWidget" name="listMatchingArticles">
<property name="enabled">
<widget class="QTreeWidget" name="treeMatchingArticles">
<attribute name="headerVisible">
<bool>false</bool>
</property>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>

View file

@ -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");
}

View file

@ -252,10 +252,6 @@ bool RssArticle::has_attachment() const {
return !torrent_url.isEmpty();
}
QString RssArticle::getId() const {
return id;
}
QHash<QString, QVariant> RssArticle::toHash() const {
QHash<QString, QVariant> 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;
}

View file

@ -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<QString, QVariant> toHash() const;
static RssArticle* fromHash(RssFeed* parent, const QHash<QString, QVariant> &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;

View file

@ -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;
}

View file

@ -34,6 +34,8 @@
#include <QStringList>
#include <QVariantHash>
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);

View file

@ -33,6 +33,7 @@
#include <QDebug>
#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());

View file

@ -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;

View file

@ -256,16 +256,27 @@ QList<RssFeed*> RssFolder::getAllFeeds() const {
QList<RssFeed*> streams;
foreach(RssFile *item, this->values()) {
if(item->getType() == RssFile::FEED) {
streams << ((RssFeed*)item);
streams << static_cast<RssFeed*>(item);
} else {
foreach(RssFeed* stream, ((RssFolder*)item)->getAllFeeds()) {
streams << stream;
}
streams << static_cast<RssFolder*>(item)->getAllFeeds();
}
}
return streams;
}
QHash<QString, RssFeed*> RssFolder::getAllFeedsAsHash() const {
QHash<QString, RssFeed*> ret;
foreach(RssFile *item, this->values()) {
if(item->getType() == RssFile::FEED) {
RssFeed* feed = static_cast<RssFeed*>(item);
ret[feed->getUrl()] = feed;
} else {
ret.unite(static_cast<RssFolder*>(item)->getAllFeedsAsHash());
}
}
return ret;
}
void RssFolder::addFile(RssFile * item) {
if(item->getType() == RssFile::FEED) {
Q_ASSERT(!this->contains(((RssFeed*)item)->getUrl()));

View file

@ -57,6 +57,7 @@ public:
unsigned int getNbFeeds() const;
QList<RssFile*> getContent() const;
QList<RssFeed*> getAllFeeds() const;
QHash<QString, RssFeed*> getAllFeedsAsHash() const;
QString getName() const;
QString getID() const;
bool hasChild(QString ID);

View file

@ -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<RssArticle*> RssManager::sortNewsList(const QList<RssArticle*>& 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;
}

View file

@ -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<RssArticle*> &list, RssArticle *item);
static QList<RssArticle*> sortNewsList(const QList<RssArticle*>& news_list);