Turn Card, Deck_List, Protocol, RNG, Network (Client, Server), Settings and Utility into libraries and remove cockatrice_common. (#6212)

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: ebbit1q <ebbit1q@gmail.com>
This commit is contained in:
BruebachL 2025-10-09 07:36:12 +02:00 committed by GitHub
parent be1403c920
commit 1ef07309d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
605 changed files with 3812 additions and 3408 deletions

View file

@ -0,0 +1,242 @@
#include "card_picture_loader.h"
#include <QApplication>
#include <QBuffer>
#include <QDebug>
#include <QDirIterator>
#include <QFileInfo>
#include <QMainWindow>
#include <QMovie>
#include <QNetworkDiskCache>
#include <QNetworkRequest>
#include <QPainter>
#include <QPixmapCache>
#include <QScreen>
#include <QStatusBar>
#include <QThread>
#include <algorithm>
#include <libcockatrice/settings/cache_settings.h>
#include <utility>
// never cache more than 300 cards at once for a single deck
#define CACHED_CARD_PER_DECK_MAX 300
CardPictureLoader::CardPictureLoader() : QObject(nullptr)
{
worker = new CardPictureLoaderWorker;
connect(&SettingsCache::instance(), &SettingsCache::picsPathChanged, this, &CardPictureLoader::picsPathChanged);
connect(&SettingsCache::instance(), &SettingsCache::picDownloadChanged, this,
&CardPictureLoader::picDownloadChanged);
connect(worker, &CardPictureLoaderWorker::imageLoaded, this, &CardPictureLoader::imageLoaded);
statusBar = new CardPictureLoaderStatusBar(nullptr);
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(QApplication::activeWindow());
if (mainWindow) {
mainWindow->statusBar()->addPermanentWidget(statusBar);
}
connect(worker, &CardPictureLoaderWorker::imageRequestQueued, statusBar,
&CardPictureLoaderStatusBar::addQueuedImageLoad);
connect(worker, &CardPictureLoaderWorker::imageRequestSucceeded, statusBar,
&CardPictureLoaderStatusBar::addSuccessfulImageLoad);
}
CardPictureLoader::~CardPictureLoader()
{
worker->deleteLater();
}
void CardPictureLoader::getCardBackPixmap(QPixmap &pixmap, QSize size)
{
QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + "x" + QString::number(size.height());
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
qCDebug(CardPictureLoaderLog) << "PictureLoader: cache miss for" << backCacheKey;
QPixmap tmpPixmap("theme:cardback");
if (tmpPixmap.isNull()) {
qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback'! Using fallback pixmap.";
tmpPixmap = QPixmap(size);
tmpPixmap.fill(Qt::gray); // Fallback to a gray pixmap
} else {
qCDebug(CardPictureLoaderLog) << "Successfully loaded 'theme:cardback'.";
}
pixmap = tmpPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
QPixmapCache::insert(backCacheKey, pixmap);
}
}
void CardPictureLoader::getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size)
{
QString backCacheKey =
"_trice_card_back_inprogress_" + QString::number(size.width()) + "x" + QString::number(size.height());
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
qCDebug(CardPictureLoaderCardBackCacheFailLog) << "PictureLoader: cache miss for" << backCacheKey;
QPixmap tmpPixmap("theme:cardback");
if (tmpPixmap.isNull()) {
qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback' for in-progress state! Using fallback.";
tmpPixmap = QPixmap(size);
tmpPixmap.fill(Qt::blue); // Fallback with blue color
} else {
qCDebug(CardPictureLoaderCardBackCacheFailLog)
<< "Successfully loaded 'theme:cardback' for in-progress state.";
}
pixmap = tmpPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
QPixmapCache::insert(backCacheKey, pixmap);
}
}
void CardPictureLoader::getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize size)
{
QString backCacheKey =
"_trice_card_back_failed_" + QString::number(size.width()) + "x" + QString::number(size.height());
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
qCDebug(CardPictureLoaderCardBackCacheFailLog) << "PictureLoader: cache miss for" << backCacheKey;
QPixmap tmpPixmap("theme:cardback");
if (tmpPixmap.isNull()) {
qCWarning(CardPictureLoaderLog) << "Failed to load 'theme:cardback' for failed state! Using fallback.";
tmpPixmap = QPixmap(size);
tmpPixmap.fill(Qt::red); // Fallback with red color
} else {
qCDebug(CardPictureLoaderCardBackCacheFailLog) << "Successfully loaded 'theme:cardback' for failed state.";
}
pixmap = tmpPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
QPixmapCache::insert(backCacheKey, pixmap);
}
}
void CardPictureLoader::getPixmap(QPixmap &pixmap, const ExactCard &card, QSize size)
{
if (!card) {
qCWarning(CardPictureLoaderLog) << "getPixmap called with null card!";
return;
}
QString key = card.getPixmapCacheKey();
QString sizeKey = key + QLatin1Char('_') + QString::number(size.width()) + "x" + QString::number(size.height());
if (QPixmapCache::find(sizeKey, &pixmap)) {
return; // Use cached version
}
// load the image and create a copy of the correct size
QPixmap bigPixmap;
if (QPixmapCache::find(key, &bigPixmap)) {
if (bigPixmap.isNull()) {
qCDebug(CardPictureLoaderLog) << "Cached pixmap for key" << key << "is NULL!";
return;
}
QScreen *screen = qApp->primaryScreen();
qreal dpr = screen ? screen->devicePixelRatio() : 1.0;
qCDebug(CardPictureLoaderLog) << "Scaling cached image for" << card.getName();
pixmap = bigPixmap.scaled(size * dpr, Qt::KeepAspectRatio, Qt::SmoothTransformation);
pixmap.setDevicePixelRatio(dpr);
QPixmapCache::insert(sizeKey, pixmap);
return;
}
// add the card to the load queue
qCDebug(CardPictureLoaderLog) << "Enqueuing " << card.getName() << " for " << card.getPixmapCacheKey();
getInstance().worker->enqueueImageLoad(card);
}
void CardPictureLoader::imageLoaded(const ExactCard &card, const QImage &image)
{
if (image.isNull()) {
qCDebug(CardPictureLoaderLog) << "Caching NULL pixmap for" << card.getName();
QPixmapCache::insert(card.getPixmapCacheKey(), QPixmap());
} else {
if (card.getInfo().getUpsideDownArt()) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 9, 0))
QImage mirrorImage = image.flipped(Qt::Horizontal | Qt::Vertical);
#else
QImage mirrorImage = image.mirrored(true, true);
#endif
QPixmapCache::insert(card.getPixmapCacheKey(), QPixmap::fromImage(mirrorImage));
} else {
QPixmapCache::insert(card.getPixmapCacheKey(), QPixmap::fromImage(image));
}
}
// imageLoaded should only be reached if the exactCard isn't already in cache.
// (plus there's a deduplication mechanism in CardPictureLoaderWorker)
// It should be safe to connect the CardInfo here without worrying about redundant connections.
connect(card.getCardPtr().data(), &QObject::destroyed, this,
[cacheKey = card.getPixmapCacheKey()] { QPixmapCache::remove(cacheKey); });
card.emitPixmapUpdated();
}
void CardPictureLoader::clearPixmapCache()
{
QPixmapCache::clear();
}
void CardPictureLoader::clearNetworkCache()
{
getInstance().worker->clearNetworkCache();
}
void CardPictureLoader::cacheCardPixmaps(const QList<ExactCard> &cards)
{
QPixmap tmp;
int max = qMin(cards.size(), CACHED_CARD_PER_DECK_MAX);
for (int i = 0; i < max; ++i) {
const ExactCard &card = cards.at(i);
if (!card) {
continue;
}
QString key = card.getPixmapCacheKey();
if (QPixmapCache::find(key, &tmp)) {
continue;
}
getInstance().worker->enqueueImageLoad(card);
}
}
void CardPictureLoader::picDownloadChanged()
{
QPixmapCache::clear();
}
void CardPictureLoader::picsPathChanged()
{
QPixmapCache::clear();
}
bool CardPictureLoader::hasCustomArt()
{
auto picsPath = SettingsCache::instance().getPicsPath();
QDirIterator it(picsPath, QDir::Dirs | QDir::NoDotAndDotDot);
// Check if there is at least one non-directory file in the pics path, other
// than in the "downloadedPics" subdirectory.
while (it.hasNext()) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
QFileInfo dir(it.nextFileInfo());
#else
// nextFileInfo() is only available in Qt 6.3+, for previous versions, we build
// the QFileInfo from a QString which requires more system calls.
QFileInfo dir(it.next());
#endif
if (it.fileName() == "downloadedPics")
continue;
QDirIterator subIt(it.filePath(), QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
if (subIt.hasNext()) {
return true;
}
}
return false;
}

View file

@ -0,0 +1,58 @@
/**
* @file card_picture_loader.h
* @ingroup PictureLoader
* @brief TODO: Document this.
*/
#ifndef CARD_PICTURE_LOADER_H
#define CARD_PICTURE_LOADER_H
#include "card_picture_loader_status_bar.h"
#include "card_picture_loader_worker.h"
#include <QLoggingCategory>
#include <libcockatrice/card/card_info.h>
inline Q_LOGGING_CATEGORY(CardPictureLoaderLog, "card_picture_loader");
inline Q_LOGGING_CATEGORY(CardPictureLoaderCardBackCacheFailLog, "card_picture_loader.card_back_cache_fail");
class CardPictureLoader : public QObject
{
Q_OBJECT
public:
static CardPictureLoader &getInstance()
{
static CardPictureLoader instance;
return instance;
}
private:
explicit CardPictureLoader();
~CardPictureLoader() override;
// Singleton - Don't implement copy constructor and assign operator
CardPictureLoader(CardPictureLoader const &);
void operator=(CardPictureLoader const &);
CardPictureLoaderWorker *worker;
CardPictureLoaderStatusBar *statusBar;
public:
static void getPixmap(QPixmap &pixmap, const ExactCard &card, QSize size);
static void getCardBackPixmap(QPixmap &pixmap, QSize size);
static void getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size);
static void getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize size);
static void clearPixmapCache();
static void cacheCardPixmaps(const QList<ExactCard> &cards);
static bool hasCustomArt();
public slots:
static void clearNetworkCache();
private slots:
void picDownloadChanged();
void picsPathChanged();
public slots:
void imageLoaded(const ExactCard &card, const QImage &image);
};
#endif

View file

