Merge remote-tracking branch 'upstream/master' into cmake_qt5

This commit is contained in:
Fabio Bas 2014-06-24 18:13:26 +02:00
commit 5c13c06982
34 changed files with 1505 additions and 1221 deletions

View file

@ -109,9 +109,3 @@ option(WITH_ORACLE "build oracle" ON)
if(WITH_ORACLE) if(WITH_ORACLE)
add_subdirectory(oracle) add_subdirectory(oracle)
endif() endif()
# Compile testclient (default off)
option(WITH_TESTCLIENT "build testclient" OFF)
if (WITH_TESTCLIENT)
add_subdirectory(testclient)
endif()

View file

@ -9,6 +9,7 @@ class CardInfo;
class AbstractCardDragItem : public QObject, public QGraphicsItem { class AbstractCardDragItem : public QObject, public QGraphicsItem {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGraphicsItem)
protected: protected:
AbstractCardItem *item; AbstractCardItem *item;
QPointF hotSpot; QPointF hotSpot;

View file

@ -9,6 +9,7 @@ class QAction;
class AbstractCounter : public QObject, public QGraphicsItem { class AbstractCounter : public QObject, public QGraphicsItem {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGraphicsItem)
protected: protected:
Player *player; Player *player;
int id; int id;

View file

@ -11,6 +11,7 @@ class ArrowTarget;
class ArrowItem : public QObject, public QGraphicsItem { class ArrowItem : public QObject, public QGraphicsItem {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGraphicsItem)
private: private:
QPainterPath path; QPainterPath path;
QMenu *menu; QMenu *menu;

View file

@ -14,7 +14,7 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QDebug> #include <QDebug>
const int CardDatabase::versionNeeded = 2; const int CardDatabase::versionNeeded = 3;
CardSet::CardSet(const QString &_shortName, const QString &_longName) CardSet::CardSet(const QString &_shortName, const QString &_longName)
: shortName(_shortName), longName(_longName) : shortName(_shortName), longName(_longName)
@ -80,8 +80,10 @@ bool PictureToLoad::nextSet()
return true; return true;
} }
PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, QObject *parent) PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent)
: QObject(parent), _picsPath(__picsPath), picDownload(_picDownload), downloadRunning(false), loadQueueRunning(false) : QObject(parent),
_picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq),
downloadRunning(false), loadQueueRunning(false)
{ {
connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection); connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection);
@ -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() void PictureLoader::startNextPicDownload()
{ {
if (cardsToDownload.isEmpty()) { if (cardsToDownload.isEmpty()) {
@ -146,27 +162,24 @@ void PictureLoader::startNextPicDownload()
downloadRunning = true; downloadRunning = true;
cardBeingDownloaded = cardsToDownload.takeFirst(); cardBeingDownloaded = cardsToDownload.takeFirst();
QString picUrl;
if (cardBeingDownloaded.getStripped()) // TODO: Do something useful when picUrl is 0 or empty, etc
picUrl = cardBeingDownloaded.getCard()->getPicURLSt(cardBeingDownloaded.getSetName()); QString picUrl = getPicUrl(cardBeingDownloaded.getCard());
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());
QUrl url(picUrl); QUrl url(picUrl);
QNetworkRequest req(url); QNetworkRequest req(url);
qDebug() << "starting picture download:" << req.url(); qDebug() << "starting picture download:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url();
networkManager->get(req); networkManager->get(req);
} }
void PictureLoader::picDownloadFinished(QNetworkReply *reply) void PictureLoader::picDownloadFinished(QNetworkReply *reply)
{ {
QString picsPath = _picsPath; QString picsPath = _picsPath;
if (reply->error()) {
qDebug() << "Download failed:" << reply->errorString();
}
const QByteArray &picData = reply->readAll(); const QByteArray &picData = reply->readAll();
QImage testImage; QImage testImage;
if (testImage.loadFromData(picData)) { if (testImage.loadFromData(picData)) {
@ -232,6 +245,12 @@ void PictureLoader::setPicDownload(bool _picDownload)
picDownload = _picDownload; picDownload = _picDownload;
} }
void PictureLoader::setPicDownloadHq(bool _picDownloadHq)
{
QMutexLocker locker(&mutex);
picDownloadHq = _picDownloadHq;
}
CardInfo::CardInfo(CardDatabase *_db, CardInfo::CardInfo(CardDatabase *_db,
const QString &_name, const QString &_name,
bool _isToken, bool _isToken,
@ -244,22 +263,18 @@ CardInfo::CardInfo(CardDatabase *_db,
bool _cipt, bool _cipt,
int _tableRow, int _tableRow,
const SetList &_sets, const SetList &_sets,
const QMap<QString, QString> &_picURLs, QMap<QString, int> _muIds)
const QMap<QString, QString> &_picURLsHq,
const QMap<QString, QString> &_picURLsSt)
: db(_db), : db(_db),
name(_name), name(_name),
isToken(_isToken), isToken(_isToken),
sets(_sets), sets(_sets),
muIds(_muIds),
manacost(_manacost), manacost(_manacost),
cardtype(_cardtype), cardtype(_cardtype),
powtough(_powtough), powtough(_powtough),
text(_text), text(_text),
colors(_colors), colors(_colors),
loyalty(_loyalty), loyalty(_loyalty),
picURLs(_picURLs),
picURLsHq(_picURLsHq),
picURLsSt(_picURLsSt),
cipt(_cipt), cipt(_cipt),
tableRow(_tableRow), tableRow(_tableRow),
pixmap(NULL) pixmap(NULL)
@ -284,6 +299,8 @@ QString CardInfo::getMainCardType() const
int pos; int pos;
if ((pos = result.indexOf('-')) != -1) if ((pos = result.indexOf('-')) != -1)
result.remove(pos, result.length()); result.remove(pos, result.length());
if ((pos = result.indexOf("")) != -1)
result.remove(pos, result.length());
if ((pos = result.indexOf("//")) != -1) if ((pos = result.indexOf("//")) != -1)
result.remove(pos, result.length()); result.remove(pos, result.length());
result = result.simplified(); result = result.simplified();
@ -315,13 +332,6 @@ void CardInfo::addToSet(CardSet *set)
sets << set; sets << set;
} }
QString CardInfo::getPicURL() const
{
SetList sortedSets = sets;
sortedSets.sortByKey();
return picURLs.value(sortedSets.first()->getShortName());
}
QPixmap *CardInfo::loadPixmap() QPixmap *CardInfo::loadPixmap()
{ {
if (pixmap) if (pixmap)
@ -400,18 +410,33 @@ void CardInfo::updatePixmapCache()
emit pixmapUpdated(); 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) QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info)
{ {
xml.writeStartElement("card"); xml.writeStartElement("card");
xml.writeTextElement("name", info->getName()); xml.writeTextElement("name", info->getName());
const SetList &sets = info->getSets(); const SetList &sets = info->getSets();
QString tmpString;
QString tmpSet;
for (int i = 0; i < sets.size(); i++) { for (int i = 0; i < sets.size(); i++) {
xml.writeStartElement("set"); xml.writeStartElement("set");
xml.writeAttribute("picURL", info->getPicURL(sets[i]->getShortName()));
xml.writeAttribute("picURLHq", info->getPicURLHq(sets[i]->getShortName())); tmpSet=sets[i]->getShortName();
xml.writeAttribute("picURLSt", info->getPicURLSt(sets[i]->getShortName())); xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet)));
xml.writeCharacters(sets[i]->getShortName());
xml.writeCharacters(tmpSet);
xml.writeEndElement(); xml.writeEndElement();
} }
const QStringList &colors = info->getColors(); const QStringList &colors = info->getColors();
@ -436,18 +461,19 @@ QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info)
} }
CardDatabase::CardDatabase(QObject *parent) CardDatabase::CardDatabase(QObject *parent)
: QObject(parent), loadSuccess(false), noCard(0) : QObject(parent), loadStatus(NotLoaded), noCard(0)
{ {
connect(settingsCache, SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged())); connect(settingsCache, SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged()));
connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabase())); connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabase()));
connect(settingsCache, SIGNAL(tokenDatabasePathChanged()), this, SLOT(loadTokenDatabase())); connect(settingsCache, SIGNAL(tokenDatabasePathChanged()), this, SLOT(loadTokenDatabase()));
connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged())); connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
connect(settingsCache, SIGNAL(picDownloadHqChanged()), this, SLOT(picDownloadHqChanged()));
loadCardDatabase(); loadCardDatabase();
loadTokenDatabase(); loadTokenDatabase();
pictureLoaderThread = new QThread; pictureLoaderThread = new QThread;
pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload()); pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload(), settingsCache->getPicDownloadHq());
pictureLoader->moveToThread(pictureLoaderThread); pictureLoader->moveToThread(pictureLoaderThread);
connect(pictureLoader, SIGNAL(imageLoaded(CardInfo *, const QImage &)), this, SLOT(imageLoaded(CardInfo *, const QImage &))); connect(pictureLoader, SIGNAL(imageLoaded(CardInfo *, const QImage &)), this, SLOT(imageLoaded(CardInfo *, const QImage &)));
pictureLoaderThread->start(QThread::LowPriority); pictureLoaderThread->start(QThread::LowPriority);
@ -572,7 +598,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
if (xml.name() == "card") { if (xml.name() == "card") {
QString name, manacost, type, pt, text; QString name, manacost, type, pt, text;
QStringList colors; QStringList colors;
QMap<QString, QString> picURLs, picURLsHq, picURLsSt; QMap<QString, int> muids;
SetList sets; SetList sets;
int tableRow = 0; int tableRow = 0;
int loyalty = 0; int loyalty = 0;
@ -592,14 +618,12 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
else if (xml.name() == "text") else if (xml.name() == "text")
text = xml.readElementText(); text = xml.readElementText();
else if (xml.name() == "set") { else if (xml.name() == "set") {
QString picURL = xml.attributes().value("picURL").toString(); QXmlStreamAttributes attrs = xml.attributes();
QString picURLHq = xml.attributes().value("picURLHq").toString();
QString picURLSt = xml.attributes().value("picURLSt").toString();
QString setName = xml.readElementText(); QString setName = xml.readElementText();
sets.append(getSet(setName)); sets.append(getSet(setName));
picURLs.insert(setName, picURL); if (attrs.hasAttribute("muId")) {
picURLsHq.insert(setName, picURLHq); muids[setName] = attrs.value("muId").toString().toInt();
picURLsSt.insert(setName, picURLSt); }
} else if (xml.name() == "color") } else if (xml.name() == "color")
colors << xml.readElementText(); colors << xml.readElementText();
else if (xml.name() == "tablerow") else if (xml.name() == "tablerow")
@ -611,17 +635,17 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
else if (xml.name() == "token") else if (xml.name() == "token")
isToken = xml.readElementText().toInt(); 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));
} }
} }
} }
bool CardDatabase::loadFromFile(const QString &fileName, bool tokens) LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens)
{ {
QFile file(fileName); QFile file(fileName);
file.open(QIODevice::ReadOnly); file.open(QIODevice::ReadOnly);
if (!file.isOpen()) if (!file.isOpen())
return false; return FileError;
if (tokens) { if (tokens) {
QMutableHashIterator<QString, CardInfo *> i(cardHash); QMutableHashIterator<QString, CardInfo *> i(cardHash);
@ -655,9 +679,12 @@ bool CardDatabase::loadFromFile(const QString &fileName, bool tokens)
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) { if (xml.readNext() == QXmlStreamReader::StartElement) {
if (xml.name() != "cockatrice_carddatabase") if (xml.name() != "cockatrice_carddatabase")
return false; return Invalid;
if (xml.attributes().value("version").toString().toInt() < versionNeeded) int version = xml.attributes().value("version").toString().toInt();
return false; if (version < versionNeeded) {
qDebug() << "loadFromFile(): Version too old: " << version;
return VersionTooOld;
}
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) if (xml.readNext() == QXmlStreamReader::EndElement)
break; break;
@ -669,7 +696,10 @@ bool CardDatabase::loadFromFile(const QString &fileName, bool tokens)
} }
} }
qDebug() << cardHash.size() << "cards in" << setHash.size() << "sets loaded"; qDebug() << cardHash.size() << "cards in" << setHash.size() << "sets loaded";
return !cardHash.isEmpty();
if (cardHash.isEmpty()) return NoCards;
return Ok;
} }
bool CardDatabase::saveToFile(const QString &fileName, bool tokens) bool CardDatabase::saveToFile(const QString &fileName, bool tokens)
@ -717,13 +747,23 @@ void CardDatabase::picDownloadChanged()
} }
} }
bool CardDatabase::loadCardDatabase(const QString &path, bool tokens) void CardDatabase::picDownloadHqChanged()
{ {
bool tempLoadSuccess = false; pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq());
if (!path.isEmpty()) if (settingsCache->getPicDownloadHq()) {
tempLoadSuccess = loadFromFile(path, tokens); QHashIterator<QString, CardInfo *> cardIterator(cardHash);
while (cardIterator.hasNext())
cardIterator.next().value()->clearPixmapCacheMiss();
}
}
if (tempLoadSuccess) { LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens)
{
LoadStatus tempLoadStatus = NotLoaded;
if (!path.isEmpty())
tempLoadStatus = loadFromFile(path, tokens);
if (tempLoadStatus == Ok) {
SetList allSets; SetList allSets;
QHashIterator<QString, CardSet *> setsIterator(setHash); QHashIterator<QString, CardSet *> setsIterator(setHash);
while (setsIterator.hasNext()) while (setsIterator.hasNext())
@ -735,10 +775,13 @@ bool CardDatabase::loadCardDatabase(const QString &path, bool tokens)
emit cardListChanged(); emit cardListChanged();
} }
if (!tokens) if (!tokens) {
loadSuccess = tempLoadSuccess; loadStatus = tempLoadStatus;
qDebug() << "loadCardDatabase(): Status = " << loadStatus;
}
return tempLoadSuccess;
return tempLoadStatus;
} }
void CardDatabase::loadCardDatabase() void CardDatabase::loadCardDatabase()

