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,15 +19,17 @@ 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:
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());
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; }
@ -43,14 +46,17 @@ public:
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 SetList : public QList<CardSet *>
{
private:
class KeyCompareFunctor;
public:
public:
void sortByKey();
void guessSortKeys();
void enableAllUnknown();
@ -61,9 +67,10 @@ public:
QStringList getUnknownSetsNames();
};
class CardInfo : public QObject {
class CardInfo : public QObject
{
Q_OBJECT
private:
private:
QString name;
/*
@ -80,13 +87,18 @@ private:
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;
@ -96,8 +108,9 @@ private:
bool cipt;
int tableRow;
QString pixmapCacheKey;
public:
CardInfo(const QString &_name = QString(),
public:
explicit CardInfo(const QString &_name = QString(),
bool _isToken = false,
const QString &_manacost = QString(),
const QString &_cmc = QString(),
@ -117,7 +130,8 @@ public:
QStringMap _collectorNumbers = QStringMap(),
QStringMap _rarities = QStringMap()
);
~CardInfo();
~CardInfo() override;
inline const QString &getName() const { return name; }
inline const QString &getSetsNames() const { return setsNames; }
const QString &getSimpleName() const { return simpleName; }
@ -131,8 +145,8 @@ public:
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 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); }
@ -154,8 +168,8 @@ public:
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 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); }
@ -168,7 +182,8 @@ public:
* less strict name-matching.
*/
static QString simplifyName(const QString &name);
signals:
signals:
void pixmapUpdated();
void cardInfoChanged(CardInfo *card);
};
@ -178,9 +193,10 @@ 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:
protected:
/*
* The cards, indexed by name.
*/
@ -197,7 +213,7 @@ protected:
SetNameMap sets;
LoadStatus loadStatus;
private:
private:
static const int versionNeeded;
void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml);
@ -205,11 +221,18 @@ private:
CardInfo *getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const;
void checkUnknownSets();
void refreshCachedReverseRelatedCards();
public:
QBasicMutex *reloadDatabaseMutex = new QBasicMutex(),
*clearDatabaseMutex = new QBasicMutex(),
*loadFromFileMutex = new QBasicMutex(),
*addCardMutex = new QBasicMutex(),
*removeCardMutex = new QBasicMutex();
public:
static const char* TOKENS_SETNAME;
CardDatabase(QObject *parent = 0);
~CardDatabase();
explicit CardDatabase(QObject *parent = nullptr);
~CardDatabase() override;
void clear();
void addCard(CardInfo *card);
void removeCard(CardInfo *card);
@ -235,11 +258,11 @@ public:
void markAllSetsAsKnown();
void notifyEnabledSetsChanged();
public slots:
public slots:
LoadStatus loadCardDatabases();
private slots:
private slots:
LoadStatus loadCardDatabase(const QString &path);
signals:
signals:
void cardDatabaseLoadingFailed();
void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames);
void cardDatabaseAllNewSetsEnabled();
@ -248,21 +271,23 @@ signals:
void cardRemoved(CardInfo *card);
};
class CardRelation : public QObject {
class CardRelation : public QObject
{
Q_OBJECT
private:
private:
QString name;
bool doesAttach;
bool isCreateAllExclusion;
bool isVariableCount;
int defaultCount;
public:
CardRelation(const QString &_name = QString(),
public:
explicit 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; }
@ -270,5 +295,4 @@ public:
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"
@ -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);
@ -130,7 +131,7 @@ GeneralSettingsPage::GeneralSettingsPage()
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,7 +165,7 @@ GeneralSettingsPage::GeneralSettingsPage()
pathsGroupBox = new QGroupBox;
pathsGroupBox->setLayout(pathsGrid);
QVBoxLayout *mainLayout = new QVBoxLayout;
auto *mainLayout = new QVBoxLayout;
mainLayout->addWidget(personalGroupBox);
mainLayout->addWidget(pathsGroupBox);
@ -333,7 +334,7 @@ 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);
@ -346,7 +347,7 @@ 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);
@ -359,7 +360,7 @@ AppearanceSettingsPage::AppearanceSettingsPage()
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);
@ -380,7 +381,7 @@ 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);
@ -390,7 +391,7 @@ AppearanceSettingsPage::AppearanceSettingsPage()
tableGroupBox = new QGroupBox;
tableGroupBox->setLayout(tableGrid);
QVBoxLayout *mainLayout = new QVBoxLayout;
auto *mainLayout = new QVBoxLayout;
mainLayout->addWidget(themeGroupBox);
mainLayout->addWidget(cardsGroupBox);
mainLayout->addWidget(handGroupBox);
@ -444,7 +445,7 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
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);
@ -457,13 +458,13 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
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);
@ -486,25 +487,123 @@ void UserInterfaceSettingsPage::retranslateUi()
tapAnimationCheckBox.setText(tr("&Tap/untap animation"));
}
DeckEditorSettingsPage::DeckEditorSettingsPage()
{
QGridLayout *generalGrid = new QGridLayout;
auto *lpGeneralGrid = new QGridLayout;
auto *lpSpoilerGrid = new QGridLayout;
generalGrid->addWidget(new QLabel(tr("Nothing is here... yet")), 0, 0);
mcDownloadSpoilersCheckBox.setChecked(settingsCache->getDownloadSpoilersStatus());
generalGroupBox = new QGroupBox;
generalGroupBox->setLayout(generalGrid);
mpSpoilerSavePathLineEdit = new QLineEdit(settingsCache->getSpoilerCardDatabasePath());
mpSpoilerSavePathLineEdit->setReadOnly(true);
mpSpoilerPathButton = new QPushButton("...");
connect(mpSpoilerPathButton, SIGNAL(clicked()), this, SLOT(spoilerPathButtonClicked()));
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(generalGroupBox);
updateNowButton = new QPushButton(tr("Update Spoilers"));
connect(updateNowButton, SIGNAL(clicked()), this, SLOT(updateSpoilers()));
setLayout(mainLayout);
// 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()
@ -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);
@ -564,7 +663,7 @@ MessagesSettingsPage::MessagesSettingsPage()
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);
@ -586,19 +685,19 @@ 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);
@ -609,7 +708,8 @@ MessagesSettingsPage::MessagesSettingsPage()
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() {
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,11 +855,13 @@ 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"));
@ -762,8 +869,7 @@ void SoundSettingsPage::retranslateUi() {
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);
@ -791,14 +897,14 @@ DlgSettings::DlgSettings(QWidget *parent)
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);
@ -857,7 +963,8 @@ void DlgSettings::changePage(QListWidgetItem *current, QListWidgetItem *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,7 +990,8 @@ 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) {
switch(loadStatus)
{
case Ok:
showLoadError = false;
break;
@ -925,23 +1033,36 @@ void DlgSettings::closeEvent(QCloseEvent *event)
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();
}
@ -962,4 +1083,3 @@ void DlgSettings::retranslateUi()
contentsWidget->reset();
}

