Merge pull request #85 from ctrlaltca/mtgjson-importer

Mtgjson importer (fixes #84)
This commit is contained in:
Gavin Bisesi 2014-06-21 08:13:11 -04:00
commit c3ad6b1f69
15 changed files with 1191 additions and 1110 deletions

View file

@ -80,11 +80,13 @@ bool PictureToLoad::nextSet()
return true;
}
PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, QObject *parent)
: QObject(parent), _picsPath(__picsPath), picDownload(_picDownload), downloadRunning(false), loadQueueRunning(false)
PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent)
: QObject(parent),
_picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq),
downloadRunning(false), loadQueueRunning(false)
{
connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection);
networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *)));
}
@ -99,7 +101,7 @@ void PictureLoader::processLoadQueue()
{
if (loadQueueRunning)
return;
loadQueueRunning = true;
forever {
mutex.lock();
@ -113,7 +115,7 @@ void PictureLoader::processLoadQueue()
QString correctedName = ptl.getCard()->getCorrectedName();
QString picsPath = _picsPath;
QString setName = ptl.getSetName();
QImage image;
if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg(setName).arg(correctedName)))
if (!image.load(QString("%1/%2/%3%4.full.jpg").arg(picsPath).arg(setName).arg(correctedName).arg(1)))
@ -135,6 +137,20 @@ void PictureLoader::processLoadQueue()
}
}
QString PictureLoader::getPicUrl(CardInfo *card)
{
if (!picDownload) return 0;
QString picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl();
picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName()));
CardSet *set = card->getPreferredSet();
picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName()));
picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName()));
picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(card->getPreferredMuId())));
return picUrl;
}
void PictureLoader::startNextPicDownload()
{
if (cardsToDownload.isEmpty()) {
@ -142,23 +158,16 @@ void PictureLoader::startNextPicDownload()
downloadRunning = false;
return;
}
downloadRunning = true;
cardBeingDownloaded = cardsToDownload.takeFirst();
QString picUrl;
if (cardBeingDownloaded.getStripped())
picUrl = cardBeingDownloaded.getCard()->getPicURLSt(cardBeingDownloaded.getSetName());
else if (cardBeingDownloaded.getHq()) {
picUrl = cardBeingDownloaded.getCard()->getPicURLHq(cardBeingDownloaded.getSetName());
if (picUrl.isEmpty()) {
picUrl = cardBeingDownloaded.getCard()->getPicURL(cardBeingDownloaded.getSetName());
cardBeingDownloaded.setHq(false);
}
} else
picUrl = cardBeingDownloaded.getCard()->getPicURL(cardBeingDownloaded.getSetName());
// TODO: Do something useful when picUrl is 0 or empty, etc
QString picUrl = getPicUrl(cardBeingDownloaded.getCard());
QUrl url(picUrl);
QNetworkRequest req(url);
qDebug() << "starting picture download:" << req.url();
networkManager->get(req);
@ -232,6 +241,12 @@ void PictureLoader::setPicDownload(bool _picDownload)
picDownload = _picDownload;
}
void PictureLoader::setPicDownloadHq(bool _picDownloadHq)
{
QMutexLocker locker(&mutex);
picDownloadHq = _picDownloadHq;
}
CardInfo::CardInfo(CardDatabase *_db,
const QString &_name,
bool _isToken,
@ -244,22 +259,18 @@ CardInfo::CardInfo(CardDatabase *_db,
bool _cipt,
int _tableRow,
const SetList &_sets,
const QMap<QString, QString> &_picURLs,
const QMap<QString, QString> &_picURLsHq,
const QMap<QString, QString> &_picURLsSt)
QMap<QString, int> _muIds)
: db(_db),
name(_name),
isToken(_isToken),
sets(_sets),
muIds(_muIds),
manacost(_manacost),
cardtype(_cardtype),
powtough(_powtough),
text(_text),
colors(_colors),
loyalty(_loyalty),
picURLs(_picURLs),
picURLsHq(_picURLsHq),
picURLsSt(_picURLsSt),
cipt(_cipt),
tableRow(_tableRow),
pixmap(NULL)
@ -315,19 +326,12 @@ void CardInfo::addToSet(CardSet *set)
sets << set;
}
QString CardInfo::getPicURL() const
{
SetList sortedSets = sets;
sortedSets.sortByKey();
return picURLs.value(sortedSets.first()->getShortName());
}
QPixmap *CardInfo::loadPixmap()
{
if (pixmap)
return pixmap;
pixmap = new QPixmap();
if (getName().isEmpty()) {
pixmap->load(settingsCache->getCardBackPicturePath());
return pixmap;
@ -400,18 +404,33 @@ void CardInfo::updatePixmapCache()
emit pixmapUpdated();
}
CardSet* CardInfo::getPreferredSet()
{
SetList sortedSets = sets;
sortedSets.sortByKey();
return sortedSets.first();
}
int CardInfo::getPreferredMuId()
{
return muIds[getPreferredSet()->getShortName()];
}
QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info)
{
xml.writeStartElement("card");
xml.writeTextElement("name", info->getName());
const SetList &sets = info->getSets();
QString tmpString;
QString tmpSet;
for (int i = 0; i < sets.size(); i++) {
xml.writeStartElement("set");
xml.writeAttribute("picURL", info->getPicURL(sets[i]->getShortName()));
xml.writeAttribute("picURLHq", info->getPicURLHq(sets[i]->getShortName()));
xml.writeAttribute("picURLSt", info->getPicURLSt(sets[i]->getShortName()));
xml.writeCharacters(sets[i]->getShortName());
tmpSet=sets[i]->getShortName();
xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet)));
xml.writeCharacters(tmpSet);
xml.writeEndElement();
}
const QStringList &colors = info->getColors();
@ -442,12 +461,13 @@ CardDatabase::CardDatabase(QObject *parent)
connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabase()));
connect(settingsCache, SIGNAL(tokenDatabasePathChanged()), this, SLOT(loadTokenDatabase()));
connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
connect(settingsCache, SIGNAL(picDownloadHqChanged()), this, SLOT(picDownloadHqChanged()));
loadCardDatabase();
loadTokenDatabase();
pictureLoaderThread = new QThread;
pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload());
pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload(), settingsCache->getPicDownloadHq());
pictureLoader->moveToThread(pictureLoaderThread);
connect(pictureLoader, SIGNAL(imageLoaded(CardInfo *, const QImage &)), this, SLOT(imageLoaded(CardInfo *, const QImage &)));
pictureLoaderThread->start(QThread::LowPriority);
@ -572,7 +592,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
if (xml.name() == "card") {
QString name, manacost, type, pt, text;
QStringList colors;
QMap<QString, QString> picURLs, picURLsHq, picURLsSt;
QMap<QString, int> muids;
SetList sets;
int tableRow = 0;
int loyalty = 0;
@ -592,14 +612,12 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
else if (xml.name() == "text")
text = xml.readElementText();
else if (xml.name() == "set") {
QString picURL = xml.attributes().value("picURL").toString();
QString picURLHq = xml.attributes().value("picURLHq").toString();
QString picURLSt = xml.attributes().value("picURLSt").toString();
QXmlStreamAttributes attrs = xml.attributes();
QString setName = xml.readElementText();
sets.append(getSet(setName));
picURLs.insert(setName, picURL);
picURLsHq.insert(setName, picURLHq);
picURLsSt.insert(setName, picURLSt);
if (attrs.hasAttribute("muId")) {
muids[setName] = attrs.value("muId").toString().toInt();
}
} else if (xml.name() == "color")
colors << xml.readElementText();
else if (xml.name() == "tablerow")
@ -611,7 +629,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
else if (xml.name() == "token")
isToken = xml.readElementText().toInt();
}
cardHash.insert(name, new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, picURLs, picURLsHq, picURLsSt));
cardHash.insert(name, new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, muids));
}
}
}
@ -717,6 +735,16 @@ void CardDatabase::picDownloadChanged()
}
}
void CardDatabase::picDownloadHqChanged()
{
pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq());
if (settingsCache->getPicDownloadHq()) {
QHashIterator<QString, CardInfo *> cardIterator(cardHash);
while (cardIterator.hasNext())
cardIterator.next().value()->clearPixmapCacheMiss();
}
}
bool CardDatabase::loadCardDatabase(const QString &path, bool tokens)
{
bool tempLoadSuccess = false;

View file

@ -68,13 +68,15 @@ private:
QNetworkAccessManager *networkManager;
QList<PictureToLoad> cardsToDownload;
PictureToLoad cardBeingDownloaded;
bool picDownload, downloadRunning, loadQueueRunning;
bool picDownload, picDownloadHq, downloadRunning, loadQueueRunning;
void startNextPicDownload();
QString getPicUrl(CardInfo* card);
public:
PictureLoader(const QString &__picsPath, bool _picDownload, QObject *parent = 0);
PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent = 0);
~PictureLoader();
void setPicsPath(const QString &path);
void setPicDownload(bool _picDownload);
void setPicDownloadHq(bool _picDownloadHq);
void loadImage(CardInfo *card, bool stripped);
private slots:
void picDownloadFinished(QNetworkReply *reply);
@ -99,7 +101,7 @@ private:
QString text;
QStringList colors;
int loyalty;
QMap<QString, QString> picURLs, picURLsHq, picURLsSt;
QMap<QString, int> muIds;
bool cipt;
int tableRow;
QPixmap *pixmap;
@ -117,9 +119,7 @@ public:
bool _cipt = false,
int _tableRow = 0,
const SetList &_sets = SetList(),
const QStringMap &_picURLs = QStringMap(),
const QStringMap &_picURLsHq = QStringMap(),
const QStringMap &_picURLsSt = QStringMap());
QMap<QString, int> muids = QMap<QString, int>());
~CardInfo();
const QString &getName() const { return name; }
bool getIsToken() const { return isToken; }
@ -136,25 +136,21 @@ public:
void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); }
void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); }
const QStringList &getColors() const { return colors; }
QString getPicURL(const QString &set) const { return picURLs.value(set); }
QString getPicURLHq(const QString &set) const { return picURLsHq.value(set); }
QString getPicURLSt(const QString &set) const { return picURLsSt.value(set); }
QString getPicURL() const;
const QMap<QString, QString> &getPicURLs() const { return picURLs; }
int getMuId(const QString &set) const { return muIds.value(set); }
QString getMainCardType() const;
QString getCorrectedName() const;
int getTableRow() const { return tableRow; }
void setTableRow(int _tableRow) { tableRow = _tableRow; }
void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); }
void setPicURL(const QString &_set, const QString &_picURL) { picURLs.insert(_set, _picURL); }
void setPicURLHq(const QString &_set, const QString &_picURL) { picURLsHq.insert(_set, _picURL); }
void setPicURLSt(const QString &_set, const QString &_picURL) { picURLsSt.insert(_set, _picURL); }
void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); }
void addToSet(CardSet *set);
QPixmap *loadPixmap();
QPixmap *getPixmap(QSize size);
void clearPixmapCache();
void clearPixmapCacheMiss();
void imageLoaded(const QImage &image);
CardSet *getPreferredSet();
int getPreferredMuId();
public slots:
void updatePixmapCache();
signals:
@ -169,7 +165,7 @@ protected:
QHash<QString, CardSet *> setHash;
bool loadSuccess;
CardInfo *noCard;
QThread *pictureLoaderThread;
PictureLoader *pictureLoader;
private:
@ -199,6 +195,7 @@ public slots:
private slots:
void imageLoaded(CardInfo *card, QImage image);
void picDownloadChanged();
void picDownloadHqChanged();
void picsPathChanged();
void loadCardDatabase();