@ -0,0 +1,155 @@
#include "card_picture_loader_local.h"
#include "card_picture_to_load.h"
#include <QDirIterator>
#include <QMovie>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/settings/cache_settings.h>
static constexpr int REFRESH_INTERVAL_MS = 10 * 1000;
CardPictureLoaderLocal::CardPictureLoaderLocal(QObject *parent)
: QObject(parent), picsPath(SettingsCache::instance().getPicsPath()),
customPicsPath(SettingsCache::instance().getCustomPicsPath())
{
// Hook up signals to settings
connect(&SettingsCache::instance(), &SettingsCache::picsPathChanged, this,
&CardPictureLoaderLocal::picsPathChanged);
refreshIndex();
refreshTimer = new QTimer(this);
connect(refreshTimer, &QTimer::timeout, this, &CardPictureLoaderLocal::refreshIndex);
refreshTimer->start(REFRESH_INTERVAL_MS);
}
void CardPictureLoaderLocal::refreshIndex()
{
customFolderIndex.clear();
QDirIterator it(customPicsPath, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
// Recursively check all subdirectories of the CUSTOM folder
while (it.hasNext()) {
QString thisPath(it.next());
QFileInfo thisFileInfo(thisPath);
if (thisFileInfo.isFile()) {
// We don't know which name is the correctedName because there might be '.'s in the cardName.
// Just add all possibilities to be sure.
customFolderIndex.insert(thisFileInfo.baseName(), thisFileInfo.absoluteFilePath());
customFolderIndex.insert(thisFileInfo.completeBaseName(), thisFileInfo.absoluteFilePath());
}
}
qCDebug(CardPictureLoaderLocalLog) << "Finished indexing local image folder CUSTOM; map now has"
<< customFolderIndex.size() << "entries.";
}
/**
* Tries to load the card image from the local images.
*
* @param toLoad The card to load
* @return The loaded image, or an empty QImage if loading failed.
*/
QImage CardPictureLoaderLocal::tryLoad(const ExactCard &toLoad) const
{
PrintingInfo setInstance = toLoad.getPrinting();
QString cardName = toLoad.getName();
QString correctedCardName = toLoad.getInfo().getCorrectedName();
QString setName, collectorNumber, providerId;
if (setInstance.getSet()) {
setName = setInstance.getSet()->getCorrectedShortName();
collectorNumber = setInstance.getProperty("num");
providerId = setInstance.getUuid();
}
qCDebug(CardPictureLoaderLocalLog).nospace()
<< "[card: " << cardName << " set: " << setName << "]: Attempting to load picture from local";
return tryLoadCardImageFromDisk(setName, correctedCardName, collectorNumber, providerId);
}
QImage CardPictureLoaderLocal::tryLoadCardImageFromDisk(const QString &setName,
const QString &correctedCardName,
const QString &collectorNumber,
const QString &providerId) const
{
QImage image;
QImageReader imgReader;
imgReader.setDecideFormatFromContent(true);
// Most-to-least specific, these will fall through in order.
QStringList nameVariants;
// cardName_providerId
if (!providerId.isEmpty()) {
nameVariants << QString("%1-%2").arg(correctedCardName, providerId)
<< QString("%1_%2").arg(correctedCardName, providerId);
}
// cardName_setName_collectorNumber & setName-collectorNumber-cardName
if (!setName.isEmpty() && !collectorNumber.isEmpty()) {
nameVariants << QString("%1_%2_%3").arg(correctedCardName, setName, collectorNumber)
<< QString("%1-%2-%3").arg(setName, collectorNumber, correctedCardName);
}
// cardName_setName
if (!setName.isEmpty()) {
nameVariants << QString("%1_%2").arg(correctedCardName, setName)
<< QString("%1-%2").arg(setName, correctedCardName);
}
// cardName
nameVariants << correctedCardName;
for (const QString &nameVariant : nameVariants) {
if (nameVariant.isEmpty()) {
continue;
}
QStringList candidatePaths = customFolderIndex.values(nameVariant);
if (!setName.isEmpty()) {
candidatePaths << picsPath + "/" + setName + "/" + nameVariant;
candidatePaths << picsPath + "/downloadedPics/" + setName + "/" + nameVariant;
}
for (const QString &path : candidatePaths) {
QFileInfo fileInfo(path);
QDir dir = fileInfo.dir();
QString baseName = fileInfo.fileName();
if (!dir.exists()) {
continue;
}
QStringList files = dir.entryList(QDir::Files);
for (const QString &file : files) {
if (!file.startsWith(baseName)) {
continue;
}
QString fullPath = dir.filePath(file);
imgReader.setFileName(fullPath);
if (imgReader.read(&image)) {
qCDebug(CardPictureLoaderLocalLog).nospace()
<< "[card: " << correctedCardName << " set: " << setName << "] Found picture at: " << fullPath;
return image;
}
}
}
}
qCDebug(CardPictureLoaderLocalLog).nospace()
<< "[card: " << correctedCardName << " set: " << setName << "]: Picture NOT found on disk.";
return QImage();
}
void CardPictureLoaderLocal::picsPathChanged()
{
picsPath = SettingsCache::instance().getPicsPath();
customPicsPath = SettingsCache::instance().getCustomPicsPath();
}

View file

@ -0,0 +1,47 @@
/**
* @file card_picture_loader_local.h
* @ingroup PictureLoader
* @brief TODO: Document this.
*/
#ifndef PICTURE_LOADER_LOCAL_H
#define PICTURE_LOADER_LOCAL_H
#include <QLoggingCategory>
#include <QObject>
#include <QTimer>
#include <libcockatrice/card/card_printing/exact_card.h>
inline Q_LOGGING_CATEGORY(CardPictureLoaderLocalLog, "card_picture_loader.local");
/**
* Handles searching for and loading card images from the local pics and custom image folders.
* This class maintains an index of the CUSTOM folder, to avoid repeatedly searching the entire directory.
*/
class CardPictureLoaderLocal : public QObject
{
Q_OBJECT
public:
explicit CardPictureLoaderLocal(QObject *parent);
QImage tryLoad(const ExactCard &toLoad) const;
private:
QString picsPath, customPicsPath;
QMultiHash<QString, QString> customFolderIndex; // multimap of cardName to picPaths
QTimer *refreshTimer;
void refreshIndex();
QImage tryLoadCardImageFromDisk(const QString &setName,
const QString &correctedCardName,
const QString &collectorNumber,
const QString &providerId) const;
private slots:
void picsPathChanged();
};
#endif // PICTURE_LOADER_LOCAL_H

View file

@ -0,0 +1,31 @@
#include "card_picture_loader_request_status_display_widget.h"
CardPictureLoaderRequestStatusDisplayWidget::CardPictureLoaderRequestStatusDisplayWidget(QWidget *parent,
const QUrl &_url,
const ExactCard &card,
const QString &setName)
: QWidget(parent)
{
layout = new QHBoxLayout(this);
name = new QLabel(this);
name->setText(card.getName());
setShortname = new QLabel(this);
setShortname->setText(setName);
providerId = new QLabel(this);
providerId->setText(card.getPrinting().getUuid());
layout->addWidget(name);
layout->addWidget(setShortname);
layout->addWidget(providerId);
startTime = new QLabel(QDateTime::currentDateTime().toString(), this);
elapsedTime = new QLabel("0", this);
finished = new QLabel("False", this);
url = new QLabel(_url.toString(), this);
layout->addWidget(startTime);
layout->addWidget(elapsedTime);
layout->addWidget(finished);
layout->addWidget(url);
}

View file

@ -0,0 +1,74 @@
/**
* @file card_picture_loader_request_status_display_widget.h
* @ingroup PictureLoader
* @brief TODO: Document this.
*/
#ifndef PICTURE_LOADER_REQUEST_STATUS_DISPLAY_WIDGET_H
#define PICTURE_LOADER_REQUEST_STATUS_DISPLAY_WIDGET_H
#include "card_picture_loader_worker_work.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QWidget>
class CardPictureLoaderRequestStatusDisplayWidget : public QWidget
{
Q_OBJECT
public:
CardPictureLoaderRequestStatusDisplayWidget(QWidget *parent,
const QUrl &url,
const ExactCard &card,
const QString &setName);
void setFinished()
{
finished->setText("True");
update();
repaint();
}
bool getFinished() const
{
return finished->text() == "True";
}
void setElapsedTime(const QString &_elapsedTime) const
{
elapsedTime->setText(_elapsedTime);
}
int queryElapsedSeconds()
{
if (!finished) {
int elapsedSeconds = QDateTime::fromString(startTime->text()).secsTo(QDateTime::currentDateTime());
elapsedTime->setText(QString::number(elapsedSeconds));
update();
repaint();
return elapsedSeconds;
}
return elapsedTime->text().toInt();
}
QString getStartTime() const
{
return startTime->text();
}
QString getUrl() const
{
return url->text();
}
private:
QHBoxLayout *layout;
QLabel *name;
QLabel *setShortname;
QLabel *providerId;
QLabel *startTime;
QLabel *elapsedTime;
QLabel *finished;
QLabel *url;
};
#endif // PICTURE_LOADER_REQUEST_STATUS_DISPLAY_WIDGET_H

View file

@ -0,0 +1,57 @@
#include "card_picture_loader_status_bar.h"
#include "card_picture_loader_request_status_display_widget.h"
CardPictureLoaderStatusBar::CardPictureLoaderStatusBar(QWidget *parent) : QWidget(parent)
{
layout = new QHBoxLayout(this);
progressBar = new QProgressBar(this);
progressBar->setMaximum(0);
progressBar->setFormat("%v/%m");
layout->addWidget(progressBar);
loadLog = new SettingsButtonWidget(this);
layout->addWidget(loadLog);
cleaner = new QTimer(this);
cleaner->setInterval(1000);
connect(cleaner, &QTimer::timeout, this, &CardPictureLoaderStatusBar::cleanOldEntries);
cleaner->start();
setLayout(layout);
}
void CardPictureLoaderStatusBar::cleanOldEntries()
{
if (!loadLog || !loadLog->popup) {
return;
}
for (CardPictureLoaderRequestStatusDisplayWidget *statusDisplayWidget :
loadLog->popup->findChildren<CardPictureLoaderRequestStatusDisplayWidget *>()) {
statusDisplayWidget->queryElapsedSeconds();
if (statusDisplayWidget->getFinished() &&
QDateTime::fromString(statusDisplayWidget->getStartTime()).secsTo(QDateTime::currentDateTime()) > 10) {
loadLog->removeSettingsWidget(statusDisplayWidget);
progressBar->setMaximum(progressBar->maximum() - 1);
progressBar->setValue(progressBar->value() - 1);
}
}
}
void CardPictureLoaderStatusBar::addQueuedImageLoad(const QUrl &url, const ExactCard &card, const QString &setName)
{
loadLog->addSettingsWidget(new CardPictureLoaderRequestStatusDisplayWidget(loadLog, url, card, setName));
progressBar->setMaximum(progressBar->maximum() + 1);
}
void CardPictureLoaderStatusBar::addSuccessfulImageLoad(const QUrl &url)
{
progressBar->setValue(progressBar->value() + 1);
for (CardPictureLoaderRequestStatusDisplayWidget *statusDisplayWidget :
loadLog->popup->findChildren<CardPictureLoaderRequestStatusDisplayWidget *>()) {
if (statusDisplayWidget->getUrl() == url.toString()) {
statusDisplayWidget->queryElapsedSeconds();
statusDisplayWidget->setFinished();
}
}
}

View file

@ -0,0 +1,35 @@
/**
* @file card_picture_loader_status_bar.h
* @ingroup PictureLoader
* @brief TODO: Document this.
*/
#ifndef PICTURE_LOADER_STATUS_BAR_H
#define PICTURE_LOADER_STATUS_BAR_H
#include "../../interface/widgets/quick_settings/settings_button_widget.h"
#include "card_picture_loader_worker_work.h"
#include <QHBoxLayout>
#include <QProgressBar>
#include <QWidget>
class CardPictureLoaderStatusBar : public QWidget
{
Q_OBJECT
public:
explicit CardPictureLoaderStatusBar(QWidget *parent);
public slots:
void addQueuedImageLoad(const QUrl &url, const ExactCard &card, const QString &setName);
void addSuccessfulImageLoad(const QUrl &url);
void cleanOldEntries();
private:
QHBoxLayout *layout;
QProgressBar *progressBar;
SettingsButtonWidget *loadLog;
QTimer *cleaner;
};
#endif // PICTURE_LOADER_STATUS_BAR_H

View file

@ -0,0 +1,238 @@
#include "card_picture_loader_worker.h"
#include "card_picture_loader_local.h"
#include "card_picture_loader_worker_work.h"
#include <QDirIterator>
#include <QMovie>
#include <QNetworkDiskCache>
#include <QNetworkReply>
#include <QThread>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/settings/cache_settings.h>
#include <utility>
static constexpr int MAX_REQUESTS_PER_SEC = 10;
CardPictureLoaderWorker::CardPictureLoaderWorker()
: QObject(nullptr), picDownload(SettingsCache::instance().getPicDownload()), requestQuota(MAX_REQUESTS_PER_SEC)
{
networkManager = new QNetworkAccessManager(this);
// We need a timeout to ensure requests don't hang indefinitely in case of
// cache corruption, see related Qt bug: https://bugreports.qt.io/browse/QTBUG-111397
// Use Qt's default timeout (30s, as of 2023-02-22)
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
networkManager->setTransferTimeout();
#endif
cache = new QNetworkDiskCache(this);
cache->setCacheDirectory(SettingsCache::instance().getNetworkCachePath());
cache->setMaximumCacheSize(1024L * 1024L *
static_cast<qint64>(SettingsCache::instance().getNetworkCacheSizeInMB()));
// Note: the settings is in MB, but QNetworkDiskCache uses bytes
connect(&SettingsCache::instance(), &SettingsCache::networkCacheSizeChanged, this,
[this](int newSizeInMB) { cache->setMaximumCacheSize(1024L * 1024L * static_cast<qint64>(newSizeInMB)); });
networkManager->setCache(cache);
// Use a ManualRedirectPolicy since we keep track of redirects in picDownloadFinished
// We can't use NoLessSafeRedirectPolicy because it is not applied with AlwaysCache
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
cacheFilePath = SettingsCache::instance().getRedirectCachePath() + REDIRECT_CACHE_FILENAME;
loadRedirectCache();
cleanStaleEntries();
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this,
&CardPictureLoaderWorker::saveRedirectCache);
localLoader = new CardPictureLoaderLocal(this);
pictureLoaderThread = new QThread;
pictureLoaderThread->start(QThread::LowPriority);
moveToThread(pictureLoaderThread);
connect(this, &CardPictureLoaderWorker::imageLoadEnqueued, this, &CardPictureLoaderWorker::handleImageLoadEnqueued);
connect(&requestTimer, &QTimer::timeout, this, &CardPictureLoaderWorker::resetRequestQuota);
requestTimer.setInterval(1000);
requestTimer.start();
}
CardPictureLoaderWorker::~CardPictureLoaderWorker()
{
saveRedirectCache();
pictureLoaderThread->deleteLater();
}
void CardPictureLoaderWorker::queueRequest(const QUrl &url, CardPictureLoaderWorkerWork *worker)
{
QUrl cachedRedirect = getCachedRedirect(url);
if (!cachedRedirect.isEmpty()) {
queueRequest(cachedRedirect, worker);
} else if (cache->metaData(url).isValid()) {
// If we hit a cached url, we get to make the request for free, since it won't contribute towards the rate-limit
makeRequest(url, worker);
} else {
requestLoadQueue.append(qMakePair(url, worker));
emit imageRequestQueued(url, worker->cardToDownload.getCard(), worker->cardToDownload.getSetName());
processQueuedRequests();
}
}
QNetworkReply *CardPictureLoaderWorker::makeRequest(const QUrl &url, CardPictureLoaderWorkerWork *worker)
{
// Check for cached redirects
QUrl cachedRedirect = getCachedRedirect(url);
if (!cachedRedirect.isEmpty()) {
emit imageRequestSucceeded(url);
return makeRequest(cachedRedirect, worker);
}
QNetworkRequest req(url);
if (!picDownload) {
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
}
QNetworkReply *reply = networkManager->get(req);
// Connect reply handling
connect(reply, &QNetworkReply::finished, worker, [reply, worker] { worker->handleNetworkReply(reply); });
return reply;
}
void CardPictureLoaderWorker::resetRequestQuota()
{
requestQuota = MAX_REQUESTS_PER_SEC;
processQueuedRequests();
}
/**
* Keeps processing requests from the queue until it is empty or until the quota runs out.
*/
void CardPictureLoaderWorker::processQueuedRequests()
{
while (requestQuota > 0 && processSingleRequest()) {
--requestQuota;
}
}
/**
* Immediately processes a single queued request. No-ops if the load queue is empty
* @return If a request was processed
*/
bool CardPictureLoaderWorker::processSingleRequest()
{
if (!requestLoadQueue.isEmpty()) {
auto request = requestLoadQueue.takeFirst();
makeRequest(request.first, request.second);
return true;
}
return false;
}
void CardPictureLoaderWorker::enqueueImageLoad(const ExactCard &card)
{
// Send call through a connection to ensure the handling is run on the pictureLoader thread
emit imageLoadEnqueued(card);
}
void CardPictureLoaderWorker::handleImageLoadEnqueued(const ExactCard &card)
{
// deduplicate loads for the same card
if (currentlyLoading.contains(card.getPixmapCacheKey())) {
qCDebug(CardPictureLoaderWorkerLog())
<< "Skipping enqueued" << card.getName() << "because it's already being loaded";
return;
}
currentlyLoading.insert(card.getPixmapCacheKey());
// try to load image from local first
QImage image = localLoader->tryLoad(card);
if (!image.isNull()) {
handleImageLoaded(card, image);
} else {
// queue up to load image from remote only after local loading failed
new CardPictureLoaderWorkerWork(this, card);
}
}
/**
* Called when image loading is done. Failures are indicated by an empty QImage.
*/
void CardPictureLoaderWorker::handleImageLoaded(const ExactCard &card, const QImage &image)
{
currentlyLoading.remove(card.getPixmapCacheKey());
emit imageLoaded(card, image);
}
void CardPictureLoaderWorker::cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl)
{
redirectCache[originalUrl] = qMakePair(redirectUrl, QDateTime::currentDateTimeUtc());
// saveRedirectCache();
}
void CardPictureLoaderWorker::removedCachedUrl(const QUrl &url)
{
networkManager->cache()->remove(url);
}
QUrl CardPictureLoaderWorker::getCachedRedirect(const QUrl &originalUrl) const
{
if (redirectCache.contains(originalUrl)) {
return redirectCache[originalUrl].first;
}
return {};
}
void CardPictureLoaderWorker::loadRedirectCache()
{
QSettings settings(cacheFilePath, QSettings::IniFormat);
redirectCache.clear();
int size = settings.beginReadArray(REDIRECT_HEADER_NAME);
for (int i = 0; i < size; ++i) {
settings.setArrayIndex(i);
QUrl originalUrl = settings.value(REDIRECT_ORIGINAL_URL).toUrl();
QUrl redirectUrl = settings.value(REDIRECT_URL).toUrl();
QDateTime timestamp = settings.value(REDIRECT_TIMESTAMP).toDateTime();
if (originalUrl.isValid() && redirectUrl.isValid()) {
redirectCache[originalUrl] = qMakePair(redirectUrl, timestamp);
}
}
settings.endArray();
}
void CardPictureLoaderWorker::saveRedirectCache() const
{
QSettings settings(cacheFilePath, QSettings::IniFormat);
settings.beginWriteArray(REDIRECT_HEADER_NAME, static_cast<int>(redirectCache.size()));
int index = 0;
for (auto it = redirectCache.cbegin(); it != redirectCache.cend(); ++it) {
settings.setArrayIndex(index++);
settings.setValue(REDIRECT_ORIGINAL_URL, it.key());
settings.setValue(REDIRECT_URL, it.value().first);
settings.setValue(REDIRECT_TIMESTAMP, it.value().second);
}
settings.endArray();
}
void CardPictureLoaderWorker::cleanStaleEntries()
{
QDateTime now = QDateTime::currentDateTimeUtc();
auto it = redirectCache.begin();
while (it != redirectCache.end()) {
if (it.value().second.addDays(SettingsCache::instance().getRedirectCacheTtl()) < now) {
it = redirectCache.erase(it); // Remove stale entry
} else {
++it;
}
}
}
void CardPictureLoaderWorker::clearNetworkCache()
{
networkManager->cache()->clear();
redirectCache.clear();
}

