Cockatrice/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp
BruebachL 1eca89d75a
[Refactor] Batch CardDatabaseModel enabled-sets rebuild into one model reset (#7113)
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-08-14 09:03:24 +02:00

200 lines
6 KiB
C++

#include "card_database_model.h"
#include <QMap>
#include <libcockatrice/card/database/card_database.h>
#define CARDDBMODEL_COLUMNS 6
CardDatabaseModel::CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromEnabledSets, QObject *parent)
: QAbstractListModel(parent), db(_db), showOnlyCardsFromEnabledSets(_showOnlyCardsFromEnabledSets)
{
connect(db, &CardDatabase::cardAdded, this, &CardDatabaseModel::cardAdded);
connect(db, &CardDatabase::cardRemoved, this, &CardDatabaseModel::cardRemoved);
connect(db, &CardDatabase::cardDatabaseEnabledSetsChanged, this,
&CardDatabaseModel::cardDatabaseEnabledSetsChanged);
connect(db, &CardDatabase::cardDatabaseReset, this, &CardDatabaseModel::resetCardList);
cardDatabaseEnabledSetsChanged();
}
CardDatabaseModel::~CardDatabaseModel() = default;
int CardDatabaseModel::rowCount(const QModelIndex & /*parent*/) const
{
return cardList.size();
}
int CardDatabaseModel::columnCount(const QModelIndex & /*parent*/) const
{
return CARDDBMODEL_COLUMNS;
}
QVariant CardDatabaseModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= cardList.size() || index.column() >= CARDDBMODEL_COLUMNS ||
(role != Qt::DisplayRole && role != SortRole)) {
return QVariant();
}
CardInfoPtr card = cardList.at(index.row());
switch (index.column()) {
case NameColumn:
return card->getName();
case SetListColumn:
return card->getSetsNames();
case ManaCostColumn:
return role == SortRole ? QString("%1%2").arg(card->getCmc(), 4, QChar('0')).arg(card->getManaCost())
: card->getManaCost();
case CardTypeColumn:
return card->getCardType();
case PTColumn:
return card->getPowTough();
case ColorColumn:
return card->getColors();
default:
return QVariant();
}
}
QVariant CardDatabaseModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole) {
return QVariant();
}
if (orientation != Qt::Horizontal) {
return QVariant();
}
switch (section) {
case NameColumn:
return QString(tr("Name"));
case SetListColumn:
return QString(tr("Sets"));
case ManaCostColumn:
return QString(tr("Mana cost"));
case CardTypeColumn:
return QString(tr("Card type"));
case PTColumn:
return QString(tr("P/T"));
case ColorColumn:
return QString(tr("Color(s)"));
default:
return QVariant();
}
}
void CardDatabaseModel::cardInfoChanged(const CardInfoPtr &card)
{
const int row = cardList.indexOf(card);
if (row == -1) {
return;
}
emit dataChanged(index(row, 0), index(row, CARDDBMODEL_COLUMNS - 1));
}
bool CardDatabaseModel::checkCardHasAtLeastOneEnabledSet(const CardInfoPtr &card) const
{
if (!showOnlyCardsFromEnabledSets) {
return true;
}
for (const auto &printings : card->getSets()) {
for (const auto &printing : printings) {
if (printing.getSet()->getEnabled()) {
return true;
}
}
}
return false;
}
void CardDatabaseModel::cardDatabaseEnabledSetsChanged()
{
// Build the new card list in a single pass.
QList<CardInfoPtr> newCardList;
newCardList.reserve(cardList.size());
for (const CardInfoPtr &card : cardList) {
if (checkCardHasAtLeastOneEnabledSet(card)) {
newCardList.append(card);
}
}
for (const CardInfoPtr &card : db->getCardList()) {
if (!cardListSet.contains(card) && checkCardHasAtLeastOneEnabledSet(card)) {
newCardList.append(card);
}
}
if (newCardList == cardList) {
return;
}
// Rebuild the whole list inside a single model reset instead of emitting
// per-card insert/remove notifications. With tens of thousands of cards the
// per-card path is the dominant cost of constructing a CardDatabaseModel.
QSet<CardInfoPtr> oldCardListSet = cardListSet;
QSet<CardInfoPtr> newCardListSet(newCardList.begin(), newCardList.end());
beginResetModel();
// Disconnect cards that are no longer shown.
for (const CardInfoPtr &card : cardList) {
if (!newCardListSet.contains(card)) {
disconnect(card.data(), nullptr, this, nullptr);
}
}
cardList = newCardList;
cardListSet = newCardListSet;
// Connect cards that are now shown for the first time.
for (const CardInfoPtr &card : cardList) {
if (!oldCardListSet.contains(card)) {
connect(card.data(), &CardInfo::cardInfoChanged, this, &CardDatabaseModel::cardInfoChanged);
}
}
endResetModel();
}
void CardDatabaseModel::cardAdded(const CardInfoPtr &card)
{
if (checkCardHasAtLeastOneEnabledSet(card)) {
// add the card if it's present in at least one enabled set
beginInsertRows(QModelIndex(), cardList.size(), cardList.size());
cardList.append(card);
cardListSet.insert(card);
connect(card.data(), &CardInfo::cardInfoChanged, this, &CardDatabaseModel::cardInfoChanged);
endInsertRows();
}
}
void CardDatabaseModel::cardRemoved(CardInfoPtr card)
{
const int row = cardList.indexOf(card);
if (row == -1) {
return;
}
beginRemoveRows(QModelIndex(), row, row);
disconnect(card.data(), nullptr, this, nullptr);
cardListSet.remove(card);
card.clear();
cardList.removeAt(row);
endRemoveRows();
}
void CardDatabaseModel::resetCardList()
{
beginResetModel();
cardList.clear();
cardListSet.clear();
for (const CardInfoPtr &card : db->getCardList()) {
if (checkCardHasAtLeastOneEnabledSet(card)) {
cardList.append(card);
cardListSet.insert(card);
connect(card.data(), &CardInfo::cardInfoChanged, this, &CardDatabaseModel::cardInfoChanged);
}
}
endResetModel();
}