View file

@ -27,7 +27,7 @@ GeneralSettingsPage::GeneralSettingsPage()
{
languageLabel = new QLabel;
languageBox = new QComboBox;
QString setLanguage = settingsCache->getLang();
QStringList qmFiles = findQmFiles();
for (int i = 0; i < qmFiles.size(); i++) {
@ -36,27 +36,32 @@ GeneralSettingsPage::GeneralSettingsPage()
if ((qmFiles[i] == setLanguage) || (setLanguage.isEmpty() && langName == tr("English")))
languageBox->setCurrentIndex(i);
}
picDownloadCheckBox = new QCheckBox;
picDownloadCheckBox->setChecked(settingsCache->getPicDownload());
picDownloadHqCheckBox = new QCheckBox;
picDownloadHqCheckBox->setChecked(settingsCache->getPicDownloadHq());
connect(languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int)));
connect(picDownloadCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownload(int)));
connect(picDownloadHqCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownloadHq(int)));
QGridLayout *personalGrid = new QGridLayout;
personalGrid->addWidget(languageLabel, 0, 0);
personalGrid->addWidget(languageBox, 0, 1);
personalGrid->addWidget(picDownloadCheckBox, 1, 0, 1, 2);
personalGrid->addWidget(picDownloadHqCheckBox, 2, 0, 1, 2);
personalGroupBox = new QGroupBox;
personalGroupBox->setLayout(personalGrid);
deckPathLabel = new QLabel;
deckPathEdit = new QLineEdit(settingsCache->getDeckPath());
deckPathEdit->setReadOnly(true);
QPushButton *deckPathButton = new QPushButton("...");
connect(deckPathButton, SIGNAL(clicked()), this, SLOT(deckPathButtonClicked()));
replaysPathLabel = new QLabel;
replaysPathEdit = new QLineEdit(settingsCache->getReplaysPath());
replaysPathEdit->setReadOnly(true);
@ -183,6 +188,7 @@ void GeneralSettingsPage::retranslateUi()
personalGroupBox->setTitle(tr("Personal settings"));
languageLabel->setText(tr("Language:"));
picDownloadCheckBox->setText(tr("Download card pictures on the fly"));
picDownloadHqCheckBox->setText(tr("Download high-quality card pictures"));
pathsGroupBox->setTitle(tr("Paths"));
deckPathLabel->setText(tr("Decks directory:"));
replaysPathLabel->setText(tr("Replays directory:"));

View file

@ -40,6 +40,7 @@ private:
QGroupBox *personalGroupBox, *pathsGroupBox;
QComboBox *languageBox;
QCheckBox *picDownloadCheckBox;
QCheckBox *picDownloadHqCheckBox;
QLabel *languageLabel, *deckPathLabel, *replaysPathLabel, *picsPathLabel, *cardDatabasePathLabel, *tokenDatabasePathLabel;
};

View file

@ -4,23 +4,27 @@
SettingsCache::SettingsCache()
{
settings = new QSettings(this);
lang = settings->value("personal/lang").toString();
deckPath = settings->value("paths/decks").toString();
replaysPath = settings->value("paths/replays").toString();
picsPath = settings->value("paths/pics").toString();
cardDatabasePath = settings->value("paths/carddatabase").toString();
tokenDatabasePath = settings->value("paths/tokendatabase").toString();
handBgPath = settings->value("zonebg/hand").toString();
stackBgPath = settings->value("zonebg/stack").toString();
tableBgPath = settings->value("zonebg/table").toString();
playerBgPath = settings->value("zonebg/playerarea").toString();
cardBackPicturePath = settings->value("paths/cardbackpicture").toString();
mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray();
picDownload = settings->value("personal/picturedownload", true).toBool();
picDownloadHq = settings->value("personal/picturedownloadhq", false).toBool();
picUrl = settings->value("personal/picUrl", PIC_URL_DEFAULT).toString();
picUrlHq = settings->value("personal/picUrlHq", PIC_URL_HQ_DEFAULT).toString();
mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray();
notificationsEnabled = settings->value("interface/notificationsenabled", true).toBool();
doubleClickToPlay = settings->value("interface/doubleclicktoplay", true).toBool();
playToStack = settings->value("interface/playtostack", false).toBool();
@ -31,15 +35,15 @@ SettingsCache::SettingsCache()
invertVerticalCoordinate = settings->value("table/invert_vertical", false).toBool();
minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 5).toInt();
tapAnimation = settings->value("cards/tapanimation", true).toBool();
zoneViewSortByName = settings->value("zoneview/sortbyname", true).toBool();
zoneViewSortByType = settings->value("zoneview/sortbytype", true).toBool();
soundEnabled = settings->value("sound/enabled", false).toBool();
soundPath = settings->value("sound/path").toString();
priceTagFeature = settings->value("deckeditor/pricetags", false).toBool();
ignoreUnregisteredUsers = settings->value("chat/ignore_unregistered", false).toBool();
}
@ -125,6 +129,25 @@ void SettingsCache::setPicDownload(int _picDownload)
emit picDownloadChanged();
}
void SettingsCache::setPicDownloadHq(int _picDownloadHq)
{
picDownloadHq = _picDownloadHq;
settings->setValue("personal/picturedownloadhq", picDownloadHq);
emit picDownloadHqChanged();
}
void SettingsCache::setPicUrl(const QString &_picUrl)
{
picUrl = _picUrl;
settings->setValue("personal/picUrl", picUrl);
}
void SettingsCache::setPicUrlHq(const QString &_picUrlHq)
{
picUrlHq = _picUrlHq;
settings->setValue("personal/picUrlHq", picUrlHq);
}
void SettingsCache::setNotificationsEnabled(int _notificationsEnabled)
{
notificationsEnabled = _notificationsEnabled;

View file

@ -3,6 +3,9 @@
#include <QObject>
#define PIC_URL_DEFAULT "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card"
#define PIC_URL_HQ_DEFAULT "http://mtgimage.com/multiverseid/!cardid!.jpg"
class QSettings;
class SettingsCache : public QObject {
@ -18,6 +21,7 @@ signals:
void playerBgPathChanged();
void cardBackPicturePathChanged();
void picDownloadChanged();
void picDownloadHqChanged();
void displayCardNamesChanged();
void horizontalHandChanged();
void invertVerticalCoordinateChanged();
@ -33,6 +37,7 @@ private:
QString deckPath, replaysPath, picsPath, cardDatabasePath, tokenDatabasePath;
QString handBgPath, stackBgPath, tableBgPath, playerBgPath, cardBackPicturePath;
bool picDownload;
bool picDownloadHq;
bool notificationsEnabled;
bool doubleClickToPlay;
bool playToStack;
@ -48,6 +53,8 @@ private:
QString soundPath;
bool priceTagFeature;
bool ignoreUnregisteredUsers;
QString picUrl;
QString picUrlHq;
public:
SettingsCache();
const QByteArray &getMainWindowGeometry() const { return mainWindowGeometry; }
@ -63,6 +70,7 @@ public:
QString getPlayerBgPath() const { return playerBgPath; }
QString getCardBackPicturePath() const { return cardBackPicturePath; }
bool getPicDownload() const { return picDownload; }
bool getPicDownloadHq() const { return picDownloadHq; }
bool getNotificationsEnabled() const { return notificationsEnabled; }
bool getDoubleClickToPlay() const { return doubleClickToPlay; }
bool getPlayToStack() const { return playToStack; }
@ -79,6 +87,8 @@ public:
QString getSoundPath() const { return soundPath; }
bool getPriceTagFeature() const { return priceTagFeature; }
bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; }
QString getPicUrl() const { return picUrl; }
QString getPicUrlHq() const { return picUrlHq; }
public slots:
void setMainWindowGeometry(const QByteArray &_mainWindowGeometry);
void setLang(const QString &_lang);
@ -93,6 +103,7 @@ public slots:
void setPlayerBgPath(const QString &_playerBgPath);
void setCardBackPicturePath(const QString &_cardBackPicturePath);
void setPicDownload(int _picDownload);
void setPicDownloadHq(int _picDownloadHq);
void setNotificationsEnabled(int _notificationsEnabled);
void setDoubleClickToPlay(int _doubleClickToPlay);
void setPlayToStack(int _playToStack);
@ -109,6 +120,8 @@ public slots:
void setSoundPath(const QString &_soundPath);
void setPriceTagFeature(int _priceTagFeature);
void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers);
void setPicUrl(const QString &_picUrl);
void setPicUrlHq(const QString &_picUrlHq);
};
extern SettingsCache *settingsCache;