View file

@ -26,17 +26,20 @@ class QSpinBox;
class QSlider;
class QSpinBox;
class AbstractSettingsPage : public QWidget {
public:
class AbstractSettingsPage : public QWidget
{
public:
virtual void retranslateUi() = 0;
};
class GeneralSettingsPage : public AbstractSettingsPage {
class GeneralSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
public:
public:
GeneralSettingsPage();
void retranslateUi();
private slots:
void retranslateUi() override;
private slots:
void deckPathButtonClicked();
void replaysPathButtonClicked();
void picsPathButtonClicked();
@ -47,7 +50,8 @@ private slots:
void setEnabledStatus(bool);
void defaultUrlRestoreButtonClicked();
void fallbackUrlRestoreButtonClicked();
private:
private:
QStringList findQmFiles();
QString languageName(const QString &qmFile);
QLineEdit *deckPathEdit;
@ -80,11 +84,13 @@ private:
QPushButton fallbackUrlRestoreButton;
};
class AppearanceSettingsPage : public AbstractSettingsPage {
class AppearanceSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
private slots:
private slots:
void themeBoxChanged(int index);
private:
private:
QLabel themeLabel;
QComboBox themeBox;
QLabel minPlayersForMultiColumnLayoutLabel;
@ -100,16 +106,19 @@ private:
QGroupBox *tableGroupBox;
QSpinBox minPlayersForMultiColumnLayoutEdit;
QSpinBox maxFontSizeForCardsEdit;
public:
public:
AppearanceSettingsPage();
void retranslateUi();
void retranslateUi() override;
};
class UserInterfaceSettingsPage : public AbstractSettingsPage {
class UserInterfaceSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
private slots:
private slots:
void setSpecNotificationEnabled(int);
private:
private:
QCheckBox notificationsEnabledCheckBox;
QCheckBox specNotificationsEnabledCheckBox;
QCheckBox doubleClickToPlayCheckBox;
@ -119,35 +128,54 @@ private:
QGroupBox *generalGroupBox;
QGroupBox *animationGroupBox;
public:
public:
UserInterfaceSettingsPage();
void retranslateUi();
void retranslateUi() override;
};
class DeckEditorSettingsPage : public AbstractSettingsPage {
class DeckEditorSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
public:
public:
DeckEditorSettingsPage();
void retranslateUi();
private slots:
signals:
private:
QGroupBox *generalGroupBox;
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:
public:
MessagesSettingsPage();
void retranslateUi();
private slots:
void retranslateUi() override;
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:
private:
QListWidget *messageList;
QAction *aAdd;
QAction *aRemove;
@ -175,12 +203,14 @@ private:
void updateHighlightPreview();
};
class SoundSettingsPage : public AbstractSettingsPage {
class SoundSettingsPage : public AbstractSettingsPage
{
Q_OBJECT
public:
public:
SoundSettingsPage();
void retranslateUi();
private:
void retranslateUi() override;
private:
QLabel themeLabel;
QComboBox themeBox;
QGroupBox *soundGroupBox;
@ -189,29 +219,34 @@ private:
QLabel masterVolumeLabel;
QSlider *masterVolumeSlider;
QSpinBox *masterVolumeSpinBox;
private slots:
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);
public:
explicit DlgSettings(QWidget *parent = nullptr);
void setTab(int index);
private slots:
private slots:
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
void updateLanguage();
private:
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);
protected:
void changeEvent(QEvent *event) override;
void closeEvent(QCloseEvent *event) override;
};
#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()));
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);
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,32 +988,46 @@ 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;
@ -21,67 +22,74 @@ class QSettings;
class OracleWizard : public QWizard
{
Q_OBJECT
public:
OracleWizard(QWidget *parent = 0);
void accept();
public:
explicit OracleWizard(QWidget *parent = nullptr);
void accept() override;
void enableButtons();
void disableButtons();
void retranslateUi();
void setTokensData(QByteArray _tokensData) { tokensData = _tokensData; }
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:
QStringList findQmFiles();
QString languageName(const QString &qmFile);
QByteArray tokensData;
protected:
void changeEvent(QEvent *event);
};
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) {};
public:
explicit OracleWizardPage(QWidget *parent = nullptr): QWizardPage(parent) {};
virtual void retranslateUi() = 0;
protected:
protected:
inline OracleWizard *wizard() { return (OracleWizard*) QWizardPage::wizard(); };
};
class IntroPage : public OracleWizardPage
{
Q_OBJECT
public:
IntroPage(QWidget *parent = 0);
void retranslateUi();
private:
public:
explicit IntroPage(QWidget *parent = nullptr);
void retranslateUi() override;
private:
QStringList findQmFiles();
QString languageName(const QString &qmFile);
private:
private:
QLabel *label, *languageLabel, *versionLabel;
QComboBox *languageBox;
private slots:
private slots:
void languageBoxChanged(int index);
};
class LoadSetsPage : public OracleWizardPage
{
Q_OBJECT
public:
LoadSetsPage(QWidget *parent = 0);
void retranslateUi();
protected:
void initializePage();
bool validatePage();
public:
explicit LoadSetsPage(QWidget *parent = nullptr);
void retranslateUi() override;
protected:
void initializePage() override;
bool validatePage() override;
void readSetsFromByteArray(QByteArray data);
void downloadSetsFile(QUrl url);
private:
private:
QRadioButton *urlRadioButton;
QRadioButton *fileRadioButton;
QLineEdit *urlLineEdit;
@ -89,12 +97,13 @@ private:
QPushButton *urlButton;
QPushButton *fileButton;
QLabel *progressLabel;
QProgressBar * progressBar;
QProgressBar *progressBar;
QNetworkAccessManager *nam;
QFutureWatcher<bool> watcher;
QFuture<bool> future;
private slots:
private slots:
void actLoadSetsFile();
void actRestoreDefaultUrl();
void actDownloadProgressSetsFile(qint64 received, qint64 total);
@ -106,39 +115,84 @@ private slots:
class SaveSetsPage : public OracleWizardPage
{
Q_OBJECT
public:
SaveSetsPage(QWidget *parent = 0);
void retranslateUi();
private:
public:
explicit SaveSetsPage(QWidget *parent = nullptr);
void retranslateUi() override;
private:
QTextEdit *messageLog;
QCheckBox * defaultPathCheckBox;
protected:
void initializePage();
void cleanupPage();
bool validatePage();
private slots:
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();
public:
explicit LoadTokensPage(QWidget *parent = nullptr);
void retranslateUi() override;
protected:
void initializePage() override;
bool validatePage() override;
void downloadTokensFile(QUrl url);
private:
private:
QLabel *urlLabel;
QLineEdit *urlLineEdit;
QPushButton *urlButton;
QLabel *progressLabel;
QProgressBar * progressBar;
QProgressBar *progressBar;
QNetworkAccessManager *nam;
private slots:
private slots:
void actRestoreDefaultUrl();
void actDownloadProgressTokensFile(qint64 received, qint64 total);
void actDownloadFinishedTokensFile();
@ -147,12 +201,15 @@ private slots:
class SaveTokensPage : public OracleWizardPage
{
Q_OBJECT
public:
SaveTokensPage(QWidget *parent = 0);
void retranslateUi();
private:
QCheckBox * defaultPathCheckBox;
protected:
bool validatePage();
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>