33 #include <QListWidgetItem>
36 #include <QScrollArea>
37 #include <QStyleOptionButton>
39 #include <QVBoxLayout>
68 const QUrl url {tracker};
69 QString scheme = url.scheme();
78 const QUrl url {tracker};
79 const QString host {url.host()};
82 if (!QHostAddress(host).isNull())
85 return host.section(
'.', -2, -1);
91 using QCheckBox::QCheckBox;
96 QPainter painter(
this);
98 QStyleOptionViewItem indicatorOption;
99 indicatorOption.initFrom(
this);
100 indicatorOption.rect = style()->subElementRect(QStyle::SE_CheckBoxIndicator, &indicatorOption,
this);
101 indicatorOption.state |= (QStyle::State_Children
102 | (isChecked() ? QStyle::State_Open : QStyle::State_None));
103 style()->drawPrimitive(QStyle::PE_IndicatorBranch, &indicatorOption, &painter,
this);
105 QStyleOptionButton labelOption;
106 initStyleOption(&labelOption);
107 labelOption.rect = style()->subElementRect(QStyle::SE_CheckBoxContents, &labelOption,
this);
108 style()->drawControl(QStyle::CE_CheckBoxLabel, &labelOption, &painter,
this);
116 : QListWidget(parent)
117 , transferList(transferList)
119 setFrameShape(QFrame::NoFrame);
120 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
121 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
122 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
123 setUniformItemSizes(
true);
128 #if defined(Q_OS_MACOS)
129 setAttribute(Qt::WA_MacShowFocusRect,
false);
132 setContextMenuPolicy(Qt::CustomContextMenu);
147 sizeHintForColumn(0),
149 static_cast<int>((sizeHintForRow(0) + 2 * spacing()) * (count() + 0.5)),
180 auto *all =
new QListWidgetItem(
this);
181 all->setData(Qt::DisplayRole, tr(
"All (0)",
"this is for the status filter"));
183 auto *downloading =
new QListWidgetItem(
this);
184 downloading->setData(Qt::DisplayRole, tr(
"Downloading (0)"));
186 auto *seeding =
new QListWidgetItem(
this);
187 seeding->setData(Qt::DisplayRole, tr(
"Seeding (0)"));
189 auto *completed =
new QListWidgetItem(
this);
190 completed->setData(Qt::DisplayRole, tr(
"Completed (0)"));
192 auto *resumed =
new QListWidgetItem(
this);
193 resumed->setData(Qt::DisplayRole, tr(
"Resumed (0)"));
195 auto *paused =
new QListWidgetItem(
this);
196 paused->setData(Qt::DisplayRole, tr(
"Paused (0)"));
198 auto *active =
new QListWidgetItem(
this);
199 active->setData(Qt::DisplayRole, tr(
"Active (0)"));
201 auto *inactive =
new QListWidgetItem(
this);
202 inactive->setData(Qt::DisplayRole, tr(
"Inactive (0)"));
204 auto *stalled =
new QListWidgetItem(
this);
205 stalled->setData(Qt::DisplayRole, tr(
"Stalled (0)"));
207 auto *stalledUploading =
new QListWidgetItem(
this);
208 stalledUploading->setData(Qt::DisplayRole, tr(
"Stalled Uploading (0)"));
210 auto *stalledDownloading =
new QListWidgetItem(
this);
211 stalledDownloading->setData(Qt::DisplayRole, tr(
"Stalled Downloading (0)"));
213 auto *checking =
new QListWidgetItem(
this);
214 checking->setData(Qt::DisplayRole, tr(
"Checking (0)"));
216 auto *errored =
new QListWidgetItem(
this);
217 errored->setData(Qt::DisplayRole, tr(
"Errored (0)"));
232 int nbDownloading = 0;
240 int nbStalledUploading = 0;
241 int nbStalledDownloading = 0;
248 if (torrent->isDownloading())
250 if (torrent->isUploading())
252 if (torrent->isCompleted())
254 if (torrent->isResumed())
256 if (torrent->isPaused())
258 if (torrent->isActive())
260 if (torrent->isInactive())
263 ++nbStalledUploading;
265 ++nbStalledDownloading;
266 if (torrent->isChecking())
268 if (torrent->isErrored())
272 nbStalled = nbStalledUploading + nbStalledDownloading;
274 item(
TorrentFilter::All)->setData(Qt::DisplayRole, tr(
"All (%1)").arg(torrents.count()));
303 , m_downloadTrackerFavicon(downloadFavicon)
305 auto *allTrackers =
new QListWidgetItem(
this);
306 allTrackers->setData(Qt::DisplayRole, tr(
"All (0)",
"this is for the tracker filter"));
308 auto *noTracker =
new QListWidgetItem(
this);
309 noTracker->setData(Qt::DisplayRole, tr(
"Trackerless (0)"));
311 auto *errorTracker =
new QListWidgetItem(
this);
312 errorTracker->setData(Qt::DisplayRole, tr(
"Error (0)"));
313 errorTracker->setData(Qt::DecorationRole, style()->standardIcon(QStyle::SP_MessageBoxCritical));
314 auto *warningTracker =
new QListWidgetItem(
this);
315 warningTracker->setData(Qt::DisplayRole, tr(
"Warning (0)"));
316 warningTracker->setData(Qt::DecorationRole, style()->standardIcon(QStyle::SP_MessageBoxWarning));
319 setCurrentRow(0, QItemSelectionModel::SelectCurrent);
331 const QString host {
getHost(tracker)};
332 const bool exists {
m_trackers.contains(host)};
333 QListWidgetItem *trackerItem {
nullptr};
346 trackerItem =
new QListWidgetItem();
349 const QString scheme =
getScheme(tracker);
350 downloadFavicon(QString::fromLatin1(
"%1://%2/favicon.ico").arg((scheme.startsWith(
"http") ? scheme :
"http"), host));
352 if (!trackerItem)
return;
354 QSet<BitTorrent::TorrentID> &torrentIDs {
m_trackers[host]};
355 torrentIDs.insert(
id);
359 trackerItem->setText(tr(
"Trackerless (%1)").arg(torrentIDs.size()));
365 trackerItem->setText(QString::fromLatin1(
"%1 (%2)").arg(host, QString::number(torrentIDs.size())));
373 Q_ASSERT(count() >= 4);
375 int insPos = count();
376 for (
int i = 4; i < count(); ++i)
378 if (naturalLessThan(host, item(i)->text()))
384 QListWidget::insertItem(insPos, trackerItem);
390 const QString host =
getHost(tracker);
391 QSet<BitTorrent::TorrentID> torrentIDs =
m_trackers.value(host);
393 if (torrentIDs.empty())
395 torrentIDs.remove(
id);
398 QListWidgetItem *trackerItem =
nullptr;
405 trackerItem = item(row);
407 if (torrentIDs.empty())
409 if (currentRow() == row)
410 setCurrentRow(0, QItemSelectionModel::SelectCurrent);
418 trackerItem->setText(QString::fromLatin1(
"%1 (%2)").arg(host, QString::number(torrentIDs.size())));
424 trackerItem->setText(tr(
"Trackerless (%1)").arg(torrentIDs.size()));
429 if (currentRow() == row)
450 const QString &tracker = i.key();
451 if (!tracker.isEmpty())
453 const QString scheme =
getScheme(tracker);
455 .arg((scheme.startsWith(
"http") ? scheme :
"http"),
getHost(tracker)));
463 const auto errorHashesIter =
m_errors.find(
id);
464 if (errorHashesIter !=
m_errors.end())
466 QSet<QString> &errored = *errorHashesIter;
467 errored.remove(tracker);
477 const auto warningHashesIter =
m_warnings.find(
id);
480 QSet<QString> &warned = *warningHashesIter;
481 warned.remove(tracker);
494 QSet<QString> &trackers {
m_errors[id]};
495 if (trackers.contains(tracker))
498 trackers.insert(tracker);
507 if (trackers.contains(tracker))
510 trackers.insert(tracker);
528 if (result.
url.endsWith(
".ico", Qt::CaseInsensitive))
542 if (!trackerItem)
return;
546 QList<QSize> sizes = icon.availableSizes();
547 bool invalid = (sizes.isEmpty() || icon.pixmap(sizes.first()).isNull());
550 if (result.
url.endsWith(
".ico", Qt::CaseInsensitive))
556 trackerItem->setData(Qt::DecorationRole, QIcon(result.
filePath));
563 QMenu *menu =
new QMenu(
this);
564 menu->setAttribute(Qt::WA_DeleteOnClose);
573 menu->popup(QCursor::pos());
580 else if (isVisible())
587 const QVector<BitTorrent::TrackerEntry> trackers {torrent->
trackers()};
589 addItem(tracker.url, torrentID);
592 if (trackers.isEmpty())
601 const QVector<BitTorrent::TrackerEntry> trackers {torrent->
trackers()};
606 if (trackers.isEmpty())
615 const QString tracker = item(row)->text();
616 QStringList parts = tracker.split(
' ');
617 Q_ASSERT(parts.size() >= 2);
619 return parts.join(
' ');
624 Q_ASSERT(!tracker.isEmpty());
625 for (
int i = 4; i < count(); ++i)
650 , m_transferList(transferList)
655 auto *vLayout =
new QVBoxLayout(
this);
656 auto *scroll =
new QScrollArea(
this);
657 QFrame *frame =
new QFrame(scroll);
658 auto *frameLayout =
new QVBoxLayout(frame);
661 font.setCapitalization(QFont::AllUppercase);
663 scroll->setWidgetResizable(
true);
664 scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
666 setStyleSheet(
"QFrame {background: transparent;}");
667 scroll->setStyleSheet(
"QFrame {border: none;}");
668 vLayout->setContentsMargins(0, 0, 0, 0);
669 frameLayout->setContentsMargins(0, 2, 0, 0);
670 frameLayout->setSpacing(2);
671 frameLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop);
673 frame->setLayout(frameLayout);
674 scroll->setWidget(frame);
675 vLayout->addWidget(scroll);
678 QCheckBox *statusLabel =
new ArrowCheckBox(tr(
"Status"),
this);
680 statusLabel->setFont(font);
681 frameLayout->addWidget(statusLabel);
684 frameLayout->addWidget(statusFilters);
686 QCheckBox *categoryLabel =
new ArrowCheckBox(tr(
"Categories"),
this);
688 categoryLabel->setFont(font);
689 connect(categoryLabel, &QCheckBox::toggled,
this
691 frameLayout->addWidget(categoryLabel);
705 QCheckBox *tagsLabel =
new ArrowCheckBox(tr(
"Tags"),
this);
707 tagsLabel->setFont(font);
709 frameLayout->addWidget(tagsLabel);
723 QCheckBox *trackerLabel =
new ArrowCheckBox(tr(
"Trackers"),
this);
725 trackerLabel->setFont(font);
726 frameLayout->addWidget(trackerLabel);
QVector< Torrent * > torrents() const
static Session * instance()
void torrentAboutToBeRemoved(Torrent *torrent)
void torrentsUpdated(const QVector< Torrent * > &torrents)
void torrentLoaded(Torrent *torrent)
virtual QVector< TrackerEntry > trackers() const =0
DownloadHandler * download(const DownloadRequest &downloadRequest)
static DownloadManager * instance()
static Preferences * instance()
bool getStatusFilterState() const
bool getTrackerFilterState() const
int getTransSelFilter() const
void setStatusFilterState(bool checked)
void setTagFilterState(bool checked)
bool getCategoryFilterState() const
void setTrackerFilterState(bool checked)
bool getTagFilterState() const
void setTransSelFilter(int index)
void setCategoryFilterState(bool checked)
void handleNewTorrent(BitTorrent::Torrent *const torrent) override
QSet< BitTorrent::TorrentID > getTorrentIDs(int row) const
void trackerError(const BitTorrent::TorrentID &id, const QString &tracker)
void removeItem(const QString &tracker, const BitTorrent::TorrentID &id)
void downloadFavicon(const QString &url)
void setDownloadTrackerFavicon(bool value)
QHash< BitTorrent::TorrentID, QSet< QString > > m_errors
bool m_downloadTrackerFavicon
TrackerFiltersList(QWidget *parent, TransferListWidget *transferList, bool downloadFavicon)
void trackerSuccess(const BitTorrent::TorrentID &id, const QString &tracker)
void addItem(const QString &tracker, const BitTorrent::TorrentID &id)
void trackerWarning(const BitTorrent::TorrentID &id, const QString &tracker)
~TrackerFiltersList() override
void applyFilter(int row) override
void handleFavicoDownloadFinished(const Net::DownloadResult &result)
QString trackerFromRow(int row) const
QHash< QString, QSet< BitTorrent::TorrentID > > m_trackers
void torrentAboutToBeDeleted(BitTorrent::Torrent *const torrent) override
QHash< BitTorrent::TorrentID, QSet< QString > > m_warnings
int rowFromTracker(const QString &tracker) const
void changeTrackerless(bool trackerless, const BitTorrent::TorrentID &id)
void showMenu(const QPoint &) override
static UIThemeManager * instance()
constexpr std::add_const_t< T > & asConst(T &t) noexcept
bool forceRemove(const QString &filePath)
QSize smallIconSize(const QWidget *widget=nullptr)
nonstd::expected< void, QString > saveToFile(const QString &path, const QByteArray &data)
T value(const QString &key, const T &defaultValue={})