View file

@ -1,333 +1,254 @@
<?xml version="1.0" encoding="UTF-8"?>
<cockatrice_setdatabase version="20130507">
<cockatrice_setdatabase version="20110126">
<picture_url>http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&amp;type=card</picture_url>
<set_url>http://gatherer.wizards.com/Pages/Search/Default.aspx?output=spoiler&amp;method=text&amp;set=[&quot;!longname!&quot;]&amp;special=true</set_url>
<set import="1">
<name>10E</name>
<longname>Tenth Edition</longname>
</set>
<set import="1">
<name>15ANN</name>
<longname>15th Anniversary</longname>
</set>
<set import="1">
<name>4E</name>
<longname>Fourth Edition</longname>
</set>
<set import="1">
<name>5DN</name>
<longname>Fifth Dawn</longname>
</set>
<set import="1">
<name>5E</name>
<longname>Fifth Edition</longname>
</set>
<set import="1">
<name>6E</name>
<longname>Classic Sixth Edition</longname>
</set>
<set import="1">
<name>7E</name>
<longname>Seventh Edition</longname>
</set>
<set import="1">
<name>8EB</name>
<longname>Eighth Edition Box Set</longname>
</set>
<set import="1">
<name>8E</name>
<longname>Eighth Edition</longname>
</set>
<set import="1">
<name>9EB</name>
<longname>Ninth Edition Box Set</longname>
</set>
<set import="1">
<name>9E</name>
<longname>Ninth Edition</longname>
</set>
<set import="1">
<name>AI</name>
<longname>Alliances</longname>
</set>
<set import="1">
<name>ALA</name>
<longname>Shards of Alara</longname>
</set>
<set import="1">
<name>AL</name>
<longname>Limited Edition Alpha</longname>
</set>
<set import="1">
<name>AN</name>
<longname>Arabian Nights</longname>
</set>
<set import="1">
<name>APAC</name>
<longname>Asia Pacific Land Program</longname>
</set>
<set import="1">
<name>AP</name>
<longname>Apocalypse</longname>
</set>
<set import="1">
<name>AQ</name>
<longname>Antiquities</longname>
</set>
<!-- <picture_url_hq>http://mtgimage.com/multiverseid/!cardid!.jpg</picture_url_hq> -->
<set_url>http://mtgjson.com/json/!name!.json</set_url>
<set import="1">
<name>ARB</name>
<longname>Alara Reborn</longname>
</set>
<set import="1">
<name>ALL</name>
<longname>Alliances</longname>
</set>
<set import="1">
<name>ATQ</name>
<longname>Antiquities</longname>
</set>
<set import="1">
<name>APC</name>
<longname>Apocalypse</longname>
</set>
<set import="1">
<name>ARN</name>
<longname>Arabian Nights</longname>
</set>
<set import="0">
<name>ARC</name>
<longname>Archenemy</longname>
</set>
<set import="1">
<name>ARENA</name>
<longname>Arena League</longname>
</set>
<set import="1">
<name>AT</name>
<longname>Anthologies</longname>
</set>
<set import="1">
<name>AVR</name>
<longname>Avacyn Restored</longname>
</set>
<set import="1">
<name>BD</name>
<longname>Beatdown Box Set</longname>
<set import="0">
<name>BRB</name>
<longname>Battle Royale Box Set</longname>
</set>
<set import="1">
<name>BE</name>
<longname>Limited Edition Beta</longname>
<set import="0">
<name>BTD</name>
<longname>Beatdown Box Set</longname>
</set>
<set import="1">
<name>BOK</name>
<longname>Betrayers of Kamigawa</longname>
</set>
<set import="1">
<name>BR</name>
<longname>Battle Royale Box Set</longname>
</set>
<set import="1">
<name>CED</name>
<longname>Collector's Edition</longname>
</set>
<set import="1">
<name>CEDI</name>
<longname>International Collectors' Edition</longname>
</set>
<set import="1">
<name>CFX</name>
<longname>Conflux</longname>
</set>
<set import="1">
<name>CH</name>
<longname>Chronicles</longname>
<name>BNG</name>
<longname>Born of the Gods</longname>
</set>
<set import="1">
<name>CHK</name>
<longname>Champions of Kamigawa</longname>
</set>
<set import="1">
<name>CMA</name>
<longname>Commander's Arsenal</longname>
<name>CHR</name>
<longname>Chronicles</longname>
</set>
<set import="1">
<name>CMD</name>
<longname>Commander</longname>
<name>6ED</name>
<longname>Classic Sixth Edition</longname>
</set>
<set import="1">
<name>CP</name>
<longname>Champs</longname>
</set>
<set import="1">
<name>CS</name>
<name>CSP</name>
<longname>Coldsnap</longname>
</set>
<set import="1">
<name>CSTD</name>
<longname>Coldsnap Theme Decks</longname>
<name>CON</name>
<longname>Conflux</longname>
</set>
<set import="0">
<name>CMA</name>
<longname>Commander's Arsenal</longname>
</set>
<set import="0">^M
<name>C13</name>^M
<longname>Commander 2013 Edition</longname>^M
</set>
<set import="1">
<name>DCILM</name>
<longname>Legend Membership</longname>
</set>
<set import="1">
<name>DDF</name>
<longname>Duel Decks: Elspeth vs. Tezzeret</longname>
</set>
<set import="1">
<name>DDG</name>
<longname>Duel Decks: Knights vs. Dragons</longname>
</set>
<set import="1">
<name>DDH</name>
<longname>Duel Decks: Ajani vs. Nicol Bolas</longname>
</set>
<set import="1">
<name>DDI</name>
<longname>Duel Decks: Venser vs. Koth</longname>
</set>
<set import="1">
<name>DDJ</name>
<longname>Duel Decks: Izzet vs. Golgari</longname>
</set>
<set import="1">
<name>DDK</name>
<longname>Duel Decks: Sorin vs. Tibalt</longname>
</set>
<set import="1">
<name>DGM</name>
<longname>Dragon's Maze</longname>
</set>
<set import="1">
<name>DI</name>
<longname>Dissension</longname>
<name>DST</name>
<longname>Darksteel</longname>
</set>
<set import="1">
<name>DKA</name>
<longname>Dark Ascension</longname>
</set>
<set import="1">
<name>DK</name>
<longname>The Dark</longname>
<name>DIS</name>
<longname>Dissension</longname>
</set>
<set import="1">
<name>DM</name>
<longname>Deckmasters</longname>
<name>DGM</name>
<longname>Dragon's Maze</longname>
</set>
<set import="1">
<name>DPA</name>
<longname>Duels of the Planeswalkers</longname>
<set import="0">
<name>DDH</name>
<longname>Duel Decks: Ajani vs. Nicol Bolas</longname>
</set>
<set import="1">
<name>DRC</name>
<longname>Dragon Con</longname>
</set>
<set import="1">
<name>DS</name>
<longname>Darksteel</longname>
</set>
<set import="1">
<name>DVD</name>
<set import="0">
<name>DDC</name>
<longname>Duel Decks: Divine vs. Demonic</longname>
</set>
<set import="0">
<name>DDF</name>
<longname>Duel Decks: Elspeth vs. Tezzeret</longname>
</set>
<set import="0">
<name>EVG</name>
<longname>Duel Decks: Elves vs. Goblins</longname>
</set>
<set import="0">
<name>DDD</name>
<longname>Duel Decks: Garruk vs. Liliana</longname>
</set>
<set import="0">
<name>DDL</name>
<longname>Duel Decks: Heroes vs. Monsters</longname>
</set>
<set import="0">
<name>DDJ</name>
<longname>Duel Decks: Izzet vs. Golgari</longname>
</set>
<set import="0">
<name>DD2</name>
<longname>Duel Decks: Jace vs. Chandra</longname>
</set>
<set import="0">
<name>DDM</name>
<longname>Duel Decks: Jace vs. Vraska</longname>
</set>
<set import="0">
<name>DDG</name>
<longname>Duel Decks: Knights vs. Dragons</longname>
</set>
<set import="0">
<name>DDE</name>
<longname>Duel Decks: Phyrexia vs. the Coalition</longname>
</set>
<set import="0">
<name>DDK</name>
<longname>Duel Decks: Sorin vs. Tibalt</longname>
</set>
<set import="0">
<name>DDI</name>
<longname>Duel Decks: Venser vs. Koth</longname>
</set>
<set import="1">
<name>EURO</name>
<longname>European Land Program</longname>
<name>8ED</name>
<longname>Eighth Edition</longname>
</set>
<set import="1">
<name>EVE</name>
<longname>Eventide</longname>
</set>
<set import="1">
<name>EVG</name>
<longname>Duel Decks: Elves vs. Goblins</longname>
</set>
<set import="1">
<name>EX</name>
<name>EXO</name>
<longname>Exodus</longname>
</set>
<set import="1">
<name>FE</name>
<name>FEM</name>
<longname>Fallen Empires</longname>
</set>
<set import="1">
<name>FNMP</name>
<longname>Friday Night Magic</longname>
<name>5DN</name>
<longname>Fifth Dawn</longname>
</set>
<set import="1">
<name>5ED</name>
<longname>Fifth Edition</longname>
</set>
<set import="1">
<name>4ED</name>
<longname>Fourth Edition</longname>
</set>
<set import="0">
<name>DRB</name>
<longname>From the Vault: Dragons</longname>
</set>
<set import="0">
<name>V09</name>
<longname>From the Vault: Exiled</longname>
</set>
<set import="0">
<name>V11</name>
<longname>From the Vault: Legends</longname>
</set>
<set import="0">
<name>V12</name>
<longname>From the Vault: Realms</longname>
</set>
<set import="0">
<name>V10</name>
<longname>From the Vault: Relics</longname>
</set>
<set import="0">
<name>V13</name>
<longname>From the Vault: Twenty</longname>
</set>
<set import="1">
<name>FUT</name>
<longname>Future Sight</longname>
</set>
<set import="1">
<name>FVD</name>
<longname>From the Vault: Dragons</longname>
</set>
<set import="1">
<name>FVE</name>
<longname>From the Vault: Exiled</longname>
</set>
<set import="1">
<name>FVL</name>
<longname>From the Vault: Legends</longname>
</set>
<set import="1">
<name>FVR</name>
<longname>From the Vault: Relics</longname>
</set>
<set import="1">
<name>GP</name>
<name>GPT</name>
<longname>Guildpact</longname>
</set>
<set import="1">
<name>GPX</name>
<longname>Grand Prix</longname>
</set>
<set import="1">
<name>GRC</name>
<longname>WPN/Gateway</longname>
</set>
<set import="1">
<name>GTC</name>
<longname>Gatecrash</longname>
</set>
<set import="1">
<name>GURU</name>
<longname>Guru</longname>
</set>
<set import="1">
<name>GVL</name>
<longname>Duel Decks: Garruk vs. Liliana</longname>
</set>
<set import="1">
<name>HHO</name>
<longname>Happy Holidays</longname>
</set>
<set import="1">
<name>HL</name>
<name>HML</name>
<longname>Homelands</longname>
</set>
<set import="1">
<name>IA</name>
<name>ICE</name>
<longname>Ice Age</longname>
</set>
<set import="1">
<name>IN</name>
<longname>Invasion</longname>
</set>
<set import="1">
<name>ISD</name>
<longname>Innistrad</longname>
</set>
<set import="1">
<name>ITP</name>
<longname>Introductory Two-Player Set</longname>
<name>INV</name>
<longname>Invasion</longname>
</set>
<set import="1">
<name>JR</name>
<longname>Judge Gift Program</longname>
<name>JOU</name>
<longname>Journey into Nyx</longname>
</set>
<set import="1">
<name>JU</name>
<name>JUD</name>
<longname>Judgment</longname>
</set>
<set import="1">
<name>JVC</name>
<longname>Duel Decks: Jace vs. Chandra</longname>
</set>
<set import="1">
<name>LE</name>
<longname>Legions</longname>
</set>
<set import="1">
<name>LG</name>
<name>LEG</name>
<longname>Legends</longname>
</set>
<set import="1">
<name>LW</name>
<name>LGN</name>
<longname>Legions</longname>
</set>
<set import="1">
<name>LEA</name>
<longname>Limited Edition Alpha</longname>
</set>
<set import="1">
<name>LEB</name>
<longname>Limited Edition Beta</longname>
</set>
<set import="1">
<name>LRW</name>
<longname>Lorwyn</longname>
</set>
<set import="1">
@ -347,63 +268,59 @@
<longname>Magic 2013</longname>
</set>
<set import="1">
<name>MBP</name>
<longname>Media Inserts</longname>
<name>M14</name>
<longname>Magic 2014 Core Set</longname>
</set>
<set import="0">
<name>CMD</name>
<longname>Magic: The Gathering-Commander</longname>
</set>
<set import="0">
<name>CNS</name>
<longname>Magic: The Gathering—Conspiracy</longname>
</set>
<set import="0">
<name>MED</name>
<longname>Masters Edition</longname>
</set>
<set import="0">
<name>ME2</name>
<longname>Masters Edition II</longname>
</set>
<set import="0">
<name>ME3</name>
<longname>Masters Edition III</longname>
</set>
<set import="0">
<name>ME4</name>
<longname>Masters Edition IV</longname>
</set>
<set import="1">
<name>MMQ</name>
<longname>Mercadian Masques</longname>
</set>
<set import="1">
<name>MIR</name>
<longname>Mirage</longname>
</set>
<set import="1">
<name>MRD</name>
<longname>Mirrodin</longname>
</set>
<set import="1">
<name>MBS</name>
<longname>Mirrodin Besieged</longname>
</set>
<set import="0">^M
<name>MMA</name>^M
<longname>Modern Masters</longname>^M
</set>^
<set import="1">
<name>ME2</name>
<longname>MTGO Masters Edition II</longname>
</set>
<set import="1">
<name>ME3</name>
<longname>MTGO Masters Edition III</longname>
</set>
<set import="1">
<name>ME4</name>
<longname>MTGO Masters Edition IV</longname>
</set>
<set import="1">
<name>MED</name>
<longname>MTGO Masters Edition</longname>
</set>
<set import="1">
<name>MGBC</name>
<longname>Multiverse Gift Box Cards</longname>
</set>
<set import="1">
<name>MGDC</name>
<longname>Magic Game Day Cards</longname>
</set>
<set import="1">
<name>MI</name>
<longname>Mirrodin</longname>
</set>
<set import="1">
<name>MLP</name>
<longname>Magic: The Gathering Launch Parties</longname>
</set>
<set import="1">
<name>MM</name>
<longname>Mercadian Masques</longname>
</set>
<set import="1">
<name>MPRP</name>
<longname>Magic Player Rewards</longname>
</set>
<set import="1">
<name>MR</name>
<longname>Mirage</longname>
</set>
<set import="1">
<name>MT</name>
<name>MOR</name>
<longname>Morningtide</longname>
</set>
<set import="1">
<name>NE</name>
<name>NMS</name>
<longname>Nemesis</longname>
</set>
<set import="1">
@ -411,104 +328,80 @@
<longname>New Phyrexia</longname>
</set>
<set import="1">
<name>OD</name>
<name>9ED</name>
<longname>Ninth Edition</longname>
</set>
<set import="1">
<name>ODY</name>
<longname>Odyssey</longname>
</set>
<set import="1">
<name>ON</name>
<name>ONS</name>
<longname>Onslaught</longname>
</set>
<set import="1">
<name>P3K</name>
<longname>Portal Three Kingdoms</longname>
<name>PLC</name>
<longname>Planar Chaos</longname>
</set>
<set import="1">
<set import="0">
<name>HOP</name>
<longname>Planechase</longname>
</set>
<set import="0">
<name>PC2</name>
<longname>Planechase 2012 Edition</longname>
</set>
<set import="1">
<name>PC</name>
<longname>Planar Chaos</longname>
<name>PLS</name>
<longname>Planeshift</longname>
</set>
<set import="1">
<name>PCH</name>
<longname>Planechase</longname>
</set>
<set import="1">
<name>PD2</name>
<longname>Premium Deck Series: Fire and Lightning</longname>
</set>
<set import="1">
<name>PD3</name>
<longname>Premium Deck Series: Graveborn</longname>
</set>
<set import="1">
<name>PDS</name>
<longname>Premium Deck Series: Slivers</longname>
<name>POR</name>
<longname>Portal</longname>
</set>
<set import="1">
<name>PO2</name>
<longname>Portal Second Age</longname>
</set>
<set import="1">
<name>PO</name>
<longname>Portal</longname>
<name>PTK</name>
<longname>Portal Three Kingdoms</longname>
</set>
<set import="0">
<name>PD2</name>
<longname>Premium Deck Series: Fire and Lightning</longname>
</set>
<set import="0">
<name>PD3</name>
<longname>Premium Deck Series: Graveborn</longname>
</set>
<set import="0">
<name>H09</name>
<longname>Premium Deck Series: Slivers</longname>
</set>
<set import="0">
<name>PPR</name>
<longname>Promo set for Gatherer</longname>
</set>
<set import="1">
<name>POT</name>
<longname>Portal Demogame</longname>
</set>
<set import="1">
<name>PR</name>
<name>PCY</name>
<longname>Prophecy</longname>
</set>
<set import="1">
<name>PRO</name>
<longname>Pro Tour</longname>
</set>
<set import="1">
<name>PS</name>
<longname>Planeshift</longname>
</set>
<set import="1">
<name>PTC</name>
<longname>Prerelease Events</longname>
</set>
<set import="1">
<name>PVC</name>
<longname>Duel Decks: Phyrexia vs. The Coalition</longname>
</set>
<set import="1">
<name>RAV</name>
<longname>Ravnica: City of Guilds</longname>
</set>
<set import="1">
<name>REP</name>
<longname>Release Events</longname>
</set>
<set import="1">
<name>ROE</name>
<longname>Rise of the Eldrazi</longname>
</set>
<set import="1">
<name>RTR</name>
<longname>Return to Ravnica</longname>
</set>
<set import="1">
<name>RV</name>
<name>3ED</name>
<longname>Revised Edition</longname>
</set>
<set import="1">
<name>SC</name>
<longname>Scourge</longname>
</set>
<set import="1">
<name>SH</name>
<longname>Stronghold</longname>
</set>
<set import="1">
<name>SHM</name>
<longname>Shadowmoor</longname>
<name>ROE</name>
<longname>Rise of the Eldrazi</longname>
</set>
<set import="1">
<name>SOK</name>
@ -519,97 +412,97 @@
<longname>Scars of Mirrodin</longname>
</set>
<set import="1">
<name>ST2K</name>
<longname>Starter 2000</longname>
<name>SCG</name>
<longname>Scourge</longname>
</set>
<set import="1">
<name>ST</name>
<name>7ED</name>
<longname>Seventh Edition</longname>
</set>
<set import="1">
<name>SHM</name>
<longname>Shadowmoor</longname>
</set>
<set import="1">
<name>ALA</name>
<longname>Shards of Alara</longname>
</set>
<set import="1">
<name>S99</name>
<longname>Starter 1999</longname>
</set>
<set import="1">
<name>SUM</name>
<longname>Summer of Magic</longname>
<name>S00</name>
<longname>Starter 2000</longname>
</set>
<set import="1">
<name>SUS</name>
<longname>Super Series</longname>
<name>STH</name>
<longname>Stronghold</longname>
</set>
<set import="1">
<name>THGT</name>
<longname>Two-Headed Giant Tournament</longname>
</set>
<set import="1">
<name>TP</name>
<name>TMP</name>
<longname>Tempest</longname>
</set>
<set import="1">
<name>TR</name>
<longname>Torment</longname>
<name>10E</name>
<longname>Tenth Edition</longname>
</set>
<set import="1">
<name>TS</name>
<name>DRK</name>
<longname>The Dark</longname>
</set>
<set import="1">
<name>THS</name>
<longname>Theros</longname>
</set>
<set import="1">
<name>TSP</name>
<longname>Time Spiral</longname>
</set>
<set import="1">
<name>TSTS</name>
<name>TSB</name>
<longname>Time Spiral "Timeshifted"</longname>
</set>
<set import="1">
<name>UD</name>
<longname>Urza's Destiny</longname>
<name>TOR</name>
<longname>Torment</longname>
</set>
<set import="1">
<name>UG</name>
<set import="0">
<name>UGL</name>
<longname>Unglued</longname>
</set>
<set import="1">
<name>UHAA</name>
<longname>Unhinged Alternate Foils</longname>
</set>
<set import="1">
<name>UH</name>
<set import="0">
<name>UNH</name>
<longname>Unhinged</longname>
</set>
<set import="1">
<name>UL</name>
<longname>Urza's Legacy</longname>
</set>
<set import="1">
<name>UN</name>
<name>2ED</name>
<longname>Unlimited Edition</longname>
</set>
<set import="1">
<name>UQC</name>
<longname>Celebration Cards</longname>
<name>UDS</name>
<longname>Urza's Destiny</longname>
</set>
<set import="1">
<name>US</name>
<name>ULG</name>
<longname>Urza's Legacy</longname>
</set>
<set import="1">
<name>USG</name>
<longname>Urza's Saga</longname>
</set>
<set import="1">
<name>V12</name>
<longname>From the Vault: Realms</longname>
<set import="0">
<name>VAN</name>
<longname>Vanguard</longname>
</set>
<set import="1">
<name>VI</name>
<name>VIS</name>
<longname>Visions</longname>
</set>
<set import="1">
<name>WL</name>
<name>WTH</name>
<longname>Weatherlight</longname>
</set>
<set import="1">
<name>WMCQ</name>
<longname>World Magic Cup Qualifiers</longname>
</set>
<set import="1">
<name>WOTC</name>
<longname>WotC Online Store</longname>
</set>
<set import="1">
<name>WRL</name>
<longname>Worlds</longname>
</set>
<set import="1">
<name>WWK</name>
<longname>Worldwake</longname>