View file

@ -0,0 +1,84 @@
/**
* @file card_picture_loader_worker.h
* @ingroup PictureLoader
* @brief TODO: Document this.
*/
#ifndef PICTURE_LOADER_WORKER_H
#define PICTURE_LOADER_WORKER_H
#include "card_picture_loader_local.h"
#include "card_picture_loader_worker_work.h"
#include "card_picture_to_load.h"
#include <QLoggingCategory>
#include <QMutex>
#include <QNetworkAccessManager>
#include <QNetworkDiskCache>
#include <QObject>
#include <QQueue>
#include <QTimer>
#include <libcockatrice/card/card_database/card_database.h>
#include <libcockatrice/card/card_info.h>
#define REDIRECT_HEADER_NAME "redirects"
#define REDIRECT_ORIGINAL_URL "original"
#define REDIRECT_URL "redirect"
#define REDIRECT_TIMESTAMP "timestamp"
#define REDIRECT_CACHE_FILENAME "cache.ini"
inline Q_LOGGING_CATEGORY(CardPictureLoaderWorkerLog, "card_picture_loader.worker");
class CardPictureLoaderWorkerWork;
class CardPictureLoaderWorker : public QObject
{
Q_OBJECT
public:
explicit CardPictureLoaderWorker();
~CardPictureLoaderWorker() override;
void enqueueImageLoad(const ExactCard &card); // Starts a thread for the image to be loaded
void queueRequest(const QUrl &url, CardPictureLoaderWorkerWork *worker); // Queues network requests for load threads
void clearNetworkCache();
public slots:
QNetworkReply *makeRequest(const QUrl &url, CardPictureLoaderWorkerWork *workThread);
void processQueuedRequests();
bool processSingleRequest();
void handleImageLoaded(const ExactCard &card, const QImage &image);
void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl);
void removedCachedUrl(const QUrl &url);
private:
QThread *pictureLoaderThread;
QNetworkAccessManager *networkManager;
QNetworkDiskCache *cache;
QHash<QUrl, QPair<QUrl, QDateTime>> redirectCache; // Stores redirect and timestamp
QString cacheFilePath; // Path to persistent storage
static constexpr int CacheTTLInDays = 30; // TODO: Make user configurable
bool picDownload;
QQueue<QPair<QUrl, CardPictureLoaderWorkerWork *>> requestLoadQueue;
int requestQuota;
QTimer requestTimer; // Timer for refreshing request quota
CardPictureLoaderLocal *localLoader;
QSet<QString> currentlyLoading; // for deduplication purposes. Contains pixmapCacheKey
QUrl getCachedRedirect(const QUrl &originalUrl) const;
void loadRedirectCache();
void saveRedirectCache() const;
void cleanStaleEntries();
private slots:
void resetRequestQuota();
void handleImageLoadEnqueued(const ExactCard &card);
signals:
void imageLoadEnqueued(const ExactCard &card);
void imageLoaded(const ExactCard &card, const QImage &image);
void imageRequestQueued(const QUrl &url, const ExactCard &card, const QString &setName);
void imageRequestSucceeded(const QUrl &url);
};
#endif // PICTURE_LOADER_WORKER_H

View file

@ -0,0 +1,223 @@
#include "card_picture_loader_worker_work.h"
#include "card_picture_loader_worker.h"
#include <QBuffer>
#include <QDirIterator>
#include <QLoggingCategory>
#include <QMovie>
#include <QNetworkDiskCache>
#include <QNetworkReply>
#include <QThread>
#include <QThreadPool>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/settings/cache_settings.h>
// Card back returned by gatherer when card is not found
static const QStringList MD5_BLACKLIST = {"db0c48db407a907c16ade38de048a441"};
CardPictureLoaderWorkerWork::CardPictureLoaderWorkerWork(const CardPictureLoaderWorker *worker, const ExactCard &toLoad)
: QObject(nullptr), cardToDownload(CardPictureToLoad(toLoad)),
picDownload(SettingsCache::instance().getPicDownload())
{
// Hook up signals to the orchestrator
connect(this, &CardPictureLoaderWorkerWork::requestImageDownload, worker, &CardPictureLoaderWorker::queueRequest);
connect(this, &CardPictureLoaderWorkerWork::urlRedirected, worker, &CardPictureLoaderWorker::cacheRedirect);
connect(this, &CardPictureLoaderWorkerWork::cachedUrlInvalidated, worker,
&CardPictureLoaderWorker::removedCachedUrl);
connect(this, &CardPictureLoaderWorkerWork::imageLoaded, worker, &CardPictureLoaderWorker::handleImageLoaded);
connect(this, &CardPictureLoaderWorkerWork::requestSucceeded, worker,
&CardPictureLoaderWorker::imageRequestSucceeded);
// Hook up signals to settings
connect(&SettingsCache::instance(), SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
startNextPicDownload();
}
void CardPictureLoaderWorkerWork::startNextPicDownload()
{
QString picUrl = cardToDownload.getCurrentUrl();
if (picUrl.isEmpty()) {
picDownloadFailed();
} else {
QUrl url(picUrl);
qCDebug(CardPictureLoaderWorkerWorkLog).nospace()
<< "PictureLoader: [card: " << cardToDownload.getCard().getInfo().getCorrectedName()
<< " set: " << cardToDownload.getSetName() << "]: Trying to fetch picture from url "
<< url.toDisplayString();
emit requestImageDownload(url, this);
}
}
/**
* Starts another pic download using the next possible url combination for the card.
* If all possibilities are exhausted, then concludes the image loading with an empty QImage.
*/
void CardPictureLoaderWorkerWork::picDownloadFailed()
{
/* Take advantage of short-circuiting here to call the nextUrl until one
is not available. Only once nextUrl evaluates to false will this move
on to nextSet. If the Urls for a particular card are empty, this will
effectively go through the sets for that card. */
if (cardToDownload.nextUrl() || cardToDownload.nextSet()) {
startNextPicDownload();
} else {
qCWarning(CardPictureLoaderWorkerWorkLog).nospace()
<< "PictureLoader: [card: " << cardToDownload.getCard().getInfo().getCorrectedName()
<< " set: " << cardToDownload.getSetName() << "]: Picture NOT found, "
<< (picDownload ? "download failed" : "downloads disabled")
<< ", no more url combinations to try: BAILING OUT";
concludeImageLoad(QImage());
}
}
/**
*
* @param reply The reply. Takes ownership of the object
*/
void CardPictureLoaderWorkerWork::handleNetworkReply(QNetworkReply *reply)
{
QVariant redirectTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (redirectTarget.isValid()) {
QUrl url = reply->request().url();
QUrl redirectUrl = redirectTarget.toUrl();
if (redirectUrl.isRelative()) {
redirectUrl = url.resolved(redirectUrl);
}
emit urlRedirected(url, redirectUrl);
}
if (reply->error()) {
handleFailedReply(reply);
} else {
handleSuccessfulReply(reply);
emit requestSucceeded(reply->url());
}
reply->deleteLater();
}
static bool imageIsBlackListed(const QByteArray &picData)
{
QString md5sum = QCryptographicHash::hash(picData, QCryptographicHash::Md5).toHex();
return MD5_BLACKLIST.contains(md5sum);
}
void CardPictureLoaderWorkerWork::handleFailedReply(const QNetworkReply *reply)
{
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 429) {
qCWarning(CardPictureLoaderWorkerWorkLog) << "Too many requests.";
} else {
bool isFromCache = reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
if (isFromCache) {
qCDebug(CardPictureLoaderWorkerWorkLog).nospace()
<< "PictureLoader: [card: " << cardToDownload.getCard().getName()
<< " set: " << cardToDownload.getSetName() << "]: Removing corrupted cache file for url "
<< reply->url().toDisplayString() << " and retrying (" << reply->errorString() << ")";
emit cachedUrlInvalidated(reply->url());
emit requestImageDownload(reply->url(), this);
} else {
qCDebug(CardPictureLoaderWorkerWorkLog).nospace()
<< "PictureLoader: [card: " << cardToDownload.getCard().getName()
<< " set: " << cardToDownload.getSetName() << "]: " << (picDownload ? "Download" : "Cache search")
<< " failed for url " << reply->url().toDisplayString() << " (" << reply->errorString() << ")";
picDownloadFailed();
}
}
}
void CardPictureLoaderWorkerWork::handleSuccessfulReply(QNetworkReply *reply)
{
bool isFromCache = reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
// List of status codes from https://doc.qt.io/qt-6/qnetworkreply.html#redirected
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305 || statusCode == 307 ||
statusCode == 308) {
QUrl redirectUrl = reply->header(QNetworkRequest::LocationHeader).toUrl();
qCDebug(CardPictureLoaderWorkerWorkLog).nospace()
<< "PictureLoader: [card: " << cardToDownload.getCard().getName() << " set: " << cardToDownload.getSetName()
<< "]: following " << (isFromCache ? "cached redirect" : "redirect") << " to "
<< redirectUrl.toDisplayString();
emit requestImageDownload(redirectUrl, this);
return;
}
// peek is used to keep the data in the buffer for use by QImageReader
const QByteArray &picData = reply->peek(reply->size());
if (imageIsBlackListed(picData)) {
qCDebug(CardPictureLoaderWorkerWorkLog).nospace()
<< "PictureLoader: [card: " << cardToDownload.getCard().getName() << " set: " << cardToDownload.getSetName()
<< "]: Picture found, but blacklisted, will consider it as not found";
picDownloadFailed();
return;
}
QImage image = tryLoadImageFromReply(reply);
if (image.isNull()) {
qCDebug(CardPictureLoaderWorkerWorkLog).nospace()
<< "PictureLoader: [card: " << cardToDownload.getCard().getName() << " set: " << cardToDownload.getSetName()
<< "]: Possible " << (isFromCache ? "cached" : "downloaded") << " picture at "
<< reply->url().toDisplayString() << " could not be loaded: " << reply->errorString();
picDownloadFailed();
} else {
qCDebug(CardPictureLoaderWorkerWorkLog).nospace()
<< "PictureLoader: [card: " << cardToDownload.getCard().getName() << " set: " << cardToDownload.getSetName()
<< "]: Image successfully " << (isFromCache ? "loaded from cached" : "downloaded from") << " url "
<< reply->url().toDisplayString();
concludeImageLoad(image);
}
}
/**
* @param reply The reply to load the image from
* @return The loaded image, or an empty QImage if loading failed
*/
QImage CardPictureLoaderWorkerWork::tryLoadImageFromReply(QNetworkReply *reply)
{
static constexpr int riffHeaderSize = 12; // RIFF_HEADER_SIZE from webp/format_constants.h
auto replyHeader = reply->peek(riffHeaderSize);
if (replyHeader.startsWith("RIFF") && replyHeader.endsWith("WEBP")) {
auto imgBuf = QBuffer(this);
imgBuf.setData(reply->readAll());
auto movie = QMovie(&imgBuf);
movie.start();
movie.stop();
return movie.currentImage();
}
QImageReader imgReader;
imgReader.setDecideFormatFromContent(true);
imgReader.setDevice(reply);
return imgReader.read();
}
/**
* Call this method when the image has finished being loaded.
* @param image The image that was loaded. Empty QImage indicates failure.
*/
void CardPictureLoaderWorkerWork::concludeImageLoad(const QImage &image)
{
emit imageLoaded(cardToDownload.getCard(), image);
deleteLater();
}
void CardPictureLoaderWorkerWork::picDownloadChanged()
{
picDownload = SettingsCache::instance().getPicDownload();
}

