Backport changes in v5.0.x branch

PR #21698.
This commit is contained in:
Vladimir Golovnev 2024-11-17 10:50:54 +03:00 committed by GitHub
commit 3454f064f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 208 additions and 99 deletions

View file

@ -195,11 +195,9 @@ void AddTorrentManager::setTorrentFileGuard(const QString &source, std::shared_p
m_guardedTorrentFiles.emplace(source, std::move(torrentFileGuard));
}
void AddTorrentManager::releaseTorrentFileGuard(const QString &source)
std::shared_ptr<TorrentFileGuard> AddTorrentManager::releaseTorrentFileGuard(const QString &source)
{
auto torrentFileGuard = m_guardedTorrentFiles.take(source);
if (torrentFileGuard)
torrentFileGuard->setAutoRemove(false);
return m_guardedTorrentFiles.take(source);
}
bool AddTorrentManager::processTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr

View file

@ -74,7 +74,7 @@ protected:
void handleAddTorrentFailed(const QString &source, const QString &reason);
void handleDuplicateTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr, BitTorrent::Torrent *existingTorrent);
void setTorrentFileGuard(const QString &source, std::shared_ptr<TorrentFileGuard> torrentFileGuard);
void releaseTorrentFileGuard(const QString &source);
std::shared_ptr<TorrentFileGuard> releaseTorrentFileGuard(const QString &source);
private:
void onDownloadFinished(const Net::DownloadResult &result);

View file

@ -4065,14 +4065,29 @@ bool SessionImpl::isPaused() const
void SessionImpl::pause()
{
if (!m_isPaused)
{
if (isRestored())
m_nativeSession->pause();
if (m_isPaused)
return;
m_isPaused = true;
emit paused();
if (isRestored())
{
m_nativeSession->pause();
for (TorrentImpl *torrent : asConst(m_torrents))
{
torrent->resetTrackerEntryStatuses();
const QList<TrackerEntryStatus> trackers = torrent->trackers();
QHash<QString, TrackerEntryStatus> updatedTrackers;
updatedTrackers.reserve(trackers.size());
for (const TrackerEntryStatus &status : trackers)
updatedTrackers.emplace(status.url, status);
emit trackerEntryStatusesUpdated(torrent, updatedTrackers);
}
}
m_isPaused = true;
emit paused();
}
void SessionImpl::resume()
@ -5215,9 +5230,6 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
if (torrent)
{
torrent->handleMoveStorageJobFinished(newPath, finishedJob.context, torrentHasOutstandingJob);
// The torrent may become "finished" at the end of the move if it was moved
// from the "incomplete" location after downloading finished.
processPendingFinishedTorrents();
}
else if (!torrentHasOutstandingJob)
{
@ -5481,6 +5493,11 @@ void SessionImpl::setTorrentContentLayout(const TorrentContentLayout value)
// Read alerts sent by libtorrent session
void SessionImpl::readAlerts()
{
// cache current datetime of Qt and libtorrent clocks in order
// to optimize conversion of time points from lt to Qt clocks
m_ltNow = lt::clock_type::now();
m_qNow = QDateTime::currentDateTime();
const std::vector<lt::alert *> alerts = getPendingAlerts();
Q_ASSERT(m_loadedTorrents.isEmpty());
@ -5507,6 +5524,9 @@ void SessionImpl::readAlerts()
}
}
// Some torrents may become "finished" after different alerts handling.
processPendingFinishedTorrents();
processTrackerStatuses();
}
@ -6146,8 +6166,6 @@ void SessionImpl::handleStateUpdateAlert(const lt::state_update_alert *alert)
if (!updatedTorrents.isEmpty())
emit torrentsUpdated(updatedTorrents);
processPendingFinishedTorrents();
if (m_needSaveTorrentsQueue)
saveTorrentsQueue();
@ -6344,3 +6362,9 @@ void SessionImpl::handleRemovedTorrent(const TorrentID &torrentID, const QString
m_removingTorrents.erase(removingTorrentDataIter);
}
QDateTime SessionImpl::fromLTTimePoint32(const libtorrent::time_point32 &timePoint) const
{
const auto secsSinceNow = lt::duration_cast<lt::seconds>(timePoint - m_ltNow + lt::milliseconds(500)).count();
return m_qNow.addSecs(secsSinceNow);
}

