Don't forget to update filter items

PR #20030.
Closes #19905.
This commit is contained in:
Vladimir Golovnev 2023-11-29 07:05:46 +03:00 committed by Vladimir Golovnev (Glassez)
parent 1773b1ea05
commit cdf66e069d
No known key found for this signature in database
GPG key ID: 52A2C7DEE2DFA6F7
5 changed files with 107 additions and 31 deletions

View file

@ -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)

View file

@ -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);
} }

View file

@ -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

View file

@ -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];
} }

View file

@ -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