View file

@ -0,0 +1,72 @@
/**
* @file picture_loader_worker_work.h
* @ingroup PictureLoader
* @brief TODO: Document this.
*/
#ifndef PICTURE_LOADER_WORKER_WORK_H
#define PICTURE_LOADER_WORKER_WORK_H
#include "card_picture_loader_worker.h"
#include "card_picture_to_load.h"
#include <QLoggingCategory>
#include <QMutex>
#include <QNetworkAccessManager>
#include <QObject>
#include <QThread>
#include <libcockatrice/card/card_database/card_database.h>
#define REDIRECT_HEADER_NAME "redirects"
#define REDIRECT_ORIGINAL_URL "original"
#define REDIRECT_URL "redirect"
#define REDIRECT_TIMESTAMP "timestamp"
#define REDIRECT_CACHE_FILENAME "cache.ini"
inline Q_LOGGING_CATEGORY(CardPictureLoaderWorkerWorkLog, "card_picture_loader.worker");
class CardPictureLoaderWorker;
class CardPictureLoaderWorkerWork : public QObject
{
Q_OBJECT
public:
explicit CardPictureLoaderWorkerWork(const CardPictureLoaderWorker *worker, const ExactCard &toLoad);
CardPictureToLoad cardToDownload;
public slots:
void handleNetworkReply(QNetworkReply *reply);
private:
bool picDownload;
void startNextPicDownload();
void picDownloadFailed();
void handleFailedReply(const QNetworkReply *reply);
void handleSuccessfulReply(QNetworkReply *reply);
QImage tryLoadImageFromReply(QNetworkReply *reply);
void concludeImageLoad(const QImage &image);
private slots:
void picDownloadChanged();
signals:
/**
* Emitted when this worker has successfully loaded the image or has exhausted all attempts at loading the image.
* Failures are represented by an empty QImage.
* Note that this object will delete itself as this signal is emitted.
*/
void imageLoaded(const ExactCard &card, const QImage &image);
/**
* Emitted when a request did not return a 400 or 500 response
*/
void requestSucceeded(const QUrl &url);
void requestImageDownload(const QUrl &url, CardPictureLoaderWorkerWork *instance);
void urlRedirected(const QUrl &originalUrl, const QUrl &redirectUrl);
void cachedUrlInvalidated(const QUrl &url);
};
#endif // PICTURE_LOADER_WORKER_WORK_H

View file

