mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-12-27 02:08:19 +03:00
commit
c894d84ed3
10 changed files with 133 additions and 42 deletions
|
@ -6,7 +6,7 @@ Mon Nov 27th 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.6.2
|
||||||
- WINDOWS: NSIS: Display correct Minimum Windows OS requirement (xavier2k6)
|
- WINDOWS: NSIS: Display correct Minimum Windows OS requirement (xavier2k6)
|
||||||
- WINDOWS: NSIS: Add Hebrew translation (avivmu)
|
- WINDOWS: NSIS: Add Hebrew translation (avivmu)
|
||||||
- LINUX: WAYLAND: Fix parent widget of "Lock qBittorrent" submenu (Vlad Zahorodnii)
|
- LINUX: WAYLAND: Fix parent widget of "Lock qBittorrent" submenu (Vlad Zahorodnii)
|
||||||
|
|
||||||
Mon Nov 20th 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.6.1
|
Mon Nov 20th 2023 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.6.1
|
||||||
- FEATURE: Add option to enable previous Add new torrent dialog behavior (glassez)
|
- FEATURE: Add option to enable previous Add new torrent dialog behavior (glassez)
|
||||||
- BUGFIX: Prevent crash due to race condition when adding magnet link (glassez)
|
- BUGFIX: Prevent crash due to race condition when adding magnet link (glassez)
|
||||||
|
|
|
@ -94,6 +94,7 @@ void showSplashScreen();
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
void adjustFileDescriptorLimit();
|
void adjustFileDescriptorLimit();
|
||||||
|
void adjustLocale();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Main
|
// Main
|
||||||
|
@ -104,6 +105,7 @@ int main(int argc, char *argv[])
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
|
adjustLocale();
|
||||||
adjustFileDescriptorLimit();
|
adjustFileDescriptorLimit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -392,4 +394,12 @@ void adjustFileDescriptorLimit()
|
||||||
limit.rlim_cur = limit.rlim_max;
|
limit.rlim_cur = limit.rlim_max;
|
||||||
setrlimit(RLIMIT_NOFILE, &limit);
|
setrlimit(RLIMIT_NOFILE, &limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void adjustLocale()
|
||||||
|
{
|
||||||
|
// specify the default locale just in case if user has not set any other locale
|
||||||
|
// only `C` locale is available universally without installing locale packages
|
||||||
|
if (qEnvironmentVariableIsEmpty("LANG"))
|
||||||
|
qputenv("LANG", "C.UTF-8");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -44,6 +44,7 @@ const QString PARAM_DOWNLOADPATH = u"download_path"_s;
|
||||||
const QString PARAM_OPERATINGMODE = u"operating_mode"_s;
|
const QString PARAM_OPERATINGMODE = u"operating_mode"_s;
|
||||||
const QString PARAM_QUEUETOP = u"add_to_top_of_queue"_s;
|
const QString PARAM_QUEUETOP = u"add_to_top_of_queue"_s;
|
||||||
const QString PARAM_STOPPED = u"stopped"_s;
|
const QString PARAM_STOPPED = u"stopped"_s;
|
||||||
|
const QString PARAM_STOPCONDITION = u"stop_condition"_s;
|
||||||
const QString PARAM_SKIPCHECKING = u"skip_checking"_s;
|
const QString PARAM_SKIPCHECKING = u"skip_checking"_s;
|
||||||
const QString PARAM_CONTENTLAYOUT = u"content_layout"_s;
|
const QString PARAM_CONTENTLAYOUT = u"content_layout"_s;
|
||||||
const QString PARAM_AUTOTMM = u"use_auto_tmm"_s;
|
const QString PARAM_AUTOTMM = u"use_auto_tmm"_s;
|
||||||
|
@ -126,17 +127,18 @@ BitTorrent::AddTorrentParams BitTorrent::parseAddTorrentParams(const QJsonObject
|
||||||
params.savePath = Path(jsonObj.value(PARAM_SAVEPATH).toString());
|
params.savePath = Path(jsonObj.value(PARAM_SAVEPATH).toString());
|
||||||
params.useDownloadPath = getOptionalBool(jsonObj, PARAM_USEDOWNLOADPATH);
|
params.useDownloadPath = getOptionalBool(jsonObj, PARAM_USEDOWNLOADPATH);
|
||||||
params.downloadPath = Path(jsonObj.value(PARAM_DOWNLOADPATH).toString());
|
params.downloadPath = Path(jsonObj.value(PARAM_DOWNLOADPATH).toString());
|
||||||
params.addForced = (getEnum<BitTorrent::TorrentOperatingMode>(jsonObj, PARAM_OPERATINGMODE) == BitTorrent::TorrentOperatingMode::Forced);
|
params.addForced = (getEnum<TorrentOperatingMode>(jsonObj, PARAM_OPERATINGMODE) == TorrentOperatingMode::Forced);
|
||||||
params.addToQueueTop = getOptionalBool(jsonObj, PARAM_QUEUETOP);
|
params.addToQueueTop = getOptionalBool(jsonObj, PARAM_QUEUETOP);
|
||||||
params.addPaused = getOptionalBool(jsonObj, PARAM_STOPPED);
|
params.addPaused = getOptionalBool(jsonObj, PARAM_STOPPED);
|
||||||
|
params.stopCondition = getOptionalEnum<Torrent::StopCondition>(jsonObj, PARAM_STOPCONDITION);
|
||||||
params.skipChecking = jsonObj.value(PARAM_SKIPCHECKING).toBool();
|
params.skipChecking = jsonObj.value(PARAM_SKIPCHECKING).toBool();
|
||||||
params.contentLayout = getOptionalEnum<BitTorrent::TorrentContentLayout>(jsonObj, PARAM_CONTENTLAYOUT);
|
params.contentLayout = getOptionalEnum<TorrentContentLayout>(jsonObj, PARAM_CONTENTLAYOUT);
|
||||||
params.useAutoTMM = getOptionalBool(jsonObj, PARAM_AUTOTMM);
|
params.useAutoTMM = getOptionalBool(jsonObj, PARAM_AUTOTMM);
|
||||||
params.uploadLimit = jsonObj.value(PARAM_UPLOADLIMIT).toInt(-1);
|
params.uploadLimit = jsonObj.value(PARAM_UPLOADLIMIT).toInt(-1);
|
||||||
params.downloadLimit = jsonObj.value(PARAM_DOWNLOADLIMIT).toInt(-1);
|
params.downloadLimit = jsonObj.value(PARAM_DOWNLOADLIMIT).toInt(-1);
|
||||||
params.seedingTimeLimit = jsonObj.value(PARAM_SEEDINGTIMELIMIT).toInt(BitTorrent::Torrent::USE_GLOBAL_SEEDING_TIME);
|
params.seedingTimeLimit = jsonObj.value(PARAM_SEEDINGTIMELIMIT).toInt(Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||||
params.inactiveSeedingTimeLimit = jsonObj.value(PARAM_INACTIVESEEDINGTIMELIMIT).toInt(BitTorrent::Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME);
|
params.inactiveSeedingTimeLimit = jsonObj.value(PARAM_INACTIVESEEDINGTIMELIMIT).toInt(Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME);
|
||||||
params.ratioLimit = jsonObj.value(PARAM_RATIOLIMIT).toDouble(BitTorrent::Torrent::USE_GLOBAL_RATIO);
|
params.ratioLimit = jsonObj.value(PARAM_RATIOLIMIT).toDouble(Torrent::USE_GLOBAL_RATIO);
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
@ -149,7 +151,7 @@ QJsonObject BitTorrent::serializeAddTorrentParams(const AddTorrentParams ¶ms
|
||||||
{PARAM_SAVEPATH, params.savePath.data()},
|
{PARAM_SAVEPATH, params.savePath.data()},
|
||||||
{PARAM_DOWNLOADPATH, params.downloadPath.data()},
|
{PARAM_DOWNLOADPATH, params.downloadPath.data()},
|
||||||
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
|
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
|
||||||
? BitTorrent::TorrentOperatingMode::Forced : BitTorrent::TorrentOperatingMode::AutoManaged)},
|
? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged)},
|
||||||
{PARAM_SKIPCHECKING, params.skipChecking},
|
{PARAM_SKIPCHECKING, params.skipChecking},
|
||||||
{PARAM_UPLOADLIMIT, params.uploadLimit},
|
{PARAM_UPLOADLIMIT, params.uploadLimit},
|
||||||
{PARAM_DOWNLOADLIMIT, params.downloadLimit},
|
{PARAM_DOWNLOADLIMIT, params.downloadLimit},
|
||||||
|
@ -162,6 +164,8 @@ QJsonObject BitTorrent::serializeAddTorrentParams(const AddTorrentParams ¶ms
|
||||||
jsonObj[PARAM_QUEUETOP] = *params.addToQueueTop;
|
jsonObj[PARAM_QUEUETOP] = *params.addToQueueTop;
|
||||||
if (params.addPaused)
|
if (params.addPaused)
|
||||||
jsonObj[PARAM_STOPPED] = *params.addPaused;
|
jsonObj[PARAM_STOPPED] = *params.addPaused;
|
||||||
|
if (params.stopCondition)
|
||||||
|
jsonObj[PARAM_STOPCONDITION] = Utils::String::fromEnum(*params.stopCondition);
|
||||||
if (params.contentLayout)
|
if (params.contentLayout)
|
||||||
jsonObj[PARAM_CONTENTLAYOUT] = Utils::String::fromEnum(*params.contentLayout);
|
jsonObj[PARAM_CONTENTLAYOUT] = Utils::String::fromEnum(*params.contentLayout);
|
||||||
if (params.useAutoTMM)
|
if (params.useAutoTMM)
|
||||||
|
|
|
@ -2681,6 +2681,7 @@ LoadTorrentParams SessionImpl::initLoadTorrentParams(const AddTorrentParams &add
|
||||||
loadTorrentParams.addToQueueTop = addTorrentParams.addToQueueTop.value_or(isAddTorrentToQueueTop());
|
loadTorrentParams.addToQueueTop = addTorrentParams.addToQueueTop.value_or(isAddTorrentToQueueTop());
|
||||||
loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
|
loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
|
||||||
loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;
|
loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;
|
||||||
|
loadTorrentParams.inactiveSeedingTimeLimit = addTorrentParams.inactiveSeedingTimeLimit;
|
||||||
|
|
||||||
const QString category = addTorrentParams.category;
|
const QString category = addTorrentParams.category;
|
||||||
if (!category.isEmpty() && !m_categories.contains(category) && !addCategory(category))
|
if (!category.isEmpty() && !m_categories.contains(category) && !addCategory(category))
|
||||||
|
@ -5831,7 +5832,7 @@ void SessionImpl::handleFileErrorAlert(const lt::file_error_alert *p)
|
||||||
|
|
||||||
const QString msg = QString::fromStdString(p->message());
|
const QString msg = QString::fromStdString(p->message());
|
||||||
LogMsg(tr("File error alert. Torrent: \"%1\". File: \"%2\". Reason: \"%3\"")
|
LogMsg(tr("File error alert. Torrent: \"%1\". File: \"%2\". Reason: \"%3\"")
|
||||||
.arg(torrent->name(), QString::fromLocal8Bit(p->filename()), msg)
|
.arg(torrent->name(), QString::fromUtf8(p->filename()), msg)
|
||||||
, Log::WARNING);
|
, Log::WARNING);
|
||||||
emit fullDiskError(torrent, msg);
|
emit fullDiskError(torrent, msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,6 +291,8 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::Torrent *const torrent)
|
||||||
// Info hashes
|
// Info hashes
|
||||||
m_ui->labelInfohash1Val->setText(m_torrent->infoHash().v1().isValid() ? m_torrent->infoHash().v1().toString() : tr("N/A"));
|
m_ui->labelInfohash1Val->setText(m_torrent->infoHash().v1().isValid() ? m_torrent->infoHash().v1().toString() : tr("N/A"));
|
||||||
m_ui->labelInfohash2Val->setText(m_torrent->infoHash().v2().isValid() ? m_torrent->infoHash().v2().toString() : tr("N/A"));
|
m_ui->labelInfohash2Val->setText(m_torrent->infoHash().v2().isValid() ? m_torrent->infoHash().v2().toString() : tr("N/A"));
|
||||||
|
// URL seeds
|
||||||
|
loadUrlSeeds();
|
||||||
if (m_torrent->hasMetadata())
|
if (m_torrent->hasMetadata())
|
||||||
{
|
{
|
||||||
// Creation date
|
// Creation date
|
||||||
|
@ -301,9 +303,6 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::Torrent *const torrent)
|
||||||
// Comment
|
// Comment
|
||||||
m_ui->labelCommentVal->setText(Utils::Misc::parseHtmlLinks(m_torrent->comment().toHtmlEscaped()));
|
m_ui->labelCommentVal->setText(Utils::Misc::parseHtmlLinks(m_torrent->comment().toHtmlEscaped()));
|
||||||
|
|
||||||
// URL seeds
|
|
||||||
loadUrlSeeds();
|
|
||||||
|
|
||||||
m_ui->labelCreatedByVal->setText(m_torrent->creator());
|
m_ui->labelCreatedByVal->setText(m_torrent->creator());
|
||||||
}
|
}
|
||||||
// Load dynamic data
|
// Load dynamic data
|
||||||
|
|
|
@ -285,7 +285,8 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
|
||||||
|
|
||||||
const bool useGlobalValue = allSameRatio && allSameSeedingTime
|
const bool useGlobalValue = allSameRatio && allSameSeedingTime
|
||||||
&& (firstTorrentRatio == BitTorrent::Torrent::USE_GLOBAL_RATIO)
|
&& (firstTorrentRatio == BitTorrent::Torrent::USE_GLOBAL_RATIO)
|
||||||
&& (firstTorrentSeedingTime == BitTorrent::Torrent::USE_GLOBAL_SEEDING_TIME);
|
&& (firstTorrentSeedingTime == BitTorrent::Torrent::USE_GLOBAL_SEEDING_TIME)
|
||||||
|
&& (firstTorrentInactiveSeedingTime == BitTorrent::Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME);
|
||||||
|
|
||||||
if (!allSameRatio || !allSameSeedingTime || !allSameInactiveSeedingTime)
|
if (!allSameRatio || !allSameSeedingTime || !allSameInactiveSeedingTime)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2016-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -38,6 +38,9 @@
|
||||||
class CategoryModelItem
|
class CategoryModelItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
inline static const QString UID_ALL {QChar(1)};
|
||||||
|
inline static const QString UID_UNCATEGORIZED;
|
||||||
|
|
||||||
CategoryModelItem() = default;
|
CategoryModelItem() = default;
|
||||||
|
|
||||||
CategoryModelItem(CategoryModelItem *parent, const QString &categoryName, const int torrentsCount = 0)
|
CategoryModelItem(CategoryModelItem *parent, const QString &categoryName, const int torrentsCount = 0)
|
||||||
|
@ -99,9 +102,21 @@ public:
|
||||||
|
|
||||||
int pos() const
|
int pos() const
|
||||||
{
|
{
|
||||||
if (!m_parent) return -1;
|
if (!m_parent)
|
||||||
|
return -1;
|
||||||
|
|
||||||
return m_parent->m_childUids.indexOf(m_name);
|
if (const int posByName = m_parent->m_childUids.indexOf(m_name); posByName >= 0)
|
||||||
|
return posByName;
|
||||||
|
|
||||||
|
// special cases
|
||||||
|
if (this == m_parent->m_children[UID_ALL])
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (this == m_parent->m_children[UID_UNCATEGORIZED])
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasChild(const QString &name) const
|
bool hasChild(const QString &name) const
|
||||||
|
@ -202,7 +217,8 @@ int CategoryFilterModel::columnCount(const QModelIndex &) const
|
||||||
|
|
||||||
QVariant CategoryFilterModel::data(const QModelIndex &index, int role) const
|
QVariant CategoryFilterModel::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid()) return {};
|
if (!index.isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
const auto *item = static_cast<const CategoryModelItem *>(index.internalPointer());
|
const auto *item = static_cast<const CategoryModelItem *>(index.internalPointer());
|
||||||
|
|
||||||
|
@ -248,8 +264,8 @@ QModelIndex CategoryFilterModel::index(int row, int column, const QModelIndex &p
|
||||||
if (parent.isValid() && (parent.column() != 0))
|
if (parent.isValid() && (parent.column() != 0))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto *parentItem = parent.isValid() ? static_cast<CategoryModelItem *>(parent.internalPointer())
|
auto *parentItem = parent.isValid()
|
||||||
: m_rootItem;
|
? static_cast<CategoryModelItem *>(parent.internalPointer()) : m_rootItem;
|
||||||
if (row < parentItem->childCount())
|
if (row < parentItem->childCount())
|
||||||
return createIndex(row, column, parentItem->childAt(row));
|
return createIndex(row, column, parentItem->childAt(row));
|
||||||
|
|
||||||
|
@ -262,7 +278,8 @@ QModelIndex CategoryFilterModel::parent(const QModelIndex &index) const
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto *item = static_cast<CategoryModelItem *>(index.internalPointer());
|
auto *item = static_cast<CategoryModelItem *>(index.internalPointer());
|
||||||
if (!item) return {};
|
if (!item)
|
||||||
|
return {};
|
||||||
|
|
||||||
return this->index(item->parent());
|
return this->index(item->parent());
|
||||||
}
|
}
|
||||||
|
@ -276,7 +293,8 @@ int CategoryFilterModel::rowCount(const QModelIndex &parent) const
|
||||||
return m_rootItem->childCount();
|
return m_rootItem->childCount();
|
||||||
|
|
||||||
auto *item = static_cast<CategoryModelItem *>(parent.internalPointer());
|
auto *item = static_cast<CategoryModelItem *>(parent.internalPointer());
|
||||||
if (!item) return 0;
|
if (!item)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return item->childCount();
|
return item->childCount();
|
||||||
}
|
}
|
||||||
|
@ -288,13 +306,16 @@ QModelIndex CategoryFilterModel::index(const QString &categoryName) const
|
||||||
|
|
||||||
QString CategoryFilterModel::categoryName(const QModelIndex &index) const
|
QString CategoryFilterModel::categoryName(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
if (!index.isValid()) return {};
|
if (!index.isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
return static_cast<CategoryModelItem *>(index.internalPointer())->fullName();
|
return static_cast<CategoryModelItem *>(index.internalPointer())->fullName();
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex CategoryFilterModel::index(CategoryModelItem *item) const
|
QModelIndex CategoryFilterModel::index(CategoryModelItem *item) const
|
||||||
{
|
{
|
||||||
if (!item || !item->parent()) return {};
|
if (!item || !item->parent())
|
||||||
|
return {};
|
||||||
|
|
||||||
return index(item->pos(), 0, index(item->parent()));
|
return index(item->pos(), 0, index(item->parent()));
|
||||||
}
|
}
|
||||||
|
@ -337,8 +358,17 @@ void CategoryFilterModel::torrentsLoaded(const QVector<BitTorrent::Torrent *> &t
|
||||||
Q_ASSERT(item);
|
Q_ASSERT(item);
|
||||||
|
|
||||||
item->increaseTorrentsCount();
|
item->increaseTorrentsCount();
|
||||||
|
QModelIndex i = index(item);
|
||||||
|
while (i.isValid())
|
||||||
|
{
|
||||||
|
emit dataChanged(i, i);
|
||||||
|
i = parent(i);
|
||||||
|
}
|
||||||
|
|
||||||
m_rootItem->childAt(0)->increaseTorrentsCount();
|
m_rootItem->childAt(0)->increaseTorrentsCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit dataChanged(index(0, 0), index(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategoryFilterModel::torrentAboutToBeRemoved(BitTorrent::Torrent *const torrent)
|
void CategoryFilterModel::torrentAboutToBeRemoved(BitTorrent::Torrent *const torrent)
|
||||||
|
@ -347,18 +377,24 @@ void CategoryFilterModel::torrentAboutToBeRemoved(BitTorrent::Torrent *const tor
|
||||||
Q_ASSERT(item);
|
Q_ASSERT(item);
|
||||||
|
|
||||||
item->decreaseTorrentsCount();
|
item->decreaseTorrentsCount();
|
||||||
|
QModelIndex i = index(item);
|
||||||
|
while (i.isValid())
|
||||||
|
{
|
||||||
|
emit dataChanged(i, i);
|
||||||
|
i = parent(i);
|
||||||
|
}
|
||||||
|
|
||||||
m_rootItem->childAt(0)->decreaseTorrentsCount();
|
m_rootItem->childAt(0)->decreaseTorrentsCount();
|
||||||
|
emit dataChanged(index(0, 0), index(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategoryFilterModel::torrentCategoryChanged(BitTorrent::Torrent *const torrent, const QString &oldCategory)
|
void CategoryFilterModel::torrentCategoryChanged(BitTorrent::Torrent *const torrent, const QString &oldCategory)
|
||||||
{
|
{
|
||||||
QModelIndex i;
|
|
||||||
|
|
||||||
auto *item = findItem(oldCategory);
|
auto *item = findItem(oldCategory);
|
||||||
Q_ASSERT(item);
|
Q_ASSERT(item);
|
||||||
|
|
||||||
item->decreaseTorrentsCount();
|
item->decreaseTorrentsCount();
|
||||||
i = index(item);
|
QModelIndex i = index(item);
|
||||||
while (i.isValid())
|
while (i.isValid())
|
||||||
{
|
{
|
||||||
emit dataChanged(i, i);
|
emit dataChanged(i, i);
|
||||||
|
@ -392,17 +428,16 @@ void CategoryFilterModel::populate()
|
||||||
const auto torrents = session->torrents();
|
const auto torrents = session->torrents();
|
||||||
m_isSubcategoriesEnabled = session->isSubcategoriesEnabled();
|
m_isSubcategoriesEnabled = session->isSubcategoriesEnabled();
|
||||||
|
|
||||||
const QString UID_ALL;
|
|
||||||
const QString UID_UNCATEGORIZED(QChar(1));
|
|
||||||
|
|
||||||
// All torrents
|
// All torrents
|
||||||
m_rootItem->addChild(UID_ALL, new CategoryModelItem(nullptr, tr("All"), torrents.count()));
|
m_rootItem->addChild(CategoryModelItem::UID_ALL
|
||||||
|
, new CategoryModelItem(nullptr, tr("All"), torrents.count()));
|
||||||
|
|
||||||
// Uncategorized torrents
|
// Uncategorized torrents
|
||||||
using Torrent = BitTorrent::Torrent;
|
using Torrent = BitTorrent::Torrent;
|
||||||
const int torrentsCount = std::count_if(torrents.begin(), torrents.end()
|
const int torrentsCount = std::count_if(torrents.begin(), torrents.end()
|
||||||
, [](Torrent *torrent) { return torrent->category().isEmpty(); });
|
, [](Torrent *torrent) { return torrent->category().isEmpty(); });
|
||||||
m_rootItem->addChild(UID_UNCATEGORIZED, new CategoryModelItem(nullptr, tr("Uncategorized"), torrentsCount));
|
m_rootItem->addChild(CategoryModelItem::UID_UNCATEGORIZED
|
||||||
|
, new CategoryModelItem(nullptr, tr("Uncategorized"), torrentsCount));
|
||||||
|
|
||||||
using BitTorrent::Torrent;
|
using BitTorrent::Torrent;
|
||||||
if (m_isSubcategoriesEnabled)
|
if (m_isSubcategoriesEnabled)
|
||||||
|
@ -446,7 +481,9 @@ CategoryModelItem *CategoryFilterModel::findItem(const QString &fullName) const
|
||||||
for (const QString &subcat : asConst(BitTorrent::Session::expandCategory(fullName)))
|
for (const QString &subcat : asConst(BitTorrent::Session::expandCategory(fullName)))
|
||||||
{
|
{
|
||||||
const QString subcatName = shortName(subcat);
|
const QString subcatName = shortName(subcat);
|
||||||
if (!item->hasChild(subcatName)) return nullptr;
|
if (!item->hasChild(subcatName))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
item = item->child(subcatName);
|
item = item->child(subcatName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2016-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2017 Tony Gregerson <tony.gregerson@gmail.com>
|
* Copyright (C) 2017 Tony Gregerson <tony.gregerson@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -36,6 +37,9 @@
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "gui/uithememanager.h"
|
#include "gui/uithememanager.h"
|
||||||
|
|
||||||
|
const int ROW_ALL = 0;
|
||||||
|
const int ROW_UNTAGGED = 1;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
QString getSpecialAllTag()
|
QString getSpecialAllTag()
|
||||||
|
@ -203,21 +207,29 @@ void TagFilterModel::tagRemoved(const QString &tag)
|
||||||
void TagFilterModel::torrentTagAdded(BitTorrent::Torrent *const torrent, const QString &tag)
|
void TagFilterModel::torrentTagAdded(BitTorrent::Torrent *const torrent, const QString &tag)
|
||||||
{
|
{
|
||||||
if (torrent->tags().count() == 1)
|
if (torrent->tags().count() == 1)
|
||||||
|
{
|
||||||
untaggedItem()->decreaseTorrentsCount();
|
untaggedItem()->decreaseTorrentsCount();
|
||||||
|
const QModelIndex i = index(ROW_UNTAGGED, 0);
|
||||||
|
emit dataChanged(i, i);
|
||||||
|
}
|
||||||
|
|
||||||
const int row = findRow(tag);
|
const int row = findRow(tag);
|
||||||
Q_ASSERT(isValidRow(row));
|
Q_ASSERT(isValidRow(row));
|
||||||
TagModelItem &item = m_tagItems[row];
|
TagModelItem &item = m_tagItems[row];
|
||||||
|
|
||||||
item.increaseTorrentsCount();
|
item.increaseTorrentsCount();
|
||||||
const QModelIndex i = index(row, 0, QModelIndex());
|
const QModelIndex i = index(row, 0);
|
||||||
emit dataChanged(i, i);
|
emit dataChanged(i, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagFilterModel::torrentTagRemoved(BitTorrent::Torrent *const torrent, const QString &tag)
|
void TagFilterModel::torrentTagRemoved(BitTorrent::Torrent *const torrent, const QString &tag)
|
||||||
{
|
{
|
||||||
if (torrent->tags().empty())
|
if (torrent->tags().empty())
|
||||||
|
{
|
||||||
untaggedItem()->increaseTorrentsCount();
|
untaggedItem()->increaseTorrentsCount();
|
||||||
|
const QModelIndex i = index(ROW_UNTAGGED, 0);
|
||||||
|
emit dataChanged(i, i);
|
||||||
|
}
|
||||||
|
|
||||||
const int row = findRow(tag);
|
const int row = findRow(tag);
|
||||||
if (row < 0)
|
if (row < 0)
|
||||||
|
@ -225,7 +237,7 @@ void TagFilterModel::torrentTagRemoved(BitTorrent::Torrent *const torrent, const
|
||||||
|
|
||||||
m_tagItems[row].decreaseTorrentsCount();
|
m_tagItems[row].decreaseTorrentsCount();
|
||||||
|
|
||||||
const QModelIndex i = index(row, 0, QModelIndex());
|
const QModelIndex i = index(row, 0);
|
||||||
emit dataChanged(i, i);
|
emit dataChanged(i, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,17 +254,39 @@ void TagFilterModel::torrentsLoaded(const QVector<BitTorrent::Torrent *> &torren
|
||||||
for (TagModelItem *item : items)
|
for (TagModelItem *item : items)
|
||||||
item->increaseTorrentsCount();
|
item->increaseTorrentsCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit dataChanged(index(0, 0), index((rowCount() - 1), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagFilterModel::torrentAboutToBeRemoved(BitTorrent::Torrent *const torrent)
|
void TagFilterModel::torrentAboutToBeRemoved(BitTorrent::Torrent *const torrent)
|
||||||
{
|
{
|
||||||
allTagsItem()->decreaseTorrentsCount();
|
allTagsItem()->decreaseTorrentsCount();
|
||||||
|
|
||||||
if (torrent->tags().isEmpty())
|
{
|
||||||
untaggedItem()->decreaseTorrentsCount();
|
const QModelIndex i = index(ROW_ALL, 0);
|
||||||
|
emit dataChanged(i, i);
|
||||||
|
}
|
||||||
|
|
||||||
for (TagModelItem *item : asConst(findItems(torrent->tags())))
|
if (torrent->tags().isEmpty())
|
||||||
item->decreaseTorrentsCount();
|
{
|
||||||
|
untaggedItem()->decreaseTorrentsCount();
|
||||||
|
const QModelIndex i = index(ROW_UNTAGGED, 0);
|
||||||
|
emit dataChanged(i, i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (const QString &tag : asConst(torrent->tags()))
|
||||||
|
{
|
||||||
|
const int row = findRow(tag);
|
||||||
|
Q_ASSERT(isValidRow(row));
|
||||||
|
if (Q_UNLIKELY(!isValidRow(row)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_tagItems[row].decreaseTorrentsCount();
|
||||||
|
const QModelIndex i = index(row, 0);
|
||||||
|
emit dataChanged(i, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TagFilterModel::tagDisplayName(const QString &tag)
|
QString TagFilterModel::tagDisplayName(const QString &tag)
|
||||||
|
@ -299,11 +333,15 @@ void TagFilterModel::removeFromModel(int row)
|
||||||
|
|
||||||
int TagFilterModel::findRow(const QString &tag) const
|
int TagFilterModel::findRow(const QString &tag) const
|
||||||
{
|
{
|
||||||
|
if (!BitTorrent::Session::isValidTag(tag))
|
||||||
|
return -1;
|
||||||
|
|
||||||
for (int i = 0; i < m_tagItems.size(); ++i)
|
for (int i = 0; i < m_tagItems.size(); ++i)
|
||||||
{
|
{
|
||||||
if (m_tagItems[i].tag() == tag)
|
if (m_tagItems[i].tag() == tag)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,11 +371,11 @@ QVector<TagModelItem *> TagFilterModel::findItems(const TagSet &tags)
|
||||||
TagModelItem *TagFilterModel::allTagsItem()
|
TagModelItem *TagFilterModel::allTagsItem()
|
||||||
{
|
{
|
||||||
Q_ASSERT(!m_tagItems.isEmpty());
|
Q_ASSERT(!m_tagItems.isEmpty());
|
||||||
return &m_tagItems[0];
|
return &m_tagItems[ROW_ALL];
|
||||||
}
|
}
|
||||||
|
|
||||||
TagModelItem *TagFilterModel::untaggedItem()
|
TagModelItem *TagFilterModel::untaggedItem()
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_tagItems.size() > 1);
|
Q_ASSERT(m_tagItems.size() > ROW_UNTAGGED);
|
||||||
return &m_tagItems[1];
|
return &m_tagItems[ROW_UNTAGGED];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2017 Tony Gregerson <tony.gregerson@gmail.com>
|
* Copyright (C) 2017 Tony Gregerson <tony.gregerson@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
|
Loading…
Reference in a new issue