mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-01-08 08:07:23 +03:00
ff9a281b72
Change project directory structure according to application structure. Change 'nox' configuration option to something more meaningful 'nogui'. Rename 'Icons' folder to 'icons' (similar to other folders). Partially add 'nowebui' option support. Remove QConf project file.
798 lines
30 KiB
C++
798 lines
30 KiB
C++
/*
|
|
* Bittorrent Client using Qt4 and libtorrent.
|
|
* Copyright (C) 2006 Christophe Dumez, Arnaud Demaiziere
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* In addition, as a special exception, the copyright holders give permission to
|
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
* and distribute the linked executables. You must obey the GNU General Public
|
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
|
* modify file(s), you may extend this exception to your version of the file(s),
|
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
|
* exception statement from your version.
|
|
*
|
|
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org
|
|
*/
|
|
|
|
#include <QDesktopServices>
|
|
#include <QMenu>
|
|
#include <QStandardItemModel>
|
|
#include <QMessageBox>
|
|
#include <QString>
|
|
#include <QClipboard>
|
|
#include <QDragMoveEvent>
|
|
#include <QDebug>
|
|
|
|
#include "rss_imp.h"
|
|
#include "feedlistwidget.h"
|
|
#include "qbtsession.h"
|
|
#include "cookiesdlg.h"
|
|
#include "preferences.h"
|
|
#include "rsssettingsdlg.h"
|
|
#include "rssmanager.h"
|
|
#include "rssfolder.h"
|
|
#include "rssarticle.h"
|
|
#include "rssparser.h"
|
|
#include "rssfeed.h"
|
|
#include "automatedrssdownloader.h"
|
|
#include "iconprovider.h"
|
|
#include "autoexpandabledialog.h"
|
|
|
|
namespace Article
|
|
{
|
|
enum ArticleRoles
|
|
{
|
|
TitleRole = Qt::DisplayRole,
|
|
IconRole = Qt::DecorationRole,
|
|
ColorRole = Qt::ForegroundRole,
|
|
IdRole = Qt::UserRole + 1,
|
|
FeedUrlRole = Qt::UserRole + 2
|
|
};
|
|
}
|
|
|
|
// display a right-click menu
|
|
void RSSImp::displayRSSListMenu(const QPoint& pos)
|
|
{
|
|
if (!m_feedList->indexAt(pos).isValid())
|
|
// No item under the mouse, clear selection
|
|
m_feedList->clearSelection();
|
|
QMenu myRSSListMenu(this);
|
|
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
|
if (selectedItems.size() > 0) {
|
|
myRSSListMenu.addAction(actionUpdate);
|
|
myRSSListMenu.addAction(actionMark_items_read);
|
|
myRSSListMenu.addSeparator();
|
|
if (selectedItems.size() == 1) {
|
|
if (m_feedList->getRSSItem(selectedItems.first()) != m_rssManager) {
|
|
myRSSListMenu.addAction(actionRename);
|
|
myRSSListMenu.addAction(actionDelete);
|
|
myRSSListMenu.addSeparator();
|
|
if (m_feedList->isFolder(selectedItems.first()))
|
|
myRSSListMenu.addAction(actionNew_folder);
|
|
else
|
|
myRSSListMenu.addAction(actionManage_cookies);
|
|
}
|
|
}
|
|
else {
|
|
myRSSListMenu.addAction(actionDelete);
|
|
myRSSListMenu.addSeparator();
|
|
}
|
|
myRSSListMenu.addAction(actionNew_subscription);
|
|
if (m_feedList->isFeed(selectedItems.first())) {
|
|
myRSSListMenu.addSeparator();
|
|
myRSSListMenu.addAction(actionCopy_feed_URL);
|
|
}
|
|
}
|
|
else {
|
|
myRSSListMenu.addAction(actionNew_subscription);
|
|
myRSSListMenu.addAction(actionNew_folder);
|
|
myRSSListMenu.addSeparator();
|
|
myRSSListMenu.addAction(actionUpdate_all_feeds);
|
|
}
|
|
myRSSListMenu.exec(QCursor::pos());
|
|
}
|
|
|
|
void RSSImp::displayItemsListMenu(const QPoint&)
|
|
{
|
|
QMenu myItemListMenu(this);
|
|
QList<QListWidgetItem*> 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);
|
|
}
|
|
myItemListMenu.exec(QCursor::pos());
|
|
}
|
|
|
|
void RSSImp::on_actionManage_cookies_triggered()
|
|
{
|
|
Q_ASSERT(!m_feedList->selectedItems().empty());
|
|
// Get feed hostname
|
|
QString feed_url = m_feedList->getItemID(m_feedList->selectedItems().first());
|
|
QString feed_hostname = QUrl::fromEncoded(feed_url.toUtf8()).host();
|
|
qDebug("RSS Feed hostname is: %s", qPrintable(feed_hostname));
|
|
Q_ASSERT(!feed_hostname.isEmpty());
|
|
bool ok = false;
|
|
Preferences* const pref = Preferences::instance();
|
|
QList<QByteArray> raw_cookies = CookiesDlg::askForCookies(this, pref->getHostNameCookies(feed_hostname), &ok);
|
|
if (ok) {
|
|
qDebug() << "Settings cookies for host name: " << feed_hostname;
|
|
pref->setHostNameCookies(feed_hostname, raw_cookies);
|
|
}
|
|
}
|
|
|
|
void RSSImp::askNewFolder()
|
|
{
|
|
QTreeWidgetItem* parent_item = 0;
|
|
RssFolderPtr rss_parent;
|
|
if (m_feedList->selectedItems().size() > 0) {
|
|
parent_item = m_feedList->selectedItems().at(0);
|
|
rss_parent = qSharedPointerDynamicCast<RssFolder>(m_feedList->getRSSItem(parent_item));
|
|
Q_ASSERT(rss_parent);
|
|
}
|
|
else {
|
|
rss_parent = m_rssManager;
|
|
}
|
|
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)
|
|
return;
|
|
|
|
RssFolderPtr newFolder = rss_parent->addFolder(new_name);
|
|
QTreeWidgetItem* folderItem = createFolderListItem(newFolder);
|
|
if (parent_item)
|
|
parent_item->addChild(folderItem);
|
|
else
|
|
m_feedList->addTopLevelItem(folderItem);
|
|
// Notify TreeWidget
|
|
m_feedList->itemAdded(folderItem, newFolder);
|
|
// Expand parent folder to display new folder
|
|
if (parent_item)
|
|
parent_item->setExpanded(true);
|
|
m_rssManager->saveStreamList();
|
|
}
|
|
|
|
// add a stream by a button
|
|
void RSSImp::on_newFeedButton_clicked()
|
|
{
|
|
// Determine parent folder for new feed
|
|
QTreeWidgetItem *parent_item = 0;
|
|
QList<QTreeWidgetItem *> selected_items = m_feedList->selectedItems();
|
|
if (!selected_items.empty()) {
|
|
parent_item = selected_items.first();
|
|
// Consider the case where the user clicked on Unread item
|
|
if (parent_item == m_feedList->stickyUnreadItem())
|
|
parent_item = 0;
|
|
else
|
|
if (!m_feedList->isFolder(parent_item))
|
|
parent_item = parent_item->parent();
|
|
}
|
|
RssFolderPtr rss_parent;
|
|
if (parent_item)
|
|
rss_parent = qSharedPointerCast<RssFolder>(m_feedList->getRSSItem(parent_item));
|
|
else
|
|
rss_parent = m_rssManager;
|
|
// Ask for feed URL
|
|
bool ok;
|
|
QString clip_txt = qApp->clipboard()->text();
|
|
QString default_url = "http://";
|
|
if (clip_txt.startsWith("http://", Qt::CaseInsensitive) || clip_txt.startsWith("https://", Qt::CaseInsensitive) || clip_txt.startsWith("ftp://", Qt::CaseInsensitive))
|
|
default_url = clip_txt;
|
|
|
|
QString newUrl = AutoExpandableDialog::getText(this, tr("Please type a rss stream url"), tr("Stream URL:"), QLineEdit::Normal, default_url, &ok);
|
|
if (!ok)
|
|
return;
|
|
|
|
newUrl = newUrl.trimmed();
|
|
if (newUrl.isEmpty())
|
|
return;
|
|
|
|
if (m_feedList->hasFeed(newUrl)) {
|
|
QMessageBox::warning(this, "qBittorrent",
|
|
tr("This rss feed is already in the list."),
|
|
QMessageBox::Ok);
|
|
return;
|
|
}
|
|
RssFeedPtr stream = rss_parent->addStream(m_rssManager.data(), newUrl);
|
|
// Create TreeWidget item
|
|
QTreeWidgetItem* item = createFolderListItem(stream);
|
|
if (parent_item)
|
|
parent_item->addChild(item);
|
|
else
|
|
m_feedList->addTopLevelItem(item);
|
|
// Notify TreeWidget
|
|
m_feedList->itemAdded(item, stream);
|
|
|
|
stream->refresh();
|
|
m_rssManager->saveStreamList();
|
|
}
|
|
|
|
// delete a stream by a button
|
|
void RSSImp::deleteSelectedItems()
|
|
{
|
|
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
|
if (selectedItems.isEmpty())
|
|
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)
|
|
return;
|
|
|
|
foreach (QTreeWidgetItem* item, selectedItems) {
|
|
if (m_feedList->currentFeed() == item) {
|
|
textBrowser->clear();
|
|
m_currentArticle = 0;
|
|
listArticles->clear();
|
|
}
|
|
if (item == m_feedList->stickyUnreadItem())
|
|
continue;
|
|
RssFilePtr rss_item = m_feedList->getRSSItem(item);
|
|
QTreeWidgetItem* parent = item->parent();
|
|
// Notify TreeWidget
|
|
m_feedList->itemAboutToBeRemoved(item);
|
|
// Actually delete the item
|
|
rss_item->parent()->removeChild(rss_item->id());
|
|
delete item;
|
|
// Update parents count
|
|
while (parent && parent != m_feedList->invisibleRootItem()) {
|
|
updateItemInfos (parent);
|
|
parent = parent->parent();
|
|
}
|
|
// Clear feed data from RSS parser (possible caching).
|
|
RssFeed* rssFeed = dynamic_cast<RssFeed*>(rss_item.data());
|
|
if (rssFeed)
|
|
m_rssManager->rssParser()->clearFeedData(rssFeed->url());
|
|
}
|
|
m_rssManager->saveStreamList();
|
|
// Update Unread items
|
|
updateItemInfos(m_feedList->stickyUnreadItem());
|
|
}
|
|
|
|
void RSSImp::loadFoldersOpenState()
|
|
{
|
|
QStringList open_folders = Preferences::instance()->getRssOpenFolders();
|
|
foreach (const QString& var_path, open_folders) {
|
|
QStringList path = var_path.split("\\");
|
|
QTreeWidgetItem* parent = 0;
|
|
foreach (const QString& name, path) {
|
|
int nbChildren = 0;
|
|
if (parent)
|
|
nbChildren = parent->childCount();
|
|
else
|
|
nbChildren = m_feedList->topLevelItemCount();
|
|
for (int i = 0; i < nbChildren; ++i) {
|
|
QTreeWidgetItem* child;
|
|
if (parent)
|
|
child = parent->child(i);
|
|
else
|
|
child = m_feedList->topLevelItem(i);
|
|
if (m_feedList->getRSSItem(child)->id() == name) {
|
|
parent = child;
|
|
parent->setExpanded(true);
|
|
qDebug("expanding folder %s", qPrintable(name));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RSSImp::saveFoldersOpenState()
|
|
{
|
|
QStringList open_folders;
|
|
QList<QTreeWidgetItem*> items = m_feedList->getAllOpenFolders();
|
|
foreach (QTreeWidgetItem* item, items) {
|
|
QString path = m_feedList->getItemPath(item).join("\\");
|
|
qDebug("saving open folder: %s", qPrintable(path));
|
|
open_folders << path;
|
|
}
|
|
Preferences::instance()->setRssOpenFolders(open_folders);
|
|
}
|
|
|
|
// refresh all streams by a button
|
|
void RSSImp::refreshAllFeeds()
|
|
{
|
|
foreach (QTreeWidgetItem* item, m_feedList->getAllFeedItems())
|
|
item->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png")));
|
|
m_rssManager->refresh();
|
|
}
|
|
|
|
void RSSImp::downloadSelectedTorrents()
|
|
{
|
|
QList<QListWidgetItem*> selected_items = listArticles->selectedItems();
|
|
foreach (const 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;
|
|
|
|
QString torrentLink = article->torrentUrl();
|
|
// Check if it is a magnet link
|
|
if (torrentLink.startsWith("magnet:", Qt::CaseInsensitive)) {
|
|
QBtSession::instance()->addMagnetInteractive(torrentLink);
|
|
}
|
|
else {
|
|
// Load possible cookies
|
|
QString feed_url = m_feedList->getItemID(m_feedList->selectedItems().first());
|
|
QString feed_hostname = QUrl::fromEncoded(feed_url.toUtf8()).host();
|
|
QList<QNetworkCookie> cookies = Preferences::instance()->getHostNameQNetworkCookies(feed_hostname);
|
|
qDebug("Loaded %d cookies for RSS item\n", cookies.size());
|
|
QBtSession::instance()->downloadFromUrl(torrentLink, cookies);
|
|
}
|
|
}
|
|
}
|
|
|
|
// open the url of the selected RSS articles in the Web browser
|
|
void RSSImp::openSelectedArticlesUrls()
|
|
{
|
|
QList<QListWidgetItem *> 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 (!link.isEmpty())
|
|
QDesktopServices::openUrl(QUrl(link));
|
|
}
|
|
}
|
|
|
|
//right-click on stream : give it an alias
|
|
void RSSImp::renameSelectedRssFile()
|
|
{
|
|
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
|
if (selectedItems.size() != 1)
|
|
return;
|
|
QTreeWidgetItem* item = selectedItems.first();
|
|
if (item == m_feedList->stickyUnreadItem())
|
|
return;
|
|
RssFilePtr rss_item = m_feedList->getRSSItem(item);
|
|
bool ok;
|
|
QString newName;
|
|
do {
|
|
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)) {
|
|
QMessageBox::warning(0, tr("Name already in use"), tr("This name is already used by another item, please choose another one."));
|
|
ok = false;
|
|
}
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
} while (!ok);
|
|
// Rename item
|
|
rss_item->rename(newName);
|
|
// Update TreeWidget
|
|
updateItemInfos(item);
|
|
}
|
|
|
|
// right-click on stream : refresh it
|
|
void RSSImp::refreshSelectedItems()
|
|
{
|
|
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
|
foreach (QTreeWidgetItem* item, selectedItems) {
|
|
RssFilePtr file = m_feedList->getRSSItem(item);
|
|
// Update icons
|
|
if (item == m_feedList->stickyUnreadItem()) {
|
|
refreshAllFeeds();
|
|
return;
|
|
}
|
|
else {
|
|
if (!file->refresh())
|
|
continue;
|
|
// Update UI
|
|
if (qSharedPointerDynamicCast<RssFeed>(file)) {
|
|
item->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png")));
|
|
}
|
|
else if (qSharedPointerDynamicCast<RssFolder>(file)) {
|
|
// Update feeds in the folder
|
|
foreach (QTreeWidgetItem *feed, m_feedList->getAllFeedItems(item))
|
|
feed->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png")));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RSSImp::copySelectedFeedsURL()
|
|
{
|
|
QStringList URLs;
|
|
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
|
QTreeWidgetItem* item;
|
|
foreach (item, selectedItems)
|
|
if (m_feedList->isFeed(item))
|
|
URLs << m_feedList->getItemID(item);
|
|
qApp->clipboard()->setText(URLs.join("\n"));
|
|
}
|
|
|
|
void RSSImp::on_markReadButton_clicked()
|
|
{
|
|
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
|
|
foreach (QTreeWidgetItem* item, selectedItems) {
|
|
RssFilePtr rss_item = m_feedList->getRSSItem(item);
|
|
Q_ASSERT(rss_item);
|
|
rss_item->markAsRead();
|
|
updateItemInfos(item);
|
|
}
|
|
// Update article list
|
|
if (!selectedItems.isEmpty())
|
|
populateArticleList(m_feedList->currentItem());
|
|
}
|
|
|
|
QTreeWidgetItem* RSSImp::createFolderListItem(const RssFilePtr& rssFile)
|
|
{
|
|
Q_ASSERT(rssFile);
|
|
QTreeWidgetItem* item = new QTreeWidgetItem;
|
|
item->setData(0, Qt::DisplayRole, QVariant(rssFile->displayName() + QString::fromUtf8(" (") + QString::number(rssFile->unreadCount()) + QString(")")));
|
|
item->setData(0, Qt::DecorationRole, rssFile->icon());
|
|
|
|
return item;
|
|
}
|
|
|
|
void RSSImp::fillFeedsList(QTreeWidgetItem* parent, const RssFolderPtr& rss_parent)
|
|
{
|
|
QList<RssFilePtr> children;
|
|
if (parent)
|
|
children = rss_parent->getContent();
|
|
else
|
|
children = m_rssManager->getContent();
|
|
foreach (const RssFilePtr& rssFile, children) {
|
|
QTreeWidgetItem* item = createFolderListItem(rssFile);
|
|
Q_ASSERT(item);
|
|
if (parent)
|
|
parent->addChild(item);
|
|
else
|
|
m_feedList->addTopLevelItem(item);
|
|
|
|
// Notify TreeWidget of item addition
|
|
m_feedList->itemAdded(item, rssFile);
|
|
|
|
// Recursive call if this is a folder.
|
|
if (RssFolderPtr folder = qSharedPointerDynamicCast<RssFolder>(rssFile))
|
|
fillFeedsList(item, folder);
|
|
}
|
|
}
|
|
|
|
QListWidgetItem* RSSImp::createArticleListItem(const RssArticlePtr& article)
|
|
{
|
|
Q_ASSERT(article);
|
|
QListWidgetItem* item = new QListWidgetItem;
|
|
|
|
item->setData(Article::TitleRole, article->title());
|
|
item->setData(Article::FeedUrlRole, article->parent()->url());
|
|
item->setData(Article::IdRole, article->guid());
|
|
if (article->isRead()) {
|
|
item->setData(Article::ColorRole, QVariant(QColor("grey")));
|
|
item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png")));
|
|
}
|
|
else {
|
|
item->setData(Article::ColorRole, QVariant(QColor("blue")));
|
|
item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere2.png")));
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
// fills the newsList
|
|
void RSSImp::populateArticleList(QTreeWidgetItem* item)
|
|
{
|
|
if (!item) {
|
|
listArticles->clear();
|
|
return;
|
|
}
|
|
|
|
RssFilePtr rss_item = m_feedList->getRSSItem(item);
|
|
if (!rss_item)
|
|
return;
|
|
|
|
// Clear the list first
|
|
textBrowser->clear();
|
|
m_currentArticle = 0;
|
|
listArticles->clear();
|
|
|
|
qDebug("Getting the list of news");
|
|
RssArticleList articles;
|
|
if (rss_item == m_rssManager)
|
|
articles = rss_item->unreadArticleListByDateDesc();
|
|
else
|
|
articles = rss_item->articleListByDateDesc();
|
|
|
|
qDebug("Got the list of news");
|
|
foreach (const RssArticlePtr& article, articles) {
|
|
QListWidgetItem* articleItem = createArticleListItem(article);
|
|
listArticles->addItem(articleItem);
|
|
}
|
|
qDebug("Added all news to the GUI");
|
|
}
|
|
|
|
// display a news
|
|
void RSSImp::refreshTextBrowser()
|
|
{
|
|
QList<QListWidgetItem*> 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;
|
|
}
|
|
RssFeedPtr stream = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString());
|
|
RssArticlePtr article = stream->getItem(item->data(Article::IdRole).toString());
|
|
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->title() + "</div>";
|
|
if (article->date().isValid())
|
|
html += "<div style='background-color: #efefef;'><b>" + tr("Date: ") + "</b>" + article->date().toLocalTime().toString(Qt::SystemLocaleLongDate) + "</div>";
|
|
if (!article->author().isEmpty())
|
|
html += "<div style='background-color: #efefef;'><b>" + tr("Author: ") + "</b>" + article->author() + "</div>";
|
|
html += "</div>";
|
|
html += "<div style='margin-left: 5px; margin-right: 5px;'>";
|
|
if(Qt::mightBeRichText(article->description())) {
|
|
html += article->description();
|
|
}
|
|
else {
|
|
QString description = article->description();
|
|
QRegExp rx;
|
|
// If description is plain text, replace BBCode tags with HTML and wrap everything in <pre></pre> so it looks nice
|
|
rx.setMinimal(true);
|
|
rx.setCaseSensitivity(Qt::CaseInsensitive);
|
|
|
|
rx.setPattern("\\[img\\](.+)\\[/img\\]");
|
|
description = description.replace(rx, "<img src=\"\\1\">");
|
|
|
|
rx.setPattern("\\[url=(\")?(.+)\\1\\]");
|
|
description = description.replace(rx, "<a href=\"\\2\">");
|
|
description = description.replace("[/url]", "</a>", Qt::CaseInsensitive);
|
|
|
|
rx.setPattern("\\[(/)?([bius])\\]");
|
|
description = description.replace(rx, "<\\1\\2>");
|
|
|
|
rx.setPattern("\\[color=(\")?(.+)\\1\\]");
|
|
description = description.replace(rx, "<span style=\"color:\\2\">");
|
|
description = description.replace("[/color]", "</span>", Qt::CaseInsensitive);
|
|
|
|
rx.setPattern("\\[size=(\")?(.+)\\d\\1\\]");
|
|
description = description.replace(rx, "<span style=\"font-size:\\2px\">");
|
|
description = description.replace("[/size]", "</span>", Qt::CaseInsensitive);
|
|
|
|
html += "<pre>" + description + "</pre>";
|
|
}
|
|
html += "</div>";
|
|
textBrowser->setHtml(html);
|
|
article->markAsRead();
|
|
item->setData(Article::ColorRole, QVariant(QColor("grey")));
|
|
item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png")));
|
|
// Decrement feed nb unread news
|
|
updateItemInfos(m_feedList->stickyUnreadItem());
|
|
updateItemInfos(m_feedList->getTreeItemFromUrl(item->data(Article::FeedUrlRole).toString()));
|
|
}
|
|
|
|
void RSSImp::saveSlidersPosition()
|
|
{
|
|
// Remember sliders positions
|
|
Preferences* const pref = Preferences::instance();
|
|
pref->setRssHSplitterState(splitter_h->saveState());
|
|
pref->setRssVSplitterState(splitter_v->saveState());
|
|
qDebug("Splitters position saved");
|
|
}
|
|
|
|
void RSSImp::restoreSlidersPosition()
|
|
{
|
|
const Preferences* const pref = Preferences::instance();
|
|
QByteArray pos_h = pref->getRssHSplitterState();
|
|
if (!pos_h.isNull())
|
|
splitter_h->restoreState(pos_h);
|
|
QByteArray pos_v = pref->getRssVSplitterState();
|
|
if (!pos_v.isNull())
|
|
splitter_v->restoreState(pos_v);
|
|
}
|
|
|
|
void RSSImp::updateItemsInfos(const QList<QTreeWidgetItem*>& items)
|
|
{
|
|
foreach (QTreeWidgetItem* item, items)
|
|
updateItemInfos(item);
|
|
}
|
|
|
|
void RSSImp::updateItemInfos(QTreeWidgetItem *item)
|
|
{
|
|
RssFilePtr rss_item = m_feedList->getRSSItem(item);
|
|
if (!rss_item)
|
|
return;
|
|
|
|
QString name;
|
|
if (rss_item == m_rssManager)
|
|
name = tr("Unread");
|
|
else
|
|
name = rss_item->displayName();
|
|
item->setText(0, name + QString::fromUtf8(" (") + QString::number(rss_item->unreadCount()) + QString(")"));
|
|
// If item has a parent, update it too
|
|
if (item->parent())
|
|
updateItemInfos(item->parent());
|
|
}
|
|
|
|
void RSSImp::updateFeedIcon(const QString& url, const QString& iconPath)
|
|
{
|
|
QTreeWidgetItem* item = m_feedList->getTreeItemFromUrl(url);
|
|
item->setData(0, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
|
}
|
|
|
|
void RSSImp::updateFeedInfos(const QString& url, const QString& display_name, uint nbUnread)
|
|
{
|
|
qDebug() << Q_FUNC_INFO << display_name;
|
|
QTreeWidgetItem *item = m_feedList->getTreeItemFromUrl(url);
|
|
RssFeedPtr stream = qSharedPointerCast<RssFeed>(m_feedList->getRSSItem(item));
|
|
item->setText(0, display_name + QString::fromUtf8(" (") + QString::number(nbUnread) + QString(")"));
|
|
if (!stream->isLoading())
|
|
item->setData(0, Qt::DecorationRole, QVariant(stream->icon()));
|
|
// Update parent
|
|
if (item->parent())
|
|
updateItemInfos(item->parent());
|
|
// Update Unread item
|
|
updateItemInfos(m_feedList->stickyUnreadItem());
|
|
}
|
|
|
|
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 ) {
|
|
populateArticleList(item);
|
|
}
|
|
else {
|
|
// Update unread items
|
|
if (m_feedList->currentItem() == m_feedList->stickyUnreadItem())
|
|
populateArticleList(m_feedList->stickyUnreadItem());
|
|
}
|
|
}
|
|
|
|
void RSSImp::updateRefreshInterval(uint val)
|
|
{
|
|
m_rssManager->updateRefreshInterval(val);
|
|
}
|
|
|
|
RSSImp::RSSImp(QWidget *parent):
|
|
QWidget(parent),
|
|
m_rssManager(new RssManager)
|
|
{
|
|
setupUi(this);
|
|
// Icons
|
|
actionCopy_feed_URL->setIcon(IconProvider::instance()->getIcon("edit-copy"));
|
|
actionDelete->setIcon(IconProvider::instance()->getIcon("edit-delete"));
|
|
actionDownload_torrent->setIcon(IconProvider::instance()->getIcon("download"));
|
|
actionManage_cookies->setIcon(IconProvider::instance()->getIcon("preferences-web-browser-cookies"));
|
|
actionMark_items_read->setIcon(IconProvider::instance()->getIcon("mail-mark-read"));
|
|
actionNew_folder->setIcon(IconProvider::instance()->getIcon("folder-new"));
|
|
actionNew_subscription->setIcon(IconProvider::instance()->getIcon("list-add"));
|
|
actionOpen_news_URL->setIcon(IconProvider::instance()->getIcon("application-x-mswinurl"));
|
|
actionRename->setIcon(IconProvider::instance()->getIcon("edit-rename"));
|
|
actionUpdate->setIcon(IconProvider::instance()->getIcon("view-refresh"));
|
|
actionUpdate_all_feeds->setIcon(IconProvider::instance()->getIcon("view-refresh"));
|
|
newFeedButton->setIcon(IconProvider::instance()->getIcon("list-add"));
|
|
markReadButton->setIcon(IconProvider::instance()->getIcon("mail-mark-read"));
|
|
updateAllButton->setIcon(IconProvider::instance()->getIcon("view-refresh"));
|
|
rssDownloaderBtn->setIcon(IconProvider::instance()->getIcon("download"));
|
|
settingsButton->setIcon(IconProvider::instance()->getIcon("preferences-system"));
|
|
|
|
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()));
|
|
deleteHotkey = new QShortcut(QKeySequence(QKeySequence::Delete), m_feedList, 0, 0, Qt::WidgetShortcut);
|
|
connect(deleteHotkey, SIGNAL(activated()), SLOT(deleteSelectedItems()));
|
|
|
|
m_rssManager->loadStreamList();
|
|
fillFeedsList();
|
|
populateArticleList(m_feedList->currentItem());
|
|
|
|
loadFoldersOpenState();
|
|
connect(m_rssManager.data(), SIGNAL(feedInfosChanged(QString, QString, unsigned int)), SLOT(updateFeedInfos(QString, QString, unsigned int)));
|
|
connect(m_rssManager.data(), SIGNAL(feedContentChanged(QString)), SLOT(onFeedContentChanged(QString)));
|
|
connect(m_rssManager.data(), SIGNAL(feedIconChanged(QString, QString)), SLOT(updateFeedIcon(QString, QString)));
|
|
|
|
connect(m_feedList, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(displayRSSListMenu(const QPoint &)));
|
|
connect(listArticles, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(displayItemsListMenu(const QPoint &)));
|
|
|
|
// Feeds list actions
|
|
connect(actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedItems()));
|
|
connect(actionRename, SIGNAL(triggered()), this, SLOT(renameSelectedRssFile()));
|
|
connect(actionUpdate, SIGNAL(triggered()), this, SLOT(refreshSelectedItems()));
|
|
connect(actionNew_folder, SIGNAL(triggered()), this, SLOT(askNewFolder()));
|
|
connect(actionNew_subscription, SIGNAL(triggered()), this, SLOT(on_newFeedButton_clicked()));
|
|
connect(actionUpdate_all_feeds, SIGNAL(triggered()), this, SLOT(refreshAllFeeds()));
|
|
connect(updateAllButton, SIGNAL(clicked()), SLOT(refreshAllFeeds()));
|
|
connect(actionCopy_feed_URL, SIGNAL(triggered()), this, SLOT(copySelectedFeedsURL()));
|
|
connect(actionMark_items_read, SIGNAL(triggered()), this, SLOT(on_markReadButton_clicked()));
|
|
// News list actions
|
|
connect(actionOpen_news_URL, SIGNAL(triggered()), this, SLOT(openSelectedArticlesUrls()));
|
|
connect(actionDownload_torrent, SIGNAL(triggered()), this, SLOT(downloadSelectedTorrents()));
|
|
|
|
connect(m_feedList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(populateArticleList(QTreeWidgetItem*)));
|
|
connect(m_feedList, SIGNAL(foldersAltered(QList<QTreeWidgetItem*>)), this, SLOT(updateItemsInfos(QList<QTreeWidgetItem*>)));
|
|
|
|
connect(listArticles, SIGNAL(itemSelectionChanged()), this, SLOT(refreshTextBrowser()));
|
|
connect(listArticles, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(downloadSelectedTorrents()));
|
|
|
|
// Refresh all feeds
|
|
m_rssManager->refresh();
|
|
// Restore sliders position
|
|
restoreSlidersPosition();
|
|
// Bind saveSliders slots
|
|
connect(splitter_v, SIGNAL(splitterMoved(int, int)), this, SLOT(saveSlidersPosition()));
|
|
connect(splitter_h, SIGNAL(splitterMoved(int, int)), this, SLOT(saveSlidersPosition()));
|
|
|
|
qDebug("RSSImp constructed");
|
|
}
|
|
|
|
RSSImp::~RSSImp()
|
|
{
|
|
qDebug("Deleting RSSImp...");
|
|
saveFoldersOpenState();
|
|
delete editHotkey;
|
|
delete deleteHotkey;
|
|
delete m_feedList;
|
|
qDebug("RSSImp deleted");
|
|
}
|
|
|
|
void RSSImp::on_settingsButton_clicked()
|
|
{
|
|
RssSettingsDlg dlg(this);
|
|
if (dlg.exec())
|
|
updateRefreshInterval(Preferences::instance()->getRSSRefreshInterval());
|
|
}
|
|
|
|
void RSSImp::on_rssDownloaderBtn_clicked()
|
|
{
|
|
AutomatedRssDownloader dlg(m_rssManager, this);
|
|
dlg.exec();
|
|
if (dlg.isRssDownloaderEnabled()) {
|
|
m_rssManager->recheckRssItemsForDownload();
|
|
refreshAllFeeds();
|
|
}
|
|
}
|