@ -0,0 +1,290 @@
#include "card_picture_to_load.h"
#include <QCoreApplication>
#include <QDate>
#include <QRegularExpression>
#include <QUrl>
#include <algorithm>
#include <libcockatrice/card/card_set/card_set_comparator.h>
#include <libcockatrice/settings/cache_settings.h>
CardPictureToLoad::CardPictureToLoad(const ExactCard &_card)
: card(_card), urlTemplates(SettingsCache::instance().downloads().getAllURLs())
{
if (card) {
sortedSets = extractSetsSorted(card);
// The first time called, nextSet will also populate the Urls for the first set.
nextSet();
}
}
/**
* Extracts a list of all the sets from the card, sorted in priority order.
* If the card does not contain any sets, then a dummy set will be inserted into the list.
*
* @return A list of sets. Will not be empty.
*/
QList<CardSetPtr> CardPictureToLoad::extractSetsSorted(const ExactCard &card)
{
QList<CardSetPtr> sortedSets;
for (const auto &printings : card.getInfo().getSets()) {
for (const auto &printing : printings) {
sortedSets << printing.getSet();
}
}
if (sortedSets.empty()) {
sortedSets << CardSet::newInstance("", "", "", QDate());
}
std::sort(sortedSets.begin(), sortedSets.end(), SetPriorityComparator());
// If the user hasn't disabled arts other than their personal preference...
if (!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) {
// If the pixmapCacheKey corresponds to a specific set, we have to try to load it first.
qsizetype setIndex = sortedSets.indexOf(card.getPrinting().getSet());
if (setIndex > 0) { // we don't need to move the set if it's already first
CardSetPtr setForCardProviderID = sortedSets.takeAt(setIndex);
sortedSets.prepend(setForCardProviderID);
}
}
return sortedSets;
}
/**
* Finds the PrintingInfo corresponding to the exactCards's card name that belongs to a given set and has the
* exactCards's providerId.
* If the set name is in the CardInfo, but no printings in that set match the card's providerId, then the first
* PrintingInfo for the set is returned.
*
* This method only exists to maintain existing behavior.
* TODO: check if going through all sets is still necessary after the ExactCard refactor.
*
* @param card The card to look in
* @param setName The set's short name
* @return A PrintingInfo, or a default-constructed PrintingInfo if the set name is not in the CardInfo.
*/
static PrintingInfo findPrintingForSet(const ExactCard &card, const QString &setName)
{
SetToPrintingsMap setsToPrintings = card.getInfo().getSets();
if (!setsToPrintings.contains(setName))
return PrintingInfo();
for (const auto &printing : setsToPrintings[setName]) {
if (printing.getUuid() == card.getPrinting().getUuid()) {
return printing;
}
}
return setsToPrintings[setName][0];
}
void CardPictureToLoad::populateSetUrls()
{
/* currentSetUrls is a list, populated each time a new set is requested for a particular card
and Urls are removed from it as a download is attempted from each one. Custom Urls for
a set are given higher priority, so should be placed first in the list. */
currentSetUrls.clear();
if (card && currentSet) {
QString setCustomURL = findPrintingForSet(card, currentSet->getShortName()).getProperty("picurl");
if (!setCustomURL.isEmpty()) {
currentSetUrls.append(setCustomURL);
}
}
for (const QString &urlTemplate : urlTemplates) {
QString transformedUrl = transformUrl(urlTemplate);
if (!transformedUrl.isEmpty()) {
currentSetUrls.append(transformedUrl);
}
}
/* Call nextUrl to make sure currentUrl is up-to-date
but we don't need the result here. */
(void)nextUrl();
}
/**
* Advances the currentSet to the next set in the list. Then repopulates the url list with the urls from that set.
* If we are already at the end of the list, then currentSet is set to empty.
* @return If we are already at the end of the list
*/
bool CardPictureToLoad::nextSet()
{
if (!sortedSets.isEmpty()) {
currentSet = sortedSets.takeFirst();
populateSetUrls();
return true;
}
currentSet = {};
return false;
}
/**
* Advances the currentUrl to the next url in the list.
* If we are already at the end of the list, then currentUrl is set to empty.
* @return If we are already at the end of the list
*/
bool CardPictureToLoad::nextUrl()
{
if (!currentSetUrls.isEmpty()) {
currentUrl = currentSetUrls.takeFirst();
return true;
}
currentUrl = QString();
return false;
}
QString CardPictureToLoad::getSetName() const
{
if (currentSet) {
return currentSet->getCorrectedShortName();
} else {
return QString();
}
}
static int parse(const QString &urlTemplate,
const QString &propType,
const QString &cardName,
const QString &setName,
std::function<QString(const QString &)> getProperty,
QMap<QString, QString> &transformMap)
{
static const QRegularExpression rxFillWith("^(.+)_fill_with_(.+)$");
static const QRegularExpression rxSubStr("^(.+)_substr_(\\d+)_(\\d+)$");
const QRegularExpression rxCardProp("!" + propType + ":([^!]+)!");
auto matches = rxCardProp.globalMatch(urlTemplate);
while (matches.hasNext()) {
auto match = matches.next();
QString templatePropertyName = match.captured(1);
auto fillMatch = rxFillWith.match(templatePropertyName);
QString cardPropertyName;
QString fillWith;
int subStrPos = 0;
int subStrLen = -1;
if (fillMatch.hasMatch()) {
cardPropertyName = fillMatch.captured(1);
fillWith = fillMatch.captured(2);
} else {
fillWith = QString();
auto subStrMatch = rxSubStr.match(templatePropertyName);
if (subStrMatch.hasMatch()) {
cardPropertyName = subStrMatch.captured(1);
subStrPos = subStrMatch.captured(2).toInt();
subStrLen = subStrMatch.captured(3).toInt();
} else {
cardPropertyName = templatePropertyName;
}
}
QString propertyValue = getProperty(cardPropertyName);
if (propertyValue.isEmpty()) {
qCDebug(CardPictureToLoadLog).nospace()
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested " << propType
<< "property (" << cardPropertyName << ") for Url template (" << urlTemplate << ") is not available";
return 1;
} else {
int propLength = propertyValue.length();
if (subStrLen > 0) {
if (subStrPos + subStrLen > propLength) {
qCDebug(CardPictureToLoadLog).nospace()
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested " << propType
<< " property (" << cardPropertyName << ") for Url template (" << urlTemplate
<< ") is smaller than substr specification (" << subStrPos << " + " << subStrLen << " > "
<< propLength << ")";
return 1;
} else {
propertyValue = propertyValue.mid(subStrPos, subStrLen);
propLength = subStrLen;
}
}
if (!fillWith.isEmpty()) {
int fillLength = fillWith.length();
if (fillLength < propLength) {
qCDebug(CardPictureToLoadLog).nospace()
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested " << propType
<< " property (" << cardPropertyName << ") for Url template (" << urlTemplate
<< ") is longer than fill specification (" << fillWith << ")";
return 1;
} else {
propertyValue = fillWith.left(fillLength - propLength) + propertyValue;
}
}
transformMap["!" + propType + ":" + templatePropertyName + "!"] = propertyValue;
}
}
return 0;
}
QString CardPictureToLoad::transformUrl(const QString &urlTemplate) const
{
/* This function takes Url templates and substitutes actual card details
into the url. This is used for making Urls with follow a predictable format
for downloading images. If information is requested by the template that is
not populated for this specific card/set combination, an empty string is returned.*/
CardSetPtr set = getCurrentSet();
QMap<QString, QString> transformMap = QMap<QString, QString>();
QString setName = getSetName();
// name
QString cardName = card.getName();
transformMap["!name!"] = cardName;
transformMap["!name_lower!"] = card.getName().toLower();
transformMap["!corrected_name!"] = card.getInfo().getCorrectedName();
transformMap["!corrected_name_lower!"] = card.getInfo().getCorrectedName().toLower();
// card properties
if (parse(
urlTemplate, "prop", cardName, setName,
[&](const QString &name) { return card.getInfo().getProperty(name); }, transformMap)) {
return QString();
}
if (set) {
transformMap["!setcode!"] = set->getShortName();
transformMap["!setcode_lower!"] = set->getShortName().toLower();
transformMap["!setname!"] = set->getLongName();
transformMap["!setname_lower!"] = set->getLongName().toLower();
if (parse(
urlTemplate, "set", cardName, setName,
[&](const QString &name) { return findPrintingForSet(card, set->getShortName()).getProperty(name); },
transformMap)) {
return QString();
}
}
// language setting
transformMap["!sflang!"] = QString(QCoreApplication::translate(
"PictureLoader", "en", "code for scryfall's language property, not available for all languages"));
QString transformedUrl = urlTemplate;
for (const QString &prop : transformMap.keys()) {
if (transformedUrl.contains(prop)) {
if (!transformMap[prop].isEmpty()) {
transformedUrl.replace(prop, QUrl::toPercentEncoding(transformMap[prop]));
} else {
/* This means the template is requesting information that is not
* populated in this card, so it should return an empty string,
* indicating an invalid Url.
*/
qCDebug(CardPictureToLoadLog).nospace()
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested information ("
<< prop << ") for Url template (" << urlTemplate << ") is not available";
return QString();
}
}
}
return transformedUrl;
}

View file

@ -0,0 +1,49 @@
/**
* @file card_picture_to_load.h
* @ingroup PictureLoader
* @brief TODO: Document this.
*/
#ifndef PICTURE_TO_LOAD_H
#define PICTURE_TO_LOAD_H
#include <QLoggingCategory>
#include <libcockatrice/card/card_printing/exact_card.h>
inline Q_LOGGING_CATEGORY(CardPictureToLoadLog, "card_picture_loader.picture_to_load");
class CardPictureToLoad
{
private:
ExactCard card;
QList<CardSetPtr> sortedSets;
QList<QString> urlTemplates;
QList<QString> currentSetUrls;
QString currentUrl;
CardSetPtr currentSet;
public:
explicit CardPictureToLoad(const ExactCard &_card);
const ExactCard &getCard() const
{
return card;
}
QString getCurrentUrl() const
{
return currentUrl;
}
CardSetPtr getCurrentSet() const
{
return currentSet;
}
QString getSetName() const;
QString transformUrl(const QString &urlTemplate) const;
bool nextSet();
bool nextUrl();
void populateSetUrls();
static QList<CardSetPtr> extractSetsSorted(const ExactCard &card);
};
#endif // PICTURE_TO_LOAD_H

View file

@ -1,13 +1,12 @@
#include "pixel_map_generator.h"
#include "pb/serverinfo_user.pb.h"
#include <QApplication>
#include <QDomDocument>
#include <QFile>
#include <QPainter>
#include <QPalette>
#include <QSvgRenderer>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#define DEFAULT_COLOR_UNREGISTERED "#32c8ec";
#define DEFAULT_COLOR_REGISTERED "#5ed900";

View file

@ -7,12 +7,11 @@
#ifndef PIXMAPGENERATOR_H
#define PIXMAPGENERATOR_H
#include "user_level.h"
#include <QIcon>
#include <QLoggingCategory>
#include <QMap>
#include <QPixmap>
#include <libcockatrice/network/server/remote/user_level.h>
inline Q_LOGGING_CATEGORY(PixelMapGeneratorLog, "pixel_map_generator");

View file

@ -6,9 +6,8 @@
#pragma once
#include "../settings/cache_settings.h"
#include <QMenu>
#include <libcockatrice/settings/cache_settings.h>
class TearOffMenu : public QMenu
{

View file

@ -1,13 +1,12 @@
#include "theme_manager.h"
#include "../settings/cache_settings.h"
#include <QApplication>
#include <QColor>
#include <QDebug>
#include <QLibraryInfo>
#include <QPixmapCache>
#include <QStandardPaths>
#include <libcockatrice/settings/cache_settings.h>
#define NONE_THEME_NAME "Default"
#define STYLE_CSS_NAME "style.css"

View file

@ -0,0 +1,55 @@
#include "deck_list_sort_filter_proxy_model.h"
#include "../../deck/deck_list_model.h"
bool DeckListSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
auto *src = sourceModel();
// Inner nodes? -> sort alphabetically by column 1
bool leftIsCard = src->data(left, Qt::UserRole + 1).toBool();
bool rightIsCard = src->data(right, Qt::UserRole + 1).toBool();
if (!leftIsCard || !rightIsCard) {
QString lName = src->data(left.siblingAtColumn(1), Qt::EditRole).toString();
QString rName = src->data(right.siblingAtColumn(1), Qt::EditRole).toString();
return lName.localeAwareCompare(rName) < 0;
}
// Both are cards -> apply sort criteria
auto *lNode = static_cast<DecklistModelCardNode *>(left.internalPointer());
auto *rNode = static_cast<DecklistModelCardNode *>(right.internalPointer());
CardInfoPtr lInfo = CardDatabaseManager::query()->guessCard({lNode->getName()}).getCardPtr();
CardInfoPtr rInfo = CardDatabaseManager::query()->guessCard({rNode->getName()}).getCardPtr();
// Example: multiple tie-break criteria (colors > cmc > name)
for (const QString &crit : sortCriteria) {
if (crit == "name") {
QString ln = lNode->getName();
QString rn = rNode->getName();
int cmp = ln.localeAwareCompare(rn);
if (cmp != 0)
return cmp < 0;
} else if (crit == "cmc") {
int lc = lInfo ? lInfo->getCmc().toInt() : 0;
int rc = rInfo ? rInfo->getCmc().toInt() : 0;
if (lc != rc)
return lc < rc;
} else if (crit == "colors") {
QString lr = lInfo ? lInfo->getColors() : QString();
QString rr = rInfo ? rInfo->getColors() : QString();
int cmp = lr.localeAwareCompare(rr);
if (cmp != 0)
return cmp < 0;
} else if (crit == "maintype") {
QString lr = lInfo ? lInfo->getMainCardType() : QString();
QString rr = rInfo ? rInfo->getMainCardType() : QString();
int cmp = lr.localeAwareCompare(rr);
if (cmp != 0)
return cmp < 0;
}
}
return false;
}

View file

@ -0,0 +1,34 @@
/**
* @file deck_list_sort_filter_proxy_model.h
* @ingroup DeckEditorCardGroupWidgets
* @brief TODO: Document this.
*/
#ifndef COCKATRICE_DECK_LIST_SORT_FILTER_PROXY_MODEL_H
#define COCKATRICE_DECK_LIST_SORT_FILTER_PROXY_MODEL_H
#include <QSortFilterProxyModel>
#include <libcockatrice/card/card_database/card_database_manager.h>
class DeckListSortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit DeckListSortFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent)
{
}
void setSortCriteria(const QStringList &criteria)
{
sortCriteria = criteria;
invalidate(); // re-sort
}
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
private:
QStringList sortCriteria;
};
#endif // COCKATRICE_DECK_LIST_SORT_FILTER_PROXY_MODEL_H

View file

@ -0,0 +1,219 @@
#include "sequence_edit.h"
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QToolTip>
#include <libcockatrice/settings/cache_settings.h>
#include <utility>
SequenceEdit::SequenceEdit(const QString &_shortcutName, QWidget *parent) : QWidget(parent)
{
lineEdit = new QLineEdit(this);
clearButton = new QPushButton("", this);
defaultButton = new QPushButton("", this);
lineEdit->setMinimumWidth(70);
clearButton->setIcon(QPixmap("theme:icons/clearsearch"));
defaultButton->setIcon(QPixmap("theme:icons/update"));
auto *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(1);
layout->addWidget(lineEdit);
layout->addWidget(clearButton);
layout->addWidget(defaultButton);
connect(clearButton, &QPushButton::clicked, this, &SequenceEdit::removeLastShortcut);
connect(defaultButton, &QPushButton::clicked, this, &SequenceEdit::restoreDefault);
lineEdit->installEventFilter(this);
setShortcutName(_shortcutName);
retranslateUi();
}
void SequenceEdit::setShortcutName(const QString &_shortcutName)
{
shortcutName = _shortcutName;
if (shortcutName.isEmpty()) {
clearButton->setEnabled(false);
defaultButton->setEnabled(false);
lineEdit->setEnabled(false);
lineEdit->setText("");
// Correct as in-line translation
lineEdit->setPlaceholderText(tr("Choose an action from the table"));
} else {
clearButton->setEnabled(true);
defaultButton->setEnabled(true);
lineEdit->setEnabled(true);
lineEdit->setText(SettingsCache::instance().shortcuts().getShortcutString(shortcutName));
// Correct as in-line translation
lineEdit->setPlaceholderText(tr("Hit the key/combination of keys you want to set for this action"));
}
}
QString SequenceEdit::getSequence()
{
return lineEdit->text();
}
void SequenceEdit::removeLastShortcut()
{
QString sequences = lineEdit->text();
if (!sequences.isEmpty()) {
if (sequences.lastIndexOf(";") > 0) {
QString validText = sequences.left(sequences.lastIndexOf(";"));
lineEdit->setText(validText);
} else {
lineEdit->clear();
}
updateSettings();
}
}
void SequenceEdit::restoreDefault()
{
lineEdit->setText(SettingsCache::instance().shortcuts().getDefaultShortcutString(shortcutName));
updateSettings();
}
void SequenceEdit::refreshShortcut()
{
lineEdit->setText(SettingsCache::instance().shortcuts().getShortcutString(shortcutName));
}
void SequenceEdit::clear()
{
lineEdit->setText("");
}
bool SequenceEdit::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
auto *keyEvent = reinterpret_cast<QKeyEvent *>(event);
// don't filter outside arrow key events
if (obj != lineEdit) {
switch (keyEvent->key()) {
case Qt::Key_Up:
case Qt::Key_Down:
case Qt::Key_Left:
case Qt::Key_Right:
return false;
default:
break;
}
}
if (event->type() == QEvent::KeyPress && !keyEvent->isAutoRepeat()) {
processKey(keyEvent);
} else if (event->type() == QEvent::KeyRelease && !keyEvent->isAutoRepeat()) {
finishShortcut();
}
return true;
}
return false;
}
void SequenceEdit::processKey(QKeyEvent *e)
{
int key = e->key();
if (key != Qt::Key_Control && key != Qt::Key_Shift && key != Qt::Key_Meta && key != Qt::Key_Alt) {
valid = true;
key |= translateModifiers(e->modifiers(), e->text());
}
keys = key;
currentKey++;
if (currentKey >= key) {
finishShortcut();
}
}
int SequenceEdit::translateModifiers(Qt::KeyboardModifiers state, const QString &text)
{
int result = 0;
// The shift modifier only counts when it is not used to type a symbol
// that is only reachable using the shift key anyway
if ((state & Qt::ShiftModifier) &&
(text.isEmpty() || !text.at(0).isPrint() || text.at(0).isLetterOrNumber() || text.at(0).isSpace())) {
result |= Qt::SHIFT;
}
if (state & Qt::ControlModifier) {
result |= Qt::CTRL;
}
if (state & Qt::MetaModifier) {
result |= Qt::META;
}
if (state & Qt::AltModifier) {
result |= Qt::ALT;
}
return result;
}
/**
*Validates that shortcut is valid (is a valid shortcut key sequence and doesn't conflict with any other shortcuts).
*Displays warning messages if it's not valid.
*
* @param sequence The shortcut key sequence
* @return True if the sequence isn't already self-contained
*/
bool SequenceEdit::validateShortcut(const QKeySequence &sequence)
{
if (sequence.isEmpty() || !valid) {
return true;
}
const auto &shortcutsSettings = SettingsCache::instance().shortcuts();
const QString sequenceString = sequence.toString();
if (!shortcutsSettings.isKeyAllowed(shortcutName, sequenceString)) {
QToolTip::showText(lineEdit->mapToGlobal(QPoint()), tr("Invalid key"));
return true;
}
if (!shortcutsSettings.isValid(shortcutName, sequenceString)) {
auto overlaps = shortcutsSettings.findOverlaps(shortcutName, sequenceString);
QToolTip::showText(lineEdit->mapToGlobal(QPoint()),
tr("Shortcut already in use by:") + " " + overlaps.join(", "));
return true;
}
if (!lineEdit->text().isEmpty()) {
if (lineEdit->text().contains(sequenceString)) {
return false;
}
lineEdit->setText(lineEdit->text() + ";");
}
lineEdit->setText(lineEdit->text() + sequenceString);
return true;
}
void SequenceEdit::finishShortcut()
{
if (!validateShortcut(QKeySequence(keys))) {
return;
}
currentKey = 0;
keys = 0;
valid = false;
updateSettings();
}
void SequenceEdit::updateSettings()
{
SettingsCache::instance().shortcuts().setShortcuts(shortcutName, lineEdit->text());
}
void SequenceEdit::retranslateUi()
{
clearButton->setText(tr("Clear"));
defaultButton->setText(tr("Restore default"));
setShortcutName(shortcutName);
}

