34 #include <QDesktopServices>
35 #include <QDragMoveEvent>
37 #include <QMessageBox>
38 #include <QRegularExpression>
56 #include "ui_rsswidget.h"
96 const auto *editHotkey =
new QShortcut(Qt::Key_F2,
m_feedListWidget,
nullptr,
nullptr, Qt::WidgetShortcut);
98 const auto *deleteHotkey =
new QShortcut(QKeySequence::Delete,
m_feedListWidget,
nullptr,
nullptr, Qt::WidgetShortcut);
123 m_ui->labelWarn->hide();
149 QMenu *menu =
new QMenu(
this);
150 menu->setAttribute(Qt::WA_DeleteOnClose);
152 const QList<QTreeWidgetItem *> selectedItems =
m_feedListWidget->selectedItems();
153 if (!selectedItems.isEmpty())
155 menu->addAction(
m_ui->actionUpdate);
156 menu->addAction(
m_ui->actionMarkItemsRead);
157 menu->addSeparator();
159 if (selectedItems.size() == 1)
163 menu->addAction(
m_ui->actionRename);
164 menu->addAction(
m_ui->actionDelete);
165 menu->addSeparator();
167 menu->addAction(
m_ui->actionNewFolder);
172 menu->addAction(
m_ui->actionDelete);
173 menu->addSeparator();
176 menu->addAction(
m_ui->actionNewSubscription);
180 menu->addSeparator();
181 menu->addAction(
m_ui->actionCopyFeedURL);
186 menu->addAction(
m_ui->actionNewSubscription);
187 menu->addAction(
m_ui->actionNewFolder);
188 menu->addSeparator();
189 menu->addAction(
m_ui->actionUpdateAllFeeds);
192 menu->popup(QCursor::pos());
197 bool hasTorrent =
false;
198 bool hasLink =
false;
201 auto article =
reinterpret_cast<RSS::Article *
>(item->data(Qt::UserRole).value<quintptr>());
204 if (!article->torrentUrl().isEmpty())
206 if (!article->link().isEmpty())
208 if (hasTorrent && hasLink)
212 QMenu *myItemListMenu =
new QMenu(
this);
213 myItemListMenu->setAttribute(Qt::WA_DeleteOnClose);
216 myItemListMenu->addAction(
m_ui->actionDownloadTorrent);
218 myItemListMenu->addAction(
m_ui->actionOpenNewsURL);
220 if (!myItemListMenu->isEmpty())
221 myItemListMenu->popup(QCursor::pos());
228 this, tr(
"Please choose a folder name"), tr(
"Folder name:"), QLineEdit::Normal
229 , tr(
"New folder"), &ok);
232 newName = newName.trimmed();
233 if (newName.isEmpty())
return;
236 QTreeWidgetItem *destItem =
nullptr;
238 if (!selectedItems.empty())
240 destItem = selectedItems.first();
242 destItem = destItem->parent();
252 QMessageBox::warning(
this,
"qBittorrent", result.error(), QMessageBox::Ok);
256 destItem->setExpanded(
true);
265 const QString clipText = qApp->clipboard()->text();
270 this, tr(
"Please type a RSS feed URL"), tr(
"Feed URL:"), QLineEdit::Normal, defaultURL, &ok);
273 newURL = newURL.trimmed();
274 if (newURL.isEmpty())
return;
277 QTreeWidgetItem *destItem =
nullptr;
279 if (!selectedItems.empty())
281 destItem = selectedItems.first();
283 destItem = destItem->parent();
294 QMessageBox::warning(
this,
"qBittorrent", result.error(), QMessageBox::Ok);
298 destItem->setExpanded(
true);
305 const QList<QTreeWidgetItem *> selectedItems =
m_feedListWidget->selectedItems();
306 if (selectedItems.isEmpty())
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)
317 for (QTreeWidgetItem *item : selectedItems)
325 for (
const QString &varPath : openedFolders)
327 QTreeWidgetItem *parent =
nullptr;
328 for (
const QString &name :
asConst(varPath.split(
'\\')))
330 int nbChildren = (parent ? parent->childCount() :
m_feedListWidget->topLevelItemCount());
331 for (
int i = 0; i < nbChildren; ++i)
333 QTreeWidgetItem *child = (parent ? parent->child(i) :
m_feedListWidget->topLevelItem(i));
337 parent->setExpanded(
true);
347 QStringList openedFolders;
362 auto article =
reinterpret_cast<RSS::Article *
>(item->data(Qt::UserRole).value<quintptr>());
366 article->markAsRead();
368 if (!article->torrentUrl().isEmpty())
383 auto article =
reinterpret_cast<RSS::Article *
>(item->data(Qt::UserRole).value<quintptr>());
387 article->markAsRead();
389 if (!article->link().isEmpty())
390 QDesktopServices::openUrl(QUrl(article->link()));
397 if (selectedItems.size() != 1)
return;
399 QTreeWidgetItem *item = selectedItems.first();
409 this, tr(
"Please choose a new name for this RSS feed"), tr(
"New feed name:")
410 , QLineEdit::Normal, rssItem->
name(), &ok);
417 QMessageBox::warning(
nullptr, tr(
"Rename failed"), result.error());
445 qApp->clipboard()->setText(URLs.join(
'\n'));
467 m_ui->textBrowser->clear();
473 article->markAsRead();
476 if (!currentItem)
return;
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();
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());
493 "<div style='margin-left: 5px; margin-right: 5px;'>";
494 if (Qt::mightBeRichText(article->description()))
496 html += article->description();
500 QString description = article->description();
501 QRegularExpression rx;
503 rx.setPatternOptions(QRegularExpression::InvertedGreedinessOption
504 | QRegularExpression::CaseInsensitiveOption);
506 rx.setPattern(
"\\[img\\](.+)\\[/img\\]");
507 description = description.replace(rx,
"<img src=\"\\1\">");
509 rx.setPattern(
"\\[url=(\")?(.+)\\1\\]");
510 description = description.replace(rx,
"<a href=\"\\2\">");
511 description = description.replace(
"[/url]",
"</a>", Qt::CaseInsensitive);
513 rx.setPattern(
"\\[(/)?([bius])\\]");
514 description = description.replace(rx,
"<\\1\\2>");
516 rx.setPattern(
"\\[color=(\")?(.+)\\1\\]");
517 description = description.replace(rx,
"<span style=\"color:\\2\">");
518 description = description.replace(
"[/color]",
"</span>", Qt::CaseInsensitive);
520 rx.setPattern(
"\\[size=(\")?(.+)\\d\\1\\]");
521 description = description.replace(rx,
"<span style=\"font-size:\\2px\">");
522 description = description.replace(
"[/size]",
"</span>", Qt::CaseInsensitive);
524 html +=
"<pre>" + description +
"</pre>";
527 m_ui->textBrowser->setHtml(html);
542 if (!stateSide.isEmpty())
543 m_ui->splitterSide->restoreState(stateSide);
545 if (!stateMain.isEmpty())
546 m_ui->splitterMain->restoreState(stateMain);
557 downloader->setAttribute(Qt::WA_DeleteOnClose);
563 m_ui->labelWarn->setVisible(!enabled);
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()
bool addTorrent(const QString &source, const AddTorrentParams ¶ms=AddTorrentParams())
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
static QString joinPath(const QString &path1, const QString &path2)
virtual void markAsRead()=0
static QString parentPath(const QString &path)
void unreadCountChanged(Item *item=nullptr)
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)
static UIThemeManager * instance()
constexpr std::add_const_t< T > & asConst(T &t) noexcept
QString toString(const lt::socket_type_t socketType)