View file

@ -9,10 +9,11 @@ set(DESKTOPDIR share/applications CACHE STRING "path to .desktop files")
SET(oracle_SOURCES
src/main.cpp
src/oraclewizard.cpp
src/oracleimporter.cpp
src/window_main.cpp
../cockatrice/src/carddatabase.cpp
../cockatrice/src/settingscache.cpp
../cockatrice/src/qt-json/json.cpp
)
SET(QT_USE_QTNETWORK TRUE)

View file

@ -1,6 +1,6 @@
#include <QApplication>
#include <QTextCodec>
#include "window_main.h"
#include "oraclewizard.h"
#include "settingscache.h"
SettingsCache *settingsCache;
@ -12,13 +12,14 @@ int main(int argc, char *argv[])
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QCoreApplication::setOrganizationName("Cockatrice");
QCoreApplication::setOrganizationDomain("cockatrice.de");
QCoreApplication::setOrganizationDomain("cockatrice");
// this can't be changed, as it influences the default savepath for cards.xml
QCoreApplication::setApplicationName("Cockatrice");
settingsCache = new SettingsCache;
WindowMain wnd;
wnd.show();
OracleWizard wizard;
wizard.show();
return app.exec();
}

View file

@ -1,325 +1,242 @@
#include "oracleimporter.h"
#include <QtGui>
#include <QtNetwork>
#include <QXmlStreamReader>
#include <QDomDocument>
#include <QDebug>
#include "qt-json/json.h"
OracleImporter::OracleImporter(const QString &_dataDir, QObject *parent)
: CardDatabase(parent), dataDir(_dataDir), setIndex(-1)
: CardDatabase(parent), dataDir(_dataDir)
{
buffer = new QBuffer(this);
http = new QHttp(this);
connect(http, SIGNAL(requestFinished(int, bool)), this, SLOT(httpRequestFinished(int, bool)));
connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)), this, SLOT(readResponseHeader(const QHttpResponseHeader &)));
connect(http, SIGNAL(dataReadProgress(int, int)), this, SIGNAL(dataReadProgress(int, int)));
}
bool OracleImporter::readSetsFromFile(const QString &fileName)
{
QFile setsFile(fileName);
if (!setsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::critical(0, tr("Error"), tr("Cannot open file '%1'.").arg(fileName));
return false;
}
QXmlStreamReader xml(&setsFile);
return readSetsFromXml(xml);
}
bool OracleImporter::readSetsFromByteArray(const QByteArray &data)
{
QXmlStreamReader xml(data);
return readSetsFromXml(xml);
}
QList<SetToDownload> newSetList;
bool ok;
setsMap = QtJson::Json::parse(QString(data), ok).toMap();
if (!ok) {
qDebug() << "error: QtJson::Json::parse()";
return 0;
}
QListIterator<QVariant> it(setsMap.values());
QVariantMap map;
bool OracleImporter::readSetsFromXml(QXmlStreamReader &xml)
{
QList<SetToDownload> newSetList;
QString edition;
QString editionLong;
QString editionURL;
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement)
break;
if (xml.name() == "set") {
bool import = xml.attributes().value("import").toString().toInt();
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement)
break;
if (xml.name() == "name")
edition = xml.readElementText();
else if (xml.name() == "longname")
editionLong = xml.readElementText();
else if (xml.name() == "url")
editionURL = xml.readElementText();
}
newSetList.append(SetToDownload(edition, editionLong, editionURL, import));
edition = editionLong = editionURL = QString();
} else if (xml.name() == "picture_url")
pictureUrl = xml.readElementText();
else if (xml.name() == "picture_url_hq")
pictureUrlHq = xml.readElementText();
else if (xml.name() == "picture_url_st")
pictureUrlSt = xml.readElementText();
else if (xml.name() == "set_url")
setUrl = xml.readElementText();
}
if (newSetList.isEmpty())
return false;
allSets = newSetList;
return true;
QString edition;
QString editionLong;
QVariant editionCards;
bool import;
while (it.hasNext()) {
map = it.next().toMap();
edition = map.value("code").toString();
editionLong = map.value("name").toString();
editionCards = map.value("cards");
// core and expansion sets are marked to be imported by default
import = (0 == QString::compare(map.value("type").toString(), QString("core"), Qt::CaseInsensitive) ||
0 == QString::compare(map.value("type").toString(), QString("expansion"), Qt::CaseInsensitive));
newSetList.append(SetToDownload(edition, editionLong, editionCards, import));
}
qSort(newSetList);
if (newSetList.isEmpty())
return false;
allSets = newSetList;
return true;
}
CardInfo *OracleImporter::addCard(const QString &setName,
QString cardName,
bool isToken,
int cardId,
const QString &cardCost,
const QString &cardType,
const QString &cardPT,
int cardLoyalty,
const QStringList &cardText)
QString cardName,
bool isToken,
int cardId,
QString &cardCost,
const QString &cardType,
const QString &cardPT,
int cardLoyalty,
const QStringList &cardText)
{
QString fullCardText = cardText.join("\n");
bool splitCard = false;
if (cardName.contains('(')) {
cardName.remove(QRegExp(" \\(.*\\)"));
splitCard = true;
}
// Workaround for card name weirdness
if (cardName.contains("XX"))
cardName.remove("XX");
cardName = cardName.replace("Æ", "AE");
cardName = cardName.replace("", "'");
QString fullCardText = cardText.join("\n");
bool splitCard = false;
if (cardName.contains('(')) {
cardName.remove(QRegExp(" \\(.*\\)"));
splitCard = true;
}
// Workaround for card name weirdness
if (cardName.contains("XX"))
cardName.remove("XX");
cardName = cardName.replace("Æ", "AE");
cardName = cardName.replace("", "'");
CardInfo *card;
if (cardHash.contains(cardName)) {
card = cardHash.value(cardName);
if (splitCard && !card->getText().contains(fullCardText))
card->setText(card->getText() + "\n---\n" + fullCardText);
} else {
bool mArtifact = false;
if (cardType.endsWith("Artifact"))
for (int i = 0; i < cardText.size(); ++i)
if (cardText[i].contains("{T}") && cardText[i].contains("to your mana pool"))
mArtifact = true;
QStringList colors;
QStringList allColors = QStringList() << "W" << "U" << "B" << "R" << "G";
for (int i = 0; i < allColors.size(); i++)
if (cardCost.contains(allColors[i]))
colors << allColors[i];
if (cardText.contains(cardName + " is white."))
colors << "W";
if (cardText.contains(cardName + " is blue."))
colors << "U";
if (cardText.contains(cardName + " is black."))
colors << "B";
if (cardText.contains(cardName + " is red."))
colors << "R";
if (cardText.contains(cardName + " is green."))
colors << "G";
bool cipt = (cardText.contains(cardName + " enters the battlefield tapped."));
card = new CardInfo(this, cardName, isToken, cardCost, cardType, cardPT, fullCardText, colors, cardLoyalty, cipt);
int tableRow = 1;
QString mainCardType = card->getMainCardType();
if ((mainCardType == "Land") || mArtifact)
tableRow = 0;
else if ((mainCardType == "Sorcery") || (mainCardType == "Instant"))
tableRow = 3;
else if (mainCardType == "Creature")
tableRow = 2;
card->setTableRow(tableRow);
cardHash.insert(cardName, card);
}
card->setPicURL(setName, getPictureUrl(pictureUrl, cardId, cardName, setName));
card->setPicURLHq(setName, getPictureUrl(pictureUrlHq, cardId, cardName, setName));
card->setPicURLSt(setName, getPictureUrl(pictureUrlSt, cardId, cardName, setName));
return card;
// Remove {} around mana costs
cardCost.remove(QChar('{'));
cardCost.remove(QChar('}'));
CardInfo *card;
if (cardHash.contains(cardName)) {
card = cardHash.value(cardName);
if (splitCard && !card->getText().contains(fullCardText))
card->setText(card->getText() + "\n---\n" + fullCardText);
} else {
bool mArtifact = false;
if (cardType.endsWith("Artifact"))
for (int i = 0; i < cardText.size(); ++i)
if (cardText[i].contains("{T}") && cardText[i].contains("to your mana pool"))
mArtifact = true;
QStringList colors;
QStringList allColors = QStringList() << "W" << "U" << "B" << "R" << "G";
for (int i = 0; i < allColors.size(); i++)
if (cardCost.contains(allColors[i]))
colors << allColors[i];
if (cardText.contains(cardName + " is white."))
colors << "W";
if (cardText.contains(cardName + " is blue."))
colors << "U";
if (cardText.contains(cardName + " is black."))
colors << "B";
if (cardText.contains(cardName + " is red."))
colors << "R";
if (cardText.contains(cardName + " is green."))
colors << "G";
bool cipt = (cardText.contains(cardName + " enters the battlefield tapped."));
card = new CardInfo(this, cardName, isToken, cardCost, cardType, cardPT, fullCardText, colors, cardLoyalty, cipt);
int tableRow = 1;
QString mainCardType = card->getMainCardType();
if ((mainCardType == "Land") || mArtifact)
tableRow = 0;
else if ((mainCardType == "Sorcery") || (mainCardType == "Instant"))
tableRow = 3;
else if (mainCardType == "Creature")
tableRow = 2;
card->setTableRow(tableRow);
cardHash.insert(cardName, card);
}
card->setMuId(setName, cardId);
return card;
}
int OracleImporter::importTextSpoiler(CardSet *set, const QByteArray &data)
int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data)
{
int cards = 0;
QString bufferContents(data);
// Workaround for ampersand bug in text spoilers
int index = -1;
while ((index = bufferContents.indexOf('&', index + 1)) != -1) {
int semicolonIndex = bufferContents.indexOf(';', index);
if (semicolonIndex > 5) {
bufferContents.insert(index + 1, "amp;");
index += 4;
}
}
QDomDocument doc;
QString errorMsg;
int errorLine, errorColumn;
if (!doc.setContent(bufferContents, &errorMsg, &errorLine, &errorColumn))
qDebug() << "error:" << errorMsg << "line:" << errorLine << "column:" << errorColumn;
int cards = 0;
QListIterator<QVariant> it(data.toList());
QVariantMap map;
QString cardName;
QString cardCost;
QString cardType;
QString cardPT;
QString cardText;
int cardId;
int cardLoyalty;
QMap<int, QVariantMap> splitCards;
QDomNodeList divs = doc.elementsByTagName("div");
for (int i = 0; i < divs.size(); ++i) {
QDomElement div = divs.at(i).toElement();
QDomNode divClass = div.attributes().namedItem("class");
if (divClass.nodeValue() == "textspoiler") {
QString cardName, cardCost, cardType, cardPT, cardText;
int cardId = 0;
int cardLoyalty = 0;
QDomNodeList trs = div.elementsByTagName("tr");
for (int j = 0; j < trs.size(); ++j) {
QDomElement tr = trs.at(j).toElement();
QDomNodeList tds = tr.elementsByTagName("td");
if (tds.size() != 2) {
QStringList cardTextSplit = cardText.split("\n");
for (int i = 0; i < cardTextSplit.size(); ++i)
cardTextSplit[i] = cardTextSplit[i].trimmed();
CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cardType, cardPT, cardLoyalty, cardTextSplit);
if (!set->contains(card)) {
card->addToSet(set);
cards++;
}
cardName = cardCost = cardType = cardPT = cardText = QString();
} else {
QString v1 = tds.at(0).toElement().text().simplified();
QString v2 = tds.at(1).toElement().text().replace(trUtf8(""), "-");
if (v1 == "Name") {
QDomElement a = tds.at(1).toElement().elementsByTagName("a").at(0).toElement();
QString href = a.attributes().namedItem("href").nodeValue();
cardId = href.mid(href.indexOf("multiverseid=") + 13).toInt();
cardName = v2.simplified();
} else if (v1 == "Cost:")
cardCost = v2.simplified();
else if (v1 == "Type:")
cardType = v2.simplified();
else if (v1 == "Pow/Tgh:")
cardPT = v2.simplified().remove('(').remove(')');
else if (v1 == "Rules Text:")
cardText = v2.trimmed();
else if (v1 == "Loyalty:")
cardLoyalty = v2.trimmed().remove('(').remove(')').toInt();
}
}
break;
}
}
return cards;
while (it.hasNext()) {
map = it.next().toMap();
if(0 == QString::compare(map.value("layout").toString(), QString("split"), Qt::CaseInsensitive))
{
// Split card handling
cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0;
if(splitCards.contains(cardId))
{
// merge two split cards
QVariantMap tmpMap = splitCards.take(cardId);
QVariantMap * card1 = 0, * card2 = 0;
// same cardid
cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0;
// this is currently an integer; can't accept 2 values
cardLoyalty = 0;
// determine which subcard is the first one in the split
QStringList names=map.contains("names") ? map.value("names").toStringList() : QStringList("");
if(names.count()>0 &&
map.contains("name") &&
0 == QString::compare(map.value("name").toString(), names.at(0)))
{
// map is the left part of the split card, tmpMap is right part
card1 = &map;
card2 = &tmpMap;
} else {
//tmpMap is the left part of the split card, map is right part
card1 = &tmpMap;
card2 = &map;
}
// add first card's data
cardName = card1->contains("name") ? card1->value("name").toString() : QString("");
cardCost = card1->contains("manaCost") ? card1->value("manaCost").toString() : QString("");
cardType = card1->contains("type") ? card1->value("type").toString() : QString("");
cardPT = card1->contains("power") || card1->contains("toughness") ? card1->value("power").toString() + QString('/') + card1->value("toughness").toString() : QString("");
cardText = card1->contains("text") ? card1->value("text").toString() : QString("");
// add second card's data
cardName += card2->contains("name") ? QString(" // ") + card2->value("name").toString() : QString("");
cardCost += card2->contains("manaCost") ? QString(" // ") + card2->value("manaCost").toString() : QString("");
cardType += card2->contains("type") ? QString(" // ") + card2->value("type").toString() : QString("");
cardPT += card2->contains("power") || card2->contains("toughness") ? QString(" // ") + card2->value("power").toString() + QString('/') + card2->value("toughness").toString() : QString("");
cardText += card2->contains("text") ? QString("\n\n---\n\n") + card2->value("text").toString() : QString("");
} else {
// first card od a pair; enqueue for later merging
splitCards.insert(cardId, map);
continue;
}
} else {
// normal cards handling
cardName = map.contains("name") ? map.value("name").toString() : QString("");
cardCost = map.contains("manaCost") ? map.value("manaCost").toString() : QString("");
cardType = map.contains("type") ? map.value("type").toString() : QString("");
cardPT = map.contains("power") || map.contains("toughness") ? map.value("power").toString() + QString('/') + map.value("toughness").toString() : QString("");
cardText = map.contains("text") ? map.value("text").toString() : QString("");
cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0;
cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0;
}
CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n"));
if (!set->contains(card)) {
card->addToSet(set);
cards++;
}
}
return cards;
}
QString OracleImporter::getPictureUrl(QString url, int cardId, QString name, const QString &setName) const
int OracleImporter::startImport()
{
if ((name == "Island") || (name == "Swamp") || (name == "Mountain") || (name == "Plains") || (name == "Forest"))
name.append("1");
return url.replace("!cardid!", QString::number(cardId)).replace("!set!", setName).replace("!name!", name
.replace("ö", "o")
// .remove('\'')
.remove(" // ")
// .remove(',')
// .remove(':')
// .remove('.')
.remove(QRegExp("\\(.*\\)"))
.simplified()
// .replace(' ', '_')
// .replace('-', '_')
);
}
int OracleImporter::startDownload()
{
clear();
setsToDownload.clear();
for (int i = 0; i < allSets.size(); ++i)
if (allSets[i].getImport())
setsToDownload.append(allSets[i]);
if (setsToDownload.isEmpty())
return 0;
setIndex = 0;
emit setIndexChanged(0, 0, setsToDownload[0].getLongName());
downloadNextFile();
return setsToDownload.size();
}
void OracleImporter::downloadNextFile()
{
QString urlString = setsToDownload[setIndex].getUrl();
if (urlString.isEmpty())
urlString = setUrl;
urlString = urlString.replace("!longname!", setsToDownload[setIndex].getLongName());
if (urlString.startsWith("http://")) {
QUrl url(urlString);
http->setHost(url.host(), QHttp::ConnectionModeHttp, url.port() == -1 ? 0 : url.port());
QString path = QUrl::toPercentEncoding(urlString.mid(url.host().size() + 7).replace(' ', '+'), "?!$&'()*+,;=:@/");
buffer->close();
buffer->setData(QByteArray());
buffer->open(QIODevice::ReadWrite | QIODevice::Text);
reqId = http->get(path, buffer);
} else {
QFile file(dataDir + "/" + urlString);
file.open(QIODevice::ReadOnly | QIODevice::Text);
buffer->close();
buffer->setData(file.readAll());
buffer->open(QIODevice::ReadWrite | QIODevice::Text);
reqId = 0;
httpRequestFinished(reqId, false);
}
}
void OracleImporter::httpRequestFinished(int requestId, bool error)
{
if (error) {
QMessageBox::information(0, tr("HTTP"), tr("Error."));
return;
}
if (requestId != reqId)
return;
CardSet *set = new CardSet(setsToDownload[setIndex].getShortName(), setsToDownload[setIndex].getLongName());
if (!setHash.contains(set->getShortName()))
setHash.insert(set->getShortName(), set);
buffer->seek(0);
buffer->close();
int cards = importTextSpoiler(set, buffer->data());
if (cards > 0)
++setIndex;
if (setIndex == setsToDownload.size()) {
emit setIndexChanged(cards, setIndex, QString());
setIndex = -1;
} else {
downloadNextFile();
emit setIndexChanged(cards, setIndex, setsToDownload[setIndex].getLongName());
}
}
void OracleImporter::readResponseHeader(const QHttpResponseHeader &responseHeader)
{
switch (responseHeader.statusCode()) {
case 200:
case 301:
case 302:
case 303:
case 307:
break;
default:
QMessageBox::information(0, tr("HTTP"), tr("Download failed: %1.").arg(responseHeader.reasonPhrase()));
http->abort();
deleteLater();
}
clear();
int setCards = 0, setIndex= 0;
QListIterator<SetToDownload> it(allSets);
const SetToDownload * curSet;
while (it.hasNext())
{
curSet = & it.next();
if(!curSet->getImport())
continue;
CardSet *set = new CardSet(curSet->getShortName(), curSet->getLongName());
if (!setHash.contains(set->getShortName()))
setHash.insert(set->getShortName(), set);
int setCards = importTextSpoiler(set, curSet->getCards());
++setIndex;
emit setIndexChanged(setCards, setIndex, curSet->getLongName());
}
emit setIndexChanged(setCards, setIndex, QString());
// total number of sets
return setIndex;
}

