diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 36001a566..3bb4de5df 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -20,6 +20,7 @@ set(oracle_SOURCES src/main.cpp src/oraclewizard.cpp src/oracleimporter.cpp + src/pages.cpp src/pagetemplates.cpp src/parsehelpers.cpp src/qt-json/json.cpp diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index 6b463d5de..46e4722c6 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -3,55 +3,19 @@ #include "client/settings/cache_settings.h" #include "main.h" #include "oracleimporter.h" -#include "version_string.h" +#include "pages.h" +#include "pagetemplates.h" -#include -#include #include -#include -#include #include #include -#include #include -#include -#include #include -#include #include -#include #include -#include -#include #include #include -#ifdef HAS_LZMA -#include "lzma/decompress.h" -#endif - -#ifdef HAS_ZLIB -#include "zip/unzip.h" -#endif - -#define ZIP_SIGNATURE "PK" -// Xz stream header: 0xFD + "7zXZ" -#define XZ_SIGNATURE "\xFD\x37\x7A\x58\x5A" -#define MTGJSON_V4_URL_COMPONENT "mtgjson.com/files/" -#define ALLSETS_URL_FALLBACK "https://www.mtgjson.com/api/v5/AllPrintings.json" -#define MTGJSON_VERSION_URL "https://www.mtgjson.com/api/v5/Meta.json" - -#ifdef HAS_LZMA -#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.xz" -#elif defined(HAS_ZLIB) -#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.zip" -#else -#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json" -#endif - -#define TOKENS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml" -#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml" - OracleWizard::OracleWizard(QWidget *parent) : QWizard(parent) { // define a dummy context that will be used where needed @@ -142,669 +106,3 @@ bool OracleWizard::saveTokensToFile(const QString &fileName) file.close(); return true; } - -IntroPage::IntroPage(QWidget *parent) : OracleWizardPage(parent) -{ - label = new QLabel(this); - label->setWordWrap(true); - - languageLabel = new QLabel(this); - versionLabel = new QLabel(this); - languageBox = new QComboBox(this); - - QStringList languageCodes = findQmFiles(); - for (const QString &code : languageCodes) { - QString langName = languageName(code); - languageBox->addItem(langName, code); - } - - QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME); - int index = languageBox->findText(setLanguage, Qt::MatchExactly); - if (index == -1) { - qWarning() << "could not find language" << setLanguage; - } else { - languageBox->setCurrentIndex(index); - } - - connect(languageBox, qOverload(&QComboBox::currentIndexChanged), this, &IntroPage::languageBoxChanged); - - auto *layout = new QGridLayout(this); - layout->addWidget(label, 0, 0, 1, 2); - layout->addWidget(languageLabel, 1, 0); - layout->addWidget(languageBox, 1, 1); - layout->addWidget(versionLabel, 2, 0, 1, 2); - - setLayout(layout); -} - -void IntroPage::initializePage() -{ - if (wizard()->backgroundMode) { - emit readyToContinue(); - } -} - -QStringList IntroPage::findQmFiles() -{ - QDir dir(translationPath); - QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name); - fileNames.replaceInStrings(QRegularExpression(translationPrefix + "_(.*)\\.qm"), "\\1"); - return fileNames; -} - -QString IntroPage::languageName(const QString &lang) -{ - QTranslator qTranslator; - - QString appNameHint = translationPrefix + "_" + lang; - bool appTranslationLoaded = qTranslator.load(appNameHint, translationPath); - if (!appTranslationLoaded) { - qDebug() << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" << translationPath; - } - - return qTranslator.translate("i18n", DEFAULT_LANG_NAME); -} - -void IntroPage::languageBoxChanged(int index) -{ - SettingsCache::instance().setLang(languageBox->itemData(index).toString()); -} - -void IntroPage::retranslateUi() -{ - setTitle(tr("Introduction")); - label->setText(tr("This wizard will import the list of sets, cards, and tokens " - "that will be used by Cockatrice.")); - languageLabel->setText(tr("Interface language:")); - versionLabel->setText(tr("Version:") + QString(" %1").arg(VERSION_STRING)); -} - -void OutroPage::retranslateUi() -{ - setTitle(tr("Finished")); - setSubTitle(tr("The wizard has finished.") + "
" + - tr("You can now start using Cockatrice with the newly updated cards.") + "

" + - tr("If the card databases don't reload automatically, restart the Cockatrice client.")); -} - -void OutroPage::initializePage() -{ - if (wizard()->backgroundMode) { - wizard()->accept(); - exit(0); - } -} - -LoadSetsPage::LoadSetsPage(QWidget *parent) : OracleWizardPage(parent) -{ - urlRadioButton = new QRadioButton(this); - fileRadioButton = new QRadioButton(this); - - urlLineEdit = new QLineEdit(this); - fileLineEdit = new QLineEdit(this); - - progressLabel = new QLabel(this); - progressBar = new QProgressBar(this); - - urlRadioButton->setChecked(true); - - urlButton = new QPushButton(this); - connect(urlButton, &QPushButton::clicked, this, &LoadSetsPage::actRestoreDefaultUrl); - - fileButton = new QPushButton(this); - connect(fileButton, &QPushButton::clicked, this, &LoadSetsPage::actLoadSetsFile); - - auto *layout = new QGridLayout(this); - layout->addWidget(urlRadioButton, 0, 0); - layout->addWidget(urlLineEdit, 0, 1); - layout->addWidget(urlButton, 1, 1, Qt::AlignRight); - layout->addWidget(fileRadioButton, 2, 0); - layout->addWidget(fileLineEdit, 2, 1); - layout->addWidget(fileButton, 3, 1, Qt::AlignRight); - layout->addWidget(progressLabel, 4, 0); - layout->addWidget(progressBar, 4, 1); - - connect(&watcher, &QFutureWatcher::finished, this, &LoadSetsPage::importFinished); - - setLayout(layout); -} - -void LoadSetsPage::initializePage() -{ - urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString()); - - progressLabel->hide(); - progressBar->hide(); - - if (wizard()->backgroundMode) { - if (isEnabled()) { - validatePage(); - } - } -} - -void LoadSetsPage::retranslateUi() -{ - setTitle(tr("Source selection")); - setSubTitle(tr("Please specify a compatible source for the list of sets and cards. " - "You can specify a URL address that will be downloaded or " - "use an existing file from your computer.")); - - urlRadioButton->setText(tr("Download URL:")); - fileRadioButton->setText(tr("Local file:")); - urlButton->setText(tr("Restore default URL")); - fileButton->setText(tr("Choose file...")); -} - -void LoadSetsPage::actRestoreDefaultUrl() -{ - urlLineEdit->setText(ALLSETS_URL); -} - -void LoadSetsPage::actLoadSetsFile() -{ - QFileDialog dialog(this, tr("Load sets file")); - dialog.setFileMode(QFileDialog::ExistingFile); - - QString extensions = "*.json *.xml"; -#ifdef HAS_ZLIB - extensions += " *.zip"; -#endif -#ifdef HAS_LZMA - extensions += " *.xz"; -#endif - dialog.setNameFilter(tr("Sets file (%1)").arg(extensions)); - - if (!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text())) { - dialog.selectFile(fileLineEdit->text()); - } - - if (!dialog.exec()) { - return; - } - - fileLineEdit->setText(dialog.selectedFiles().at(0)); -} - -bool LoadSetsPage::validatePage() -{ - // once the import is finished, we call next(); skip validation - if (wizard()->downloadedPlainXml || wizard()->importer->getSets().count() > 0) { - return true; - } - - // else, try to import sets - if (urlRadioButton->isChecked()) { - // If a user attempts to download from V4, redirect them to V5 - if (urlLineEdit->text().contains(MTGJSON_V4_URL_COMPONENT)) { - actRestoreDefaultUrl(); - } - - const auto url = QUrl::fromUserInput(urlLineEdit->text()); - - if (!url.isValid()) { - QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); - return false; - } - - progressLabel->setText(tr("Downloading (0MB)")); - // show an infinite progressbar - progressBar->setMaximum(0); - progressBar->setMinimum(0); - progressBar->setValue(0); - progressLabel->show(); - progressBar->show(); - - wizard()->disableButtons(); - setEnabled(false); - - downloadSetsFile(url); - } else if (fileRadioButton->isChecked()) { - QFile setsFile(fileLineEdit->text()); - if (!setsFile.exists()) { - QMessageBox::critical(this, tr("Error"), tr("Please choose a file.")); - return false; - } - - if (!setsFile.open(QIODevice::ReadOnly)) { - QMessageBox::critical(nullptr, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text())); - return false; - } - - wizard()->disableButtons(); - setEnabled(false); - - wizard()->setCardSourceUrl(setsFile.fileName()); - wizard()->setCardSourceVersion("unknown"); - - readSetsFromByteArray(setsFile.readAll()); - } - - return false; -} - -void LoadSetsPage::downloadSetsFile(const QUrl &url) -{ - wizard()->setCardSourceVersion("unknown"); - - const auto urlString = url.toString(); - if (urlString == ALLSETS_URL || urlString == ALLSETS_URL_FALLBACK) { - const auto versionUrl = QUrl::fromUserInput(MTGJSON_VERSION_URL); - auto *versionReply = wizard()->nam->get(QNetworkRequest(versionUrl)); - connect(versionReply, &QNetworkReply::finished, [this, versionReply]() { - if (versionReply->error() == QNetworkReply::NoError) { - auto jsonData = versionReply->readAll(); - QJsonParseError jsonError{}; - auto jsonResponse = QJsonDocument::fromJson(jsonData, &jsonError); - - if (jsonError.error == QJsonParseError::NoError) { - const auto jsonMap = jsonResponse.toVariant().toMap(); - - auto versionString = jsonMap.value("meta").toMap().value("version").toString(); - if (versionString.isEmpty()) { - versionString = "unknown"; - } - wizard()->setCardSourceVersion(versionString); - } - } - - versionReply->deleteLater(); - }); - } - - wizard()->setCardSourceUrl(url.toString()); - - auto *reply = wizard()->nam->get(QNetworkRequest(url)); - - connect(reply, &QNetworkReply::finished, this, &LoadSetsPage::actDownloadFinishedSetsFile); - connect(reply, &QNetworkReply::downloadProgress, this, &LoadSetsPage::actDownloadProgressSetsFile); -} - -void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total) -{ - if (total > 0) { - progressBar->setMaximum(static_cast(total)); - progressBar->setValue(static_cast(received)); - } - progressLabel->setText(tr("Downloading (%1MB)").arg((int)received / (1024 * 1024))); -} - -void LoadSetsPage::actDownloadFinishedSetsFile() -{ - // check for a reply - auto *reply = dynamic_cast(sender()); - auto errorCode = reply->error(); - if (errorCode != QNetworkReply::NoError) { - QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); - - wizard()->enableButtons(); - setEnabled(true); - - reply->deleteLater(); - return; - } - - auto statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (statusCode == 301 || statusCode == 302) { - const auto redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); - qDebug() << "following redirect url:" << redirectUrl.toString(); - downloadSetsFile(redirectUrl); - reply->deleteLater(); - return; - } - - progressLabel->hide(); - progressBar->hide(); - - // save AllPrintings.json url, but only if the user customized it and download was successful - if (urlLineEdit->text() != QString(ALLSETS_URL)) { - wizard()->settings->setValue("allsetsurl", urlLineEdit->text()); - } else { - wizard()->settings->remove("allsetsurl"); - } - - readSetsFromByteArray(reply->readAll()); - reply->deleteLater(); -} - -void LoadSetsPage::readSetsFromByteArray(QByteArray _data) -{ - // show an infinite progressbar - progressBar->setMaximum(0); - progressBar->setMinimum(0); - progressBar->setValue(0); - progressLabel->setText(tr("Parsing file")); - progressLabel->show(); - progressBar->show(); - - wizard()->downloadedPlainXml = false; - wizard()->xmlData.clear(); - readSetsFromByteArrayRef(_data); -} - -void LoadSetsPage::readSetsFromByteArrayRef(QByteArray &_data) -{ - // unzip the file if needed - if (_data.startsWith(XZ_SIGNATURE)) { -#ifdef HAS_LZMA - // zipped file - auto *inBuffer = new QBuffer(&_data); - auto newData = QByteArray(); - auto *outBuffer = new QBuffer(&newData); - inBuffer->open(QBuffer::ReadOnly); - outBuffer->open(QBuffer::WriteOnly); - XzDecompressor xz; - if (!xz.decompress(inBuffer, outBuffer)) { - zipDownloadFailed(tr("Xz extraction failed.")); - return; - } - _data.clear(); - readSetsFromByteArrayRef(newData); - return; -#else - zipDownloadFailed(tr("Sorry, this version of Oracle does not support xz compressed files.")); - - wizard()->enableButtons(); - setEnabled(true); - progressLabel->hide(); - progressBar->hide(); - return; -#endif - } else if (_data.startsWith(ZIP_SIGNATURE)) { -#ifdef HAS_ZLIB - // zipped file - auto *inBuffer = new QBuffer(&_data); - auto newData = QByteArray(); - auto *outBuffer = new QBuffer(&newData); - QString fileName; - UnZip::ErrorCode ec; - UnZip uz; - - ec = uz.openArchive(inBuffer); - if (ec != UnZip::Ok) { - zipDownloadFailed(tr("Failed to open Zip archive: %1.").arg(uz.formatError(ec))); - return; - } - - if (uz.fileList().size() != 1) { - zipDownloadFailed(tr("Zip extraction failed: the Zip archive doesn't contain exactly one file.")); - return; - } - fileName = uz.fileList().at(0); - - outBuffer->open(QBuffer::ReadWrite); - ec = uz.extractFile(fileName, outBuffer); - if (ec != UnZip::Ok) { - zipDownloadFailed(tr("Zip extraction failed: %1.").arg(uz.formatError(ec))); - uz.closeArchive(); - return; - } - _data.clear(); - readSetsFromByteArrayRef(newData); - return; -#else - zipDownloadFailed(tr("Sorry, this version of Oracle does not support zipped files.")); - - wizard()->enableButtons(); - setEnabled(true); - progressLabel->hide(); - progressBar->hide(); - return; -#endif - } else if (_data.startsWith("{")) { - // Start the computation. - jsonData = std::move(_data); - future = QtConcurrent::run([this] { return wizard()->importer->readSetsFromByteArray(std::move(jsonData)); }); - watcher.setFuture(future); - } else if (_data.startsWith("<")) { - // save xml file and don't do any processing - wizard()->downloadedPlainXml = true; - wizard()->xmlData = std::move(_data); - importFinished(); - } else { - wizard()->enableButtons(); - setEnabled(true); - progressLabel->hide(); - progressBar->hide(); - QMessageBox::critical(this, tr("Error"), tr("Failed to interpret downloaded data.")); - } -} - -void LoadSetsPage::zipDownloadFailed(const QString &message) -{ - wizard()->enableButtons(); - setEnabled(true); - progressLabel->hide(); - progressBar->hide(); - - QMessageBox::StandardButton reply; - reply = static_cast(QMessageBox::question( - this, tr("Error"), message + "
" + tr("Do you want to download the uncompressed file instead?"), - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)); - - if (reply == QMessageBox::Yes) { - urlRadioButton->setChecked(true); - urlLineEdit->setText(ALLSETS_URL_FALLBACK); - - wizard()->next(); - } -} - -void LoadSetsPage::importFinished() -{ - wizard()->enableButtons(); - setEnabled(true); - progressLabel->hide(); - progressBar->hide(); - - if (wizard()->downloadedPlainXml || watcher.future().result()) { - wizard()->next(); - } else { - QMessageBox::critical(this, tr("Error"), - tr("The file was retrieved successfully, but it does not contain any sets data.")); - } -} - -SaveSetsPage::SaveSetsPage(QWidget *parent) : OracleWizardPage(parent) -{ - pathLabel = new QLabel(this); - saveLabel = new QLabel(this); - - defaultPathCheckBox = new QCheckBox(this); - - messageLog = new QTextEdit(this); - messageLog->setReadOnly(true); - - auto *layout = new QGridLayout(this); - layout->addWidget(messageLog, 0, 0); - layout->addWidget(saveLabel, 1, 0); - layout->addWidget(pathLabel, 2, 0); - layout->addWidget(defaultPathCheckBox, 3, 0); - - setLayout(layout); -} - -void SaveSetsPage::cleanupPage() -{ - wizard()->importer->clear(); - disconnect(wizard()->importer, &OracleImporter::setIndexChanged, nullptr, nullptr); -} - -void SaveSetsPage::initializePage() -{ - messageLog->clear(); - - retranslateUi(); - if (wizard()->downloadedPlainXml) { - messageLog->hide(); - } else { - messageLog->show(); - connect(wizard()->importer, &OracleImporter::setIndexChanged, this, &SaveSetsPage::updateTotalProgress); - - int setsImported = wizard()->importer->startImport(); - - if (setsImported == 0) { - QMessageBox::critical(this, tr("Error"), tr("No set has been imported.")); - } - } - - if (wizard()->backgroundMode) { - emit readyToContinue(); - } -} - -void SaveSetsPage::retranslateUi() -{ - setTitle(tr("Sets imported")); - if (wizard()->downloadedPlainXml) { - setSubTitle(tr("A cockatrice database file of %1 MB has been downloaded.") - .arg(qRound(wizard()->xmlData.size() / 1000000.0))); - } else { - setSubTitle(tr("The following sets have been found:")); - } - - saveLabel->setText(tr("Press \"Save\" to store the imported cards in the Cockatrice database.")); - pathLabel->setText(tr("The card database will be saved at the following location:") + "
" + - SettingsCache::instance().getCardDatabasePath()); - defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)")); - - setButtonText(QWizard::NextButton, tr("&Save")); -} - -void SaveSetsPage::updateTotalProgress(int cardsImported, int /* setIndex */, const QString &setName) -{ - if (setName.isEmpty()) { - messageLog->append("" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + - ""); - } else { - messageLog->append(tr("%1: %2 cards imported").arg(setName).arg(cardsImported)); - } - - messageLog->verticalScrollBar()->setValue(messageLog->verticalScrollBar()->maximum()); -} - -bool SaveSetsPage::validatePage() -{ - QString defaultPath = SettingsCache::instance().getCardDatabasePath(); - QString windowName = tr("Save card database"); - QString fileType = tr("XML; card database (*.xml)"); - - QString fileName; - if (defaultPathCheckBox->isChecked()) { - fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType); - } else { - fileName = defaultPath; - } - - if (fileName.isEmpty()) { - return false; - } - - QFileInfo fi(fileName); - QDir fileDir(fi.path()); - if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) { - return false; - } - - if (wizard()->downloadedPlainXml) { - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) { - qDebug() << "File write (w) failed for" << fileName; - return false; - } - if (file.write(wizard()->xmlData) < 1) { - qDebug() << "File write (w) failed for" << fileName; - return false; - } - wizard()->xmlData.clear(); - } else if (!wizard()->importer->saveToFile(fileName, wizard()->getCardSourceUrl(), - wizard()->getCardSourceVersion())) { - QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName)); - return false; - } - - return true; -} - -void LoadTokensPage::initializePage() -{ - SimpleDownloadFilePage::initializePage(); - - if (wizard()->backgroundMode) { - emit readyToContinue(); - } -} - -QString LoadTokensPage::getDefaultUrl() -{ - return TOKENS_URL; -} - -QString LoadTokensPage::getCustomUrlSettingsKey() -{ - return "tokensurl"; -} - -QString LoadTokensPage::getDefaultSavePath() -{ - return SettingsCache::instance().getTokenDatabasePath(); -} - -QString LoadTokensPage::getWindowTitle() -{ - return tr("Save token database"); -} - -QString LoadTokensPage::getFileType() -{ - return tr("XML; token database (*.xml)"); -} - -void LoadTokensPage::retranslateUi() -{ - setTitle(tr("Tokens import")); - setSubTitle(tr("Please specify a compatible source for token data.")); - - urlLabel->setText(tr("Download URL:")); - urlButton->setText(tr("Restore default URL")); - pathLabel->setText(tr("The token database will be saved at the following location:") + "
" + - SettingsCache::instance().getTokenDatabasePath()); - defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)")); -} - -QString LoadSpoilersPage::getDefaultUrl() -{ - return SPOILERS_URL; -} - -QString LoadSpoilersPage::getCustomUrlSettingsKey() -{ - return "spoilersurl"; -} - -QString LoadSpoilersPage::getDefaultSavePath() -{ - return SettingsCache::instance().getTokenDatabasePath(); -} - -QString LoadSpoilersPage::getWindowTitle() -{ - return tr("Save spoiler database"); -} - -QString LoadSpoilersPage::getFileType() -{ - return tr("XML; spoiler database (*.xml)"); -} - -void LoadSpoilersPage::retranslateUi() -{ - setTitle(tr("Spoilers import")); - setSubTitle(tr("Please specify a compatible source for spoiler data.")); - - urlLabel->setText(tr("Download URL:")); - urlButton->setText(tr("Restore default URL")); - pathLabel->setText(tr("The spoiler database will be saved at the following location:") + "
" + - SettingsCache::instance().getSpoilerCardDatabasePath()); - defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)")); -} diff --git a/oracle/src/oraclewizard.h b/oracle/src/oraclewizard.h index fe0bf1a21..c913094e3 100644 --- a/oracle/src/oraclewizard.h +++ b/oracle/src/oraclewizard.h @@ -1,9 +1,6 @@ #ifndef ORACLEWIZARD_H #define ORACLEWIZARD_H -#include -#include -#include #include #include @@ -20,8 +17,6 @@ class QVBoxLayout; class OracleImporter; class QSettings; -#include "pagetemplates.h" - class OracleWizard : public QWizard { Q_OBJECT @@ -84,133 +79,4 @@ protected: void changeEvent(QEvent *event) override; }; -class IntroPage : public OracleWizardPage -{ - Q_OBJECT -public: - explicit IntroPage(QWidget *parent = nullptr); - void retranslateUi() override; - -private: - QStringList findQmFiles(); - QString languageName(const QString &lang); - -private: - QLabel *label, *languageLabel, *versionLabel; - QComboBox *languageBox; - -private slots: - void languageBoxChanged(int index); - -protected slots: - void initializePage() override; -}; - -class OutroPage : public OracleWizardPage -{ - Q_OBJECT -public: - explicit OutroPage(QWidget * = nullptr) - { - } - void retranslateUi() override; - -protected: - void initializePage() override; -}; - -class LoadSetsPage : public OracleWizardPage -{ - Q_OBJECT -public: - explicit LoadSetsPage(QWidget *parent = nullptr); - void retranslateUi() override; - -protected: - void initializePage() override; - bool validatePage() override; - void readSetsFromByteArray(QByteArray _data); - void readSetsFromByteArrayRef(QByteArray &_data); - void downloadSetsFile(const QUrl &url); - -private: - QRadioButton *urlRadioButton; - QRadioButton *fileRadioButton; - QLineEdit *urlLineEdit; - QLineEdit *fileLineEdit; - QPushButton *urlButton; - QPushButton *fileButton; - QLabel *progressLabel; - QProgressBar *progressBar; - - QFutureWatcher watcher; - QFuture future; - QByteArray jsonData; - -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: - explicit SaveSetsPage(QWidget *parent = nullptr); - void retranslateUi() override; - -private: - QTextEdit *messageLog; - QCheckBox *defaultPathCheckBox; - QLabel *pathLabel; - QLabel *saveLabel; - -protected: - void initializePage() override; - void cleanupPage() override; - bool validatePage() override; - -private slots: - void updateTotalProgress(int cardsImported, int setIndex, const QString &setName); -}; - -class LoadSpoilersPage : public SimpleDownloadFilePage -{ - Q_OBJECT -public: - explicit LoadSpoilersPage(QWidget * = nullptr) - { - } - void retranslateUi() override; - -protected: - QString getDefaultUrl() override; - QString getCustomUrlSettingsKey() override; - QString getDefaultSavePath() override; - QString getWindowTitle() override; - QString getFileType() override; -}; - -class LoadTokensPage : public SimpleDownloadFilePage -{ - Q_OBJECT -public: - explicit LoadTokensPage(QWidget * = nullptr) - { - } - void retranslateUi() override; - -protected: - QString getDefaultUrl() override; - QString getCustomUrlSettingsKey() override; - QString getDefaultSavePath() override; - QString getWindowTitle() override; - QString getFileType() override; - void initializePage() override; -}; - #endif diff --git a/oracle/src/pages.cpp b/oracle/src/pages.cpp new file mode 100644 index 000000000..9b2f5ce7c --- /dev/null +++ b/oracle/src/pages.cpp @@ -0,0 +1,722 @@ +#include "pages.h" + +#include "client/settings/cache_settings.h" +#include "main.h" +#include "oracleimporter.h" +#include "oraclewizard.h" +#include "pages.h" +#include "pagetemplates.h" +#include "version_string.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAS_LZMA +#include "lzma/decompress.h" +#endif + +#ifdef HAS_ZLIB +#include "zip/unzip.h" +#endif + +#define ZIP_SIGNATURE "PK" +// Xz stream header: 0xFD + "7zXZ" +#define XZ_SIGNATURE "\xFD\x37\x7A\x58\x5A" +#define MTGJSON_V4_URL_COMPONENT "mtgjson.com/files/" +#define ALLSETS_URL_FALLBACK "https://www.mtgjson.com/api/v5/AllPrintings.json" +#define MTGJSON_VERSION_URL "https://www.mtgjson.com/api/v5/Meta.json" + +#ifdef HAS_LZMA +#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.xz" +#elif defined(HAS_ZLIB) +#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.zip" +#else +#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json" +#endif + +#define TOKENS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml" +#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml" + +IntroPage::IntroPage(QWidget *parent) : OracleWizardPage(parent) +{ + label = new QLabel(this); + label->setWordWrap(true); + + languageLabel = new QLabel(this); + versionLabel = new QLabel(this); + languageBox = new QComboBox(this); + + QStringList languageCodes = findQmFiles(); + for (const QString &code : languageCodes) { + QString langName = languageName(code); + languageBox->addItem(langName, code); + } + + QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME); + int index = languageBox->findText(setLanguage, Qt::MatchExactly); + if (index == -1) { + qWarning() << "could not find language" << setLanguage; + } else { + languageBox->setCurrentIndex(index); + } + + connect(languageBox, qOverload(&QComboBox::currentIndexChanged), this, &IntroPage::languageBoxChanged); + + auto *layout = new QGridLayout(this); + layout->addWidget(label, 0, 0, 1, 2); + layout->addWidget(languageLabel, 1, 0); + layout->addWidget(languageBox, 1, 1); + layout->addWidget(versionLabel, 2, 0, 1, 2); + + setLayout(layout); +} + +void IntroPage::initializePage() +{ + if (wizard()->backgroundMode) { + emit readyToContinue(); + } +} + +QStringList IntroPage::findQmFiles() +{ + QDir dir(translationPath); + QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name); + fileNames.replaceInStrings(QRegularExpression(translationPrefix + "_(.*)\\.qm"), "\\1"); + return fileNames; +} + +QString IntroPage::languageName(const QString &lang) +{ + QTranslator qTranslator; + + QString appNameHint = translationPrefix + "_" + lang; + bool appTranslationLoaded = qTranslator.load(appNameHint, translationPath); + if (!appTranslationLoaded) { + qDebug() << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" << translationPath; + } + + return qTranslator.translate("i18n", DEFAULT_LANG_NAME); +} + +void IntroPage::languageBoxChanged(int index) +{ + SettingsCache::instance().setLang(languageBox->itemData(index).toString()); +} + +void IntroPage::retranslateUi() +{ + setTitle(tr("Introduction")); + label->setText(tr("This wizard will import the list of sets, cards, and tokens " + "that will be used by Cockatrice.")); + languageLabel->setText(tr("Interface language:")); + versionLabel->setText(tr("Version:") + QString(" %1").arg(VERSION_STRING)); +} + +void OutroPage::retranslateUi() +{ + setTitle(tr("Finished")); + setSubTitle(tr("The wizard has finished.") + "
" + + tr("You can now start using Cockatrice with the newly updated cards.") + "

