Merge branch 'master' into fix-compiler-warnings
4
Doxyfile
|
|
@ -991,7 +991,7 @@ WARN_LOGFILE =
|
|||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = cockatrice doc/doxygen-groups libcockatrice_card libcockatrice_deck_list libcockatrice_filters libcockatrice_interfaces libcockatrice_models libcockatrice_network libcockatrice_protocol libcockatrice_rng libcockatrice_settings libcockatrice_utility
|
||||
INPUT = cockatrice doc/doxygen-extra-pages doc/doxygen-groups libcockatrice_card libcockatrice_deck_list libcockatrice_filters libcockatrice_interfaces libcockatrice_models libcockatrice_network libcockatrice_protocol libcockatrice_rng libcockatrice_settings libcockatrice_utility
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
|
@ -1145,7 +1145,7 @@ EXAMPLE_RECURSIVE = NO
|
|||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH = doc/doxygen-images
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that Doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <QtMath>
|
||||
|
||||
CardCounterSettings::CardCounterSettings(const QString &settingsPath, QObject *parent)
|
||||
: SettingsManager(settingsPath + "global.ini", parent)
|
||||
: SettingsManager(settingsPath + "global.ini", "cards", "counters", parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ void DeckViewCardDragItem::updatePosition(const QPointF &cursorScenePos)
|
|||
|
||||
void DeckViewCardDragItem::handleDrop(DeckViewCardContainer *target)
|
||||
{
|
||||
DeckViewCard *card = static_cast<DeckViewCard *>(item);
|
||||
DeckViewCardContainer *start = static_cast<DeckViewCardContainer *>(item->parentItem());
|
||||
auto *card = static_cast<DeckViewCard *>(item);
|
||||
auto *start = static_cast<DeckViewCardContainer *>(item->parentItem());
|
||||
start->removeCard(card);
|
||||
target->addCard(card);
|
||||
card->setParentItem(target);
|
||||
|
|
@ -51,13 +51,13 @@ void DeckViewCardDragItem::handleDrop(DeckViewCardContainer *target)
|
|||
void DeckViewCardDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
DeckViewScene *sc = static_cast<DeckViewScene *>(scene());
|
||||
auto *sc = static_cast<DeckViewScene *>(scene());
|
||||
sc->removeItem(this);
|
||||
|
||||
if (currentZone) {
|
||||
handleDrop(currentZone);
|
||||
for (int i = 0; i < childDrags.size(); i++) {
|
||||
DeckViewCardDragItem *c = static_cast<DeckViewCardDragItem *>(childDrags[i]);
|
||||
auto *c = static_cast<DeckViewCardDragItem *>(childDrags[i]);
|
||||
c->handleDrop(currentZone);
|
||||
sc->removeItem(c);
|
||||
}
|
||||
|
|
@ -118,12 +118,12 @@ void DeckViewCard::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|||
QList<QGraphicsItem *> sel = scene()->selectedItems();
|
||||
int j = 0;
|
||||
for (int i = 0; i < sel.size(); i++) {
|
||||
DeckViewCard *c = static_cast<DeckViewCard *>(sel.at(i));
|
||||
auto *c = static_cast<DeckViewCard *>(sel.at(i));
|
||||
if (c == this)
|
||||
continue;
|
||||
++j;
|
||||
QPointF childPos = QPointF(j * CARD_WIDTH / 2, 0);
|
||||
DeckViewCardDragItem *drag = new DeckViewCardDragItem(c, childPos, dragItem);
|
||||
auto childPos = QPointF(j * CARD_WIDTH / 2, 0);
|
||||
auto *drag = new DeckViewCardDragItem(c, childPos, dragItem);
|
||||
drag->setPos(dragItem->pos() + childPos);
|
||||
scene()->addItem(drag);
|
||||
}
|
||||
|
|
@ -140,8 +140,8 @@ void DeckView::mouseDoubleClickEvent(QMouseEvent *event)
|
|||
QList<QGraphicsItem *> sel = scene()->selectedItems();
|
||||
|
||||
for (int i = 0; i < sel.size(); i++) {
|
||||
DeckViewCard *c = static_cast<DeckViewCard *>(sel.at(i));
|
||||
DeckViewCardContainer *zone = static_cast<DeckViewCardContainer *>(c->parentItem());
|
||||
auto *c = static_cast<DeckViewCard *>(sel.at(i));
|
||||
auto *zone = static_cast<DeckViewCardContainer *>(c->parentItem());
|
||||
MoveCard_ToZone m;
|
||||
m.set_card_name(c->getName().toStdString());
|
||||
m.set_start_zone(zone->getName().toStdString());
|
||||
|
|
@ -345,7 +345,7 @@ void DeckViewScene::rebuildTree()
|
|||
|
||||
InnerDecklistNode *listRoot = deck->getRoot();
|
||||
for (int i = 0; i < listRoot->size(); i++) {
|
||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
auto *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
|
||||
DeckViewCardContainer *container = cardContainers.value(currentZone->getName(), 0);
|
||||
if (!container) {
|
||||
|
|
@ -355,12 +355,12 @@ void DeckViewScene::rebuildTree()
|
|||
}
|
||||
|
||||
for (int j = 0; j < currentZone->size(); j++) {
|
||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
auto *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
if (!currentCard)
|
||||
continue;
|
||||
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
DeckViewCard *newCard = new DeckViewCard(container, currentCard->toCardRef(), currentZone->getName());
|
||||
auto *newCard = new DeckViewCard(container, currentCard->toCardRef(), currentZone->getName());
|
||||
container->addCard(newCard);
|
||||
emit newCardAdded(newCard);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
|
|||
faceDownCheckBox = new QCheckBox(tr("Create face-down (Only hides name)"));
|
||||
connect(faceDownCheckBox, &QCheckBox::toggled, this, &DlgCreateToken::faceDownCheckBoxToggled);
|
||||
|
||||
QGridLayout *grid = new QGridLayout;
|
||||
auto *grid = new QGridLayout;
|
||||
grid->addWidget(nameLabel, 0, 0);
|
||||
grid->addWidget(nameEdit, 0, 1);
|
||||
grid->addWidget(colorLabel, 1, 0);
|
||||
|
|
@ -79,7 +79,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
|
|||
grid->addWidget(destroyCheckBox, 4, 0, 1, 2);
|
||||
grid->addWidget(faceDownCheckBox, 5, 0, 1, 2);
|
||||
|
||||
QGroupBox *tokenDataGroupBox = new QGroupBox(tr("Token data"));
|
||||
auto *tokenDataGroupBox = new QGroupBox(tr("Token data"));
|
||||
tokenDataGroupBox->setLayout(grid);
|
||||
|
||||
cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this);
|
||||
|
|
@ -125,25 +125,25 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
|
|||
#endif
|
||||
}
|
||||
|
||||
QVBoxLayout *tokenChooseLayout = new QVBoxLayout;
|
||||
auto *tokenChooseLayout = new QVBoxLayout;
|
||||
tokenChooseLayout->addWidget(chooseTokenFromAllRadioButton);
|
||||
tokenChooseLayout->addWidget(chooseTokenFromDeckRadioButton);
|
||||
tokenChooseLayout->addWidget(chooseTokenView);
|
||||
|
||||
QGroupBox *tokenChooseGroupBox = new QGroupBox(tr("Choose token from list"));
|
||||
auto *tokenChooseGroupBox = new QGroupBox(tr("Choose token from list"));
|
||||
tokenChooseGroupBox->setLayout(tokenChooseLayout);
|
||||
|
||||
QGridLayout *hbox = new QGridLayout;
|
||||
auto *hbox = new QGridLayout;
|
||||
hbox->addWidget(pic, 0, 0, 1, 1);
|
||||
hbox->addWidget(tokenDataGroupBox, 1, 0, 1, 1);
|
||||
hbox->addWidget(tokenChooseGroupBox, 0, 1, 2, 1);
|
||||
hbox->setColumnStretch(1, 1);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgCreateToken::actOk);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgCreateToken::actReject);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(hbox);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
setLayout(mainLayout);
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ void Player::processPlayerInfo(const ServerInfo_Player &info)
|
|||
} else {
|
||||
for (int j = 0; j < cardListSize; ++j) {
|
||||
const ServerInfo_Card &cardInfo = zoneInfo.card_list(j);
|
||||
CardItem *card = new CardItem(this);
|
||||
auto *card = new CardItem(this);
|
||||
card->processCardInfo(cardInfo);
|
||||
zone->addCard(card, false, cardInfo.x(), cardInfo.y());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ void PlayerGraphicsItem::onPlayerActiveChanged(bool _active)
|
|||
void PlayerGraphicsItem::initializeZones()
|
||||
{
|
||||
deckZoneGraphicsItem = new PileZone(player->getDeckZone(), this);
|
||||
QPointF base = QPointF(counterAreaWidth + (CARD_HEIGHT - CARD_WIDTH + 15) / 2.0,
|
||||
10 + playerTarget->boundingRect().height() + 5 - (CARD_HEIGHT - CARD_WIDTH) / 2.0);
|
||||
auto base = QPointF(counterAreaWidth + (CARD_HEIGHT - CARD_WIDTH + 15) / 2.0,
|
||||
10 + playerTarget->boundingRect().height() + 5 - (CARD_HEIGHT - CARD_WIDTH) / 2.0);
|
||||
deckZoneGraphicsItem->setPos(base);
|
||||
|
||||
qreal h = deckZoneGraphicsItem->boundingRect().width() + 5;
|
||||
|
|
@ -147,7 +147,7 @@ void PlayerGraphicsItem::rearrangeCounters()
|
|||
|
||||
void PlayerGraphicsItem::rearrangeZones()
|
||||
{
|
||||
QPointF base = QPointF(CARD_HEIGHT + counterAreaWidth + 15, 0);
|
||||
auto base = QPointF(CARD_HEIGHT + counterAreaWidth + 15, 0);
|
||||
if (SettingsCache::instance().getHorizontalHand()) {
|
||||
if (mirrored) {
|
||||
if (player->getHandZone()->contentsKnown()) {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ bool PlayerListItemDelegate::editorEvent(QEvent *event,
|
|||
const QModelIndex &index)
|
||||
{
|
||||
if ((event->type() == QEvent::MouseButtonPress) && index.isValid()) {
|
||||
QMouseEvent *const mouseEvent = static_cast<QMouseEvent *>(event);
|
||||
auto *const mouseEvent = static_cast<QMouseEvent *>(event);
|
||||
if (mouseEvent->button() == Qt::RightButton) {
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
static_cast<PlayerListWidget *>(parent())->showContextMenu(mouseEvent->globalPosition().toPoint(), index);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
/**
|
||||
* @file card_picture_loader.h
|
||||
* @ingroup PictureLoader
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef CARD_PICTURE_LOADER_H
|
||||
#define CARD_PICTURE_LOADER_H
|
||||
|
||||
|
|
@ -16,10 +10,37 @@
|
|||
inline Q_LOGGING_CATEGORY(CardPictureLoaderLog, "card_picture_loader");
|
||||
inline Q_LOGGING_CATEGORY(CardPictureLoaderCardBackCacheFailLog, "card_picture_loader.card_back_cache_fail");
|
||||
|
||||
/**
|
||||
* @class CardPictureLoader
|
||||
* @ingroup PictureLoader
|
||||
* @brief Singleton class to manage card image loading and caching. Provides functionality to asynchronously load,
|
||||
* cache, and manage card images for the client.
|
||||
*
|
||||
* This class is a singleton and handles:
|
||||
* - Loading card images from disk or network.
|
||||
* - Caching images in QPixmapCache for fast reuse.
|
||||
* - Providing themed card backs, including fallback and in-progress/failed states.
|
||||
* - Emitting updates when pixmaps are loaded.
|
||||
*
|
||||
* It interacts with CardPictureLoaderWorker for background loading and
|
||||
* CardPictureLoaderStatusBar to display loading progress in the main window.
|
||||
*
|
||||
* Provides static accessors for:
|
||||
* - Card images by ExactCard.
|
||||
* - Card back images (normal, in-progress, failed).
|
||||
* - Cache management (clearPixmapCache(), clearNetworkCache(), cacheCardPixmaps(const QList<ExactCard> &cards)).
|
||||
*
|
||||
* Uses a worker thread for asynchronous image loading and a status bar widget
|
||||
* to track load progress.
|
||||
*/
|
||||
class CardPictureLoader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief Access the singleton instance of CardPictureLoader.
|
||||
* @return Reference to the singleton.
|
||||
*/
|
||||
static CardPictureLoader &getInstance()
|
||||
{
|
||||
static CardPictureLoader instance;
|
||||
|
|
@ -29,30 +50,87 @@ public:
|
|||
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;
|
||||
// Disable copy and assignment for singleton
|
||||
CardPictureLoader(CardPictureLoader const &) = delete;
|
||||
void operator=(CardPictureLoader const &) = delete;
|
||||
|
||||
CardPictureLoaderWorker *worker; ///< Worker thread for async image loading
|
||||
CardPictureLoaderStatusBar *statusBar; ///< Status bar widget showing load progress
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Retrieve a card pixmap, either from cache or enqueued for loading.
|
||||
* @param pixmap Reference to QPixmap where result will be stored.
|
||||
* @param card ExactCard to load.
|
||||
* @param size Desired size of pixmap.
|
||||
*/
|
||||
static void getPixmap(QPixmap &pixmap, const ExactCard &card, QSize size);
|
||||
|
||||
/**
|
||||
* @brief Retrieve a generic card back pixmap.
|
||||
* @param pixmap Reference to QPixmap where result will be stored.
|
||||
* @param size Desired size of pixmap.
|
||||
*/
|
||||
static void getCardBackPixmap(QPixmap &pixmap, QSize size);
|
||||
|
||||
/**
|
||||
* @brief Retrieve a card back pixmap for the loading-in-progress state.
|
||||
* @param pixmap Reference to QPixmap where result will be stored.
|
||||
* @param size Desired size of pixmap.
|
||||
*/
|
||||
static void getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size);
|
||||
|
||||
/**
|
||||
* @brief Retrieve a card back pixmap for the loading-failed state.
|
||||
* @param pixmap Reference to QPixmap where result will be stored.
|
||||
* @param size Desired size of pixmap.
|
||||
*/
|
||||
static void getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize size);
|
||||
static void clearPixmapCache();
|
||||
|
||||
/**
|
||||
* @brief Preload a list of cards into the pixmap cache (limited to CACHED_CARD_PER_DECK_MAX).
|
||||
* @param cards List of ExactCard objects to preload.
|
||||
*/
|
||||
static void cacheCardPixmaps(const QList<ExactCard> &cards);
|
||||
|
||||
/**
|
||||
* @brief Check if the user has custom card art in the picsPath directory.
|
||||
* @return True if any custom art exists.
|
||||
*/
|
||||
static bool hasCustomArt();
|
||||
|
||||
/**
|
||||
* @brief Clears the in-memory QPixmap cache for all cards.
|
||||
*/
|
||||
static void clearPixmapCache();
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Clears the network disk cache of the worker.
|
||||
*/
|
||||
static void clearNetworkCache();
|
||||
|
||||
private slots:
|
||||
void picDownloadChanged();
|
||||
void picsPathChanged();
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Slot called by the worker when an image is loaded.
|
||||
* Inserts the pixmap into the cache and emits pixmap updated signals.
|
||||
* @param card ExactCard that was loaded.
|
||||
* @param image Loaded QImage.
|
||||
*/
|
||||
void imageLoaded(const ExactCard &card, const QImage &image);
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Triggered when the user changes the picture download settings.
|
||||
* Clears the QPixmap cache to reload images.
|
||||
*/
|
||||
void picDownloadChanged();
|
||||
|
||||
/**
|
||||
* @brief Triggered when the pictures path changes.
|
||||
* Clears the QPixmap cache to reload images.
|
||||
*/
|
||||
void picsPathChanged();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -47,12 +47,6 @@ void CardPictureLoaderLocal::refreshIndex()
|
|||
<< 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();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
/**
|
||||
* @file card_picture_loader_local.h
|
||||
* @ingroup PictureLoader
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef PICTURE_LOADER_LOCAL_H
|
||||
#define PICTURE_LOADER_LOCAL_H
|
||||
|
||||
|
|
@ -15,32 +9,80 @@
|
|||
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
|
||||
* @ingroup PictureLoader
|
||||
* @brief Handles searching for and loading card images from local and custom image folders.
|
||||
*
|
||||
* This class maintains an index of the CUSTOM folder to avoid repeatedly scanning
|
||||
* directories, and supports periodic refreshes to update the index.
|
||||
*
|
||||
* Responsibilities:
|
||||
* - Load images for ExactCard objects from local disk or custom folders.
|
||||
* - Maintain an index for fast lookup of images in the CUSTOM folder.
|
||||
* - Refresh the index periodically to account for changes in local image directories.
|
||||
*/
|
||||
class CardPictureLoaderLocal : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a CardPictureLoaderLocal object.
|
||||
* @param parent Optional parent QObject.
|
||||
*
|
||||
* Initializes paths from SettingsCache, connects to settings change signals,
|
||||
* builds the initial folder index, and starts a periodic refresh timer.
|
||||
*/
|
||||
explicit CardPictureLoaderLocal(QObject *parent);
|
||||
|
||||
/**
|
||||
* @brief Attempts to load a card image from local disk or custom folders.
|
||||
* @param toLoad ExactCard object representing the card to load.
|
||||
* @return Loaded QImage if found; otherwise, an empty QImage.
|
||||
*
|
||||
* Uses a set of name variants and folder paths to attempt to locate the correct image.
|
||||
*/
|
||||
QImage tryLoad(const ExactCard &toLoad) const;
|
||||
|
||||
private:
|
||||
QString picsPath, customPicsPath;
|
||||
QString picsPath; ///< Path to standard card image folder
|
||||
QString customPicsPath; ///< Path to custom card image folder
|
||||
|
||||
QMultiHash<QString, QString> customFolderIndex; // multimap of cardName to picPaths
|
||||
QTimer *refreshTimer;
|
||||
QMultiHash<QString, QString> customFolderIndex; ///< Multimap from cardName to file paths in CUSTOM folder
|
||||
QTimer *refreshTimer; ///< Timer for periodic folder index refresh
|
||||
|
||||
/**
|
||||
* @brief Rebuilds the index of the custom image folder.
|
||||
*
|
||||
* Iterates through all subdirectories of the CUSTOM folder and populates
|
||||
* `customFolderIndex` with all discovered image files keyed by base name
|
||||
* and complete base name.
|
||||
*/
|
||||
void refreshIndex();
|
||||
|
||||
/**
|
||||
* @brief Attempts to load a card image from disk given its set and name info.
|
||||
* @param setName Corrected short name of the card's set.
|
||||
* @param correctedCardName Corrected card name (e.g., normalized name).
|
||||
* @param collectorNumber Collector number of the card.
|
||||
* @param providerId Optional provider UUID of the card.
|
||||
* @return Loaded QImage if found; otherwise, an empty QImage.
|
||||
*
|
||||
* Searches in both the custom folder index and standard pictures paths.
|
||||
* Uses several filename patterns to match card images, in order from
|
||||
* most-specific to least-specific.
|
||||
*/
|
||||
QImage tryLoadCardImageFromDisk(const QString &setName,
|
||||
const QString &correctedCardName,
|
||||
const QString &collectorNumber,
|
||||
const QString &providerId) const;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Updates internal paths when the user changes picture settings.
|
||||
*
|
||||
* Triggered by `SettingsCache::picsPathChanged`.
|
||||
*/
|
||||
void picsPathChanged();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,43 @@
|
|||
/**
|
||||
* @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
|
||||
* @ingroup PictureLoader
|
||||
* @brief A small widget to display the status of a single card image request.
|
||||
*
|
||||
* Displays:
|
||||
* - Card name
|
||||
* - Set short name
|
||||
* - Provider ID
|
||||
* - Start time of request
|
||||
* - Elapsed time since start
|
||||
* - Finished status
|
||||
* - URL of the requested image
|
||||
*/
|
||||
class CardPictureLoaderRequestStatusDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a new status widget for a specific card image request.
|
||||
* @param parent Parent widget
|
||||
* @param url URL of the image being requested
|
||||
* @param card The ExactCard being downloaded
|
||||
* @param setName Set name for the card
|
||||
*/
|
||||
CardPictureLoaderRequestStatusDisplayWidget(QWidget *parent,
|
||||
const QUrl &url,
|
||||
const ExactCard &card,
|
||||
const QString &setName);
|
||||
|
||||
/** Marks the request as finished */
|
||||
void setFinished()
|
||||
{
|
||||
finished->setText("True");
|
||||
|
|
@ -28,19 +45,25 @@ public:
|
|||
repaint();
|
||||
}
|
||||
|
||||
/** Returns whether the request has finished */
|
||||
[[nodiscard]] bool getFinished() const
|
||||
{
|
||||
return finished->text() == "True";
|
||||
}
|
||||
|
||||
/** Updates the elapsed time display */
|
||||
void setElapsedTime(const QString &_elapsedTime) const
|
||||
{
|
||||
elapsedTime->setText(_elapsedTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Queries the elapsed time in seconds since the request started
|
||||
* @return Elapsed time in seconds
|
||||
*/
|
||||
int queryElapsedSeconds()
|
||||
{
|
||||
if (!finished) {
|
||||
if (!getFinished()) {
|
||||
int elapsedSeconds = QDateTime::fromString(startTime->text()).secsTo(QDateTime::currentDateTime());
|
||||
elapsedTime->setText(QString::number(elapsedSeconds));
|
||||
update();
|
||||
|
|
@ -50,25 +73,27 @@ public:
|
|||
return elapsedTime->text().toInt();
|
||||
}
|
||||
|
||||
/** Returns the start time as a string */
|
||||
[[nodiscard]] QString getStartTime() const
|
||||
{
|
||||
return startTime->text();
|
||||
}
|
||||
|
||||
/** Returns the URL of the request */
|
||||
[[nodiscard]] QString getUrl() const
|
||||
{
|
||||
return url->text();
|
||||
}
|
||||
|
||||
private:
|
||||
QHBoxLayout *layout;
|
||||
QLabel *name;
|
||||
QLabel *setShortname;
|
||||
QLabel *providerId;
|
||||
QLabel *startTime;
|
||||
QLabel *elapsedTime;
|
||||
QLabel *finished;
|
||||
QLabel *url;
|
||||
QHBoxLayout *layout; ///< Horizontal layout for arranging labels
|
||||
QLabel *name; ///< Card name
|
||||
QLabel *setShortname; ///< Set short name
|
||||
QLabel *providerId; ///< Provider ID for the card
|
||||
QLabel *startTime; ///< Start time of the request
|
||||
QLabel *elapsedTime; ///< Elapsed time since start
|
||||
QLabel *finished; ///< Whether the request has finished
|
||||
QLabel *url; ///< URL of the requested image
|
||||
};
|
||||
|
||||
#endif // PICTURE_LOADER_REQUEST_STATUS_DISPLAY_WIDGET_H
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
/**
|
||||
* @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
|
||||
|
||||
|
|
@ -12,24 +6,56 @@
|
|||
|
||||
#include <QHBoxLayout>
|
||||
#include <QProgressBar>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
/**
|
||||
* @class CardPictureLoaderStatusBar
|
||||
* @ingroup PictureLoader
|
||||
* @brief Displays the status of card image downloads in a horizontal progress bar with a log popup.
|
||||
*
|
||||
* Responsibilities:
|
||||
* - Shows overall progress of image downloads.
|
||||
* - Maintains a log of individual requests via a popup (SettingsButtonWidget).
|
||||
* - Cleans up finished request entries automatically after a delay.
|
||||
*/
|
||||
class CardPictureLoaderStatusBar : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a status bar with progress bar and log button.
|
||||
* @param parent Parent widget
|
||||
*/
|
||||
explicit CardPictureLoaderStatusBar(QWidget *parent);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Adds a queued image download to the log and increments the progress bar maximum.
|
||||
* @param url URL of the image
|
||||
* @param card The card being loaded
|
||||
* @param setName The set name of the card
|
||||
*/
|
||||
void addQueuedImageLoad(const QUrl &url, const ExactCard &card, const QString &setName);
|
||||
|
||||
/**
|
||||
* @brief Marks an image as successfully loaded.
|
||||
* Updates the progress bar and marks the corresponding log entry as finished.
|
||||
* @param url URL of the successfully loaded image
|
||||
*/
|
||||
void addSuccessfulImageLoad(const QUrl &url);
|
||||
|
||||
/**
|
||||
* @brief Cleans up old entries from the log that have finished more than 10 seconds ago.
|
||||
* Adjusts the progress bar accordingly.
|
||||
*/
|
||||
void cleanOldEntries();
|
||||
|
||||
private:
|
||||
QHBoxLayout *layout;
|
||||
QProgressBar *progressBar;
|
||||
SettingsButtonWidget *loadLog;
|
||||
QTimer *cleaner;
|
||||
QHBoxLayout *layout; ///< Horizontal layout containing progress bar and log button
|
||||
QProgressBar *progressBar; ///< Progress bar showing overall download progress
|
||||
SettingsButtonWidget *loadLog; ///< Popup log showing individual request statuses
|
||||
QTimer *cleaner; ///< Timer for periodically cleaning old log entries
|
||||
};
|
||||
|
||||
#endif // PICTURE_LOADER_STATUS_BAR_H
|
||||
|
|
|
|||
|
|
@ -105,9 +105,6 @@ void CardPictureLoaderWorker::resetRequestQuota()
|
|||
processQueuedRequests();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps processing requests from the queue until it is empty or until the quota runs out.
|
||||
*/
|
||||
void CardPictureLoaderWorker::processQueuedRequests()
|
||||
{
|
||||
while (requestQuota > 0 && processSingleRequest()) {
|
||||
|
|
@ -115,10 +112,6 @@ void CardPictureLoaderWorker::processQueuedRequests()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
/**
|
||||
* @file card_picture_loader_worker.h
|
||||
* @ingroup PictureLoader
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef PICTURE_LOADER_WORKER_H
|
||||
#define PICTURE_LOADER_WORKER_H
|
||||
|
||||
|
|
@ -27,57 +21,131 @@
|
|||
#define REDIRECT_TIMESTAMP "timestamp"
|
||||
#define REDIRECT_CACHE_FILENAME "cache.ini"
|
||||
|
||||
inline Q_LOGGING_CATEGORY(CardPictureLoaderWorkerLog, "card_picture_loader.worker");
|
||||
inline Q_LOGGING_CATEGORY(CardPictureLoaderWorkerLog, "card_picture_loader.worker")
|
||||
|
||||
class CardPictureLoaderWorkerWork;
|
||||
class CardPictureLoaderWorkerWork;
|
||||
|
||||
/**
|
||||
* @class CardPictureLoaderWorker
|
||||
* @ingroup PictureLoader
|
||||
* @brief Handles asynchronous loading of card images, both locally and via network.
|
||||
*
|
||||
* Responsibilities:
|
||||
* - Maintain a queue of network image requests with rate-limiting.
|
||||
* - Load images from local cache first via CardPictureLoaderLocal.
|
||||
* - Handle network redirects and persistent caching of redirects.
|
||||
* - Deduplicate simultaneous requests for the same card.
|
||||
* - Emit signals for status updates and loaded images.
|
||||
*/
|
||||
class CardPictureLoaderWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a CardPictureLoaderWorker.
|
||||
*
|
||||
* Initializes network manager, cache, redirect cache, local loader, and request quota timer.
|
||||
*/
|
||||
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
|
||||
/**
|
||||
* @brief Enqueues an ExactCard for loading.
|
||||
* @param card ExactCard to load
|
||||
*
|
||||
* This will first try to load the image locally; if that fails, it will enqueue a network request.
|
||||
*/
|
||||
void enqueueImageLoad(const ExactCard &card);
|
||||
|
||||
/**
|
||||
* @brief Queues a network request for a given URL and worker thread.
|
||||
* @param url URL to load
|
||||
* @param worker Worker handling this request
|
||||
*/
|
||||
void queueRequest(const QUrl &url, CardPictureLoaderWorkerWork *worker);
|
||||
|
||||
/** @brief Clears the network cache and redirect cache. */
|
||||
void clearNetworkCache();
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Makes a network request for the given URL using the specified worker.
|
||||
* @param url URL to load
|
||||
* @param workThread Worker handling the request
|
||||
* @return The QNetworkReply object representing the request
|
||||
*/
|
||||
QNetworkReply *makeRequest(const QUrl &url, CardPictureLoaderWorkerWork *workThread);
|
||||
|
||||
/** @brief Processes all queued requests respecting the request quota. */
|
||||
void processQueuedRequests();
|
||||
|
||||
/**
|
||||
* @brief Processes a single queued request.
|
||||
* @return true if a request was processed, false if queue is empty.
|
||||
*/
|
||||
bool processSingleRequest();
|
||||
|
||||
/**
|
||||
* @brief Handles an image that has finished loading.
|
||||
* @param card The ExactCard that was loaded
|
||||
* @param image The loaded QImage; empty if loading failed
|
||||
*/
|
||||
void handleImageLoaded(const ExactCard &card, const QImage &image);
|
||||
|
||||
/** @brief Caches a redirect mapping between original and redirected URL. */
|
||||
void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl);
|
||||
|
||||
/** @brief Removes a URL from the network cache. */
|
||||
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;
|
||||
QThread *pictureLoaderThread; ///< Thread for executing worker tasks
|
||||
QNetworkAccessManager *networkManager; ///< Network manager for HTTP requests
|
||||
QNetworkDiskCache *cache; ///< Disk cache for downloaded images
|
||||
QHash<QUrl, QPair<QUrl, QDateTime>> redirectCache; ///< Maps original URLs to redirects with timestamp
|
||||
QString cacheFilePath; ///< Path to persistent redirect cache file
|
||||
static constexpr int CacheTTLInDays = 30; ///< Time-to-live for redirect cache entries (days)
|
||||
bool picDownload; ///< Whether downloading images from network is enabled
|
||||
QQueue<QPair<QUrl, CardPictureLoaderWorkerWork *>> requestLoadQueue; ///< Queue of pending network requests
|
||||
|
||||
int requestQuota;
|
||||
QTimer requestTimer; // Timer for refreshing request quota
|
||||
int requestQuota; ///< Remaining requests allowed per second
|
||||
QTimer requestTimer; ///< Timer to reset the request quota
|
||||
|
||||
CardPictureLoaderLocal *localLoader;
|
||||
QSet<QString> currentlyLoading; // for deduplication purposes. Contains pixmapCacheKey
|
||||
CardPictureLoaderLocal *localLoader; ///< Loader for local images
|
||||
QSet<QString> currentlyLoading; ///< Deduplication: contains pixmapCacheKey currently being loaded
|
||||
|
||||
/** @brief Returns cached redirect URL for the given original URL, if available. */
|
||||
[[nodiscard]] QUrl getCachedRedirect(const QUrl &originalUrl) const;
|
||||
|
||||
/** @brief Loads redirect cache from disk. */
|
||||
void loadRedirectCache();
|
||||
|
||||
/** @brief Saves redirect cache to disk. */
|
||||
void saveRedirectCache() const;
|
||||
|
||||
/** @brief Removes stale redirect entries older than TTL. */
|
||||
void cleanStaleEntries();
|
||||
|
||||
private slots:
|
||||
/** @brief Resets the request quota for rate-limiting. */
|
||||
void resetRequestQuota();
|
||||
|
||||
/** @brief Handles image load requests enqueued on this worker. */
|
||||
void handleImageLoadEnqueued(const ExactCard &card);
|
||||
|
||||
signals:
|
||||
/** @brief Emitted when an image load is enqueued. */
|
||||
void imageLoadEnqueued(const ExactCard &card);
|
||||
|
||||
/** @brief Emitted when an image has finished loading. */
|
||||
void imageLoaded(const ExactCard &card, const QImage &image);
|
||||
|
||||
/** @brief Emitted when a request is added to the network queue. */
|
||||
void imageRequestQueued(const QUrl &url, const ExactCard &card, const QString &setName);
|
||||
|
||||
/** @brief Emitted when a network request successfully completes. */
|
||||
void imageRequestSucceeded(const QUrl &url);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -51,10 +51,6 @@ void CardPictureLoaderWorkerWork::startNextPicDownload()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -73,10 +69,6 @@ void CardPictureLoaderWorkerWork::picDownloadFailed()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param reply The reply. Takes ownership of the object
|
||||
*/
|
||||
void CardPictureLoaderWorkerWork::handleNetworkReply(QNetworkReply *reply)
|
||||
{
|
||||
QVariant redirectTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
|
|
@ -180,10 +172,6 @@ void CardPictureLoaderWorkerWork::handleSuccessfulReply(QNetworkReply *reply)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
|
@ -207,10 +195,6 @@ QImage CardPictureLoaderWorkerWork::tryLoadImageFromReply(QNetworkReply *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);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
/**
|
||||
* @file picture_loader_worker_work.h
|
||||
* @ingroup PictureLoader
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef PICTURE_LOADER_WORKER_WORK_H
|
||||
#define PICTURE_LOADER_WORKER_WORK_H
|
||||
|
||||
|
|
@ -17,55 +11,96 @@
|
|||
#include <QThread>
|
||||
#include <libcockatrice/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
|
||||
* @ingroup PictureLoader
|
||||
* @brief Handles downloading a single card image from network or local sources.
|
||||
*
|
||||
* Responsibilities:
|
||||
* - Try to load images from all possible URLs and sets for a given ExactCard.
|
||||
* - Handle network redirects and cache invalidation.
|
||||
* - Check for blacklisted images and discard them.
|
||||
* - Emit signals when image is successfully loaded or all attempts fail.
|
||||
* - Delete itself after loading completes.
|
||||
*/
|
||||
class CardPictureLoaderWorkerWork : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a worker for downloading a specific card image.
|
||||
* @param worker The orchestrating CardPictureLoaderWorker
|
||||
* @param toLoad The ExactCard to download
|
||||
*/
|
||||
explicit CardPictureLoaderWorkerWork(const CardPictureLoaderWorker *worker, const ExactCard &toLoad);
|
||||
|
||||
CardPictureToLoad cardToDownload;
|
||||
CardPictureToLoad cardToDownload; ///< The card and associated URLs to try downloading
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Handles a finished network reply for the card image.
|
||||
* @param reply The QNetworkReply object to process. Ownership is transferred.
|
||||
*/
|
||||
void handleNetworkReply(QNetworkReply *reply);
|
||||
|
||||
private:
|
||||
bool picDownload;
|
||||
bool picDownload; ///< Whether network downloading is enabled
|
||||
|
||||
/** @brief Starts downloading the next URL for this card. */
|
||||
void startNextPicDownload();
|
||||
|
||||
/** @brief Called when all URLs have been exhausted or download failed. */
|
||||
void picDownloadFailed();
|
||||
|
||||
/** @brief Processes a failed network reply. */
|
||||
void handleFailedReply(const QNetworkReply *reply);
|
||||
|
||||
/** @brief Processes a successful network reply. */
|
||||
void handleSuccessfulReply(QNetworkReply *reply);
|
||||
|
||||
/**
|
||||
* @brief Attempts to read an image from a network reply.
|
||||
* @param reply The network reply
|
||||
* @return The loaded QImage or an empty image if reading failed
|
||||
*/
|
||||
QImage tryLoadImageFromReply(QNetworkReply *reply);
|
||||
|
||||
/**
|
||||
* @brief Finalizes the image loading process.
|
||||
* @param image The loaded image (empty if failed)
|
||||
*
|
||||
* Emits imageLoaded() and deletes this object.
|
||||
*/
|
||||
void concludeImageLoad(const QImage &image);
|
||||
|
||||
private slots:
|
||||
/** @brief Updates the picDownload setting when it changes. */
|
||||
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.
|
||||
* @brief Emitted when the image has been loaded or all attempts failed.
|
||||
* @param card The card corresponding to the image
|
||||
* @param image The loaded image (empty if failed)
|
||||
*
|
||||
* The worker deletes itself after emitting this signal.
|
||||
*/
|
||||
void imageLoaded(const ExactCard &card, const QImage &image);
|
||||
|
||||
/**
|
||||
* Emitted when a request did not return a 400 or 500 response
|
||||
*/
|
||||
/** @brief Emitted when a network request completes successfully. */
|
||||
void requestSucceeded(const QUrl &url);
|
||||
|
||||
/** @brief Request that a URL be downloaded. */
|
||||
void requestImageDownload(const QUrl &url, CardPictureLoaderWorkerWork *instance);
|
||||
|
||||
/** @brief Emitted when a URL has been redirected. */
|
||||
void urlRedirected(const QUrl &originalUrl, const QUrl &redirectUrl);
|
||||
|
||||
/** @brief Emitted when a cached URL is invalid and must be removed. */
|
||||
void cachedUrlInvalidated(const QUrl &url);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,12 +20,6 @@ CardPictureToLoad::CardPictureToLoad(const ExactCard &_card)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
|
@ -109,11 +103,6 @@ void CardPictureToLoad::populateSetUrls()
|
|||
(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()) {
|
||||
|
|
@ -125,11 +114,6 @@ bool CardPictureToLoad::nextSet()
|
|||
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()) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
/**
|
||||
* @file card_picture_to_load.h
|
||||
* @ingroup PictureLoader
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef PICTURE_TO_LOAD_H
|
||||
#define PICTURE_TO_LOAD_H
|
||||
|
||||
|
|
@ -12,37 +6,97 @@
|
|||
|
||||
inline Q_LOGGING_CATEGORY(CardPictureToLoadLog, "card_picture_loader.picture_to_load");
|
||||
|
||||
/**
|
||||
* @class CardPictureToLoad
|
||||
* @ingroup PictureLoader
|
||||
* @brief Manages all URLs and sets for downloading a specific card image.
|
||||
*
|
||||
* Responsibilities:
|
||||
* - Maintains a sorted list of sets for a card.
|
||||
* - Generates URLs to download images from, including custom URLs and URL templates.
|
||||
* - Tracks which URL and set is currently being attempted.
|
||||
* - Provides helper methods to advance to next URL or set.
|
||||
*/
|
||||
class CardPictureToLoad
|
||||
{
|
||||
private:
|
||||
ExactCard card;
|
||||
QList<CardSetPtr> sortedSets;
|
||||
QList<QString> urlTemplates;
|
||||
QList<QString> currentSetUrls;
|
||||
QString currentUrl;
|
||||
CardSetPtr currentSet;
|
||||
ExactCard card; ///< The ExactCard being downloaded
|
||||
QList<CardSetPtr> sortedSets; ///< All sets for this card, sorted by priority
|
||||
QList<QString> urlTemplates; ///< URL templates from settings
|
||||
QList<QString> currentSetUrls; ///< URLs for the current set being attempted
|
||||
QString currentUrl; ///< Currently active URL to download
|
||||
CardSetPtr currentSet; ///< Currently active set
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a CardPictureToLoad for a given ExactCard.
|
||||
* @param _card The card to download
|
||||
*
|
||||
* Initializes URL templates and pre-populates the first set URLs.
|
||||
*/
|
||||
explicit CardPictureToLoad(const ExactCard &_card);
|
||||
|
||||
/** @return The card being loaded. */
|
||||
[[nodiscard]] const ExactCard &getCard() const
|
||||
{
|
||||
return card;
|
||||
}
|
||||
|
||||
/** @return The current URL being attempted. */
|
||||
[[nodiscard]] QString getCurrentUrl() const
|
||||
{
|
||||
return currentUrl;
|
||||
}
|
||||
|
||||
/** @return The current set being attempted. */
|
||||
[[nodiscard]] CardSetPtr getCurrentSet() const
|
||||
{
|
||||
return currentSet;
|
||||
}
|
||||
|
||||
/** @return The short name of the current set, or empty string if no set. */
|
||||
[[nodiscard]] QString getSetName() const;
|
||||
[[nodiscard]] QString transformUrl(const QString &urlTemplate) const;
|
||||
|
||||
/**
|
||||
* @brief Transforms a URL template into a concrete URL for this card/set.
|
||||
* @param urlTemplate The URL template to transform
|
||||
* @return The transformed URL or empty string if the template cannot be fulfilled
|
||||
*/
|
||||
QString transformUrl(const QString &urlTemplate) const;
|
||||
|
||||
/**
|
||||
* @brief Advance to the next set in the list.
|
||||
* @return True if a next set exists and was selected, false if at the end.
|
||||
*
|
||||
* Updates currentSet and repopulates currentSetUrls.
|
||||
* If we are already at the end of the list, then currentSet is set to empty.
|
||||
*/
|
||||
bool nextSet();
|
||||
|
||||
/**
|
||||
* @brief Advance to the next URL in the current set's list.
|
||||
* @return True if a next URL exists, false if at the end.
|
||||
*
|
||||
* Updates currentUrl.
|
||||
* If we are already at the end of the list, then currentUrl is set to empty.
|
||||
*/
|
||||
bool nextUrl();
|
||||
|
||||
/**
|
||||
* @brief Populates the currentSetUrls list with URLs for the current set.
|
||||
*
|
||||
* Includes custom URLs first, followed by template-based URLs.
|
||||
*/
|
||||
void populateSetUrls();
|
||||
|
||||
/**
|
||||
* @brief Extract all sets from the card and sort them by priority.
|
||||
* @param card The card to extract sets from
|
||||
* @return A non-empty list of CardSetPtr, sorted by priority
|
||||
*
|
||||
* If the card has no sets, a dummy set is inserted. Also ensures
|
||||
* the printing corresponding to the ExactCard is first in the list.
|
||||
*/
|
||||
static QList<CardSetPtr> extractSetsSorted(const ExactCard &card);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -78,20 +78,14 @@ void ManaBaseWidget::updateDisplay()
|
|||
QHash<QString, int> ManaBaseWidget::analyzeManaBase()
|
||||
{
|
||||
manaBaseMap.clear();
|
||||
InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
|
||||
for (int i = 0; i < listRoot->size(); i++) {
|
||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
for (int j = 0; j < currentZone->size(); j++) {
|
||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
if (!currentCard)
|
||||
continue;
|
||||
QList<DecklistCardNode *> cardsInDeck = deckListModel->getDeckList()->getCardNodes();
|
||||
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
|
||||
if (info) {
|
||||
auto devotion = determineManaProduction(info->getText());
|
||||
mergeManaCounts(manaBaseMap, devotion);
|
||||
}
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
|
||||
if (info) {
|
||||
auto devotion = determineManaProduction(info->getText());
|
||||
mergeManaCounts(manaBaseMap, devotion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,20 +44,15 @@ void ManaCurveWidget::setDeckModel(DeckListModel *deckModel)
|
|||
std::unordered_map<int, int> ManaCurveWidget::analyzeManaCurve()
|
||||
{
|
||||
manaCurveMap.clear();
|
||||
InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
|
||||
for (int i = 0; i < listRoot->size(); i++) {
|
||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
for (int j = 0; j < currentZone->size(); j++) {
|
||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
if (!currentCard)
|
||||
continue;
|
||||
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
|
||||
if (info) {
|
||||
int cmc = info->getCmc().toInt();
|
||||
manaCurveMap[cmc]++;
|
||||
}
|
||||
QList<DecklistCardNode *> cardsInDeck = deckListModel->getDeckList()->getCardNodes();
|
||||
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
|
||||
if (info) {
|
||||
int cmc = info->getCmc().toInt();
|
||||
manaCurveMap[cmc]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,20 +46,15 @@ void ManaDevotionWidget::setDeckModel(DeckListModel *deckModel)
|
|||
std::unordered_map<char, int> ManaDevotionWidget::analyzeManaDevotion()
|
||||
{
|
||||
manaDevotionMap.clear();
|
||||
InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
|
||||
for (int i = 0; i < listRoot->size(); i++) {
|
||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
for (int j = 0; j < currentZone->size(); j++) {
|
||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
if (!currentCard)
|
||||
continue;
|
||||
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
|
||||
if (info) {
|
||||
auto devotion = countManaSymbols(info->getManaCost());
|
||||
mergeManaCounts(manaDevotionMap, devotion);
|
||||
}
|
||||
QList<DecklistCardNode *> cardsInDeck = deckListModel->getDeckList()->getCardNodes();
|
||||
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
|
||||
if (info) {
|
||||
auto devotion = countManaSymbols(info->getManaCost());
|
||||
mergeManaCounts(manaDevotionMap, devotion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
|||
|
||||
deckLoader = new DeckLoader(this, deckModel->getDeckList());
|
||||
|
||||
DeckListStyleProxy *proxy = new DeckListStyleProxy(this);
|
||||
proxy = new DeckListStyleProxy(this);
|
||||
proxy->setSourceModel(deckModel);
|
||||
|
||||
deckView = new QTreeView();
|
||||
|
|
@ -283,18 +283,12 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
|
|||
|
||||
// Prepare the new items with deduplication
|
||||
QSet<QPair<QString, QString>> bannerCardSet;
|
||||
InnerDecklistNode *listRoot = deckModel->getDeckList()->getRoot();
|
||||
for (int i = 0; i < listRoot->size(); i++) {
|
||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
for (int j = 0; j < currentZone->size(); j++) {
|
||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
if (!currentCard)
|
||||
continue;
|
||||
QList<DecklistCardNode *> cardsInDeck = deckModel->getDeckList()->getCardNodes();
|
||||
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
if (CardDatabaseManager::query()->getCard(currentCard->toCardRef())) {
|
||||
bannerCardSet.insert({currentCard->getName(), currentCard->getCardProviderId()});
|
||||
}
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
if (CardDatabaseManager::query()->getCard(currentCard->toCardRef())) {
|
||||
bannerCardSet.insert({currentCard->getName(), currentCard->getCardProviderId()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -438,7 +432,9 @@ QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodes() const
|
|||
{
|
||||
auto selectedRows = deckView->selectionModel()->selectedRows();
|
||||
|
||||
const auto notLeafNode = [this](const auto &index) { return deckModel->hasChildren(index); };
|
||||
const auto notLeafNode = [this](const QModelIndex &index) {
|
||||
return deckModel->hasChildren(proxy->mapToSource(index));
|
||||
};
|
||||
selectedRows.erase(std::remove_if(selectedRows.begin(), selectedRows.end(), notLeafNode), selectedRows.end());
|
||||
|
||||
std::reverse(selectedRows.begin(), selectedRows.end());
|
||||
|
|
@ -505,7 +501,7 @@ bool DeckEditorDeckDockWidget::swapCard(const QModelIndex ¤tIndex)
|
|||
QModelIndex newCardIndex = card ? deckModel->addCard(card, otherZoneName)
|
||||
// Third argument (true) says create the card no matter what, even if not in DB
|
||||
: deckModel->addPreferredPrintingCard(cardName, otherZoneName, true);
|
||||
recursiveExpand(newCardIndex);
|
||||
recursiveExpand(proxy->mapToSource(newCardIndex));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -526,7 +522,7 @@ void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString z
|
|||
}
|
||||
|
||||
deckView->clearSelection();
|
||||
deckView->setCurrentIndex(idx);
|
||||
deckView->setCurrentIndex(proxy->mapToSource(idx));
|
||||
offsetCountAtIndex(idx, -1);
|
||||
}
|
||||
|
||||
|
|
@ -562,7 +558,8 @@ void DeckEditorDeckDockWidget::actRemoveCard()
|
|||
if (!index.isValid() || deckModel->hasChildren(index)) {
|
||||
continue;
|
||||
}
|
||||
deckModel->removeRow(index.row(), index.parent());
|
||||
QModelIndex sourceIndex = proxy->mapToSource(index);
|
||||
deckModel->removeRow(sourceIndex.row(), sourceIndex.parent());
|
||||
isModified = true;
|
||||
}
|
||||
|
||||
|
|
@ -579,13 +576,17 @@ void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, int of
|
|||
return;
|
||||
}
|
||||
|
||||
const QModelIndex numberIndex = idx.sibling(idx.row(), 0);
|
||||
QModelIndex sourceIndex = proxy->mapToSource(idx);
|
||||
|
||||
const QModelIndex numberIndex = sourceIndex.sibling(sourceIndex.row(), 0);
|
||||
const int count = deckModel->data(numberIndex, Qt::EditRole).toInt();
|
||||
const int new_count = count + offset;
|
||||
if (new_count <= 0)
|
||||
deckModel->removeRow(idx.row(), idx.parent());
|
||||
else
|
||||
|
||||
if (new_count <= 0) {
|
||||
deckModel->removeRow(sourceIndex.row(), sourceIndex.parent());
|
||||
} else {
|
||||
deckModel->setData(numberIndex, new_count, Qt::EditRole);
|
||||
}
|
||||
|
||||
emit deckModified();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "../../key_signals.h"
|
||||
#include "../utility/custom_line_edit.h"
|
||||
#include "../visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
|
||||
#include "deck_list_style_proxy.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDockWidget>
|
||||
|
|
@ -28,6 +29,7 @@ class DeckEditorDeckDockWidget : public QDockWidget
|
|||
public:
|
||||
explicit DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent);
|
||||
DeckLoader *deckLoader;
|
||||
DeckListStyleProxy *proxy;
|
||||
DeckListModel *deckModel;
|
||||
QTreeView *deckView;
|
||||
QComboBox *bannerCardComboBox;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ void DlgCreateGame::sharedCtor()
|
|||
maxPlayersEdit->setValue(2);
|
||||
maxPlayersLabel->setBuddy(maxPlayersEdit);
|
||||
|
||||
QGridLayout *generalGrid = new QGridLayout;
|
||||
auto *generalGrid = new QGridLayout;
|
||||
generalGrid->addWidget(descriptionLabel, 0, 0);
|
||||
generalGrid->addWidget(descriptionEdit, 0, 1);
|
||||
generalGrid->addWidget(maxPlayersLabel, 1, 0);
|
||||
|
|
@ -43,17 +43,17 @@ void DlgCreateGame::sharedCtor()
|
|||
generalGroupBox = new QGroupBox(tr("General"));
|
||||
generalGroupBox->setLayout(generalGrid);
|
||||
|
||||
QVBoxLayout *gameTypeLayout = new QVBoxLayout;
|
||||
auto *gameTypeLayout = new QVBoxLayout;
|
||||
QMapIterator<int, QString> gameTypeIterator(gameTypes);
|
||||
while (gameTypeIterator.hasNext()) {
|
||||
gameTypeIterator.next();
|
||||
QRadioButton *gameTypeRadioButton = new QRadioButton(gameTypeIterator.value(), this);
|
||||
auto *gameTypeRadioButton = new QRadioButton(gameTypeIterator.value(), this);
|
||||
gameTypeLayout->addWidget(gameTypeRadioButton);
|
||||
gameTypeCheckBoxes.insert(gameTypeIterator.key(), gameTypeRadioButton);
|
||||
bool isChecked = SettingsCache::instance().getGameTypes().contains(gameTypeIterator.value() + ", ");
|
||||
gameTypeCheckBoxes[gameTypeIterator.key()]->setChecked(isChecked);
|
||||
}
|
||||
QGroupBox *gameTypeGroupBox = new QGroupBox(tr("Game type"));
|
||||
auto *gameTypeGroupBox = new QGroupBox(tr("Game type"));
|
||||
gameTypeGroupBox->setLayout(gameTypeLayout);
|
||||
|
||||
passwordLabel = new QLabel(tr("&Password:"));
|
||||
|
|
@ -70,13 +70,13 @@ void DlgCreateGame::sharedCtor()
|
|||
onlyRegisteredCheckBox->setEnabled(false);
|
||||
}
|
||||
|
||||
QGridLayout *joinRestrictionsLayout = new QGridLayout;
|
||||
auto *joinRestrictionsLayout = new QGridLayout;
|
||||
joinRestrictionsLayout->addWidget(passwordLabel, 0, 0);
|
||||
joinRestrictionsLayout->addWidget(passwordEdit, 0, 1);
|
||||
joinRestrictionsLayout->addWidget(onlyBuddiesCheckBox, 1, 0, 1, 2);
|
||||
joinRestrictionsLayout->addWidget(onlyRegisteredCheckBox, 2, 0, 1, 2);
|
||||
|
||||
QGroupBox *joinRestrictionsGroupBox = new QGroupBox(tr("Joining restrictions"));
|
||||
auto *joinRestrictionsGroupBox = new QGroupBox(tr("Joining restrictions"));
|
||||
joinRestrictionsGroupBox->setLayout(joinRestrictionsLayout);
|
||||
|
||||
spectatorsAllowedCheckBox = new QCheckBox(tr("&Spectators can watch"));
|
||||
|
|
@ -86,7 +86,7 @@ void DlgCreateGame::sharedCtor()
|
|||
spectatorsCanTalkCheckBox = new QCheckBox(tr("Spectators can &chat"));
|
||||
spectatorsSeeEverythingCheckBox = new QCheckBox(tr("Spectators can see &hands"));
|
||||
createGameAsSpectatorCheckBox = new QCheckBox(tr("Create game as spectator"));
|
||||
QVBoxLayout *spectatorsLayout = new QVBoxLayout;
|
||||
auto *spectatorsLayout = new QVBoxLayout;
|
||||
spectatorsLayout->addWidget(spectatorsAllowedCheckBox);
|
||||
spectatorsLayout->addWidget(spectatorsNeedPasswordCheckBox);
|
||||
spectatorsLayout->addWidget(spectatorsCanTalkCheckBox);
|
||||
|
|
@ -106,7 +106,7 @@ void DlgCreateGame::sharedCtor()
|
|||
shareDecklistsOnLoadCheckBox = new QCheckBox();
|
||||
shareDecklistsOnLoadLabel->setBuddy(shareDecklistsOnLoadCheckBox);
|
||||
|
||||
QGridLayout *gameSetupOptionsLayout = new QGridLayout;
|
||||
auto *gameSetupOptionsLayout = new QGridLayout;
|
||||
gameSetupOptionsLayout->addWidget(startingLifeTotalLabel, 0, 0);
|
||||
gameSetupOptionsLayout->addWidget(startingLifeTotalEdit, 0, 1);
|
||||
gameSetupOptionsLayout->addWidget(shareDecklistsOnLoadLabel, 1, 0);
|
||||
|
|
@ -136,7 +136,7 @@ void DlgCreateGame::sharedCtor()
|
|||
buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgCreateGame::reject);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(grid);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
|
||||
|
|
@ -275,7 +275,7 @@ void DlgCreateGame::actOK()
|
|||
cmd.set_starting_life_total(startingLifeTotalEdit->value());
|
||||
cmd.set_share_decklists_on_load(shareDecklistsOnLoadCheckBox->isChecked());
|
||||
|
||||
QString _gameTypes = QString();
|
||||
auto _gameTypes = QString();
|
||||
QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes);
|
||||
while (gameTypeCheckBoxIterator.hasNext()) {
|
||||
gameTypeCheckBoxIterator.next();
|
||||
|
|
|
|||
|
|
@ -23,16 +23,16 @@ DlgEditAvatar::DlgEditAvatar(QWidget *parent) : QDialog(parent), image()
|
|||
browseButton = new QPushButton(tr("Browse..."));
|
||||
connect(browseButton, &QPushButton::clicked, this, &DlgEditAvatar::actBrowse);
|
||||
|
||||
QGridLayout *grid = new QGridLayout;
|
||||
auto *grid = new QGridLayout;
|
||||
grid->addWidget(imageLabel, 0, 0, 1, 2);
|
||||
grid->addWidget(textLabel, 1, 0);
|
||||
grid->addWidget(browseButton, 1, 1);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgEditAvatar::actOk);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgEditAvatar::reject);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(grid);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
setLayout(mainLayout);
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ DlgForgotPasswordChallenge::DlgForgotPasswordChallenge(QWidget *parent) : QDialo
|
|||
playernameEdit->hide();
|
||||
}
|
||||
|
||||
QGridLayout *grid = new QGridLayout;
|
||||
auto *grid = new QGridLayout;
|
||||
grid->addWidget(infoLabel, 0, 0, 1, 2);
|
||||
grid->addWidget(hostLabel, 1, 0);
|
||||
grid->addWidget(hostEdit, 1, 1);
|
||||
|
|
@ -77,11 +77,11 @@ DlgForgotPasswordChallenge::DlgForgotPasswordChallenge(QWidget *parent) : QDialo
|
|||
grid->addWidget(emailLabel, 4, 0);
|
||||
grid->addWidget(emailEdit, 4, 1);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgForgotPasswordChallenge::actOk);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgForgotPasswordChallenge::reject);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(grid);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
setLayout(mainLayout);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ DlgForgotPasswordRequest::DlgForgotPasswordRequest(QWidget *parent) : QDialog(pa
|
|||
playernameEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
playernameLabel->setBuddy(playernameEdit);
|
||||
|
||||
QGridLayout *grid = new QGridLayout;
|
||||
auto *grid = new QGridLayout;
|
||||
grid->addWidget(infoLabel, 0, 0, 1, 2);
|
||||
grid->addWidget(hostLabel, 1, 0);
|
||||
grid->addWidget(hostEdit, 1, 1);
|
||||
|
|
@ -54,11 +54,11 @@ DlgForgotPasswordRequest::DlgForgotPasswordRequest(QWidget *parent) : QDialog(pa
|
|||
grid->addWidget(playernameLabel, 3, 0);
|
||||
grid->addWidget(playernameEdit, 3, 1);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgForgotPasswordRequest::actOk);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgForgotPasswordRequest::reject);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(grid);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
setLayout(mainLayout);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ AbstractDlgDeckTextEdit::AbstractDlgDeckTextEdit(QWidget *parent) : QDialog(pare
|
|||
refreshButton = new QPushButton(tr("&Refresh"));
|
||||
connect(refreshButton, &QPushButton::clicked, this, &AbstractDlgDeckTextEdit::actRefresh);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
buttonBox->addButton(refreshButton, QDialogButtonBox::ActionRole);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &AbstractDlgDeckTextEdit::actOK);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &AbstractDlgDeckTextEdit::reject);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ DlgLoadRemoteDeck::DlgLoadRemoteDeck(AbstractClient *_client, QWidget *parent) :
|
|||
connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgLoadRemoteDeck::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgLoadRemoteDeck::reject);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(dirView);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ DlgRegister::DlgRegister(QWidget *parent) : QDialog(parent)
|
|||
realnameEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
realnameLabel->setBuddy(realnameEdit);
|
||||
|
||||
QGridLayout *grid = new QGridLayout;
|
||||
auto *grid = new QGridLayout;
|
||||
grid->addWidget(infoLabel, 0, 0, 1, 2);
|
||||
grid->addWidget(hostLabel, 1, 0);
|
||||
grid->addWidget(hostEdit, 1, 1);
|
||||
|
|
@ -342,11 +342,11 @@ DlgRegister::DlgRegister(QWidget *parent) : QDialog(parent)
|
|||
grid->addWidget(realnameLabel, 10, 0);
|
||||
grid->addWidget(realnameEdit, 10, 1);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgRegister::actOk);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgRegister::reject);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(grid);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
setLayout(mainLayout);
|
||||
|
|
|
|||
|
|
@ -209,30 +209,19 @@ QMap<QString, int> DlgSelectSetForCards::getSetsForCards()
|
|||
if (!decklist)
|
||||
return setCounts;
|
||||
|
||||
InnerDecklistNode *listRoot = decklist->getRoot();
|
||||
if (!listRoot)
|
||||
return setCounts;
|
||||
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes();
|
||||
|
||||
for (auto *i : *listRoot) {
|
||||
auto *countCurrentZone = dynamic_cast<InnerDecklistNode *>(i);
|
||||
if (!countCurrentZone)
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
|
||||
if (!infoPtr)
|
||||
continue;
|
||||
|
||||
for (auto *cardNode : *countCurrentZone) {
|
||||
auto *currentCard = dynamic_cast<DecklistCardNode *>(cardNode);
|
||||
if (!currentCard)
|
||||
continue;
|
||||
|
||||
CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
|
||||
if (!infoPtr)
|
||||
continue;
|
||||
|
||||
SetToPrintingsMap setMap = infoPtr->getSets();
|
||||
for (auto &setName : setMap.keys()) {
|
||||
setCounts[setName]++;
|
||||
}
|
||||
SetToPrintingsMap setMap = infoPtr->getSets();
|
||||
for (auto &setName : setMap.keys()) {
|
||||
setCounts[setName]++;
|
||||
}
|
||||
}
|
||||
|
||||
return setCounts;
|
||||
}
|
||||
|
||||
|
|
@ -263,48 +252,36 @@ void DlgSelectSetForCards::updateCardLists()
|
|||
if (!decklist)
|
||||
return;
|
||||
|
||||
InnerDecklistNode *listRoot = decklist->getRoot();
|
||||
if (!listRoot)
|
||||
return;
|
||||
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes();
|
||||
|
||||
for (auto *i : *listRoot) {
|
||||
auto *countCurrentZone = dynamic_cast<InnerDecklistNode *>(i);
|
||||
if (!countCurrentZone)
|
||||
continue;
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
bool found = false;
|
||||
QString foundSetName;
|
||||
|
||||
for (auto *cardNode : *countCurrentZone) {
|
||||
auto *currentCard = dynamic_cast<DecklistCardNode *>(cardNode);
|
||||
if (!currentCard)
|
||||
continue;
|
||||
|
||||
bool found = false;
|
||||
QString foundSetName;
|
||||
|
||||
// Check across all sets if the card is present
|
||||
for (auto it = selectedCardsBySet.begin(); it != selectedCardsBySet.end(); ++it) {
|
||||
if (it.value().contains(currentCard->getName())) {
|
||||
found = true;
|
||||
foundSetName = it.key(); // Store the set name where it was found
|
||||
break; // Stop at the first match
|
||||
}
|
||||
// Check across all sets if the card is present
|
||||
for (auto it = selectedCardsBySet.begin(); it != selectedCardsBySet.end(); ++it) {
|
||||
if (it.value().contains(currentCard->getName())) {
|
||||
found = true;
|
||||
foundSetName = it.key(); // Store the set name where it was found
|
||||
break; // Stop at the first match
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// The card was not in any selected set
|
||||
ExactCard card = CardDatabaseManager::query()->getCard({currentCard->getName()});
|
||||
CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(uneditedCardsFlowWidget);
|
||||
picture_widget->setCard(card);
|
||||
uneditedCardsFlowWidget->addWidget(picture_widget);
|
||||
} else {
|
||||
ExactCard card = CardDatabaseManager::query()->getCard(
|
||||
{currentCard->getName(), CardDatabaseManager::getInstance()
|
||||
->query()
|
||||
->getSpecificPrinting(currentCard->getName(), foundSetName, "")
|
||||
.getUuid()});
|
||||
CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(modifiedCardsFlowWidget);
|
||||
picture_widget->setCard(card);
|
||||
modifiedCardsFlowWidget->addWidget(picture_widget);
|
||||
}
|
||||
if (!found) {
|
||||
// The card was not in any selected set
|
||||
ExactCard card = CardDatabaseManager::query()->getCard({currentCard->getName()});
|
||||
CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(uneditedCardsFlowWidget);
|
||||
picture_widget->setCard(card);
|
||||
uneditedCardsFlowWidget->addWidget(picture_widget);
|
||||
} else {
|
||||
ExactCard card = CardDatabaseManager::query()->getCard(
|
||||
{currentCard->getName(), CardDatabaseManager::getInstance()
|
||||
->query()
|
||||
->getSpecificPrinting(currentCard->getName(), foundSetName, "")
|
||||
.getUuid()});
|
||||
CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(modifiedCardsFlowWidget);
|
||||
picture_widget->setCard(card);
|
||||
modifiedCardsFlowWidget->addWidget(picture_widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -364,30 +341,19 @@ QMap<QString, QStringList> DlgSelectSetForCards::getCardsForSets()
|
|||
if (!decklist)
|
||||
return setCards;
|
||||
|
||||
InnerDecklistNode *listRoot = decklist->getRoot();
|
||||
if (!listRoot)
|
||||
return setCards;
|
||||
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes();
|
||||
|
||||
for (auto *i : *listRoot) {
|
||||
auto *countCurrentZone = dynamic_cast<InnerDecklistNode *>(i);
|
||||
if (!countCurrentZone)
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
|
||||
if (!infoPtr)
|
||||
continue;
|
||||
|
||||
for (auto *cardNode : *countCurrentZone) {
|
||||
auto *currentCard = dynamic_cast<DecklistCardNode *>(cardNode);
|
||||
if (!currentCard)
|
||||
continue;
|
||||
|
||||
CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
|
||||
if (!infoPtr)
|
||||
continue;
|
||||
|
||||
SetToPrintingsMap setMap = infoPtr->getSets();
|
||||
for (auto it = setMap.begin(); it != setMap.end(); ++it) {
|
||||
setCards[it.key()].append(currentCard->getName());
|
||||
}
|
||||
SetToPrintingsMap setMap = infoPtr->getSets();
|
||||
for (auto it = setMap.begin(); it != setMap.end(); ++it) {
|
||||
setCards[it.key()].append(currentCard->getName());
|
||||
}
|
||||
}
|
||||
|
||||
return setCards;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -141,38 +141,38 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
|
||||
deckPathEdit = new QLineEdit(settings.getDeckPath());
|
||||
deckPathEdit->setReadOnly(true);
|
||||
QPushButton *deckPathButton = new QPushButton("...");
|
||||
auto *deckPathButton = new QPushButton("...");
|
||||
connect(deckPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::deckPathButtonClicked);
|
||||
|
||||
filtersPathEdit = new QLineEdit(settings.getFiltersPath());
|
||||
filtersPathEdit->setReadOnly(true);
|
||||
QPushButton *filtersPathButton = new QPushButton("...");
|
||||
auto *filtersPathButton = new QPushButton("...");
|
||||
connect(filtersPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::filtersPathButtonClicked);
|
||||
|
||||
replaysPathEdit = new QLineEdit(settings.getReplaysPath());
|
||||
replaysPathEdit->setReadOnly(true);
|
||||
QPushButton *replaysPathButton = new QPushButton("...");
|
||||
auto *replaysPathButton = new QPushButton("...");
|
||||
connect(replaysPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::replaysPathButtonClicked);
|
||||
|
||||
picsPathEdit = new QLineEdit(settings.getPicsPath());
|
||||
picsPathEdit->setReadOnly(true);
|
||||
QPushButton *picsPathButton = new QPushButton("...");
|
||||
auto *picsPathButton = new QPushButton("...");
|
||||
connect(picsPathButton, &QPushButton::clicked, this, &GeneralSettingsPage::picsPathButtonClicked);
|
||||
|
||||
cardDatabasePathEdit = new QLineEdit(settings.getCardDatabasePath());
|
||||
cardDatabasePathEdit->setReadOnly(true);
|
||||
QPushButton *cardDatabasePathButton = new QPushButton("...");
|
||||
auto *cardDatabasePathButton = new QPushButton("...");
|
||||
connect(cardDatabasePathButton, &QPushButton::clicked, this, &GeneralSettingsPage::cardDatabasePathButtonClicked);
|
||||
|
||||
customCardDatabasePathEdit = new QLineEdit(settings.getCustomCardDatabasePath());
|
||||
customCardDatabasePathEdit->setReadOnly(true);
|
||||
QPushButton *customCardDatabasePathButton = new QPushButton("...");
|
||||
auto *customCardDatabasePathButton = new QPushButton("...");
|
||||
connect(customCardDatabasePathButton, &QPushButton::clicked, this,
|
||||
&GeneralSettingsPage::customCardDatabaseButtonClicked);
|
||||
|
||||
tokenDatabasePathEdit = new QLineEdit(settings.getTokenDatabasePath());
|
||||
tokenDatabasePathEdit->setReadOnly(true);
|
||||
QPushButton *tokenDatabasePathButton = new QPushButton("...");
|
||||
auto *tokenDatabasePathButton = new QPushButton("...");
|
||||
connect(tokenDatabasePathButton, &QPushButton::clicked, this, &GeneralSettingsPage::tokenDatabasePathButtonClicked);
|
||||
|
||||
// Required init here to avoid crashing on Portable builds
|
||||
|
|
|
|||
|
|
@ -266,35 +266,16 @@ int CardAmountWidget::countCardsInZone(const QString &deckZone)
|
|||
return -1;
|
||||
}
|
||||
|
||||
InnerDecklistNode *listRoot = decklist->getRoot();
|
||||
if (!listRoot) {
|
||||
return -1;
|
||||
}
|
||||
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes({deckZone});
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (auto *i : *listRoot) {
|
||||
auto *countCurrentZone = dynamic_cast<InnerDecklistNode *>(i);
|
||||
if (!countCurrentZone) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (countCurrentZone->getName() != deckZone) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto *cardNode : *countCurrentZone) {
|
||||
auto *currentCard = dynamic_cast<DecklistCardNode *>(cardNode);
|
||||
if (!currentCard) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
if (currentCard->getCardProviderId() == rootCard.getPrinting().getProperty("uuid")) {
|
||||
count++;
|
||||
}
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
if (currentCard->getCardProviderId() == rootCard.getPrinting().getProperty("uuid")) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
|
@ -218,7 +218,7 @@ void PrintingSelector::getAllSetsForCurrentCard()
|
|||
|
||||
connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=, this]() mutable {
|
||||
for (int i = 0; i < BATCH_SIZE && currentIndex < printingsToUse.size(); ++i, ++currentIndex) {
|
||||
ExactCard card = ExactCard(selectedCard, printingsToUse[currentIndex]);
|
||||
auto card = ExactCard(selectedCard, printingsToUse[currentIndex]);
|
||||
auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(
|
||||
this, deckEditor, deckModel, deckView, cardSizeWidget->getSlider(), card, currentZone);
|
||||
flowWidget->addWidget(cardDisplayWidget);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ void PrintingSelectorCardSelectionWidget::connectSignals()
|
|||
|
||||
void PrintingSelectorCardSelectionWidget::selectSetForCards()
|
||||
{
|
||||
DlgSelectSetForCards *setSelectionDialog = new DlgSelectSetForCards(nullptr, parent->getDeckModel());
|
||||
auto *setSelectionDialog = new DlgSelectSetForCards(nullptr, parent->getDeckModel());
|
||||
if (!setSelectionDialog->exec()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ enum GameListColumn
|
|||
|
||||
const int DEFAULT_MAX_PLAYERS_MIN = 1;
|
||||
const int DEFAULT_MAX_PLAYERS_MAX = 99;
|
||||
constexpr QTime DEFAULT_MAX_GAME_AGE = QTime();
|
||||
constexpr auto DEFAULT_MAX_GAME_AGE = QTime();
|
||||
|
||||
const QString GamesModel::getGameCreatedString(const int secs)
|
||||
{
|
||||
|
|
@ -333,7 +333,7 @@ void GamesProxyModel::setGameFilters(bool _hideBuddiesOnlyGames,
|
|||
|
||||
int GamesProxyModel::getNumFilteredGames() const
|
||||
{
|
||||
GamesModel *model = qobject_cast<GamesModel *>(sourceModel());
|
||||
auto *model = qobject_cast<GamesModel *>(sourceModel());
|
||||
if (!model)
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ int RemoteReplayList_TreeModel::rowCount(const QModelIndex &parent) const
|
|||
if (!parent.isValid())
|
||||
return replayMatches.size();
|
||||
|
||||
MatchNode *matchNode = dynamic_cast<MatchNode *>(static_cast<Node *>(parent.internalPointer()));
|
||||
auto *matchNode = dynamic_cast<MatchNode *>(static_cast<Node *>(parent.internalPointer()));
|
||||
if (matchNode)
|
||||
return matchNode->size();
|
||||
else
|
||||
|
|
@ -62,7 +62,7 @@ QVariant RemoteReplayList_TreeModel::data(const QModelIndex &index, int role) co
|
|||
if (index.column() >= numberOfColumns)
|
||||
return QVariant();
|
||||
|
||||
ReplayNode *replayNode = dynamic_cast<ReplayNode *>(static_cast<Node *>(index.internalPointer()));
|
||||
auto *replayNode = dynamic_cast<ReplayNode *>(static_cast<Node *>(index.internalPointer()));
|
||||
if (replayNode) {
|
||||
const ServerInfo_Replay &replayInfo = replayNode->getReplayInfo();
|
||||
switch (role) {
|
||||
|
|
@ -84,7 +84,7 @@ QVariant RemoteReplayList_TreeModel::data(const QModelIndex &index, int role) co
|
|||
return index.column() == 0 ? fileIcon : QVariant();
|
||||
}
|
||||
} else {
|
||||
MatchNode *matchNode = dynamic_cast<MatchNode *>(static_cast<Node *>(index.internalPointer()));
|
||||
auto *matchNode = dynamic_cast<MatchNode *>(static_cast<Node *>(index.internalPointer()));
|
||||
const ServerInfo_ReplayMatch &matchInfo = matchNode->getMatchInfo();
|
||||
switch (role) {
|
||||
case Qt::TextAlignmentRole:
|
||||
|
|
@ -170,7 +170,7 @@ QModelIndex RemoteReplayList_TreeModel::index(int row, int column, const QModelI
|
|||
if (!hasIndex(row, column, parent))
|
||||
return QModelIndex();
|
||||
|
||||
MatchNode *matchNode = dynamic_cast<MatchNode *>(static_cast<Node *>(parent.internalPointer()));
|
||||
auto *matchNode = dynamic_cast<MatchNode *>(static_cast<Node *>(parent.internalPointer()));
|
||||
if (matchNode) {
|
||||
if (row >= matchNode->size())
|
||||
return QModelIndex();
|
||||
|
|
@ -188,7 +188,7 @@ QModelIndex RemoteReplayList_TreeModel::parent(const QModelIndex &ind) const
|
|||
if (matchNode)
|
||||
return QModelIndex();
|
||||
else {
|
||||
ReplayNode *replayNode = dynamic_cast<ReplayNode *>(static_cast<Node *>(ind.internalPointer()));
|
||||
auto *replayNode = dynamic_cast<ReplayNode *>(static_cast<Node *>(ind.internalPointer()));
|
||||
return createIndex(replayNode->getParent()->indexOf(replayNode), 0, replayNode->getParent());
|
||||
}
|
||||
}
|
||||
|
|
@ -206,7 +206,7 @@ ServerInfo_Replay const *RemoteReplayList_TreeModel::getReplay(const QModelIndex
|
|||
if (!index.isValid())
|
||||
return 0;
|
||||
|
||||
ReplayNode *node = dynamic_cast<ReplayNode *>(static_cast<Node *>(index.internalPointer()));
|
||||
auto *node = dynamic_cast<ReplayNode *>(static_cast<Node *>(index.internalPointer()));
|
||||
if (!node)
|
||||
return 0;
|
||||
return &node->getReplayInfo();
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ void UserContextMenu::gamesOfUserReceived(const Response &resp, const CommandCon
|
|||
gameTypeMap.insert(roomInfo.room_id(), tempMap);
|
||||
}
|
||||
|
||||
GameSelector *selector = new GameSelector(client, tabSupervisor, nullptr, roomMap, gameTypeMap, false, false);
|
||||
auto *selector = new GameSelector(client, tabSupervisor, nullptr, roomMap, gameTypeMap, false, false);
|
||||
selector->setParent(static_cast<QWidget *>(parent()), Qt::Window);
|
||||
const int gameListSize = response.game_list_size();
|
||||
for (int i = 0; i < gameListSize; ++i) {
|
||||
|
|
@ -128,7 +128,7 @@ void UserContextMenu::banUser_processUserInfoResponse(const Response &r)
|
|||
const Response_GetUserInfo &response = r.GetExtension(Response_GetUserInfo::ext);
|
||||
|
||||
// The dialog needs to be non-modal in order to not block the event queue of the client.
|
||||
BanDialog *dlg = new BanDialog(response.user_info(), static_cast<QWidget *>(parent()));
|
||||
auto *dlg = new BanDialog(response.user_info(), static_cast<QWidget *>(parent()));
|
||||
connect(dlg, &QDialog::accepted, this, &UserContextMenu::banUser_dialogFinished);
|
||||
dlg->show();
|
||||
}
|
||||
|
|
@ -141,7 +141,7 @@ void UserContextMenu::warnUser_processGetWarningsListResponse(const Response &r)
|
|||
QString clientid = QString::fromStdString(response.user_clientid()).simplified();
|
||||
|
||||
// The dialog needs to be non-modal in order to not block the event queue of the client.
|
||||
WarningDialog *dlg = new WarningDialog(user, clientid, static_cast<QWidget *>(parent()));
|
||||
auto *dlg = new WarningDialog(user, clientid, static_cast<QWidget *>(parent()));
|
||||
connect(dlg, &QDialog::accepted, this, &UserContextMenu::warnUser_dialogFinished);
|
||||
|
||||
if (response.warning_size() > 0) {
|
||||
|
|
@ -171,7 +171,7 @@ void UserContextMenu::banUserHistory_processResponse(const Response &resp)
|
|||
if (resp.response_code() == Response::RespOk) {
|
||||
|
||||
if (response.ban_list_size() > 0) {
|
||||
QTableWidget *table = new QTableWidget();
|
||||
auto *table = new QTableWidget();
|
||||
table->setWindowTitle(tr("Ban History"));
|
||||
table->setRowCount(response.ban_list_size());
|
||||
table->setColumnCount(5);
|
||||
|
|
@ -209,7 +209,7 @@ void UserContextMenu::warnUserHistory_processResponse(const Response &resp)
|
|||
if (resp.response_code() == Response::RespOk) {
|
||||
|
||||
if (response.warn_list_size() > 0) {
|
||||
QTableWidget *table = new QTableWidget();
|
||||
auto *table = new QTableWidget();
|
||||
table->setWindowTitle(tr("Warning History"));
|
||||
table->setRowCount(response.warn_list_size());
|
||||
table->setColumnCount(4);
|
||||
|
|
@ -278,7 +278,7 @@ void UserContextMenu::adjustMod_processUserResponse(const Response &resp, const
|
|||
|
||||
void UserContextMenu::banUser_dialogFinished()
|
||||
{
|
||||
BanDialog *dlg = static_cast<BanDialog *>(sender());
|
||||
auto *dlg = static_cast<BanDialog *>(sender());
|
||||
|
||||
Command_BanFromServer cmd;
|
||||
cmd.set_user_name(dlg->getBanName().toStdString());
|
||||
|
|
@ -297,7 +297,7 @@ void UserContextMenu::banUser_dialogFinished()
|
|||
|
||||
void UserContextMenu::warnUser_dialogFinished()
|
||||
{
|
||||
WarningDialog *dlg = static_cast<WarningDialog *>(sender());
|
||||
auto *dlg = static_cast<WarningDialog *>(sender());
|
||||
|
||||
if (dlg->getName().isEmpty() || userListProxy->getOwnUsername().simplified().isEmpty())
|
||||
return;
|
||||
|
|
@ -433,7 +433,7 @@ void UserContextMenu::showContextMenu(const QPoint &pos,
|
|||
QAction *actionClicked = menu->exec(pos);
|
||||
if (actionClicked == nullptr) {
|
||||
} else if (actionClicked == aDetails) {
|
||||
UserInfoBox *infoWidget =
|
||||
auto *infoWidget =
|
||||
new UserInfoBox(client, false, static_cast<QWidget *>(parent()),
|
||||
Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint);
|
||||
infoWidget->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
|
|
|||
|
|
@ -64,14 +64,14 @@ TabEdhRecMain::TabEdhRecMain(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor
|
|||
auto cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this);
|
||||
auto displayModel = new CardDatabaseDisplayModel(this);
|
||||
displayModel->setSourceModel(cardDatabaseModel);
|
||||
CardSearchModel *searchModel = new CardSearchModel(displayModel, this);
|
||||
auto *searchModel = new CardSearchModel(displayModel, this);
|
||||
|
||||
CardCompleterProxyModel *proxyModel = new CardCompleterProxyModel(this);
|
||||
auto *proxyModel = new CardCompleterProxyModel(this);
|
||||
proxyModel->setSourceModel(searchModel);
|
||||
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
proxyModel->setFilterRole(Qt::DisplayRole);
|
||||
|
||||
QCompleter *completer = new QCompleter(proxyModel, this);
|
||||
auto *completer = new QCompleter(proxyModel, this);
|
||||
completer->setCompletionRole(Qt::DisplayRole);
|
||||
completer->setCompletionMode(QCompleter::PopupCompletion);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
|
|
|||
|
|
@ -17,22 +17,22 @@
|
|||
|
||||
ShutdownDialog::ShutdownDialog(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
QLabel *reasonLabel = new QLabel(tr("&Reason for shutdown:"));
|
||||
auto *reasonLabel = new QLabel(tr("&Reason for shutdown:"));
|
||||
reasonEdit = new QLineEdit;
|
||||
reasonEdit->setMaxLength(MAX_TEXT_LENGTH);
|
||||
reasonLabel->setBuddy(reasonEdit);
|
||||
QLabel *minutesLabel = new QLabel(tr("&Time until shutdown (minutes):"));
|
||||
auto *minutesLabel = new QLabel(tr("&Time until shutdown (minutes):"));
|
||||
minutesEdit = new QSpinBox;
|
||||
minutesLabel->setBuddy(minutesEdit);
|
||||
minutesEdit->setMinimum(0);
|
||||
minutesEdit->setValue(5);
|
||||
minutesEdit->setMaximum(999);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &ShutdownDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &ShutdownDialog::reject);
|
||||
|
||||
QGridLayout *mainLayout = new QGridLayout;
|
||||
auto *mainLayout = new QGridLayout;
|
||||
mainLayout->addWidget(reasonLabel, 0, 0);
|
||||
mainLayout->addWidget(reasonEdit, 0, 1);
|
||||
mainLayout->addWidget(minutesLabel, 1, 0);
|
||||
|
|
@ -109,7 +109,7 @@ TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool
|
|||
lockButton->setEnabled(false);
|
||||
connect(lockButton, &QPushButton::clicked, this, &TabAdmin::actLock);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(adminGroupBox);
|
||||
mainLayout->addWidget(moderatorGroupBox);
|
||||
mainLayout->addStretch();
|
||||
|
|
@ -118,7 +118,7 @@ TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool
|
|||
|
||||
retranslateUi();
|
||||
|
||||
QWidget *mainWidget = new QWidget(this);
|
||||
auto *mainWidget = new QWidget(this);
|
||||
mainWidget->setLayout(mainLayout);
|
||||
setCentralWidget(mainWidget);
|
||||
|
||||
|
|
|
|||
|
|
@ -982,7 +982,7 @@ void TabGame::createMenuItems()
|
|||
phasesMenu = new TearOffMenu(this);
|
||||
|
||||
for (int i = 0; i < phasesToolbar->phaseCount(); ++i) {
|
||||
QAction *temp = new QAction(QString(), this);
|
||||
auto *temp = new QAction(QString(), this);
|
||||
connect(temp, &QAction::triggered, this, &TabGame::actPhaseAction);
|
||||
phasesMenu->addAction(temp);
|
||||
phaseActions.append(temp);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor,
|
|||
sayEdit->setMaxLength(MAX_TEXT_LENGTH);
|
||||
connect(sayEdit, &LineEditUnfocusable::returnPressed, this, &TabMessage::sendMessage);
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
auto *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(chatView);
|
||||
vbox->addWidget(sayEdit);
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor,
|
|||
|
||||
retranslateUi();
|
||||
|
||||
QWidget *mainWidget = new QWidget(this);
|
||||
auto *mainWidget = new QWidget(this);
|
||||
mainWidget->setLayout(vbox);
|
||||
setCentralWidget(mainWidget);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
|
|||
sayLabel->setBuddy(sayEdit);
|
||||
connect(sayEdit, &LineEditCompleter::returnPressed, this, &TabRoom::sendMessage);
|
||||
|
||||
QMenu *chatSettingsMenu = new QMenu(this);
|
||||
auto *chatSettingsMenu = new QMenu(this);
|
||||
|
||||
aClearChat = chatSettingsMenu->addAction(QString());
|
||||
connect(aClearChat, &QAction::triggered, this, &TabRoom::actClearChat);
|
||||
|
|
@ -77,28 +77,28 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
|
|||
aOpenChatSettings = chatSettingsMenu->addAction(QString());
|
||||
connect(aOpenChatSettings, &QAction::triggered, this, &TabRoom::actOpenChatSettings);
|
||||
|
||||
QToolButton *chatSettingsButton = new QToolButton;
|
||||
auto *chatSettingsButton = new QToolButton;
|
||||
chatSettingsButton->setIcon(QPixmap("theme:icons/settings"));
|
||||
chatSettingsButton->setMenu(chatSettingsMenu);
|
||||
chatSettingsButton->setPopupMode(QToolButton::InstantPopup);
|
||||
|
||||
QHBoxLayout *sayHbox = new QHBoxLayout;
|
||||
auto *sayHbox = new QHBoxLayout;
|
||||
sayHbox->addWidget(sayLabel);
|
||||
sayHbox->addWidget(sayEdit);
|
||||
sayHbox->addWidget(chatSettingsButton);
|
||||
|
||||
QVBoxLayout *chatVbox = new QVBoxLayout;
|
||||
auto *chatVbox = new QVBoxLayout;
|
||||
chatVbox->addWidget(chatView);
|
||||
chatVbox->addLayout(sayHbox);
|
||||
|
||||
chatGroupBox = new QGroupBox;
|
||||
chatGroupBox->setLayout(chatVbox);
|
||||
|
||||
QSplitter *splitter = new QSplitter(Qt::Vertical);
|
||||
auto *splitter = new QSplitter(Qt::Vertical);
|
||||
splitter->addWidget(gameSelector);
|
||||
splitter->addWidget(chatGroupBox);
|
||||
|
||||
QHBoxLayout *hbox = new QHBoxLayout;
|
||||
auto *hbox = new QHBoxLayout;
|
||||
hbox->addWidget(splitter, 3);
|
||||
hbox->addWidget(userList, 1);
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
|
|||
|
||||
retranslateUi();
|
||||
|
||||
QWidget *mainWidget = new QWidget(this);
|
||||
auto *mainWidget = new QWidget(this);
|
||||
mainWidget->setLayout(hbox);
|
||||
setCentralWidget(mainWidget);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -931,7 +931,7 @@ void TabSupervisor::deckEditorClosed(AbstractTabDeckEditor *tab)
|
|||
|
||||
void TabSupervisor::tabUserEvent(bool globalEvent)
|
||||
{
|
||||
Tab *tab = static_cast<Tab *>(sender());
|
||||
auto *tab = static_cast<Tab *>(sender());
|
||||
if (tab != currentWidget()) {
|
||||
tab->setContentsChanged(true);
|
||||
setTabIcon(indexOf(tab), QPixmap("theme:icons/tab_changed"));
|
||||
|
|
@ -1032,7 +1032,7 @@ void TabSupervisor::processUserJoined(const ServerInfo_User &userInfoJoined)
|
|||
void TabSupervisor::updateCurrent(int index)
|
||||
{
|
||||
if (index != -1) {
|
||||
Tab *tab = static_cast<Tab *>(widget(index));
|
||||
auto *tab = static_cast<Tab *>(widget(index));
|
||||
if (tab->getContentsChanged()) {
|
||||
setTabIcon(index, QIcon());
|
||||
tab->setContentsChanged(false);
|
||||
|
|
|
|||
|
|
@ -67,21 +67,13 @@ void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck()
|
|||
DeckList *decklist = deckListModel->getDeckList();
|
||||
if (!decklist)
|
||||
return;
|
||||
InnerDecklistNode *listRoot = decklist->getRoot();
|
||||
if (!listRoot)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < listRoot->size(); i++) {
|
||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
if (!currentZone)
|
||||
continue;
|
||||
for (int j = 0; j < currentZone->size(); j++) {
|
||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
if (!currentCard)
|
||||
continue;
|
||||
createNameFilter(currentCard->getName());
|
||||
}
|
||||
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes();
|
||||
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
createNameFilter(currentCard->getName());
|
||||
}
|
||||
|
||||
updateFilterModel();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,28 +83,15 @@ QList<ExactCard> VisualDeckEditorSampleHandWidget::getRandomCards(int amountToGe
|
|||
DeckList *decklist = deckListModel->getDeckList();
|
||||
if (!decklist)
|
||||
return randomCards;
|
||||
InnerDecklistNode *listRoot = decklist->getRoot();
|
||||
if (!listRoot)
|
||||
return randomCards;
|
||||
|
||||
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes({DECK_ZONE_MAIN});
|
||||
|
||||
// Collect all cards in the main deck, allowing duplicates based on their count
|
||||
for (int i = 0; i < listRoot->size(); i++) {
|
||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
||||
if (!currentZone)
|
||||
continue;
|
||||
if (currentZone->getName() != DECK_ZONE_MAIN)
|
||||
continue; // Only process the main deck
|
||||
|
||||
for (int j = 0; j < currentZone->size(); j++) {
|
||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
||||
if (!currentCard)
|
||||
continue;
|
||||
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
ExactCard card = CardDatabaseManager::query()->getCard(currentCard->toCardRef());
|
||||
if (card) {
|
||||
mainDeckCards.append(card);
|
||||
}
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
ExactCard card = CardDatabaseManager::query()->getCard(currentCard->toCardRef());
|
||||
if (card) {
|
||||
mainDeckCards.append(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,17 +232,12 @@ void DeckPreviewWidget::updateBannerCardComboBox()
|
|||
|
||||
// Prepare the new items with deduplication
|
||||
QSet<QPair<QString, QString>> bannerCardSet;
|
||||
InnerDecklistNode *listRoot = deckLoader->getDeckList()->getRoot();
|
||||
for (auto i : *listRoot) {
|
||||
auto *currentZone = dynamic_cast<InnerDecklistNode *>(i);
|
||||
for (auto j : *currentZone) {
|
||||
auto *currentCard = dynamic_cast<DecklistCardNode *>(j);
|
||||
if (!currentCard)
|
||||
continue;
|
||||
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
bannerCardSet.insert(QPair<QString, QString>(currentCard->getName(), currentCard->getCardProviderId()));
|
||||
}
|
||||
QList<DecklistCardNode *> cardsInDeck = deckLoader->getDeckList()->getCardNodes();
|
||||
|
||||
for (auto currentCard : cardsInDeck) {
|
||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
||||
bannerCardSet.insert(QPair<QString, QString>(currentCard->getName(), currentCard->getCardProviderId()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#include "mocks.h"
|
||||
|
||||
CardDatabaseSettings::CardDatabaseSettings(const QString &settingPath, QObject *parent)
|
||||
: SettingsManager(settingPath + "cardDatabase.ini", parent)
|
||||
: SettingsManager(settingPath + "cardDatabase.ini", QString(), QString(), parent)
|
||||
{
|
||||
}
|
||||
void CardDatabaseSettings::setSortKey(QString /* shortName */, unsigned int /* sortKey */)
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
@page card_database_schema_and_parsing Card Database Schema and Parsing
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
@page displaying_cards Displaying Cards
|
||||
|
||||
Cockatrice offers a number of widgets for displaying cards. To pick the right one for your purpose, it is first
|
||||
important
|
||||
to determine the context in which you will be displaying the card.
|
||||
|
||||
# In-client
|
||||
|
||||
In the client (like in your custom widgets), you can use the existing card display widgets.
|
||||
|
||||
## Simple Display
|
||||
|
||||
The most general purpose of these is @ref CardInfoPictureWidget, which simply displays a picture of a card.
|
||||
|
||||
The textual equivalent to this is @ref CardInfoTextWidget, which displays all the properties of a card in text form.
|
||||
|
||||
## Detailed Display
|
||||
|
||||
The convenience class @ref CardInfoDisplayWidget displays both of these widgets in a tabbed container, which allows
|
||||
choosing between either visual, textual, or both representations at the same time. It is the class that is most
|
||||
familiar to our users when a single card is highlighted or inspected, since it is used in the deck editor and in-game
|
||||
for these purposes.
|
||||
|
||||
If you would like the user to be able to inspect a single card in your custom widget, you should
|
||||
expose a @ref CardInfoDisplayWidget to them and incorporate it as a dock widget inside your custom widget.
|
||||
|
||||
@ref CardInfoDisplayWidget is great for ensuring that the user has all the relevant details of a card available.
|
||||
It is crucial anywhere the user might encounter a card for the first time or needs to make an informed decision.
|
||||
|
||||
## Visual Display
|
||||
|
||||
However, there is a reason why it is possible to have a visual-only representation of the card in the
|
||||
@ref CardInfoDisplayWidget.
|
||||
|
||||
Cards are, by design, meant to be represented as images and cards which we can reasonably
|
||||
expect the user to know (for example, cards contained in a user made deck or explicitly chosen by the user) can be
|
||||
represented with just their image as a sort of "visual shorthand".
|
||||
|
||||
The simplest way to do this is of course, the above-mentioned @ref CardInfoPictureWidget which simply displays the
|
||||
picture without modifications.
|
||||
|
||||
However, there exist a couple of other convenience classes which subclass @ref CardInfoPictureWidget to accomplish some
|
||||
things which you might find useful as well.
|
||||
|
||||
- @ref CardInfoPictureWithTextOverlayWidget displays text overlaid on top of the card picture and scales this text to
|
||||
fit automatically.
|
||||
- @ref DeckPreviewCardPictureWidget is a @ref CardInfoPictureWithTextOverlayWidget which also features a
|
||||
raise-on-mouse-enter animation, controlled by the global setting (TODO: specify which).
|
||||
- @ref CardInfoPictureArtCropWidget attempts to display an 'art crop' of the card by cropping the upper region of the
|
||||
card and only displaying that.
|
||||
|
||||
## Groups of Cards
|
||||
|
||||
Sometimes it is useful to display images of cards in groups, for example to represent the deck in the @ref
|
||||
VisualDeckEditorWidget or during confirmation in the pre-game lobby.
|
||||
|
||||
To this end, Cockatrice offers three convenience classes to display cards for an index in a @ref DeckListModel in a
|
||||
group and one to display all cards in a particular zone of a @ref DeckListModel.
|
||||
|
||||
The generic way to do this is @ref CardGroupDisplayWidget, which displays cards in @ref QVBoxLayout. This class is very
|
||||
generic and it is probably a better choice to either subclass it or use one of the two existing implementations.
|
||||
|
||||
@ref FlatCardGroupDisplayWidget displays cards using a horizontal @ref FlowWidget. This means it will fill available
|
||||
screen space, left to right with no margins, with card pictures, skipping to a new line if it overflows available screen
|
||||
space horizontally.
|
||||
|
||||
@ref OverlappedCardGroupDisplayWidget displays cards using an @ref OverlapWidget, which displays widgets on top of each
|
||||
other in vertical columns until it exceeds available screen space in which case it will start a new column. The amount
|
||||
of overlap as well as the overlap direction is configurable.
|
||||
|
||||
@ref DeckCardZoneDisplayWidget can be used to visualize all cards in a zone with either a @ref
|
||||
FlatCardGroupDisplayWidget or a @ref OverlappedCardGroupDisplayWidget and headed by a @ref BannerWidget. It also allows
|
||||
grouping and sorting.
|
||||
|
||||
# In-game
|
||||
|
||||
The in-game view is a @ref QGraphicsScene, which means any widget we want to display inside of it should be a @ref
|
||||
QGraphicsObject.
|
||||
|
||||
- @ref CardItem
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
@page developer_reference Developer Reference
|
||||
|
||||
- [Contributing](@subpage contributing)
|
||||
- @subpage primer_cards
|
||||
|
||||
- @subpage card_database_schema_and_parsing
|
||||
- @subpage querying_the_card_database
|
||||
|
||||
- @subpage loading_card_pictures
|
||||
- @subpage displaying_cards
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
@page loading_card_pictures Loading Card Pictures
|
||||
|
||||
Pictures associated with CardInfo%s are retrieved either from on-disk or the network through the CardPictureLoader.
|
||||
|
||||
In most cases, you don't need to concern yourself with the internals of CardPictureLoader.
|
||||
|
||||
Simply using one of the ways described in @ref displaying_cards is enough to automatically queue a request to the
|
||||
CardPictureLoader when the chosen widget is shown, emitting signals to refresh the widget when the request is finished.
|
||||
|
||||
# How requests are triggered
|
||||
|
||||
CardPictureLoader::getPixmap() is called exactly two times in the code base, in CardInfoPictureWidget::loadPixmap(), the
|
||||
base class
|
||||
for all widget based card picture display, and AbstractCardItem::paintPicture(), the base class for all QGraphicsItem
|
||||
based card picture
|
||||
display. See @ref displaying_cards for more information on the difference between these two display methods.
|
||||
|
||||
Because both of these calls are made in the paintEvent() methods of their respective classes, this means that requests
|
||||
are issued as soon as but not before the widget is shown on screen.
|
||||
|
||||
It is also possible to "warm up" the cache by issuing card picture load requests to the CardPictureLoader without using
|
||||
a display widget and waiting for it to be shown by calling CardPictureLoader::cacheCardPixmaps() with a list of
|
||||
ExactCard%s.
|
||||
|
||||
# The QPixmapCache and QNetworkDiskCache
|
||||
|
||||
Cockatrice uses the QPixmapCache from the Qt GUI module to store card pictures in-memory and the QNetworkDiskCache from
|
||||
the Qt Network module to cache network requests for card pictures on-disk.
|
||||
|
||||
What this means is that the CardPictureLoader will first attempt to look up a card in the QPixmapCache according to the
|
||||
ExactCard::getPixmapCacheKey() method of an ExactCard object. If it does not find it in the in-memory cache, it will
|
||||
issue a load request, which will first look for local images on-disk and then consult the QNetworkDiskCache and if
|
||||
found, use the stored binary data from the network cache to populate the in-memory pixmap cache under the card's cache
|
||||
key. If it is not found, it will then proceed with issuing a network request.
|
||||
|
||||
The size of both of these caches can be configured by the user in the "Card Sources" settings page.
|
||||
|
||||
# PixmapCacheKeys and ProviderIDs
|
||||
|
||||
TODO
|
||||
|
||||
# The Redirect Cache
|
||||
|
||||
TODO
|
||||
|
||||
# Local Image Loading
|
||||
|
||||
TODO
|
||||
|
||||
# URL Generation and Resolution
|
||||
|
||||
TODO
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
@page primer_cards A Primer on Cards
|
||||
|
||||
# The Cockatrice Card Library
|
||||
|
||||
All non-gui code related to cards used by Cockatrice is contained within libcockatrice_card.
|
||||
|
||||
# A Basic Card Object: CardInfo
|
||||
|
||||
A CardInfo object is the translational unit used by the CardDatabase to represent an on-disk XML entry as an in-memory
|
||||
Qt object.
|
||||
|
||||
For a complete overview of the fields that define a CardInfo object, see the class documentation and more specifically
|
||||
@ref PrivateCardProperties.
|
||||
|
||||
These fields correspond to either entries or a combination of entries in the XML card entry to which the
|
||||
CardDatabaseParser applies special logic to map, populate or determine their respective values in the CardInfo object.
|
||||
|
||||
Any property to which special parsing is not applied is stored in CardInfo::properties and may be retrieved or set with
|
||||
CardInfo::getProperty() or CardInfo::setProperty() as well as CardInfo::getProperties() to get a collection of the
|
||||
property keys a CardInfo contains.
|
||||
|
||||
For more information on how special fields contained within @ref PrivateCardProperties are parsed, see
|
||||
CockatriceXml4Parser::loadCardsFromXml().
|
||||
|
||||
Apart from access to the basic and extended properties of a card, CardInfo also provides two important mechanisms to us.
|
||||
The first is the CardInfo::pixmapUpdated() signal, which allows the CardPictureLoader to signal when it is done
|
||||
manipulating (in most cases: loading) the QPixmap of an ExactCard::getPixmapCacheKey(), where the ExactCard::card
|
||||
corresponds to this CardInfo object.
|
||||
|
||||
Put simply: Whenever the CardPictureLoader loads a new PrintingInfo which is not already in the cache, it emits this
|
||||
signal so that any widget using a CardInfo object can update the display of it to possibly use this new QPixmap.
|
||||
|
||||
\attention It should be noted that it is not possible to use CardPictureLoader::getPixmap() with anything but an
|
||||
ExactCard, which is a CardInfo object associated with a PrintingInfo, i.e. a definitive printing of a specific card
|
||||
|
||||
The other signal, CardInfo::cardInfoChanged() conceptually works much the same way, except it is concerned with the
|
||||
properties of the CardInfo object.
|
||||
|
||||
# Getting specific: PrintingInfo and ExactCard
|
||||
|
||||
## Printing Info
|
||||
|
||||
A CardInfo object describes the basic properties of a card in much the same way that we say "Two cards with the same
|
||||
name are and should be equal if their properties are equal". However, there are certain (mostly visual) properties which
|
||||
might differ between printings of a card but still conceptually classify two instances of a card as "the same card".
|
||||
The most obvious example to this are cards which fundamentally share all properties except the artwork depicted on the
|
||||
card.
|
||||
|
||||
We refer to such differences as Printings and track them in the PrintingInfo class. A PrintingInfo is always related to
|
||||
the CardSet that introduced the variation. Multiple variations can exist in the same CardSet and the respective
|
||||
PrintingInfo::getProperty() can be used to determine the differences, such as the collector numbers on the printings, if
|
||||
there are any.
|
||||
|
||||
## Exact Card
|
||||
|
||||
A CardInfo object is used to hold the functional properties of a card and a PrintingInfo object can be used to hold the
|
||||
visual properties of a card. To represent a 'physical' card, as in, a concrete immutable instance of a card, we can use
|
||||
ExactCard, which combines a CardInfo and a PrintingInfo object into one class. The class is used as a container around
|
||||
CardInfo objects whenever the user (and thus the program) expects to be presented with a defined card printing.
|
||||
|
||||
# Using Cards
|
||||
|
||||
For more information on the XML database schema which the CardDatabaseParser parses to generate CardInfo objects, see
|
||||
@ref card_database_schema_and_parsing.
|
||||
|
||||
For more information on querying the CardDatabase for CardInfo objects, see @ref querying_the_card_database.
|
||||
|
||||
For more information on displaying CardInfo and ExactCard objects using Qt Widgets, see @ref displaying_cards.
|
||||
|
||||
For more information on how card pictures are loaded from disk or the network, see @ref loading_card_pictures as well as
|
||||
the CardPictureLoader documentation.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
@page querying_the_card_database Querying the Card Database
|
||||
|
||||
# The CardDatabaseQuerier Class
|
||||
|
||||
The CardDatabaseQuerier is the only class used for querying the database. The CardDatabase is an in-memory map and thus
|
||||
provides no structured query language. CardDatabaseQuerier offers methods to retrieve cards by name, by providerID or in
|
||||
bulk, as CardSet%s.
|
||||
|
||||
## Obtaining a handle to the CardDatabaseQuerier for usage
|
||||
|
||||
To obtain the CardDatabaseQuerier related to the global CardDatabase singleton, use CardDatabaseManager::query().
|
||||
|
||||
## Querying for known cards
|
||||
|
||||
There are, essentially, two ways to ensure card equality, with the second being optional but necessitating the first.
|
||||
These two ways are CardInfo name equality and PrintingInfo provider ID equality.
|
||||
|
||||
Because of this, most queries require, at the very least, a card name to match against and optionally a providerID to
|
||||
narrow the results.
|
||||
|
||||
### Generic Card Infos
|
||||
|
||||
To check if a card with the exact provided name exists as a CardInfo in the CardDatabase use,
|
||||
CardDatabaseQuerier::getCardInfo() or CardDatabaseQuerier::getCardInfos() for multiple cards.
|
||||
|
||||
### Guessing Cards
|
||||
|
||||
If the exact name might not be present in the CardDatabase, you can use CardDatabaseQuerier::getCardBySimpleName(),
|
||||
which automatically simplifies the card name and matches it against simplified card names in the CardDatabase.
|
||||
|
||||
Alternatively, you can use CardDatabaseQuerier::lookupCardByName(), which first attempts an exact match search and then
|
||||
uses CardDatabaseQuerier::getCardBySimpleName() as a fallback.
|
||||
|
||||
### ExactCard%s
|
||||
|
||||
To obtain an ExactCard from the CardDatabaseQuerier, you must use a CardRef as a parameter to
|
||||
CardDatabaseQuerier::getCard(), CardDatabaseQuerier::getCards(), or CardDatabaseQuerier::guessCard().
|
||||
|
||||
CardRef is a simple struct consisting of a card name and a card provider ID as QString%s.
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
@page creating_decks Creating Decks
|
||||
|
||||
Creating a new deck is done using either the TabDeckEditor or TabDeckEditorVisual.
|
||||
|
||||
They can be accessed either by clicking the "Create Deck" button in the TabHome screen, which will open the default deck
|
||||
editor configured in the "User Interface" -> "Deck editor/storage settings" -> "Default deck editor type" setting, or
|
||||
by selecting "Deck Editor" or "Visual Deck Editor" under the "Tabs" application menu located at the top.
|
||||
|
||||
# Further References
|
||||
|
||||
See @ref editing_decks for information on how to modify the attributes and contents of a deck in the Deck Editor
|
||||
widgets.
|
||||
|
||||
See @ref exporting_decks for information on how to store and persist your deck either in-client or to external
|
||||
services.
|
||||
|
||||
See @ref importing_decks for information on how to import existing decks either in-client or from external services
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
@page editing_decks Editing Decks
|
||||
|
||||
@subpage editing_decks_classic
|
||||
|
||||
@subpage editing_decks_visual
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
@page editing_decks_classic Classic Deck Editor
|
||||
|
||||
\image html classic_deck_editor.png width=900px
|
||||
|
||||
# Editing Basic Deck Information
|
||||
|
||||
Editing basic deck information is done through the deck dock widget (DeckEditorDeckDockWidget).
|
||||
|
||||
\image html deckeditordeckdockwidget.png
|
||||
|
||||
This widget allows editing:
|
||||
|
||||
- The name
|
||||
- The comments
|
||||
- The banner card, which is used to represent the deck in the visual deck storage
|
||||
- The tags, which are used for filtering in the visual deck storage
|
||||
|
||||
# Adding Cards
|
||||
|
||||
Adding cards is done using the list of cards in the database presented in the list view on the left.
|
||||
|
||||
\image html classic_database_display.png
|
||||
|
||||
Cards can be added by either double-clicking an entry, pressing return with an entry selected to add it to the mainboard
|
||||
or pressing ctrl/cmd + return to add it to the sideboard.
|
||||
|
||||
There are also buttons for these two functions on the right of the search bar above the list view.
|
||||
|
||||
\image html classic_database_display_add_buttons.png
|
||||
|
||||
# Modifying the Deck List
|
||||
|
||||
To modify or remove cards in the deck list, the tree list view in the deck dock widget can be used.
|
||||
|
||||
\image html deck_dock_deck_list.png
|
||||
|
||||
Just above the list, at the top right, there are four buttons to manipulate the currently selected card(s):
|
||||
|
||||
- Increment Card
|
||||
- Decrement Card
|
||||
- Remove Card
|
||||
- Switch Card between Mainboard and Sideboard
|
||||
|
||||
\image html deck_dock_deck_list_buttons.png
|
||||
|
||||
Additionally, there is a combo box above the list, which may be used to change how cards are grouped in the list
|
||||
display. This is only for visual display and does not affect how the list is saved.
|
||||
|
||||
\image html deck_dock_deck_list_group_by.png
|
||||
|
||||
# Modifying printings
|
||||
|
||||
For more information on modifying the printings in a deck see @ref editing_decks_printings
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
@page editing_decks_printings Printing Selector
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
@page editing_decks_visual Visual Deck Editor
|
||||
|
||||
# Editing Basic Deck Information
|
||||
|
||||
Editing basic deck information is done through the deck dock widget (DeckEditorDeckDockWidget).
|
||||
|
||||
\image html deckeditordeckdockwidget.png
|
||||
|
||||
This widget allows editing:
|
||||
|
||||
- The name
|
||||
- The comments
|
||||
- The banner card, which is used to represent the deck in the visual deck storage
|
||||
- The tags, which are used for filtering in the visual deck storage
|
||||
|
||||
# Adding Cards
|
||||
|
||||
Adding cards is done by either using the "Quick search and add card" search bar at the top of the "Visual Deck View" tab
|
||||
or by clicking on a picture of a card in the "Visual Database Display" tab.
|
||||
|
||||
See @ref visual_database_display for more information on how to utilize the visual database display.
|
||||
|
||||
# Modifying the Deck List
|
||||
|
||||
To modify or remove cards in the deck list, the tree list view in the deck dock widget can be used.
|
||||
|
||||
Just above the list, at the top right, there are four buttons to manipulate the currently selected card(s):
|
||||
|
||||
- Increment Card
|
||||
- Decrement Card
|
||||
- Remove Card
|
||||
- Switch Card between Mainboard and Sideboard
|
||||
|
||||
Additionally, there is a combo box above the list, which may be used to change how cards are grouped in the list
|
||||
display. This is only for visual display and does not affect how the list is saved.
|
||||
|
||||
# Modifying the visual deck layout
|
||||
|
||||
The visual deck editor displays cards visually, as opposed to simply in list form in the Deck Dock Widget. Each entry in
|
||||
the deck list is represented by a picture. These entries are grouped together under their respective sub-groups.
|
||||
Sub-groups may be collapsed (i.e. the pictures contained within them are hidden) by clicking on the name of the group in
|
||||
the banner.
|
||||
|
||||
Cards may either be displayed in a "Flat" layout, which displays each picture next to each other and ensures full
|
||||
visibility for each card, or in an "Overlap" layout, which overlaps cards on top of each other (leaving the top 20% of
|
||||
the card uncovered so names remain readable) and arranges them in stacks to save space and allow for an easy overview.
|
||||
|
||||
Additionally, it is possible to change how the cards in the deck list are grouped by selecting a different grouping
|
||||
method from the combo box, either in the top left of the "Visual Deck View" tab or above the list view in the deck dock
|
||||
widget.
|
||||
|
||||
Furthermore, it is possible to change how the cards are sorted within the sub-group. This is done by clicking on the
|
||||
button with the cogwheel icon next to the combo box that adjusts grouping in the top left of the "Visual Deck View" tab.
|
||||
This presents a list of available sort criteria, which may be rearranged to change their priorities.
|
||||
|
||||
# Modifying printings
|
||||
|
||||
For more information on modifying the printings in a deck see @ref editing_decks_printings
|
||||
|
||||
# Deck Analytics
|
||||
|
||||
The visual deck editor offers a "Deck Analytics" tab, which displays information about:
|
||||
|
||||
- The mana curve
|
||||
- The mana devotion
|
||||
- The mana base
|
||||
|
||||
# Sample Hand
|
||||
|
||||
The visual deck editor offers a "Sample Hand" tab, which allows simulating drawing a configurable amount of cards from
|
||||
the deck, which reduces the need to launch a single player game for testing purposes.
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
@page exporting_decks Exporting Decks
|
||||
|
||||
# Where to export?
|
||||
|
||||
There are two screens in the client which can be used to import decks, depending on the context.
|
||||
|
||||
- The deck editor tab
|
||||
- The deck storage tab (not to be confused with the visual deck storage tab)
|
||||
|
||||
# The Deck Editor Tab
|
||||
|
||||
The deck editor tabs (Classic and Visual) offer three ways of export a deck:
|
||||
|
||||
- To a file on your local storage
|
||||
- To your clipboard
|
||||
- To an online service
|
||||
|
||||
## Local File Storage
|
||||
|
||||
To save a deck to a file on your local storage, select the "Save Deck" action in the "Deck Editor" or "Visual Deck
|
||||
Editor" menu in the application menu bar at the top of the screen. Alternatively, you can use the shortcut Ctrl/Cmd + S
|
||||
to access this action.
|
||||
|
||||
Selecting this action will open a file picker dialog provided by your operating system. Simply enter a file name and
|
||||
select a format (.cod is recommended) and confirm.
|
||||
|
||||
Just below the "Save Deck" action described above is the "Save Deck as..." option, which allows saving an existing file
|
||||
under a different filename, which is useful for saving a different version or copy of a deck.
|
||||
|
||||
## From Clipboard
|
||||
|
||||
To save a deck to your clipboard, select the "Save deck to clipboard..." action in the "Deck Editor" or "Visual Deck
|
||||
Editor" menu in the application menu bar at the top of the screen. Alternatively, you can use the shortcut Ctrl/Cmd +
|
||||
Shift + C or Ctrl/Cmd + Shift + R to access this action.
|
||||
|
||||
Selecting this action will save the currently open deck list to your clipboard.
|
||||
|
||||
Saving the decklist without annotations will export the decklist, with each card being described in the following format
|
||||
|
||||
```
|
||||
CARD_AMOUNT CARD_NAME (SET_SHORT_NAME) CARD_COLLECTOR_NUMBER
|
||||
```
|
||||
|
||||
There is also the (no set info) option, which will simply export each card as
|
||||
|
||||
```
|
||||
CARD_AMOUNT CARD_NAME
|
||||
```
|
||||
|
||||
Mainboard and sideboard are delimited by a newline like so:
|
||||
|
||||
```
|
||||
1 MainboardCard
|
||||
1 OtherMainboardCard
|
||||
|
||||
1 SideboardCard
|
||||
```
|
||||
|
||||
Saving the decklist as annotated will insert comments (marked with // in front of them).
|
||||
It will first insert the name and any comments associated with the deck before separating each deck section into its own
|
||||
newline delimited and annotated group.
|
||||
|
||||
Example: TODO: Adjust this to be non mtg based.
|
||||
|
||||
```
|
||||
// Deck Name
|
||||
|
||||
// Deck Comment
|
||||
|
||||
// 10 Maindeck
|
||||
// 6 Artifact
|
||||
2 The Darkness Crystal (FIN) 335
|
||||
2 The Fire Crystal (FIN) 337
|
||||
2 Black Mage's Rod (FIN) 90
|
||||
|
||||
// 6 Sorcery
|
||||
2 Nibelheim Aflame (FIN) 339
|
||||
4 Cornered by Black Mages (FIN) 93
|
||||
|
||||
// 6 Sideboard
|
||||
// 6 Creature
|
||||
SB: 4 Blazing Bomb (FIN) 130
|
||||
SB: 1 Garland, Knight of Cornelia (FIN) 221
|
||||
SB: 1 Undercity Dire Rat (FIN) 123
|
||||
```
|
||||
|
||||
## From an online service
|
||||
|
||||
To export a deck to an online service, select the "Send deck to online service..." action in the "Deck Editor" or "
|
||||
Visual Deck Editor" menu in the application menu bar at the top of the screen.
|
||||
|
||||
Selecting this action will open your browser with the selected service open and the deck list information from the
|
||||
client supplied to it.
|
||||
|
||||
Currently supported services are DeckList and TappedOut.
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
@page importing_decks Importing Decks
|
||||
|
||||
# Where to import?
|
||||
|
||||
There are three screens in the client which can be used to import decks, depending on the context.
|
||||
|
||||
- The deck editor tab
|
||||
- The pre-game lobby tab
|
||||
- The deck storage tab (not to be confused with the visual deck storage tab)
|
||||
|
||||
# The Deck Editor Tab
|
||||
|
||||
The deck editor tabs (Classic and Visual) offer three ways of importing a deck:
|
||||
|
||||
- From a file on your local storage
|
||||
- From your clipboard
|
||||
- From an online service
|
||||
|
||||
## Local File Storage
|
||||
|
||||
To load a deck from a file on your local storage, select the "Load Deck" action in the "Deck Editor" or "Visual Deck
|
||||
Editor" menu in the application menu bar at the top of the screen. Alternatively, you can use the shortcut Ctrl/Cmd + O
|
||||
to access this action.
|
||||
|
||||
Selecting this action will open a file picker dialog provided by your operating system. Simply select a supported file
|
||||
here and it will be loaded.
|
||||
|
||||
Just below the "Load Deck" action described above is the "Load recent deck" option, which keeps a record of the last 10
|
||||
loaded decks for quick access.
|
||||
|
||||
## From Clipboard
|
||||
|
||||
To load a deck from your clipboard, select the "Load deck from clipboard..." action in the "Deck Editor" or "Visual Deck
|
||||
Editor" menu in the application menu bar at the top of the screen. Alternatively, you can use the shortcut Ctrl/Cmd +
|
||||
Shift + V to access this action.
|
||||
|
||||
Selecting this action will open a new text editor dialog with the contents of your clipboard pasted inside it.
|
||||
|
||||
The import dialog expects each line to be a card with the following format:
|
||||
|
||||
TODO
|
||||
|
||||
Each card should be on a separate line and there should be no empty lines between cards. The first empty line between
|
||||
two blocks of cards will be considered as the divider between mainboard and sideboard.
|
||||
|
||||
Selecting "Parse Set Name and Number (if available)" will automatically parse these options and attempt to resolve them
|
||||
to valid provider IDs found in the card database. If this option is unselected, Cockatrice will import all cards as
|
||||
versions without provider IDs, which means they will display to everyone according to their own user defined set
|
||||
preferences, rather than being the same defined printing for everyone.
|
||||
|
||||
## From an online service
|
||||
|
||||
To load a deck from an online service, select the "Load deck from online service..." action in the "Deck Editor" or "
|
||||
Visual Deck Editor" menu in the application menu bar at the top of the screen.
|
||||
|
||||
Selecting this action will open a dialog containing the contents of your clipboard pasted into it. If your clipboard
|
||||
currently contains a supported URL, the dialog will accept it and close on its own, otherwise you may adjust the URL and
|
||||
confirm.
|
||||
|
||||
The action will automatically import the deck from the online service without any other required user action.
|
||||
|
||||
Currently supported services are Archidekt, Deckstats, Moxfield, and TappedOut.
|
||||
|
|
@ -1,4 +1,13 @@
|
|||
@page user_reference User Reference
|
||||
|
||||
@subpage search_syntax_help
|
||||
@subpage deck_search_syntax_help
|
||||
|
||||
@subpage deck_search_syntax_help
|
||||
|
||||
@subpage creating_decks
|
||||
|
||||
@subpage importing_decks
|
||||
|
||||
@subpage editing_decks
|
||||
|
||||
@subpage exporting_decks
|
||||
BIN
doc/doxygen-images/classic_database_display.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
doc/doxygen-images/classic_database_display_add_buttons.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
doc/doxygen-images/classic_deck_editor.png
Normal file
|
After Width: | Height: | Size: 458 KiB |
BIN
doc/doxygen-images/deck_dock_deck_list.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
doc/doxygen-images/deck_dock_deck_list_buttons.png
Normal file
|
After Width: | Height: | Size: 885 B |
BIN
doc/doxygen-images/deck_dock_deck_list_group_by.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
doc/doxygen-images/deckeditordeckdockwidget.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
|
@ -1,9 +1,3 @@
|
|||
/**
|
||||
* @file card_info.h
|
||||
* @ingroup Cards
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
#ifndef CARD_INFO_H
|
||||
#define CARD_INFO_H
|
||||
|
||||
|
|
@ -54,6 +48,10 @@ class CardInfo : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
private:
|
||||
/** @name Private Card Properties
|
||||
* @anchor PrivateCardProperties
|
||||
*/
|
||||
///@{
|
||||
CardInfoPtr smartThis; ///< Smart pointer to self for safe cross-references.
|
||||
QString name; ///< Full name of the card.
|
||||
QString simpleName; ///< Simplified name for fuzzy matching.
|
||||
|
|
@ -69,6 +67,7 @@ private:
|
|||
bool landscapeOrientation; ///< Orientation flag for rendering.
|
||||
int tableRow; ///< Row index in a table or visual representation.
|
||||
bool upsideDownArt; ///< Whether artwork is flipped for visual purposes.
|
||||
///@}
|
||||
|
||||
public:
|
||||
/**
|
||||
|
|
@ -161,7 +160,7 @@ public:
|
|||
*/
|
||||
CardInfoPtr clone() const
|
||||
{
|
||||
CardInfoPtr newCardInfo = CardInfoPtr(new CardInfo(*this));
|
||||
auto newCardInfo = CardInfoPtr(new CardInfo(*this));
|
||||
newCardInfo->setSmartPointer(newCardInfo); // Set the smart pointer for the new instance
|
||||
return newCardInfo;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,3 @@
|
|||
/**
|
||||
* @file card_database.h
|
||||
* @ingroup CardDatabase
|
||||
* @brief The CardDatabase is responsible for holding the card and set maps, managing card additions/removals,
|
||||
* and providing access to sets and card querying via CardDatabaseQuerier.
|
||||
*/
|
||||
|
||||
#ifndef CARDDATABASE_H
|
||||
#define CARDDATABASE_H
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
/**
|
||||
* @file card_database_loader.h
|
||||
* @ingroup CardDatabase
|
||||
* @brief The CardDatabaseLoader is responsible for discovering, loading, and saving card databases from files on disk.
|
||||
*
|
||||
* This class interacts with available parsers to populate a CardDatabase instance. It also handles
|
||||
* loading main, token, spoiler, and custom card databases.
|
||||
*/
|
||||
|
||||
#ifndef COCKATRICE_CARD_DATABASE_LOADER_H
|
||||
#define COCKATRICE_CARD_DATABASE_LOADER_H
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
/**
|
||||
* @class ExactCard
|
||||
* @ingroup Cards
|
||||
* @ingroup CardPrintings
|
||||
*
|
||||
* @brief Represents a specific card instance, defined by its CardInfo
|
||||
* and a particular printing.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ using SetToPrintingsMap = QMap<QString, QList<PrintingInfo>>;
|
|||
|
||||
/**
|
||||
* @class PrintingInfo
|
||||
* @ingroup Cards
|
||||
* @ingroup CardPrintings
|
||||
*
|
||||
* @brief Represents metadata for a specific variation of a card within a set.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ using CardSetPtr = QSharedPointer<CardSet>;
|
|||
|
||||
/**
|
||||
* @class CardSet
|
||||
* @ingroup Cards
|
||||
* @ingroup CardSets
|
||||
*
|
||||
* @brief A collection of cards grouped under a common identifier.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* @file card_set_comparator.h
|
||||
* @ingroup Cards
|
||||
* @ingroup CardSets
|
||||
* @brief TODO: Document this.
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
/**
|
||||
* @class CardSetList
|
||||
* @ingroup Cards
|
||||
* @ingroup CardSets
|
||||
*
|
||||
* @brief A list-like container for CardSet objects with extended management methods.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -563,6 +563,29 @@ QList<CardRef> DeckList::getCardRefList() const
|
|||
return result;
|
||||
}
|
||||
|
||||
QList<DecklistCardNode *> DeckList::getCardNodes(const QStringList &restrictToZones) const
|
||||
{
|
||||
QList<DecklistCardNode *> result;
|
||||
|
||||
for (auto *node : *root) {
|
||||
auto *zoneNode = dynamic_cast<InnerDecklistNode *>(node);
|
||||
if (zoneNode == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (!restrictToZones.isEmpty() && !restrictToZones.contains(node->getName())) {
|
||||
continue;
|
||||
}
|
||||
for (auto *cardNode : *zoneNode) {
|
||||
auto *cardCardNode = dynamic_cast<DecklistCardNode *>(cardNode);
|
||||
if (cardCardNode != nullptr) {
|
||||
result.append(cardCardNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int DeckList::getSideboardSize() const
|
||||
{
|
||||
int size = 0;
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ public:
|
|||
}
|
||||
QStringList getCardList() const;
|
||||
QList<CardRef> getCardRefList() const;
|
||||
QList<DecklistCardNode *> getCardNodes(const QStringList &restrictToZones = QStringList()) const;
|
||||
int getSideboardSize() const;
|
||||
InnerDecklistNode *getRoot() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -145,10 +145,10 @@ bool InnerDecklistNode::readElement(QXmlStreamReader *xml)
|
|||
const QString childName = xml->name().toString();
|
||||
if (xml->isStartElement()) {
|
||||
if (childName == "zone") {
|
||||
InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this);
|
||||
auto *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this);
|
||||
newZone->readElement(xml);
|
||||
} else if (childName == "card") {
|
||||
DecklistCardNode *newCard = new DecklistCardNode(
|
||||
auto *newCard = new DecklistCardNode(
|
||||
xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(),
|
||||
this, -1, xml->attributes().value("setShortName").toString(),
|
||||
xml->attributes().value("collectorNumber").toString(), xml->attributes().value("uuid").toString());
|
||||
|
|
|
|||
|
|
@ -1095,12 +1095,11 @@ Server_AbstractPlayer::cmdCreateToken(const Command_CreateToken &cmd, ResponseCo
|
|||
arrowInfo->set_start_player_id(player->getPlayerId());
|
||||
arrowInfo->set_start_zone(startCard->getZone()->getName().toStdString());
|
||||
arrowInfo->set_start_card_id(startCard->getId());
|
||||
const Server_AbstractPlayer *arrowTargetPlayer =
|
||||
qobject_cast<const Server_AbstractPlayer *>(targetItem);
|
||||
const auto *arrowTargetPlayer = qobject_cast<const Server_AbstractPlayer *>(targetItem);
|
||||
if (arrowTargetPlayer != nullptr) {
|
||||
arrowInfo->set_target_player_id(arrowTargetPlayer->getPlayerId());
|
||||
} else {
|
||||
const Server_Card *arrowTargetCard = qobject_cast<const Server_Card *>(targetItem);
|
||||
const auto *arrowTargetCard = qobject_cast<const Server_Card *>(targetItem);
|
||||
arrowInfo->set_target_player_id(arrowTargetCard->getZone()->getPlayer()->getPlayerId());
|
||||
arrowInfo->set_target_zone(arrowTargetCard->getZone()->getName().toStdString());
|
||||
arrowInfo->set_target_card_id(arrowTargetCard->getId());
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ void Server_Arrow::getInfo(ServerInfo_Arrow *info)
|
|||
info->set_start_card_id(startCard->getId());
|
||||
info->mutable_arrow_color()->CopyFrom(arrowColor);
|
||||
|
||||
Server_Card *targetCard = qobject_cast<Server_Card *>(targetItem);
|
||||
auto *targetCard = qobject_cast<Server_Card *>(targetItem);
|
||||
if (targetCard) {
|
||||
info->set_target_player_id(targetCard->getZone()->getPlayer()->getPlayerId());
|
||||
info->set_target_zone(targetCard->getZone()->getName().toStdString());
|
||||
|
|
|
|||
|
|
@ -589,7 +589,7 @@ void Server_Game::removeArrowsRelatedToPlayer(GameEventStorage &ges, Server_Abst
|
|||
for (Server_AbstractPlayer *anyPlayer : getPlayers().values()) {
|
||||
QList<Server_Arrow *> toDelete;
|
||||
for (auto *arrow : anyPlayer->getArrows().values()) {
|
||||
Server_Card *targetCard = qobject_cast<Server_Card *>(arrow->getTargetItem());
|
||||
auto *targetCard = qobject_cast<Server_Card *>(arrow->getTargetItem());
|
||||
if (targetCard) {
|
||||
if (targetCard->getZone() != nullptr && targetCard->getZone()->getPlayer() == player)
|
||||
toDelete.append(arrow);
|
||||
|
|
@ -781,7 +781,7 @@ void Server_Game::sendGameEventContainer(GameEventContainer *cont,
|
|||
GameEventContainer *
|
||||
Server_Game::prepareGameEvent(const ::google::protobuf::Message &gameEvent, int playerId, GameEventContext *context)
|
||||
{
|
||||
GameEventContainer *cont = new GameEventContainer;
|
||||
auto *cont = new GameEventContainer;
|
||||
cont->set_game_id(gameId);
|
||||
if (context)
|
||||
cont->mutable_context()->CopyFrom(*context);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ void Server_AbstractUserInterface::sendProtocolItemByType(ServerMessage::Message
|
|||
|
||||
SessionEvent *Server_AbstractUserInterface::prepareSessionEvent(const ::google::protobuf::Message &sessionEvent)
|
||||
{
|
||||
SessionEvent *event = new SessionEvent;
|
||||
auto *event = new SessionEvent;
|
||||
event->GetReflection()
|
||||
->MutableMessage(event, sessionEvent.GetDescriptor()->FindExtensionByName("ext"))
|
||||
->CopyFrom(sessionEvent);
|
||||
|
|
|
|||
|
|
@ -473,7 +473,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
|
|||
|
||||
if (!missingClientFeatures.isEmpty()) {
|
||||
if (features.isRequiredFeaturesMissing(missingClientFeatures, server->getServerRequiredFeatureList())) {
|
||||
Response_Login *re = new Response_Login;
|
||||
auto *re = new Response_Login;
|
||||
re->set_denied_reason_str("Client upgrade required");
|
||||
QMap<QString, bool>::iterator i;
|
||||
for (i = missingClientFeatures.begin(); i != missingClientFeatures.end(); ++i) {
|
||||
|
|
@ -491,7 +491,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
|
|||
clientId, clientVersion, connectionType);
|
||||
switch (res) {
|
||||
case UserIsBanned: {
|
||||
Response_Login *re = new Response_Login;
|
||||
auto *re = new Response_Login;
|
||||
re->set_denied_reason_str(reasonStr.toStdString());
|
||||
if (banSecondsLeft != 0)
|
||||
re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsLeft).toSecsSinceEpoch());
|
||||
|
|
@ -503,7 +503,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
|
|||
case WouldOverwriteOldSession:
|
||||
return Response::RespWouldOverwriteOldSession;
|
||||
case UsernameInvalid: {
|
||||
Response_Login *re = new Response_Login;
|
||||
auto *re = new Response_Login;
|
||||
re->set_denied_reason_str(reasonStr.toStdString());
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespUsernameInvalid;
|
||||
|
|
@ -534,7 +534,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
|
|||
event.set_message(server->getLoginMessage().toStdString());
|
||||
rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event));
|
||||
|
||||
Response_Login *re = new Response_Login;
|
||||
auto *re = new Response_Login;
|
||||
re->mutable_user_info()->CopyFrom(copyUserInfo(true));
|
||||
|
||||
if (authState == PasswordRight) {
|
||||
|
|
@ -616,7 +616,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetGamesOfUser(const Command_G
|
|||
// We don't need to check whether the user is logged in; persistent games should also work.
|
||||
// The client needs to deal with an empty result list.
|
||||
|
||||
Response_GetGamesOfUser *re = new Response_GetGamesOfUser;
|
||||
auto *re = new Response_GetGamesOfUser;
|
||||
server->roomsLock.lockForRead();
|
||||
QMapIterator<int, Server_Room *> roomIterator(server->getRooms());
|
||||
while (roomIterator.hasNext()) {
|
||||
|
|
@ -640,7 +640,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetUserInfo(const Command_GetU
|
|||
return Response::RespLoginNeeded;
|
||||
|
||||
QString userName = nameFromStdString(cmd.user_name());
|
||||
Response_GetUserInfo *re = new Response_GetUserInfo;
|
||||
auto *re = new Response_GetUserInfo;
|
||||
if (userName.isEmpty())
|
||||
re->mutable_user_info()->CopyFrom(*userInfo);
|
||||
else {
|
||||
|
|
@ -713,7 +713,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdJoinRoom(const Command_JoinRoo
|
|||
joinMessageEvent.set_message_type(Event_RoomSay::Welcome);
|
||||
rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, room->prepareRoomEvent(joinMessageEvent));
|
||||
|
||||
Response_JoinRoom *re = new Response_JoinRoom;
|
||||
auto *re = new Response_JoinRoom;
|
||||
room->getInfo(*re->mutable_room_info(), true);
|
||||
|
||||
rc.setResponseExtension(re);
|
||||
|
|
@ -725,7 +725,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdListUsers(const Command_ListUs
|
|||
if (authState == NotLoggedIn)
|
||||
return Response::RespLoginNeeded;
|
||||
|
||||
Response_ListUsers *re = new Response_ListUsers;
|
||||
auto *re = new Response_ListUsers;
|
||||
server->clientsLock.lockForRead();
|
||||
QMapIterator<QString, Server_ProtocolHandler *> userIterator = server->getUsers();
|
||||
while (userIterator.hasNext())
|
||||
|
|
@ -838,10 +838,10 @@ Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room
|
|||
|
||||
// When server doesn't permit registered users to exist, do not honor only-reg setting
|
||||
bool onlyRegisteredUsers = cmd.only_registered() && (server->permitUnregisteredUsers());
|
||||
Server_Game *game = new Server_Game(
|
||||
copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()), cmd.max_players(), gameTypes,
|
||||
cmd.only_buddies(), onlyRegisteredUsers, cmd.spectators_allowed(), cmd.spectators_need_password(),
|
||||
cmd.spectators_can_talk(), cmd.spectators_see_everything(), startingLifeTotal, shareDecklistsOnLoad, room);
|
||||
auto *game = new Server_Game(copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()),
|
||||
cmd.max_players(), gameTypes, cmd.only_buddies(), onlyRegisteredUsers,
|
||||
cmd.spectators_allowed(), cmd.spectators_need_password(), cmd.spectators_can_talk(),
|
||||
cmd.spectators_see_everything(), startingLifeTotal, shareDecklistsOnLoad, room);
|
||||
|
||||
game->addPlayer(this, rc, asSpectator, asJudge, false);
|
||||
room->addGame(game);
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ Server_Room::getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes,
|
|||
|
||||
RoomEvent *Server_Room::prepareRoomEvent(const ::google::protobuf::Message &roomEvent)
|
||||
{
|
||||
RoomEvent *event = new RoomEvent;
|
||||
auto *event = new RoomEvent;
|
||||
event->set_room_id(id);
|
||||
event->GetReflection()
|
||||
->MutableMessage(event, roomEvent.GetDescriptor()->FindExtensionByName("ext"))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "card_database_settings.h"
|
||||
|
||||
CardDatabaseSettings::CardDatabaseSettings(const QString &settingPath, QObject *parent)
|
||||
: SettingsManager(settingPath + "cardDatabase.ini", parent)
|
||||
: SettingsManager(settingPath + "cardDatabase.ini", QString(), QString(), parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
#include "card_override_settings.h"
|
||||
|
||||
CardOverrideSettings::CardOverrideSettings(const QString &settingPath, QObject *parent)
|
||||
: SettingsManager(settingPath + "cardPreferenceOverrides.ini", parent)
|
||||
: SettingsManager(settingPath + "cardPreferenceOverrides.ini", "cards", QString(), parent)
|
||||
{
|
||||
}
|
||||
|
||||
void CardOverrideSettings::setCardPreferenceOverride(const CardRef &cardRef)
|
||||
{
|
||||
setValue(cardRef.providerId, cardRef.name, "cards");
|
||||
setValue(cardRef.providerId, cardRef.name);
|
||||
}
|
||||
|
||||
void CardOverrideSettings::deleteCardPreferenceOverride(const QString &cardName)
|
||||
{
|
||||
deleteValue(cardName, "cards");
|
||||
deleteValue(cardName);
|
||||
}
|
||||
|
||||
QString CardOverrideSettings::getCardPreferenceOverride(const QString &cardName)
|
||||
{
|
||||
return getValue(cardName, "cards").toString();
|
||||
return getValue(cardName).toString();
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
#include <QtCore/QFile>
|
||||
|
||||
DebugSettings::DebugSettings(const QString &settingPath, QObject *parent)
|
||||
: SettingsManager(settingPath + "debug.ini", parent)
|
||||
: SettingsManager(settingPath + "debug.ini", "debug", QString(), parent)
|
||||
{
|
||||
// Create the default debug.ini if it doesn't exist yet
|
||||
if (!QFile(settingPath + "debug.ini").exists()) {
|
||||
|
|
@ -13,7 +13,7 @@ DebugSettings::DebugSettings(const QString &settingPath, QObject *parent)
|
|||
|
||||
bool DebugSettings::getShowCardId()
|
||||
{
|
||||
return getValue("showCardId", "debug").toBool();
|
||||
return getValue("showCardId").toBool();
|
||||
}
|
||||
|
||||
bool DebugSettings::getLocalGameOnStartup()
|
||||
|
|
|
|||
|
|
@ -9,21 +9,21 @@ const QStringList DownloadSettings::DEFAULT_DOWNLOAD_URLS = {
|
|||
"https://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card"};
|
||||
|
||||
DownloadSettings::DownloadSettings(const QString &settingPath, QObject *parent = nullptr)
|
||||
: SettingsManager(settingPath + "downloads.ini", parent)
|
||||
: SettingsManager(settingPath + "downloads.ini", "downloads", QString(), parent)
|
||||
{
|
||||
}
|
||||
|
||||
void DownloadSettings::setDownloadUrls(const QStringList &downloadURLs)
|
||||
{
|
||||
setValue(QVariant::fromValue(downloadURLs), "urls", "downloads");
|
||||
setValue(QVariant::fromValue(downloadURLs), "urls");
|
||||
}
|
||||
|
||||
QStringList DownloadSettings::getAllURLs()
|
||||
{
|
||||
return getValue("urls", "downloads").toStringList();
|
||||
return getValue("urls").toStringList();
|
||||
}
|
||||
|
||||
void DownloadSettings::resetToDefaultURLs()
|
||||
{
|
||||
setValue(QVariant::fromValue(DEFAULT_DOWNLOAD_URLS), "urls", "downloads");
|
||||
setValue(QVariant::fromValue(DEFAULT_DOWNLOAD_URLS), "urls");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include <QTime>
|
||||
|
||||
GameFiltersSettings::GameFiltersSettings(const QString &settingPath, QObject *parent)
|
||||
: SettingsManager(settingPath + "gamefilters.ini", parent)
|
||||
: SettingsManager(settingPath + "gamefilters.ini", "filter_games", QString(), parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -19,190 +19,190 @@ QString GameFiltersSettings::hashGameType(const QString &gameType) const
|
|||
|
||||
void GameFiltersSettings::setHideBuddiesOnlyGames(bool hide)
|
||||
{
|
||||
setValue(hide, "hide_buddies_only_games", "filter_games");
|
||||
setValue(hide, "hide_buddies_only_games");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isHideBuddiesOnlyGames()
|
||||
{
|
||||
QVariant previous = getValue("hide_buddies_only_games", "filter_games");
|
||||
QVariant previous = getValue("hide_buddies_only_games");
|
||||
return previous == QVariant() || previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setHideFullGames(bool hide)
|
||||
{
|
||||
setValue(hide, "hide_full_games", "filter_games");
|
||||
setValue(hide, "hide_full_games");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isHideFullGames()
|
||||
{
|
||||
QVariant previous = getValue("hide_full_games", "filter_games");
|
||||
QVariant previous = getValue("hide_full_games");
|
||||
return !(previous == QVariant()) && previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setHideGamesThatStarted(bool hide)
|
||||
{
|
||||
setValue(hide, "hide_games_that_started", "filter_games");
|
||||
setValue(hide, "hide_games_that_started");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isHideGamesThatStarted()
|
||||
{
|
||||
QVariant previous = getValue("hide_games_that_started", "filter_games");
|
||||
QVariant previous = getValue("hide_games_that_started");
|
||||
return !(previous == QVariant()) && previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setHidePasswordProtectedGames(bool hide)
|
||||
{
|
||||
setValue(hide, "hide_password_protected_games", "filter_games");
|
||||
setValue(hide, "hide_password_protected_games");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isHidePasswordProtectedGames()
|
||||
{
|
||||
QVariant previous = getValue("hide_password_protected_games", "filter_games");
|
||||
QVariant previous = getValue("hide_password_protected_games");
|
||||
return previous == QVariant() || previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setHideIgnoredUserGames(bool hide)
|
||||
{
|
||||
setValue(hide, "hide_ignored_user_games", "filter_games");
|
||||
setValue(hide, "hide_ignored_user_games");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isHideIgnoredUserGames()
|
||||
{
|
||||
QVariant previous = getValue("hide_ignored_user_games", "filter_games");
|
||||
QVariant previous = getValue("hide_ignored_user_games");
|
||||
return !(previous == QVariant()) && previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setHideNotBuddyCreatedGames(bool hide)
|
||||
{
|
||||
setValue(hide, "hide_not_buddy_created_games", "filter_games");
|
||||
setValue(hide, "hide_not_buddy_created_games");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isHideNotBuddyCreatedGames()
|
||||
{
|
||||
QVariant previous = getValue("hide_not_buddy_created_games", "filter_games");
|
||||
QVariant previous = getValue("hide_not_buddy_created_games");
|
||||
return !(previous == QVariant()) && previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setHideOpenDecklistGames(bool hide)
|
||||
{
|
||||
setValue(hide, "hide_open_decklist_games", "filter_games");
|
||||
setValue(hide, "hide_open_decklist_games");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isHideOpenDecklistGames()
|
||||
{
|
||||
QVariant previous = getValue("hide_open_decklist_games", "filter_games");
|
||||
QVariant previous = getValue("hide_open_decklist_games");
|
||||
return !(previous == QVariant()) && previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setGameNameFilter(QString gameName)
|
||||
{
|
||||
setValue(gameName, "game_name_filter", "filter_games");
|
||||
setValue(gameName, "game_name_filter");
|
||||
}
|
||||
|
||||
QString GameFiltersSettings::getGameNameFilter()
|
||||
{
|
||||
return getValue("game_name_filter", "filter_games").toString();
|
||||
return getValue("game_name_filter").toString();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setCreatorNameFilter(QString creatorName)
|
||||
{
|
||||
setValue(creatorName, "creator_name_filter", "filter_games");
|
||||
setValue(creatorName, "creator_name_filter");
|
||||
}
|
||||
|
||||
QString GameFiltersSettings::getCreatorNameFilter()
|
||||
{
|
||||
return getValue("creator_name_filter", "filter_games").toString();
|
||||
return getValue("creator_name_filter").toString();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setMinPlayers(int min)
|
||||
{
|
||||
setValue(min, "min_players", "filter_games");
|
||||
setValue(min, "min_players");
|
||||
}
|
||||
|
||||
int GameFiltersSettings::getMinPlayers()
|
||||
{
|
||||
QVariant previous = getValue("min_players", "filter_games");
|
||||
QVariant previous = getValue("min_players");
|
||||
return previous == QVariant() ? 1 : previous.toInt();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setMaxPlayers(int max)
|
||||
{
|
||||
setValue(max, "max_players", "filter_games");
|
||||
setValue(max, "max_players");
|
||||
}
|
||||
|
||||
int GameFiltersSettings::getMaxPlayers()
|
||||
{
|
||||
QVariant previous = getValue("max_players", "filter_games");
|
||||
QVariant previous = getValue("max_players");
|
||||
return previous == QVariant() ? 99 : previous.toInt();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setMaxGameAge(const QTime &maxGameAge)
|
||||
{
|
||||
setValue(maxGameAge, "max_game_age_time", "filter_games");
|
||||
setValue(maxGameAge, "max_game_age_time");
|
||||
}
|
||||
|
||||
QTime GameFiltersSettings::getMaxGameAge()
|
||||
{
|
||||
QVariant previous = getValue("max_game_age_time", "filter_games");
|
||||
QVariant previous = getValue("max_game_age_time");
|
||||
return previous.toTime();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setGameTypeEnabled(QString gametype, bool enabled)
|
||||
{
|
||||
setValue(enabled, "game_type/" + hashGameType(gametype), "filter_games");
|
||||
setValue(enabled, "game_type/" + hashGameType(gametype));
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setGameHashedTypeEnabled(QString gametypeHASHED, bool enabled)
|
||||
{
|
||||
setValue(enabled, gametypeHASHED, "filter_games");
|
||||
setValue(enabled, gametypeHASHED);
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isGameTypeEnabled(QString gametype)
|
||||
{
|
||||
QVariant previous = getValue("game_type/" + hashGameType(gametype), "filter_games");
|
||||
QVariant previous = getValue("game_type/" + hashGameType(gametype));
|
||||
return previous == QVariant() ? false : previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setShowOnlyIfSpectatorsCanWatch(bool show)
|
||||
{
|
||||
setValue(show, "show_only_if_spectators_can_watch", "filter_games");
|
||||
setValue(show, "show_only_if_spectators_can_watch");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isShowOnlyIfSpectatorsCanWatch()
|
||||
{
|
||||
QVariant previous = getValue("show_only_if_spectators_can_watch", "filter_games");
|
||||
QVariant previous = getValue("show_only_if_spectators_can_watch");
|
||||
return previous == QVariant() ? false : previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setShowSpectatorPasswordProtected(bool show)
|
||||
{
|
||||
setValue(show, "show_spectator_password_protected", "filter_games");
|
||||
setValue(show, "show_spectator_password_protected");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isShowSpectatorPasswordProtected()
|
||||
{
|
||||
QVariant previous = getValue("show_spectator_password_protected", "filter_games");
|
||||
QVariant previous = getValue("show_spectator_password_protected");
|
||||
return previous == QVariant() ? true : previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setShowOnlyIfSpectatorsCanChat(bool show)
|
||||
{
|
||||
setValue(show, "show_only_if_spectators_can_chat", "filter_games");
|
||||
setValue(show, "show_only_if_spectators_can_chat");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isShowOnlyIfSpectatorsCanChat()
|
||||
{
|
||||
QVariant previous = getValue("show_only_if_spectators_can_chat", "filter_games");
|
||||
QVariant previous = getValue("show_only_if_spectators_can_chat");
|
||||
return previous == QVariant() ? true : previous.toBool();
|
||||
}
|
||||
|
||||
void GameFiltersSettings::setShowOnlyIfSpectatorsCanSeeHands(bool show)
|
||||
{
|
||||
setValue(show, "show_only_if_spectators_can_see_hands", "filter_games");
|
||||
setValue(show, "show_only_if_spectators_can_see_hands");
|
||||
}
|
||||
|
||||
bool GameFiltersSettings::isShowOnlyIfSpectatorsCanSeeHands()
|
||||
{
|
||||
QVariant previous = getValue("show_only_if_spectators_can_see_hands", "filter_games");
|
||||
QVariant previous = getValue("show_only_if_spectators_can_see_hands");
|
||||
return previous == QVariant() ? true : previous.toBool();
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include "layouts_settings.h"
|
||||
|
||||
LayoutsSettings::LayoutsSettings(const QString &settingPath, QObject *parent)
|
||||
: SettingsManager(settingPath + "layouts.ini", parent)
|
||||
: SettingsManager(settingPath + "layouts.ini", "layouts", QString(), parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
#include "message_settings.h"
|
||||
|
||||
MessageSettings::MessageSettings(const QString &settingPath, QObject *parent)
|
||||
: SettingsManager(settingPath + "messages.ini", parent)
|
||||
: SettingsManager(settingPath + "messages.ini", "messages", QString(), parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString MessageSettings::getMessageAt(int index)
|
||||
{
|
||||
return getValue(QString("msg%1").arg(index), "messages").toString();
|
||||
return getValue(QString("msg%1").arg(index)).toString();
|
||||
}
|
||||
|
||||
int MessageSettings::getCount()
|
||||
{
|
||||
return getValue("count", "messages").toInt();
|
||||
return getValue("count").toInt();
|
||||
}
|
||||
|
||||
void MessageSettings::setCount(int count)
|
||||
{
|
||||
setValue(count, "count", "messages");
|
||||
setValue(count, "count");
|
||||
}
|
||||
|
||||
void MessageSettings::setMessageAt(int index, QString message)
|
||||
{
|
||||
setValue(message, QString("msg%1").arg(index), "messages");
|
||||
setValue(message, QString("msg%1").arg(index));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,22 +3,22 @@
|
|||
#define MAX_RECENT_DECK_COUNT 10
|
||||
|
||||
RecentsSettings::RecentsSettings(const QString &settingPath, QObject *parent)
|
||||
: SettingsManager(settingPath + "recents.ini", parent)
|
||||
: SettingsManager(settingPath + "recents.ini", "deckbuilder", QString(), parent)
|
||||
{
|
||||
}
|
||||
|
||||
QStringList RecentsSettings::getRecentlyOpenedDeckPaths()
|
||||
{
|
||||
return getValue("deckpaths", "deckbuilder").toStringList();
|
||||
return getValue("deckpaths").toStringList();
|
||||
}
|
||||
void RecentsSettings::clearRecentlyOpenedDeckPaths()
|
||||
{
|
||||
deleteValue("deckpaths", "deckbuilder");
|
||||
deleteValue("deckpaths");
|
||||
emit recentlyOpenedDeckPathsChanged();
|
||||
}
|
||||
void RecentsSettings::updateRecentlyOpenedDeckPaths(const QString &deckPath)
|
||||
{
|
||||
auto deckPaths = getValue("deckpaths", "deckbuilder").toStringList();
|
||||
auto deckPaths = getValue("deckpaths").toStringList();
|
||||
deckPaths.removeAll(deckPath);
|
||||
|
||||
deckPaths.prepend(deckPath);
|
||||
|
|
@ -27,7 +27,7 @@ void RecentsSettings::updateRecentlyOpenedDeckPaths(const QString &deckPath)
|
|||
deckPaths.removeLast();
|
||||
}
|
||||
|
||||
setValue(deckPaths, "deckpaths", "deckbuilder");
|
||||
setValue(deckPaths, "deckpaths");
|
||||
emit recentlyOpenedDeckPathsChanged();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,34 +4,34 @@
|
|||
#include <utility>
|
||||
|
||||
ServersSettings::ServersSettings(const QString &settingPath, QObject *parent)
|
||||
: SettingsManager(settingPath + "servers.ini", parent)
|
||||
: SettingsManager(settingPath + "servers.ini", "server", QString(), parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ServersSettings::setPreviousHostLogin(int previous)
|
||||
{
|
||||
setValue(previous, "previoushostlogin", "server");
|
||||
setValue(previous, "previoushostlogin");
|
||||
}
|
||||
|
||||
int ServersSettings::getPreviousHostLogin()
|
||||
{
|
||||
QVariant previous = getValue("previoushostlogin", "server");
|
||||
QVariant previous = getValue("previoushostlogin");
|
||||
return previous == QVariant() ? 1 : previous.toInt();
|
||||
}
|
||||
|
||||
void ServersSettings::setPreviousHostList(QStringList list)
|
||||
{
|
||||
setValue(list, "previoushosts", "server");
|
||||
setValue(list, "previoushosts");
|
||||
}
|
||||
|
||||
QStringList ServersSettings::getPreviousHostList()
|
||||
{
|
||||
return getValue("previoushosts", "server").toStringList();
|
||||
return getValue("previoushosts").toStringList();
|
||||
}
|
||||
|
||||
void ServersSettings::setPrevioushostName(const QString &name)
|
||||
{
|
||||
setValue(name, "previoushostName", "server");
|
||||
setValue(name, "previoushostName");
|
||||
}
|
||||
|
||||
QString ServersSettings::getSaveName(QString defaultname)
|
||||
|
|
@ -50,7 +50,7 @@ QString ServersSettings::getSite(QString defaultSite)
|
|||
|
||||
QString ServersSettings::getPrevioushostName()
|
||||
{
|
||||
QVariant value = getValue("previoushostName", "server");
|
||||
QVariant value = getValue("previoushostName");
|
||||
return value == QVariant() ? "Rooster Ranges" : value.toString();
|
||||
}
|
||||
|
||||
|
|
@ -107,56 +107,56 @@ bool ServersSettings::getSavePassword()
|
|||
|
||||
void ServersSettings::setAutoConnect(int autoconnect)
|
||||
{
|
||||
setValue(autoconnect, "auto_connect", "server");
|
||||
setValue(autoconnect, "auto_connect");
|
||||
}
|
||||
|
||||
int ServersSettings::getAutoConnect()
|
||||
{
|
||||
QVariant autoconnect = getValue("auto_connect", "server");
|
||||
QVariant autoconnect = getValue("auto_connect");
|
||||
return autoconnect == QVariant() ? 0 : autoconnect.toInt();
|
||||
}
|
||||
|
||||
void ServersSettings::setFPHostName(QString hostname)
|
||||
{
|
||||
setValue(hostname, "fphostname", "server");
|
||||
setValue(hostname, "fphostname");
|
||||
}
|
||||
|
||||
QString ServersSettings::getFPHostname(QString defaultHost)
|
||||
{
|
||||
QVariant hostname = getValue("fphostname", "server");
|
||||
QVariant hostname = getValue("fphostname");
|
||||
return hostname == QVariant() ? std::move(defaultHost) : hostname.toString();
|
||||
}
|
||||
|
||||
void ServersSettings::setFPPort(QString port)
|
||||
{
|
||||
setValue(port, "fpport", "server");
|
||||
setValue(port, "fpport");
|
||||
}
|
||||
|
||||
QString ServersSettings::getFPPort(QString defaultPort)
|
||||
{
|
||||
QVariant port = getValue("fpport", "server");
|
||||
QVariant port = getValue("fpport");
|
||||
return port == QVariant() ? std::move(defaultPort) : port.toString();
|
||||
}
|
||||
|
||||
void ServersSettings::setFPPlayerName(QString playerName)
|
||||
{
|
||||
setValue(playerName, "fpplayername", "server");
|
||||
setValue(playerName, "fpplayername");
|
||||
}
|
||||
|
||||
QString ServersSettings::getFPPlayerName(QString defaultName)
|
||||
{
|
||||
QVariant name = getValue("fpplayername", "server");
|
||||
QVariant name = getValue("fpplayername");
|
||||
return name == QVariant() ? std::move(defaultName) : name.toString();
|
||||
}
|
||||
|
||||
void ServersSettings::setClearDebugLogStatus(bool abIsChecked)
|
||||
{
|
||||
setValue(abIsChecked, "save_debug_log", "server");
|
||||
setValue(abIsChecked, "save_debug_log");
|
||||
}
|
||||
|
||||
bool ServersSettings::getClearDebugLogStatus(bool abDefaultValue)
|
||||
{
|
||||
QVariant cbFlushLog = getValue("save_debug_log", "server");
|
||||
QVariant cbFlushLog = getValue("save_debug_log");
|
||||
return cbFlushLog == QVariant() ? abDefaultValue : cbFlushLog.toBool();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,35 @@
|
|||
#include "settings_manager.h"
|
||||
|
||||
SettingsManager::SettingsManager(const QString &settingPath, QObject *parent)
|
||||
: QObject(parent), settings(settingPath, QSettings::IniFormat)
|
||||
SettingsManager::SettingsManager(const QString &settingPath,
|
||||
const QString &_defaultGroup,
|
||||
const QString &_defaultSubGroup,
|
||||
QObject *parent)
|
||||
: QObject(parent), settings(settingPath, QSettings::IniFormat), defaultGroup(_defaultGroup),
|
||||
defaultSubGroup(_defaultSubGroup)
|
||||
{
|
||||
}
|
||||
|
||||
void SettingsManager::setValue(const QVariant &value, const QString &name)
|
||||
{
|
||||
if (!defaultGroup.isEmpty()) {
|
||||
settings.beginGroup(defaultGroup);
|
||||
}
|
||||
|
||||
if (!defaultSubGroup.isEmpty()) {
|
||||
settings.beginGroup(defaultSubGroup);
|
||||
}
|
||||
|
||||
settings.setValue(name, value);
|
||||
|
||||
if (!defaultSubGroup.isEmpty()) {
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
if (!defaultGroup.isEmpty()) {
|
||||
settings.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsManager::setValue(const QVariant &value,
|
||||
const QString &name,
|
||||
const QString &group,
|
||||
|
|
@ -29,6 +54,27 @@ void SettingsManager::setValue(const QVariant &value,
|
|||
}
|
||||
}
|
||||
|
||||
void SettingsManager::deleteValue(const QString &name)
|
||||
{
|
||||
if (!defaultGroup.isEmpty()) {
|
||||
settings.beginGroup(defaultGroup);
|
||||
}
|
||||
|
||||
if (!defaultSubGroup.isEmpty()) {
|
||||
settings.beginGroup(defaultSubGroup);
|
||||
}
|
||||
|
||||
settings.remove(name);
|
||||
|
||||
if (!defaultSubGroup.isEmpty()) {
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
if (!defaultGroup.isEmpty()) {
|
||||
settings.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsManager::deleteValue(const QString &name, const QString &group, const QString &subGroup)
|
||||
{
|
||||
if (!group.isEmpty()) {
|
||||
|
|
@ -50,6 +96,29 @@ void SettingsManager::deleteValue(const QString &name, const QString &group, con
|
|||
}
|
||||
}
|
||||
|
||||
QVariant SettingsManager::getValue(const QString &name)
|
||||
{
|
||||
if (!defaultGroup.isEmpty()) {
|
||||
settings.beginGroup(defaultGroup);
|
||||
}
|
||||
|
||||
if (!defaultSubGroup.isEmpty()) {
|
||||
settings.beginGroup(defaultSubGroup);
|
||||
}
|
||||
|
||||
QVariant value = settings.value(name);
|
||||
|
||||
if (!defaultSubGroup.isEmpty()) {
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
if (!defaultGroup.isEmpty()) {
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
QVariant SettingsManager::getValue(const QString &name, const QString &group, const QString &subGroup)
|
||||
{
|
||||
if (!group.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -16,14 +16,23 @@ class SettingsManager : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SettingsManager(const QString &settingPath, QObject *parent = nullptr);
|
||||
QVariant getValue(const QString &name, const QString &group = "", const QString &subGroup = "");
|
||||
explicit SettingsManager(const QString &settingPath,
|
||||
const QString &defaultGroup = QString(),
|
||||
const QString &defaultSubGroup = QString(),
|
||||
QObject *parent = nullptr);
|
||||
QVariant getValue(const QString &name);
|
||||
QVariant getValue(const QString &name, const QString &group, const QString &subGroup = QString());
|
||||
void sync();
|
||||
|
||||
protected:
|
||||
QSettings settings;
|
||||
void setValue(const QVariant &value, const QString &name, const QString &group = "", const QString &subGroup = "");
|
||||
void deleteValue(const QString &name, const QString &group = "", const QString &subGroup = "");
|
||||
QString defaultGroup;
|
||||
QString defaultSubGroup;
|
||||
void setValue(const QVariant &value, const QString &name);
|
||||
void
|
||||
setValue(const QVariant &value, const QString &name, const QString &group, const QString &subGroup = QString());
|
||||
void deleteValue(const QString &name);
|
||||
void deleteValue(const QString &name, const QString &group, const QString &subGroup = QString());
|
||||
};
|
||||
|
||||
#endif // SETTINGSMANAGER_H
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ set(oracle_SOURCES
|
|||
src/main.cpp
|
||||
src/oraclewizard.cpp
|
||||
src/oracleimporter.cpp
|
||||
src/pages.cpp
|
||||
src/pagetemplates.cpp
|
||||
src/parsehelpers.cpp
|
||||
src/qt-json/json.cpp
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "oracleimporter.h"
|
||||
|
||||
#include "client/settings/cache_settings.h"
|
||||
#include "libcockatrice/interfaces/noop_card_preference_provider.h"
|
||||
#include "libcockatrice/interfaces/noop_card_set_priority_controller.h"
|
||||
#include "parsehelpers.h"
|
||||
#include "qt-json/json.h"
|
||||
|
||||
|
|
@ -8,7 +10,6 @@
|
|||
#include <QRegularExpression>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <database/interface/settings_card_preference_provider.h>
|
||||
#include <libcockatrice/card/database/parser/cockatrice_xml_4.h>
|
||||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
|
||||
|
|
@ -457,14 +458,17 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList
|
|||
|
||||
int OracleImporter::startImport()
|
||||
{
|
||||
int setIndex = 0;
|
||||
static ICardSetPriorityController *noOpController = new NoopCardSetPriorityController();
|
||||
|
||||
// add an empty set for tokens
|
||||
CardSetPtr tokenSet = CardSet::newInstance(SettingsCache::instance().cardDatabase(), CardSet::TOKENS_SETNAME,
|
||||
tr("Dummy set containing tokens"), "Tokens");
|
||||
CardSetPtr tokenSet =
|
||||
CardSet::newInstance(noOpController, CardSet::TOKENS_SETNAME, tr("Dummy set containing tokens"), "Tokens");
|
||||
sets.insert(CardSet::TOKENS_SETNAME, tokenSet);
|
||||
|
||||
int setIndex = 0;
|
||||
|
||||
for (const SetToDownload &curSetToParse : allSets) {
|
||||
CardSetPtr newSet = CardSet::newInstance(SettingsCache::instance().cardDatabase(), curSetToParse.getShortName(),
|
||||
CardSetPtr newSet = CardSet::newInstance(noOpController, curSetToParse.getShortName(),
|
||||
curSetToParse.getLongName(), curSetToParse.getSetType(),
|
||||
curSetToParse.getReleaseDate(), curSetToParse.getPriority());
|
||||
if (!sets.contains(newSet->getShortName()))
|
||||
|
|
@ -485,7 +489,7 @@ int OracleImporter::startImport()
|
|||
|
||||
bool OracleImporter::saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion)
|
||||
{
|
||||
CockatriceXml4Parser parser(new SettingsCardPreferenceProvider());
|
||||
CockatriceXml4Parser parser(new NoopCardPreferenceProvider());
|
||||
return parser.saveToFile(sets, cards, fileName, sourceUrl, sourceVersion);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,55 +3,19 @@
|
|||
#include "client/settings/cache_settings.h"
|
||||
#include "main.h"
|
||||
#include "oracleimporter.h"
|
||||
#include "version_string.h"
|
||||
#include "pages.h"
|
||||
#include "pagetemplates.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QBuffer>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QProgressBar>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QScrollBar>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextEdit>
|
||||
#include <QtConcurrent>
|
||||
#include <QtGui>
|
||||
|
||||
#ifdef HAS_LZMA
|
||||
#include "lzma/decompress.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_ZLIB
|
||||
#include "zip/unzip.h"
|
||||
#endif
|
||||
|
||||
#define ZIP_SIGNATURE "PK"
|
||||
// Xz stream header: 0xFD + "7zXZ"
|
||||
#define XZ_SIGNATURE "\xFD\x37\x7A\x58\x5A"
|
||||
#define MTGJSON_V4_URL_COMPONENT "mtgjson.com/files/"
|
||||
#define ALLSETS_URL_FALLBACK "https://www.mtgjson.com/api/v5/AllPrintings.json"
|
||||
#define MTGJSON_VERSION_URL "https://www.mtgjson.com/api/v5/Meta.json"
|
||||
|
||||
#ifdef HAS_LZMA
|
||||
#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.xz"
|
||||
#elif defined(HAS_ZLIB)
|
||||
#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.zip"
|
||||
#else
|
||||
#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json"
|
||||
#endif
|
||||
|
||||
#define TOKENS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml"
|
||||
#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml"
|
||||
|
||||
OracleWizard::OracleWizard(QWidget *parent) : QWizard(parent)
|
||||
{
|
||||
// define a dummy context that will be used where needed
|
||||
|
|
@ -142,669 +106,3 @@ bool OracleWizard::saveTokensToFile(const QString &fileName)
|
|||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
IntroPage::IntroPage(QWidget *parent) : OracleWizardPage(parent)
|
||||
{
|
||||
label = new QLabel(this);
|
||||
label->setWordWrap(true);
|
||||
|
||||
languageLabel = new QLabel(this);
|
||||
versionLabel = new QLabel(this);
|
||||
languageBox = new QComboBox(this);
|
||||
|
||||
QStringList languageCodes = findQmFiles();
|
||||
for (const QString &code : languageCodes) {
|
||||
QString langName = languageName(code);
|
||||
languageBox->addItem(langName, code);
|
||||
}
|
||||
|
||||
QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME);
|
||||
int index = languageBox->findText(setLanguage, Qt::MatchExactly);
|
||||
if (index == -1) {
|
||||
qWarning() << "could not find language" << setLanguage;
|
||||
} else {
|
||||
languageBox->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
connect(languageBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &IntroPage::languageBoxChanged);
|
||||
|
||||
auto *layout = new QGridLayout(this);
|
||||
layout->addWidget(label, 0, 0, 1, 2);
|
||||
layout->addWidget(languageLabel, 1, 0);
|
||||
layout->addWidget(languageBox, 1, 1);
|
||||
layout->addWidget(versionLabel, 2, 0, 1, 2);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void IntroPage::initializePage()
|
||||
{
|
||||
if (wizard()->backgroundMode) {
|
||||
emit readyToContinue();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList IntroPage::findQmFiles()
|
||||
{
|
||||
QDir dir(translationPath);
|
||||
QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name);
|
||||
fileNames.replaceInStrings(QRegularExpression(translationPrefix + "_(.*)\\.qm"), "\\1");
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
QString IntroPage::languageName(const QString &lang)
|
||||
{
|
||||
QTranslator qTranslator;
|
||||
|
||||
QString appNameHint = translationPrefix + "_" + lang;
|
||||
bool appTranslationLoaded = qTranslator.load(appNameHint, translationPath);
|
||||
if (!appTranslationLoaded) {
|
||||
qDebug() << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" << translationPath;
|
||||
}
|
||||
|
||||
return qTranslator.translate("i18n", DEFAULT_LANG_NAME);
|
||||
}
|
||||
|
||||
void IntroPage::languageBoxChanged(int index)
|
||||
{
|
||||
SettingsCache::instance().setLang(languageBox->itemData(index).toString());
|
||||
}
|
||||
|
||||
void IntroPage::retranslateUi()
|
||||
{
|
||||
setTitle(tr("Introduction"));
|
||||
label->setText(tr("This wizard will import the list of sets, cards, and tokens "
|
||||
"that will be used by Cockatrice."));
|
||||
languageLabel->setText(tr("Interface language:"));
|
||||
versionLabel->setText(tr("Version:") + QString(" %1").arg(VERSION_STRING));
|
||||
}
|
||||
|
||||
void OutroPage::retranslateUi()
|
||||
{
|
||||
setTitle(tr("Finished"));
|
||||
setSubTitle(tr("The wizard has finished.") + "<br>" +
|
||||
tr("You can now start using Cockatrice with the newly updated cards.") + "<br><br>" +
|
||||
tr("If the card databases don't reload automatically, restart the Cockatrice client."));
|
||||
}
|
||||
|
||||
void OutroPage::initializePage()
|
||||
{
|
||||
if (wizard()->backgroundMode) {
|
||||
wizard()->accept();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
LoadSetsPage::LoadSetsPage(QWidget *parent) : OracleWizardPage(parent)
|
||||
{
|
||||
urlRadioButton = new QRadioButton(this);
|
||||
fileRadioButton = new QRadioButton(this);
|
||||
|
||||
urlLineEdit = new QLineEdit(this);
|
||||
fileLineEdit = new QLineEdit(this);
|
||||
|
||||
progressLabel = new QLabel(this);
|
||||
progressBar = new QProgressBar(this);
|
||||
|
||||
urlRadioButton->setChecked(true);
|
||||
|
||||
urlButton = new QPushButton(this);
|
||||
connect(urlButton, &QPushButton::clicked, this, &LoadSetsPage::actRestoreDefaultUrl);
|
||||
|
||||
fileButton = new QPushButton(this);
|
||||
connect(fileButton, &QPushButton::clicked, this, &LoadSetsPage::actLoadSetsFile);
|
||||
|
||||
auto *layout = new QGridLayout(this);
|
||||
layout->addWidget(urlRadioButton, 0, 0);
|
||||
layout->addWidget(urlLineEdit, 0, 1);
|
||||
layout->addWidget(urlButton, 1, 1, Qt::AlignRight);
|
||||
layout->addWidget(fileRadioButton, 2, 0);
|
||||
layout->addWidget(fileLineEdit, 2, 1);
|
||||
layout->addWidget(fileButton, 3, 1, Qt::AlignRight);
|
||||
layout->addWidget(progressLabel, 4, 0);
|
||||
layout->addWidget(progressBar, 4, 1);
|
||||
|
||||
connect(&watcher, &QFutureWatcher<bool>::finished, this, &LoadSetsPage::importFinished);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void LoadSetsPage::initializePage()
|
||||
{
|
||||
urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString());
|
||||
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
|
||||
if (wizard()->backgroundMode) {
|
||||
if (isEnabled()) {
|
||||
validatePage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSetsPage::retranslateUi()
|
||||
{
|
||||
setTitle(tr("Source selection"));
|
||||
setSubTitle(tr("Please specify a compatible source for the list of sets and cards. "
|
||||
"You can specify a URL address that will be downloaded or "
|
||||
"use an existing file from your computer."));
|
||||
|
||||
urlRadioButton->setText(tr("Download URL:"));
|
||||
fileRadioButton->setText(tr("Local file:"));
|
||||
urlButton->setText(tr("Restore default URL"));
|
||||
fileButton->setText(tr("Choose file..."));
|
||||
}
|
||||
|
||||
void LoadSetsPage::actRestoreDefaultUrl()
|
||||
{
|
||||
urlLineEdit->setText(ALLSETS_URL);
|
||||
}
|
||||
|
||||
void LoadSetsPage::actLoadSetsFile()
|
||||
{
|
||||
QFileDialog dialog(this, tr("Load sets file"));
|
||||
dialog.setFileMode(QFileDialog::ExistingFile);
|
||||
|
||||
QString extensions = "*.json *.xml";
|
||||
#ifdef HAS_ZLIB
|
||||
extensions += " *.zip";
|
||||
#endif
|
||||
#ifdef HAS_LZMA
|
||||
extensions += " *.xz";
|
||||
#endif
|
||||
dialog.setNameFilter(tr("Sets file (%1)").arg(extensions));
|
||||
|
||||
if (!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text())) {
|
||||
dialog.selectFile(fileLineEdit->text());
|
||||
}
|
||||
|
||||
if (!dialog.exec()) {
|
||||
return;
|
||||
}
|
||||
|
||||
fileLineEdit->setText(dialog.selectedFiles().at(0));
|
||||
}
|
||||
|
||||
bool LoadSetsPage::validatePage()
|
||||
{
|
||||
// once the import is finished, we call next(); skip validation
|
||||
if (wizard()->downloadedPlainXml || wizard()->importer->getSets().count() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// else, try to import sets
|
||||
if (urlRadioButton->isChecked()) {
|
||||
// If a user attempts to download from V4, redirect them to V5
|
||||
if (urlLineEdit->text().contains(MTGJSON_V4_URL_COMPONENT)) {
|
||||
actRestoreDefaultUrl();
|
||||
}
|
||||
|
||||
const auto url = QUrl::fromUserInput(urlLineEdit->text());
|
||||
|
||||
if (!url.isValid()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid."));
|
||||
return false;
|
||||
}
|
||||
|
||||
progressLabel->setText(tr("Downloading (0MB)"));
|
||||
// show an infinite progressbar
|
||||
progressBar->setMaximum(0);
|
||||
progressBar->setMinimum(0);
|
||||
progressBar->setValue(0);
|
||||
progressLabel->show();
|
||||
progressBar->show();
|
||||
|
||||
wizard()->disableButtons();
|
||||
setEnabled(false);
|
||||
|
||||
downloadSetsFile(url);
|
||||
} else if (fileRadioButton->isChecked()) {
|
||||
QFile setsFile(fileLineEdit->text());
|
||||
if (!setsFile.exists()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Please choose a file."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setsFile.open(QIODevice::ReadOnly)) {
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text()));
|
||||
return false;
|
||||
}
|
||||
|
||||
wizard()->disableButtons();
|
||||
setEnabled(false);
|
||||
|
||||
wizard()->setCardSourceUrl(setsFile.fileName());
|
||||
wizard()->setCardSourceVersion("unknown");
|
||||
|
||||
readSetsFromByteArray(setsFile.readAll());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LoadSetsPage::downloadSetsFile(const QUrl &url)
|
||||
{
|
||||
wizard()->setCardSourceVersion("unknown");
|
||||
|
||||
const auto urlString = url.toString();
|
||||
if (urlString == ALLSETS_URL || urlString == ALLSETS_URL_FALLBACK) {
|
||||
const auto versionUrl = QUrl::fromUserInput(MTGJSON_VERSION_URL);
|
||||
auto *versionReply = wizard()->nam->get(QNetworkRequest(versionUrl));
|
||||
connect(versionReply, &QNetworkReply::finished, [this, versionReply]() {
|
||||
if (versionReply->error() == QNetworkReply::NoError) {
|
||||
auto jsonData = versionReply->readAll();
|
||||
QJsonParseError jsonError{};
|
||||
auto jsonResponse = QJsonDocument::fromJson(jsonData, &jsonError);
|
||||
|
||||
if (jsonError.error == QJsonParseError::NoError) {
|
||||
const auto jsonMap = jsonResponse.toVariant().toMap();
|
||||
|
||||
auto versionString = jsonMap.value("meta").toMap().value("version").toString();
|
||||
if (versionString.isEmpty()) {
|
||||
versionString = "unknown";
|
||||
}
|
||||
wizard()->setCardSourceVersion(versionString);
|
||||
}
|
||||
}
|
||||
|
||||
versionReply->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
wizard()->setCardSourceUrl(url.toString());
|
||||
|
||||
auto *reply = wizard()->nam->get(QNetworkRequest(url));
|
||||
|
||||
connect(reply, &QNetworkReply::finished, this, &LoadSetsPage::actDownloadFinishedSetsFile);
|
||||
connect(reply, &QNetworkReply::downloadProgress, this, &LoadSetsPage::actDownloadProgressSetsFile);
|
||||
}
|
||||
|
||||
void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total)
|
||||
{
|
||||
if (total > 0) {
|
||||
progressBar->setMaximum(static_cast<int>(total));
|
||||
progressBar->setValue(static_cast<int>(received));
|
||||
}
|
||||
progressLabel->setText(tr("Downloading (%1MB)").arg((int)received / (1024 * 1024)));
|
||||
}
|
||||
|
||||
void LoadSetsPage::actDownloadFinishedSetsFile()
|
||||
{
|
||||
// check for a reply
|
||||
auto *reply = dynamic_cast<QNetworkReply *>(sender());
|
||||
auto errorCode = reply->error();
|
||||
if (errorCode != QNetworkReply::NoError) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString()));
|
||||
|
||||
wizard()->enableButtons();
|
||||
setEnabled(true);
|
||||
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
auto statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (statusCode == 301 || statusCode == 302) {
|
||||
const auto redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
||||
qDebug() << "following redirect url:" << redirectUrl.toString();
|
||||
downloadSetsFile(redirectUrl);
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
|
||||
// save AllPrintings.json url, but only if the user customized it and download was successful
|
||||
if (urlLineEdit->text() != QString(ALLSETS_URL)) {
|
||||
wizard()->settings->setValue("allsetsurl", urlLineEdit->text());
|
||||
} else {
|
||||
wizard()->settings->remove("allsetsurl");
|
||||
}
|
||||
|
||||
readSetsFromByteArray(reply->readAll());
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void LoadSetsPage::readSetsFromByteArray(QByteArray _data)
|
||||
{
|
||||
// show an infinite progressbar
|
||||
progressBar->setMaximum(0);
|
||||
progressBar->setMinimum(0);
|
||||
progressBar->setValue(0);
|
||||
progressLabel->setText(tr("Parsing file"));
|
||||
progressLabel->show();
|
||||
progressBar->show();
|
||||
|
||||
wizard()->downloadedPlainXml = false;
|
||||
wizard()->xmlData.clear();
|
||||
readSetsFromByteArrayRef(_data);
|
||||
}
|
||||
|
||||
void LoadSetsPage::readSetsFromByteArrayRef(QByteArray &_data)
|
||||
{
|
||||
// unzip the file if needed
|
||||
if (_data.startsWith(XZ_SIGNATURE)) {
|
||||
#ifdef HAS_LZMA
|
||||
// zipped file
|
||||
auto *inBuffer = new QBuffer(&_data);
|
||||
auto newData = QByteArray();
|
||||
auto *outBuffer = new QBuffer(&newData);
|
||||
inBuffer->open(QBuffer::ReadOnly);
|
||||
outBuffer->open(QBuffer::WriteOnly);
|
||||
XzDecompressor xz;
|
||||
if (!xz.decompress(inBuffer, outBuffer)) {
|
||||
zipDownloadFailed(tr("Xz extraction failed."));
|
||||
return;
|
||||
}
|
||||
_data.clear();
|
||||
readSetsFromByteArrayRef(newData);
|
||||
return;
|
||||
#else
|
||||
zipDownloadFailed(tr("Sorry, this version of Oracle does not support xz compressed files."));
|
||||
|
||||
wizard()->enableButtons();
|
||||
setEnabled(true);
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
return;
|
||||
#endif
|
||||
} else if (_data.startsWith(ZIP_SIGNATURE)) {
|
||||
#ifdef HAS_ZLIB
|
||||
// zipped file
|
||||
auto *inBuffer = new QBuffer(&_data);
|
||||
auto newData = QByteArray();
|
||||
auto *outBuffer = new QBuffer(&newData);
|
||||
QString fileName;
|
||||
UnZip::ErrorCode ec;
|
||||
UnZip uz;
|
||||
|
||||
ec = uz.openArchive(inBuffer);
|
||||
if (ec != UnZip::Ok) {
|
||||
zipDownloadFailed(tr("Failed to open Zip archive: %1.").arg(uz.formatError(ec)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (uz.fileList().size() != 1) {
|
||||
zipDownloadFailed(tr("Zip extraction failed: the Zip archive doesn't contain exactly one file."));
|
||||
return;
|
||||
}
|
||||
fileName = uz.fileList().at(0);
|
||||
|
||||
outBuffer->open(QBuffer::ReadWrite);
|
||||
ec = uz.extractFile(fileName, outBuffer);
|
||||
if (ec != UnZip::Ok) {
|
||||
zipDownloadFailed(tr("Zip extraction failed: %1.").arg(uz.formatError(ec)));
|
||||
uz.closeArchive();
|
||||
return;
|
||||
}
|
||||
_data.clear();
|
||||
readSetsFromByteArrayRef(newData);
|
||||
return;
|
||||
#else
|
||||
zipDownloadFailed(tr("Sorry, this version of Oracle does not support zipped files."));
|
||||
|
||||
wizard()->enableButtons();
|
||||
setEnabled(true);
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
return;
|
||||
#endif
|
||||
} else if (_data.startsWith("{")) {
|
||||
// Start the computation.
|
||||
jsonData = std::move(_data);
|
||||
future = QtConcurrent::run([this] { return wizard()->importer->readSetsFromByteArray(std::move(jsonData)); });
|
||||
watcher.setFuture(future);
|
||||
} else if (_data.startsWith("<")) {
|
||||
// save xml file and don't do any processing
|
||||
wizard()->downloadedPlainXml = true;
|
||||
wizard()->xmlData = std::move(_data);
|
||||
importFinished();
|
||||
} else {
|
||||
wizard()->enableButtons();
|
||||
setEnabled(true);
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to interpret downloaded data."));
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSetsPage::zipDownloadFailed(const QString &message)
|
||||
{
|
||||
wizard()->enableButtons();
|
||||
setEnabled(true);
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
|
||||
QMessageBox::StandardButton reply;
|
||||
reply = static_cast<QMessageBox::StandardButton>(QMessageBox::question(
|
||||
this, tr("Error"), message + "<br>" + tr("Do you want to download the uncompressed file instead?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes));
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
urlRadioButton->setChecked(true);
|
||||
urlLineEdit->setText(ALLSETS_URL_FALLBACK);
|
||||
|
||||
wizard()->next();
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSetsPage::importFinished()
|
||||
{
|
||||
wizard()->enableButtons();
|
||||
setEnabled(true);
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
|
||||
if (wizard()->downloadedPlainXml || watcher.future().result()) {
|
||||
wizard()->next();
|
||||
} else {
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("The file was retrieved successfully, but it does not contain any sets data."));
|
||||
}
|
||||
}
|
||||
|
||||
SaveSetsPage::SaveSetsPage(QWidget *parent) : OracleWizardPage(parent)
|
||||
{
|
||||
pathLabel = new QLabel(this);
|
||||
saveLabel = new QLabel(this);
|
||||
|
||||
defaultPathCheckBox = new QCheckBox(this);
|
||||
|
||||
messageLog = new QTextEdit(this);
|
||||
messageLog->setReadOnly(true);
|
||||
|
||||
auto *layout = new QGridLayout(this);
|
||||
layout->addWidget(messageLog, 0, 0);
|
||||
layout->addWidget(saveLabel, 1, 0);
|
||||
layout->addWidget(pathLabel, 2, 0);
|
||||
layout->addWidget(defaultPathCheckBox, 3, 0);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void SaveSetsPage::cleanupPage()
|
||||
{
|
||||
wizard()->importer->clear();
|
||||
disconnect(wizard()->importer, &OracleImporter::setIndexChanged, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void SaveSetsPage::initializePage()
|
||||
{
|
||||
messageLog->clear();
|
||||
|
||||
retranslateUi();
|
||||
if (wizard()->downloadedPlainXml) {
|
||||
messageLog->hide();
|
||||
} else {
|
||||
messageLog->show();
|
||||
connect(wizard()->importer, &OracleImporter::setIndexChanged, this, &SaveSetsPage::updateTotalProgress);
|
||||
|
||||
int setsImported = wizard()->importer->startImport();
|
||||
|
||||
if (setsImported == 0) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("No set has been imported."));
|
||||
}
|
||||
}
|
||||
|
||||
if (wizard()->backgroundMode) {
|
||||
emit readyToContinue();
|
||||
}
|
||||
}
|
||||
|
||||
void SaveSetsPage::retranslateUi()
|
||||
{
|
||||
setTitle(tr("Sets imported"));
|
||||
if (wizard()->downloadedPlainXml) {
|
||||
setSubTitle(tr("A cockatrice database file of %1 MB has been downloaded.")
|
||||
.arg(qRound(wizard()->xmlData.size() / 1000000.0)));
|
||||
} else {
|
||||
setSubTitle(tr("The following sets have been found:"));
|
||||
}
|
||||
|
||||
saveLabel->setText(tr("Press \"Save\" to store the imported cards in the Cockatrice database."));
|
||||
pathLabel->setText(tr("The card database will be saved at the following location:") + "<br>" +
|
||||
SettingsCache::instance().getCardDatabasePath());
|
||||
defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)"));
|
||||
|
||||
setButtonText(QWizard::NextButton, tr("&Save"));
|
||||
}
|
||||
|
||||
void SaveSetsPage::updateTotalProgress(int cardsImported, int /* setIndex */, const QString &setName)
|
||||
{
|
||||
if (setName.isEmpty()) {
|
||||
messageLog->append("<b>" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) +
|
||||
"</b>");
|
||||
} else {
|
||||
messageLog->append(tr("%1: %2 cards imported").arg(setName).arg(cardsImported));
|
||||
}
|
||||
|
||||
messageLog->verticalScrollBar()->setValue(messageLog->verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
bool SaveSetsPage::validatePage()
|
||||
{
|
||||
QString defaultPath = SettingsCache::instance().getCardDatabasePath();
|
||||
QString windowName = tr("Save card database");
|
||||
QString fileType = tr("XML; card database (*.xml)");
|
||||
|
||||
QString fileName;
|
||||
if (defaultPathCheckBox->isChecked()) {
|
||||
fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType);
|
||||
} else {
|
||||
fileName = defaultPath;
|
||||
}
|
||||
|
||||
if (fileName.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QFileInfo fi(fileName);
|
||||
QDir fileDir(fi.path());
|
||||
if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wizard()->downloadedPlainXml) {
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qDebug() << "File write (w) failed for" << fileName;
|
||||
return false;
|
||||
}
|
||||
if (file.write(wizard()->xmlData) < 1) {
|
||||
qDebug() << "File write (w) failed for" << fileName;
|
||||
return false;
|
||||
}
|
||||
wizard()->xmlData.clear();
|
||||
} else if (!wizard()->importer->saveToFile(fileName, wizard()->getCardSourceUrl(),
|
||||
wizard()->getCardSourceVersion())) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoadTokensPage::initializePage()
|
||||
{
|
||||
SimpleDownloadFilePage::initializePage();
|
||||
|
||||
if (wizard()->backgroundMode) {
|
||||
emit readyToContinue();
|
||||
}
|
||||
}
|
||||
|
||||
QString LoadTokensPage::getDefaultUrl()
|
||||
{
|
||||
return TOKENS_URL;
|
||||
}
|
||||
|
||||
QString LoadTokensPage::getCustomUrlSettingsKey()
|
||||
{
|
||||
return "tokensurl";
|
||||
}
|
||||
|
||||
QString LoadTokensPage::getDefaultSavePath()
|
||||
{
|
||||
return SettingsCache::instance().getTokenDatabasePath();
|
||||
}
|
||||
|
||||
QString LoadTokensPage::getWindowTitle()
|
||||
{
|
||||
return tr("Save token database");
|
||||
}
|
||||
|
||||
QString LoadTokensPage::getFileType()
|
||||
{
|
||||
return tr("XML; token database (*.xml)");
|
||||
}
|
||||
|
||||
void LoadTokensPage::retranslateUi()
|
||||
{
|
||||
setTitle(tr("Tokens import"));
|
||||
setSubTitle(tr("Please specify a compatible source for token data."));
|
||||
|
||||
urlLabel->setText(tr("Download URL:"));
|
||||
urlButton->setText(tr("Restore default URL"));
|
||||
pathLabel->setText(tr("The token database will be saved at the following location:") + "<br>" +
|
||||
SettingsCache::instance().getTokenDatabasePath());
|
||||
defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)"));
|
||||
}
|
||||
|
||||
QString LoadSpoilersPage::getDefaultUrl()
|
||||
{
|
||||
return SPOILERS_URL;
|
||||
}
|
||||
|
||||
QString LoadSpoilersPage::getCustomUrlSettingsKey()
|
||||
{
|
||||
return "spoilersurl";
|
||||
}
|
||||
|
||||
QString LoadSpoilersPage::getDefaultSavePath()
|
||||
{
|
||||
return SettingsCache::instance().getTokenDatabasePath();
|
||||
}
|
||||
|
||||
QString LoadSpoilersPage::getWindowTitle()
|
||||
{
|
||||
return tr("Save spoiler database");
|
||||
}
|
||||
|
||||
QString LoadSpoilersPage::getFileType()
|
||||
{
|
||||
return tr("XML; spoiler database (*.xml)");
|
||||
}
|
||||
|
||||
void LoadSpoilersPage::retranslateUi()
|
||||
{
|
||||
setTitle(tr("Spoilers import"));
|
||||
setSubTitle(tr("Please specify a compatible source for spoiler data."));
|
||||
|
||||
urlLabel->setText(tr("Download URL:"));
|
||||
urlButton->setText(tr("Restore default URL"));
|
||||
pathLabel->setText(tr("The spoiler database will be saved at the following location:") + "<br>" +
|
||||
SettingsCache::instance().getSpoilerCardDatabasePath());
|
||||
defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)"));
|
||||
}
|
||||
|
|
|
|||