View file

@ -1,53 +1,42 @@
#ifndef ORACLEIMPORTER_H
#define ORACLEIMPORTER_H
#include <carddatabase.h>
#include <QHttp>
#include <QMap>
class QBuffer;
class QXmlStreamReader;
#include <carddatabase.h>
class SetToDownload {
private:
QString shortName, longName, url;
QString shortName, longName;
bool import;
QVariant cards;
public:
const QString &getShortName() const { return shortName; }
const QString &getLongName() const { return longName; }
const QString &getUrl() const { return url; }
const QVariant &getCards() const { return cards; }
bool getImport() const { return import; }
void setImport(bool _import) { import = _import; }
SetToDownload(const QString &_shortName, const QString &_longName, const QString &_url, bool _import)
: shortName(_shortName), longName(_longName), url(_url), import(_import) { }
SetToDownload(const QString &_shortName, const QString &_longName, const QVariant &_cards, bool _import)
: shortName(_shortName), longName(_longName), cards(_cards), import(_import) { }
bool operator<(const SetToDownload &set) const { return longName.compare(set.longName, Qt::CaseInsensitive) < 0; }
};
class OracleImporter : public CardDatabase {
Q_OBJECT
private:
QList<SetToDownload> allSets, setsToDownload;
QString pictureUrl, pictureUrlHq, pictureUrlSt, setUrl;
QList<SetToDownload> allSets;
QVariantMap setsMap;
QString dataDir;
int setIndex;
int reqId;
QBuffer *buffer;
QHttp *http;
QString getPictureUrl(QString url, int cardId, QString name, const QString &setName) const;
void downloadNextFile();
bool readSetsFromXml(QXmlStreamReader &xml);
CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, const QString &cardCost, const QString &cardType, const QString &cardPT, int cardLoyalty, const QStringList &cardText);
private slots:
void httpRequestFinished(int requestId, bool error);
void readResponseHeader(const QHttpResponseHeader &responseHeader);
CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, QString &cardCost, const QString &cardType, const QString &cardPT, int cardLoyalty, const QStringList &cardText);
signals:
void setIndexChanged(int cardsImported, int setIndex, const QString &nextSetName);
void setIndexChanged(int cardsImported, int setIndex, const QString &setName);
void dataReadProgress(int bytesRead, int totalBytes);
public:
OracleImporter(const QString &_dataDir, QObject *parent = 0);
bool readSetsFromByteArray(const QByteArray &data);
bool readSetsFromFile(const QString &fileName);
int startDownload();
int importTextSpoiler(CardSet *set, const QByteArray &data);
int startImport();
int importTextSpoiler(CardSet *set, const QVariant &data);
QList<SetToDownload> &getSets() { return allSets; }
const QString &getDataDir() const { return dataDir; }
};

