diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index 6ebfaaf4c84..2f71a896786 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -98,6 +98,7 @@ AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent) m_ui->mainSplitter->setCollapsible(0, false); m_ui->mainSplitter->setCollapsible(1, false); m_ui->mainSplitter->setCollapsible(2, true); // Only the preview list is collapsible + m_ui->listFeeds->setHeaderHidden(true); connect(m_ui->checkRegex, &QAbstractButton::toggled, this, &AutomatedRssDownloader::updateFieldsToolTips); connect(m_ui->ruleList, &QWidget::customContextMenuRequested, this, &AutomatedRssDownloader::displayRulesListMenu); @@ -136,7 +137,7 @@ AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent) connect(m_ui->spinIgnorePeriod, qOverload(&QSpinBox::valueChanged) , this, &AutomatedRssDownloader::handleRuleDefinitionChanged); - connect(m_ui->listFeeds, &QListWidget::itemChanged, this, &AutomatedRssDownloader::handleFeedCheckStateChange); + connect(m_ui->listFeeds, &QTreeWidget::itemChanged, this, &AutomatedRssDownloader::handleFeedCheckStateChange); connect(m_ui->ruleList, &QListWidget::itemSelectionChanged, this, &AutomatedRssDownloader::updateRuleDefinitionBox); connect(m_ui->ruleList, &QListWidget::itemChanged, this, &AutomatedRssDownloader::handleRuleCheckStateChange); @@ -204,15 +205,82 @@ void AutomatedRssDownloader::createRuleItem(const RSS::AutoDownloadRule &rule) void AutomatedRssDownloader::loadFeedList() { const QSignalBlocker feedListSignalBlocker(m_ui->listFeeds); + QTreeWidgetItem *rssRoot = createFeedTreeItem(RSS::Session::instance()->rootFolder(), nullptr); + loadFeedTree(rssRoot, RSS::Session::instance()->rootFolder()); - for (const auto *feed : asConst(RSS::Session::instance()->feeds())) + updateFeedList(); +} + +QTreeWidgetItem * AutomatedRssDownloader::createFeedTreeItem(const RSS::Item *rssItem, QTreeWidgetItem *parentItem) +{ + auto *item = new QTreeWidgetItem; + item->setData(0, Qt::UserRole, QVariant::fromValue(rssItem)); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable | Qt::ItemIsAutoTristate); + item->setCheckState(0, Qt::Unchecked); // Init state for folders as we don't manually set them later + + QIcon icon; + if(auto feed = qobject_cast(rssItem)) + { + const QPixmap pixmap{feed->iconPath().data()}; + if(!pixmap.isNull()) + icon = {pixmap}; + else + icon = UIThemeManager::instance()->getIcon(u"application-rss"_qs); + } + else { - QListWidgetItem *item = new QListWidgetItem(feed->name(), m_ui->listFeeds); - item->setData(Qt::UserRole, feed->url()); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable | Qt::ItemIsAutoTristate); + icon = UIThemeManager::instance()->getIcon(u"directory"_qs); } + item->setData(0, Qt::DecorationRole, icon); - updateFeedList(); + if(!parentItem) + { + item->setData(0, Qt::DisplayRole, u"/"_qs); + m_ui->listFeeds->addTopLevelItem(item); + item->setExpanded(true); + } + else + { + item->setData(0, Qt::DisplayRole, rssItem->name()); + parentItem->addChild(item); + } + return item; +} + +void AutomatedRssDownloader::loadFeedTree(QTreeWidgetItem *parent, const RSS::Folder *rssParent) +{ + for (const auto *rssItem : asConst(rssParent->items())) + { + QTreeWidgetItem *item = createFeedTreeItem(rssItem, parent); + + // Recursive call if this is a folder. + if (const auto *folder = qobject_cast(rssItem)) + loadFeedTree(item, folder); + } +} + +QList AutomatedRssDownloader::getAllFeedTreeItems(QTreeWidgetItem *parent) const +{ + QList feedItems; + int nbChildren = (parent ? parent->childCount() : m_ui->listFeeds->topLevelItemCount()); + for (int i = 0; i < nbChildren; ++i) + { + QTreeWidgetItem *item (parent ? parent->child(i) : m_ui->listFeeds->topLevelItem(i)); + if (qobject_cast(item->data(0, Qt::UserRole).value())) + feedItems << item; + else + feedItems << getAllFeedTreeItems(item); + } + return feedItems; +} + +void AutomatedRssDownloader::sortTreeRecursively(QTreeWidgetItem *item) +{ + for (int i = 0; i < item->childCount(); i++) { + QTreeWidgetItem *child = item->child(i); + sortTreeRecursively(child); + } + item->sortChildren(0, Qt::AscendingOrder); } void AutomatedRssDownloader::updateFeedList() @@ -228,33 +296,37 @@ void AutomatedRssDownloader::updateFeedList() bool enable = !selection.isEmpty(); - for (int i = 0; i < m_ui->listFeeds->count(); ++i) + for (int i = 0; i < m_ui->listFeeds->topLevelItemCount(); ++i) + m_ui->listFeeds->topLevelItem(i)->setHidden(!enable); + + for (QTreeWidgetItem* item: asConst(getAllFeedTreeItems())) { - QListWidgetItem *item = m_ui->listFeeds->item(i); - const QString feedURL = item->data(Qt::UserRole).toString(); - item->setHidden(!enable); + if (auto rssItem = qobject_cast(item->data(0, Qt::UserRole).value())) + { + const QString feedURL = rssItem->url(); + item->setHidden(!enable); - bool allEnabled = true; - bool anyEnabled = false; + bool allEnabled = true; + bool anyEnabled = false; - for (const QListWidgetItem *ruleItem : asConst(selection)) - { - const auto rule = RSS::AutoDownloader::instance()->ruleByName(ruleItem->text()); - if (rule.feedURLs().contains(feedURL)) - anyEnabled = true; + for (const QListWidgetItem *ruleItem : asConst(selection)) + { + const auto rule = RSS::AutoDownloader::instance()->ruleByName(ruleItem->text()); + if (rule.feedURLs().contains(feedURL)) + anyEnabled = true; + else + allEnabled = false; + } + if (anyEnabled && allEnabled) + item->setCheckState(0, Qt::Checked); + else if (anyEnabled) + item->setCheckState(0, Qt::PartiallyChecked); else - allEnabled = false; + item->setCheckState(0, Qt::Unchecked); } - - if (anyEnabled && allEnabled) - item->setCheckState(Qt::Checked); - else if (anyEnabled) - item->setCheckState(Qt::PartiallyChecked); - else - item->setCheckState(Qt::Unchecked); } - m_ui->listFeeds->sortItems(); + sortTreeRecursively(m_ui->listFeeds->invisibleRootItem()); m_ui->lblListFeeds->setEnabled(enable); m_ui->listFeeds->setEnabled(enable); } @@ -569,25 +641,30 @@ void AutomatedRssDownloader::clearSelectedRuleDownloadedEpisodeList() } } -void AutomatedRssDownloader::handleFeedCheckStateChange(QListWidgetItem *feedItem) +void AutomatedRssDownloader::handleFeedCheckStateChange(QTreeWidgetItem *feedItem, int column) { - const QString feedURL = feedItem->data(Qt::UserRole).toString(); - for (QListWidgetItem *ruleItem : asConst(m_ui->ruleList->selectedItems())) + Q_ASSERT(column == 0); + const RSS::Item *rssItem = feedItem->data(0, Qt::UserRole).value(); + if (auto *feed = qobject_cast(rssItem)) { - RSS::AutoDownloadRule rule = (ruleItem == m_currentRuleItem - ? m_currentRule - : RSS::AutoDownloader::instance()->ruleByName(ruleItem->text())); - QStringList affectedFeeds = rule.feedURLs(); - if ((feedItem->checkState() == Qt::Checked) && !affectedFeeds.contains(feedURL)) - affectedFeeds << feedURL; - else if ((feedItem->checkState() == Qt::Unchecked) && affectedFeeds.contains(feedURL)) - affectedFeeds.removeOne(feedURL); - - rule.setFeedURLs(affectedFeeds); - if (ruleItem != m_currentRuleItem) - RSS::AutoDownloader::instance()->setRule(rule); - else - m_currentRule = rule; + const QString feedURL = feed->url(); + for (QListWidgetItem *ruleItem : asConst(m_ui->ruleList->selectedItems())) + { + RSS::AutoDownloadRule rule = (ruleItem == m_currentRuleItem + ? m_currentRule + : RSS::AutoDownloader::instance()->ruleByName(ruleItem->text())); + QStringList affectedFeeds = rule.feedURLs(); + if ((feedItem->checkState(0) == Qt::Checked) && !affectedFeeds.contains(feedURL)) + affectedFeeds << feedURL; + else if ((feedItem->checkState(0) == Qt::Unchecked) && affectedFeeds.contains(feedURL)) + affectedFeeds.removeOne(feedURL); + + rule.setFeedURLs(affectedFeeds); + if (ruleItem != m_currentRuleItem) + RSS::AutoDownloader::instance()->setRule(rule); + else + m_currentRule = rule; + } } handleRuleDefinitionChanged(); diff --git a/src/gui/rss/automatedrssdownloader.h b/src/gui/rss/automatedrssdownloader.h index 7c95bcb65a3..d63ceb918c2 100644 --- a/src/gui/rss/automatedrssdownloader.h +++ b/src/gui/rss/automatedrssdownloader.h @@ -34,16 +34,20 @@ #include #include #include +#include #include "base/rss/rss_autodownloadrule.h" #include "base/settingvalue.h" class QListWidgetItem; +class QTreeWidgetItem; class QRegularExpression; namespace RSS { class Feed; + class Folder; + class Item; } namespace Ui @@ -69,7 +73,7 @@ private slots: void onImportBtnClicked(); void onRenameRuleBtnClicked(); void handleRuleCheckStateChange(QListWidgetItem *ruleItem); - void handleFeedCheckStateChange(QListWidgetItem *feedItem); + void handleFeedCheckStateChange(QTreeWidgetItem *feedItem, int column); void displayRulesListMenu(); void renameSelectedRule(); void updateRuleDefinitionBox(); @@ -95,6 +99,10 @@ private slots: void updateMatchingArticles(); void saveEditedRule(); void loadFeedList(); + QTreeWidgetItem *createFeedTreeItem(const RSS::Item *rssItem, QTreeWidgetItem *parentItem); + QListgetAllFeedTreeItems(QTreeWidgetItem *parent = nullptr) const; + void loadFeedTree(QTreeWidgetItem *parent, const RSS::Folder *rssParent); + void sortTreeRecursively(QTreeWidgetItem *item); void updateFeedList(); void addFeedArticlesToTree(RSS::Feed *feed, const QStringList &articles); diff --git a/src/gui/rss/automatedrssdownloader.ui b/src/gui/rss/automatedrssdownloader.ui index a78b26f3de3..d2aeb855fe1 100644 --- a/src/gui/rss/automatedrssdownloader.ui +++ b/src/gui/rss/automatedrssdownloader.ui @@ -356,7 +356,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also - +