View file

@ -53,10 +53,8 @@ public:
bool getStripped() const { return stripped; } bool getStripped() const { return stripped; }
QString getSetName() const { return sortedSets[setIndex]->getShortName(); } QString getSetName() const { return sortedSets[setIndex]->getShortName(); }
bool nextSet(); bool nextSet();
bool getHq() const { return hq; } bool getHq() const { return hq; }
void setHq(bool _hq) { hq = _hq; } void setHq(bool _hq) { hq = _hq; }
}; };
class PictureLoader : public QObject { class PictureLoader : public QObject {
@ -68,13 +66,15 @@ private:
QNetworkAccessManager *networkManager; QNetworkAccessManager *networkManager;
QList<PictureToLoad> cardsToDownload; QList<PictureToLoad> cardsToDownload;
PictureToLoad cardBeingDownloaded; PictureToLoad cardBeingDownloaded;
bool picDownload, downloadRunning, loadQueueRunning; bool picDownload, picDownloadHq, downloadRunning, loadQueueRunning;
void startNextPicDownload(); void startNextPicDownload();
QString getPicUrl(CardInfo* card);
public: public:
PictureLoader(const QString &__picsPath, bool _picDownload, QObject *parent = 0); PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent = 0);
~PictureLoader(); ~PictureLoader();
void setPicsPath(const QString &path); void setPicsPath(const QString &path);
void setPicDownload(bool _picDownload); void setPicDownload(bool _picDownload);
void setPicDownloadHq(bool _picDownloadHq);
void loadImage(CardInfo *card, bool stripped); void loadImage(CardInfo *card, bool stripped);
private slots: private slots:
void picDownloadFinished(QNetworkReply *reply); void picDownloadFinished(QNetworkReply *reply);
@ -99,7 +99,7 @@ private:
QString text; QString text;
QStringList colors; QStringList colors;
int loyalty; int loyalty;
QMap<QString, QString> picURLs, picURLsHq, picURLsSt; QMap<QString, int> muIds;
bool cipt; bool cipt;
int tableRow; int tableRow;
QPixmap *pixmap; QPixmap *pixmap;
@ -117,9 +117,7 @@ public:
bool _cipt = false, bool _cipt = false,
int _tableRow = 0, int _tableRow = 0,
const SetList &_sets = SetList(), const SetList &_sets = SetList(),
const QStringMap &_picURLs = QStringMap(), QMap<QString, int> muids = QMap<QString, int>());
const QStringMap &_picURLsHq = QStringMap(),
const QStringMap &_picURLsSt = QStringMap());
~CardInfo(); ~CardInfo();
const QString &getName() const { return name; } const QString &getName() const { return name; }
bool getIsToken() const { return isToken; } bool getIsToken() const { return isToken; }
@ -136,25 +134,21 @@ public:
void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); } void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); }
void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); } void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); }
const QStringList &getColors() const { return colors; } const QStringList &getColors() const { return colors; }
QString getPicURL(const QString &set) const { return picURLs.value(set); } int getMuId(const QString &set) const { return muIds.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; }
QString getMainCardType() const; QString getMainCardType() const;
QString getCorrectedName() const; QString getCorrectedName() const;
int getTableRow() const { return tableRow; } int getTableRow() const { return tableRow; }
void setTableRow(int _tableRow) { tableRow = _tableRow; } void setTableRow(int _tableRow) { tableRow = _tableRow; }
void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); } void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); }
void setPicURL(const QString &_set, const QString &_picURL) { picURLs.insert(_set, _picURL); } void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); }
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 addToSet(CardSet *set); void addToSet(CardSet *set);
QPixmap *loadPixmap(); QPixmap *loadPixmap();
QPixmap *getPixmap(QSize size); QPixmap *getPixmap(QSize size);
void clearPixmapCache(); void clearPixmapCache();
void clearPixmapCacheMiss(); void clearPixmapCacheMiss();
void imageLoaded(const QImage &image); void imageLoaded(const QImage &image);
CardSet *getPreferredSet();
int getPreferredMuId();
public slots: public slots:
void updatePixmapCache(); void updatePixmapCache();
signals: signals:
@ -162,16 +156,18 @@ signals:
void cardInfoChanged(CardInfo *card); void cardInfoChanged(CardInfo *card);
}; };
enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards };
class CardDatabase : public QObject { class CardDatabase : public QObject {
Q_OBJECT Q_OBJECT
protected: protected:
QHash<QString, CardInfo *> cardHash; QHash<QString, CardInfo *> cardHash;
QHash<QString, CardSet *> setHash; QHash<QString, CardSet *> setHash;
bool loadSuccess;
CardInfo *noCard; CardInfo *noCard;
QThread *pictureLoaderThread; QThread *pictureLoaderThread;
PictureLoader *pictureLoader; PictureLoader *pictureLoader;
LoadStatus loadStatus;
private: private:
static const int versionNeeded; static const int versionNeeded;
void loadCardsFromXml(QXmlStreamReader &xml); void loadCardsFromXml(QXmlStreamReader &xml);
@ -186,19 +182,21 @@ public:
CardSet *getSet(const QString &setName); CardSet *getSet(const QString &setName);
QList<CardInfo *> getCardList() const { return cardHash.values(); } QList<CardInfo *> getCardList() const { return cardHash.values(); }
SetList getSetList() const; SetList getSetList() const;
bool loadFromFile(const QString &fileName, bool tokens = false); LoadStatus loadFromFile(const QString &fileName, bool tokens = false);
bool saveToFile(const QString &fileName, bool tokens = false); bool saveToFile(const QString &fileName, bool tokens = false);
QStringList getAllColors() const; QStringList getAllColors() const;
QStringList getAllMainCardTypes() const; QStringList getAllMainCardTypes() const;
bool getLoadSuccess() const { return loadSuccess; } LoadStatus getLoadStatus() const { return loadStatus; }
bool getLoadSuccess() const { return loadStatus == Ok; }
void cacheCardPixmaps(const QStringList &cardNames); void cacheCardPixmaps(const QStringList &cardNames);
void loadImage(CardInfo *card); void loadImage(CardInfo *card);
public slots: public slots:
void clearPixmapCache(); void clearPixmapCache();
bool loadCardDatabase(const QString &path, bool tokens = false); LoadStatus loadCardDatabase(const QString &path, bool tokens = false);
private slots: private slots:
void imageLoaded(CardInfo *card, QImage image); void imageLoaded(CardInfo *card, QImage image);
void picDownloadChanged(); void picDownloadChanged();
void picDownloadHqChanged();
void picsPathChanged(); void picsPathChanged();
void loadCardDatabase(); void loadCardDatabase();

View file

@ -1,5 +1,6 @@
#include <QStringList> #include <QStringList>
#include <QFile> #include <QFile>
#include <QDebug>
#include "deck_loader.h" #include "deck_loader.h"
#include "decklist.h" #include "decklist.h"
@ -48,7 +49,16 @@ bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt)
bool result = false; bool result = false;
switch (fmt) { switch (fmt) {
case PlainTextFormat: result = loadFromFile_Plain(&file); break; case PlainTextFormat: result = loadFromFile_Plain(&file); break;
case CockatriceFormat: result = loadFromFile_Native(&file); break; case CockatriceFormat:
result = loadFromFile_Native(&file);
qDebug() << "Loaded from" << fileName << "-" << result;
if (!result) {
qDebug() << "Retying as plain format";
file.seek(0);
result = loadFromFile_Plain(&file);
fmt = PlainTextFormat;
}
break;
} }
if (result) { if (result) {
lastFileName = fileName; lastFileName = fileName;
@ -56,6 +66,7 @@ bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt)
emit deckLoaded(); emit deckLoaded();
} }
qDebug() << "Deck was loaded -" << result;
return result; return result;
} }