View file

@ -0,0 +1,50 @@
/**
* @file sequence_edit.h
* @ingroup UI
* @brief TODO: Document this.
*/
#ifndef SEQUENCEEDIT_H
#define SEQUENCEEDIT_H
#include <QEvent>
#include <QKeySequence>
#include <QLineEdit>
#include <QPushButton>
#include <QWidget>
class SequenceEdit : public QWidget
{
Q_OBJECT
public:
explicit SequenceEdit(const QString &_shortcutName, QWidget *parent = nullptr);
QString getSequence();
void setShortcutName(const QString &_shortcutName);
void refreshShortcut();
void clear();
void retranslateUi();
private slots:
void removeLastShortcut();
void restoreDefault();
protected:
bool eventFilter(QObject *, QEvent *event) override;
private:
QString shortcutName;
QLineEdit *lineEdit;
QPushButton *clearButton;
QPushButton *defaultButton;
int keys = 0;
int currentKey = 0;
bool valid = false;
void processKey(QKeyEvent *e);
int translateModifiers(Qt::KeyboardModifiers state, const QString &text);
bool validateShortcut(const QKeySequence &sequence);
void finishShortcut();
void updateSettings();
};
#endif // SEQUENCEEDIT_H

View file

@ -1,6 +1,5 @@
#include "color_identity_widget.h"
#include "../../../../settings/cache_settings.h"
#include "mana_symbol_widget.h"
#include <QHBoxLayout>
@ -9,6 +8,7 @@
#include <QRegularExpression>
#include <QResizeEvent>
#include <QSize>
#include <libcockatrice/settings/cache_settings.h>
ColorIdentityWidget::ColorIdentityWidget(QWidget *parent, CardInfoPtr _card) : QWidget(parent), card(_card)
{

View file

@ -7,10 +7,9 @@
#ifndef COLOR_IDENTITY_WIDGET_H
#define COLOR_IDENTITY_WIDGET_H
#include "../../../../card/card_info.h"
#include <QHBoxLayout>
#include <QWidget>
#include <libcockatrice/card/card_info.h>
class ColorIdentityWidget : public QWidget
{

View file

@ -7,10 +7,9 @@
#ifndef MANA_COST_WIDGET_H
#define MANA_COST_WIDGET_H
#include "../../../../card/card_info.h"
#include <QHBoxLayout>
#include <QWidget>
#include <libcockatrice/card/card_info.h>
class ManaCostWidget : public QWidget
{

View file

@ -1,8 +1,7 @@
#include "mana_symbol_widget.h"
#include "../../../../settings/cache_settings.h"
#include <QResizeEvent>
#include <libcockatrice/settings/cache_settings.h>
ManaSymbolWidget::ManaSymbolWidget(QWidget *parent, QString _symbol, bool _isActive, bool _mayBeToggled)
: QLabel(parent), symbol(_symbol), isActive(_isActive), mayBeToggled(_mayBeToggled)

View file

@ -1,12 +1,12 @@
#include "card_group_display_widget.h"
#include "../../../../database/card_database_manager.h"
#include "../../../../deck/deck_list_model.h"
#include "../../../../utility/card_info_comparator.h"
#include "../../../../utility/deck_list_sort_filter_proxy_model.h"
#include "../../../utility/deck_list_sort_filter_proxy_model.h"
#include "../card_info_picture_with_text_overlay_widget.h"
#include <QResizeEvent>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/card/card_info_comparator.h>
CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,

View file

@ -7,7 +7,6 @@
#ifndef CARD_GROUP_DISPLAY_WIDGET_H
#define CARD_GROUP_DISPLAY_WIDGET_H
#include "../../../../card/card_info.h"
#include "../../../../deck/deck_list_model.h"
#include "../../general/display/banner_widget.h"
#include "../card_info_picture_with_text_overlay_widget.h"
@ -16,6 +15,7 @@
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
#include <libcockatrice/card/card_info.h>
class CardGroupDisplayWidget : public QWidget
{

View file

@ -1,11 +1,11 @@
#include "flat_card_group_display_widget.h"
#include "../../../../database/card_database_manager.h"
#include "../../../../deck/deck_list_model.h"
#include "../../../../utility/card_info_comparator.h"
#include "../card_info_picture_with_text_overlay_widget.h"
#include <QResizeEvent>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/card/card_info_comparator.h>
#include <utility>
FlatCardGroupDisplayWidget::FlatCardGroupDisplayWidget(QWidget *parent,

View file

@ -1,11 +1,11 @@
#include "overlapped_card_group_display_widget.h"
#include "../../../../database/card_database_manager.h"
#include "../../../../deck/deck_list_model.h"
#include "../../../../utility/card_info_comparator.h"
#include "../card_info_picture_with_text_overlay_widget.h"
#include <QResizeEvent>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/card/card_info_comparator.h>
OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,

View file

@ -1,6 +1,5 @@
#include "card_info_display_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../game/board/card_item.h"
#include "../../../main.h"
#include "card_info_picture_widget.h"
@ -9,6 +8,7 @@
#include <QApplication>
#include <QScreen>
#include <QVBoxLayout>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <utility>
CardInfoDisplayWidget::CardInfoDisplayWidget(const CardRef &cardRef, QWidget *parent, Qt::WindowFlags flags)

View file

@ -7,12 +7,11 @@
#ifndef CARDINFOWIDGET_H
#define CARDINFOWIDGET_H
#include "../../../card/exact_card.h"
#include "card_ref.h"
#include <QComboBox>
#include <QFrame>
#include <QStringList>
#include <libcockatrice/card/card_printing/exact_card.h>
#include <libcockatrice/utility/card_ref.h>
class CardInfoPictureWidget;
class CardInfoTextWidget;

View file

@ -1,15 +1,15 @@
#include "card_info_frame_widget.h"
#include "../../../card/card_relation.h"
#include "../../../database/card_database_manager.h"
#include "../../../game/board/card_item.h"
#include "../../../settings/cache_settings.h"
#include "card_info_display_widget.h"
#include "card_info_picture_widget.h"
#include "card_info_text_widget.h"
#include <QSplitter>
#include <QVBoxLayout>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/card/card_relation/card_relation.h>
#include <libcockatrice/settings/cache_settings.h>
#include <utility>
CardInfoFrameWidget::CardInfoFrameWidget(QWidget *parent)

View file

@ -7,11 +7,10 @@
#ifndef CARDFRAME_H
#define CARDFRAME_H
#include "../../../card/exact_card.h"
#include "card_ref.h"
#include <QPushButton>
#include <QTabWidget>
#include <libcockatrice/card/card_printing/exact_card.h>
#include <libcockatrice/utility/card_ref.h>
class AbstractCardItem;
class CardInfoPictureWidget;

View file

@ -1,6 +1,6 @@
#include "card_info_picture_art_crop_widget.h"
#include "../../../picture_loader/picture_loader.h"
#include "../../../interface/card_picture_loader/card_picture_loader.h"
CardInfoPictureArtCropWidget::CardInfoPictureArtCropWidget(QWidget *parent)
: CardInfoPictureWidget(parent, false, false)
@ -13,9 +13,9 @@ QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &target
// Load the full-resolution card image, not a pre-scaled one
QPixmap fullResPixmap;
if (getCard()) {
PictureLoader::getPixmap(fullResPixmap, getCard(), QSize(745, 1040)); // or a high default size
CardPictureLoader::getPixmap(fullResPixmap, getCard(), QSize(745, 1040)); // or a high default size
} else {
PictureLoader::getCardBackPixmap(fullResPixmap, QSize(745, 1040));
CardPictureLoader::getCardBackPixmap(fullResPixmap, QSize(745, 1040));
}
// Fail-safe if loading failed

View file

@ -1,10 +1,10 @@
#include "card_info_picture_enlarged_widget.h"
#include "../../../picture_loader/picture_loader.h"
#include "../../../settings/cache_settings.h"
#include "../../../interface/card_picture_loader/card_picture_loader.h"
#include <QPainterPath>
#include <QStylePainter>
#include <libcockatrice/settings/cache_settings.h>
#include <utility>
/**
@ -35,9 +35,9 @@ CardInfoPictureEnlargedWidget::CardInfoPictureEnlargedWidget(QWidget *parent) :
void CardInfoPictureEnlargedWidget::loadPixmap(const QSize &size)
{
if (card) {
PictureLoader::getPixmap(enlargedPixmap, card, size);
CardPictureLoader::getPixmap(enlargedPixmap, card, size);
} else {
PictureLoader::getCardBackPixmap(enlargedPixmap, size);
CardPictureLoader::getCardBackPixmap(enlargedPixmap, size);
}
pixmapDirty = false;
}

View file

@ -8,10 +8,9 @@
#ifndef CARD_PICTURE_ENLARGED_WIDGET_H
#define CARD_PICTURE_ENLARGED_WIDGET_H
#include "../../../card/exact_card.h"
#include <QPixmap>
#include <QWidget>
#include <libcockatrice/card/card_printing/exact_card.h>
class CardInfoPictureEnlargedWidget final : public QWidget
{

View file

@ -1,10 +1,7 @@
#include "card_info_picture_widget.h"
#include "../../../card/card_relation.h"
#include "../../../database/card_database_manager.h"
#include "../../../game/board/card_item.h"
#include "../../../picture_loader/picture_loader.h"
#include "../../../settings/cache_settings.h"
#include "../../../interface/card_picture_loader/card_picture_loader.h"
#include "../../../tabs/tab_supervisor.h"
#include "../../window_main.h"
@ -13,6 +10,9 @@
#include <QScreen>
#include <QStylePainter>
#include <QWidget>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/card/card_relation/card_relation.h>
#include <libcockatrice/settings/cache_settings.h>
#include <utility>
/**
@ -152,11 +152,11 @@ void CardInfoPictureWidget::updatePixmap()
*/
void CardInfoPictureWidget::loadPixmap()
{
PictureLoader::getCardBackLoadingInProgressPixmap(resizedPixmap, size());
CardPictureLoader::getCardBackLoadingInProgressPixmap(resizedPixmap, size());
if (exactCard) {
PictureLoader::getPixmap(resizedPixmap, exactCard, size());
CardPictureLoader::getPixmap(resizedPixmap, exactCard, size());
} else {
PictureLoader::getCardBackLoadingFailedPixmap(resizedPixmap, size());
CardPictureLoader::getCardBackLoadingFailedPixmap(resizedPixmap, size());
}
pixmapDirty = false;

View file

@ -7,12 +7,12 @@
#ifndef CARD_INFO_PICTURE_H
#define CARD_INFO_PICTURE_H
#include "../../../card/exact_card.h"
#include "card_info_picture_enlarged_widget.h"
#include <QPropertyAnimation>
#include <QTimer>
#include <QWidget>
#include <libcockatrice/card/card_printing/exact_card.h>
inline Q_LOGGING_CATEGORY(CardInfoPictureWidgetLog, "card_info_picture_widget");

View file

@ -1,7 +1,5 @@
#include "card_info_text_widget.h"
#include "../../../card/card_relation.h"
#include "../../../card/game_specific_terms.h"
#include "../../../game/board/card_item.h"
#include <QGridLayout>
@ -10,6 +8,8 @@
#include <QScrollBar>
#include <QSizePolicy>
#include <QTextEdit>
#include <libcockatrice/card/card_relation/card_relation.h>
#include <libcockatrice/card/game_specific_terms.h>
CardInfoTextWidget::CardInfoTextWidget(QWidget *parent) : QFrame(parent), info(nullptr)
{

View file

@ -7,9 +7,8 @@
#ifndef CARDINFOTEXT_H
#define CARDINFOTEXT_H
#include "../../../card/card_info.h"
#include <QFrame>
#include <libcockatrice/card/card_info.h>
class QLabel;
class QScrollArea;
class QTextEdit;

View file

@ -1,9 +1,10 @@
#include "card_size_widget.h"
#include "../../../settings/cache_settings.h"
#include "../printing_selector/printing_selector.h"
#include "../visual_deck_storage/visual_deck_storage_widget.h"
#include <libcockatrice/settings/cache_settings.h>
/**
* @class CardSizeWidget
* @brief A widget for adjusting card sizes using a slider.

View file

@ -1,11 +1,11 @@
#include "deck_card_zone_display_widget.h"
#include "../../../deck/deck_list_model.h"
#include "../../../utility/card_info_comparator.h"
#include "card_group_display_widgets/flat_card_group_display_widget.h"
#include "card_group_display_widgets/overlapped_card_group_display_widget.h"
#include <QResizeEvent>
#include <libcockatrice/card/card_info_comparator.h>
DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
DeckListModel *_deckListModel,

View file

@ -7,7 +7,6 @@
#ifndef DECK_CARD_ZONE_DISPLAY_WIDGET_H
#define DECK_CARD_ZONE_DISPLAY_WIDGET_H
#include "../../../card/card_info.h"
#include "../../../deck/deck_list_model.h"
#include "../general/display/banner_widget.h"
#include "../general/layout_containers/overlap_widget.h"
@ -18,6 +17,7 @@
#include <QVBoxLayout>
#include <QWidget>
#include <libcockatrice/card/card_info.h>
class DeckCardZoneDisplayWidget : public QWidget
{

View file

@ -1,7 +1,5 @@
#include "deck_preview_card_picture_widget.h"
#include "../../../settings/cache_settings.h"
#include <QApplication>
#include <QFileInfo>
#include <QFontMetrics>
@ -9,6 +7,7 @@
#include <QPainterPath>
#include <QStylePainter>
#include <QTextOption>
#include <libcockatrice/settings/cache_settings.h>
/**
* @brief Constructs a CardPictureWithTextOverlay widget.

View file

@ -17,7 +17,7 @@
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
#include <deck_list.h>
#include <libcockatrice/deck_list/deck_list.h>
class DeckAnalyticsWidget : public QWidget
{

View file

@ -1,14 +1,14 @@
#include "mana_base_widget.h"
#include "../../../database/card_database.h"
#include "../../../database/card_database_manager.h"
#include "../../../deck/deck_loader.h"
#include "../general/display/banner_widget.h"
#include "../general/display/bar_widget.h"
#include <QHash>
#include <QRegularExpression>
#include <deck_list.h>
#include <libcockatrice/card/card_database/card_database.h>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h>
ManaBaseWidget::ManaBaseWidget(QWidget *parent, DeckListModel *_deckListModel)
: QWidget(parent), deckListModel(_deckListModel)

View file

@ -12,7 +12,7 @@
#include <QHBoxLayout>
#include <QWidget>
#include <deck_list.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <utility>
class ManaBaseWidget : public QWidget

View file

@ -1,13 +1,13 @@
#include "mana_curve_widget.h"
#include "../../../database/card_database.h"
#include "../../../database/card_database_manager.h"
#include "../../../deck/deck_loader.h"
#include "../../../main.h"
#include "../general/display/banner_widget.h"
#include "../general/display/bar_widget.h"
#include <deck_list.h>
#include <libcockatrice/card/card_database/card_database.h>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <unordered_map>
ManaCurveWidget::ManaCurveWidget(QWidget *parent, DeckListModel *_deckListModel)

View file

@ -1,14 +1,14 @@
#include "mana_devotion_widget.h"
#include "../../../database/card_database.h"
#include "../../../database/card_database_manager.h"
#include "../../../deck/deck_loader.h"
#include "../../../main.h"
#include "../general/display/banner_widget.h"
#include "../general/display/bar_widget.h"
#include <deck_list.h>
#include <iostream>
#include <libcockatrice/card/card_database/card_database.h>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <regex>
#include <string>
#include <unordered_map>

View file

@ -12,7 +12,7 @@
#include <QHBoxLayout>
#include <QWidget>
#include <deck_list.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <utility>
class ManaDevotionWidget : public QWidget

View file

@ -1,9 +1,6 @@
#include "deck_editor_database_display_widget.h"
#include "../../../card/card_relation.h"
#include "../../../database/card_database_manager.h"
#include "../../../filters/syntax_help.h"
#include "../../../settings/cache_settings.h"
#include "../../../tabs/abstract_tab_deck_editor.h"
#include "../../../tabs/tab_supervisor.h"
#include "../../pixel_map_generator.h"
@ -15,6 +12,9 @@
#include <QTextBrowser>
#include <QToolButton>
#include <QTreeView>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/card/card_relation/card_relation.h>
#include <libcockatrice/settings/cache_settings.h>
static bool canBeCommander(const CardInfo &cardInfo)
{

View file

@ -8,14 +8,14 @@
#ifndef DECK_EDITOR_DATABASE_DISPLAY_WIDGET_H
#define DECK_EDITOR_DATABASE_DISPLAY_WIDGET_H
#include "../../../database/model/card_database_display_model.h"
#include "../../../database/model/card_database_model.h"
#include "../../../deck/custom_line_edit.h"
#include "../../../tabs/abstract_tab_deck_editor.h"
#include "../../../utility/key_signals.h"
#include <QHBoxLayout>
#include <QWidget>
#include <libcockatrice/card/card_database/model/card_database_display_model.h>
#include <libcockatrice/card/card_database/model/card_database_model.h>
#include <libcockatrice/utility/key_signals.h>
class AbstractTabDeckEditor;
class DeckEditorDatabaseDisplayWidget : public QWidget

View file

@ -1,15 +1,14 @@
#include "deck_editor_deck_dock_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../settings/cache_settings.h"
#include <QComboBox>
#include <QDockWidget>
#include <QHeaderView>
#include <QLabel>
#include <QSplitter>
#include <QTextEdit>
#include <trice_limits.h>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/settings/cache_settings.h>
#include <libcockatrice/utility/trice_limits.h>
DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent)
: QDockWidget(parent), deckEditor(parent)

View file

@ -8,10 +8,8 @@
#ifndef DECK_EDITOR_DECK_DOCK_WIDGET_H
#define DECK_EDITOR_DECK_DOCK_WIDGET_H
#include "../../../card/card_info.h"
#include "../../../deck/custom_line_edit.h"
#include "../../../tabs/abstract_tab_deck_editor.h"
#include "../../../utility/key_signals.h"
#include "../visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
#include <QComboBox>
@ -19,6 +17,8 @@
#include <QLabel>
#include <QTextEdit>
#include <QTreeView>
#include <libcockatrice/card/card_info.h>
#include <libcockatrice/utility/key_signals.h>
class DeckListModel;
class AbstractTabDeckEditor;

View file

@ -1,13 +1,13 @@
#include "deck_editor_filter_dock_widget.h"
#include "../../../database/model/card_database_model.h"
#include "../../../filters/filter_builder.h"
#include "../../../filters/filter_tree_model.h"
#include "../../../settings/cache_settings.h"
#include <QGridLayout>
#include <QMenu>
#include <QToolButton>
#include <libcockatrice/card/card_database/model/card_database_model.h>
#include <libcockatrice/settings/cache_settings.h>
DeckEditorFilterDockWidget::DeckEditorFilterDockWidget(AbstractTabDeckEditor *parent)
: QDockWidget(parent), deckEditor(parent)

View file

@ -9,10 +9,10 @@
#define DECK_EDITOR_FILTER_DOCK_WIDGET_H
#include "../../../tabs/abstract_tab_deck_editor.h"
#include "../../../utility/key_signals.h"
#include <QDockWidget>
#include <QTreeView>
#include <libcockatrice/utility/key_signals.h>
class FilterTreeModel;
class AbstractTabDeckEditor;

View file

@ -1,8 +1,5 @@
#include "home_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../server/remote/remote_client.h"
#include "../../../settings/cache_settings.h"
#include "../../../tabs/tab_supervisor.h"
#include "../../window_main.h"
#include "background_sources.h"
@ -12,6 +9,9 @@
#include <QPainterPath>
#include <QPushButton>
#include <QVBoxLayout>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/network/client/remote/remote_client.h>
#include <libcockatrice/settings/cache_settings.h>
HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
: QWidget(parent), tabSupervisor(_tabSupervisor), background("theme:backgrounds/home"), overlay("theme:cockatrice")

View file

@ -7,7 +7,6 @@
#ifndef HOME_WIDGET_H
#define HOME_WIDGET_H
#include "../../../server/abstract_client.h"
#include "../../../tabs/tab_supervisor.h"
#include "../cards/card_info_picture_art_crop_widget.h"
#include "home_styled_button.h"
@ -15,6 +14,7 @@
#include <QGridLayout>
#include <QGroupBox>
#include <QWidget>
#include <libcockatrice/network/client/abstract/abstract_client.h>
class HomeWidget : public QWidget
{

View file

@ -8,7 +8,6 @@
#ifndef CARD_AMOUNT_WIDGET_H
#define CARD_AMOUNT_WIDGET_H
#include "../../../card/card_info.h"
#include "../../../deck/deck_list_model.h"
#include "../../../deck/deck_loader.h"
#include "../../../tabs/abstract_tab_deck_editor.h"
@ -19,6 +18,7 @@
#include <QPushButton>
#include <QTreeView>
#include <QWidget>
#include <libcockatrice/card/card_info.h>
class CardAmountWidget : public QWidget
{

View file

@ -1,8 +1,7 @@
#include "printing_selector.h"
#include "../../../dialogs/dlg_select_set_for_cards.h"
#include "../../../picture_loader/picture_loader.h"
#include "../../../settings/cache_settings.h"
#include "../../../interface/card_picture_loader/card_picture_loader.h"
#include "printing_selector_card_display_widget.h"
#include "printing_selector_card_search_widget.h"
#include "printing_selector_card_selection_widget.h"
@ -10,6 +9,7 @@
#include <QFrame>
#include <QScrollBar>
#include <libcockatrice/settings/cache_settings.h>
#include <qboxlayout.h>
/**

View file

@ -7,7 +7,6 @@
#ifndef PRINTING_SELECTOR_H
#define PRINTING_SELECTOR_H
#include "../../../card/card_info.h"
#include "../../../deck/deck_list_model.h"
#include "../cards/card_size_widget.h"
#include "../general/layout_containers/flow_widget.h"
@ -19,6 +18,7 @@
#include <QTreeView>
#include <QVBoxLayout>
#include <QWidget>
#include <libcockatrice/card/card_info.h>
#define BATCH_SIZE 10

View file

@ -7,7 +7,6 @@
#ifndef PRINTING_SELECTOR_CARD_DISPLAY_WIDGET_H
#define PRINTING_SELECTOR_CARD_DISPLAY_WIDGET_H
#include "../../../card/card_info.h"
#include "../../../deck/deck_list_model.h"
#include "../../../tabs/abstract_tab_deck_editor.h"
#include "printing_selector_card_overlay_widget.h"
@ -15,6 +14,7 @@
#include <QPainter>
#include <QWidget>
#include <libcockatrice/card/card_info.h>
class PrintingSelectorCardDisplayWidget : public QWidget
{

View file

@ -1,13 +1,13 @@
#include "printing_selector_card_overlay_widget.h"
#include "../../../card/card_relation.h"
#include "../../../database/card_database_manager.h"
#include "../../../settings/cache_settings.h"
#include "printing_selector_card_display_widget.h"
#include <QMenu>
#include <QMouseEvent>
#include <QVBoxLayout>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/card/card_relation/card_relation.h>
#include <libcockatrice/settings/cache_settings.h>
#include <utility>
/**

View file

@ -7,7 +7,6 @@
#ifndef PRINTING_SELECTOR_CARD_OVERLAY_WIDGET_H
#define PRINTING_SELECTOR_CARD_OVERLAY_WIDGET_H
#include "../../../card/card_info.h"
#include "../../../deck/deck_list_model.h"
#include "../../../tabs/abstract_tab_deck_editor.h"
#include "../cards/card_info_picture_widget.h"
@ -15,6 +14,8 @@
#include "card_amount_widget.h"
#include "set_name_and_collectors_number_display_widget.h"
#include <libcockatrice/card/card_info.h>
class PrintingSelectorCardOverlayWidget : public QWidget
{
Q_OBJECT

View file

@ -1,7 +1,7 @@
#include "printing_selector_card_sorting_widget.h"
#include "../../../settings/cache_settings.h"
#include "../../../utility/card_set_comparator.h"
#include <libcockatrice/card/card_set/card_set_comparator.h>
#include <libcockatrice/settings/cache_settings.h>
const QString PrintingSelectorCardSortingWidget::SORT_OPTIONS_ALPHABETICAL = tr("Alphabetical");
const QString PrintingSelectorCardSortingWidget::SORT_OPTIONS_PREFERENCE = tr("Preference");

View file

@ -1,13 +1,13 @@
#include "visual_database_display_filter_save_load_widget.h"
#include "../../../filters/filter_tree.h"
#include "../../../settings/cache_settings.h"
#include "visual_database_filter_display_widget.h"
#include <QHBoxLayout>
#include <QJsonArray>
#include <QJsonObject>
#include <QMessageBox>
#include <libcockatrice/settings/cache_settings.h>
VisualDatabaseDisplayFilterSaveLoadWidget::VisualDatabaseDisplayFilterSaveLoadWidget(QWidget *parent,
FilterTreeModel *_filterModel)

View file

@ -1,12 +1,12 @@
#include "visual_database_display_main_type_filter_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../filters/filter_tree.h"
#include "../../../filters/filter_tree_model.h"
#include <QPushButton>
#include <QSpinBox>
#include <QTimer>
#include <libcockatrice/card/card_database/card_database_manager.h>
VisualDatabaseDisplayMainTypeFilterWidget::VisualDatabaseDisplayMainTypeFilterWidget(QWidget *parent,
FilterTreeModel *_filterModel)

View file

@ -1,14 +1,14 @@
#include "visual_database_display_set_filter_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../filters/filter_tree.h"
#include "../../../filters/filter_tree_model.h"
#include "../../../settings/cache_settings.h"
#include <QLineEdit>
#include <QPushButton>
#include <QTimer>
#include <algorithm>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/settings/cache_settings.h>
VisualDatabaseDisplayRecentSetFilterSettingsWidget::VisualDatabaseDisplayRecentSetFilterSettingsWidget(QWidget *parent)
: QWidget(parent)

View file

@ -1,6 +1,5 @@
#include "visual_database_display_sub_type_filter_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../filters/filter_tree.h"
#include "../../../filters/filter_tree_model.h"
@ -8,6 +7,7 @@
#include <QPushButton>
#include <QSpinBox>
#include <QTimer>
#include <libcockatrice/card/card_database/card_database_manager.h>
VisualDatabaseDisplaySubTypeFilterWidget::VisualDatabaseDisplaySubTypeFilterWidget(QWidget *parent,
FilterTreeModel *_filterModel)

View file

@ -1,12 +1,8 @@
#include "visual_database_display_widget.h"
#include "../../../database/card_database.h"
#include "../../../database/card_database_manager.h"
#include "../../../deck/custom_line_edit.h"
#include "../../../filters/filter_tree_model.h"
#include "../../../filters/syntax_help.h"
#include "../../../settings/cache_settings.h"
#include "../../../utility/card_info_comparator.h"
#include "../../pixel_map_generator.h"
#include "../cards/card_info_picture_with_text_overlay_widget.h"
#include "../quick_settings/settings_button_widget.h"
@ -19,6 +15,10 @@
#include <QHeaderView>
#include <QScrollBar>
#include <libcockatrice/card/card_database/card_database.h>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/card/card_info_comparator.h>
#include <libcockatrice/settings/cache_settings.h>
#include <qpropertyanimation.h>
#include <utility>

View file

@ -7,13 +7,10 @@
#ifndef VISUAL_DATABASE_DISPLAY_WIDGET_H
#define VISUAL_DATABASE_DISPLAY_WIDGET_H
#include "../../../database/card_database.h"
#include "../../../database/model/card_database_model.h"
#include "../../../deck/custom_line_edit.h"
#include "../../../deck/deck_list_model.h"
#include "../../../filters/filter_tree_model.h"
#include "../../../tabs/abstract_tab_deck_editor.h"
#include "../../../utility/key_signals.h"
#include "../../layouts/flow_layout.h"
#include "../cards/card_info_picture_with_text_overlay_widget.h"
#include "../cards/card_size_widget.h"
@ -31,6 +28,9 @@
#include <QVBoxLayout>
#include <QWheelEvent>
#include <QWidget>
#include <libcockatrice/card/card_database/card_database.h>
#include <libcockatrice/card/card_database/model/card_database_model.h>
#include <libcockatrice/utility/key_signals.h>
#include <qscrollarea.h>
inline Q_LOGGING_CATEGORY(VisualDatabaseDisplayLog, "visual_database_display");

View file

@ -1,13 +1,12 @@
#include "visual_database_filter_display_widget.h"
#include "../../../settings/cache_settings.h"
#include <QDir>
#include <QFile>
#include <QFontMetrics>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QPushButton>
#include <libcockatrice/settings/cache_settings.h>
FilterDisplayWidget::FilterDisplayWidget(QWidget *parent, const QString &filename, FilterTreeModel *_filterModel)
: QWidget(parent), filterFilename(filename), filterModel(_filterModel)

View file

@ -1,10 +1,10 @@
#include "visual_deck_editor_sample_hand_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../deck/deck_loader.h"
#include "../../../settings/cache_settings.h"
#include "../cards/card_info_picture_widget.h"
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/settings/cache_settings.h>
#include <random>
VisualDeckEditorSampleHandWidget::VisualDeckEditorSampleHandWidget(QWidget *parent, DeckListModel *_deckListModel)

View file

@ -1,14 +1,8 @@
#include "visual_deck_editor_widget.h"
#include "../../../database/card_database.h"
#include "../../../database/card_database_manager.h"
#include "../../../database/model/card/card_completer_proxy_model.h"
#include "../../../database/model/card/card_search_model.h"
#include "../../../database/model/card_database_model.h"
#include "../../../deck/deck_list_model.h"
#include "../../../deck/deck_loader.h"
#include "../../../main.h"
#include "../../../utility/card_info_comparator.h"
#include "../../layouts/overlap_layout.h"
#include "../cards/card_info_picture_with_text_overlay_widget.h"
#include "../cards/deck_card_zone_display_widget.h"
@ -21,6 +15,12 @@
#include <QLineEdit>
#include <QPushButton>
#include <QResizeEvent>
#include <libcockatrice/card/card_database/card_database.h>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/card/card_database/model/card/card_completer_proxy_model.h>
#include <libcockatrice/card/card_database/model/card/card_search_model.h>
#include <libcockatrice/card/card_database/model/card_database_model.h>
#include <libcockatrice/card/card_info_comparator.h>
#include <qscrollarea.h>
VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_deckListModel)

View file

@ -7,10 +7,6 @@
#ifndef VISUAL_DECK_EDITOR_H
#define VISUAL_DECK_EDITOR_H
#include "../../../database/card_database.h"
#include "../../../database/model/card/card_completer_proxy_model.h"
#include "../../../database/model/card_database_display_model.h"
#include "../../../database/model/card_database_model.h"
#include "../../../deck/deck_list_model.h"
#include "../cards/card_info_picture_with_text_overlay_widget.h"
#include "../cards/card_size_widget.h"
@ -22,6 +18,10 @@
#include <QListWidget>
#include <QPushButton>
#include <QWidget>
#include <libcockatrice/card/card_database/card_database.h>
#include <libcockatrice/card/card_database/model/card/card_completer_proxy_model.h>
#include <libcockatrice/card/card_database/model/card_database_display_model.h>
#include <libcockatrice/card/card_database/model/card_database_model.h>
#include <qscrollarea.h>
class DeckCardZoneDisplayWidget;

View file

@ -1,7 +1,6 @@
#include "deck_preview_deck_tags_display_widget.h"
#include "../../../../dialogs/dlg_convert_deck_to_cod_format.h"
#include "../../../../settings/cache_settings.h"
#include "../../../../tabs/tab_deck_editor.h"
#include "../../general/layout_containers/flow_widget.h"
#include "deck_preview_tag_addition_widget.h"
@ -13,6 +12,7 @@
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <libcockatrice/settings/cache_settings.h>
DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList)
: QWidget(_parent), deckList(nullptr)

View file

@ -1,6 +1,5 @@
#include "deck_preview_tag_addition_widget.h"
#include "../../../../settings/cache_settings.h"
#include "../../../../tabs/abstract_tab_deck_editor.h"
#include "deck_preview_tag_dialog.h"
@ -8,6 +7,7 @@
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QPainter>
#include <libcockatrice/settings/cache_settings.h>
#include <utility>
DeckPreviewTagAdditionWidget::DeckPreviewTagAdditionWidget(QWidget *_parent, QString _tagName)

View file

@ -1,7 +1,6 @@
#include "deck_preview_tag_dialog.h"
#include "../../../../dialogs/dlg_default_tags_editor.h"
#include "../../../../settings/cache_settings.h"
#include "deck_preview_tag_item_widget.h"
#include <QCheckBox>
@ -12,6 +11,7 @@
#include <QPushButton>
#include <QTimer>
#include <QVBoxLayout>
#include <libcockatrice/settings/cache_settings.h>
DeckPreviewTagDialog::DeckPreviewTagDialog(const QStringList &knownTags,
const QStringList &_activeTags,

View file

@ -1,7 +1,5 @@
#include "deck_preview_widget.h"
#include "../../../../database/card_database_manager.h"
#include "../../../../settings/cache_settings.h"
#include "../../cards/additional_info/color_identity_widget.h"
#include "../../cards/deck_preview_card_picture_widget.h"
#include "deck_preview_deck_tags_display_widget.h"
@ -15,6 +13,8 @@
#include <QSet>
#include <QStandardItemModel>
#include <QVBoxLayout>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/settings/cache_settings.h>
DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent,
VisualDeckStorageWidget *_visualDeckStorageWidget,

View file

@ -1,10 +1,10 @@
#include "visual_deck_storage_folder_display_widget.h"
#include "../../../settings/cache_settings.h"
#include "deck_preview/deck_preview_widget.h"
#include <QDirIterator>
#include <QMouseEvent>
#include <libcockatrice/settings/cache_settings.h>
VisualDeckStorageFolderDisplayWidget::VisualDeckStorageFolderDisplayWidget(
QWidget *parent,

View file

@ -1,11 +1,11 @@
#include "visual_deck_storage_quick_settings_widget.h"
#include "../../../settings/cache_settings.h"
#include "visual_deck_storage_widget.h"
#include <QCheckBox>
#include <QComboBox>
#include <QSpinBox>
#include <libcockatrice/settings/cache_settings.h>
VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidget *parent)
: SettingsButtonWidget(parent)

View file

@ -2,10 +2,10 @@
#include "../../../filters/deck_filter_string.h"
#include "../../../filters/syntax_help.h"
#include "../../../settings/cache_settings.h"
#include "../../pixel_map_generator.h"
#include <QAction>
#include <libcockatrice/settings/cache_settings.h>
/**
* @brief Constructs a PrintingSelectorCardSearchWidget for searching cards by set name or set code.

View file

@ -1,6 +1,6 @@
#include "visual_deck_storage_sort_widget.h"
#include "../../../settings/cache_settings.h"
#include <libcockatrice/settings/cache_settings.h>
/**
* @brief Constructs a PrintingSelectorCardSortWidget for searching cards by set name or set code.

View file

@ -1,7 +1,5 @@
#include "visual_deck_storage_widget.h"
#include "../../../database/card_database_manager.h"
#include "../../../settings/cache_settings.h"
#include "../quick_settings/settings_button_widget.h"
#include "deck_preview/deck_preview_widget.h"
#include "visual_deck_storage_folder_display_widget.h"
@ -14,6 +12,8 @@
#include <QMouseEvent>
#include <QSpinBox>
#include <QVBoxLayout>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/settings/cache_settings.h>
VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(parent), folderWidget(nullptr)
{

View file

@ -22,8 +22,6 @@
#include "../client/get_text_with_max.h"
#include "../client/network/client_update_checker.h"
#include "../client/network/release_channel.h"
#include "../database/card_database.h"
#include "../database/card_database_manager.h"
#include "../dialogs/dlg_connect.h"
#include "../dialogs/dlg_edit_tokens.h"
#include "../dialogs/dlg_forgot_password_challenge.h"
@ -37,18 +35,8 @@
#include "../dialogs/dlg_update.h"
#include "../dialogs/dlg_view_log.h"
#include "../main.h"
#include "../server/local_client.h"
#include "../server/local_server.h"
#include "../server/local_server_interface.h"
#include "../server/remote/remote_client.h"
#include "../settings/cache_settings.h"
#include "../tabs/tab_game.h"
#include "../tabs/tab_supervisor.h"
#include "../utility/logger.h"
#include "pb/event_connection_closed.pb.h"
#include "pb/event_server_shutdown.pb.h"
#include "pb/game_replay.pb.h"
#include "pb/room_commands.pb.h"
#include "version_string.h"
#include <QAction>
@ -72,6 +60,18 @@
#include <QWindow>
#include <QtConcurrent>
#include <QtNetwork>
#include <libcockatrice/card/card_database/card_database.h>
#include <libcockatrice/card/card_database/card_database_manager.h>
#include <libcockatrice/network/client/local/local_client.h>
#include <libcockatrice/network/client/remote/remote_client.h>
#include <libcockatrice/network/server/local/local_server.h>
#include <libcockatrice/network/server/local/local_server_interface.h>
#include <libcockatrice/protocol/pb/event_connection_closed.pb.h>
#include <libcockatrice/protocol/pb/event_server_shutdown.pb.h>
#include <libcockatrice/protocol/pb/game_replay.pb.h>
#include <libcockatrice/protocol/pb/room_commands.pb.h>
#include <libcockatrice/settings/cache_settings.h>
#include <libcockatrice/utility/logger.h>
#define GITHUB_PAGES_URL "https://cockatrice.github.io"
#define GITHUB_CONTRIBUTORS_URL "https://github.com/Cockatrice/Cockatrice/graphs/contributors?type=c"

View file

@ -25,9 +25,6 @@
#ifndef WINDOW_H
#define WINDOW_H
#include "../server/abstract_client.h"
#include "pb/response.pb.h"
#include <QList>
#include <QLoggingCategory>
#include <QMainWindow>
@ -35,6 +32,8 @@
#include <QProcess>
#include <QSystemTrayIcon>
#include <QtNetwork>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/response.pb.h>
inline Q_LOGGING_CATEGORY(WindowMainLog, "window_main");
inline Q_LOGGING_CATEGORY(WindowMainStartupLog, "window_main.startup");