398
oracle/src/oraclewizard.cpp Normal file
View file

@ -0,0 +1,398 @@
#include <QtGui>
#include <QGridLayout>
#include <QDesktopServices>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include "oraclewizard.h"
#include "oracleimporter.h"
#define ALLSETS_URL "http://mtgjson.com/json/AllSets.json"
OracleWizard::OracleWizard(QWidget *parent)
: QWizard(parent)
{
settings = new QSettings(this);
importer = new OracleImporter(QDesktopServices::storageLocation(QDesktopServices::DataLocation), 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);
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(fileRadioButton, 1, 0);
layout->addWidget(fileLineEdit, 1, 1);
layout->addWidget(fileButton, 2, 1, Qt::AlignRight);
layout->addWidget(progressLabel, 3, 0);
layout->addWidget(progressBar, 3, 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::actLoadSetsFile()
{
QFileDialog dialog(this, tr("Load sets file"));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter("Sets JSON file (*.json)");
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 | QIODevice::Text)) {
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<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;
}
// 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();
// 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."));
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<SetToDownload> &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<QCheckBox *>(sender());
QList<SetToDownload> &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("<b>" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + "</b>");
} else {
messageLog->append(tr("%1: %2 cards imported").arg(setName).arg(cardsImported));
}
messageLog->verticalScrollBar()->setValue(messageLog->verticalScrollBar()->maximum());
}
bool SaveSetsPage::validatePage()
{
bool ok = false;
const QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
QDir dir(dataDir);
if (!dir.exists())
dir.mkpath(dataDir);
QString savePath = dataDir + "/cards.xml";
do {
QString fileName;
if (savePath.isEmpty() || !defaultPathCheckBox->isChecked())
fileName = QFileDialog::getSaveFileName(this, tr("Save card database"), dataDir + "/cards.xml", tr("XML card database (*.xml)"));
else {
fileName = savePath;
savePath.clear();
}
if (fileName.isEmpty()) {
return false;
}
if (wizard()->importer->saveToFile(fileName))
ok = true;
else
QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to the desired location."));
} while (!ok);
return true;
}

114
oracle/src/oraclewizard.h Normal file
View file

@ -0,0 +1,114 @@
#ifndef ORACLEWIZARD_H
#define ORACLEWIZARD_H
#include <QWizard>
#include <QFutureWatcher>
#include <QFuture>
class QCheckBox;
class QGroupBox;
class QLabel;
class QLineEdit;
class QRadioButton;
class QProgressBar;
class QNetworkAccessManager;
class QTextEdit;
class QVBoxLayout;
class OracleImporter;
class QSettings;
class OracleWizard : public QWizard
{
Q_OBJECT
public:
OracleWizard(QWidget *parent = 0);
void accept();
void enableButtons();
void disableButtons();
public:
OracleImporter *importer;
QSettings * settings;
};
class OracleWizardPage : public QWizardPage
{
Q_OBJECT
public:
OracleWizardPage(QWidget *parent = 0): QWizardPage(parent) {};
protected:
inline OracleWizard *wizard() { return (OracleWizard*) QWizardPage::wizard(); };
};
class IntroPage : public OracleWizardPage
{
Q_OBJECT
public:
IntroPage(QWidget *parent = 0);
private:
QLabel *label;
};
class LoadSetsPage : public OracleWizardPage
{
Q_OBJECT
public:
LoadSetsPage(QWidget *parent = 0);
protected:
void initializePage();
bool validatePage();
void readSetsFromByteArray(QByteArray data);
private:
QRadioButton *urlRadioButton;
QRadioButton *fileRadioButton;
QLineEdit *urlLineEdit;
QLineEdit *fileLineEdit;
QPushButton *fileButton;
QLabel *progressLabel;
QProgressBar * progressBar;
QNetworkAccessManager *nam;
QFutureWatcher<bool> watcher;
QFuture<bool> future;
private slots:
void actLoadSetsFile();
void actDownloadProgressSetsFile(qint64 received, qint64 total);
void actDownloadFinishedSetsFile();
void importFinished();
};
class ChooseSetsPage : public OracleWizardPage
{
Q_OBJECT
public:
ChooseSetsPage(QWidget *parent = 0);
protected:
void initializePage();
bool validatePage();
private:
QPushButton *checkAllButton, *uncheckAllButton;
QVBoxLayout *checkBoxLayout;
QList<QCheckBox *> checkBoxList;
private slots:
void actCheckAll();
void actUncheckAll();
void checkBoxChanged(int state);
};
class SaveSetsPage : public OracleWizardPage
{
Q_OBJECT
public:
SaveSetsPage(QWidget *parent = 0);
private:
QTextEdit *messageLog;
QCheckBox * defaultPathCheckBox;
protected:
void initializePage();
void cleanupPage();
bool validatePage();
private slots:
void updateTotalProgress(int cardsImported, int setIndex, const QString &setName);
};
#endif

View file

@ -1,248 +0,0 @@
#include <QApplication>
#include <QMenu>
#include <QMenuBar>
#include <QStatusBar>
#include <QLineEdit>
#include <QFileDialog>
#include <QMessageBox>
#include <QScrollArea>
#include <QTextEdit>
#include <QInputDialog>
#include <QLabel>
#include <QPushButton>
#include <QCheckBox>
#include <QProgressBar>
#include <QVBoxLayout>
#include <QDesktopServices>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include "window_main.h"
#include "oracleimporter.h"
const QString WindowMain::defaultSetsUrl = QString("http://www.woogerworks.com/files/sets.xml");
WindowMain::WindowMain(QWidget *parent)
: QMainWindow(parent)
{
importer = new OracleImporter(QDesktopServices::storageLocation(QDesktopServices::DataLocation), this);
nam = new QNetworkAccessManager(this);
checkBoxLayout = new QVBoxLayout;
QWidget *checkboxFrame = new QWidget;
checkboxFrame->setLayout(checkBoxLayout);
QScrollArea *checkboxArea = new QScrollArea;
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()));
QHBoxLayout *checkAllButtonLayout = new QHBoxLayout;
checkAllButtonLayout->addWidget(checkAllButton);
checkAllButtonLayout->addWidget(uncheckAllButton);
startButton = new QPushButton(tr("&Start download"));
connect(startButton, SIGNAL(clicked()), this, SLOT(actStart()));
QVBoxLayout *settingsLayout = new QVBoxLayout;
settingsLayout->addWidget(checkboxArea);
settingsLayout->addLayout(checkAllButtonLayout);
settingsLayout->addWidget(startButton);
totalLabel = new QLabel(tr("Total progress:"));
totalProgressBar = new QProgressBar;
nextSetLabel1 = new QLabel(tr("Current file:"));
nextSetLabel2 = new QLabel;
fileLabel = new QLabel(tr("Progress:"));
fileProgressBar = new QProgressBar;
messageLog = new QTextEdit;
messageLog->setReadOnly(true);
QGridLayout *grid = new QGridLayout;
grid->addWidget(totalLabel, 0, 0);
grid->addWidget(totalProgressBar, 0, 1);
grid->addWidget(nextSetLabel1, 1, 0);
grid->addWidget(nextSetLabel2, 1, 1);
grid->addWidget(fileLabel, 2, 0);
grid->addWidget(fileProgressBar, 2, 1);
grid->addWidget(messageLog, 3, 0, 1, 2);
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addLayout(settingsLayout, 6);
mainLayout->addSpacing(10);
mainLayout->addLayout(grid, 10);
QWidget *centralWidget = new QWidget;
centralWidget->setLayout(mainLayout);
setCentralWidget(centralWidget);
connect(importer, SIGNAL(setIndexChanged(int, int, const QString &)), this, SLOT(updateTotalProgress(int, int, const QString &)));
connect(importer, SIGNAL(dataReadProgress(int, int)), this, SLOT(updateFileProgress(int, int)));
aLoadSetsFile = new QAction(tr("Load sets information from &file..."), this);
connect(aLoadSetsFile, SIGNAL(triggered()), this, SLOT(actLoadSetsFile()));
aDownloadSetsFile = new QAction(tr("&Download sets information..."), this);
connect(aDownloadSetsFile, SIGNAL(triggered()), this, SLOT(actDownloadSetsFile()));
aExit = new QAction(tr("E&xit"), this);
connect(aExit, SIGNAL(triggered()), this, SLOT(close()));
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(aLoadSetsFile);
fileMenu->addAction(aDownloadSetsFile);
fileMenu->addSeparator();
fileMenu->addAction(aExit);
setWindowTitle(tr("Oracle importer"));
setMinimumSize(750, 500);
QStringList args = qApp->arguments();
if (args.contains("-dlsets"))
downloadSetsFile(defaultSetsUrl);
statusLabel = new QLabel;
statusBar()->addWidget(statusLabel);
statusLabel->setText(tr("Sets data not loaded."));
}
void WindowMain::updateSetList()
{
for (int i = 0; i < checkBoxList.size(); ++i)
delete checkBoxList[i];
checkBoxList.clear();
QList<SetToDownload> &sets = 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;
}
statusLabel->setText(tr("Ready."));
}
void WindowMain::actLoadSetsFile()
{
QFileDialog dialog(this, tr("Load sets file"));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter("Sets XML file (*.xml)");
if (!dialog.exec())
return;
QString fileName = dialog.selectedFiles().at(0);
if (importer->readSetsFromFile(fileName))
updateSetList();
else
QMessageBox::critical(this, tr("Error"), tr("This file does not contain any sets data."));
}
void WindowMain::actDownloadSetsFile()
{
QString url = QInputDialog::getText(this, tr("Load sets from URL"), tr("Please enter the URL of the sets file:"), QLineEdit::Normal, defaultSetsUrl);
if (!url.isEmpty())
downloadSetsFile(url);
}
void WindowMain::downloadSetsFile(const QString &url)
{
QNetworkReply *reply = nam->get(QNetworkRequest(url));
connect(reply, SIGNAL(finished()), this, SLOT(setsDownloadFinished()));
}
void WindowMain::setsDownloadFinished()
{
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
QNetworkReply::NetworkError errorCode = reply->error();
if (errorCode == QNetworkReply::NoError) {
if (importer->readSetsFromByteArray(reply->readAll()))
updateSetList();
else
QMessageBox::critical(this, tr("Error"), tr("The file was retrieved successfully, but it does not contain any sets data."));
} else
QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString()));
reply->deleteLater();
}
void WindowMain::updateTotalProgress(int cardsImported, int setIndex, const QString &nextSetName)
{
if (setIndex != 0)
messageLog->append(QString("%1: %2 cards imported").arg(nextSetLabel2->text()).arg(cardsImported));
totalProgressBar->setValue(setIndex);
if (nextSetName.isEmpty()) {
QMessageBox::information(this, tr("Oracle importer"), tr("Import finished: %1 cards.").arg(importer->getCardList().size()));
bool ok = false;
const QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
QDir dir(dataDir);
if (!dir.exists())
dir.mkpath(dataDir);
QString savePath = dataDir + "/cards.xml";
do {
QString fileName;
if (savePath.isEmpty())
fileName = QFileDialog::getSaveFileName(this, tr("Save card database"), dataDir + "/cards.xml", tr("XML card database (*.xml)"));
else {
fileName = savePath;
savePath.clear();
}
if (fileName.isEmpty()) {
qApp->quit();
return;
}
if (importer->saveToFile(fileName))
ok = true;
else
QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to the desired location."));
} while (!ok);
qApp->quit();
} else {
nextSetLabel2->setText(nextSetName);
}
}
void WindowMain::updateFileProgress(int bytesRead, int totalBytes)
{
fileProgressBar->setMaximum(totalBytes);
fileProgressBar->setValue(bytesRead);
}
void WindowMain::actCheckAll()
{
for (int i = 0; i < checkBoxList.size(); ++i)
checkBoxList[i]->setChecked(true);
}
void WindowMain::actUncheckAll()
{
for (int i = 0; i < checkBoxList.size(); ++i)
checkBoxList[i]->setChecked(false);
}
void WindowMain::actStart()
{
int setsCount = importer->startDownload();
if (!setsCount) {
QMessageBox::critical(this, tr("Error"), tr("No sets to download selected."));
return;
}
for (int i = 0; i < checkBoxList.size(); ++i)
checkBoxList[i]->setEnabled(false);
startButton->setEnabled(false);
totalProgressBar->setMaximum(setsCount);
statusLabel->setText(tr("Downloading card data..."));
}
void WindowMain::checkBoxChanged(int state)
{
QCheckBox *checkBox = qobject_cast<QCheckBox *>(sender());
QList<SetToDownload> &sets = importer->getSets();
for (int i = 0; i < sets.size(); ++i)
if (sets[i].getLongName() == checkBox->text()) {
sets[i].setImport(state);
break;
}
}

View file

@ -1,52 +0,0 @@
#ifndef WINDOW_MAIN_H
#define WINDOW_MAIN_H
#include <QMainWindow>
#include <QList>
class OracleImporter;
class QLabel;
class QProgressBar;
class QTextEdit;
class QPushButton;
class QCheckBox;
class QVBoxLayout;
class QMenu;
class QAction;
class QNetworkAccessManager;
class WindowMain : public QMainWindow {
Q_OBJECT
private:
static const QString defaultSetsUrl;
OracleImporter *importer;
QNetworkAccessManager *nam;
QMenu *fileMenu;
QAction *aLoadSetsFile, *aDownloadSetsFile, *aExit;
QPushButton *checkAllButton, *uncheckAllButton, *startButton;
QLabel *totalLabel, *fileLabel, *nextSetLabel1, *nextSetLabel2;
QProgressBar *totalProgressBar, *fileProgressBar;
QTextEdit *messageLog;
QVBoxLayout *checkBoxLayout;
QList<QCheckBox *> checkBoxList;
QLabel *statusLabel;
void downloadSetsFile(const QString &url);
private slots:
void updateTotalProgress(int cardsImported, int setIndex, const QString &nextSetName);
void updateFileProgress(int bytesRead, int totalBytes);
void updateSetList();
void actCheckAll();
void actUncheckAll();
void actStart();
void actLoadSetsFile();
void actDownloadSetsFile();
void setsDownloadFinished();
void checkBoxChanged(int state);
public:
WindowMain(QWidget *parent = 0);
};
#endif