View file

@ -14,7 +14,7 @@
#include "deck_loader.h" #include "deck_loader.h"
DeckListModel::DeckListModel(QObject *parent) DeckListModel::DeckListModel(QObject *parent)
: QAbstractItemModel(parent) : QAbstractItemModel(parent), lastKnownColumn(1), lastKnownOrder(Qt::AscendingOrder)
{ {
deckList = new DeckLoader; deckList = new DeckLoader;
connect(deckList, SIGNAL(deckLoaded()), this, SLOT(rebuildTree())); connect(deckList, SIGNAL(deckLoaded()), this, SLOT(rebuildTree()));
@ -275,23 +275,20 @@ QModelIndex DeckListModel::addCard(const QString &cardName, const QString &zoneN
QString cardType = info->getMainCardType(); QString cardType = info->getMainCardType();
InnerDecklistNode *cardTypeNode = createNodeIfNeeded(cardType, zoneNode); InnerDecklistNode *cardTypeNode = createNodeIfNeeded(cardType, zoneNode);
QModelIndex parentIndex = nodeToIndex(cardTypeNode);
DecklistModelCardNode *cardNode = dynamic_cast<DecklistModelCardNode *>(cardTypeNode->findChild(cardName)); DecklistModelCardNode *cardNode = dynamic_cast<DecklistModelCardNode *>(cardTypeNode->findChild(cardName));
if (!cardNode) { if (!cardNode) {
DecklistCardNode *decklistCard = deckList->addCard(cardName, zoneName); DecklistCardNode *decklistCard = deckList->addCard(cardName, zoneName);
QModelIndex parentIndex = nodeToIndex(cardTypeNode);
beginInsertRows(parentIndex, cardTypeNode->size(), cardTypeNode->size()); beginInsertRows(parentIndex, cardTypeNode->size(), cardTypeNode->size());
cardNode = new DecklistModelCardNode(decklistCard, cardTypeNode); cardNode = new DecklistModelCardNode(decklistCard, cardTypeNode);
endInsertRows(); endInsertRows();
sort(1);
emitRecursiveUpdates(parentIndex);
return nodeToIndex(cardNode);
} else { } else {
cardNode->setNumber(cardNode->getNumber() + 1); cardNode->setNumber(cardNode->getNumber() + 1);
QModelIndex ind = nodeToIndex(cardNode);
emitRecursiveUpdates(ind);
deckList->updateDeckHash(); deckList->updateDeckHash();
return ind;
} }
sort(lastKnownColumn, lastKnownOrder);
emitRecursiveUpdates(parentIndex);
return nodeToIndex(cardNode);
} }
QModelIndex DeckListModel::nodeToIndex(AbstractDecklistNode *node) const QModelIndex DeckListModel::nodeToIndex(AbstractDecklistNode *node) const
@ -327,9 +324,24 @@ void DeckListModel::sortHelper(InnerDecklistNode *node, Qt::SortOrder order)
} }
} }
void DeckListModel::sort(int /*column*/, Qt::SortOrder order) void DeckListModel::sort(int column, Qt::SortOrder order)
{ {
lastKnownColumn = column;
lastKnownOrder = order;
emit layoutAboutToBeChanged(); emit layoutAboutToBeChanged();
DeckSortMethod sortMethod;
switch(column) {
case 0:
sortMethod = ByNumber;
break;
case 1:
sortMethod = ByName;
break;
case 2:
sortMethod = ByPrice;
}
root->setSortMethod(sortMethod);
sortHelper(root, order); sortHelper(root, order);
emit layoutChanged(); emit layoutChanged();
} }

View file

@ -55,6 +55,8 @@ public:
private: private:
DeckLoader *deckList; DeckLoader *deckList;
InnerDecklistNode *root; InnerDecklistNode *root;
int lastKnownColumn;
Qt::SortOrder lastKnownOrder;
InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent); InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent);
QModelIndex nodeToIndex(AbstractDecklistNode *node) const; QModelIndex nodeToIndex(AbstractDecklistNode *node) const;
DecklistModelCardNode *findCardNode(const QString &cardName, const QString &zoneName) const; DecklistModelCardNode *findCardNode(const QString &cardName, const QString &zoneName) const;

View file

