diff --git a/src/gui/rss/rss.ui b/src/gui/rss/rss.ui index 7d0d348bb..d83b562fa 100644 --- a/src/gui/rss/rss.ui +++ b/src/gui/rss/rss.ui @@ -116,16 +116,12 @@ - 50 - false + 75 + true - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Torrents:</span> <span style=" font-style:italic;">(double-click to download)</span></p></body></html> + Torrents: (double-click to download) @@ -139,7 +135,7 @@ p, li { white-space: pre-wrap; } Qt::CustomContextMenu - QAbstractItemView::SingleSelection + QAbstractItemView::ExtendedSelection QAbstractItemView::SelectItems diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index f31edc1fb..613288a2d 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -112,20 +112,31 @@ void RSSImp::displayItemsListMenu(const QPoint&) { QMenu myItemListMenu(this); QList selectedItems = listArticles->selectedItems(); - if (selectedItems.size() > 0) { - bool has_attachment = false; - foreach (const QListWidgetItem* item, selectedItems) { - if (m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()) - ->getItem(item->data(Article::IdRole).toString())->hasAttachment()) { - has_attachment = true; - break; - } - } - if (has_attachment) - myItemListMenu.addAction(actionDownload_torrent); - myItemListMenu.addAction(actionOpen_news_URL); + if (selectedItems.size() <= 0) + return; + + bool hasTorrent = false; + bool hasLink = false; + foreach (const QListWidgetItem* item, selectedItems) { + if (!item) continue; + RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); + if (!feed) continue; + RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); + if (!article) continue; + + if (!article->torrentUrl().isEmpty()) + hasTorrent = true; + if (!article->link().isEmpty()) + hasLink = true; + if (hasTorrent && hasLink) + break; } - myItemListMenu.exec(QCursor::pos()); + if (hasTorrent) + myItemListMenu.addAction(actionDownload_torrent); + if (hasLink) + myItemListMenu.addAction(actionOpen_news_URL); + if (hasTorrent || hasLink) + myItemListMenu.exec(QCursor::pos()); } void RSSImp::on_actionManage_cookies_triggered() @@ -238,21 +249,13 @@ void RSSImp::deleteSelectedItems() QList selectedItems = m_feedList->selectedItems(); if (selectedItems.isEmpty()) return; + if ((selectedItems.size() == 1) && (selectedItems.first() == m_feedList->stickyUnreadItem())) + return; - int ret; - if (selectedItems.size() > 1) { - ret = QMessageBox::question(this, tr("Are you sure? -- qBittorrent"), tr("Are you sure you want to delete these elements from the list?"), - tr("&Yes"), tr("&No"), - QString(), 0, 1); - } - else { - if (selectedItems.first() == m_feedList->stickyUnreadItem()) - return; - ret = QMessageBox::question(this, tr("Are you sure? -- qBittorrent"), tr("Are you sure you want to delete this element from the list?"), - tr("&Yes"), tr("&No"), - QString(), 0, 1); - } - if (ret) + QMessageBox::StandardButton answer = QMessageBox::question(this, tr("Deletion confirmation"), + tr("Are you sure you want to delete the selected RSS feeds?"), + QMessageBox::Yes|QMessageBox::No, QMessageBox::No); + if (answer == QMessageBox::No) return; foreach (QTreeWidgetItem* item, selectedItems) { @@ -337,31 +340,57 @@ void RSSImp::refreshAllFeeds() void RSSImp::downloadSelectedTorrents() { QList selected_items = listArticles->selectedItems(); - foreach (const QListWidgetItem* item, selected_items) { + if (selected_items.size() <= 0) + return; + foreach (QListWidgetItem* item, selected_items) { if (!item) continue; RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); if (!feed) continue; RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); if (!article) continue; + // Mark as read + article->markAsRead(); + item->setData(Article::ColorRole, QVariant(QColor("grey"))); + item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png"))); + + if (article->torrentUrl().isEmpty()) + continue; if (Preferences::instance()->useAdditionDialog()) AddNewTorrentDialog::show(article->torrentUrl()); else BitTorrent::Session::instance()->addTorrent(article->torrentUrl()); } + // Decrement feed nb unread news + updateItemInfos(m_feedList->stickyUnreadItem()); + updateItemInfos(m_feedList->getTreeItemFromUrl(selected_items.first()->data(Article::FeedUrlRole).toString())); } // open the url of the selected RSS articles in the Web browser void RSSImp::openSelectedArticlesUrls() { QList selected_items = listArticles->selectedItems(); - foreach (const QListWidgetItem* item, selected_items) { - RssArticlePtr news = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()) - ->getItem(item->data(Article::IdRole).toString()); - const QString link = news->link(); + if (selected_items.size() <= 0) + return; + foreach (QListWidgetItem* item, selected_items) { + if (!item) continue; + RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); + if (!feed) continue; + RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); + if (!article) continue; + + // Mark as read + article->markAsRead(); + item->setData(Article::ColorRole, QVariant(QColor("grey"))); + item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png"))); + + const QString link = article->link(); if (!link.isEmpty()) QDesktopServices::openUrl(QUrl(link)); } + // Decrement feed nb unread news + updateItemInfos(m_feedList->stickyUnreadItem()); + updateItemInfos(m_feedList->getTreeItemFromUrl(selected_items.first()->data(Article::FeedUrlRole).toString())); } //right-click on stream : give it an alias @@ -538,21 +567,11 @@ void RSSImp::refreshTextBrowser() { QList selection = listArticles->selectedItems(); if (selection.empty()) return; - Q_ASSERT(selection.size() == 1); QListWidgetItem *item = selection.first(); Q_ASSERT(item); if (item == m_currentArticle) return; - // Stop displaying previous news if necessary - if (m_feedList->currentFeed() == m_feedList->stickyUnreadItem()) { - if (m_currentArticle) { - disconnect(listArticles, SIGNAL(itemSelectionChanged()), this, SLOT(refreshTextBrowser())); - listArticles->removeItemWidget(m_currentArticle); - Q_ASSERT(m_currentArticle); - delete m_currentArticle; - connect(listArticles, SIGNAL(itemSelectionChanged()), this, SLOT(refreshTextBrowser())); - } - m_currentArticle = item; - } + m_currentArticle = item; + RssFeedPtr stream = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); RssArticlePtr article = stream->getItem(item->data(Article::IdRole).toString()); QString html; @@ -675,14 +694,11 @@ void RSSImp::onFeedContentChanged(const QString& url) qDebug() << Q_FUNC_INFO << url; QTreeWidgetItem *item = m_feedList->getTreeItemFromUrl(url); // If the feed is selected, update the displayed news - if (m_feedList->currentItem() == item ) { + if (m_feedList->currentItem() == item) populateArticleList(item); - } - else { - // Update unread items - if (m_feedList->currentItem() == m_feedList->stickyUnreadItem()) - populateArticleList(m_feedList->stickyUnreadItem()); - } + // Update unread items + else if (m_feedList->currentItem() == m_feedList->stickyUnreadItem()) + populateArticleList(m_feedList->stickyUnreadItem()); } void RSSImp::updateRefreshInterval(uint val) @@ -715,8 +731,6 @@ RSSImp::RSSImp(QWidget *parent): m_feedList = new FeedListWidget(splitter_h, m_rssManager); splitter_h->insertWidget(0, m_feedList); - listArticles->setSelectionBehavior(QAbstractItemView::SelectItems); - listArticles->setSelectionMode(QAbstractItemView::SingleSelection); editHotkey = new QShortcut(QKeySequence("F2"), m_feedList, 0, 0, Qt::WidgetShortcut); connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedRssFile())); connect(m_feedList, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedRssFile())); diff --git a/src/gui/rss/rssarticle.cpp b/src/gui/rss/rssarticle.cpp index a0262c1de..fbc7218bf 100644 --- a/src/gui/rss/rssarticle.cpp +++ b/src/gui/rss/rssarticle.cpp @@ -82,7 +82,7 @@ const QString& RssArticle::author() const { } const QString& RssArticle::torrentUrl() const { - return m_torrentUrl.isEmpty() ? m_link : m_torrentUrl; + return m_torrentUrl; } const QString& RssArticle::link() const { @@ -123,6 +123,6 @@ const QString& RssArticle::title() const } void RssArticle::handleTorrentDownloadSuccess(const QString &url) { - if (url == m_torrentUrl || url == m_link) + if (url == m_torrentUrl) markAsRead(); } diff --git a/src/gui/rss/rssfeed.cpp b/src/gui/rss/rssfeed.cpp index 837f5470b..6587e9123 100644 --- a/src/gui/rss/rssfeed.cpp +++ b/src/gui/rss/rssfeed.cpp @@ -364,6 +364,12 @@ void RssFeed::downloadArticleTorrentIfMatching(RssDownloadRuleList* rules, const rules->saveRulesToStorage(); // Download the torrent const QString& torrent_url = article->torrentUrl(); + if (torrent_url.isEmpty()) { + Logger::instance()->addMessage(tr("Automatic download %1 from %2 RSS feed failed because it doesn't contain a torrent or a magnet link...").arg(article->title()).arg(displayName()), Log::WARNING); + article->markAsRead(); + return; + } + 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); connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleStateChanged()), Qt::UniqueConnection); diff --git a/src/gui/rss/rssparser.cpp b/src/gui/rss/rssparser.cpp index 9f9b87207..fb0932d59 100644 --- a/src/gui/rss/rssparser.cpp +++ b/src/gui/rss/rssparser.cpp @@ -254,24 +254,32 @@ void RssParser::parseRssArticle(QXmlStreamReader& xml, const QString& feedUrl) if (xml.isStartElement()) { if (xml.name() == "title") - article["title"] = xml.readElementText(); + article["title"] = xml.readElementText().trimmed(); else if (xml.name() == "enclosure") { if (xml.attributes().value("type") == "application/x-bittorrent") article["torrent_url"] = xml.attributes().value("url").toString(); } - else if (xml.name() == "link") - article["news_link"] = xml.readElementText(); + else if (xml.name() == "link") { + QString link = xml.readElementText().trimmed(); + if (link.startsWith("magnet:", Qt::CaseInsensitive)) + article["torrent_url"] = link; // magnet link instead of a news URL + else + article["news_link"] = link; + } else if (xml.name() == "description") - article["description"] = xml.readElementText(); + article["description"] = xml.readElementText().trimmed(); else if (xml.name() == "pubDate") - article["date"] = parseDate(xml.readElementText()); + article["date"] = parseDate(xml.readElementText().trimmed()); else if (xml.name() == "author") - article["author"] = xml.readElementText(); + article["author"] = xml.readElementText().trimmed(); else if (xml.name() == "guid") - article["id"] = xml.readElementText(); + article["id"] = xml.readElementText().trimmed(); } } + if (!article.contains("torrent_url") && article.contains("news_link")) + article["torrent_url"] = article["news_link"]; + if (!article.contains("id")) { // Item does not have a guid, fall back to some other identifier const QString link = article.value("news_link").toString(); @@ -338,20 +346,21 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, // Workaround for CDATA (QString cannot parse html escapes on it's own) QTextDocument doc; doc.setHtml(xml.readElementText()); - article["title"] = doc.toPlainText(); + article["title"] = doc.toPlainText().trimmed(); } else if (xml.name() == "link") { - QString theLink = ( xml.attributes().isEmpty() ? - xml.readElementText() : - xml.attributes().value("href").toString() ); + QString link = ( xml.attributes().isEmpty() ? + xml.readElementText().trimmed() : + xml.attributes().value("href").toString() ); + + if (link.startsWith("magnet:", Qt::CaseInsensitive)) + article["torrent_url"] = link; // magnet link instead of a news URL + else + // Atom feeds can have relative links, work around this and + // take the stress of figuring article full URI from UI + // Assemble full URI + article["news_link"] = ( baseUrl.isEmpty() ? link : baseUrl + link ); - // Atom feeds can have relative links, work around this and - // take the stress of figuring article full URI from UI - - // Assemble full URI - article["news_link"] = ( baseUrl.isEmpty() ? - theLink : - baseUrl + theLink ); } else if (xml.name() == "summary" || xml.name() == "content"){ if(double_content) { // Duplicate content -> ignore @@ -367,13 +376,13 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, // Actually works great for non-broken content too QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements); if (!feedText.isEmpty()) - article["description"] = feedText; + article["description"] = feedText.trimmed(); double_content = true; } else if (xml.name() == "updated"){ // ATOM uses standard compliant date, don't do fancy stuff - QDateTime articleDate = QDateTime::fromString(xml.readElementText(), Qt::ISODate); + QDateTime articleDate = QDateTime::fromString(xml.readElementText().trimmed(), Qt::ISODate); article["date"] = ( articleDate.isValid() ? articleDate : QDateTime::currentDateTime() ); @@ -382,15 +391,18 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, xml.readNext(); while(xml.name() != "author") { if(xml.name() == "name") - article["author"] = xml.readElementText(); + article["author"] = xml.readElementText().trimmed(); xml.readNext(); } } else if (xml.name() == "id") - article["id"] = xml.readElementText(); + article["id"] = xml.readElementText().trimmed(); } } + if (!article.contains("torrent_url") && article.contains("news_link")) + article["torrent_url"] = article["news_link"]; + if (!article.contains("id")) { // Item does not have a guid, fall back to some other identifier const QString link = article.value("news_link").toString();