View file

@ -475,6 +475,8 @@ namespace BitTorrent
void addMappedPorts(const QSet<quint16> &ports);
void removeMappedPorts(const QSet<quint16> &ports);
QDateTime fromLTTimePoint32(const lt::time_point32 &timePoint) const;
template <typename Func>
void invoke(Func &&func)
{
@ -820,6 +822,9 @@ namespace BitTorrent
QList<TorrentImpl *> m_pendingFinishedTorrents;
QDateTime m_qNow;
lt::clock_type::time_point m_ltNow;
friend void Session::initInstance();
friend void Session::freeInstance();
friend Session *Session::instance();

View file

@ -49,6 +49,7 @@
#include <QtSystemDetection>
#include <QByteArray>
#include <QCache>
#include <QDebug>
#include <QPointer>
#include <QSet>
@ -92,37 +93,28 @@ namespace
return entry;
}
QDateTime fromLTTimePoint32(const lt::time_point32 &timePoint)
{
const auto ltNow = lt::clock_type::now();
const auto qNow = QDateTime::currentDateTime();
const auto secsSinceNow = lt::duration_cast<lt::seconds>(timePoint - ltNow + lt::milliseconds(500)).count();
return qNow.addSecs(secsSinceNow);
}
QString toString(const lt::tcp::endpoint &ltTCPEndpoint)
{
return QString::fromStdString((std::stringstream() << ltTCPEndpoint).str());
static QCache<lt::tcp::endpoint, QString> cache;
if (const QString *endpointName = cache.object(ltTCPEndpoint))
return *endpointName;
const std::string tmp = (std::ostringstream() << ltTCPEndpoint).str();
const auto endpointName = QString::fromLatin1(tmp.c_str(), tmp.size());
cache.insert(ltTCPEndpoint, new QString(endpointName));
return endpointName;
}
template <typename FromLTTimePoint32Func>
void updateTrackerEntryStatus(TrackerEntryStatus &trackerEntryStatus, const lt::announce_entry &nativeEntry
, const QSet<int> &btProtocols, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo)
, const QSet<int> &btProtocols, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo
, const FromLTTimePoint32Func &fromLTTimePoint32)
{
Q_ASSERT(trackerEntryStatus.url == QString::fromStdString(nativeEntry.url));
trackerEntryStatus.tier = nativeEntry.tier;
// remove outdated endpoints
trackerEntryStatus.endpoints.removeIf([&nativeEntry](const QHash<std::pair<QString, int>, TrackerEndpointStatus>::iterator &iter)
{
return std::none_of(nativeEntry.endpoints.cbegin(), nativeEntry.endpoints.cend()
, [&endpointName = std::get<0>(iter.key())](const auto &existingEndpoint)
{
return (endpointName == toString(existingEndpoint.local_endpoint));
});
});
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size()) * btProtocols.size();
int numUpdating = 0;
@ -205,6 +197,19 @@ namespace
}
}
if (trackerEntryStatus.endpoints.size() > numEndpoints)
{
// remove outdated endpoints
trackerEntryStatus.endpoints.removeIf([&nativeEntry](const QHash<std::pair<QString, int>, TrackerEndpointStatus>::iterator &iter)
{
return std::none_of(nativeEntry.endpoints.cbegin(), nativeEntry.endpoints.cend()
, [&endpointName = std::get<0>(iter.key())](const auto &existingEndpoint)
{
return (endpointName == toString(existingEndpoint.local_endpoint));
});
});
}
if (numEndpoints > 0)
{
if (numUpdating > 0)
@ -1632,9 +1637,19 @@ void TorrentImpl::forceRecheck()
return;
m_nativeHandle.force_recheck();
// We have to force update the cached state, otherwise someone will be able to get
// an incorrect one during the interval until the cached state is updated in a regular way.
m_nativeStatus.state = lt::torrent_status::checking_resume_data;
m_nativeStatus.pieces.clear_all();
m_nativeStatus.num_pieces = 0;
m_ltAddTorrentParams.have_pieces.clear();
m_ltAddTorrentParams.verified_pieces.clear();
m_ltAddTorrentParams.unfinished_pieces.clear();
m_completedFiles.fill(false);
m_filesProgress.fill(0);
m_pieces.fill(false);
m_unchecked = false;
if (m_hasMissingFiles)
{
@ -1647,14 +1662,6 @@ void TorrentImpl::forceRecheck()
}
}
m_unchecked = false;
m_completedFiles.fill(false);
m_filesProgress.fill(0);
m_pieces.fill(false);
m_nativeStatus.pieces.clear_all();
m_nativeStatus.num_pieces = 0;
if (isStopped())
{
// When "force recheck" is applied on Stopped torrent, we start them to perform checking
@ -1759,7 +1766,13 @@ TrackerEntryStatus TorrentImpl::updateTrackerEntryStatus(const lt::announce_entr
#else
const QSet<int> btProtocols {1};
#endif
::updateTrackerEntryStatus(*it, announceEntry, btProtocols, updateInfo);
const auto fromLTTimePoint32 = [this](const lt::time_point32 &timePoint)
{
return m_session->fromLTTimePoint32(timePoint);
};
::updateTrackerEntryStatus(*it, announceEntry, btProtocols, updateInfo, fromLTTimePoint32);
return *it;
}
@ -2142,6 +2155,7 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
m_ltAddTorrentParams.have_pieces.clear();
m_ltAddTorrentParams.verified_pieces.clear();
m_ltAddTorrentParams.unfinished_pieces.clear();
m_nativeStatus.torrent_file = m_ltAddTorrentParams.ti;
@ -2184,23 +2198,37 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
void TorrentImpl::prepareResumeData(const lt::add_torrent_params &params)
{
if (m_hasMissingFiles)
{
const auto havePieces = m_ltAddTorrentParams.have_pieces;
const auto unfinishedPieces = m_ltAddTorrentParams.unfinished_pieces;
const auto verifiedPieces = m_ltAddTorrentParams.verified_pieces;
decltype(params.have_pieces) havePieces;
decltype(params.unfinished_pieces) unfinishedPieces;
decltype(params.verified_pieces) verifiedPieces;
// The resume data obtained from libtorrent contains an empty "progress" in the following cases:
// 1. when it was requested at a time when the initial resume data has not yet been checked,
// 2. when initial resume data was rejected
// We should preserve the initial "progress" in such cases.
const bool needPreserveProgress = m_hasMissingFiles
|| (!m_ltAddTorrentParams.have_pieces.empty() && params.have_pieces.empty());
const bool preserveSeedMode = !m_hasMissingFiles && !hasMetadata()
&& (m_ltAddTorrentParams.flags & lt::torrent_flags::seed_mode);
if (needPreserveProgress)
{
havePieces = std::move(m_ltAddTorrentParams.have_pieces);
unfinishedPieces = std::move(m_ltAddTorrentParams.unfinished_pieces);
verifiedPieces = std::move(m_ltAddTorrentParams.verified_pieces);
}
// Update recent resume data but preserve existing progress
m_ltAddTorrentParams = params;
m_ltAddTorrentParams.have_pieces = havePieces;
m_ltAddTorrentParams.unfinished_pieces = unfinishedPieces;
m_ltAddTorrentParams.verified_pieces = verifiedPieces;
}
else
{
const bool preserveSeedMode = (!hasMetadata() && (m_ltAddTorrentParams.flags & lt::torrent_flags::seed_mode));
// Update recent resume data
m_ltAddTorrentParams = params;
if (needPreserveProgress)
{
m_ltAddTorrentParams.have_pieces = std::move(havePieces);
m_ltAddTorrentParams.unfinished_pieces = std::move(unfinishedPieces);
m_ltAddTorrentParams.verified_pieces = std::move(verifiedPieces);
}
if (preserveSeedMode)
m_ltAddTorrentParams.flags |= lt::torrent_flags::seed_mode;
}
@ -2350,7 +2378,8 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
// only apply Mark-of-the-Web to new download files
if (Preferences::instance()->isMarkOfTheWebEnabled() && isDownloading())
if (Preferences::instance()->isMarkOfTheWebEnabled()
&& (m_nativeStatus.state == lt::torrent_status::downloading))
{
const Path fullpath = actualStorageLocation() / actualPath;
Utils::OS::applyMarkOfTheWeb(fullpath);

View file

@ -31,6 +31,8 @@
#include "os.h"
#ifdef Q_OS_WIN
#include <algorithm>
#include <windows.h>
#include <powrprof.h>
#include <shlobj.h>
@ -42,6 +44,8 @@
#include <CoreServices/CoreServices.h>
#endif // Q_OS_MACOS
#include <QScopeGuard>
#ifdef QBT_USES_DBUS
#include <QDBusInterface>
#endif // QBT_USES_DBUS
@ -283,34 +287,49 @@ bool Utils::OS::applyMarkOfTheWeb(const Path &file, const QString &url)
// https://searchfox.org/mozilla-central/rev/ffdc4971dc18e1141cb2a90c2b0b776365650270/xpcom/io/CocoaFileUtils.mm#230
// https://github.com/transmission/transmission/blob/f62f7427edb1fd5c430e0ef6956bbaa4f03ae597/macosx/Torrent.mm#L1945-L1955
const CFStringRef fileString = file.toString().toCFString();
[[maybe_unused]] const auto fileStringGuard = qScopeGuard([&fileString] { ::CFRelease(fileString); });
const CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, fileString, kCFURLPOSIXPathStyle, false);
[[maybe_unused]] const auto fileURLGuard = qScopeGuard([&fileURL] { ::CFRelease(fileURL); });
if (CFDictionaryRef currentProperties = nullptr;
::CFURLCopyResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey, &currentProperties, NULL)
&& currentProperties)
{
[[maybe_unused]] const auto currentPropertiesGuard = qScopeGuard([&currentProperties] { ::CFRelease(currentProperties); });
if (CFStringRef quarantineType = nullptr;
::CFDictionaryGetValueIfPresent(currentProperties, kLSQuarantineTypeKey, reinterpret_cast<const void **>(&quarantineType))
&& quarantineType)
{
if (::CFStringCompare(quarantineType, kLSQuarantineTypeOtherDownload, 0) == kCFCompareEqualTo)
return true;
}
}
CFMutableDictionaryRef properties = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0
, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (properties == NULL)
if (!properties)
return false;
[[maybe_unused]] const auto propertiesGuard = qScopeGuard([&properties] { ::CFRelease(properties); });
::CFDictionarySetValue(properties, kLSQuarantineTypeKey, kLSQuarantineTypeOtherDownload);
if (!url.isEmpty())
::CFDictionarySetValue(properties, kLSQuarantineDataURLKey, url.toCFString());
const CFStringRef fileString = file.toString().toCFString();
const CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault
, fileString, kCFURLPOSIXPathStyle, false);
const Boolean success = ::CFURLSetResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey
, properties, NULL);
::CFRelease(fileURL);
::CFRelease(fileString);
::CFRelease(properties);
return success;
#elif defined(Q_OS_WIN)
const QString zoneIDStream = file.toString() + u":Zone.Identifier";
HANDLE handle = ::CreateFileW(zoneIDStream.toStdWString().c_str(), GENERIC_WRITE
HANDLE handle = ::CreateFileW(zoneIDStream.toStdWString().c_str(), (GENERIC_READ | GENERIC_WRITE)
, (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE)
, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return false;
[[maybe_unused]] const auto handleGuard = qScopeGuard([&handle] { ::CloseHandle(handle); });
// 5.6.1 Zone.Identifier Stream Name
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8
@ -318,10 +337,27 @@ bool Utils::OS::applyMarkOfTheWeb(const Path &file, const QString &url)
const QByteArray zoneID = QByteArrayLiteral("[ZoneTransfer]\r\nZoneId=3\r\n")
+ u"HostUrl=%1\r\n"_s.arg(hostURL).toUtf8();
if (LARGE_INTEGER streamSize = {0};
::GetFileSizeEx(handle, &streamSize) && (streamSize.QuadPart > 0))
{
const DWORD expectedReadSize = std::min<LONGLONG>(streamSize.QuadPart, 1024);
QByteArray buf {expectedReadSize, '\0'};
if (DWORD actualReadSize = 0;
::ReadFile(handle, buf.data(), expectedReadSize, &actualReadSize, nullptr) && (actualReadSize == expectedReadSize))
{
if (buf.startsWith("[ZoneTransfer]\r\n") && buf.contains("\r\nZoneId=3\r\n") && buf.contains("\r\nHostUrl="))
return true;
}
}
if (!::SetFilePointerEx(handle, {0}, nullptr, FILE_BEGIN))
return false;
if (!::SetEndOfFile(handle))
return false;
DWORD written = 0;
const BOOL writeResult = ::WriteFile(handle, zoneID.constData(), zoneID.size(), &written, nullptr);
::CloseHandle(handle);
return writeResult && (written == zoneID.size());
#endif
}

View file

@ -821,6 +821,8 @@ void AddNewTorrentDialog::reject()
if (!m_currentContext) [[unlikely]]
return;
emit torrentRejected(m_currentContext->torrentDescr);
const BitTorrent::TorrentDescriptor &torrentDescr = m_currentContext->torrentDescr;
const bool hasMetadata = torrentDescr.info().has_value();
if (!hasMetadata)

View file

@ -66,6 +66,7 @@ public:
signals:
void torrentAccepted(const BitTorrent::TorrentDescriptor &torrentDescriptor, const BitTorrent::AddTorrentParams &addTorrentParams);
void torrentRejected(const BitTorrent::TorrentDescriptor &torrentDescriptor);
private slots:
void updateDiskSpaceLabel();

View file

@ -235,15 +235,22 @@ bool GUIAddTorrentManager::processTorrent(const QString &source
dlg->setAttribute(Qt::WA_DeleteOnClose);
m_dialogs[infoHash] = dlg;
connect(dlg, &AddNewTorrentDialog::torrentAccepted, this
, [this, source](const BitTorrent::TorrentDescriptor &torrentDescr, const BitTorrent::AddTorrentParams &addTorrentParams)
{
addTorrentToSession(source, torrentDescr, addTorrentParams);
});
connect(dlg, &QDialog::finished, this, [this, source, infoHash, dlg]
, [this, source, dlg](const BitTorrent::TorrentDescriptor &torrentDescr, const BitTorrent::AddTorrentParams &addTorrentParams)
{
if (dlg->isDoNotDeleteTorrentChecked())
releaseTorrentFileGuard(source);
{
if (auto torrentFileGuard = releaseTorrentFileGuard(source))
torrentFileGuard->setAutoRemove(false);
}
addTorrentToSession(source, torrentDescr, addTorrentParams);
});
connect(dlg, &AddNewTorrentDialog::torrentRejected, this, [this, source]
{
releaseTorrentFileGuard(source);
});
connect(dlg, &QDialog::finished, this, [this, source, infoHash]
{
m_dialogs.remove(infoHash);
});

View file

@ -1703,11 +1703,10 @@ void OptionsDialog::initializeStyleCombo()
QStringList styleNames = QStyleFactory::keys();
std::sort(styleNames.begin(), styleNames.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
m_ui->comboStyle->addItems(styleNames);
const QString prefStyleName = Preferences::instance()->getStyle();
const QString selectedStyleName = prefStyleName.isEmpty() ? QApplication::style()->name() : prefStyleName;
if (selectedStyleName.compare(u"system"_s, Qt::CaseInsensitive) != 0)
m_ui->comboStyle->setCurrentText(selectedStyleName);
m_ui->comboStyle->setCurrentIndex(m_ui->comboStyle->findText(selectedStyleName, Qt::MatchFixedString));
#else
m_ui->labelStyle->hide();
m_ui->comboStyle->hide();

View file

@ -1,7 +1,7 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2020, Will Da Silva <will@willdasilva.xyz>
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@ -120,6 +120,7 @@ SearchWidget::SearchWidget(IGUIApplication *app, MainWindow *mainWindow)
#endif
connect(m_ui->tabWidget, &QTabWidget::tabCloseRequested, this, &SearchWidget::closeTab);
connect(m_ui->tabWidget, &QTabWidget::currentChanged, this, &SearchWidget::tabChanged);
connect(m_ui->tabWidget->tabBar(), &QTabBar::tabMoved, this, &SearchWidget::tabMoved);
const auto *searchManager = SearchPluginManager::instance();
const auto onPluginChanged = [this]()
@ -262,6 +263,11 @@ void SearchWidget::tabChanged(const int index)
m_currentSearchTab = ((index < 0) ? nullptr : m_allTabs.at(m_ui->tabWidget->currentIndex()));
}
void SearchWidget::tabMoved(const int from, const int to)
{
m_allTabs.move(from, to);
}
void SearchWidget::selectMultipleBox([[maybe_unused]] const int index)
{
if (selectedPlugin() == u"multi")

View file

@ -1,7 +1,7 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2020, Will Da Silva <will@willdasilva.xyz>
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@ -66,6 +66,7 @@ private slots:
private:
bool eventFilter(QObject *object, QEvent *event) override;
void tabChanged(int index);
void tabMoved(int from, int to);
void closeTab(int index);
void closeAllTabs();
void tabStatusChanged(QWidget *tab);

View file

@ -394,15 +394,11 @@ void TrackersFilterWidget::handleTrackerStatusesUpdated(const BitTorrent::Torren
{
if (trackerEntryStatus.state == BitTorrent::TrackerEndpointState::Working)
{
// remove tracker from "error" and "tracker error" categories
if (errorHashesIt != m_errors.end())
{
errorHashesIt->remove(trackerEntryStatus.url);
}
if (trackerErrorHashesIt != m_trackerErrors.end())
{
trackerErrorHashesIt->remove(trackerEntryStatus.url);
}
const bool hasNoWarningMessages = std::all_of(trackerEntryStatus.endpoints.cbegin(), trackerEntryStatus.endpoints.cend()
, [](const BitTorrent::TrackerEndpointStatus &endpointEntry)
@ -426,12 +422,24 @@ void TrackersFilterWidget::handleTrackerStatusesUpdated(const BitTorrent::Torren
else if ((trackerEntryStatus.state == BitTorrent::TrackerEndpointState::NotWorking)
|| (trackerEntryStatus.state == BitTorrent::TrackerEndpointState::Unreachable))
{
// remove tracker from "tracker error" and "warning" categories
if (warningHashesIt != m_warnings.end())
warningHashesIt->remove(trackerEntryStatus.url);
if (trackerErrorHashesIt != m_trackerErrors.end())
trackerErrorHashesIt->remove(trackerEntryStatus.url);
if (errorHashesIt == m_errors.end())
errorHashesIt = m_errors.insert(id, {});
errorHashesIt->insert(trackerEntryStatus.url);
}
else if (trackerEntryStatus.state == BitTorrent::TrackerEndpointState::TrackerError)
{
// remove tracker from "error" and "warning" categories
if (warningHashesIt != m_warnings.end())
warningHashesIt->remove(trackerEntryStatus.url);
if (errorHashesIt != m_errors.end())
errorHashesIt->remove(trackerEntryStatus.url);
if (trackerErrorHashesIt == m_trackerErrors.end())
trackerErrorHashesIt = m_trackerErrors.insert(id, {});
trackerErrorHashesIt->insert(trackerEntryStatus.url);

View file

@ -47,16 +47,9 @@ namespace
{
bool isDarkTheme()
{
switch (qApp->styleHints()->colorScheme())
{
case Qt::ColorScheme::Dark:
return true;
case Qt::ColorScheme::Light:
return false;
default:
// fallback to custom method
return (qApp->palette().color(QPalette::Active, QPalette::Base).lightness() < 127);
}
const QPalette palette = qApp->palette();
const QColor &color = palette.color(QPalette::Active, QPalette::Base);
return (color.lightness() < 127);
}
}