" + + tr("If the card databases don't reload automatically, restart the Cockatrice client.")); +} + +void OutroPage::initializePage() +{ + if (wizard()->backgroundMode) { + wizard()->accept(); + exit(0); + } +} + +LoadSetsPage::LoadSetsPage(QWidget *parent) : OracleWizardPage(parent) +{ + urlRadioButton = new QRadioButton(this); + fileRadioButton = new QRadioButton(this); + + urlLineEdit = new QLineEdit(this); + fileLineEdit = new QLineEdit(this); + + progressLabel = new QLabel(this); + progressBar = new QProgressBar(this); + + urlRadioButton->setChecked(true); + + urlButton = new QPushButton(this); + connect(urlButton, &QPushButton::clicked, this, &LoadSetsPage::actRestoreDefaultUrl); + + fileButton = new QPushButton(this); + connect(fileButton, &QPushButton::clicked, this, &LoadSetsPage::actLoadSetsFile); + + auto *layout = new QGridLayout(this); + layout->addWidget(urlRadioButton, 0, 0); + layout->addWidget(urlLineEdit, 0, 1); + layout->addWidget(urlButton, 1, 1, Qt::AlignRight); + layout->addWidget(fileRadioButton, 2, 0); + layout->addWidget(fileLineEdit, 2, 1); + layout->addWidget(fileButton, 3, 1, Qt::AlignRight); + layout->addWidget(progressLabel, 4, 0); + layout->addWidget(progressBar, 4, 1); + + connect(&watcher, &QFutureWatcher::finished, this, &LoadSetsPage::importFinished); + + setLayout(layout); +} + +void LoadSetsPage::initializePage() +{ + urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString()); + + progressLabel->hide(); + progressBar->hide(); + + if (wizard()->backgroundMode) { + if (isEnabled()) { + validatePage(); + } + } +} + +void LoadSetsPage::retranslateUi() +{ + setTitle(tr("Source selection")); + setSubTitle(tr("Please specify a compatible source for the list of sets and cards. " + "You can specify a URL address that will be downloaded or " + "use an existing file from your computer.")); + + urlRadioButton->setText(tr("Download URL:")); + fileRadioButton->setText(tr("Local file:")); + urlButton->setText(tr("Restore default URL")); + fileButton->setText(tr("Choose file...")); +} + +void LoadSetsPage::actRestoreDefaultUrl() +{ + urlLineEdit->setText(ALLSETS_URL); +} + +void LoadSetsPage::actLoadSetsFile() +{ + QFileDialog dialog(this, tr("Load sets file")); + dialog.setFileMode(QFileDialog::ExistingFile); + + QString extensions = "*.json *.xml"; +#ifdef HAS_ZLIB + extensions += " *.zip"; +#endif +#ifdef HAS_LZMA + extensions += " *.xz"; +#endif + dialog.setNameFilter(tr("Sets file (%1)").arg(extensions)); + + if (!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text())) { + dialog.selectFile(fileLineEdit->text()); + } + + if (!dialog.exec()) { + return; + } + + fileLineEdit->setText(dialog.selectedFiles().at(0)); +} + +bool LoadSetsPage::validatePage() +{ + // once the import is finished, we call next(); skip validation + if (wizard()->downloadedPlainXml || wizard()->importer->getSets().count() > 0) { + return true; + } + + // else, try to import sets + if (urlRadioButton->isChecked()) { + // If a user attempts to download from V4, redirect them to V5 + if (urlLineEdit->text().contains(MTGJSON_V4_URL_COMPONENT)) { + actRestoreDefaultUrl(); + } + + const auto url = QUrl::fromUserInput(urlLineEdit->text()); + + if (!url.isValid()) { + QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); + return false; + } + + progressLabel->setText(tr("Downloading (0MB)")); + // show an infinite progressbar + progressBar->setMaximum(0); + progressBar->setMinimum(0); + progressBar->setValue(0); + progressLabel->show(); + progressBar->show(); + + wizard()->disableButtons(); + setEnabled(false); + + downloadSetsFile(url); + } else if (fileRadioButton->isChecked()) { + QFile setsFile(fileLineEdit->text()); + if (!setsFile.exists()) { + QMessageBox::critical(this, tr("Error"), tr("Please choose a file.")); + return false; + } + + if (!setsFile.open(QIODevice::ReadOnly)) { + QMessageBox::critical(nullptr, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text())); + return false; + } + + wizard()->disableButtons(); + setEnabled(false); + + wizard()->setCardSourceUrl(setsFile.fileName()); + wizard()->setCardSourceVersion("unknown"); + + readSetsFromByteArray(setsFile.readAll()); + } + + return false; +} + +void LoadSetsPage::downloadSetsFile(const QUrl &url) +{ + wizard()->setCardSourceVersion("unknown"); + + const auto urlString = url.toString(); + if (urlString == ALLSETS_URL || urlString == ALLSETS_URL_FALLBACK) { + const auto versionUrl = QUrl::fromUserInput(MTGJSON_VERSION_URL); + auto *versionReply = wizard()->nam->get(QNetworkRequest(versionUrl)); + connect(versionReply, &QNetworkReply::finished, [this, versionReply]() { + if (versionReply->error() == QNetworkReply::NoError) { + auto data = versionReply->readAll(); + QJsonParseError jsonError{}; + auto jsonResponse = QJsonDocument::fromJson(data, &jsonError); + + if (jsonError.error == QJsonParseError::NoError) { + const auto jsonMap = jsonResponse.toVariant().toMap(); + + auto versionString = jsonMap.value("meta").toMap().value("version").toString(); + if (versionString.isEmpty()) { + versionString = "unknown"; + } + wizard()->setCardSourceVersion(versionString); + } + } + + versionReply->deleteLater(); + }); + } + + wizard()->setCardSourceUrl(url.toString()); + + auto *reply = wizard()->nam->get(QNetworkRequest(url)); + + connect(reply, &QNetworkReply::finished, this, &LoadSetsPage::actDownloadFinishedSetsFile); + connect(reply, &QNetworkReply::downloadProgress, this, &LoadSetsPage::actDownloadProgressSetsFile); +} + +void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total) +{ + if (total > 0) { + progressBar->setMaximum(static_cast(total)); + progressBar->setValue(static_cast(received)); + } + progressLabel->setText(tr("Downloading (%1MB)").arg((int)received / (1024 * 1024))); +} + +void LoadSetsPage::actDownloadFinishedSetsFile() +{ + // check for a reply + auto *reply = dynamic_cast(sender()); + auto errorCode = reply->error(); + if (errorCode != QNetworkReply::NoError) { + QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); + + wizard()->enableButtons(); + setEnabled(true); + + reply->deleteLater(); + return; + } + + auto statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 301 || statusCode == 302) { + const auto redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + qDebug() << "following redirect url:" << redirectUrl.toString(); + downloadSetsFile(redirectUrl); + reply->deleteLater(); + return; + } + + progressLabel->hide(); + progressBar->hide(); + + // save AllPrintings.json url, but only if the user customized it and download was successful + if (urlLineEdit->text() != QString(ALLSETS_URL)) { + wizard()->settings->setValue("allsetsurl", urlLineEdit->text()); + } else { + wizard()->settings->remove("allsetsurl"); + } + + readSetsFromByteArray(reply->readAll()); + reply->deleteLater(); +} + +void LoadSetsPage::readSetsFromByteArray(QByteArray _data) +{ + // show an infinite progressbar + progressBar->setMaximum(0); + progressBar->setMinimum(0); + progressBar->setValue(0); + progressLabel->setText(tr("Parsing file")); + progressLabel->show(); + progressBar->show(); + + wizard()->downloadedPlainXml = false; + wizard()->xmlData.clear(); + readSetsFromByteArrayRef(_data); +} + +void LoadSetsPage::readSetsFromByteArrayRef(QByteArray &_data) +{ + // unzip the file if needed + if (_data.startsWith(XZ_SIGNATURE)) { +#ifdef HAS_LZMA + // zipped file + auto *inBuffer = new QBuffer(&_data); + auto newData = QByteArray(); + auto *outBuffer = new QBuffer(&newData); + inBuffer->open(QBuffer::ReadOnly); + outBuffer->open(QBuffer::WriteOnly); + XzDecompressor xz; + if (!xz.decompress(inBuffer, outBuffer)) { + zipDownloadFailed(tr("Xz extraction failed.")); + return; + } + _data.clear(); + readSetsFromByteArrayRef(newData); + return; +#else + zipDownloadFailed(tr("Sorry, this version of Oracle does not support xz compressed files.")); + + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + return; +#endif + } else if (_data.startsWith(ZIP_SIGNATURE)) { +#ifdef HAS_ZLIB + // zipped file + auto *inBuffer = new QBuffer(&_data); + auto newData = QByteArray(); + auto *outBuffer = new QBuffer(&newData); + QString fileName; + UnZip::ErrorCode ec; + UnZip uz; + + ec = uz.openArchive(inBuffer); + if (ec != UnZip::Ok) { + zipDownloadFailed(tr("Failed to open Zip archive: %1.").arg(uz.formatError(ec))); + return; + } + + if (uz.fileList().size() != 1) { + zipDownloadFailed(tr("Zip extraction failed: the Zip archive doesn't contain exactly one file.")); + return; + } + fileName = uz.fileList().at(0); + + outBuffer->open(QBuffer::ReadWrite); + ec = uz.extractFile(fileName, outBuffer); + if (ec != UnZip::Ok) { + zipDownloadFailed(tr("Zip extraction failed: %1.").arg(uz.formatError(ec))); + uz.closeArchive(); + return; + } + _data.clear(); + readSetsFromByteArrayRef(newData); + return; +#else + zipDownloadFailed(tr("Sorry, this version of Oracle does not support zipped files.")); + + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + return; +#endif + } else if (_data.startsWith("{")) { + // Start the computation. + jsonData = std::move(_data); + future = QtConcurrent::run([this] { return wizard()->importer->readSetsFromByteArray(std::move(jsonData)); }); + watcher.setFuture(future); + } else if (_data.startsWith("<")) { + // save xml file and don't do any processing + wizard()->downloadedPlainXml = true; + wizard()->xmlData = std::move(_data); + importFinished(); + } else { + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + QMessageBox::critical(this, tr("Error"), tr("Failed to interpret downloaded data.")); + } +} + +void LoadSetsPage::zipDownloadFailed(const QString &message) +{ + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + + QMessageBox::StandardButton reply; + reply = static_cast(QMessageBox::question( + this, tr("Error"), message + "
" + tr("Do you want to download the uncompressed file instead?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)); + + if (reply == QMessageBox::Yes) { + urlRadioButton->setChecked(true); + urlLineEdit->setText(ALLSETS_URL_FALLBACK); + + wizard()->next(); + } +} + +void LoadSetsPage::importFinished() +{ + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + + if (wizard()->downloadedPlainXml || watcher.future().result()) { + wizard()->next(); + } else { + QMessageBox::critical(this, tr("Error"), + tr("The file was retrieved successfully, but it does not contain any sets data.")); + } +} + +SaveSetsPage::SaveSetsPage(QWidget *parent) : OracleWizardPage(parent) +{ + pathLabel = new QLabel(this); + saveLabel = new QLabel(this); + + defaultPathCheckBox = new QCheckBox(this); + + messageLog = new QTextEdit(this); + messageLog->setReadOnly(true); + + auto *layout = new QGridLayout(this); + layout->addWidget(messageLog, 0, 0); + layout->addWidget(saveLabel, 1, 0); + layout->addWidget(pathLabel, 2, 0); + layout->addWidget(defaultPathCheckBox, 3, 0); + + setLayout(layout); +} + +void SaveSetsPage::cleanupPage() +{ + wizard()->importer->clear(); + disconnect(wizard()->importer, &OracleImporter::setIndexChanged, nullptr, nullptr); +} + +void SaveSetsPage::initializePage() +{ + messageLog->clear(); + + retranslateUi(); + if (wizard()->downloadedPlainXml) { + messageLog->hide(); + } else { + messageLog->show(); + connect(wizard()->importer, &OracleImporter::setIndexChanged, this, &SaveSetsPage::updateTotalProgress); + + int setsImported = wizard()->importer->startImport(); + + if (setsImported == 0) { + QMessageBox::critical(this, tr("Error"), tr("No set has been imported.")); + } + } + + if (wizard()->backgroundMode) { + emit readyToContinue(); + } +} + +void SaveSetsPage::retranslateUi() +{ + setTitle(tr("Sets imported")); + if (wizard()->downloadedPlainXml) { + setSubTitle(tr("A cockatrice database file of %1 MB has been downloaded.") + .arg(qRound(wizard()->xmlData.size() / 1000000.0))); + } else { + setSubTitle(tr("The following sets have been found:")); + } + + saveLabel->setText(tr("Press \"Save\" to store the imported cards in the Cockatrice database.")); + pathLabel->setText(tr("The card database will be saved at the following location:") + "
" + + SettingsCache::instance().getCardDatabasePath()); + defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)")); + + setButtonText(QWizard::NextButton, tr("&Save")); +} + +void SaveSetsPage::updateTotalProgress(int cardsImported, int /* setIndex */, const QString &setName) +{ + if (setName.isEmpty()) { + messageLog->append("" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + + ""); + } else { + messageLog->append(tr("%1: %2 cards imported").arg(setName).arg(cardsImported)); + } + + messageLog->verticalScrollBar()->setValue(messageLog->verticalScrollBar()->maximum()); +} + +bool SaveSetsPage::validatePage() +{ + QString defaultPath = SettingsCache::instance().getCardDatabasePath(); + QString windowName = tr("Save card database"); + QString fileType = tr("XML; card database (*.xml)"); + + QString fileName; + if (defaultPathCheckBox->isChecked()) { + fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType); + } else { + fileName = defaultPath; + } + + if (fileName.isEmpty()) { + return false; + } + + QFileInfo fi(fileName); + QDir fileDir(fi.path()); + if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) { + return false; + } + + if (wizard()->downloadedPlainXml) { + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) { + qDebug() << "File write (w) failed for" << fileName; + return false; + } + if (file.write(wizard()->xmlData) < 1) { + qDebug() << "File write (w) failed for" << fileName; + return false; + } + wizard()->xmlData.clear(); + } else if (!wizard()->importer->saveToFile(fileName, wizard()->getCardSourceUrl(), + wizard()->getCardSourceVersion())) { + QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName)); + return false; + } + + return true; +} + +void LoadTokensPage::initializePage() +{ + SimpleDownloadFilePage::initializePage(); + + if (wizard()->backgroundMode) { + emit readyToContinue(); + } +} + +QString LoadTokensPage::getDefaultUrl() +{ + return TOKENS_URL; +} + +QString LoadTokensPage::getCustomUrlSettingsKey() +{ + return "tokensurl"; +} + +QString LoadTokensPage::getDefaultSavePath() +{ + return SettingsCache::instance().getTokenDatabasePath(); +} + +QString LoadTokensPage::getWindowTitle() +{ + return tr("Save token database"); +} + +QString LoadTokensPage::getFileType() +{ + return tr("XML; token database (*.xml)"); +} + +void LoadTokensPage::retranslateUi() +{ + setTitle(tr("Tokens import")); + setSubTitle(tr("Please specify a compatible source for token data.")); + + urlLabel->setText(tr("Download URL:")); + urlButton->setText(tr("Restore default URL")); + pathLabel->setText(tr("The token database will be saved at the following location:") + "
" + + SettingsCache::instance().getTokenDatabasePath()); + defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)")); +} + +QString LoadSpoilersPage::getDefaultUrl() +{ + return SPOILERS_URL; +} + +QString LoadSpoilersPage::getCustomUrlSettingsKey() +{ + return "spoilersurl"; +} + +QString LoadSpoilersPage::getDefaultSavePath() +{ + return SettingsCache::instance().getTokenDatabasePath(); +} + +QString LoadSpoilersPage::getWindowTitle() +{ + return tr("Save spoiler database"); +} + +QString LoadSpoilersPage::getFileType() +{ + return tr("XML; spoiler database (*.xml)"); +} + +void LoadSpoilersPage::retranslateUi() +{ + setTitle(tr("Spoilers import")); + setSubTitle(tr("Please specify a compatible source for spoiler data.")); + + urlLabel->setText(tr("Download URL:")); + urlButton->setText(tr("Restore default URL")); + pathLabel->setText(tr("The spoiler database will be saved at the following location:") + "
" + + SettingsCache::instance().getSpoilerCardDatabasePath()); + defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)")); +} \ No newline at end of file diff --git a/oracle/src/pages.h b/oracle/src/pages.h new file mode 100644 index 000000000..6a88b6099 --- /dev/null +++ b/oracle/src/pages.h @@ -0,0 +1,154 @@ +#ifndef COCKATRICE_PAGES_H +#define COCKATRICE_PAGES_H + +#include "pagetemplates.h" + +#include +#include +#include +#include +#include + +class QCheckBox; +class QGroupBox; +class QComboBox; +class QLabel; +class QLineEdit; +class QRadioButton; +class QProgressBar; +class QNetworkAccessManager; +class QTextEdit; +class QVBoxLayout; +class OracleImporter; +class QSettings; + +class IntroPage : public OracleWizardPage +{ + Q_OBJECT +public: + explicit IntroPage(QWidget *parent = nullptr); + void retranslateUi() override; + +private: + QStringList findQmFiles(); + QString languageName(const QString &lang); + +private: + QLabel *label, *languageLabel, *versionLabel; + QComboBox *languageBox; + +private slots: + void languageBoxChanged(int index); + +protected slots: + void initializePage() override; +}; + +class OutroPage : public OracleWizardPage +{ + Q_OBJECT +public: + explicit OutroPage(QWidget * = nullptr) + { + } + void retranslateUi() override; + +protected: + void initializePage() override; +}; + +class LoadSetsPage : public OracleWizardPage +{ + Q_OBJECT +public: + explicit LoadSetsPage(QWidget *parent = nullptr); + void retranslateUi() override; + +protected: + void initializePage() override; + bool validatePage() override; + void readSetsFromByteArray(QByteArray _data); + void readSetsFromByteArrayRef(QByteArray &_data); + void downloadSetsFile(const QUrl &url); + +private: + QRadioButton *urlRadioButton; + QRadioButton *fileRadioButton; + QLineEdit *urlLineEdit; + QLineEdit *fileLineEdit; + QPushButton *urlButton; + QPushButton *fileButton; + QLabel *progressLabel; + QProgressBar *progressBar; + + QFutureWatcher watcher; + QFuture future; + QByteArray jsonData; + +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: + explicit SaveSetsPage(QWidget *parent = nullptr); + void retranslateUi() override; + +private: + QTextEdit *messageLog; + QCheckBox *defaultPathCheckBox; + QLabel *pathLabel; + QLabel *saveLabel; + +protected: + void initializePage() override; + void cleanupPage() override; + bool validatePage() override; + +private slots: + void updateTotalProgress(int cardsImported, int setIndex, const QString &setName); +}; + +class LoadSpoilersPage : public SimpleDownloadFilePage +{ + Q_OBJECT +public: + explicit LoadSpoilersPage(QWidget * = nullptr) + { + } + void retranslateUi() override; + +protected: + QString getDefaultUrl() override; + QString getCustomUrlSettingsKey() override; + QString getDefaultSavePath() override; + QString getWindowTitle() override; + QString getFileType() override; +}; + +class LoadTokensPage : public SimpleDownloadFilePage +{ + Q_OBJECT +public: + explicit LoadTokensPage(QWidget * = nullptr) + { + } + void retranslateUi() override; + +protected: + QString getDefaultUrl() override; + QString getCustomUrlSettingsKey() override; + QString getDefaultSavePath() override; + QString getWindowTitle() override; + QString getFileType() override; + void initializePage() override; +}; + +#endif // COCKATRICE_PAGES_H