qBittorrent
rsswidget.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2017 Vladimir Golovnev <[email protected]>
4  * Copyright (C) 2006 Christophe Dumez <[email protected]>
5  * Copyright (C) 2006 Arnaud Demaiziere <[email protected]>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * In addition, as a special exception, the copyright holders give permission to
22  * link this program with the OpenSSL project's "OpenSSL" library (or with
23  * modified versions of it that use the same license as the "OpenSSL" library),
24  * and distribute the linked executables. You must obey the GNU General Public
25  * License in all respects for all of the code used other than "OpenSSL". If you
26  * modify file(s), you may extend this exception to your version of the file(s),
27  * but you are not obligated to do so. If you do not wish to do so, delete this
28  * exception statement from your version.
29  */
30 
31 #include "rsswidget.h"
32 
33 #include <QClipboard>
34 #include <QDesktopServices>
35 #include <QDragMoveEvent>
36 #include <QMenu>
37 #include <QMessageBox>
38 #include <QRegularExpression>
39 #include <QShortcut>
40 #include <QString>
41 
43 #include "base/global.h"
45 #include "base/preferences.h"
46 #include "base/rss/rss_article.h"
47 #include "base/rss/rss_feed.h"
48 #include "base/rss/rss_folder.h"
49 #include "base/rss/rss_session.h"
52 #include "gui/uithememanager.h"
53 #include "articlelistwidget.h"
54 #include "automatedrssdownloader.h"
55 #include "feedlistwidget.h"
56 #include "ui_rsswidget.h"
57 
58 RSSWidget::RSSWidget(QWidget *parent)
59  : QWidget(parent)
60  , m_ui(new Ui::RSSWidget)
61 {
62  m_ui->setupUi(this);
63 
64  // Icons
65  m_ui->actionCopyFeedURL->setIcon(UIThemeManager::instance()->getIcon("edit-copy"));
66  m_ui->actionDelete->setIcon(UIThemeManager::instance()->getIcon("edit-delete"));
67  m_ui->actionDownloadTorrent->setIcon(UIThemeManager::instance()->getIcon("download"));
68  m_ui->actionMarkItemsRead->setIcon(UIThemeManager::instance()->getIcon("mail-mark-read"));
69  m_ui->actionNewFolder->setIcon(UIThemeManager::instance()->getIcon("folder-new"));
70  m_ui->actionNewSubscription->setIcon(UIThemeManager::instance()->getIcon("list-add"));
71  m_ui->actionOpenNewsURL->setIcon(UIThemeManager::instance()->getIcon("application-x-mswinurl"));
72  m_ui->actionRename->setIcon(UIThemeManager::instance()->getIcon("edit-rename"));
73  m_ui->actionUpdate->setIcon(UIThemeManager::instance()->getIcon("view-refresh"));
74  m_ui->actionUpdateAllFeeds->setIcon(UIThemeManager::instance()->getIcon("view-refresh"));
75 #ifndef Q_OS_MACOS
76  m_ui->newFeedButton->setIcon(UIThemeManager::instance()->getIcon("list-add"));
77  m_ui->markReadButton->setIcon(UIThemeManager::instance()->getIcon("mail-mark-read"));
78  m_ui->updateAllButton->setIcon(UIThemeManager::instance()->getIcon("view-refresh"));
79  m_ui->rssDownloaderBtn->setIcon(UIThemeManager::instance()->getIcon("download"));
80 #endif
81 
82  m_articleListWidget = new ArticleListWidget(m_ui->splitterMain);
83  m_ui->splitterMain->insertWidget(0, m_articleListWidget);
84  connect(m_articleListWidget, &ArticleListWidget::customContextMenuRequested, this, &RSSWidget::displayItemsListMenu);
85  connect(m_articleListWidget, &ArticleListWidget::currentItemChanged, this, &RSSWidget::handleCurrentArticleItemChanged);
86  connect(m_articleListWidget, &ArticleListWidget::itemDoubleClicked, this, &RSSWidget::downloadSelectedTorrents);
87 
88  m_feedListWidget = new FeedListWidget(m_ui->splitterSide);
89  m_ui->splitterSide->insertWidget(0, m_feedListWidget);
90  connect(m_feedListWidget, &QAbstractItemView::doubleClicked, this, &RSSWidget::renameSelectedRSSItem);
91  connect(m_feedListWidget, &QTreeWidget::currentItemChanged, this, &RSSWidget::handleCurrentFeedItemChanged);
92  connect(m_feedListWidget, &QWidget::customContextMenuRequested, this, &RSSWidget::displayRSSListMenu);
95 
96  const auto *editHotkey = new QShortcut(Qt::Key_F2, m_feedListWidget, nullptr, nullptr, Qt::WidgetShortcut);
97  connect(editHotkey, &QShortcut::activated, this, &RSSWidget::renameSelectedRSSItem);
98  const auto *deleteHotkey = new QShortcut(QKeySequence::Delete, m_feedListWidget, nullptr, nullptr, Qt::WidgetShortcut);
99  connect(deleteHotkey, &QShortcut::activated, this, &RSSWidget::deleteSelectedItems);
100 
101  // Feeds list actions
102  connect(m_ui->actionDelete, &QAction::triggered, this, &RSSWidget::deleteSelectedItems);
103  connect(m_ui->actionRename, &QAction::triggered, this, &RSSWidget::renameSelectedRSSItem);
104  connect(m_ui->actionUpdate, &QAction::triggered, this, &RSSWidget::refreshSelectedItems);
105  connect(m_ui->actionNewFolder, &QAction::triggered, this, &RSSWidget::askNewFolder);
106  connect(m_ui->actionNewSubscription, &QAction::triggered, this, &RSSWidget::on_newFeedButton_clicked);
107  connect(m_ui->actionUpdateAllFeeds, &QAction::triggered, this, &RSSWidget::refreshAllFeeds);
108  connect(m_ui->updateAllButton, &QAbstractButton::clicked, this, &RSSWidget::refreshAllFeeds);
109  connect(m_ui->actionCopyFeedURL, &QAction::triggered, this, &RSSWidget::copySelectedFeedsURL);
110  connect(m_ui->actionMarkItemsRead, &QAction::triggered, this, &RSSWidget::on_markReadButton_clicked);
111 
112  // News list actions
113  connect(m_ui->actionOpenNewsURL, &QAction::triggered, this, &RSSWidget::openSelectedArticlesUrls);
114  connect(m_ui->actionDownloadTorrent, &QAction::triggered, this, &RSSWidget::downloadSelectedTorrents);
115 
116  // Restore sliders position
118  // Bind saveSliders slots
119  connect(m_ui->splitterMain, &QSplitter::splitterMoved, this, &RSSWidget::saveSlidersPosition);
120  connect(m_ui->splitterSide, &QSplitter::splitterMoved, this, &RSSWidget::saveSlidersPosition);
121 
122  if (RSS::Session::instance()->isProcessingEnabled())
123  m_ui->labelWarn->hide();
128 }
129 
131 {
132  // we need it here to properly mark latest article
133  // as read without having additional code
134  m_articleListWidget->clear();
135 
137 
138  delete m_feedListWidget;
139  delete m_ui;
140 }
141 
142 // display a right-click menu
143 void RSSWidget::displayRSSListMenu(const QPoint &pos)
144 {
145  if (!m_feedListWidget->indexAt(pos).isValid())
146  // No item under the mouse, clear selection
147  m_feedListWidget->clearSelection();
148 
149  QMenu *menu = new QMenu(this);
150  menu->setAttribute(Qt::WA_DeleteOnClose);
151 
152  const QList<QTreeWidgetItem *> selectedItems = m_feedListWidget->selectedItems();
153  if (!selectedItems.isEmpty())
154  {
155  menu->addAction(m_ui->actionUpdate);
156  menu->addAction(m_ui->actionMarkItemsRead);
157  menu->addSeparator();
158 
159  if (selectedItems.size() == 1)
160  {
161  if (selectedItems.first() != m_feedListWidget->stickyUnreadItem())
162  {
163  menu->addAction(m_ui->actionRename);
164  menu->addAction(m_ui->actionDelete);
165  menu->addSeparator();
166  if (m_feedListWidget->isFolder(selectedItems.first()))
167  menu->addAction(m_ui->actionNewFolder);
168  }
169  }
170  else
171  {
172  menu->addAction(m_ui->actionDelete);
173  menu->addSeparator();
174  }
175 
176  menu->addAction(m_ui->actionNewSubscription);
177 
178  if (m_feedListWidget->isFeed(selectedItems.first()))
179  {
180  menu->addSeparator();
181  menu->addAction(m_ui->actionCopyFeedURL);
182  }
183  }
184  else
185  {
186  menu->addAction(m_ui->actionNewSubscription);
187  menu->addAction(m_ui->actionNewFolder);
188  menu->addSeparator();
189  menu->addAction(m_ui->actionUpdateAllFeeds);
190  }
191 
192  menu->popup(QCursor::pos());
193 }
194 
196 {
197  bool hasTorrent = false;
198  bool hasLink = false;
199  for (const QListWidgetItem *item : asConst(m_articleListWidget->selectedItems()))
200  {
201  auto article = reinterpret_cast<RSS::Article *>(item->data(Qt::UserRole).value<quintptr>());
202  Q_ASSERT(article);
203 
204  if (!article->torrentUrl().isEmpty())
205  hasTorrent = true;
206  if (!article->link().isEmpty())
207  hasLink = true;
208  if (hasTorrent && hasLink)
209  break;
210  }
211 
212  QMenu *myItemListMenu = new QMenu(this);
213  myItemListMenu->setAttribute(Qt::WA_DeleteOnClose);
214 
215  if (hasTorrent)
216  myItemListMenu->addAction(m_ui->actionDownloadTorrent);
217  if (hasLink)
218  myItemListMenu->addAction(m_ui->actionOpenNewsURL);
219 
220  if (!myItemListMenu->isEmpty())
221  myItemListMenu->popup(QCursor::pos());
222 }
223 
225 {
226  bool ok = false;
227  QString newName = AutoExpandableDialog::getText(
228  this, tr("Please choose a folder name"), tr("Folder name:"), QLineEdit::Normal
229  , tr("New folder"), &ok);
230  if (!ok) return;
231 
232  newName = newName.trimmed();
233  if (newName.isEmpty()) return;
234 
235  // Determine destination folder for new item
236  QTreeWidgetItem *destItem = nullptr;
237  QList<QTreeWidgetItem *> selectedItems = m_feedListWidget->selectedItems();
238  if (!selectedItems.empty())
239  {
240  destItem = selectedItems.first();
241  if (!m_feedListWidget->isFolder(destItem))
242  destItem = destItem->parent();
243  }
244  // Consider the case where the user clicked on Unread item
245  RSS::Folder *rssDestFolder = ((!destItem || (destItem == m_feedListWidget->stickyUnreadItem()))
247  : qobject_cast<RSS::Folder *>(m_feedListWidget->getRSSItem(destItem)));
248 
249  const QString newFolderPath = RSS::Item::joinPath(rssDestFolder->path(), newName);
250  const nonstd::expected<void, QString> result = RSS::Session::instance()->addFolder(newFolderPath);
251  if (!result)
252  QMessageBox::warning(this, "qBittorrent", result.error(), QMessageBox::Ok);
253 
254  // Expand destination folder to display new feed
255  if (destItem && (destItem != m_feedListWidget->stickyUnreadItem()))
256  destItem->setExpanded(true);
257  // As new RSS items are added synchronously, we can do the following here.
259 }
260 
261 // add a stream by a button
263 {
264  // Ask for feed URL
265  const QString clipText = qApp->clipboard()->text();
266  const QString defaultURL = Net::DownloadManager::hasSupportedScheme(clipText) ? clipText : "http://";
267 
268  bool ok = false;
269  QString newURL = AutoExpandableDialog::getText(
270  this, tr("Please type a RSS feed URL"), tr("Feed URL:"), QLineEdit::Normal, defaultURL, &ok);
271  if (!ok) return;
272 
273  newURL = newURL.trimmed();
274  if (newURL.isEmpty()) return;
275 
276  // Determine destination folder for new item
277  QTreeWidgetItem *destItem = nullptr;
278  QList<QTreeWidgetItem *> selectedItems = m_feedListWidget->selectedItems();
279  if (!selectedItems.empty())
280  {
281  destItem = selectedItems.first();
282  if (!m_feedListWidget->isFolder(destItem))
283  destItem = destItem->parent();
284  }
285  // Consider the case where the user clicked on Unread item
286  RSS::Folder *rssDestFolder = ((!destItem || (destItem == m_feedListWidget->stickyUnreadItem()))
288  : qobject_cast<RSS::Folder *>(m_feedListWidget->getRSSItem(destItem)));
289 
290  // NOTE: We still add feed using legacy way (with URL as feed name)
291  const QString newFeedPath = RSS::Item::joinPath(rssDestFolder->path(), newURL);
292  const nonstd::expected<void, QString> result = RSS::Session::instance()->addFeed(newURL, newFeedPath);
293  if (!result)
294  QMessageBox::warning(this, "qBittorrent", result.error(), QMessageBox::Ok);
295 
296  // Expand destination folder to display new feed
297  if (destItem && (destItem != m_feedListWidget->stickyUnreadItem()))
298  destItem->setExpanded(true);
299  // As new RSS items are added synchronously, we can do the following here.
301 }
302 
304 {
305  const QList<QTreeWidgetItem *> selectedItems = m_feedListWidget->selectedItems();
306  if (selectedItems.isEmpty())
307  return;
308  if ((selectedItems.size() == 1) && (selectedItems.first() == m_feedListWidget->stickyUnreadItem()))
309  return;
310 
311  QMessageBox::StandardButton answer = QMessageBox::question(
312  this, tr("Deletion confirmation"), tr("Are you sure you want to delete the selected RSS feeds?")
313  , QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
314  if (answer == QMessageBox::No)
315  return;
316 
317  for (QTreeWidgetItem *item : selectedItems)
318  if (item != m_feedListWidget->stickyUnreadItem())
320 }
321 
323 {
324  const QStringList openedFolders = Preferences::instance()->getRssOpenFolders();
325  for (const QString &varPath : openedFolders)
326  {
327  QTreeWidgetItem *parent = nullptr;
328  for (const QString &name : asConst(varPath.split('\\')))
329  {
330  int nbChildren = (parent ? parent->childCount() : m_feedListWidget->topLevelItemCount());
331  for (int i = 0; i < nbChildren; ++i)
332  {
333  QTreeWidgetItem *child = (parent ? parent->child(i) : m_feedListWidget->topLevelItem(i));
334  if (m_feedListWidget->getRSSItem(child)->name() == name)
335  {
336  parent = child;
337  parent->setExpanded(true);
338  break;
339  }
340  }
341  }
342  }
343 }
344 
346 {
347  QStringList openedFolders;
348  for (QTreeWidgetItem *item : asConst(m_feedListWidget->getAllOpenedFolders()))
349  openedFolders << m_feedListWidget->itemPath(item);
350  Preferences::instance()->setRssOpenFolders(openedFolders);
351 }
352 
354 {
356 }
357 
359 {
360  for (QListWidgetItem *item : asConst(m_articleListWidget->selectedItems()))
361  {
362  auto article = reinterpret_cast<RSS::Article *>(item->data(Qt::UserRole).value<quintptr>());
363  Q_ASSERT(article);
364 
365  // Mark as read
366  article->markAsRead();
367 
368  if (!article->torrentUrl().isEmpty())
369  {
371  AddNewTorrentDialog::show(article->torrentUrl(), window());
372  else
373  BitTorrent::Session::instance()->addTorrent(article->torrentUrl());
374  }
375  }
376 }
377 
378 // open the url of the selected RSS articles in the Web browser
380 {
381  for (QListWidgetItem *item : asConst(m_articleListWidget->selectedItems()))
382  {
383  auto article = reinterpret_cast<RSS::Article *>(item->data(Qt::UserRole).value<quintptr>());
384  Q_ASSERT(article);
385 
386  // Mark as read
387  article->markAsRead();
388 
389  if (!article->link().isEmpty())
390  QDesktopServices::openUrl(QUrl(article->link()));
391  }
392 }
393 
395 {
396  QList<QTreeWidgetItem *> selectedItems = m_feedListWidget->selectedItems();
397  if (selectedItems.size() != 1) return;
398 
399  QTreeWidgetItem *item = selectedItems.first();
400  if (item == m_feedListWidget->stickyUnreadItem())
401  return;
402 
403  RSS::Item *rssItem = m_feedListWidget->getRSSItem(item);
404  const QString parentPath = RSS::Item::parentPath(rssItem->path());
405  bool ok = false;
406  do
407  {
408  QString newName = AutoExpandableDialog::getText(
409  this, tr("Please choose a new name for this RSS feed"), tr("New feed name:")
410  , QLineEdit::Normal, rssItem->name(), &ok);
411  // Check if name is already taken
412  if (!ok) return;
413 
414  const nonstd::expected<void, QString> result = RSS::Session::instance()->moveItem(rssItem, RSS::Item::joinPath(parentPath, newName));
415  if (!result)
416  {
417  QMessageBox::warning(nullptr, tr("Rename failed"), result.error());
418  ok = false;
419  }
420  } while (!ok);
421 }
422 
424 {
425  for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems()))
426  {
427  if (item == m_feedListWidget->stickyUnreadItem())
428  {
429  refreshAllFeeds();
430  return;
431  }
432 
434  }
435 }
436 
438 {
439  QStringList URLs;
440  for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems()))
441  {
442  if (auto feed = qobject_cast<RSS::Feed *>(m_feedListWidget->getRSSItem(item)))
443  URLs << feed->url();
444  }
445  qApp->clipboard()->setText(URLs.join('\n'));
446 }
447 
448 void RSSWidget::handleCurrentFeedItemChanged(QTreeWidgetItem *currentItem)
449 {
451  , (currentItem == m_feedListWidget->stickyUnreadItem()));
452 }
453 
455 {
456  for (QTreeWidgetItem *item : asConst(m_feedListWidget->selectedItems()))
457  {
459  if (item == m_feedListWidget->stickyUnreadItem())
460  break; // all items was read
461  }
462 }
463 
464 // display a news
465 void RSSWidget::handleCurrentArticleItemChanged(QListWidgetItem *currentItem, QListWidgetItem *previousItem)
466 {
467  m_ui->textBrowser->clear();
468 
469  if (previousItem)
470  {
471  auto article = m_articleListWidget->getRSSArticle(previousItem);
472  Q_ASSERT(article);
473  article->markAsRead();
474  }
475 
476  if (!currentItem) return;
477 
478  auto article = m_articleListWidget->getRSSArticle(currentItem);
479  Q_ASSERT(article);
480 
481  const QString highlightedBaseColor = m_ui->textBrowser->palette().color(QPalette::Highlight).name();
482  const QString highlightedBaseTextColor = m_ui->textBrowser->palette().color(QPalette::HighlightedText).name();
483  const QString alternateBaseColor = m_ui->textBrowser->palette().color(QPalette::AlternateBase).name();
484 
485  QString html =
486  QString::fromLatin1("<div style='border: 2px solid red; margin-left: 5px; margin-right: 5px; margin-bottom: 5px;'>") +
487  QString::fromLatin1("<div style='background-color: \"%1\"; font-weight: bold; color: \"%2\";'>%3</div>").arg(highlightedBaseColor, highlightedBaseTextColor, article->title());
488  if (article->date().isValid())
489  html += QString::fromLatin1("<div style='background-color: \"%1\";'><b>%2</b>%3</div>").arg(alternateBaseColor, tr("Date: "), QLocale::system().toString(article->date().toLocalTime()));
490  if (!article->author().isEmpty())
491  html += QString::fromLatin1("<div style='background-color: \"%1\";'><b>%2</b>%3</div>").arg(alternateBaseColor, tr("Author: "), article->author());
492  html += "</div>"
493  "<div style='margin-left: 5px; margin-right: 5px;'>";
494  if (Qt::mightBeRichText(article->description()))
495  {
496  html += article->description();
497  }
498  else
499  {
500  QString description = article->description();
501  QRegularExpression rx;
502  // If description is plain text, replace BBCode tags with HTML and wrap everything in <pre></pre> so it looks nice
503  rx.setPatternOptions(QRegularExpression::InvertedGreedinessOption
504  | QRegularExpression::CaseInsensitiveOption);
505 
506  rx.setPattern("\\[img\\](.+)\\[/img\\]");
507  description = description.replace(rx, "<img src=\"\\1\">");
508 
509  rx.setPattern("\\[url=(\")?(.+)\\1\\]");
510  description = description.replace(rx, "<a href=\"\\2\">");
511  description = description.replace("[/url]", "</a>", Qt::CaseInsensitive);
512 
513  rx.setPattern("\\[(/)?([bius])\\]");
514  description = description.replace(rx, "<\\1\\2>");
515 
516  rx.setPattern("\\[color=(\")?(.+)\\1\\]");
517  description = description.replace(rx, "<span style=\"color:\\2\">");
518  description = description.replace("[/color]", "</span>", Qt::CaseInsensitive);
519 
520  rx.setPattern("\\[size=(\")?(.+)\\d\\1\\]");
521  description = description.replace(rx, "<span style=\"font-size:\\2px\">");
522  description = description.replace("[/size]", "</span>", Qt::CaseInsensitive);
523 
524  html += "<pre>" + description + "</pre>";
525  }
526  html += "</div>";
527  m_ui->textBrowser->setHtml(html);
528 }
529 
531 {
532  // Remember sliders positions
533  Preferences *const pref = Preferences::instance();
534  pref->setRssSideSplitterState(m_ui->splitterSide->saveState());
535  pref->setRssMainSplitterState(m_ui->splitterMain->saveState());
536 }
537 
539 {
540  const Preferences *const pref = Preferences::instance();
541  const QByteArray stateSide = pref->getRssSideSplitterState();
542  if (!stateSide.isEmpty())
543  m_ui->splitterSide->restoreState(stateSide);
544  const QByteArray stateMain = pref->getRssMainSplitterState();
545  if (!stateMain.isEmpty())
546  m_ui->splitterMain->restoreState(stateMain);
547 }
548 
550 {
552 }
553 
555 {
556  auto *downloader = new AutomatedRssDownloader(this);
557  downloader->setAttribute(Qt::WA_DeleteOnClose);
558  downloader->open();
559 }
560 
562 {
563  m_ui->labelWarn->setVisible(!enabled);
564 }
565 
567 {
568  emit unreadCountUpdated(RSS::Session::instance()->rootFolder()->unreadCount());
569 }
static void show(const QString &source, const BitTorrent::AddTorrentParams &inParams, QWidget *parent)
void setRSSItem(RSS::Item *rssItem, bool unreadOnly=false)
RSS::Article * getRSSArticle(QListWidgetItem *item) const
static QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode=QLineEdit::Normal, const QString &text={}, bool *ok=nullptr, bool excludeExtension=false, Qt::InputMethodHints inputMethodHints=Qt::ImhNone)
static Session * instance()
Definition: session.cpp:997
bool addTorrent(const QString &source, const AddTorrentParams &params=AddTorrentParams())
Definition: session.cpp:2007
QString itemPath(QTreeWidgetItem *item) const
QTreeWidgetItem * stickyUnreadItem() const
bool isFolder(QTreeWidgetItem *item) const
RSS::Item * getRSSItem(QTreeWidgetItem *item) const
QTreeWidgetItem * mapRSSItem(RSS::Item *rssItem) const
bool isFeed(QTreeWidgetItem *item) const
QList< QTreeWidgetItem * > getAllOpenedFolders(QTreeWidgetItem *parent=nullptr) const
static bool hasSupportedScheme(const QString &url)
static Preferences * instance()
void setRssMainSplitterState(const QByteArray &state)
QByteArray getRssMainSplitterState() const
QByteArray getRssSideSplitterState() const
void setRssOpenFolders(const QStringList &folders)
void setRssSideSplitterState(const QByteArray &state)
QStringList getRssOpenFolders() const
Definition: rss_article.h:43
static QString joinPath(const QString &path1, const QString &path2)
Definition: rss_item.cpp:82
virtual void markAsRead()=0
static QString parentPath(const QString &path)
Definition: rss_item.cpp:108
void unreadCountChanged(Item *item=nullptr)
QString name() const
Definition: rss_item.cpp:62
virtual void refresh()=0
QString path() const
Definition: rss_item.cpp:57
static Session * instance()
nonstd::expected< void, QString > addFolder(const QString &path)
Folder * rootFolder() const
nonstd::expected< void, QString > addFeed(const QString &url, const QString &path)
void setRefreshInterval(int refreshInterval)
Item * itemByPath(const QString &path) const
nonstd::expected< void, QString > moveItem(const QString &itemPath, const QString &destPath)
void processingStateChanged(bool enabled)
nonstd::expected< void, QString > removeItem(const QString &itemPath)
void refreshAllFeeds()
Definition: rsswidget.cpp:353
void saveFoldersOpenState()
Definition: rsswidget.cpp:345
FeedListWidget * m_feedListWidget
Definition: rsswidget.h:86
void displayRSSListMenu(const QPoint &)
Definition: rsswidget.cpp:143
void on_newFeedButton_clicked()
Definition: rsswidget.cpp:262
ArticleListWidget * m_articleListWidget
Definition: rsswidget.h:85
void unreadCountUpdated(int count)
void saveSlidersPosition()
Definition: rsswidget.cpp:530
void askNewFolder()
Definition: rsswidget.cpp:224
void handleCurrentArticleItemChanged(QListWidgetItem *currentItem, QListWidgetItem *previousItem)
Definition: rsswidget.cpp:465
void copySelectedFeedsURL()
Definition: rsswidget.cpp:437
void openSelectedArticlesUrls()
Definition: rsswidget.cpp:379
RSSWidget(QWidget *parent)
Definition: rsswidget.cpp:58
void displayItemsListMenu(const QPoint &)
Definition: rsswidget.cpp:195
void refreshSelectedItems()
Definition: rsswidget.cpp:423
void handleCurrentFeedItemChanged(QTreeWidgetItem *currentItem)
Definition: rsswidget.cpp:448
void on_markReadButton_clicked()
Definition: rsswidget.cpp:454
void updateRefreshInterval(int val) const
Definition: rsswidget.cpp:549
void handleUnreadCountChanged()
Definition: rsswidget.cpp:566
void renameSelectedRSSItem()
Definition: rsswidget.cpp:394
void restoreSlidersPosition()
Definition: rsswidget.cpp:538
void on_rssDownloaderBtn_clicked()
Definition: rsswidget.cpp:554
void loadFoldersOpenState()
Definition: rsswidget.cpp:322
void deleteSelectedItems()
Definition: rsswidget.cpp:303
void handleSessionProcessingStateChanged(bool enabled)
Definition: rsswidget.cpp:561
void downloadSelectedTorrents()
Definition: rsswidget.cpp:358
Ui::RSSWidget * m_ui
Definition: rsswidget.h:84
static UIThemeManager * instance()
constexpr std::add_const_t< T > & asConst(T &t) noexcept
Definition: global.h:42
QString toString(const lt::socket_type_t socketType)
Definition: session.cpp:183