mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-28 21:38:51 +03:00
commit
3454f064f0
14 changed files with 208 additions and 99 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 <TCPEndpoint)
|
||||
{
|
||||
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 ¶ms)
|
||||
{
|
||||
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);
|
||||
|
|
|
@ -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, ¤tProperties, NULL)
|
||||
&& currentProperties)
|
||||
{
|
||||
[[maybe_unused]] const auto currentPropertiesGuard = qScopeGuard([¤tProperties] { ::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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue