Automatic Spoiler Season (#2991)

* oracle now can be run in spoiler or normal mode

* tests for travis

* only run on relaunch

* spoilers in client (not oracle now) and tray icon shows when done

* spoiler status will be checked before downloading spoiler file

* only download if they care about spoilers

* reload db on spoiler download

* manual update button, code cleanup, and fix enabling sets when new

* cleanup, nullchecks, and fixes to spoiler

* reload DB even if not in spoiler season; necessary as we have a check elsewhere to prevent the reload if spoiler check happens

* Implement changes from 2991#issuecomment-356169374

* Change implicit nullptrs, alert on file deletion, minor changes

* make reload thread safe and minor changes from 2991#issuecomment-356450302

* Fix locking

* Disable update now button while process running
This commit is contained in:
Zach H 2018-01-10 13:27:43 -05:00 committed by GitHub
parent 51ec593759
commit d19744236e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 2106 additions and 913 deletions

View file

@ -116,6 +116,7 @@ SET(cockatrice_SOURCES
src/logger.cpp
src/releasechannel.cpp
src/userconnection_information.cpp
src/spoilerbackgroundupdater.cpp
${VERSION_STRING_CPP}
)

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,7 @@
#include <QDataStream>
#include <QList>
#include <QXmlStreamReader>
#include <QBasicMutex>
class CardDatabase;
class CardInfo;
@ -18,159 +19,173 @@ typedef QMap<QString, QString> QStringMap;
// If we don't typedef this, CardInfo::CardInfo will refuse to compile on OS X < 10.9
typedef QMap<QString, int> MuidMap;
class CardSet : public QList<CardInfo *> {
private:
QString shortName, longName;
unsigned int sortKey;
QDate releaseDate;
QString setType;
bool enabled, isknown;
public:
CardSet(const QString &_shortName = QString(), const QString &_longName = QString(), const QString &_setType = QString(), const QDate &_releaseDate = QDate());
QString getCorrectedShortName() const;
QString getShortName() const { return shortName; }
QString getLongName() const { return longName; }
QString getSetType() const { return setType; }
QDate getReleaseDate() const { return releaseDate; }
void setLongName(QString & _longName) { longName = _longName; }
void setSetType(QString & _setType) { setType = _setType; }
void setReleaseDate(QDate & _releaseDate) { releaseDate = _releaseDate; }
class CardSet : public QList<CardInfo *>
{
private:
QString shortName, longName;
unsigned int sortKey;
QDate releaseDate;
QString setType;
bool enabled, isknown;
void loadSetOptions();
int getSortKey() const { return sortKey; }
void setSortKey(unsigned int _sortKey);
bool getEnabled() const { return enabled; }
void setEnabled(bool _enabled);
bool getIsKnown() const { return isknown; }
void setIsKnown(bool _isknown);
//Determine incomplete sets.
bool getIsKnownIgnored() const { return longName.length() + setType.length() + releaseDate.toString().length() == 0 ; }
public:
explicit CardSet(const QString &_shortName = QString(), const QString &_longName = QString(), const QString &_setType = QString(), const QDate &_releaseDate = QDate());
QString getCorrectedShortName() const;
QString getShortName() const { return shortName; }
QString getLongName() const { return longName; }
QString getSetType() const { return setType; }
QDate getReleaseDate() const { return releaseDate; }
void setLongName(QString & _longName) { longName = _longName; }
void setSetType(QString & _setType) { setType = _setType; }
void setReleaseDate(QDate & _releaseDate) { releaseDate = _releaseDate; }
void loadSetOptions();
int getSortKey() const { return sortKey; }
void setSortKey(unsigned int _sortKey);
bool getEnabled() const { return enabled; }
void setEnabled(bool _enabled);
bool getIsKnown() const { return isknown; }
void setIsKnown(bool _isknown);
//Determine incomplete sets.
bool getIsKnownIgnored() const { return longName.length() + setType.length() + releaseDate.toString().length() == 0 ; }
};
class SetList : public QList<CardSet *> {
private:
class KeyCompareFunctor;
public:
void sortByKey();
void guessSortKeys();
void enableAllUnknown();
void enableAll();
void markAllAsKnown();
int getEnabledSetsNum();
int getUnknownSetsNum();
QStringList getUnknownSetsNames();
class SetList : public QList<CardSet *>
{
private:
class KeyCompareFunctor;
public:
void sortByKey();
void guessSortKeys();
void enableAllUnknown();
void enableAll();
void markAllAsKnown();
int getEnabledSetsNum();
int getUnknownSetsNum();
QStringList getUnknownSetsNames();
};
class CardInfo : public QObject {
class CardInfo : public QObject
{
Q_OBJECT
private:
QString name;
private:
QString name;
/*
* The name without punctuation or capitalization, for better card tag name
* recognition.
*/
QString simpleName;
/*
* The name without punctuation or capitalization, for better card tag name
* recognition.
*/
QString simpleName;
bool isToken;
SetList sets;
QString manacost;
QString cmc;
QString cardtype;
QString powtough;
QString text;
QStringList colors;
// the cards i'm related to
QList<CardRelation *> relatedCards;
// the card i'm reverse-related to
QList<CardRelation *> reverseRelatedCards;
// the cards thare are reverse-related to me
QList<CardRelation *> reverseRelatedCardsToMe;
QString setsNames;
bool upsideDownArt;
int loyalty;
QStringMap customPicURLs;
MuidMap muIds;
QStringMap collectorNumbers;
QStringMap rarities;
bool cipt;
int tableRow;
QString pixmapCacheKey;
public:
CardInfo(const QString &_name = QString(),
bool _isToken = false,
const QString &_manacost = QString(),
const QString &_cmc = QString(),
const QString &_cardtype = QString(),
const QString &_powtough = QString(),
const QString &_text = QString(),
const QStringList &_colors = QStringList(),
const QList<CardRelation *> &_relatedCards = QList<CardRelation *>(),
const QList<CardRelation *> &_reverseRelatedCards = QList<CardRelation *>(),
bool _upsideDownArt = false,
int _loyalty = 0,
bool _cipt = false,
int _tableRow = 0,
const SetList &_sets = SetList(),
const QStringMap &_customPicURLs = QStringMap(),
MuidMap muids = MuidMap(),
QStringMap _collectorNumbers = QStringMap(),
QStringMap _rarities = QStringMap()
);
~CardInfo();
inline const QString &getName() const { return name; }
inline const QString &getSetsNames() const { return setsNames; }
const QString &getSimpleName() const { return simpleName; }
bool getIsToken() const { return isToken; }
const SetList &getSets() const { return sets; }
inline const QString &getManaCost() const { return manacost; }
inline const QString &getCmc() const { return cmc; }
inline const QString &getCardType() const { return cardtype; }
inline const QString &getPowTough() const { return powtough; }
const QString &getText() const { return text; }
const QString &getPixmapCacheKey() const { return pixmapCacheKey; }
const int &getLoyalty() const { return loyalty; }
bool getCipt() const { return cipt; }
void setManaCost(const QString &_manaCost) { manacost = _manaCost; emit cardInfoChanged(this); }
void setCmc(const QString &_cmc) { cmc = _cmc; emit cardInfoChanged(this); }
void setCardType(const QString &_cardType) { cardtype = _cardType; emit cardInfoChanged(this); }
void setPowTough(const QString &_powTough) { powtough = _powTough; emit cardInfoChanged(this); }
void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); }
void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); }
const QChar getColorChar() const;
const QStringList &getColors() const { return colors; }
const QList<CardRelation *> &getRelatedCards() const { return relatedCards; }
const QList<CardRelation *> &getReverseRelatedCards() const { return reverseRelatedCards; }
const QList<CardRelation *> &getReverseRelatedCards2Me() const { return reverseRelatedCardsToMe; }
void resetReverseRelatedCards2Me();
void addReverseRelatedCards2Me(CardRelation * cardRelation) { reverseRelatedCardsToMe.append(cardRelation); }
bool getUpsideDownArt() const { return upsideDownArt; }
QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); }
int getMuId(const QString &set) const { return muIds.value(set); }
QString getCollectorNumber(const QString &set) const { return collectorNumbers.value(set); }
QString getRarity(const QString &set) const { return rarities.value(set); }
QStringMap getRarities() const { return rarities; }
QString getMainCardType() const;
QString getCorrectedName() const;
int getTableRow() const { return tableRow; }
void setTableRow(int _tableRow) { tableRow = _tableRow; }
void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); }
void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set, _customPicURL); }
void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); }
void setSetNumber(const QString &_set, const QString &_setNumber) { collectorNumbers.insert(_set, _setNumber); }
void setRarity(const QString &_set, const QString &_setNumber) { rarities.insert(_set, _setNumber); }
void addToSet(CardSet *set);
void emitPixmapUpdated() { emit pixmapUpdated(); }
void refreshCachedSetNames();
bool isToken;
SetList sets;
QString manacost;
QString cmc;
QString cardtype;
QString powtough;
QString text;
QStringList colors;
/**
* Simplify a name to have no punctuation and lowercase all letters, for
* less strict name-matching.
*/
static QString simplifyName(const QString &name);
signals:
void pixmapUpdated();
void cardInfoChanged(CardInfo *card);
// the cards i'm related to
QList<CardRelation *> relatedCards;
// the card i'm reverse-related to
QList<CardRelation *> reverseRelatedCards;
// the cards thare are reverse-related to me
QList<CardRelation *> reverseRelatedCardsToMe;
QString setsNames;
bool upsideDownArt;
int loyalty;
QStringMap customPicURLs;
MuidMap muIds;
QStringMap collectorNumbers;
QStringMap rarities;
bool cipt;
int tableRow;
QString pixmapCacheKey;
public:
explicit CardInfo(const QString &_name = QString(),
bool _isToken = false,
const QString &_manacost = QString(),
const QString &_cmc = QString(),
const QString &_cardtype = QString(),
const QString &_powtough = QString(),
const QString &_text = QString(),
const QStringList &_colors = QStringList(),
const QList<CardRelation *> &_relatedCards = QList<CardRelation *>(),
const QList<CardRelation *> &_reverseRelatedCards = QList<CardRelation *>(),
bool _upsideDownArt = false,
int _loyalty = 0,
bool _cipt = false,
int _tableRow = 0,
const SetList &_sets = SetList(),
const QStringMap &_customPicURLs = QStringMap(),
MuidMap muids = MuidMap(),
QStringMap _collectorNumbers = QStringMap(),
QStringMap _rarities = QStringMap()
);
~CardInfo() override;
inline const QString &getName() const { return name; }
inline const QString &getSetsNames() const { return setsNames; }
const QString &getSimpleName() const { return simpleName; }
bool getIsToken() const { return isToken; }
const SetList &getSets() const { return sets; }
inline const QString &getManaCost() const { return manacost; }
inline const QString &getCmc() const { return cmc; }
inline const QString &getCardType() const { return cardtype; }
inline const QString &getPowTough() const { return powtough; }
const QString &getText() const { return text; }
const QString &getPixmapCacheKey() const { return pixmapCacheKey; }
const int &getLoyalty() const { return loyalty; }
bool getCipt() const { return cipt; }
//void setManaCost(const QString &_manaCost) { manacost = _manaCost; emit cardInfoChanged(this); }
//void setCmc(const QString &_cmc) { cmc = _cmc; emit cardInfoChanged(this); }
void setCardType(const QString &_cardType) { cardtype = _cardType; emit cardInfoChanged(this); }
void setPowTough(const QString &_powTough) { powtough = _powTough; emit cardInfoChanged(this); }
void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); }
void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); }
const QChar getColorChar() const;
const QStringList &getColors() const { return colors; }
const QList<CardRelation *> &getRelatedCards() const { return relatedCards; }
const QList<CardRelation *> &getReverseRelatedCards() const { return reverseRelatedCards; }
const QList<CardRelation *> &getReverseRelatedCards2Me() const { return reverseRelatedCardsToMe; }
void resetReverseRelatedCards2Me();
void addReverseRelatedCards2Me(CardRelation * cardRelation) { reverseRelatedCardsToMe.append(cardRelation); }
bool getUpsideDownArt() const { return upsideDownArt; }
QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); }
int getMuId(const QString &set) const { return muIds.value(set); }
QString getCollectorNumber(const QString &set) const { return collectorNumbers.value(set); }
QString getRarity(const QString &set) const { return rarities.value(set); }
QStringMap getRarities() const { return rarities; }
QString getMainCardType() const;
QString getCorrectedName() const;
int getTableRow() const { return tableRow; }
void setTableRow(int _tableRow) { tableRow = _tableRow; }
//void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); }
//void setCustomPicURL(const QString &_set, const QString &_customPicURL) { customPicURLs.insert(_set, _customPicURL); }
void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); }
void setSetNumber(const QString &_set, const QString &_setNumber) { collectorNumbers.insert(_set, _setNumber); }
void setRarity(const QString &_set, const QString &_setNumber) { rarities.insert(_set, _setNumber); }
void addToSet(CardSet *set);
void emitPixmapUpdated() { emit pixmapUpdated(); }
void refreshCachedSetNames();
/**
* Simplify a name to have no punctuation and lowercase all letters, for
* less strict name-matching.
*/
static QString simplifyName(const QString &name);
signals:
void pixmapUpdated();
void cardInfoChanged(CardInfo *card);
};
enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards };
@ -178,97 +193,106 @@ enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards };
typedef QHash<QString, CardInfo *> CardNameMap;
typedef QHash<QString, CardSet *> SetNameMap;
class CardDatabase : public QObject {
class CardDatabase : public QObject
{
Q_OBJECT
protected:
/*
* The cards, indexed by name.
*/
CardNameMap cards;
protected:
/*
* The cards, indexed by name.
*/
CardNameMap cards;
/**
* The cards, indexed by their simple name.
*/
CardNameMap simpleNameCards;
/**
* The cards, indexed by their simple name.
*/
CardNameMap simpleNameCards;
/*
* The sets, indexed by short name.
*/
SetNameMap sets;
/*
* The sets, indexed by short name.
*/
SetNameMap sets;
LoadStatus loadStatus;
private:
static const int versionNeeded;
void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml);
LoadStatus loadStatus;
private:
static const int versionNeeded;
void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml);
CardInfo *getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const;
void checkUnknownSets();
void refreshCachedReverseRelatedCards();
public:
static const char* TOKENS_SETNAME;
CardInfo *getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const;
void checkUnknownSets();
void refreshCachedReverseRelatedCards();
CardDatabase(QObject *parent = 0);
~CardDatabase();
void clear();
void addCard(CardInfo *card);
void removeCard(CardInfo *card);
CardInfo *getCard(const QString &cardName) const;
QList <CardInfo *> getCards(const QStringList &cardNames) const;
QBasicMutex *reloadDatabaseMutex = new QBasicMutex(),
*clearDatabaseMutex = new QBasicMutex(),
*loadFromFileMutex = new QBasicMutex(),
*addCardMutex = new QBasicMutex(),
*removeCardMutex = new QBasicMutex();
/*
* Get a card by its simple name. The name will be simplified in this
* function, so you don't need to simplify it beforehand.
*/
CardInfo *getCardBySimpleName(const QString &cardName) const;
public:
static const char* TOKENS_SETNAME;
CardSet *getSet(const QString &setName);
QList<CardInfo *> getCardList() const { return cards.values(); }
SetList getSetList() const;
LoadStatus loadFromFile(const QString &fileName);
bool saveToFile(const QString &fileName, bool tokens = false);
bool saveCustomTokensToFile();
QStringList getAllColors() const;
QStringList getAllMainCardTypes() const;
LoadStatus getLoadStatus() const { return loadStatus; }
void enableAllUnknownSets();
void markAllSetsAsKnown();
void notifyEnabledSetsChanged();
explicit CardDatabase(QObject *parent = nullptr);
~CardDatabase() override;
void clear();
void addCard(CardInfo *card);
void removeCard(CardInfo *card);
CardInfo *getCard(const QString &cardName) const;
QList <CardInfo *> getCards(const QStringList &cardNames) const;
public slots:
LoadStatus loadCardDatabases();
private slots:
LoadStatus loadCardDatabase(const QString &path);
signals:
void cardDatabaseLoadingFailed();
void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames);
void cardDatabaseAllNewSetsEnabled();
void cardDatabaseEnabledSetsChanged();
void cardAdded(CardInfo *card);
void cardRemoved(CardInfo *card);
/*
* Get a card by its simple name. The name will be simplified in this
* function, so you don't need to simplify it beforehand.
*/
CardInfo *getCardBySimpleName(const QString &cardName) const;
CardSet *getSet(const QString &setName);
QList<CardInfo *> getCardList() const { return cards.values(); }
SetList getSetList() const;
LoadStatus loadFromFile(const QString &fileName);
bool saveToFile(const QString &fileName, bool tokens = false);
bool saveCustomTokensToFile();
QStringList getAllColors() const;
QStringList getAllMainCardTypes() const;
LoadStatus getLoadStatus() const { return loadStatus; }
void enableAllUnknownSets();
void markAllSetsAsKnown();
void notifyEnabledSetsChanged();
public slots:
LoadStatus loadCardDatabases();
private slots:
LoadStatus loadCardDatabase(const QString &path);
signals:
void cardDatabaseLoadingFailed();
void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames);
void cardDatabaseAllNewSetsEnabled();
void cardDatabaseEnabledSetsChanged();
void cardAdded(CardInfo *card);
void cardRemoved(CardInfo *card);
};
class CardRelation : public QObject {
class CardRelation : public QObject
{
Q_OBJECT
private:
QString name;
bool doesAttach;
bool isCreateAllExclusion;
bool isVariableCount;
int defaultCount;
public:
CardRelation(const QString &_name = QString(),
bool _doesAttach = false,
bool _isCreateAllExclusion = false,
bool _isVariableCount = false,
int _defaultCount = 1
);
inline const QString &getName() const { return name; }
bool getDoesAttach() const { return doesAttach; }
bool getCanCreateAnother() const { return !doesAttach; }
bool getIsCreateAllExclusion() const { return isCreateAllExclusion; }
bool getIsVariable() const { return isVariableCount; }
int getDefaultCount() const { return defaultCount; }
};
private:
QString name;
bool doesAttach;
bool isCreateAllExclusion;
bool isVariableCount;
int defaultCount;
public:
explicit CardRelation(const QString &_name = QString(),
bool _doesAttach = false,
bool _isCreateAllExclusion = false,
bool _isVariableCount = false,
int _defaultCount = 1
);
#endif
inline const QString &getName() const { return name; }
bool getDoesAttach() const { return doesAttach; }
bool getCanCreateAnother() const { return !doesAttach; }
bool getIsCreateAllExclusion() const { return isCreateAllExclusion; }
bool getIsVariable() const { return isVariableCount; }
int getDefaultCount() const { return defaultCount; }
};
#endif

View file

@ -16,8 +16,7 @@
#define PUBLIC_SERVERS_URL "https://github.com/Cockatrice/Cockatrice/wiki/Public-Servers"
DlgConnect::DlgConnect(QWidget *parent)
: QDialog(parent)
DlgConnect::DlgConnect(QWidget *parent) : QDialog(parent)
{
previousHostButton = new QRadioButton(tr("Known Hosts"), this);
previousHosts = new QComboBox(this);
@ -64,7 +63,7 @@ DlgConnect::DlgConnect(QWidget *parent)
if (savePasswordCheckBox->isChecked())
{
autoConnectCheckBox->setChecked(settingsCache->servers().getAutoConnect());
autoConnectCheckBox->setChecked(static_cast<bool>(settingsCache->servers().getAutoConnect()));
autoConnectCheckBox->setEnabled(true);
}
else
@ -87,11 +86,11 @@ DlgConnect::DlgConnect(QWidget *parent)
btnCancel->setFixedWidth(100);
connect(btnCancel, SIGNAL(released()), this, SLOT(actCancel()));
QGridLayout *newHostLayout = new QGridLayout;
auto *newHostLayout = new QGridLayout;
newHostLayout->addWidget(newHostButton, 0, 1);
newHostLayout->addWidget(publicServersLabel, 0, 2);
QGridLayout *connectionLayout = new QGridLayout;
auto *connectionLayout = new QGridLayout;
connectionLayout->addWidget(previousHostButton, 0, 1);
connectionLayout->addWidget(previousHosts, 1, 1);
connectionLayout->addLayout(newHostLayout, 2, 1, 1, 2);
@ -103,7 +102,7 @@ DlgConnect::DlgConnect(QWidget *parent)
connectionLayout->addWidget(portEdit, 5, 1);
connectionLayout->addWidget(autoConnectCheckBox, 6, 1);
QGridLayout *buttons = new QGridLayout;
auto *buttons = new QGridLayout;
buttons->addWidget(btnOk, 0, 0);
buttons->addWidget(btnForgotPassword, 0, 1);
buttons->addWidget(btnCancel, 0, 2);
@ -111,7 +110,7 @@ DlgConnect::DlgConnect(QWidget *parent)
QGroupBox *restrictionsGroupBox = new QGroupBox(tr("Server"));
restrictionsGroupBox->setLayout(connectionLayout);
QGridLayout *loginLayout = new QGridLayout;
auto *loginLayout = new QGridLayout;
loginLayout->addWidget(playernameLabel, 0, 0);
loginLayout->addWidget(playernameEdit, 0, 1);
loginLayout->addWidget(passwordLabel, 1, 0);
@ -124,12 +123,12 @@ DlgConnect::DlgConnect(QWidget *parent)
QGroupBox *btnGroupBox = new QGroupBox(tr(""));
btnGroupBox->setLayout(buttons);
QGridLayout *grid = new QGridLayout;
auto *grid = new QGridLayout;
grid->addWidget(restrictionsGroupBox, 0, 0);
grid->addWidget(loginGroupBox, 1, 0);
grid->addWidget(btnGroupBox, 2, 0);
QVBoxLayout *mainLayout = new QVBoxLayout;
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(grid);
setLayout(mainLayout);
@ -289,9 +288,9 @@ void DlgConnect::actCancel()
bool DeleteHighlightedItemWhenShiftDelPressedEventFilter::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
auto *keyEvent = dynamic_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Delete) {
QComboBox* combobox = reinterpret_cast<QComboBox *>(obj);
auto *combobox = reinterpret_cast<QComboBox *>(obj);
combobox->removeItem(combobox->currentIndex());
return true;
}

View file

@ -30,6 +30,7 @@
#include "releasechannel.h"
#include "soundengine.h"
#include "sequenceEdit/shortcutstab.h"
#include "spoilerbackgroundupdater.h"
#define WIKI_CUSTOM_PIC_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Picture-Download-URLs"
@ -63,7 +64,7 @@ GeneralSettingsPage::GeneralSettingsPage()
pixmapCacheEdit.setSingleStep(64);
pixmapCacheEdit.setValue(settingsCache->getPixmapCacheSize());
pixmapCacheEdit.setSuffix(" MB");
defaultUrlEdit = new QLineEdit(settingsCache->getPicUrl());
fallbackUrlEdit = new QLineEdit(settingsCache->getPicUrlFallback());
@ -81,7 +82,7 @@ GeneralSettingsPage::GeneralSettingsPage()
setEnabledStatus(settingsCache->getPicDownload());
QGridLayout *personalGrid = new QGridLayout;
auto *personalGrid = new QGridLayout;
personalGrid->addWidget(&languageLabel, 0, 0);
personalGrid->addWidget(&languageBox, 0, 1);
personalGrid->addWidget(&updateReleaseChannelLabel, 1, 0);
@ -98,7 +99,7 @@ GeneralSettingsPage::GeneralSettingsPage()
personalGrid->addWidget(&fallbackUrlRestoreButton, 6, 2, 1, 1);
personalGrid->addWidget(&urlLinkLabel, 7, 1, 1, 1);
personalGrid->addWidget(&clearDownloadedPicsButton, 8, 0, 1, 3);
urlLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse);
urlLinkLabel.setOpenExternalLinks(true);
@ -114,23 +115,23 @@ GeneralSettingsPage::GeneralSettingsPage()
replaysPathEdit->setReadOnly(true);
QPushButton *replaysPathButton = new QPushButton("...");
connect(replaysPathButton, SIGNAL(clicked()), this, SLOT(replaysPathButtonClicked()));
picsPathEdit = new QLineEdit(settingsCache->getPicsPath());
picsPathEdit->setReadOnly(true);
QPushButton *picsPathButton = new QPushButton("...");
connect(picsPathButton, SIGNAL(clicked()), this, SLOT(picsPathButtonClicked()));
cardDatabasePathEdit = new QLineEdit(settingsCache->getCardDatabasePath());
cardDatabasePathEdit->setReadOnly(true);
QPushButton *cardDatabasePathButton = new QPushButton("...");
connect(cardDatabasePathButton, SIGNAL(clicked()), this, SLOT(cardDatabasePathButtonClicked()));
tokenDatabasePathEdit = new QLineEdit(settingsCache->getTokenDatabasePath());
tokenDatabasePathEdit->setReadOnly(true);
QPushButton *tokenDatabasePathButton = new QPushButton("...");
connect(tokenDatabasePathButton, SIGNAL(clicked()), this, SLOT(tokenDatabasePathButtonClicked()));
if(settingsCache->getIsPortableBuild())
if (settingsCache->getIsPortableBuild())
{
deckPathEdit->setEnabled(false);
replaysPathEdit->setEnabled(false);
@ -145,7 +146,7 @@ GeneralSettingsPage::GeneralSettingsPage()
tokenDatabasePathButton->setVisible(false);
}
QGridLayout *pathsGrid = new QGridLayout;
auto *pathsGrid = new QGridLayout;
pathsGrid->addWidget(&deckPathLabel, 0, 0);
pathsGrid->addWidget(deckPathEdit, 0, 1);
pathsGrid->addWidget(deckPathButton, 0, 2);
@ -164,10 +165,10 @@ GeneralSettingsPage::GeneralSettingsPage()
pathsGroupBox = new QGroupBox;
pathsGroupBox->setLayout(pathsGrid);
QVBoxLayout *mainLayout = new QVBoxLayout;
auto *mainLayout = new QVBoxLayout;
mainLayout->addWidget(personalGroupBox);
mainLayout->addWidget(pathsGroupBox);
setLayout(mainLayout);
}
@ -186,7 +187,7 @@ QString GeneralSettingsPage::languageName(const QString &qmFile)
QTranslator translator;
translator.load(translationPrefix + "_" + qmFile + ".qm", translationPath);
return translator.translate("i18n", DEFAULT_LANG_NAME);
}
@ -209,7 +210,7 @@ void GeneralSettingsPage::deckPathButtonClicked()
QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"));
if (path.isEmpty())
return;
deckPathEdit->setText(path);
settingsCache->setDeckPath(path);
}
@ -219,7 +220,7 @@ void GeneralSettingsPage::replaysPathButtonClicked()
QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"));
if (path.isEmpty())
return;
replaysPathEdit->setText(path);
settingsCache->setReplaysPath(path);
}
@ -229,7 +230,7 @@ void GeneralSettingsPage::picsPathButtonClicked()
QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"));
if (path.isEmpty())
return;
picsPathEdit->setText(path);
settingsCache->setPicsPath(path);
}
@ -263,7 +264,7 @@ void GeneralSettingsPage::cardDatabasePathButtonClicked()
QString path = QFileDialog::getOpenFileName(this, tr("Choose path"));
if (path.isEmpty())
return;
cardDatabasePathEdit->setText(path);
settingsCache->setCardDatabasePath(path);
}
@ -273,7 +274,7 @@ void GeneralSettingsPage::tokenDatabasePathButtonClicked()
QString path = QFileDialog::getOpenFileName(this, tr("Choose path"));
if (path.isEmpty())
return;
tokenDatabasePathEdit->setText(path);
settingsCache->setTokenDatabasePath(path);
}
@ -332,8 +333,8 @@ AppearanceSettingsPage::AppearanceSettingsPage()
}
connect(&themeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(themeBoxChanged(int)));
QGridLayout *themeGrid = new QGridLayout;
auto *themeGrid = new QGridLayout;
themeGrid->addWidget(&themeLabel, 0, 0);
themeGrid->addWidget(&themeBox, 0, 1);
@ -345,30 +346,30 @@ AppearanceSettingsPage::AppearanceSettingsPage()
cardScalingCheckBox.setChecked(settingsCache->getScaleCards());
connect(&cardScalingCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setCardScaling(int)));
QGridLayout *cardsGrid = new QGridLayout;
auto *cardsGrid = new QGridLayout;
cardsGrid->addWidget(&displayCardNamesCheckBox, 0, 0, 1, 2);
cardsGrid->addWidget(&cardScalingCheckBox, 1, 0, 1, 2);
cardsGroupBox = new QGroupBox;
cardsGroupBox->setLayout(cardsGrid);
horizontalHandCheckBox.setChecked(settingsCache->getHorizontalHand());
connect(&horizontalHandCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setHorizontalHand(int)));
leftJustifiedHandCheckBox.setChecked(settingsCache->getLeftJustified());
connect(&leftJustifiedHandCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setLeftJustified(int)));
QGridLayout *handGrid = new QGridLayout;
auto *handGrid = new QGridLayout;
handGrid->addWidget(&horizontalHandCheckBox, 0, 0, 1, 2);
handGrid->addWidget(&leftJustifiedHandCheckBox, 1, 0, 1, 2);
handGroupBox = new QGroupBox;
handGroupBox->setLayout(handGrid);
invertVerticalCoordinateCheckBox.setChecked(settingsCache->getInvertVerticalCoordinate());
connect(&invertVerticalCoordinateCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setInvertVerticalCoordinate(int)));
minPlayersForMultiColumnLayoutEdit.setMinimum(2);
minPlayersForMultiColumnLayoutEdit.setValue(settingsCache->getMinPlayersForMultiColumnLayout());
connect(&minPlayersForMultiColumnLayoutEdit, SIGNAL(valueChanged(int)), settingsCache, SLOT(setMinPlayersForMultiColumnLayout(int)));
@ -380,22 +381,22 @@ AppearanceSettingsPage::AppearanceSettingsPage()
maxFontSizeForCardsEdit.setMinimum(9);
maxFontSizeForCardsEdit.setMaximum(100);
QGridLayout *tableGrid = new QGridLayout;
auto *tableGrid = new QGridLayout;
tableGrid->addWidget(&invertVerticalCoordinateCheckBox, 0, 0, 1, 2);
tableGrid->addWidget(&minPlayersForMultiColumnLayoutLabel, 1, 0, 1, 1);
tableGrid->addWidget(&minPlayersForMultiColumnLayoutEdit, 1, 1, 1, 1);
tableGrid->addWidget(&maxFontSizeForCardsLabel, 2, 0, 1, 1);
tableGrid->addWidget(&maxFontSizeForCardsEdit, 2, 1, 1, 1);
tableGroupBox = new QGroupBox;
tableGroupBox->setLayout(tableGrid);
QVBoxLayout *mainLayout = new QVBoxLayout;
auto *mainLayout = new QVBoxLayout;
mainLayout->addWidget(themeGroupBox);
mainLayout->addWidget(cardsGroupBox);
mainLayout->addWidget(handGroupBox);
mainLayout->addWidget(tableGroupBox);
setLayout(mainLayout);
}
@ -410,15 +411,15 @@ void AppearanceSettingsPage::retranslateUi()
{
themeGroupBox->setTitle(tr("Theme settings"));
themeLabel.setText(tr("Current theme:"));
cardsGroupBox->setTitle(tr("Card rendering"));
displayCardNamesCheckBox.setText(tr("Display card names on cards having a picture"));
cardScalingCheckBox.setText(tr("Scale cards on mouse over"));
handGroupBox->setTitle(tr("Hand layout"));
horizontalHandCheckBox.setText(tr("Display hand horizontally (wastes space)"));
leftJustifiedHandCheckBox.setText(tr("Enable left justification"));
tableGroupBox->setTitle(tr("Table grid layout"));
invertVerticalCoordinateCheckBox.setText(tr("Invert vertical coordinate"));
minPlayersForMultiColumnLayoutLabel.setText(tr("Minimum player count for multi-column layout:"));
@ -437,36 +438,36 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
doubleClickToPlayCheckBox.setChecked(settingsCache->getDoubleClickToPlay());
connect(&doubleClickToPlayCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setDoubleClickToPlay(int)));
playToStackCheckBox.setChecked(settingsCache->getPlayToStack());
connect(&playToStackCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPlayToStack(int)));
annotateTokensCheckBox.setChecked(settingsCache->getAnnotateTokens());
connect(&annotateTokensCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setAnnotateTokens(int)));
QGridLayout *generalGrid = new QGridLayout;
auto *generalGrid = new QGridLayout;
generalGrid->addWidget(&notificationsEnabledCheckBox, 0, 0);
generalGrid->addWidget(&specNotificationsEnabledCheckBox, 1, 0);
generalGrid->addWidget(&doubleClickToPlayCheckBox, 2, 0);
generalGrid->addWidget(&playToStackCheckBox, 3, 0);
generalGrid->addWidget(&annotateTokensCheckBox, 4, 0);
generalGroupBox = new QGroupBox;
generalGroupBox->setLayout(generalGrid);
tapAnimationCheckBox.setChecked(settingsCache->getTapAnimation());
connect(&tapAnimationCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setTapAnimation(int)));
QGridLayout *animationGrid = new QGridLayout;
auto *animationGrid = new QGridLayout;
animationGrid->addWidget(&tapAnimationCheckBox, 0, 0);
animationGroupBox = new QGroupBox;
animationGroupBox->setLayout(animationGrid);
QVBoxLayout *mainLayout = new QVBoxLayout;
auto *mainLayout = new QVBoxLayout;
mainLayout->addWidget(generalGroupBox);
mainLayout->addWidget(animationGroupBox);
setLayout(mainLayout);
}
@ -486,25 +487,123 @@ void UserInterfaceSettingsPage::retranslateUi()
tapAnimationCheckBox.setText(tr("&Tap/untap animation"));
}
DeckEditorSettingsPage::DeckEditorSettingsPage()
{
QGridLayout *generalGrid = new QGridLayout;
generalGrid->addWidget(new QLabel(tr("Nothing is here... yet")), 0, 0);
generalGroupBox = new QGroupBox;
generalGroupBox->setLayout(generalGrid);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(generalGroupBox);
setLayout(mainLayout);
auto *lpGeneralGrid = new QGridLayout;
auto *lpSpoilerGrid = new QGridLayout;
mcDownloadSpoilersCheckBox.setChecked(settingsCache->getDownloadSpoilersStatus());
mpSpoilerSavePathLineEdit = new QLineEdit(settingsCache->getSpoilerCardDatabasePath());
mpSpoilerSavePathLineEdit->setReadOnly(true);
mpSpoilerPathButton = new QPushButton("...");
connect(mpSpoilerPathButton, SIGNAL(clicked()), this, SLOT(spoilerPathButtonClicked()));
updateNowButton = new QPushButton(tr("Update Spoilers"));
connect(updateNowButton, SIGNAL(clicked()), this, SLOT(updateSpoilers()));
// Update the GUI depending on if the box is ticked or not
setSpoilersEnabled(mcDownloadSpoilersCheckBox.isChecked());
// Create the layout
lpGeneralGrid->addWidget(&mcGeneralMessageLabel, 0, 0);
lpSpoilerGrid->addWidget(&mcDownloadSpoilersCheckBox, 0, 0);
lpSpoilerGrid->addWidget(updateNowButton, 0, 2);
lpSpoilerGrid->addWidget(&mcSpoilerSaveLabel, 1, 0);
lpSpoilerGrid->addWidget(mpSpoilerSavePathLineEdit, 1, 1);
lpSpoilerGrid->addWidget(mpSpoilerPathButton, 1, 2);
lpSpoilerGrid->addWidget(&infoOnSpoilersLabel, 2, 0, 1, 3, Qt::AlignTop);
// On a change to the check box, hide/unhide the other fields
connect(&mcDownloadSpoilersCheckBox, SIGNAL(toggled(bool)), settingsCache, SLOT(setDownloadSpoilerStatus(bool)));
connect(&mcDownloadSpoilersCheckBox, SIGNAL(toggled(bool)), this, SLOT(setSpoilersEnabled(bool)));
mpGeneralGroupBox = new QGroupBox;
mpGeneralGroupBox->setLayout(lpGeneralGrid);
mpSpoilerGroupBox = new QGroupBox;
mpSpoilerGroupBox->setLayout(lpSpoilerGrid);
auto *lpMainLayout = new QVBoxLayout;
lpMainLayout->addWidget(mpGeneralGroupBox);
lpMainLayout->addWidget(mpSpoilerGroupBox);
setLayout(lpMainLayout);
}
void DeckEditorSettingsPage::updateSpoilers()
{
// Disable the button so the user can only press it once at a time
updateNowButton->setDisabled(true);
updateNowButton->setText(tr("Updating Spoilers"));
// Create a new SBU that will act as if the client was just reloaded
auto *sbu = new SpoilerBackgroundUpdater();
connect(sbu, SIGNAL(spoilerCheckerDone()), this, SLOT(unlockSettings()));
connect(sbu, SIGNAL(spoilersUpdatedSuccessfully()), this, SLOT(unlockSettings()));
}
void DeckEditorSettingsPage::unlockSettings()
{
updateNowButton->setDisabled(false);
updateNowButton->setText(tr("Update Spoilers"));
}
QString DeckEditorSettingsPage::getLastUpdateTime()
{
QString fileName = settingsCache->getSpoilerCardDatabasePath();
QFileInfo fi(fileName);
QDir fileDir(fi.path());
QFile file(fileName);
if (file.exists())
{
return fi.lastModified().toString("MMM d, hh:mm");
}
return QString();
}
void DeckEditorSettingsPage::spoilerPathButtonClicked()
{
QString lsPath = QFileDialog::getExistingDirectory(this, tr("Choose path"));
if (lsPath.isEmpty())
{
return;
}
mpSpoilerSavePathLineEdit->setText(lsPath + "/spoiler.xml");
settingsCache->setSpoilerDatabasePath(lsPath + "/spoiler.xml");
}
void DeckEditorSettingsPage::setSpoilersEnabled(bool anInput)
{
msDownloadSpoilersLabel.setEnabled(anInput);
mcSpoilerSaveLabel.setEnabled(anInput);
mpSpoilerSavePathLineEdit->setEnabled(anInput);
mpSpoilerPathButton->setEnabled(anInput);
updateNowButton->setEnabled(anInput);
infoOnSpoilersLabel.setEnabled(anInput);
if (! anInput)
{
SpoilerBackgroundUpdater::deleteSpoilerFile();
}
}
void DeckEditorSettingsPage::retranslateUi()
{
generalGroupBox->setTitle(tr("General"));
mpSpoilerGroupBox->setTitle(tr("Spoilers"));
mcDownloadSpoilersCheckBox.setText(tr("Download Spoilers Automatically"));
mcSpoilerSaveLabel.setText(tr("Spoiler Location:"));
mcGeneralMessageLabel.setText(tr("Hey, something's here finally!"));
infoOnSpoilersLabel.setText(
tr("Last Updated") + ": " + getLastUpdateTime() + "\n\n" +
tr("Spoilers download automatically on launch") + "\n" +
tr("Press the button to manually update without relaunching") + "\n\n" +
tr("Do not close settings until manual update complete")
);
}
MessagesSettingsPage::MessagesSettingsPage()
@ -514,12 +613,12 @@ MessagesSettingsPage::MessagesSettingsPage()
chatMentionCompleterCheckbox.setChecked(settingsCache->getChatMentionCompleter());
connect(&chatMentionCompleterCheckbox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setChatMentionCompleter(int)));
ignoreUnregUsersMainChat.setChecked(settingsCache->getIgnoreUnregisteredUsers());
ignoreUnregUserMessages.setChecked(settingsCache->getIgnoreUnregisteredUserMessages());
connect(&ignoreUnregUsersMainChat, SIGNAL(stateChanged(int)), settingsCache, SLOT(setIgnoreUnregisteredUsers(int)));
connect(&ignoreUnregUserMessages, SIGNAL(stateChanged(int)), settingsCache, SLOT(setIgnoreUnregisteredUserMessages(int)));
invertMentionForeground.setChecked(settingsCache->getChatMentionForeground());
connect(&invertMentionForeground, SIGNAL(stateChanged(int)), this, SLOT(updateTextColor(int)));
@ -545,7 +644,7 @@ MessagesSettingsPage::MessagesSettingsPage()
customAlertString->setText(settingsCache->getHighlightWords());
connect(customAlertString, SIGNAL(textChanged(QString)), settingsCache, SLOT(setHighlightWords(QString)));
QGridLayout *chatGrid = new QGridLayout;
auto *chatGrid = new QGridLayout;
chatGrid->addWidget(&chatMentionCheckBox, 0, 0);
chatGrid->addWidget(&invertMentionForeground, 0, 1);
chatGrid->addWidget(mentionColor, 0, 2);
@ -558,13 +657,13 @@ MessagesSettingsPage::MessagesSettingsPage()
chatGrid->addWidget(&roomHistory, 6, 0);
chatGroupBox = new QGroupBox;
chatGroupBox->setLayout(chatGrid);
highlightColor = new QLineEdit();
highlightColor->setText(settingsCache->getChatHighlightColor());
updateHighlightPreview();
connect(highlightColor, SIGNAL(textChanged(QString)), this, SLOT(updateHighlightColor(QString)));
QGridLayout *highlightNotice = new QGridLayout;
auto *highlightNotice = new QGridLayout;
highlightNotice->addWidget(highlightColor, 0, 2);
highlightNotice->addWidget(&invertHighlightForeground, 0, 1);
highlightNotice->addWidget(&hexHighlightLabel, 1, 2);
@ -578,7 +677,7 @@ MessagesSettingsPage::MessagesSettingsPage()
int count = settingsCache->messages().getCount();
for (int i = 0; i < count; i++)
messageList->addItem(settingsCache->messages().getMessageAt(i));
aAdd = new QAction(this);
aAdd->setIcon(QPixmap("theme:icons/increment"));
connect(aAdd, SIGNAL(triggered()), this, SLOT(actAdd()));
@ -586,30 +685,31 @@ MessagesSettingsPage::MessagesSettingsPage()
aRemove->setIcon(QPixmap("theme:icons/decrement"));
connect(aRemove, SIGNAL(triggered()), this, SLOT(actRemove()));
QToolBar *messageToolBar = new QToolBar;
auto *messageToolBar = new QToolBar;
messageToolBar->setOrientation(Qt::Vertical);
messageToolBar->addAction(aAdd);
messageToolBar->addAction(aRemove);
QHBoxLayout *messageListLayout = new QHBoxLayout;
auto *messageListLayout = new QHBoxLayout;
messageListLayout->addWidget(messageToolBar);
messageListLayout->addWidget(messageList);
messageShortcuts = new QGroupBox;
messageShortcuts->setLayout(messageListLayout);
QVBoxLayout *mainLayout = new QVBoxLayout;
auto *mainLayout = new QVBoxLayout;
mainLayout->addWidget(messageShortcuts);
mainLayout->addWidget(chatGroupBox);
mainLayout->addWidget(highlightGroupBox);
setLayout(mainLayout);
retranslateUi();
}
void MessagesSettingsPage::updateColor(const QString &value) {
void MessagesSettingsPage::updateColor(const QString &value)
{
QColor colorToSet;
colorToSet.setNamedColor("#" + value);
if (colorToSet.isValid()) {
@ -618,7 +718,8 @@ void MessagesSettingsPage::updateColor(const QString &value) {
}
}
void MessagesSettingsPage::updateHighlightColor(const QString &value) {
void MessagesSettingsPage::updateHighlightColor(const QString &value)
{
QColor colorToSet;
colorToSet.setNamedColor("#" + value);
if (colorToSet.isValid()) {
@ -627,22 +728,26 @@ void MessagesSettingsPage::updateHighlightColor(const QString &value) {
}
}
void MessagesSettingsPage::updateTextColor(int value) {
void MessagesSettingsPage::updateTextColor(int value)
{
settingsCache->setChatMentionForeground(value);
updateMentionPreview();
}
void MessagesSettingsPage::updateTextHighlightColor(int value) {
void MessagesSettingsPage::updateTextHighlightColor(int value)
{
settingsCache->setChatHighlightForeground(value);
updateHighlightPreview();
}
void MessagesSettingsPage::updateMentionPreview() {
mentionColor->setStyleSheet("QLineEdit{background:#" + settingsCache->getChatMentionColor() +
void MessagesSettingsPage::updateMentionPreview()
{
mentionColor->setStyleSheet("QLineEdit{background:#" + settingsCache->getChatMentionColor() +
";color: " + (settingsCache->getChatMentionForeground() ? "white" : "black") + ";}");
}
void MessagesSettingsPage::updateHighlightPreview() {
void MessagesSettingsPage::updateHighlightPreview()
{
highlightColor->setStyleSheet("QLineEdit{background:#" + settingsCache->getChatHighlightColor() +
";color: " + (settingsCache->getChatHighlightForeground() ? "white" : "black") + ";}");
}
@ -725,7 +830,7 @@ SoundSettingsPage::SoundSettingsPage()
connect(masterVolumeSlider, SIGNAL(valueChanged(int)), masterVolumeSpinBox, SLOT(setValue(int)));
connect(masterVolumeSpinBox, SIGNAL(valueChanged(int)), masterVolumeSlider, SLOT(setValue(int)));
QGridLayout *soundGrid = new QGridLayout;
auto *soundGrid = new QGridLayout;
soundGrid->addWidget(&soundEnabledCheckBox, 0, 0, 1, 3);
soundGrid->addWidget(&masterVolumeLabel, 1, 0);
soundGrid->addWidget(masterVolumeSlider, 1, 1);
@ -737,7 +842,7 @@ SoundSettingsPage::SoundSettingsPage()
soundGroupBox = new QGroupBox;
soundGroupBox->setLayout(soundGrid);
QVBoxLayout *mainLayout = new QVBoxLayout;
auto *mainLayout = new QVBoxLayout;
mainLayout->addWidget(soundGroupBox);
setLayout(mainLayout);
@ -750,27 +855,28 @@ void SoundSettingsPage::themeBoxChanged(int index)
settingsCache->setSoundThemeName(themeDirs.at(index));
}
void SoundSettingsPage::masterVolumeChanged(int value) {
void SoundSettingsPage::masterVolumeChanged(int value)
{
masterVolumeSlider->setToolTip(QString::number(value));
}
void SoundSettingsPage::retranslateUi() {
void SoundSettingsPage::retranslateUi()
{
soundEnabledCheckBox.setText(tr("Enable &sounds"));
themeLabel.setText(tr("Current sounds theme:"));
soundTestButton.setText(tr("Test system sound engine"));
soundGroupBox->setTitle(tr("Sound settings"));
masterVolumeLabel.setText(tr("Master volume"));
masterVolumeLabel.setText(tr("Master volume"));
}
DlgSettings::DlgSettings(QWidget *parent)
: QDialog(parent)
DlgSettings::DlgSettings(QWidget *parent) : QDialog(parent)
{
QRect rec = QApplication::desktop()->availableGeometry();
this->setMinimumSize(rec.width() / 2, rec.height() - 100);
this->setBaseSize(rec.width(), rec.height());
connect(settingsCache, SIGNAL(langChanged()), this, SLOT(updateLanguage()));
contentsWidget = new QListWidget;
contentsWidget->setViewMode(QListView::IconMode);
contentsWidget->setIconSize(QSize(58, 50));
@ -778,7 +884,7 @@ DlgSettings::DlgSettings(QWidget *parent)
contentsWidget->setMinimumHeight(85);
contentsWidget->setMaximumHeight(85);
contentsWidget->setSpacing(5);
pagesWidget = new QStackedWidget;
pagesWidget->addWidget(new GeneralSettingsPage);
pagesWidget->addWidget(new AppearanceSettingsPage);
@ -787,25 +893,25 @@ DlgSettings::DlgSettings(QWidget *parent)
pagesWidget->addWidget(new MessagesSettingsPage);
pagesWidget->addWidget(new SoundSettingsPage);
pagesWidget->addWidget(new ShortcutsTab);
createIcons();
contentsWidget->setCurrentRow(0);
QVBoxLayout *vboxLayout = new QVBoxLayout;
auto *vboxLayout = new QVBoxLayout;
vboxLayout->addWidget(contentsWidget);
vboxLayout->addWidget(pagesWidget);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(close()));
QVBoxLayout *mainLayout = new QVBoxLayout;
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(vboxLayout);
mainLayout->addSpacing(12);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
retranslateUi();
adjustSize();
}
@ -815,22 +921,22 @@ void DlgSettings::createIcons()
generalButton->setTextAlignment(Qt::AlignHCenter);
generalButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
generalButton->setIcon(QPixmap("theme:config/general"));
appearanceButton = new QListWidgetItem(contentsWidget);
appearanceButton->setTextAlignment(Qt::AlignHCenter);
appearanceButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
appearanceButton->setIcon(QPixmap("theme:config/appearance"));
userInterfaceButton = new QListWidgetItem(contentsWidget);
userInterfaceButton->setTextAlignment(Qt::AlignHCenter);
userInterfaceButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
userInterfaceButton->setIcon(QPixmap("theme:config/interface"));
deckEditorButton = new QListWidgetItem(contentsWidget);
deckEditorButton->setTextAlignment(Qt::AlignHCenter);
deckEditorButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
deckEditorButton->setIcon(QPixmap("theme:config/deckeditor"));
messagesButton = new QListWidgetItem(contentsWidget);
messagesButton->setTextAlignment(Qt::AlignHCenter);
messagesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
@ -840,7 +946,7 @@ void DlgSettings::createIcons()
soundButton->setTextAlignment(Qt::AlignHCenter);
soundButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
soundButton->setIcon(QPixmap("theme:config/sound"));
shortcutsButton = new QListWidgetItem(contentsWidget);
shortcutsButton->setTextAlignment(Qt::AlignHCenter);
shortcutsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
@ -853,11 +959,12 @@ void DlgSettings::changePage(QListWidgetItem *current, QListWidgetItem *previous
{
if (!current)
current = previous;
pagesWidget->setCurrentIndex(contentsWidget->row(current));
}
void DlgSettings::setTab(int index) {
void DlgSettings::setTab(int index)
{
if (index <= contentsWidget->count()-1 && index >= 0) {
changePage(contentsWidget->item(index), contentsWidget->currentItem());
contentsWidget->setCurrentRow(index);
@ -883,72 +990,86 @@ void DlgSettings::closeEvent(QCloseEvent *event)
QString loadErrorMessage = tr("Unknown Error loading card database");
LoadStatus loadStatus = db->getLoadStatus();
qDebug() << "Card Database load status: " << loadStatus;
switch(loadStatus) {
case Ok:
showLoadError = false;
break;
case Invalid:
loadErrorMessage =
tr("Your card database is invalid.\n\n"
"Cockatrice may not function correctly with an invalid database\n\n"
"You may need to rerun oracle to update your card database.\n\n"
"Would you like to change your database location setting?");
break;
case VersionTooOld:
loadErrorMessage =
tr("Your card database version is too old.\n\n"
"This can cause problems loading card information or images\n\n"
"Usually this can be fixed by rerunning oracle to to update your card database.\n\n"
"Would you like to change your database location setting?");
break;
case NotLoaded:
loadErrorMessage =
tr("Your card database did not finish loading\n\n"
"Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached\n\n"
"Would you like to change your database location setting?");
break;
case FileError:
loadErrorMessage =
tr("File Error loading your card database.\n\n"
"Would you like to change your database location setting?");
break;
case NoCards:
loadErrorMessage =
tr("Your card database was loaded but contains no cards.\n\n"
"Would you like to change your database location setting?");
break;
default:
loadErrorMessage =
tr("Unknown card database load status\n\n"
"Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues\n\n"
"Would you like to change your database location setting?");
switch(loadStatus)
{
case Ok:
showLoadError = false;
break;
case Invalid:
loadErrorMessage =
tr("Your card database is invalid.\n\n"
"Cockatrice may not function correctly with an invalid database\n\n"
"You may need to rerun oracle to update your card database.\n\n"
"Would you like to change your database location setting?");
break;
case VersionTooOld:
loadErrorMessage =
tr("Your card database version is too old.\n\n"
"This can cause problems loading card information or images\n\n"
"Usually this can be fixed by rerunning oracle to to update your card database.\n\n"
"Would you like to change your database location setting?");
break;
case NotLoaded:
loadErrorMessage =
tr("Your card database did not finish loading\n\n"
"Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached\n\n"
"Would you like to change your database location setting?");
break;
case FileError:
loadErrorMessage =
tr("File Error loading your card database.\n\n"
"Would you like to change your database location setting?");
break;
case NoCards:
loadErrorMessage =
tr("Your card database was loaded but contains no cards.\n\n"
"Would you like to change your database location setting?");
break;
default:
loadErrorMessage =
tr("Unknown card database load status\n\n"
"Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues\n\n"
"Would you like to change your database location setting?");
break;
break;
}
if (showLoadError)
if (QMessageBox::critical(this, tr("Error"), loadErrorMessage, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
{
if (QMessageBox::critical(this, tr("Error"), loadErrorMessage, QMessageBox::Yes | QMessageBox::No) ==
QMessageBox::Yes)
{
event->ignore();
return;
}
}
if (!QDir(settingsCache->getDeckPath()).exists() || settingsCache->getDeckPath().isEmpty())
{
// TODO: Prompt to create it
if (QMessageBox::critical(this, tr("Error"), tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
if (QMessageBox::critical(this, tr("Error"), tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
{
event->ignore();
return;
}
}
if (!QDir(settingsCache->getPicsPath()).exists() || settingsCache->getPicsPath().isEmpty())
{
// TODO: Prompt to create it
if (QMessageBox::critical(this, tr("Error"), tr("The path to your card pictures directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
if (QMessageBox::critical(this, tr("Error"), tr("The path to your card pictures directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
{
event->ignore();
return;
}
}
event->accept();
}
void DlgSettings::retranslateUi()
{
setWindowTitle(tr("Settings"));
generalButton->setText(tr("General"));
appearanceButton->setText(tr("Appearance"));
userInterfaceButton->setText(tr("User Interface"));
@ -956,10 +1077,9 @@ void DlgSettings::retranslateUi()
messagesButton->setText(tr("Chat"));
soundButton->setText(tr("Sound"));
shortcutsButton->setText(tr("Shortcuts"));
for (int i = 0; i < pagesWidget->count(); i++)
dynamic_cast<AbstractSettingsPage *>(pagesWidget->widget(i))->retranslateUi();
contentsWidget->reset();
}
}

View file

@ -26,192 +26,227 @@ class QSpinBox;
class QSlider;
class QSpinBox;
class AbstractSettingsPage : public QWidget {
public:
virtual void retranslateUi() = 0;
class AbstractSettingsPage : public QWidget
{
public:
virtual void retranslateUi() = 0;
};
class GeneralSettingsPage : public AbstractSettingsPage {
class GeneralSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
public:
GeneralSettingsPage();
void retranslateUi();
private slots:
void deckPathButtonClicked();
void replaysPathButtonClicked();
void picsPathButtonClicked();
void clearDownloadedPicsButtonClicked();
void cardDatabasePathButtonClicked();
void tokenDatabasePathButtonClicked();
void languageBoxChanged(int index);
void setEnabledStatus(bool);
void defaultUrlRestoreButtonClicked();
void fallbackUrlRestoreButtonClicked();
private:
QStringList findQmFiles();
QString languageName(const QString &qmFile);
QLineEdit *deckPathEdit;
QLineEdit *replaysPathEdit;
QLineEdit *picsPathEdit;
QLineEdit *cardDatabasePathEdit;
QLineEdit *tokenDatabasePathEdit;
QLineEdit *defaultUrlEdit;
QLineEdit *fallbackUrlEdit;
QSpinBox pixmapCacheEdit;
QGroupBox *personalGroupBox;
QGroupBox *pathsGroupBox;
QComboBox languageBox;
QCheckBox picDownloadCheckBox;
QCheckBox updateNotificationCheckBox;
QComboBox updateReleaseChannelBox;
QLabel languageLabel;
QLabel pixmapCacheLabel;
QLabel deckPathLabel;
QLabel replaysPathLabel;
QLabel picsPathLabel;
QLabel cardDatabasePathLabel;
QLabel tokenDatabasePathLabel;
QLabel defaultUrlLabel;
QLabel fallbackUrlLabel;
QLabel urlLinkLabel;
QLabel updateReleaseChannelLabel;
QPushButton clearDownloadedPicsButton;
QPushButton defaultUrlRestoreButton;
QPushButton fallbackUrlRestoreButton;
public:
GeneralSettingsPage();
void retranslateUi() override;
private slots:
void deckPathButtonClicked();
void replaysPathButtonClicked();
void picsPathButtonClicked();
void clearDownloadedPicsButtonClicked();
void cardDatabasePathButtonClicked();
void tokenDatabasePathButtonClicked();
void languageBoxChanged(int index);
void setEnabledStatus(bool);
void defaultUrlRestoreButtonClicked();
void fallbackUrlRestoreButtonClicked();
private:
QStringList findQmFiles();
QString languageName(const QString &qmFile);
QLineEdit *deckPathEdit;
QLineEdit *replaysPathEdit;
QLineEdit *picsPathEdit;
QLineEdit *cardDatabasePathEdit;
QLineEdit *tokenDatabasePathEdit;
QLineEdit *defaultUrlEdit;
QLineEdit *fallbackUrlEdit;
QSpinBox pixmapCacheEdit;
QGroupBox *personalGroupBox;
QGroupBox *pathsGroupBox;
QComboBox languageBox;
QCheckBox picDownloadCheckBox;
QCheckBox updateNotificationCheckBox;
QComboBox updateReleaseChannelBox;
QLabel languageLabel;
QLabel pixmapCacheLabel;
QLabel deckPathLabel;
QLabel replaysPathLabel;
QLabel picsPathLabel;
QLabel cardDatabasePathLabel;
QLabel tokenDatabasePathLabel;
QLabel defaultUrlLabel;
QLabel fallbackUrlLabel;
QLabel urlLinkLabel;
QLabel updateReleaseChannelLabel;
QPushButton clearDownloadedPicsButton;
QPushButton defaultUrlRestoreButton;
QPushButton fallbackUrlRestoreButton;
};
class AppearanceSettingsPage : public AbstractSettingsPage {
class AppearanceSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
private slots:
void themeBoxChanged(int index);
private:
QLabel themeLabel;
QComboBox themeBox;
QLabel minPlayersForMultiColumnLayoutLabel;
QLabel maxFontSizeForCardsLabel;
QCheckBox displayCardNamesCheckBox;
QCheckBox cardScalingCheckBox;
QCheckBox horizontalHandCheckBox;
QCheckBox leftJustifiedHandCheckBox;
QCheckBox invertVerticalCoordinateCheckBox;
QGroupBox *themeGroupBox;
QGroupBox *cardsGroupBox;
QGroupBox *handGroupBox;
QGroupBox *tableGroupBox;
QSpinBox minPlayersForMultiColumnLayoutEdit;
QSpinBox maxFontSizeForCardsEdit;
public:
AppearanceSettingsPage();
void retranslateUi();
private slots:
void themeBoxChanged(int index);
private:
QLabel themeLabel;
QComboBox themeBox;
QLabel minPlayersForMultiColumnLayoutLabel;
QLabel maxFontSizeForCardsLabel;
QCheckBox displayCardNamesCheckBox;
QCheckBox cardScalingCheckBox;
QCheckBox horizontalHandCheckBox;
QCheckBox leftJustifiedHandCheckBox;
QCheckBox invertVerticalCoordinateCheckBox;
QGroupBox *themeGroupBox;
QGroupBox *cardsGroupBox;
QGroupBox *handGroupBox;
QGroupBox *tableGroupBox;
QSpinBox minPlayersForMultiColumnLayoutEdit;
QSpinBox maxFontSizeForCardsEdit;
public:
AppearanceSettingsPage();
void retranslateUi() override;
};
class UserInterfaceSettingsPage : public AbstractSettingsPage {
class UserInterfaceSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
private slots:
void setSpecNotificationEnabled(int);
private:
QCheckBox notificationsEnabledCheckBox;
QCheckBox specNotificationsEnabledCheckBox;
QCheckBox doubleClickToPlayCheckBox;
QCheckBox playToStackCheckBox;
QCheckBox annotateTokensCheckBox;
QCheckBox tapAnimationCheckBox;
QGroupBox *generalGroupBox;
QGroupBox *animationGroupBox;
public:
UserInterfaceSettingsPage();
void retranslateUi();
private slots:
void setSpecNotificationEnabled(int);
private:
QCheckBox notificationsEnabledCheckBox;
QCheckBox specNotificationsEnabledCheckBox;
QCheckBox doubleClickToPlayCheckBox;
QCheckBox playToStackCheckBox;
QCheckBox annotateTokensCheckBox;
QCheckBox tapAnimationCheckBox;
QGroupBox *generalGroupBox;
QGroupBox *animationGroupBox;
public:
UserInterfaceSettingsPage();
void retranslateUi() override;
};
class DeckEditorSettingsPage : public AbstractSettingsPage {
class DeckEditorSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
public:
DeckEditorSettingsPage();
void retranslateUi();
private slots:
signals:
private:
QGroupBox *generalGroupBox;
public:
DeckEditorSettingsPage();
void retranslateUi() override;
QString getLastUpdateTime();
private slots:
void setSpoilersEnabled(bool);
void spoilerPathButtonClicked();
void updateSpoilers();
void unlockSettings();
private:
QCheckBox mcDownloadSpoilersCheckBox;
QLabel msDownloadSpoilersLabel;
QGroupBox *mpGeneralGroupBox;
QGroupBox *mpSpoilerGroupBox;
QLineEdit *mpSpoilerSavePathLineEdit;
QLabel mcSpoilerSaveLabel;
QLabel mcGeneralMessageLabel;
QLabel infoOnSpoilersLabel;
QPushButton *mpSpoilerPathButton;
QPushButton *updateNowButton;
};
class MessagesSettingsPage : public AbstractSettingsPage {
class MessagesSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
public:
MessagesSettingsPage();
void retranslateUi();
private slots:
void actAdd();
void actRemove();
void updateColor(const QString &value);
void updateHighlightColor(const QString &value);
void updateTextColor(int value);
void updateTextHighlightColor(int value);
private:
QListWidget *messageList;
QAction *aAdd;
QAction *aRemove;
QCheckBox chatMentionCheckBox;
QCheckBox chatMentionCompleterCheckbox;
QCheckBox invertMentionForeground;
QCheckBox invertHighlightForeground;
QCheckBox ignoreUnregUsersMainChat;
QCheckBox ignoreUnregUserMessages;
QCheckBox messagePopups;
QCheckBox mentionPopups;
QCheckBox roomHistory;
QGroupBox *chatGroupBox;
QGroupBox *highlightGroupBox;
QGroupBox *messageShortcuts;
QLineEdit *mentionColor;
QLineEdit *highlightColor;
QLineEdit *customAlertString;
QLabel hexLabel;
QLabel hexHighlightLabel;
QLabel customAlertStringLabel;
public:
MessagesSettingsPage();
void retranslateUi() override;
void storeSettings();
void updateMentionPreview();
void updateHighlightPreview();
private slots:
void actAdd();
void actRemove();
void updateColor(const QString &value);
void updateHighlightColor(const QString &value);
void updateTextColor(int value);
void updateTextHighlightColor(int value);
private:
QListWidget *messageList;
QAction *aAdd;
QAction *aRemove;
QCheckBox chatMentionCheckBox;
QCheckBox chatMentionCompleterCheckbox;
QCheckBox invertMentionForeground;
QCheckBox invertHighlightForeground;
QCheckBox ignoreUnregUsersMainChat;
QCheckBox ignoreUnregUserMessages;
QCheckBox messagePopups;
QCheckBox mentionPopups;
QCheckBox roomHistory;
QGroupBox *chatGroupBox;
QGroupBox *highlightGroupBox;
QGroupBox *messageShortcuts;
QLineEdit *mentionColor;
QLineEdit *highlightColor;
QLineEdit *customAlertString;
QLabel hexLabel;
QLabel hexHighlightLabel;
QLabel customAlertStringLabel;
void storeSettings();
void updateMentionPreview();
void updateHighlightPreview();
};
class SoundSettingsPage : public AbstractSettingsPage {
class SoundSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
public:
SoundSettingsPage();
void retranslateUi();
private:
QLabel themeLabel;
QComboBox themeBox;
QGroupBox *soundGroupBox;
QPushButton soundTestButton;
QCheckBox soundEnabledCheckBox;
QLabel masterVolumeLabel;
QSlider *masterVolumeSlider;
QSpinBox *masterVolumeSpinBox;
private slots:
void masterVolumeChanged(int value);
void themeBoxChanged(int index);
public:
SoundSettingsPage();
void retranslateUi() override;
private:
QLabel themeLabel;
QComboBox themeBox;
QGroupBox *soundGroupBox;
QPushButton soundTestButton;
QCheckBox soundEnabledCheckBox;
QLabel masterVolumeLabel;
QSlider *masterVolumeSlider;
QSpinBox *masterVolumeSpinBox;
private slots:
void masterVolumeChanged(int value);
void themeBoxChanged(int index);
};
class DlgSettings : public QDialog {
class DlgSettings : public QDialog
{
Q_OBJECT
public:
DlgSettings(QWidget *parent = 0);
void setTab(int index);
private slots:
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
void updateLanguage();
private:
QListWidget *contentsWidget;
QStackedWidget *pagesWidget;
QListWidgetItem *generalButton, *appearanceButton, *userInterfaceButton, *deckEditorButton, *messagesButton, *soundButton;
QListWidgetItem *shortcutsButton;
void createIcons();
void retranslateUi();
protected:
void changeEvent(QEvent *event);
void closeEvent(QCloseEvent *event);
public:
explicit DlgSettings(QWidget *parent = nullptr);
void setTab(int index);
private slots:
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
void updateLanguage();
private:
QListWidget *contentsWidget;
QStackedWidget *pagesWidget;
QListWidgetItem *generalButton, *appearanceButton, *userInterfaceButton, *deckEditorButton, *messagesButton, *soundButton;
QListWidgetItem *shortcutsButton;
void createIcons();
void retranslateUi();
protected:
void changeEvent(QEvent *event) override;
void closeEvent(QCloseEvent *event) override;
};
#endif
#endif

View file

@ -42,6 +42,7 @@
#include "soundengine.h"
#include "featureset.h"
#include "logger.h"
#include "spoilerbackgroundupdater.h"
CardDatabase *db;
QTranslator *translator, *qtTranslator;
@ -129,6 +130,10 @@ int main(int argc, char *argv[])
settingsCache->setClientID(generateClientID());
// If spoiler mode is enabled, we will download the spoilers
// then reload the DB. otherwise just reload the DB
SpoilerBackgroundUpdater spoilerBackgroundUpdater;
ui.show();
qDebug("main(): ui.show() finished");

View file

@ -7,6 +7,7 @@ class QSystemTrayIcon;
class SoundEngine;
extern CardDatabase *db;
extern QSystemTrayIcon *trayIcon;
extern QTranslator *translator;
extern const QString translationPrefix;

View file

@ -1,36 +1,36 @@
#include "carddatabasesettings.h"
CardDatabaseSettings::CardDatabaseSettings(QString settingPath, QObject *parent)
: SettingsManager(settingPath+"cardDatabase.ini", parent)
CardDatabaseSettings::CardDatabaseSettings(QString settingPath, QObject *parent) : SettingsManager(settingPath+"cardDatabase.ini", parent)
{
}
void CardDatabaseSettings::setSortKey(QString shortName, unsigned int sortKey)
{
setValue(sortKey,"sortkey", "sets", shortName);
setValue(sortKey, "sortkey", "sets", std::move(shortName));
}
void CardDatabaseSettings::setEnabled(QString shortName, bool enabled)
{
setValue(enabled, "enabled", "sets", shortName);
setValue(enabled, "enabled", "sets", std::move(shortName));
}
void CardDatabaseSettings::setIsKnown(QString shortName, bool isknown)
{
setValue(isknown, "isknown", "sets", shortName);
setValue(isknown, "isknown", "sets", std::move(shortName));
}
unsigned int CardDatabaseSettings::getSortKey(QString shortName)
{
return getValue("sortkey", "sets", shortName).toUInt();
return getValue("sortkey", "sets", std::move(shortName)).toUInt();
}
bool CardDatabaseSettings::isEnabled(QString shortName)
{
return getValue("enabled", "sets", shortName).toBool();
return getValue("enabled", "sets", std::move(shortName)).toBool();
}
bool CardDatabaseSettings::isKnown(QString shortName)
{
return getValue("isknown", "sets", shortName).toBool();
return getValue("isknown", "sets", std::move(shortName)).toBool();
}

View file

@ -1,43 +1,58 @@
#include "settingsmanager.h"
SettingsManager::SettingsManager(QString settingPath, QObject *parent)
: QObject(parent),
settings(settingPath, QSettings::IniFormat)
SettingsManager::SettingsManager(QString settingPath, QObject *parent) : QObject(parent), settings(settingPath, QSettings::IniFormat)
{
}
void SettingsManager::setValue(QVariant value, QString name, QString group, QString subGroup)
{
if(!group.isEmpty())
if (!group.isEmpty())
{
settings.beginGroup(group);
}
if(!subGroup.isEmpty())
if (!subGroup.isEmpty())
{
settings.beginGroup(subGroup);
}
settings.setValue(name, value);
if(!subGroup.isEmpty())
if (!subGroup.isEmpty())
{
settings.endGroup();
}
if(!group.isEmpty())
if (!group.isEmpty())
{
settings.endGroup();
}
}
QVariant SettingsManager::getValue(QString name, QString group, QString subGroup)
{
if(!group.isEmpty())
if (!group.isEmpty())
{
settings.beginGroup(group);
}
if(!subGroup.isEmpty())
if (!subGroup.isEmpty())
{
settings.beginGroup(subGroup);
}
QVariant value = settings.value(name);
if(!subGroup.isEmpty())
if (!subGroup.isEmpty())
{
settings.endGroup();
}
if(!group.isEmpty())
if (!group.isEmpty())
{
settings.endGroup();
}
return value;
}

View file

@ -171,6 +171,8 @@ SettingsCache::SettingsCache()
releaseChannels << new StableReleaseChannel();
releaseChannels << new DevReleaseChannel();
mbDownloadSpoilers = settings->value("personal/downloadspoilers", false).toBool();
notifyAboutUpdates = settings->value("personal/updatenotification", true).toBool();
updateReleaseChannel = settings->value("personal/updatereleasechannel", 0).toInt();
@ -187,6 +189,7 @@ SettingsCache::SettingsCache()
cardDatabasePath = getSafeConfigFilePath("paths/carddatabase", dataPath + "/cards.xml");
tokenDatabasePath = getSafeConfigFilePath("paths/tokendatabase", dataPath + "/tokens.xml");
spoilerDatabasePath = getSafeConfigFilePath("paths/spoilerdatabase", dataPath + "/spoiler.xml");
themeName = settings->value("theme/name").toString();
@ -348,6 +351,13 @@ void SettingsCache::setCardDatabasePath(const QString &_cardDatabasePath)
emit cardDatabasePathChanged();
}
void SettingsCache::setSpoilerDatabasePath(const QString &_spoilerDatabasePath)
{
spoilerDatabasePath = _spoilerDatabasePath;
settings->setValue("paths/spoilerdatabase", spoilerDatabasePath);
emit cardDatabasePathChanged();
}
void SettingsCache::setTokenDatabasePath(const QString &_tokenDatabasePath)
{
tokenDatabasePath = _tokenDatabasePath;
@ -644,10 +654,17 @@ void SettingsCache::setRememberGameSettings(const bool _rememberGameSettings)
void SettingsCache::setNotifyAboutUpdate(int _notifyaboutupdate)
{
notifyAboutUpdates = _notifyaboutupdate;
notifyAboutUpdates = static_cast<bool>(_notifyaboutupdate);
settings->setValue("personal/updatenotification", notifyAboutUpdates);
}
void SettingsCache::setDownloadSpoilerStatus(bool _spoilerStatus)
{
mbDownloadSpoilers = _spoilerStatus;
settings->setValue("personal/downloadspoilers", mbDownloadSpoilers);
emit downloadSpoilerStatusChanged();
}
void SettingsCache::setUpdateReleaseChannel(int _updateReleaseChannel)
{
updateReleaseChannel = _updateReleaseChannel;

View file

@ -48,6 +48,8 @@ signals:
void pixmapCacheSizeChanged(int newSizeInMBs);
void masterVolumeChanged(int value);
void chatMentionCompleterChanged();
void downloadSpoilerTimeIndexChanged();
void downloadSpoilerStatusChanged();
private:
QSettings *settings;
ShortcutsSettings *shortcutsSettings;
@ -60,8 +62,9 @@ private:
QByteArray mainWindowGeometry;
QByteArray tokenDialogGeometry;
QString lang;
QString deckPath, replaysPath, picsPath, customPicsPath, cardDatabasePath, customCardDatabasePath, tokenDatabasePath, themeName;
QString deckPath, replaysPath, picsPath, customPicsPath, cardDatabasePath, customCardDatabasePath, spoilerDatabasePath, tokenDatabasePath, themeName;
bool notifyAboutUpdates;
bool mbDownloadSpoilers;
int updateReleaseChannel;
int maxFontSize;
bool picDownload;
@ -130,6 +133,7 @@ public:
QString getCustomPicsPath() const { return customPicsPath; }
QString getCustomCardDatabasePath() const { return customCardDatabasePath; }
QString getCardDatabasePath() const { return cardDatabasePath; }
QString getSpoilerCardDatabasePath() const { return spoilerDatabasePath; }
QString getTokenDatabasePath() const { return tokenDatabasePath; }
QString getThemeName() const { return themeName; }
QString getChatMentionColor() const { return chatMentionColor; }
@ -200,7 +204,10 @@ public:
GameFiltersSettings& gameFilters() const { return *gameFiltersSettings; }
LayoutsSettings& layouts() const { return *layoutsSettings; }
bool getIsPortableBuild() const { return isPortableBuild; }
bool getDownloadSpoilersStatus() const { return mbDownloadSpoilers; }
public slots:
void setDownloadSpoilerStatus(bool _spoilerStatus);
void setMainWindowGeometry(const QByteArray &_mainWindowGeometry);
void setTokenDialogGeometry(const QByteArray &_tokenDialog);
void setLang(const QString &_lang);
@ -208,6 +215,7 @@ public slots:
void setReplaysPath(const QString &_replaysPath);
void setPicsPath(const QString &_picsPath);
void setCardDatabasePath(const QString &_cardDatabasePath);
void setSpoilerDatabasePath(const QString &_spoilerDatabasePath);
void setTokenDatabasePath(const QString &_tokenDatabasePath);
void setThemeName(const QString &_themeName);
void setChatMentionColor(const QString &_chatMentionColor);

View file

@ -0,0 +1,251 @@
#include <QDateTime>
#include <QDebug>
#include <QUrl>
#include <QNetworkReply>
#include <QMessageBox>
#include <QFile>
#include <QApplication>
#include <QtConcurrent>
#include <QCryptographicHash>
#include "spoilerbackgroundupdater.h"
#include "settingscache.h"
#include "carddatabase.h"
#include "main.h"
#include "window_main.h"
#define SPOILERS_STATUS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/SpoilerSeasonEnabled"
#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml"
SpoilerBackgroundUpdater::SpoilerBackgroundUpdater(QObject *apParent) : QObject(apParent), cardUpdateProcess(nullptr)
{
isSpoilerDownloadEnabled = settingsCache->getDownloadSpoilersStatus();
if (isSpoilerDownloadEnabled)
{
// Start the process of checking if we're in spoiler season
// File exists means we're in spoiler season
// We will load the database before attempting to download spoilers, incase they fail
QtConcurrent::run(db, &CardDatabase::loadCardDatabases);
startSpoilerDownloadProcess(SPOILERS_STATUS_URL, false);
}
}
void SpoilerBackgroundUpdater::startSpoilerDownloadProcess(QString url, bool saveResults)
{
auto spoilerURL = QUrl(url);
downloadFromURL(spoilerURL, saveResults);
}
void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults)
{
auto *nam = new QNetworkAccessManager(this);
QNetworkReply *reply = nam->get(QNetworkRequest(url));
if (saveResults)
{
// This will write out to the file (used for spoiler.xml)
connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSpoilersFile()));
}
else
{
// This will check the status (used to see if we're in spoiler season or not)
connect(reply, SIGNAL(finished()), this, SLOT(actCheckIfSpoilerSeasonEnabled()));
}
}
void SpoilerBackgroundUpdater::actDownloadFinishedSpoilersFile()
{
// Check for server reply
auto *reply = dynamic_cast<QNetworkReply *>(sender());
QNetworkReply::NetworkError errorCode = reply->error();
if (errorCode == QNetworkReply::NoError)
{
spoilerData = reply->readAll();
// Save the spoiler.xml file to the disk
saveDownloadedFile(spoilerData);
reply->deleteLater();
emit spoilerCheckerDone();
}
else
{
qDebug() << "Error downloading spoilers file" << errorCode;
emit spoilerCheckerDone();
}
}
bool SpoilerBackgroundUpdater::deleteSpoilerFile()
{
QString fileName = settingsCache->getSpoilerCardDatabasePath();
QFileInfo fi(fileName);
QDir fileDir(fi.path());
QFile file(fileName);
// Delete the spoiler.xml file
if (file.exists() && file.remove())
{
qDebug() << "Deleting spoiler.xml";
return true;
}
qDebug() << "Error: Spoiler.xml not found or not deleted";
return false;
}
void SpoilerBackgroundUpdater::actCheckIfSpoilerSeasonEnabled()
{
auto *response = dynamic_cast<QNetworkReply *>(sender());
QNetworkReply::NetworkError errorCode = response->error();
if (errorCode == QNetworkReply::ContentNotFoundError)
{
// Spoiler season is offline at this point, so the spoiler.xml file can be safely deleted
// The user should run Oracle to get the latest card information
if (deleteSpoilerFile() && trayIcon)
{
trayIcon->showMessage(tr("Spoilers season has ended"), tr("Deleting spoiler.xml. Please run Oracle"));
}
qDebug() << "Spoiler Season Offline";
emit spoilerCheckerDone();
}
else if (errorCode == QNetworkReply::NoError)
{
qDebug() << "Spoiler Service Online";
startSpoilerDownloadProcess(SPOILERS_URL, true);
}
else if (errorCode == QNetworkReply::HostNotFoundError)
{
if (trayIcon)
{
trayIcon->showMessage(tr("Spoilers download failed"), tr("No internet connection"));
}
qDebug() << "Spoiler download failed due to no internet connection";
emit spoilerCheckerDone();
}
else
{
if (trayIcon)
{
trayIcon->showMessage(tr("Spoilers download failed"), tr("Error") + " " + errorCode);
}
qDebug() << "Spoiler download failed with reason" << errorCode;
emit spoilerCheckerDone();
}
}
bool SpoilerBackgroundUpdater::saveDownloadedFile(QByteArray data)
{
QString fileName = settingsCache->getSpoilerCardDatabasePath();
QFileInfo fi(fileName);
QDir fileDir(fi.path());
if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath()))
{
return false;
}
// Check if the data matches. If it does, then spoilers are up to date.
if (getHash(fileName) == getHash(data))
{
if (trayIcon)
{
trayIcon->showMessage(tr("Spoilers already up to date"), tr("No new spoilers added"));
}
qDebug() << "Spoilers Up to Date";
return false;
}
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly))
{
qDebug() << "Spoiler Service Error: File open (w) failed for" << fileName;
file.close();
return false;
}
if (file.write(data) == -1)
{
qDebug() << "Spoiler Service Error: File write (w) failed for" << fileName;
file.close();
return false;
}
file.close();
// Data written, so reload the card database
qDebug() << "Spoiler Service Data Written";
QtConcurrent::run(db, &CardDatabase::loadCardDatabases);
// If the user has notifications enabled, let them know
// when the database was last updated
if (trayIcon)
{
QList<QByteArray> lines = data.split('\n');
foreach (QByteArray line, lines)
{
if (line.indexOf("created:") > -1)
{
QString timeStamp = QString(line).replace("created:", "").trimmed();
timeStamp.chop(6); // Remove " (UTC)"
auto utcTime = QDateTime::fromString(timeStamp, QString("ddd, MMM dd yyyy, hh:mm:ss"));
utcTime.setTimeSpec(Qt::UTC);
QString localTime = utcTime.toLocalTime().toString("MMM d, hh:mm");
trayIcon->showMessage(tr("Spoilers have been updated!"), tr("Last change:") + " " + localTime);
emit spoilersUpdatedSuccessfully();
return true;
}
}
}
return true;
}
QByteArray SpoilerBackgroundUpdater::getHash(const QString fileName)
{
QFile file(fileName);
if (file.open(QFile::ReadOnly))
{
// Only read the first 512 bytes (enough to get the "created" tag)
const QByteArray bytes = file.read(512);
QCryptographicHash hash(QCryptographicHash::Algorithm::Md5);
hash.addData(bytes);
qDebug() << "File Hash =" << hash.result();
file.close();
return hash.result();
}
else
{
qDebug() << "getHash ReadOnly failed!";
file.close();
return QByteArray();
}
}
QByteArray SpoilerBackgroundUpdater::getHash(QByteArray data)
{
// Only read the first 512 bytes (enough to get the "created" tag)
const QByteArray bytes = data.left(512);
QCryptographicHash hash(QCryptographicHash::Algorithm::Md5);
hash.addData(bytes);
qDebug() << "Data Hash =" << hash.result();
return hash.result();
}

View file

@ -0,0 +1,35 @@
#ifndef COCKATRICE_SPOILER_DOWNLOADER_H
#define COCKATRICE_SPOILER_DOWNLOADER_H
#include <QObject>
#include <QProcess>
#include <QByteArray>
class SpoilerBackgroundUpdater : public QObject
{
Q_OBJECT
public:
explicit SpoilerBackgroundUpdater(QObject *apParent = nullptr);
inline QString getCardUpdaterBinaryName() { return "oracle"; };
QByteArray getHash(const QString fileName);
QByteArray getHash(QByteArray data);
static bool deleteSpoilerFile();
private slots:
void actDownloadFinishedSpoilersFile();
void actCheckIfSpoilerSeasonEnabled();
private:
bool isSpoilerDownloadEnabled;
QProcess *cardUpdateProcess;
QByteArray spoilerData;
void startSpoilerDownloadProcess(QString url, bool saveResults);
void downloadFromURL(QUrl url, bool saveResults);
bool saveDownloadedFile(QByteArray data);
signals:
void spoilersUpdatedSuccessfully();
void spoilerCheckerDone();
};
#endif //COCKATRICE_SPOILER_DOWNLOADER_H

View file

@ -76,11 +76,11 @@ const QStringList MainWindow::fileNameFilters = QStringList()
void MainWindow::updateTabMenu(const QList<QMenu *> &newMenuList)
{
for (int i = 0; i < tabMenus.size(); ++i)
menuBar()->removeAction(tabMenus[i]->menuAction());
for (auto &tabMenu : tabMenus)
menuBar()->removeAction(tabMenu->menuAction());
tabMenus = newMenuList;
for (int i = 0; i < tabMenus.size(); ++i)
menuBar()->insertMenu(helpMenu->menuAction(), tabMenus[i]);
for (auto &tabMenu : tabMenus)
menuBar()->insertMenu(helpMenu->menuAction(), tabMenu);
}
void MainWindow::processConnectionClosedEvent(const Event_ConnectionClosed &event)
@ -166,10 +166,10 @@ void MainWindow::activateAccepted()
void MainWindow::actConnect()
{
DlgConnect *dlg = new DlgConnect(this);
auto *dlg = new DlgConnect(this);
connect(dlg, SIGNAL(sigStartForgotPasswordRequest()), this, SLOT(actForgotPasswordRequest()));
if (dlg->exec())
client->connectToServer(dlg->getHost(), dlg->getPort(), dlg->getPlayerName(), dlg->getPassword());
client->connectToServer(dlg->getHost(), static_cast<unsigned int>(dlg->getPort()), dlg->getPlayerName(), dlg->getPassword());
}
void MainWindow::actRegister()
@ -179,7 +179,7 @@ void MainWindow::actRegister()
{
client->registerToServer(
dlg.getHost(),
dlg.getPort(),
static_cast<unsigned int>(dlg.getPort()),
dlg.getPlayerName(),
dlg.getPassword(),
dlg.getEmail(),
@ -220,7 +220,7 @@ void MainWindow::actSinglePlayer()
tabSupervisor->startLocal(localClients);
Command_CreateGame createCommand;
createCommand.set_max_players(numberPlayers);
createCommand.set_max_players(static_cast<google::protobuf::uint32>(numberPlayers));
mainClient->sendCommand(mainClient->prepareRoomCommand(createCommand, 0));
}
@ -239,7 +239,7 @@ void MainWindow::actWatchReplay()
QByteArray buf = file.readAll();
file.close();
GameReplay *replay = new GameReplay;
auto *replay = new GameReplay;
replay->ParseFromArray(buf.data(), buf.size());
tabSupervisor->openReplay(replay);
@ -248,7 +248,7 @@ void MainWindow::actWatchReplay()
void MainWindow::localGameEnded()
{
delete localServer;
localServer = 0;
localServer = nullptr;
aConnect->setEnabled(true);
aRegister->setEnabled(true);
@ -257,7 +257,7 @@ void MainWindow::localGameEnded()
void MainWindow::actDeckEditor()
{
tabSupervisor->addDeckEditorTab(0);
tabSupervisor->addDeckEditorTab(nullptr);
}
void MainWindow::actFullScreen(bool checked)
@ -655,7 +655,7 @@ void MainWindow::createMenus()
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), localServer(0), bHasActivated(false), cardUpdateProcess(0), logviewDialog(0)
: QMainWindow(parent), localServer(nullptr), bHasActivated(false), cardUpdateProcess(nullptr), logviewDialog(nullptr)
{
connect(settingsCache, SIGNAL(pixmapCacheSizeChanged(int)), this, SLOT(pixmapCacheSizeChanged(int)));
pixmapCacheSizeChanged(settingsCache->getPixmapCacheSize());
@ -691,7 +691,7 @@ MainWindow::MainWindow(QWidget *parent)
connect(tabSupervisor, SIGNAL(setMenu(QList<QMenu *>)), this, SLOT(updateTabMenu(QList<QMenu *>)));
connect(tabSupervisor, SIGNAL(localGameEnded()), this, SLOT(localGameEnded()));
connect(tabSupervisor, SIGNAL(showWindowIfHidden()), this, SLOT(showWindowIfHidden()));
tabSupervisor->addDeckEditorTab(0);
tabSupervisor->addDeckEditorTab(nullptr);
setCentralWidget(tabSupervisor);
@ -699,7 +699,7 @@ MainWindow::MainWindow(QWidget *parent)
resize(900, 700);
restoreGeometry(settingsCache->getMainWindowGeometry());
aFullScreen->setChecked(windowState() & Qt::WindowFullScreen);
aFullScreen->setChecked(static_cast<bool>(windowState() & Qt::WindowFullScreen));
if (QSystemTrayIcon::isSystemTrayAvailable()) {
createTrayActions();
@ -712,7 +712,12 @@ MainWindow::MainWindow(QWidget *parent)
connect(db, SIGNAL(cardDatabaseLoadingFailed()), this, SLOT(cardDatabaseLoadingFailed()));
connect(db, SIGNAL(cardDatabaseNewSetsFound(int, QStringList)), this, SLOT(cardDatabaseNewSetsFound(int, QStringList)));
connect(db, SIGNAL(cardDatabaseAllNewSetsEnabled()), this, SLOT(cardDatabaseAllNewSetsEnabled()));
QtConcurrent::run(db, &CardDatabase::loadCardDatabases);
if (! settingsCache->getDownloadSpoilersStatus())
{
qDebug() << "Spoilers Disabled";
QtConcurrent::run(db, &CardDatabase::loadCardDatabases);
}
}
MainWindow::~MainWindow()
@ -727,7 +732,7 @@ MainWindow::~MainWindow()
}
void MainWindow::createTrayIcon() {
QMenu *trayIconMenu = new QMenu(this);
auto *trayIconMenu = new QMenu(this);
trayIconMenu->addAction(closeAction);
trayIcon = new QSystemTrayIcon(this);
@ -754,7 +759,7 @@ void MainWindow::promptForgotPasswordChallenge()
{
DlgForgotPasswordChallenge dlg(this);
if (dlg.exec())
client->submitForgotPasswordChallengeToServer(dlg.getHost(),dlg.getPort(),dlg.getPlayerName(),dlg.getEmail());
client->submitForgotPasswordChallengeToServer(dlg.getHost(), static_cast<unsigned int>(dlg.getPort()), dlg.getPlayerName(), dlg.getEmail());
}
@ -794,7 +799,7 @@ void MainWindow::changeEvent(QEvent *event)
if(settingsCache->servers().getAutoConnect()) {
qDebug() << "Attempting auto-connect...";
DlgConnect dlg(this);
client->connectToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName(), dlg.getPassword());
client->connectToServer(dlg.getHost(), static_cast<unsigned int>(dlg.getPort()), dlg.getPlayerName(), dlg.getPassword());
}
}
}
@ -856,11 +861,17 @@ void MainWindow::cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknow
msgBox.exec();
if (msgBox.clickedButton() == yesButton) {
if (msgBox.clickedButton() == yesButton)
{
db->enableAllUnknownSets();
} else if (msgBox.clickedButton() == noButton) {
QtConcurrent::run(db, &CardDatabase::loadCardDatabases);
}
else if (msgBox.clickedButton() == noButton)
{
db->markAllSetsAsKnown();
} else if (msgBox.clickedButton() == settingsButton) {
}
else if (msgBox.clickedButton() == settingsButton)
{
db->markAllSetsAsKnown();
actEditSets();
}
@ -873,10 +884,9 @@ void MainWindow::cardDatabaseAllNewSetsEnabled()
}
/* CARD UPDATER */
void MainWindow::actCheckCardUpdates()
{
if(cardUpdateProcess)
if (cardUpdateProcess)
{
QMessageBox::information(this, tr("Information"), tr("A card database update is already running."));
return;
@ -946,7 +956,7 @@ void MainWindow::cardUpdateError(QProcess::ProcessError err)
}
cardUpdateProcess->deleteLater();
cardUpdateProcess = 0;
cardUpdateProcess = nullptr;
QMessageBox::warning(this, tr("Error"), tr("The card database updater exited with an error: %1").arg(error));
}
@ -954,10 +964,9 @@ void MainWindow::cardUpdateError(QProcess::ProcessError err)
void MainWindow::cardUpdateFinished(int, QProcess::ExitStatus)
{
cardUpdateProcess->deleteLater();
cardUpdateProcess = 0;
cardUpdateProcess = nullptr;
QMessageBox::information(this, tr("Information"), tr("Update completed successfully.\nCockatrice will now reload the card database."));
QtConcurrent::run(db, &CardDatabase::loadCardDatabases);
}
@ -989,7 +998,7 @@ void MainWindow::actOpenCustomFolder()
#if defined(Q_OS_MAC)
QStringList scriptArgs;
scriptArgs << QLatin1String("-e");
scriptArgs << QString::fromLatin1("tell application \"Finder\" to open POSIX file \"%1\"").arg(dir);
scriptArgs << QString::fromLatin1(R"(tell application "Finder" to open POSIX file "%1")").arg(dir);
scriptArgs << QLatin1String("-e");
scriptArgs << QLatin1String("tell application \"Finder\" to activate");
@ -1008,7 +1017,7 @@ void MainWindow::actOpenCustomsetsFolder()
#if defined(Q_OS_MAC)
QStringList scriptArgs;
scriptArgs << QLatin1String("-e");
scriptArgs << QString::fromLatin1("tell application \"Finder\" to open POSIX file \"%1\"").arg(dir);
scriptArgs << QString::fromLatin1(R"(tell application "Finder" to open POSIX file "%1")").arg(dir);
scriptArgs << QLatin1String("-e");
scriptArgs << QLatin1String("tell application \"Finder\" to activate");
@ -1025,16 +1034,20 @@ void MainWindow::actAddCustomSet()
QFileDialog dialog(this, tr("Load sets/cards"), QDir::homePath());
dialog.setNameFilters(MainWindow::fileNameFilters);
if (!dialog.exec())
{
return;
}
QString fullFilePath = dialog.selectedFiles().at(0);
if (!QFile::exists(fullFilePath)) {
if (!QFile::exists(fullFilePath))
{
QMessageBox::warning(this, tr("Load sets/cards"), tr("Selected file cannot be found."));
return;
}
if (QFileInfo(fullFilePath).suffix() != "xml") { // fileName = *.xml
if (QFileInfo(fullFilePath).suffix() != "xml") // fileName = *.xml
{
QMessageBox::warning(this, tr("Load sets/cards"), tr("You can only import XML databases at this time."));
return;
}
@ -1042,7 +1055,7 @@ void MainWindow::actAddCustomSet()
QDir dir = settingsCache->getCustomCardDatabasePath();
int nextPrefix = getNextCustomSetPrefix(dir);
bool res = false;
bool res;
QString fileName = QFileInfo(fullFilePath).fileName();
if (fileName.compare("spoiler.xml", Qt::CaseInsensitive) == 0)
@ -1078,7 +1091,8 @@ void MainWindow::actAddCustomSet()
}
}
int MainWindow::getNextCustomSetPrefix(QDir dataDir) {
int MainWindow::getNextCustomSetPrefix(QDir dataDir)
{
QStringList files = dataDir.entryList();
int maxIndex = 0;
@ -1094,7 +1108,7 @@ int MainWindow::getNextCustomSetPrefix(QDir dataDir) {
void MainWindow::actEditSets()
{
WndSets *w = new WndSets;
auto *w = new WndSets;
w->setWindowModality(Qt::WindowModal);
w->show();
}
@ -1110,7 +1124,7 @@ void MainWindow::actForgotPasswordRequest()
{
DlgForgotPasswordRequest dlg(this);
if (dlg.exec())
client->requestForgotPasswordToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName());
client->requestForgotPasswordToServer(dlg.getHost(), static_cast<unsigned int>(dlg.getPort()), dlg.getPlayerName());
}
void MainWindow::forgotPasswordSuccess()
@ -1134,5 +1148,8 @@ void MainWindow::promptForgotPasswordReset()
QMessageBox::information(this, tr("Forgot Password"), tr("Activation request received, please check your email for an activation token."));
DlgForgotPasswordReset dlg(this);
if (dlg.exec())
client->submitForgotPasswordResetToServer(dlg.getHost(), dlg.getPort(), dlg.getPlayerName(), dlg.getToken(), dlg.getPassword());
{
client->submitForgotPasswordResetToServer(dlg.getHost(), static_cast<unsigned int>(dlg.getPort()),
dlg.getPlayerName(), dlg.getToken(), dlg.getPassword());
}
}

View file

@ -40,6 +40,8 @@ class DlgViewLog;
class MainWindow : public QMainWindow {
Q_OBJECT
public slots:
void actCheckCardUpdates();
private slots:
void updateTabMenu(const QList<QMenu *> &newMenuList);
void statusChanged(ClientStatus _status);
@ -78,7 +80,6 @@ private slots:
void promptForgotPasswordChallenge();
void showWindowIfHidden();
void actCheckCardUpdates();
void cardUpdateError(QProcess::ProcessError err);
void cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus);
void refreshShortcuts();

View file

@ -3,6 +3,7 @@
#include <QIcon>
#include <QTranslator>
#include <QLibraryInfo>
#include <QCommandLineParser>
#include "main.h"
#include "oraclewizard.h"
@ -15,6 +16,7 @@ ThemeManager *themeManager;
const QString translationPrefix = "oracle";
QString translationPath;
bool isSpoilersOnly;
void installNewTranslator()
{
@ -35,6 +37,13 @@ int main(int argc, char *argv[])
// this can't be changed, as it influences the default savepath for cards.xml
QCoreApplication::setApplicationName("Cockatrice");
// If the program is opened with the -s flag, it will only do spoilers. Otherwise it will do MTGJSON/Tokens
QCommandLineParser parser;
QCommandLineOption showProgressOption("s", QCoreApplication::translate("main", "Only run in spoiler mode"));
parser.addOption(showProgressOption);
parser.process(app);
isSpoilersOnly = parser.isSet(showProgressOption);
#ifdef Q_OS_MAC
translationPath = qApp->applicationDirPath() + "/../Resources/translations";
#elif defined(Q_OS_WIN)

View file

@ -6,6 +6,7 @@ class QTranslator;
extern QTranslator *translator;
extern const QString translationPrefix;
extern QString translationPath;
extern bool isSpoilersOnly;
void installNewTranslator();

View file

@ -37,21 +37,28 @@
#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)
OracleWizard::OracleWizard(QWidget *parent) : QWizard(parent)
{
settings = new QSettings(settingsCache->getSettingsPath()+"global.ini",QSettings::IniFormat, this);
connect(settingsCache, SIGNAL(langChanged()), this, SLOT(updateLanguage()));
importer = new OracleImporter(settingsCache->getDataPath(), this);
addPage(new IntroPage);
addPage(new LoadSetsPage);
addPage(new SaveSetsPage);
addPage(new LoadTokensPage);
addPage(new SaveTokensPage);
if (! isSpoilersOnly)
{
addPage(new IntroPage);
addPage(new LoadSetsPage);
addPage(new SaveSetsPage);
addPage(new LoadTokensPage);
addPage(new SaveTokensPage);
}
else
{
addPage(new LoadSpoilersPage);
addPage(new SaveSpoilersPage);
}
retranslateUi();
}
@ -65,7 +72,10 @@ void OracleWizard::updateLanguage()
void OracleWizard::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange)
{
retranslateUi();
}
QDialog::changeEvent(event);
}
@ -75,7 +85,9 @@ void OracleWizard::retranslateUi()
QWizard::setButtonText(QWizard::FinishButton, tr("Save"));
for (int i = 0; i < pageIds().count(); i++)
{
dynamic_cast<OracleWizardPage *>(page(i))->retranslateUi();
}
}
void OracleWizard::accept()
@ -98,22 +110,23 @@ void OracleWizard::disableButtons()
bool OracleWizard::saveTokensToFile(const QString & fileName)
{
QFile file(fileName);
if(!file.open(QIODevice::WriteOnly))
if (!file.open(QIODevice::WriteOnly))
{
qDebug() << "File open (w) failed for" << fileName;
return false;
}
if(file.write(tokensData) == -1)
if (file.write(tokensData) == -1)
{
qDebug() << "File write (w) failed for" << fileName;
return false;
}
file.close();
return true;
}
IntroPage::IntroPage(QWidget *parent)
: OracleWizardPage(parent)
IntroPage::IntroPage(QWidget *parent) : OracleWizardPage(parent)
{
label = new QLabel(this);
label->setWordWrap(true);
@ -122,16 +135,21 @@ IntroPage::IntroPage(QWidget *parent)
versionLabel = new QLabel(this);
languageBox = new QComboBox(this);
QString setLanguage = settingsCache->getLang();
QStringList qmFiles = findQmFiles();
for (int i = 0; i < qmFiles.size(); i++) {
for (int i = 0; i < qmFiles.size(); i++)
{
QString langName = languageName(qmFiles[i]);
languageBox->addItem(langName, qmFiles[i]);
if ((qmFiles[i] == setLanguage) || (setLanguage.isEmpty() && langName == QCoreApplication::translate("i18n", DEFAULT_LANG_NAME)))
{
languageBox->setCurrentIndex(i);
}
}
connect(languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int)));
QGridLayout *layout = new QGridLayout(this);
auto *layout = new QGridLayout(this);
layout->addWidget(label, 0, 0, 1, 2);
layout->addWidget(languageLabel, 1, 0);
layout->addWidget(languageBox, 1, 1);
@ -150,8 +168,10 @@ QStringList IntroPage::findQmFiles()
QString IntroPage::languageName(const QString &qmFile)
{
if(qmFile == DEFAULT_LANG_CODE)
if (qmFile == DEFAULT_LANG_CODE)
{
return DEFAULT_LANG_NAME;
}
QTranslator translator;
translator.load(translationPrefix + "_" + qmFile + ".qm", translationPath);
@ -173,8 +193,7 @@ void IntroPage::retranslateUi()
versionLabel->setText(tr("Version:") + QString(" %1").arg(VERSION_STRING));
}
LoadSetsPage::LoadSetsPage(QWidget *parent)
: OracleWizardPage(parent), nam(0)
LoadSetsPage::LoadSetsPage(QWidget *parent) : OracleWizardPage(parent), nam(nullptr)
{
urlRadioButton = new QRadioButton(this);
fileRadioButton = new QRadioButton(this);
@ -193,7 +212,7 @@ LoadSetsPage::LoadSetsPage(QWidget *parent)
fileButton = new QPushButton(this);
connect(fileButton, SIGNAL(clicked()), this, SLOT(actLoadSetsFile()));
QGridLayout *layout = new QGridLayout(this);
auto *layout = new QGridLayout(this);
layout->addWidget(urlRadioButton, 0, 0);
layout->addWidget(urlLineEdit, 0, 1);
layout->addWidget(urlButton, 1, 1, Qt::AlignRight);
@ -245,11 +264,15 @@ void LoadSetsPage::actLoadSetsFile()
dialog.setNameFilter(tr("Sets JSON file (*.json)"));
#endif
if(!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text()))
if (!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text()))
{
dialog.selectFile(fileLineEdit->text());
}
if (!dialog.exec())
{
return;
}
fileLineEdit->setText(dialog.selectedFiles().at(0));
}
@ -257,14 +280,16 @@ void LoadSetsPage::actLoadSetsFile()
bool LoadSetsPage::validatePage()
{
// once the import is finished, we call next(); skip validation
if(wizard()->importer->getSets().count() > 0)
if (wizard()->importer->getSets().count() > 0)
{
return true;
}
// else, try to import sets
if(urlRadioButton->isChecked())
if (urlRadioButton->isChecked())
{
QUrl url = QUrl::fromUserInput(urlLineEdit->text());
if(!url.isValid())
if (!url.isValid())
{
QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid."));
return false;
@ -282,16 +307,19 @@ bool LoadSetsPage::validatePage()
setEnabled(false);
downloadSetsFile(url);
} else if(fileRadioButton->isChecked()) {
}
else if (fileRadioButton->isChecked())
{
QFile setsFile(fileLineEdit->text());
if(!setsFile.exists())
if (!setsFile.exists())
{
QMessageBox::critical(this, tr("Error"), tr("Please choose a file."));
return false;
}
if (!setsFile.open(QIODevice::ReadOnly)) {
QMessageBox::critical(0, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text()));
if (!setsFile.open(QIODevice::ReadOnly))
{
QMessageBox::critical(nullptr, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text()));
return false;
}
@ -301,13 +329,16 @@ bool LoadSetsPage::validatePage()
readSetsFromByteArray(setsFile.readAll());
}
return false;
}
void LoadSetsPage::downloadSetsFile(QUrl url)
{
if(!nam)
if (!nam)
{
nam = new QNetworkAccessManager(this);
}
QNetworkReply *reply = nam->get(QNetworkRequest(url));
connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSetsFile()));
@ -316,10 +347,10 @@ void LoadSetsPage::downloadSetsFile(QUrl url)
void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total)
{
if(total > 0)
if (total > 0)
{
progressBar->setMaximum(total);
progressBar->setValue(received);
progressBar->setMaximum(static_cast<int>(total));
progressBar->setValue(static_cast<int>(received));
}
progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / (1024 * 1024)));
}
@ -327,9 +358,10 @@ void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total)
void LoadSetsPage::actDownloadFinishedSetsFile()
{
// check for a reply
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
auto *reply = dynamic_cast<QNetworkReply *>(sender());
QNetworkReply::NetworkError errorCode = reply->error();
if (errorCode != QNetworkReply::NoError) {
if (errorCode != QNetworkReply::NoError)
{
QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString()));
wizard()->enableButtons();
@ -340,7 +372,8 @@ void LoadSetsPage::actDownloadFinishedSetsFile()
}
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode == 301 || statusCode == 302) {
if (statusCode == 301 || statusCode == 302)
{
QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
qDebug() << "following redirect url:" << redirectUrl.toString();
downloadSetsFile(redirectUrl);
@ -352,10 +385,14 @@ void LoadSetsPage::actDownloadFinishedSetsFile()
progressBar->hide();
// save allsets.json url, but only if the user customized it and download was successfull
if(urlLineEdit->text() != QString(ALLSETS_URL))
if (urlLineEdit->text() != QString(ALLSETS_URL))
{
wizard()->settings->setValue("allsetsurl", urlLineEdit->text());
}
else
{
wizard()->settings->remove("allsetsurl");
}
readSetsFromByteArray(reply->readAll());
reply->deleteLater();
@ -376,19 +413,20 @@ void LoadSetsPage::readSetsFromByteArray(QByteArray data)
{
#ifdef HAS_ZLIB
// zipped file
QBuffer *inBuffer = new QBuffer(&data);
QBuffer *outBuffer = new QBuffer(this);
auto *inBuffer = new QBuffer(&data);
auto *outBuffer = new QBuffer(this);
QString fileName;
UnZip::ErrorCode ec;
UnZip uz;
ec = uz.openArchive(inBuffer);
if (ec != UnZip::Ok) {
if (ec != UnZip::Ok)
{
zipDownloadFailed(tr("Failed to open Zip archive: %1.").arg(uz.formatError(ec)));
return;
}
if(uz.fileList().size() != 1)
if (uz.fileList().size() != 1)
{
zipDownloadFailed(tr("Zip extraction failed: the Zip archive doesn't contain exactly one file."));
return;
@ -397,7 +435,8 @@ void LoadSetsPage::readSetsFromByteArray(QByteArray data)
outBuffer->open(QBuffer::ReadWrite);
ec = uz.extractFile(fileName, outBuffer);
if (ec != UnZip::Ok) {
if (ec != UnZip::Ok)
{
zipDownloadFailed(tr("Zip extraction failed: %1.").arg(uz.formatError(ec)));
uz.closeArchive();
return;
@ -429,7 +468,8 @@ void LoadSetsPage::zipDownloadFailed(const QString &message)
progressBar->hide();
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, tr("Error"), message + "<br/>" + tr("Do you want to try to download a fresh copy of the uncompressed file instead?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
reply = static_cast<QMessageBox::StandardButton>(QMessageBox::question(this, tr("Error"), message + "<br/>" + tr("Do you want to try to download a fresh copy of the uncompressed file instead?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes));
if (reply == QMessageBox::Yes)
{
urlRadioButton->setChecked(true);
@ -446,16 +486,17 @@ void LoadSetsPage::importFinished()
progressLabel->hide();
progressBar->hide();
if(watcher.future().result())
if (watcher.future().result())
{
wizard()->next();
} else {
}
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)
SaveSetsPage::SaveSetsPage(QWidget *parent) : OracleWizardPage(parent)
{
defaultPathCheckBox = new QCheckBox(this);
defaultPathCheckBox->setChecked(true);
@ -463,7 +504,7 @@ SaveSetsPage::SaveSetsPage(QWidget *parent)
messageLog = new QTextEdit(this);
messageLog->setReadOnly(true);
QGridLayout *layout = new QGridLayout(this);
auto *layout = new QGridLayout(this);
layout->addWidget(defaultPathCheckBox, 0, 0);
layout->addWidget(messageLog, 1, 0);
@ -472,7 +513,7 @@ SaveSetsPage::SaveSetsPage(QWidget *parent)
void SaveSetsPage::cleanupPage()
{
disconnect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), 0, 0);
disconnect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), nullptr, nullptr);
}
void SaveSetsPage::initializePage()
@ -482,7 +523,9 @@ void SaveSetsPage::initializePage()
connect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), this, SLOT(updateTotalProgress(int, int, const QString &)));
if (!wizard()->importer->startImport())
{
QMessageBox::critical(this, tr("Error"), tr("No set has been imported."));
}
}
void SaveSetsPage::retranslateUi()
@ -496,11 +539,15 @@ void SaveSetsPage::retranslateUi()
void SaveSetsPage::updateTotalProgress(int cardsImported, int /* setIndex */, const QString &setName)
{
if (setName.isEmpty()) {
if (setName.isEmpty())
{
messageLog->append("<b>" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + "</b>");
} else {
}
else
{
messageLog->append(tr("%1: %2 cards imported").arg(setName).arg(cardsImported));
}
messageLog->verticalScrollBar()->setValue(messageLog->verticalScrollBar()->maximum());
}
@ -511,39 +558,51 @@ bool SaveSetsPage::validatePage()
QString windowName = tr("Save card database");
QString fileType = tr("XML; card database (*.xml)");
do {
do
{
QString fileName;
if (defaultPathCheckBox->isChecked())
{
fileName = defaultPath;
}
else
{
fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType);
}
if (fileName.isEmpty())
{
return false;
}
QFileInfo fi(fileName);
QDir fileDir(fi.path());
if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) {
if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath()))
{
return false;
}
if (wizard()->importer->saveToFile(fileName))
{
ok = true;
QMessageBox::information(this,
tr("Success"),
tr("The card database has been saved successfully to\n%1").arg(fileName));
} else {
}
else
{
QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName));;
if (defaultPathCheckBox->isChecked())
{
defaultPathCheckBox->setChecked(false);
}
}
} while (!ok);
return true;
}
LoadTokensPage::LoadTokensPage(QWidget *parent)
: OracleWizardPage(parent), nam(0)
LoadSpoilersPage::LoadSpoilersPage(QWidget *parent) : OracleWizardPage(parent), nam(nullptr)
{
urlLabel = new QLabel(this);
urlLineEdit = new QLineEdit(this);
@ -554,7 +613,152 @@ LoadTokensPage::LoadTokensPage(QWidget *parent)
urlButton = new QPushButton(this);
connect(urlButton, SIGNAL(clicked()), this, SLOT(actRestoreDefaultUrl()));
QGridLayout *layout = new QGridLayout(this);
auto *layout = new QGridLayout(this);
layout->addWidget(urlLabel, 0, 0);
layout->addWidget(urlLineEdit, 0, 1);
layout->addWidget(urlButton, 1, 1, Qt::AlignRight);
layout->addWidget(progressLabel, 2, 0);
layout->addWidget(progressBar, 2, 1);
}
void LoadSpoilersPage::actRestoreDefaultUrl()
{
urlLineEdit->setText(SPOILERS_URL);
}
void LoadSpoilersPage::initializePage()
{
urlLineEdit->setText(wizard()->settings->value("spoilersurl", SPOILERS_URL).toString());
progressLabel->hide();
progressBar->hide();
}
void LoadSpoilersPage::actDownloadProgressSpoilersFile(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 LoadSpoilersPage::actDownloadFinishedSpoilersFile()
{
// Check for server reply
auto *reply = dynamic_cast<QNetworkReply *>(sender());
QNetworkReply::NetworkError 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;
}
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode == 301 || statusCode == 302)
{
QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
qDebug() << "following redirect url:" << redirectUrl.toString();
downloadSpoilersFile(redirectUrl);
reply->deleteLater();
return;
}
progressLabel->hide();
progressBar->hide();
// save spoiler.xml url, but only if the user customized it and download was successful
if (urlLineEdit->text() != QString(SPOILERS_URL))
{
wizard()->settings->setValue("spoilersurl", urlLineEdit->text());
}
else
{
wizard()->settings->remove("spoilersurl");
}
wizard()->setTokensData(reply->readAll());
reply->deleteLater();
wizard()->enableButtons();
setEnabled(true);
progressLabel->hide();
progressBar->hide();
wizard()->next();
}
void LoadSpoilersPage::downloadSpoilersFile(QUrl url)
{
if (!nam)
{
nam = new QNetworkAccessManager(this);
}
QNetworkReply *reply = nam->get(QNetworkRequest(url));
connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSpoilersFile()));
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(actDownloadProgressSpoilersFile(qint64, qint64)));
}
bool LoadSpoilersPage::validatePage()
{
// once the import is finished, we call next(); skip validation
if (wizard()->hasTokensData())
{
return true;
}
QUrl 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);
downloadSpoilersFile(url);
return false;
}
void LoadSpoilersPage::retranslateUi()
{
setTitle(tr("Spoilers source selection"));
setSubTitle(tr("Please specify a spoiler source."));
urlLabel->setText(tr("Download URL:"));
urlButton->setText(tr("Restore default URL"));
}
LoadTokensPage::LoadTokensPage(QWidget *parent) : OracleWizardPage(parent), nam(nullptr)
{
urlLabel = new QLabel(this);
urlLineEdit = new QLineEdit(this);
progressLabel = new QLabel(this);
progressBar = new QProgressBar(this);
urlButton = new QPushButton(this);
connect(urlButton, SIGNAL(clicked()), this, SLOT(actRestoreDefaultUrl()));
auto *layout = new QGridLayout(this);
layout->addWidget(urlLabel, 0, 0);
layout->addWidget(urlLineEdit, 0, 1);
layout->addWidget(urlButton, 1, 1, Qt::AlignRight);
@ -589,11 +793,13 @@ void LoadTokensPage::actRestoreDefaultUrl()
bool LoadTokensPage::validatePage()
{
// once the import is finished, we call next(); skip validation
if(wizard()->hasTokensData())
if (wizard()->hasTokensData())
{
return true;
}
QUrl url = QUrl::fromUserInput(urlLineEdit->text());
if(!url.isValid())
if (!url.isValid())
{
QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid."));
return false;
@ -616,8 +822,10 @@ bool LoadTokensPage::validatePage()
void LoadTokensPage::downloadTokensFile(QUrl url)
{
if(!nam)
if (!nam)
{
nam = new QNetworkAccessManager(this);
}
QNetworkReply *reply = nam->get(QNetworkRequest(url));
connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedTokensFile()));
@ -626,10 +834,10 @@ void LoadTokensPage::downloadTokensFile(QUrl url)
void LoadTokensPage::actDownloadProgressTokensFile(qint64 received, qint64 total)
{
if(total > 0)
if (total > 0)
{
progressBar->setMaximum(total);
progressBar->setValue(received);
progressBar->setMaximum(static_cast<int>(total));
progressBar->setValue(static_cast<int>(received));
}
progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / (1024 * 1024)));
}
@ -637,9 +845,10 @@ void LoadTokensPage::actDownloadProgressTokensFile(qint64 received, qint64 total
void LoadTokensPage::actDownloadFinishedTokensFile()
{
// check for a reply
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
auto *reply = dynamic_cast<QNetworkReply *>(sender());
QNetworkReply::NetworkError errorCode = reply->error();
if (errorCode != QNetworkReply::NoError) {
if (errorCode != QNetworkReply::NoError)
{
QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString()));
wizard()->enableButtons();
@ -650,7 +859,8 @@ void LoadTokensPage::actDownloadFinishedTokensFile()
}
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode == 301 || statusCode == 302) {
if (statusCode == 301 || statusCode == 302)
{
QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
qDebug() << "following redirect url:" << redirectUrl.toString();
downloadTokensFile(redirectUrl);
@ -662,10 +872,14 @@ void LoadTokensPage::actDownloadFinishedTokensFile()
progressBar->hide();
// save tokens.xml url, but only if the user customized it and download was successfull
if(urlLineEdit->text() != QString(TOKENS_URL))
if (urlLineEdit->text() != QString(TOKENS_URL))
{
wizard()->settings->setValue("tokensurl", urlLineEdit->text());
}
else
{
wizard()->settings->remove("tokensurl");
}
wizard()->setTokensData(reply->readAll());
reply->deleteLater();
@ -678,13 +892,81 @@ void LoadTokensPage::actDownloadFinishedTokensFile()
wizard()->next();
}
SaveTokensPage::SaveTokensPage(QWidget *parent)
: OracleWizardPage(parent)
SaveSpoilersPage::SaveSpoilersPage(QWidget *parent) : OracleWizardPage(parent)
{
defaultPathCheckBox = new QCheckBox(this);
defaultPathCheckBox->setChecked(true);
QGridLayout *layout = new QGridLayout(this);
auto *layout = new QGridLayout(this);
layout->addWidget(defaultPathCheckBox, 0, 0);
setLayout(layout);
}
void SaveSpoilersPage::retranslateUi()
{
setTitle(tr("Spoilers imported"));
setSubTitle(tr("The spoilers file has been imported. "
"Press \"Save\" to save the imported spoilers to the Cockatrice card database."));
defaultPathCheckBox->setText(tr("Save to the default path (recommended)"));
}
bool SaveSpoilersPage::validatePage()
{
bool ok = false;
QString defaultPath = settingsCache->getSpoilerCardDatabasePath();
QString windowName = tr("Save spoiler database");
QString fileType = tr("XML; card database (*.xml)");
do
{
QString fileName;
if (defaultPathCheckBox->isChecked())
{
fileName = defaultPath;
}
else
{
fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType);
}
if (fileName.isEmpty())
{
return false;
}
QFileInfo fi(fileName);
QDir fileDir(fi.path());
if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath()))
{
return false;
}
if (wizard()->saveTokensToFile(fileName))
{
ok = true;
}
else
{
QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName));;
if (defaultPathCheckBox->isChecked())
{
defaultPathCheckBox->setChecked(false);
}
}
} while (!ok);
return true;
}
SaveTokensPage::SaveTokensPage(QWidget *parent) : OracleWizardPage(parent)
{
defaultPathCheckBox = new QCheckBox(this);
defaultPathCheckBox->setChecked(true);
auto *layout = new QGridLayout(this);
layout->addWidget(defaultPathCheckBox, 0, 0);
setLayout(layout);
@ -706,33 +988,47 @@ bool SaveTokensPage::validatePage()
QString windowName = tr("Save token database");
QString fileType = tr("XML; token database (*.xml)");
do {
do
{
QString fileName;
if (defaultPathCheckBox->isChecked())
{
fileName = defaultPath;
}
else
{
fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType);
}
if (fileName.isEmpty())
{
return false;
}
QFileInfo fi(fileName);
QDir fileDir(fi.path());
if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) {
if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath()))
{
return false;
}
if (wizard()->saveTokensToFile(fileName))
{
ok = true;
QMessageBox::information(this,
tr("Success"),
tr("The token database has been saved successfully to\n%1").arg(fileName));
} else {
}
else
{
QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName));;
if (defaultPathCheckBox->isChecked())
{
defaultPathCheckBox->setChecked(false);
}
}
} while (!ok);
return true;
}
}

View file

@ -4,6 +4,7 @@
#include <QWizard>
#include <QFutureWatcher>
#include <QFuture>
#include <utility>
class QCheckBox;
class QGroupBox;
@ -20,139 +21,195 @@ class QSettings;
class OracleWizard : public QWizard
{
Q_OBJECT
public:
OracleWizard(QWidget *parent = 0);
void accept();
void enableButtons();
void disableButtons();
void retranslateUi();
void setTokensData(QByteArray _tokensData) { tokensData = _tokensData; }
bool hasTokensData() { return !tokensData.isEmpty(); }
bool saveTokensToFile(const QString & fileName);
public:
OracleImporter *importer;
QSettings * settings;
private slots:
void updateLanguage();
private:
QStringList findQmFiles();
QString languageName(const QString &qmFile);
QByteArray tokensData;
protected:
void changeEvent(QEvent *event);
};
Q_OBJECT
public:
explicit OracleWizard(QWidget *parent = nullptr);
void accept() override;
void enableButtons();
void disableButtons();
void retranslateUi();
void setTokensData(QByteArray _tokensData) { tokensData = std::move(_tokensData); }
bool hasTokensData() { return !tokensData.isEmpty(); }
bool saveTokensToFile(const QString & fileName);
public:
OracleImporter *importer;
QSettings *settings;
private slots:
void updateLanguage();
private:
QByteArray tokensData;
protected:
void changeEvent(QEvent *event) override;
};
class OracleWizardPage : public QWizardPage
{
Q_OBJECT
public:
OracleWizardPage(QWidget *parent = 0): QWizardPage(parent) {};
virtual void retranslateUi() = 0;
protected:
inline OracleWizard *wizard() { return (OracleWizard*) QWizardPage::wizard(); };
Q_OBJECT
public:
explicit OracleWizardPage(QWidget *parent = nullptr): QWizardPage(parent) {};
virtual void retranslateUi() = 0;
protected:
inline OracleWizard *wizard() { return (OracleWizard*) QWizardPage::wizard(); };
};
class IntroPage : public OracleWizardPage
{
Q_OBJECT
public:
IntroPage(QWidget *parent = 0);
void retranslateUi();
private:
QStringList findQmFiles();
QString languageName(const QString &qmFile);
private:
QLabel *label, *languageLabel, *versionLabel;
QComboBox *languageBox;
private slots:
void languageBoxChanged(int index);
Q_OBJECT
public:
explicit IntroPage(QWidget *parent = nullptr);
void retranslateUi() override;
private:
QStringList findQmFiles();
QString languageName(const QString &qmFile);
private:
QLabel *label, *languageLabel, *versionLabel;
QComboBox *languageBox;
private slots:
void languageBoxChanged(int index);
};
class LoadSetsPage : public OracleWizardPage
{
Q_OBJECT
public:
LoadSetsPage(QWidget *parent = 0);
void retranslateUi();
protected:
void initializePage();
bool validatePage();
void readSetsFromByteArray(QByteArray data);
void downloadSetsFile(QUrl url);
private:
QRadioButton *urlRadioButton;
QRadioButton *fileRadioButton;
QLineEdit *urlLineEdit;
QLineEdit *fileLineEdit;
QPushButton *urlButton;
QPushButton *fileButton;
QLabel *progressLabel;
QProgressBar * progressBar;
public:
explicit LoadSetsPage(QWidget *parent = nullptr);
void retranslateUi() override;
QNetworkAccessManager *nam;
QFutureWatcher<bool> watcher;
QFuture<bool> future;
private slots:
void actLoadSetsFile();
void actRestoreDefaultUrl();
void actDownloadProgressSetsFile(qint64 received, qint64 total);
void actDownloadFinishedSetsFile();
void importFinished();
void zipDownloadFailed(const QString &message);
protected:
void initializePage() override;
bool validatePage() override;
void readSetsFromByteArray(QByteArray data);
void downloadSetsFile(QUrl url);
private:
QRadioButton *urlRadioButton;
QRadioButton *fileRadioButton;
QLineEdit *urlLineEdit;
QLineEdit *fileLineEdit;
QPushButton *urlButton;
QPushButton *fileButton;
QLabel *progressLabel;
QProgressBar *progressBar;
QNetworkAccessManager *nam;
QFutureWatcher<bool> watcher;
QFuture<bool> future;
private slots:
void actLoadSetsFile();
void actRestoreDefaultUrl();
void actDownloadProgressSetsFile(qint64 received, qint64 total);
void actDownloadFinishedSetsFile();
void importFinished();
void zipDownloadFailed(const QString &message);
};
class SaveSetsPage : public OracleWizardPage
{
Q_OBJECT
public:
SaveSetsPage(QWidget *parent = 0);
void retranslateUi();
private:
QTextEdit *messageLog;
QCheckBox * defaultPathCheckBox;
protected:
void initializePage();
void cleanupPage();
bool validatePage();
private slots:
void updateTotalProgress(int cardsImported, int setIndex, const QString &setName);
Q_OBJECT
public:
explicit SaveSetsPage(QWidget *parent = nullptr);
void retranslateUi() override;
private:
QTextEdit *messageLog;
QCheckBox *defaultPathCheckBox;
protected:
void initializePage() override;
void cleanupPage() override;
bool validatePage() override;
private slots:
void updateTotalProgress(int cardsImported, int setIndex, const QString &setName);
};
class LoadSpoilersPage : public OracleWizardPage
{
Q_OBJECT
public:
explicit LoadSpoilersPage(QWidget *parent = nullptr);
void retranslateUi() override;
private:
QLabel *urlLabel;
QLineEdit *urlLineEdit;
QPushButton *urlButton;
QLabel *progressLabel;
QProgressBar *progressBar;
QNetworkAccessManager *nam;
private slots:
void actRestoreDefaultUrl();
void actDownloadProgressSpoilersFile(qint64 received, qint64 total);
void actDownloadFinishedSpoilersFile();
protected:
void initializePage() override;
bool validatePage() override;
void downloadSpoilersFile(QUrl url);
};
class SaveSpoilersPage : public OracleWizardPage
{
Q_OBJECT
public:
explicit SaveSpoilersPage(QWidget *parent = nullptr);
void retranslateUi() override;
private:
QCheckBox *defaultPathCheckBox;
protected:
bool validatePage() override;
};
class LoadTokensPage : public OracleWizardPage
{
Q_OBJECT
public:
LoadTokensPage(QWidget *parent = 0);
void retranslateUi();
protected:
void initializePage();
bool validatePage();
void downloadTokensFile(QUrl url);
private:
QLabel *urlLabel;
QLineEdit *urlLineEdit;
QPushButton *urlButton;
QLabel *progressLabel;
QProgressBar * progressBar;
Q_OBJECT
public:
explicit LoadTokensPage(QWidget *parent = nullptr);
void retranslateUi() override;
QNetworkAccessManager *nam;
private slots:
void actRestoreDefaultUrl();
void actDownloadProgressTokensFile(qint64 received, qint64 total);
void actDownloadFinishedTokensFile();
protected:
void initializePage() override;
bool validatePage() override;
void downloadTokensFile(QUrl url);
private:
QLabel *urlLabel;
QLineEdit *urlLineEdit;
QPushButton *urlButton;
QLabel *progressLabel;
QProgressBar *progressBar;
QNetworkAccessManager *nam;
private slots:
void actRestoreDefaultUrl();
void actDownloadProgressTokensFile(qint64 received, qint64 total);
void actDownloadFinishedTokensFile();
};
class SaveTokensPage : public OracleWizardPage
{
Q_OBJECT
public:
SaveTokensPage(QWidget *parent = 0);
void retranslateUi();
private:
QCheckBox * defaultPathCheckBox;
protected:
bool validatePage();
Q_OBJECT
public:
explicit SaveTokensPage(QWidget *parent = nullptr);
void retranslateUi() override;
private:
QCheckBox *defaultPathCheckBox;
protected:
bool validatePage() override;
};
#endif

View file

@ -14,6 +14,7 @@ SettingsCache::~SettingsCache() { delete cardDatabaseSettings; };
QString SettingsCache::getCustomCardDatabasePath() const { return QString("%1/customsets/").arg(CARDDB_DATADIR); }
QString SettingsCache::getCardDatabasePath() const { return QString("%1/cards.xml").arg(CARDDB_DATADIR); }
QString SettingsCache::getTokenDatabasePath() const { return QString("%1/tokens.xml").arg(CARDDB_DATADIR); }
QString SettingsCache::getSpoilerCardDatabasePath() const { return QString("%1/spoiler.xml").arg(CARDDB_DATADIR); }
CardDatabaseSettings& SettingsCache::cardDatabase() const { return *cardDatabaseSettings; }
SettingsCache *settingsCache;

View file

@ -30,6 +30,7 @@ public:
QString getCustomCardDatabasePath() const;
QString getCardDatabasePath() const;
QString getTokenDatabasePath() const;
QString getSpoilerCardDatabasePath() const;
CardDatabaseSettings& cardDatabase() const;
signals:
void cardDatabasePathChanged();

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<cockatrice_carddatabase version="3">
<cards>
<card>
<name>Fluffy</name>
<set muId="311">CAT</set>
<color>G</color>
<manacost></manacost>
<cmc></cmc>
<type>Token</type>
<pt>0/1</pt>
<tablerow>0</tablerow>
<text></text>
<token>1</token>
</card>
</cards>
</cockatrice_carddatabase>