@ -18,6 +18,7 @@
#include <QInputDialog> #include <QInputDialog>
#include <QSpinBox> #include <QSpinBox>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QDebug>
#include "carddatabase.h" #include "carddatabase.h"
#include "dlg_settings.h" #include "dlg_settings.h"
#include "main.h" #include "main.h"
@ -40,13 +41,18 @@ GeneralSettingsPage::GeneralSettingsPage()
picDownloadCheckBox = new QCheckBox; picDownloadCheckBox = new QCheckBox;
picDownloadCheckBox->setChecked(settingsCache->getPicDownload()); picDownloadCheckBox->setChecked(settingsCache->getPicDownload());
picDownloadHqCheckBox = new QCheckBox;
picDownloadHqCheckBox->setChecked(settingsCache->getPicDownloadHq());
connect(languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int))); connect(languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int)));
connect(picDownloadCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownload(int))); connect(picDownloadCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownload(int)));
connect(picDownloadHqCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownloadHq(int)));
QGridLayout *personalGrid = new QGridLayout; QGridLayout *personalGrid = new QGridLayout;
personalGrid->addWidget(languageLabel, 0, 0); personalGrid->addWidget(languageLabel, 0, 0);
personalGrid->addWidget(languageBox, 0, 1); personalGrid->addWidget(languageBox, 0, 1);
personalGrid->addWidget(picDownloadCheckBox, 1, 0, 1, 2); personalGrid->addWidget(picDownloadCheckBox, 1, 0, 1, 2);
personalGrid->addWidget(picDownloadHqCheckBox, 2, 0, 1, 2);
personalGroupBox = new QGroupBox; personalGroupBox = new QGroupBox;
personalGroupBox->setLayout(personalGrid); personalGroupBox->setLayout(personalGrid);
@ -183,6 +189,7 @@ void GeneralSettingsPage::retranslateUi()
personalGroupBox->setTitle(tr("Personal settings")); personalGroupBox->setTitle(tr("Personal settings"));
languageLabel->setText(tr("Language:")); languageLabel->setText(tr("Language:"));
picDownloadCheckBox->setText(tr("Download card pictures on the fly")); picDownloadCheckBox->setText(tr("Download card pictures on the fly"));
picDownloadHqCheckBox->setText(tr("Download high-quality card pictures"));
pathsGroupBox->setTitle(tr("Paths")); pathsGroupBox->setTitle(tr("Paths"));
deckPathLabel->setText(tr("Decks directory:")); deckPathLabel->setText(tr("Decks directory:"));
replaysPathLabel->setText(tr("Replays directory:")); replaysPathLabel->setText(tr("Replays directory:"));
@ -698,17 +705,65 @@ void DlgSettings::changeEvent(QEvent *event)
void DlgSettings::closeEvent(QCloseEvent *event) void DlgSettings::closeEvent(QCloseEvent *event)
{ {
if (!db->getLoadSuccess()) bool showLoadError = true;
if (QMessageBox::critical(this, tr("Error"), tr("Your card database is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { QString loadErrorMessage = tr("Unknown Error loading card database");
LoadStatus loadStatus = db->getLoadStatus();
qDebug() << "Card Database load status: " << loadStatus;
switch(loadStatus) {
case Ok:
showLoadError = false;
break;
case Invalid:
loadErrorMessage =
tr("Your card database is invalid.\n\n"
"Cockatrice may not function correctly with an invalid database\n\n"
"You may need to rerun oracle to update your card database.\n\n"
"Would you like to change your database location setting?");
break;
case VersionTooOld:
loadErrorMessage =
tr("Your card database version is too old.\n\n"
"This can cause problems loading card information or images\n\n"
"Usually this can be fixed by rerunning oracle to to update your card database.\n\n"
"Would you like to change your database location setting?");
break;
case NotLoaded:
loadErrorMessage =
tr("Your card database did not finish loading\n\n"
"Please file a ticket at http://github.com/Daenyth/Cockatrice/issues with your cards.xml attached\n\n"
"Would you like to change your database location setting?");
break;
case FileError:
loadErrorMessage =
tr("File Error loading your card database.\n\n"
"Would you like to change your database location setting?");
break;
case NoCards:
loadErrorMessage =
tr("Your card database was loaded but contains no cards.\n\n"
"Would you like to change your database location setting?");
break;
default:
loadErrorMessage =
tr("Unknown card database load status\n\n"
"Please file a ticket at http://github.com/Daenyth/Cockatrice/issues\n\n"
"Would you like to change your database location setting?");
break;
}
if (showLoadError)
if (QMessageBox::critical(this, tr("Error"), loadErrorMessage, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
event->ignore(); event->ignore();
return; return;
} }
if (!QDir(settingsCache->getDeckPath()).exists() || settingsCache->getDeckPath().isEmpty()) if (!QDir(settingsCache->getDeckPath()).exists() || settingsCache->getDeckPath().isEmpty())
// TODO: Prompt to create it
if (QMessageBox::critical(this, tr("Error"), tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { if (QMessageBox::critical(this, tr("Error"), tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
event->ignore(); event->ignore();
return; return;
} }
if (!QDir(settingsCache->getPicsPath()).exists() || settingsCache->getPicsPath().isEmpty()) if (!QDir(settingsCache->getPicsPath()).exists() || settingsCache->getPicsPath().isEmpty())
// TODO: Prompt to create it
if (QMessageBox::critical(this, tr("Error"), tr("The path to your card pictures directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { if (QMessageBox::critical(this, tr("Error"), tr("The path to your card pictures directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
event->ignore(); event->ignore();
return; return;

View file

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

View file

@ -74,6 +74,14 @@ void installNewTranslator()
qApp->installTranslator(translator); qApp->installTranslator(translator);
} }
bool settingsValid()
{
return QDir(settingsCache->getDeckPath()).exists() &&
!settingsCache->getDeckPath().isEmpty() &&
QDir(settingsCache->getPicsPath()).exists() &&
!settingsCache->getPicsPath().isEmpty();
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QApplication app(argc, argv); QApplication app(argc, argv);
@ -142,14 +150,14 @@ int main(int argc, char *argv[])
QDir().mkpath(dataDir + "/pics"); QDir().mkpath(dataDir + "/pics");
settingsCache->setPicsPath(dataDir + "/pics"); settingsCache->setPicsPath(dataDir + "/pics");
} }
if (!db->getLoadSuccess() || !QDir(settingsCache->getDeckPath()).exists() || settingsCache->getDeckPath().isEmpty() || settingsCache->getPicsPath().isEmpty() || !QDir(settingsCache->getPicsPath()).exists()) { if (!settingsValid() || db->getLoadStatus() != Ok) {
qDebug("main(): invalid settings or load status");
DlgSettings dlgSettings; DlgSettings dlgSettings;
dlgSettings.show(); dlgSettings.show();
app.exec(); app.exec();
startMainProgram = (db->getLoadSuccess() && QDir(settingsCache->getDeckPath()).exists() && !settingsCache->getDeckPath().isEmpty() && QDir(settingsCache->getPicsPath()).exists() && !settingsCache->getPicsPath().isEmpty());
} }
if (startMainProgram) { if (settingsValid()) {
qDebug("main(): starting main program"); qDebug("main(): starting main program");
soundEngine = new SoundEngine; soundEngine = new SoundEngine;
qDebug("main(): SoundEngine constructor finished"); qDebug("main(): SoundEngine constructor finished");

View file

@ -11,6 +11,7 @@ class GameCommand;
class PhaseButton : public QObject, public QGraphicsItem { class PhaseButton : public QObject, public QGraphicsItem {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGraphicsItem)
private: private:
QString name; QString name;
bool active, highlightable; bool active, highlightable;
@ -39,6 +40,7 @@ protected:
class PhasesToolbar : public QObject, public QGraphicsItem { class PhasesToolbar : public QObject, public QGraphicsItem {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGraphicsItem)
private: private:
QList<PhaseButton *> buttonList; QList<PhaseButton *> buttonList;
PhaseButton *nextTurnButton; PhaseButton *nextTurnButton;

View file

@ -58,6 +58,7 @@ class PendingCommand;
class PlayerArea : public QObject, public QGraphicsItem { class PlayerArea : public QObject, public QGraphicsItem {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGraphicsItem)
private: private:
QBrush bgPixmapBrush; QBrush bgPixmapBrush;
QRectF bRect; QRectF bRect;
@ -76,6 +77,7 @@ public:
class Player : public QObject, public QGraphicsItem { class Player : public QObject, public QGraphicsItem {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGraphicsItem)
signals: signals:
void openDeckEditor(const DeckLoader *deck); void openDeckEditor(const DeckLoader *deck);
void newCardAdded(AbstractCardItem *card); void newCardAdded(AbstractCardItem *card);

View file

@ -27,7 +27,7 @@ void PriceUpdater::updatePrices()
QString q = "http://blacklotusproject.com/json/?cards="; QString q = "http://blacklotusproject.com/json/?cards=";
QStringList cards = deck->getCardList(); QStringList cards = deck->getCardList();
for (int i = 0; i < cards.size(); ++i) { for (int i = 0; i < cards.size(); ++i) {
q += cards[i] + "|"; q += cards[i].toLower() + "|";
} }
QUrl url(q.replace(' ', '+')); QUrl url(q.replace(' ', '+'));

View file

@ -19,8 +19,12 @@ SettingsCache::SettingsCache()
playerBgPath = settings->value("zonebg/playerarea").toString(); playerBgPath = settings->value("zonebg/playerarea").toString();
cardBackPicturePath = settings->value("paths/cardbackpicture").toString(); cardBackPicturePath = settings->value("paths/cardbackpicture").toString();
mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray();
picDownload = settings->value("personal/picturedownload", true).toBool(); 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(); notificationsEnabled = settings->value("interface/notificationsenabled", true).toBool();
doubleClickToPlay = settings->value("interface/doubleclicktoplay", true).toBool(); doubleClickToPlay = settings->value("interface/doubleclicktoplay", true).toBool();
playToStack = settings->value("interface/playtostack", false).toBool(); playToStack = settings->value("interface/playtostack", false).toBool();
@ -125,6 +129,25 @@ void SettingsCache::setPicDownload(int _picDownload)
emit picDownloadChanged(); 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) void SettingsCache::setNotificationsEnabled(int _notificationsEnabled)
{ {
notificationsEnabled = _notificationsEnabled; notificationsEnabled = _notificationsEnabled;
@ -219,6 +242,7 @@ void SettingsCache::setPriceTagFeature(int _priceTagFeature)
{ {
priceTagFeature = _priceTagFeature; priceTagFeature = _priceTagFeature;
settings->setValue("deckeditor/pricetags", priceTagFeature); settings->setValue("deckeditor/pricetags", priceTagFeature);
emit priceTagFeatureChanged(priceTagFeature);
} }
void SettingsCache::setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers) void SettingsCache::setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers)

View file

@ -3,6 +3,9 @@
#include <QObject> #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 QSettings;
class SettingsCache : public QObject { class SettingsCache : public QObject {
@ -18,12 +21,14 @@ signals:
void playerBgPathChanged(); void playerBgPathChanged();
void cardBackPicturePathChanged(); void cardBackPicturePathChanged();
void picDownloadChanged(); void picDownloadChanged();
void picDownloadHqChanged();
void displayCardNamesChanged(); void displayCardNamesChanged();
void horizontalHandChanged(); void horizontalHandChanged();
void invertVerticalCoordinateChanged(); void invertVerticalCoordinateChanged();
void minPlayersForMultiColumnLayoutChanged(); void minPlayersForMultiColumnLayoutChanged();
void soundEnabledChanged(); void soundEnabledChanged();
void soundPathChanged(); void soundPathChanged();
void priceTagFeatureChanged(int enabled);
void ignoreUnregisteredUsersChanged(); void ignoreUnregisteredUsersChanged();
private: private:
QSettings *settings; QSettings *settings;
@ -33,6 +38,7 @@ private:
QString deckPath, replaysPath, picsPath, cardDatabasePath, tokenDatabasePath; QString deckPath, replaysPath, picsPath, cardDatabasePath, tokenDatabasePath;
QString handBgPath, stackBgPath, tableBgPath, playerBgPath, cardBackPicturePath; QString handBgPath, stackBgPath, tableBgPath, playerBgPath, cardBackPicturePath;
bool picDownload; bool picDownload;
bool picDownloadHq;
bool notificationsEnabled; bool notificationsEnabled;
bool doubleClickToPlay; bool doubleClickToPlay;
bool playToStack; bool playToStack;
@ -48,6 +54,8 @@ private:
QString soundPath; QString soundPath;
bool priceTagFeature; bool priceTagFeature;
bool ignoreUnregisteredUsers; bool ignoreUnregisteredUsers;
QString picUrl;
QString picUrlHq;
public: public:
SettingsCache(); SettingsCache();
const QByteArray &getMainWindowGeometry() const { return mainWindowGeometry; } const QByteArray &getMainWindowGeometry() const { return mainWindowGeometry; }
@ -63,6 +71,7 @@ public:
QString getPlayerBgPath() const { return playerBgPath; } QString getPlayerBgPath() const { return playerBgPath; }
QString getCardBackPicturePath() const { return cardBackPicturePath; } QString getCardBackPicturePath() const { return cardBackPicturePath; }
bool getPicDownload() const { return picDownload; } bool getPicDownload() const { return picDownload; }
bool getPicDownloadHq() const { return picDownloadHq; }
bool getNotificationsEnabled() const { return notificationsEnabled; } bool getNotificationsEnabled() const { return notificationsEnabled; }
bool getDoubleClickToPlay() const { return doubleClickToPlay; } bool getDoubleClickToPlay() const { return doubleClickToPlay; }
bool getPlayToStack() const { return playToStack; } bool getPlayToStack() const { return playToStack; }
@ -79,6 +88,8 @@ public:
QString getSoundPath() const { return soundPath; } QString getSoundPath() const { return soundPath; }
bool getPriceTagFeature() const { return priceTagFeature; } bool getPriceTagFeature() const { return priceTagFeature; }
bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; } bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; }
QString getPicUrl() const { return picUrl; }
QString getPicUrlHq() const { return picUrlHq; }
public slots: public slots:
void setMainWindowGeometry(const QByteArray &_mainWindowGeometry); void setMainWindowGeometry(const QByteArray &_mainWindowGeometry);
void setLang(const QString &_lang); void setLang(const QString &_lang);
@ -93,6 +104,7 @@ public slots:
void setPlayerBgPath(const QString &_playerBgPath); void setPlayerBgPath(const QString &_playerBgPath);
void setCardBackPicturePath(const QString &_cardBackPicturePath); void setCardBackPicturePath(const QString &_cardBackPicturePath);
void setPicDownload(int _picDownload); void setPicDownload(int _picDownload);
void setPicDownloadHq(int _picDownloadHq);
void setNotificationsEnabled(int _notificationsEnabled); void setNotificationsEnabled(int _notificationsEnabled);
void setDoubleClickToPlay(int _doubleClickToPlay); void setDoubleClickToPlay(int _doubleClickToPlay);
void setPlayToStack(int _playToStack); void setPlayToStack(int _playToStack);
@ -109,6 +121,8 @@ public slots:
void setSoundPath(const QString &_soundPath); void setSoundPath(const QString &_soundPath);
void setPriceTagFeature(int _priceTagFeature); void setPriceTagFeature(int _priceTagFeature);
void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers); void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers);
void setPicUrl(const QString &_picUrl);
void setPicUrlHq(const QString &_picUrlHq);
}; };
extern SettingsCache *settingsCache; extern SettingsCache *settingsCache;

View file

@ -132,6 +132,8 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
deckView = new QTreeView(); deckView = new QTreeView();
deckView->setModel(deckModel); deckView->setModel(deckModel);
deckView->setUniformRowHeights(true); deckView->setUniformRowHeights(true);
deckView->setSortingEnabled(true);
deckView->sortByColumn(1, Qt::AscendingOrder);
deckView->header()->setResizeMode(QHeaderView::ResizeToContents); deckView->header()->setResizeMode(QHeaderView::ResizeToContents);
deckView->installEventFilter(&deckViewKeySignals); deckView->installEventFilter(&deckViewKeySignals);
connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(updateCardInfoRight(const QModelIndex &, const QModelIndex &))); connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(updateCardInfoRight(const QModelIndex &, const QModelIndex &)));
@ -170,6 +172,7 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
connect(aUpdatePrices, SIGNAL(triggered()), this, SLOT(actUpdatePrices())); connect(aUpdatePrices, SIGNAL(triggered()), this, SLOT(actUpdatePrices()));
if (!settingsCache->getPriceTagFeature()) if (!settingsCache->getPriceTagFeature())
aUpdatePrices->setVisible(false); aUpdatePrices->setVisible(false);
connect(settingsCache, SIGNAL(priceTagFeatureChanged(int)), this, SLOT(setPriceTagFeatureEnabled(int)));
QToolBar *deckToolBar = new QToolBar; QToolBar *deckToolBar = new QToolBar;
deckToolBar->setOrientation(Qt::Vertical); deckToolBar->setOrientation(Qt::Vertical);
@ -633,6 +636,11 @@ void TabDeckEditor::actDecrement()
offsetCountAtIndex(currentIndex, -1); offsetCountAtIndex(currentIndex, -1);
} }
void TabDeckEditor::setPriceTagFeatureEnabled(int enabled)
{
aUpdatePrices->setVisible(enabled);
}
void TabDeckEditor::actUpdatePrices() void TabDeckEditor::actUpdatePrices()
{ {
aUpdatePrices->setDisabled(true); aUpdatePrices->setDisabled(true);
@ -655,7 +663,7 @@ void TabDeckEditor::setDeck(DeckLoader *_deck)
nameEdit->setText(deckModel->getDeckList()->getName()); nameEdit->setText(deckModel->getDeckList()->getName());
commentsEdit->setText(deckModel->getDeckList()->getComments()); commentsEdit->setText(deckModel->getDeckList()->getComments());
updateHash(); updateHash();
deckModel->sort(1); deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
deckView->expandAll(); deckView->expandAll();
setModified(false); setModified(false);

View file

@ -67,6 +67,7 @@ private slots:
void saveDeckRemoteFinished(const Response &r); void saveDeckRemoteFinished(const Response &r);
void filterViewCustomContextMenu(const QPoint &point); void filterViewCustomContextMenu(const QPoint &point);
void filterRemove(QAction *action); void filterRemove(QAction *action);
void setPriceTagFeatureEnabled(int enabled);
private: private:
CardInfo *currentCardInfo() const; CardInfo *currentCardInfo() const;
void addCardHelper(QString zoneName); void addCardHelper(QString zoneName);

View file

@ -11,6 +11,7 @@ class QGraphicsSceneWheelEvent;
class ZoneViewZone : public SelectZone, public QGraphicsLayoutItem { class ZoneViewZone : public SelectZone, public QGraphicsLayoutItem {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGraphicsLayoutItem)
private: private:
QRectF bRect, optimumRect; QRectF bRect, optimumRect;
int minRows, numberCards; int minRows, numberCards;

View file

@ -2,6 +2,7 @@
#include <QTextStream> #include <QTextStream>
#include <QVariant> #include <QVariant>
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QDebug>
#include "decklist.h" #include "decklist.h"
SideboardPlan::SideboardPlan(const QString &_name, const QList<MoveCard_ToZone> &_moveList) SideboardPlan::SideboardPlan(const QString &_name, const QList<MoveCard_ToZone> &_moveList)
@ -104,6 +105,13 @@ QString InnerDecklistNode::visibleNameFromName(const QString &_name)
return _name; return _name;
} }
void InnerDecklistNode::setSortMethod(DeckSortMethod method)
{
sortMethod = method;
for (int i = 0; i < size(); i++)
at(i)->setSortMethod(method);
}
QString InnerDecklistNode::getVisibleName() const QString InnerDecklistNode::getVisibleName() const
{ {
return visibleNameFromName(name); return visibleNameFromName(name);
@ -163,22 +171,96 @@ float InnerDecklistNode::recursivePrice(bool countTotalCards) const
} }
bool InnerDecklistNode::compare(AbstractDecklistNode *other) const bool InnerDecklistNode::compare(AbstractDecklistNode *other) const
{
switch (sortMethod) {
case 0:
return compareNumber(other);
case 1:
return compareName(other);
case 2:
return comparePrice(other);
}
}
bool InnerDecklistNode::compareNumber(AbstractDecklistNode *other) const
{ {
InnerDecklistNode *other2 = dynamic_cast<InnerDecklistNode *>(other); InnerDecklistNode *other2 = dynamic_cast<InnerDecklistNode *>(other);
if (other2) if (other2) {
return (getName() > other->getName()); int n1 = recursiveCount(true);
else int n2 = other2->recursiveCount(true);
return (n1 != n2) ? (n1 > n2) : compareName(other);
} else {
return false; return false;
} }
}
bool InnerDecklistNode::compareName(AbstractDecklistNode *other) const
{
InnerDecklistNode *other2 = dynamic_cast<InnerDecklistNode *>(other);
if (other2) {
return (getName() > other2->getName());
} else {
return false;
}
}
bool InnerDecklistNode::comparePrice(AbstractDecklistNode *other) const
{
InnerDecklistNode *other2 = dynamic_cast<InnerDecklistNode *>(other);
if (other2) {
int p1 = 100*recursivePrice(true);
int p2 = 100*other2->recursivePrice(true);
return (p1 != p2) ? (p1 > p2) : compareName(other);
} else {
return false;
}
}
bool AbstractDecklistCardNode::compare(AbstractDecklistNode *other) const bool AbstractDecklistCardNode::compare(AbstractDecklistNode *other) const
{
switch (sortMethod) {
case ByNumber:
return compareNumber(other);
case ByName:
return compareName(other);
case ByPrice:
return compareTotalPrice(other);
}
}
bool AbstractDecklistCardNode::compareNumber(AbstractDecklistNode *other) const
{ {
AbstractDecklistCardNode *other2 = dynamic_cast<AbstractDecklistCardNode *>(other); AbstractDecklistCardNode *other2 = dynamic_cast<AbstractDecklistCardNode *>(other);
if (other2) if (other2) {
return (getName() > other->getName()); int n1 = getNumber();
else int n2 = other2->getNumber();
return (n1 != n2) ? (n1 > n2) : compareName(other);
} else {
return true; return true;
} }
}
bool AbstractDecklistCardNode::compareName(AbstractDecklistNode *other) const
{
AbstractDecklistCardNode *other2 = dynamic_cast<AbstractDecklistCardNode *>(other);
if (other2) {
return (getName() > other2->getName());
} else {
return true;
}
}
bool AbstractDecklistCardNode::compareTotalPrice(AbstractDecklistNode *other) const
{
AbstractDecklistCardNode *other2 = dynamic_cast<AbstractDecklistCardNode *>(other);
if (other2) {
int p1 = 100*getTotalPrice();
int p2 = 100*other2->getTotalPrice();
return (p1 != p2) ? (p1 > p2) : compareName(other);
} else {
return true;
}
}
class InnerDecklistNode::compareFunctor { class InnerDecklistNode::compareFunctor {
private: private:
@ -360,6 +442,11 @@ void DeckList::write(QXmlStreamWriter *xml)
bool DeckList::loadFromXml(QXmlStreamReader *xml) bool DeckList::loadFromXml(QXmlStreamReader *xml)
{ {
if (xml->error()) {
qDebug() << "Error loading deck from xml: " << xml->errorString();
return false;
}
cleanList(); cleanList();
while (!xml->atEnd()) { while (!xml->atEnd()) {
xml->readNext(); xml->readNext();
@ -374,6 +461,10 @@ bool DeckList::loadFromXml(QXmlStreamReader *xml)
} }
} }
updateDeckHash(); updateDeckHash();
if (xml->error()) {
qDebug() << "Error loading deck from xml: " << xml->errorString();
return false;
}
return true; return true;
} }
@ -396,8 +487,7 @@ QString DeckList::writeToString_Native()
bool DeckList::loadFromFile_Native(QIODevice *device) bool DeckList::loadFromFile_Native(QIODevice *device)
{ {
QXmlStreamReader xml(device); QXmlStreamReader xml(device);
loadFromXml(&xml); return loadFromXml(&xml);
return true;
} }
bool DeckList::saveToFile_Native(QIODevice *device) bool DeckList::saveToFile_Native(QIODevice *device)

View file

@ -35,12 +35,16 @@ public:
void setMoveList(const QList<MoveCard_ToZone> &_moveList); void setMoveList(const QList<MoveCard_ToZone> &_moveList);
}; };
enum DeckSortMethod { ByNumber, ByName, ByPrice };
class AbstractDecklistNode { class AbstractDecklistNode {
protected: protected:
InnerDecklistNode *parent; InnerDecklistNode *parent;
DeckSortMethod sortMethod;
public: public:
AbstractDecklistNode(InnerDecklistNode *_parent = 0); AbstractDecklistNode(InnerDecklistNode *_parent = 0);
virtual ~AbstractDecklistNode() { } virtual ~AbstractDecklistNode() { }
virtual void setSortMethod(DeckSortMethod method) { sortMethod = method; }
virtual QString getName() const = 0; virtual QString getName() const = 0;
InnerDecklistNode *getParent() const { return parent; } InnerDecklistNode *getParent() const { return parent; }
int depth() const; int depth() const;
@ -59,6 +63,7 @@ public:
InnerDecklistNode(const QString &_name = QString(), InnerDecklistNode *_parent = 0) : AbstractDecklistNode(_parent), name(_name) { } InnerDecklistNode(const QString &_name = QString(), InnerDecklistNode *_parent = 0) : AbstractDecklistNode(_parent), name(_name) { }
InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent = 0); InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent = 0);
virtual ~InnerDecklistNode(); virtual ~InnerDecklistNode();
void setSortMethod(DeckSortMethod method);
QString getName() const { return name; } QString getName() const { return name; }
void setName(const QString &_name) { name = _name; } void setName(const QString &_name) { name = _name; }
static QString visibleNameFromName(const QString &_name); static QString visibleNameFromName(const QString &_name);
@ -69,6 +74,9 @@ public:
int recursiveCount(bool countTotalCards = false) const; int recursiveCount(bool countTotalCards = false) const;
float recursivePrice(bool countTotalCards = false) const; float recursivePrice(bool countTotalCards = false) const;
bool compare(AbstractDecklistNode *other) const; bool compare(AbstractDecklistNode *other) const;
bool compareNumber(AbstractDecklistNode *other) const;
bool compareName(AbstractDecklistNode *other) const;
bool comparePrice(AbstractDecklistNode *other) const;
QVector<QPair<int, int> > sort(Qt::SortOrder order = Qt::AscendingOrder); QVector<QPair<int, int> > sort(Qt::SortOrder order = Qt::AscendingOrder);
bool readElement(QXmlStreamReader *xml); bool readElement(QXmlStreamReader *xml);
@ -87,6 +95,9 @@ public:
float getTotalPrice() const { return getNumber() * getPrice(); } float getTotalPrice() const { return getNumber() * getPrice(); }
int height() const { return 0; } int height() const { return 0; }
bool compare(AbstractDecklistNode *other) const; bool compare(AbstractDecklistNode *other) const;
bool compareNumber(AbstractDecklistNode *other) const;
bool compareName(AbstractDecklistNode *other) const;
bool compareTotalPrice(AbstractDecklistNode *other) const;
bool readElement(QXmlStreamReader *xml); bool readElement(QXmlStreamReader *xml);
void writeElement(QXmlStreamWriter *xml); void writeElement(QXmlStreamWriter *xml);

View file

@ -1,333 +1,254 @@
<?xml version="1.0" encoding="UTF-8"?> <?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> <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> <!-- <picture_url_hq>http://mtgimage.com/multiverseid/!cardid!.jpg</picture_url_hq> -->
<set import="1"> <set_url>http://mtgjson.com/json/!name!.json</set_url>
<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>
<set import="1"> <set import="1">
<name>ARB</name> <name>ARB</name>
<longname>Alara Reborn</longname> <longname>Alara Reborn</longname>
</set> </set>
<set import="1"> <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> <name>ARC</name>
<longname>Archenemy</longname> <longname>Archenemy</longname>
</set> </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"> <set import="1">
<name>AVR</name> <name>AVR</name>
<longname>Avacyn Restored</longname> <longname>Avacyn Restored</longname>
</set> </set>
<set import="1"> <set import="0">
<name>BD</name> <name>BRB</name>
<longname>Beatdown Box Set</longname> <longname>Battle Royale Box Set</longname>
</set> </set>
<set import="1"> <set import="0">
<name>BE</name> <name>BTD</name>
<longname>Limited Edition Beta</longname> <longname>Beatdown Box Set</longname>
</set> </set>
<set import="1"> <set import="1">
<name>BOK</name> <name>BOK</name>
<longname>Betrayers of Kamigawa</longname> <longname>Betrayers of Kamigawa</longname>
</set> </set>
<set import="1"> <set import="1">
<name>BR</name> <name>BNG</name>
<longname>Battle Royale Box Set</longname> <longname>Born of the Gods</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>
</set> </set>
<set import="1"> <set import="1">
<name>CHK</name> <name>CHK</name>
<longname>Champions of Kamigawa</longname> <longname>Champions of Kamigawa</longname>
</set> </set>
<set import="1"> <set import="1">
<name>CMA</name> <name>CHR</name>
<longname>Commander's Arsenal</longname> <longname>Chronicles</longname>
</set> </set>
<set import="1"> <set import="1">
<name>CMD</name> <name>6ED</name>
<longname>Commander</longname> <longname>Classic Sixth Edition</longname>
</set> </set>
<set import="1"> <set import="1">
<name>CP</name> <name>CSP</name>
<longname>Champs</longname>
</set>
<set import="1">
<name>CS</name>
<longname>Coldsnap</longname> <longname>Coldsnap</longname>
</set> </set>
<set import="1"> <set import="1">
<name>CSTD</name> <name>CON</name>
<longname>Coldsnap Theme Decks</longname> <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>
<set import="1"> <set import="1">
<name>DCILM</name> <name>DST</name>
<longname>Legend Membership</longname> <longname>Darksteel</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>
</set> </set>
<set import="1"> <set import="1">
<name>DKA</name> <name>DKA</name>
<longname>Dark Ascension</longname> <longname>Dark Ascension</longname>
</set> </set>
<set import="1"> <set import="1">
<name>DK</name> <name>DIS</name>
<longname>The Dark</longname> <longname>Dissension</longname>
</set> </set>
<set import="1"> <set import="1">
<name>DM</name> <name>DGM</name>
<longname>Deckmasters</longname> <longname>Dragon's Maze</longname>
</set> </set>
<set import="1"> <set import="0">
<name>DPA</name> <name>DDH</name>
<longname>Duels of the Planeswalkers</longname> <longname>Duel Decks: Ajani vs. Nicol Bolas</longname>
</set> </set>
<set import="1"> <set import="0">
<name>DRC</name> <name>DDC</name>
<longname>Dragon Con</longname>
</set>
<set import="1">
<name>DS</name>
<longname>Darksteel</longname>
</set>
<set import="1">
<name>DVD</name>
<longname>Duel Decks: Divine vs. Demonic</longname> <longname>Duel Decks: Divine vs. Demonic</longname>
</set> </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"> <set import="1">
<name>EURO</name> <name>8ED</name>
<longname>European Land Program</longname> <longname>Eighth Edition</longname>
</set> </set>
<set import="1"> <set import="1">
<name>EVE</name> <name>EVE</name>
<longname>Eventide</longname> <longname>Eventide</longname>
</set> </set>
<set import="1"> <set import="1">
<name>EVG</name> <name>EXO</name>
<longname>Duel Decks: Elves vs. Goblins</longname>
</set>
<set import="1">
<name>EX</name>
<longname>Exodus</longname> <longname>Exodus</longname>
</set> </set>
<set import="1"> <set import="1">
<name>FE</name> <name>FEM</name>
<longname>Fallen Empires</longname> <longname>Fallen Empires</longname>
</set> </set>
<set import="1"> <set import="1">
<name>FNMP</name> <name>5DN</name>
<longname>Friday Night Magic</longname> <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>
<set import="1"> <set import="1">
<name>FUT</name> <name>FUT</name>
<longname>Future Sight</longname> <longname>Future Sight</longname>
</set> </set>
<set import="1"> <set import="1">
<name>FVD</name> <name>GPT</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>
<longname>Guildpact</longname> <longname>Guildpact</longname>
</set> </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"> <set import="1">
<name>GTC</name> <name>GTC</name>
<longname>Gatecrash</longname> <longname>Gatecrash</longname>
</set> </set>
<set import="1"> <set import="1">
<name>GURU</name> <name>HML</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>
<longname>Homelands</longname> <longname>Homelands</longname>
</set> </set>
<set import="1"> <set import="1">
<name>IA</name> <name>ICE</name>
<longname>Ice Age</longname> <longname>Ice Age</longname>
</set> </set>
<set import="1">
<name>IN</name>
<longname>Invasion</longname>
</set>
<set import="1"> <set import="1">
<name>ISD</name> <name>ISD</name>
<longname>Innistrad</longname> <longname>Innistrad</longname>
</set> </set>
<set import="1"> <set import="1">
<name>ITP</name> <name>INV</name>
<longname>Introductory Two-Player Set</longname> <longname>Invasion</longname>
</set> </set>
<set import="1"> <set import="1">
<name>JR</name> <name>JOU</name>
<longname>Judge Gift Program</longname> <longname>Journey into Nyx</longname>
</set> </set>
<set import="1"> <set import="1">
<name>JU</name> <name>JUD</name>
<longname>Judgment</longname> <longname>Judgment</longname>
</set> </set>
<set import="1"> <set import="1">
<name>JVC</name> <name>LEG</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>
<longname>Legends</longname> <longname>Legends</longname>
</set> </set>
<set import="1"> <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> <longname>Lorwyn</longname>
</set> </set>
<set import="1"> <set import="1">
@ -347,63 +268,59 @@
<longname>Magic 2013</longname> <longname>Magic 2013</longname>
</set> </set>
<set import="1"> <set import="1">
<name>MBP</name> <name>M14</name>
<longname>Media Inserts</longname> <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>
<set import="1"> <set import="1">
<name>MBS</name> <name>MBS</name>
<longname>Mirrodin Besieged</longname> <longname>Mirrodin Besieged</longname>
</set> </set>
<set import="0">^M
<name>MMA</name>^M
<longname>Modern Masters</longname>^M
</set>^
<set import="1"> <set import="1">
<name>ME2</name> <name>MOR</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>
<longname>Morningtide</longname> <longname>Morningtide</longname>
</set> </set>
<set import="1"> <set import="1">
<name>NE</name> <name>NMS</name>
<longname>Nemesis</longname> <longname>Nemesis</longname>
</set> </set>
<set import="1"> <set import="1">
@ -411,104 +328,80 @@
<longname>New Phyrexia</longname> <longname>New Phyrexia</longname>
</set> </set>
<set import="1"> <set import="1">
<name>OD</name> <name>9ED</name>
<longname>Ninth Edition</longname>
</set>
<set import="1">
<name>ODY</name>
<longname>Odyssey</longname> <longname>Odyssey</longname>
</set> </set>
<set import="1"> <set import="1">
<name>ON</name> <name>ONS</name>
<longname>Onslaught</longname> <longname>Onslaught</longname>
</set> </set>
<set import="1"> <set import="1">
<name>P3K</name> <name>PLC</name>
<longname>Portal Three Kingdoms</longname> <longname>Planar Chaos</longname>
</set> </set>
<set import="1"> <set import="0">
<name>HOP</name>
<longname>Planechase</longname>
</set>
<set import="0">
<name>PC2</name> <name>PC2</name>
<longname>Planechase 2012 Edition</longname> <longname>Planechase 2012 Edition</longname>
</set> </set>
<set import="1"> <set import="1">
<name>PC</name> <name>PLS</name>
<longname>Planar Chaos</longname> <longname>Planeshift</longname>
</set> </set>
<set import="1"> <set import="1">
<name>PCH</name> <name>POR</name>
<longname>Planechase</longname> <longname>Portal</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>
</set> </set>
<set import="1"> <set import="1">
<name>PO2</name> <name>PO2</name>
<longname>Portal Second Age</longname> <longname>Portal Second Age</longname>
</set> </set>
<set import="1"> <set import="1">
<name>PO</name> <name>PTK</name>
<longname>Portal</longname> <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>
<set import="1"> <set import="1">
<name>POT</name> <name>PCY</name>
<longname>Portal Demogame</longname>
</set>
<set import="1">
<name>PR</name>
<longname>Prophecy</longname> <longname>Prophecy</longname>
</set> </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"> <set import="1">
<name>RAV</name> <name>RAV</name>
<longname>Ravnica: City of Guilds</longname> <longname>Ravnica: City of Guilds</longname>
</set> </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"> <set import="1">
<name>RTR</name> <name>RTR</name>
<longname>Return to Ravnica</longname> <longname>Return to Ravnica</longname>
</set> </set>
<set import="1"> <set import="1">
<name>RV</name> <name>3ED</name>
<longname>Revised Edition</longname> <longname>Revised Edition</longname>
</set> </set>
<set import="1"> <set import="1">
<name>SC</name> <name>ROE</name>
<longname>Scourge</longname> <longname>Rise of the Eldrazi</longname>
</set>
<set import="1">
<name>SH</name>
<longname>Stronghold</longname>
</set>
<set import="1">
<name>SHM</name>
<longname>Shadowmoor</longname>
</set> </set>
<set import="1"> <set import="1">
<name>SOK</name> <name>SOK</name>
@ -519,97 +412,97 @@
<longname>Scars of Mirrodin</longname> <longname>Scars of Mirrodin</longname>
</set> </set>
<set import="1"> <set import="1">
<name>ST2K</name> <name>SCG</name>
<longname>Starter 2000</longname> <longname>Scourge</longname>
</set> </set>
<set import="1"> <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> <longname>Starter 1999</longname>
</set> </set>
<set import="1"> <set import="1">
<name>SUM</name> <name>S00</name>
<longname>Summer of Magic</longname> <longname>Starter 2000</longname>
</set> </set>
<set import="1"> <set import="1">
<name>SUS</name> <name>STH</name>
<longname>Super Series</longname> <longname>Stronghold</longname>
</set> </set>
<set import="1"> <set import="1">
<name>THGT</name> <name>TMP</name>
<longname>Two-Headed Giant Tournament</longname>
</set>
<set import="1">
<name>TP</name>
<longname>Tempest</longname> <longname>Tempest</longname>
</set> </set>
<set import="1"> <set import="1">
<name>TR</name> <name>10E</name>
<longname>Torment</longname> <longname>Tenth Edition</longname>
</set> </set>
<set import="1"> <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> <longname>Time Spiral</longname>
</set> </set>
<set import="1"> <set import="1">
<name>TSTS</name> <name>TSB</name>
<longname>Time Spiral "Timeshifted"</longname> <longname>Time Spiral "Timeshifted"</longname>
</set> </set>
<set import="1"> <set import="1">
<name>UD</name> <name>TOR</name>
<longname>Urza's Destiny</longname> <longname>Torment</longname>
</set> </set>
<set import="1"> <set import="0">
<name>UG</name> <name>UGL</name>
<longname>Unglued</longname> <longname>Unglued</longname>
</set> </set>
<set import="1"> <set import="0">
<name>UHAA</name> <name>UNH</name>
<longname>Unhinged Alternate Foils</longname>
</set>
<set import="1">
<name>UH</name>
<longname>Unhinged</longname> <longname>Unhinged</longname>
</set> </set>
<set import="1"> <set import="1">
<name>UL</name> <name>2ED</name>
<longname>Urza's Legacy</longname>
</set>
<set import="1">
<name>UN</name>
<longname>Unlimited Edition</longname> <longname>Unlimited Edition</longname>
</set> </set>
<set import="1"> <set import="1">
<name>UQC</name> <name>UDS</name>
<longname>Celebration Cards</longname> <longname>Urza's Destiny</longname>
</set> </set>
<set import="1"> <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> <longname>Urza's Saga</longname>
</set> </set>
<set import="1"> <set import="0">
<name>V12</name> <name>VAN</name>
<longname>From the Vault: Realms</longname> <longname>Vanguard</longname>
</set> </set>
<set import="1"> <set import="1">
<name>VI</name> <name>VIS</name>
<longname>Visions</longname> <longname>Visions</longname>
</set> </set>
<set import="1"> <set import="1">
<name>WL</name> <name>WTH</name>
<longname>Weatherlight</longname> <longname>Weatherlight</longname>
</set> </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"> <set import="1">
<name>WWK</name> <name>WWK</name>
<longname>Worldwake</longname> <longname>Worldwake</longname>

Binary file not shown.

View file

@ -224,7 +224,7 @@ Start the oracle.exe (the installer does this automatically) and let it generate
\end{enumerate} \end{enumerate}
Congratulations, you may now use Cockatrice! Congratulations, you may now use Cockatrice!
\subsubsection{Linux and BSD} \subsubsection{Linux, BSD, OS X}
The following procedures have been tested with Debian Wheezy, Fedora 18, XUbuntu 13.10, FreeBSD 9.1 and 10.0. The following procedures have been tested with Debian Wheezy, Fedora 18, XUbuntu 13.10, FreeBSD 9.1 and 10.0.
If you use Gentoo with KDE you have the needed prerequisites and may continue with downloading the source. If you use Gentoo with KDE you have the needed prerequisites and may continue with downloading the source.
If you use Bodhi or Arch Linux (AUR) or another distribution that includes Cockatrice, you might install Cockatrice from the default packages -- though the package might be old, If you use Bodhi or Arch Linux (AUR) or another distribution that includes Cockatrice, you might install Cockatrice from the default packages -- though the package might be old,
@ -239,6 +239,7 @@ Before you install new software, you should update your system. The following in
yum install qt-devel qt-mobility-devel protobuf-devel protobuf-compiler cmake} yum install qt-devel qt-mobility-devel protobuf-devel protobuf-compiler cmake}
\item[FreeBSD 9] \shellcmd{pkg\_add -r qt4 qt4-linguist qt4-moc qt4-qmake qt4-rcc qt4-uic git cmake protobuf} \item[FreeBSD 9] \shellcmd{pkg\_add -r qt4 qt4-linguist qt4-moc qt4-qmake qt4-rcc qt4-uic git cmake protobuf}
\item[FreeBSD 10] \shellcmd{pkg install qt4 qt4-linguist qt4-moc qt4-qmake qt4-rcc qt4-uic git cmake protobuf} \item[FreeBSD 10] \shellcmd{pkg install qt4 qt4-linguist qt4-moc qt4-qmake qt4-rcc qt4-uic git cmake protobuf}
\item[OS X] \shellcmd{brew install qt cmake protobuf}
\end{description} \end{description}
\item Download the sources from github via \\ \shellcmd{cd\\ git clone https://github.com/Daenyth/Cockatrice.git} \item Download the sources from github via \\ \shellcmd{cd\\ git clone https://github.com/Daenyth/Cockatrice.git}
\item To compile the sources, change into the newly created directory, create a build directory and invoke cmake:\\ \item To compile the sources, change into the newly created directory, create a build directory and invoke cmake:\\
@ -253,9 +254,6 @@ make}\\
The default paths for decks, pics, cards and tokens are located in \\ \shellcmd{/home/<user>/.local/share/data/Cockatrice/Cockatrice}. The default paths for decks, pics, cards and tokens are located in \\ \shellcmd{/home/<user>/.local/share/data/Cockatrice/Cockatrice}.
\end{enumerate} \end{enumerate}
\subsubsection{MacOS X}
TODO, please contribute this section! See Linux section, then use the \shellcmd{prepareMacRelease.sh} script from Cockatrice.
\subsection{Building the Server} \subsection{Building the Server}
You don't need your own server if you plan to play only. But as Cockatrice is open source you are free to run your own. You don't need your own server if you plan to play only. But as Cockatrice is open source you are free to run your own.
The compilation works like already written above, but instead of invoking \shellcmd{cmake ..}, you have to do it like this: The compilation works like already written above, but instead of invoking \shellcmd{cmake ..}, you have to do it like this:
@ -706,6 +704,8 @@ USE AT YOUR OWN RISK.
\begin{enumerate} \begin{enumerate}
\item Open a command shell and install the prerequisites \item Open a command shell and install the prerequisites
\begin{enumerate} \begin{enumerate}
\item \shellcmd{cd /etc/yum.repos.d/}
\item \shellcmd{sudo wget http://kdeforge.unl.edu/apt/kde-redhat/epel/kde.repo}
\item \shellcmd{yum -y groupinstall "development tools"} \item \shellcmd{yum -y groupinstall "development tools"}
\item \shellcmd{rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86\_64/epel-release-6-8.noarch.rpm} \item \shellcmd{rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86\_64/epel-release-6-8.noarch.rpm}
\item \shellcmd{yum -y install qt-mysql qt-devel qt-mobility-devel protobuf-devel protobuf-compiler cmake28 libgcrypt-devel} \item \shellcmd{yum -y install qt-mysql qt-devel qt-mobility-devel protobuf-devel protobuf-compiler cmake28 libgcrypt-devel}

View file

@ -91,7 +91,7 @@ SectionEnd
Section "Start menu item" SecStartMenu Section "Start menu item" SecStartMenu
createDirectory "$SMPROGRAMS\Cockatrice" createDirectory "$SMPROGRAMS\Cockatrice"
createShortCut "$SMPROGRAMS\Cockatrice\Cockatrice.lnk" "$INSTDIR\cockatrice.exe" createShortCut "$SMPROGRAMS\Cockatrice\Cockatrice.lnk" "$INSTDIR\cockatrice.exe" '--debug-output'
createShortCut "$SMPROGRAMS\Cockatrice\Oracle.lnk" "$INSTDIR\oracle.exe" createShortCut "$SMPROGRAMS\Cockatrice\Oracle.lnk" "$INSTDIR\oracle.exe"
createShortCut "$SMPROGRAMS\Cockatrice\Usermanual.lnk" "$INSTDIR\Usermanual.pdf" createShortCut "$SMPROGRAMS\Cockatrice\Usermanual.lnk" "$INSTDIR\Usermanual.pdf"
SectionEnd SectionEnd

View file

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

View file

@ -1,6 +1,6 @@
#include <QApplication> #include <QApplication>
#include <QTextCodec> #include <QTextCodec>
#include "window_main.h" #include "oraclewizard.h"
#include "settingscache.h" #include "settingscache.h"
SettingsCache *settingsCache; SettingsCache *settingsCache;
@ -12,13 +12,14 @@ int main(int argc, char *argv[])
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QCoreApplication::setOrganizationName("Cockatrice"); 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"); QCoreApplication::setApplicationName("Cockatrice");
settingsCache = new SettingsCache; settingsCache = new SettingsCache;
WindowMain wnd; OracleWizard wizard;
wnd.show(); wizard.show();
return app.exec(); return app.exec();
} }

View file

@ -4,73 +4,49 @@
#else #else
#include <QtWidgets> #include <QtWidgets>
#endif #endif
#include <QtNetwork>
#include <QXmlStreamReader>
#include <QDomDocument>
#include <QDebug> #include <QDebug>
#include "qt-json/json.h"
OracleImporter::OracleImporter(const QString &_dataDir, QObject *parent) 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) bool OracleImporter::readSetsFromByteArray(const QByteArray &data)
{ {
QXmlStreamReader xml(data); QList<SetToDownload> newSetList;
return readSetsFromXml(xml);
bool ok;
setsMap = QtJson::Json::parse(QString(data), ok).toMap();
if (!ok) {
qDebug() << "error: QtJson::Json::parse()";
return 0;
} }
bool OracleImporter::readSetsFromXml(QXmlStreamReader &xml) QListIterator<QVariant> it(setsMap.values());
{ QVariantMap map;
QList<SetToDownload> newSetList;
QString edition; QString edition;
QString editionLong; QString editionLong;
QString editionURL; QVariant editionCards;
while (!xml.atEnd()) { bool import;
if (xml.readNext() == QXmlStreamReader::EndElement)
break; while (it.hasNext()) {
if (xml.name() == "set") { map = it.next().toMap();
bool import = xml.attributes().value("import").toString().toInt(); edition = map.value("code").toString();
while (!xml.atEnd()) { editionLong = map.value("name").toString();
if (xml.readNext() == QXmlStreamReader::EndElement) editionCards = map.value("cards");
break;
if (xml.name() == "name") // core and expansion sets are marked to be imported by default
edition = xml.readElementText(); import = (0 == QString::compare(map.value("type").toString(), QString("core"), Qt::CaseInsensitive) ||
else if (xml.name() == "longname") 0 == QString::compare(map.value("type").toString(), QString("expansion"), Qt::CaseInsensitive));
editionLong = xml.readElementText();
else if (xml.name() == "url") newSetList.append(SetToDownload(edition, editionLong, editionCards, import));
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();
} }
qSort(newSetList);
if (newSetList.isEmpty()) if (newSetList.isEmpty())
return false; return false;
allSets = newSetList; allSets = newSetList;
@ -81,7 +57,7 @@ CardInfo *OracleImporter::addCard(const QString &setName,
QString cardName, QString cardName,
bool isToken, bool isToken,
int cardId, int cardId,
const QString &cardCost, QString &cardCost,
const QString &cardType, const QString &cardType,
const QString &cardPT, const QString &cardPT,
int cardLoyalty, int cardLoyalty,
@ -99,6 +75,10 @@ CardInfo *OracleImporter::addCard(const QString &setName,
cardName = cardName.replace("Æ", "AE"); cardName = cardName.replace("Æ", "AE");
cardName = cardName.replace("", "'"); cardName = cardName.replace("", "'");
// Remove {} around mana costs
cardCost.remove(QChar('{'));
cardCost.remove(QChar('}'));
CardInfo *card; CardInfo *card;
if (cardHash.contains(cardName)) { if (cardHash.contains(cardName)) {
card = cardHash.value(cardName); card = cardHash.value(cardName);
@ -143,188 +123,124 @@ CardInfo *OracleImporter::addCard(const QString &setName,
cardHash.insert(cardName, card); cardHash.insert(cardName, card);
} }
card->setPicURL(setName, getPictureUrl(pictureUrl, cardId, cardName, setName)); card->setMuId(setName, cardId);
card->setPicURLHq(setName, getPictureUrl(pictureUrlHq, cardId, cardName, setName));
card->setPicURLSt(setName, getPictureUrl(pictureUrlSt, cardId, cardName, setName));
return card; return card;
} }
int OracleImporter::importTextSpoiler(CardSet *set, const QByteArray &data) int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data)
{ {
int cards = 0; int cards = 0;
QString bufferContents(data);
// Workaround for ampersand bug in text spoilers QListIterator<QVariant> it(data.toList());
int index = -1; QVariantMap map;
while ((index = bufferContents.indexOf('&', index + 1)) != -1) { QString cardName;
int semicolonIndex = bufferContents.indexOf(';', index); QString cardCost;
if (semicolonIndex > 5) { QString cardType;
bufferContents.insert(index + 1, "amp;"); QString cardPT;
index += 4; QString cardText;
} int cardId;
int cardLoyalty;
QMap<int, QVariantMap> splitCards;
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;
} }
QDomDocument doc; // add first card's data
QString errorMsg; cardName = card1->contains("name") ? card1->value("name").toString() : QString("");
int errorLine, errorColumn; cardCost = card1->contains("manaCost") ? card1->value("manaCost").toString() : QString("");
if (!doc.setContent(bufferContents, &errorMsg, &errorLine, &errorColumn)) cardType = card1->contains("type") ? card1->value("type").toString() : QString("");
qDebug() << "error:" << errorMsg << "line:" << errorLine << "column:" << errorColumn; 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("");
QDomNodeList divs = doc.elementsByTagName("div"); // add second card's data
for (int i = 0; i < divs.size(); ++i) { cardName += card2->contains("name") ? QString(" // ") + card2->value("name").toString() : QString("");
QDomElement div = divs.at(i).toElement(); cardCost += card2->contains("manaCost") ? QString(" // ") + card2->value("manaCost").toString() : QString("");
QDomNode divClass = div.attributes().namedItem("class"); cardType += card2->contains("type") ? QString(" // ") + card2->value("type").toString() : QString("");
if (divClass.nodeValue() == "textspoiler") { cardPT += card2->contains("power") || card2->contains("toughness") ? QString(" // ") + card2->value("power").toString() + QString('/') + card2->value("toughness").toString() : QString("");
QString cardName, cardCost, cardType, cardPT, cardText; cardText += card2->contains("text") ? QString("\n\n---\n\n") + card2->value("text").toString() : QString("");
int cardId = 0; } else {
int cardLoyalty = 0; // 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;
}
QDomNodeList trs = div.elementsByTagName("tr"); CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n"));
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)) { if (!set->contains(card)) {
card->addToSet(set); card->addToSet(set);
cards++; 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; 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(); clear();
setsToDownload.clear(); int setCards = 0, setIndex= 0;
for (int i = 0; i < allSets.size(); ++i) QListIterator<SetToDownload> it(allSets);
if (allSets[i].getImport()) const SetToDownload * curSet;
setsToDownload.append(allSets[i]);
if (setsToDownload.isEmpty()) while (it.hasNext())
return 0;
setIndex = 0;
emit setIndexChanged(0, 0, setsToDownload[0].getLongName());
downloadNextFile();
return setsToDownload.size();
}
void OracleImporter::downloadNextFile()
{ {
QString urlString = setsToDownload[setIndex].getUrl(); curSet = & it.next();
if (urlString.isEmpty()) if(!curSet->getImport())
urlString = setUrl; continue;
urlString = urlString.replace("!longname!", setsToDownload[setIndex].getLongName());
if (urlString.startsWith("http://")) { CardSet *set = new CardSet(curSet->getShortName(), curSet->getLongName());
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())) if (!setHash.contains(set->getShortName()))
setHash.insert(set->getShortName(), set); setHash.insert(set->getShortName(), set);
buffer->seek(0); int setCards = importTextSpoiler(set, curSet->getCards());
buffer->close();
int cards = importTextSpoiler(set, buffer->data());
if (cards > 0)
++setIndex; ++setIndex;
if (setIndex == setsToDownload.size()) { emit setIndexChanged(setCards, setIndex, curSet->getLongName());
emit setIndexChanged(cards, setIndex, QString());
setIndex = -1;
} else {
downloadNextFile();
emit setIndexChanged(cards, setIndex, setsToDownload[setIndex].getLongName());
}
} }
void OracleImporter::readResponseHeader(const QHttpResponseHeader &responseHeader) emit setIndexChanged(setCards, setIndex, QString());
{
switch (responseHeader.statusCode()) { // total number of sets
case 200: return setIndex;
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();
}
} }

View file

@ -1,53 +1,42 @@
#ifndef ORACLEIMPORTER_H #ifndef ORACLEIMPORTER_H
#define ORACLEIMPORTER_H #define ORACLEIMPORTER_H
#include <carddatabase.h> #include <QMap>
#include <QHttp>
class QBuffer; #include <carddatabase.h>
class QXmlStreamReader;
class SetToDownload { class SetToDownload {
private: private:
QString shortName, longName, url; QString shortName, longName;
bool import; bool import;
QVariant cards;
public: public:
const QString &getShortName() const { return shortName; } const QString &getShortName() const { return shortName; }
const QString &getLongName() const { return longName; } const QString &getLongName() const { return longName; }
const QString &getUrl() const { return url; } const QVariant &getCards() const { return cards; }
bool getImport() const { return import; } bool getImport() const { return import; }
void setImport(bool _import) { import = _import; } void setImport(bool _import) { import = _import; }
SetToDownload(const QString &_shortName, const QString &_longName, const QString &_url, bool _import) SetToDownload(const QString &_shortName, const QString &_longName, const QVariant &_cards, bool _import)
: shortName(_shortName), longName(_longName), url(_url), import(_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 { class OracleImporter : public CardDatabase {
Q_OBJECT Q_OBJECT
private: private:
QList<SetToDownload> allSets, setsToDownload; QList<SetToDownload> allSets;
QString pictureUrl, pictureUrlHq, pictureUrlSt, setUrl; QVariantMap setsMap;
QString dataDir; QString dataDir;
int setIndex;
int reqId;
QBuffer *buffer;
QHttp *http;
QString getPictureUrl(QString url, int cardId, QString name, const QString &setName) const;
void downloadNextFile(); CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, QString &cardCost, const QString &cardType, const QString &cardPT, int cardLoyalty, const QStringList &cardText);
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);
signals: 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); void dataReadProgress(int bytesRead, int totalBytes);
public: public:
OracleImporter(const QString &_dataDir, QObject *parent = 0); OracleImporter(const QString &_dataDir, QObject *parent = 0);
bool readSetsFromByteArray(const QByteArray &data); bool readSetsFromByteArray(const QByteArray &data);
bool readSetsFromFile(const QString &fileName); int startImport();
int startDownload(); int importTextSpoiler(CardSet *set, const QVariant &data);
int importTextSpoiler(CardSet *set, const QByteArray &data);
QList<SetToDownload> &getSets() { return allSets; } QList<SetToDownload> &getSets() { return allSets; }
const QString &getDataDir() const { return dataDir; } 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,256 +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)
{
#if QT_VERSION < 0x050000
importer = new OracleImporter(QDesktopServices::storageLocation(QDesktopServices::DataLocation), this);
#else
importer = new OracleImporter(QStandardPaths::standardLocations(QStandardPaths::DataLocation)).toString(), this);
#endif
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;
#if QT_VERSION < 0x050000
const QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
#else
const QString dataDir = QStandardPaths::standardLocations(QStandardPaths::DataLocation)).toString();
#endif
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