#include #if QT_VERSION < 0x050000 #include #else #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "oraclewizard.h" #include "oracleimporter.h" #ifdef HAS_ZLIB #include "zip/unzip.h" #define ZIP_SIGNATURE "PK" #define ALLSETS_URL "http://mtgjson.com/json/AllSets.json.zip" #else #define ALLSETS_URL "http://mtgjson.com/json/AllSets.json" #endif OracleWizard::OracleWizard(QWidget *parent) : QWizard(parent) { settings = new QSettings(this); importer = new OracleImporter( #if QT_VERSION < 0x050000 QDesktopServices::storageLocation(QDesktopServices::DataLocation) #else QStandardPaths::standardLocations(QStandardPaths::DataLocation).first() #endif , this); addPage(new IntroPage); addPage(new LoadSetsPage); addPage(new ChooseSetsPage); addPage(new SaveSetsPage); setWindowTitle(tr("Oracle Importer")); QWizard::setButtonText(QWizard::FinishButton, tr("Save")); } void OracleWizard::accept() { QDialog::accept(); } void OracleWizard::enableButtons() { button(QWizard::NextButton)->setDisabled(false); button(QWizard::BackButton)->setDisabled(false); } void OracleWizard::disableButtons() { button(QWizard::NextButton)->setDisabled(true); button(QWizard::BackButton)->setDisabled(true); } IntroPage::IntroPage(QWidget *parent) : OracleWizardPage(parent) { setTitle(tr("Introduction")); label = new QLabel(tr("This wizard will import the list of sets and cards " "that will be used by Cockatrice. You will need to " "specify an url or a filename that will be used as a " "source, and then choose the wanted sets from the list " "of the available ones."), this); label->setWordWrap(true); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(label); setLayout(layout); } LoadSetsPage::LoadSetsPage(QWidget *parent) : OracleWizardPage(parent), nam(0) { setTitle(tr("Source selection")); setSubTitle(tr("Please specify a source for the list of sets and cards. " "You can specify an url address that will be download or " "use an existing file from your computer.")); urlRadioButton = new QRadioButton(tr("Download url:"), this); fileRadioButton = new QRadioButton(tr("Local file:"), this); urlLineEdit = new QLineEdit(this); fileLineEdit = new QLineEdit(this); progressLabel = new QLabel(this); progressBar = new QProgressBar(this); urlRadioButton->setChecked(true); urlButton = new QPushButton(tr("Restore default url"), this); connect(urlButton, SIGNAL(clicked()), this, SLOT(actRestoreDefaultUrl())); fileButton = new QPushButton(tr("Choose file..."), this); connect(fileButton, SIGNAL(clicked()), this, SLOT(actLoadSetsFile())); QGridLayout *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, SIGNAL(finished()), this, SLOT(importFinished())); setLayout(layout); } void LoadSetsPage::initializePage() { urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString()); progressLabel->hide(); progressBar->hide(); } void LoadSetsPage::actRestoreDefaultUrl() { urlLineEdit->setText(ALLSETS_URL); } void LoadSetsPage::actLoadSetsFile() { QFileDialog dialog(this, tr("Load sets file")); dialog.setFileMode(QFileDialog::ExistingFile); #ifdef HAS_ZLIB dialog.setNameFilter(tr("Sets JSON file (*.json *.zip)")); #else dialog.setNameFilter(tr("Sets JSON file (*.json)")); #endif 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()->importer->getSets().count() > 0) return true; // else, try to import sets if(urlRadioButton->isChecked()) { 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); if(!nam) nam = new QNetworkAccessManager(this); QNetworkReply *reply = nam->get(QNetworkRequest(url)); connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSetsFile())); connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(actDownloadProgressSetsFile(qint64, qint64))); } 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(0, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text())); return false; } wizard()->disableButtons(); setEnabled(false); readSetsFromByteArray(setsFile.readAll()); } return false; } void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total) { if(total > 0 && progressBar->maximum()==0) { progressBar->setMaximum(total); progressBar->setValue(received); } progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / 1048576)); } void LoadSetsPage::actDownloadFinishedSetsFile() { progressLabel->hide(); progressBar->hide(); // check for a reply QNetworkReply *reply = static_cast(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; } // save allsets.json url, but only if the user customized it and download was successfull 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(); // unzip the file if needed if(data.startsWith(ZIP_SIGNATURE)) { #ifdef HAS_ZLIB // zipped file QBuffer *inBuffer = new QBuffer(&data); QBuffer *outBuffer = new QBuffer(this); QString fileName; UnZip::ErrorCode ec; UnZip uz; ec = uz.openArchive(inBuffer); if (ec != UnZip::Ok) { QMessageBox::critical(this, tr("Error"), tr("Failed to open Zip archive: %1").arg(uz.formatError(ec))); wizard()->enableButtons(); setEnabled(true); return; } if(uz.fileList().size() != 1) { QMessageBox::critical(this, tr("Error"), tr("The Zip archive doesn't contain exactly one file")); wizard()->enableButtons(); setEnabled(true); return; } fileName = uz.fileList().at(0); outBuffer->open(QBuffer::ReadWrite); ec = uz.extractFile(fileName, outBuffer); if (ec != UnZip::Ok) { QMessageBox::critical(this, tr("Error"), tr("Zip extraction failed: %1").arg(uz.formatError(ec))); uz.closeArchive(); wizard()->enableButtons(); setEnabled(true); return; } future = QtConcurrent::run(wizard()->importer, &OracleImporter::readSetsFromByteArray, outBuffer->data()); watcher.setFuture(future); return; #else QMessageBox::critical(this, tr("Error"), tr("Sorry, this version of Oracle does not support zipped files.")); wizard()->enableButtons(); setEnabled(true); return; #endif } // Start the computation. future = QtConcurrent::run(wizard()->importer, &OracleImporter::readSetsFromByteArray, data); watcher.setFuture(future); } void LoadSetsPage::importFinished() { wizard()->enableButtons(); setEnabled(true); progressLabel->hide(); progressBar->hide(); if(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.")); } } ChooseSetsPage::ChooseSetsPage(QWidget *parent) : OracleWizardPage(parent) { setTitle(tr("Sets selection")); setSubTitle(tr("The following sets has been found in the source file. " "Please mark the sets that will be imported.\n" "All core and expansion sets are selected by default.")); checkBoxLayout = new QVBoxLayout; QWidget *checkboxFrame = new QWidget(this); checkboxFrame->setLayout(checkBoxLayout); QScrollArea *checkboxArea = new QScrollArea(this); checkboxArea->setWidget(checkboxFrame); checkboxArea->setWidgetResizable(true); checkAllButton = new QPushButton(tr("&Check all")); connect(checkAllButton, SIGNAL(clicked()), this, SLOT(actCheckAll())); uncheckAllButton = new QPushButton(tr("&Uncheck all")); connect(uncheckAllButton, SIGNAL(clicked()), this, SLOT(actUncheckAll())); QGridLayout *layout = new QGridLayout(this); layout->addWidget(checkboxArea, 0, 0, 1, 2); layout->addWidget(checkAllButton, 1, 0); layout->addWidget(uncheckAllButton, 1, 1); setLayout(layout); } void ChooseSetsPage::initializePage() { // populate checkbox list for (int i = 0; i < checkBoxList.size(); ++i) delete checkBoxList[i]; checkBoxList.clear(); QList &sets = wizard()->importer->getSets(); for (int i = 0; i < sets.size(); ++i) { QCheckBox *checkBox = new QCheckBox(sets[i].getLongName()); checkBox->setChecked(sets[i].getImport()); connect(checkBox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxChanged(int))); checkBoxLayout->addWidget(checkBox); checkBoxList << checkBox; } } void ChooseSetsPage::checkBoxChanged(int state) { QCheckBox *checkBox = qobject_cast(sender()); QList &sets = wizard()->importer->getSets(); for (int i = 0; i < sets.size(); ++i) if (sets[i].getLongName() == checkBox->text()) { sets[i].setImport(state); break; } } void ChooseSetsPage::actCheckAll() { for (int i = 0; i < checkBoxList.size(); ++i) checkBoxList[i]->setChecked(true); } void ChooseSetsPage::actUncheckAll() { for (int i = 0; i < checkBoxList.size(); ++i) checkBoxList[i]->setChecked(false); } bool ChooseSetsPage::validatePage() { for (int i = 0; i < checkBoxList.size(); ++i) { if(checkBoxList[i]->isChecked()) return true; } QMessageBox::critical(this, tr("Error"), tr("Please mark at least one set.")); return false; } SaveSetsPage::SaveSetsPage(QWidget *parent) : OracleWizardPage(parent) { setTitle(tr("Sets imported")); setSubTitle(tr("The following sets has been imported. " "Press \"Save\" to save the imported cards to the Cockatrice database.")); defaultPathCheckBox = new QCheckBox(this); defaultPathCheckBox->setText(tr("Save to the default path (recommended)")); defaultPathCheckBox->setChecked(true); messageLog = new QTextEdit(this); messageLog->setReadOnly(true); QGridLayout *layout = new QGridLayout(this); layout->addWidget(defaultPathCheckBox, 0, 0); layout->addWidget(messageLog, 1, 0); setLayout(layout); } void SaveSetsPage::cleanupPage() { disconnect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), 0, 0); } void SaveSetsPage::initializePage() { messageLog->clear(); 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::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() { bool ok = false; const QString dataDir = #if QT_VERSION < 0x050000 QDesktopServices::storageLocation(QDesktopServices::DataLocation); #else QStandardPaths::standardLocations(QStandardPaths::DataLocation).first(); #endif QSettings* settings = new QSettings(this); QString defaultPath = settings->value("paths/carddatabase").toString(); QString windowName = tr("Save card database"); QString fileType = tr("XML; card database (*.xml)"); do { QString fileName; if (defaultPath.isEmpty()) { if (defaultPathCheckBox->isChecked()) fileName = dataDir + "/cards.xml"; else fileName = QFileDialog::getSaveFileName(this, windowName, dataDir + "/cards.xml", fileType); settings->setValue("paths/carddatabase", fileName); } else { 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()->importer->saveToFile(fileName)) { ok = true; QMessageBox::information(this, tr("Success"), tr("The card database has been saved successfully to\n%1").arg(fileName)); } 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; }