diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 678f499fb..de4934bd9 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -1580,6 +1580,19 @@ void Preferences::setConfirmMergeTrackers(const bool enabled) setValue(u"GUI/ConfirmActions/MergeTrackers"_s, enabled); } +bool Preferences::confirmRemoveTrackerFromAllTorrents() const +{ + return value(u"GUI/ConfirmActions/RemoveTrackerFromAllTorrents"_s, true); +} + +void Preferences::setConfirmRemoveTrackerFromAllTorrents(const bool enabled) +{ + if (enabled == confirmRemoveTrackerFromAllTorrents()) + return; + + setValue(u"GUI/ConfirmActions/RemoveTrackerFromAllTorrents"_s, enabled); +} + #ifndef Q_OS_MACOS TrayIcon::Style Preferences::trayIconStyle() const { diff --git a/src/base/preferences.h b/src/base/preferences.h index 58f710330..27d14d469 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -321,6 +321,8 @@ public: void setConfirmPauseAndResumeAll(bool enabled); bool confirmMergeTrackers() const; void setConfirmMergeTrackers(bool enabled); + bool confirmRemoveTrackerFromAllTorrents() const; + void setConfirmRemoveTrackerFromAllTorrents(bool enabled); #ifndef Q_OS_MACOS bool systemTrayEnabled() const; void setSystemTrayEnabled(bool enabled); diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp index f5af1a095..fa227201a 100644 --- a/src/gui/advancedsettings.cpp +++ b/src/gui/advancedsettings.cpp @@ -88,6 +88,7 @@ namespace NOTIFICATION_TIMEOUT, #endif CONFIRM_REMOVE_ALL_TAGS, + CONFIRM_REMOVE_TRACKER_FROM_ALL_TORRENTS, REANNOUNCE_WHEN_ADDRESS_CHANGED, DOWNLOAD_TRACKER_FAVICON, SAVE_PATH_HISTORY_LENGTH, @@ -100,6 +101,7 @@ namespace TRACKER_PORT, TRACKER_PORT_FORWARDING, PYTHON_EXECUTABLE_PATH, + // libtorrent section LIBTORRENT_HEADER, BDECODE_DEPTH_LIMIT, @@ -327,6 +329,7 @@ void AdvancedSettings::saveAdvancedSettings() const pref->setConfirmTorrentRecheck(m_checkBoxConfirmTorrentRecheck.isChecked()); pref->setConfirmRemoveAllTags(m_checkBoxConfirmRemoveAllTags.isChecked()); + pref->setConfirmRemoveTrackerFromAllTorrents(m_checkBoxConfirmRemoveTrackerFromAllTorrents.isChecked()); session->setAnnounceToAllTrackers(m_checkBoxAnnounceAllTrackers.isChecked()); session->setAnnounceToAllTiers(m_checkBoxAnnounceAllTiers.isChecked()); @@ -838,6 +841,10 @@ void AdvancedSettings::loadAdvancedSettings() m_checkBoxConfirmRemoveAllTags.setChecked(pref->confirmRemoveAllTags()); addRow(CONFIRM_REMOVE_ALL_TAGS, tr("Confirm removal of all tags"), &m_checkBoxConfirmRemoveAllTags); + // Remove tracker from all torrents confirmation + m_checkBoxConfirmRemoveTrackerFromAllTorrents.setChecked(pref->confirmRemoveTrackerFromAllTorrents()); + addRow(CONFIRM_REMOVE_TRACKER_FROM_ALL_TORRENTS, tr("Confirm removal of tracker from all torrents"), &m_checkBoxConfirmRemoveTrackerFromAllTorrents); + // Announce to all trackers in a tier m_checkBoxAnnounceAllTrackers.setChecked(session->announceToAllTrackers()); addRow(ANNOUNCE_ALL_TRACKERS, (tr("Always announce to all trackers in a tier") diff --git a/src/gui/advancedsettings.h b/src/gui/advancedsettings.h index ed9a14148..a6c2ff7c1 100644 --- a/src/gui/advancedsettings.h +++ b/src/gui/advancedsettings.h @@ -78,7 +78,7 @@ private: m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxReannounceWhenAddressChanged, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus, m_checkBoxTrackerPortForwarding, m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers, m_checkBoxMultiConnectionsPerIp, m_checkBoxValidateHTTPSTrackerCertificate, m_checkBoxSSRFMitigation, m_checkBoxBlockPeersOnPrivilegedPorts, m_checkBoxPieceExtentAffinity, - m_checkBoxSuggestMode, m_checkBoxSpeedWidgetEnabled, m_checkBoxIDNSupport; + m_checkBoxSuggestMode, m_checkBoxSpeedWidgetEnabled, m_checkBoxIDNSupport, m_checkBoxConfirmRemoveTrackerFromAllTorrents; QComboBox m_comboBoxInterface, m_comboBoxInterfaceAddress, m_comboBoxDiskIOReadMode, m_comboBoxDiskIOWriteMode, m_comboBoxUtpMixedMode, m_comboBoxChokingAlgorithm, m_comboBoxSeedChokingAlgorithm, m_comboBoxResumeDataStorage; QLineEdit m_pythonExecutablePath, m_lineEditAnnounceIP, m_lineEditDHTBootstrapNodes; diff --git a/src/gui/transferlistfilters/trackersfilterwidget.cpp b/src/gui/transferlistfilters/trackersfilterwidget.cpp index 3d29c9a17..2e8586f28 100644 --- a/src/gui/transferlistfilters/trackersfilterwidget.cpp +++ b/src/gui/transferlistfilters/trackersfilterwidget.cpp @@ -29,9 +29,11 @@ #include "trackersfilterwidget.h" +#include #include #include #include +#include #include #include "base/algorithm.h" @@ -54,7 +56,7 @@ namespace OTHERERROR_ROW, WARNING_ROW, - NUM_ROWS + NUM_SPECIAL_ROWS }; const QString NULL_HOST = u""_s; @@ -262,10 +264,10 @@ void TrackersFilterWidget::addItems(const QString &trackerURL, const QVector= NUM_ROWS); + Q_ASSERT(count() >= NUM_SPECIAL_ROWS); const Utils::Compare::NaturalLessThan naturalLessThan {}; int insPos = count(); - for (int i = NUM_ROWS; i < count(); ++i) + for (int i = NUM_SPECIAL_ROWS; i < count(); ++i) { if (naturalLessThan(host, item(i)->text())) { @@ -469,6 +471,28 @@ void TrackersFilterWidget::downloadFavicon(const QString &trackerHost, const QSt downloadingFaviconNode.insert(trackerHost); } +void TrackersFilterWidget::removeTracker(const QString &tracker) +{ + for (const BitTorrent::TorrentID &torrentID : asConst(m_trackers.value(tracker).torrents)) + { + auto *torrent = BitTorrent::Session::instance()->getTorrent(torrentID); + Q_ASSERT(torrent); + if (!torrent) [[unlikely]] + continue; + + QStringList trackersToRemove; + for (const BitTorrent::TrackerEntry &trackerEntry : asConst(torrent->trackers())) + { + if ((trackerEntry.url == tracker) || (QUrl(trackerEntry.url).host() == tracker)) + trackersToRemove.append(trackerEntry.url); + } + + torrent->removeTrackers({trackersToRemove}); + } + + updateGeometry(); +} + void TrackersFilterWidget::handleFavicoDownloadFinished(const Net::DownloadResult &result) { const QSet trackerHosts = m_downloadingFavicons.take(result.url); @@ -537,6 +561,13 @@ void TrackersFilterWidget::showMenu() QMenu *menu = new QMenu(this); menu->setAttribute(Qt::WA_DeleteOnClose); + if (currentRow() >= NUM_SPECIAL_ROWS) + { + menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_s, u"list-remove"_s), tr("Remove tracker") + , this, &TrackersFilterWidget::onRemoveTrackerTriggered); + menu->addSeparator(); + } + menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-start"_s, u"media-playback-start"_s), tr("Resume torrents") , transferList(), &TransferListWidget::startVisibleTorrents); menu->addAction(UIThemeManager::instance()->getIcon(u"torrent-stop"_s, u"media-playback-pause"_s), tr("Pause torrents") @@ -593,6 +624,34 @@ void TrackersFilterWidget::torrentAboutToBeDeleted(BitTorrent::Torrent *const to item(ALL_ROW)->setText(formatItemText(ALL_ROW, --m_totalTorrents)); } +void TrackersFilterWidget::onRemoveTrackerTriggered() +{ + const int row = currentRow(); + if (row < NUM_SPECIAL_ROWS) + return; + + const QString &tracker = trackerFromRow(row); + if (!Preferences::instance()->confirmRemoveTrackerFromAllTorrents()) + { + removeTracker(tracker); + return; + } + + auto *confirmBox = new QMessageBox(QMessageBox::Question, tr("Removal confirmation") + , tr("Are you sure you want to remove tracker \"%1\" from all torrents?").arg(tracker) + , (QMessageBox::Yes | QMessageBox::No), this); + confirmBox->setCheckBox(new QCheckBox(tr("Don't ask me again."))); + confirmBox->setAttribute(Qt::WA_DeleteOnClose); + connect(confirmBox, &QDialog::accepted, this, [this, confirmBox, tracker] + { + removeTracker(tracker); + + if (confirmBox->checkBox()->isChecked()) + Preferences::instance()->setConfirmRemoveTrackerFromAllTorrents(false); + }); + confirmBox->open(); +} + QString TrackersFilterWidget::trackerFromRow(int row) const { Q_ASSERT(row > 1); @@ -606,7 +665,7 @@ QString TrackersFilterWidget::trackerFromRow(int row) const int TrackersFilterWidget::rowFromTracker(const QString &tracker) const { Q_ASSERT(!tracker.isEmpty()); - for (int i = NUM_ROWS; i < count(); ++i) + for (int i = NUM_SPECIAL_ROWS; i < count(); ++i) { if (tracker == trackerFromRow(i)) return i; diff --git a/src/gui/transferlistfilters/trackersfilterwidget.h b/src/gui/transferlistfilters/trackersfilterwidget.h index 87d60628c..fe740b2c1 100644 --- a/src/gui/transferlistfilters/trackersfilterwidget.h +++ b/src/gui/transferlistfilters/trackersfilterwidget.h @@ -70,12 +70,15 @@ private: void handleTorrentsLoaded(const QVector &torrents) override; void torrentAboutToBeDeleted(BitTorrent::Torrent *torrent) override; + void onRemoveTrackerTriggered(); + void addItems(const QString &trackerURL, const QVector &torrents); void removeItem(const QString &trackerURL, const BitTorrent::TorrentID &id); QString trackerFromRow(int row) const; int rowFromTracker(const QString &tracker) const; QSet getTorrentIDs(int row) const; void downloadFavicon(const QString &trackerHost, const QString &faviconURL); + void removeTracker(const QString &tracker); struct TrackerData {