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)
add_subdirectory(oracle)
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 {
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
protected:
AbstractCardItem *item;
QPointF hotSpot;

View file

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

View file

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

View file

@ -14,7 +14,7 @@
#include <QNetworkRequest>
#include <QDebug>
const int CardDatabase::versionNeeded = 2;
const int CardDatabase::versionNeeded = 3;
CardSet::CardSet(const QString &_shortName, const QString &_longName)
: shortName(_shortName), longName(_longName)
@ -80,11 +80,13 @@ bool PictureToLoad::nextSet()
return true;
}
PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, QObject *parent)
: QObject(parent), _picsPath(__picsPath), picDownload(_picDownload), downloadRunning(false), loadQueueRunning(false)
PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent)
: QObject(parent),
_picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq),
downloadRunning(false), loadQueueRunning(false)
{
connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection);
networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *)));
}
@ -99,7 +101,7 @@ void PictureLoader::processLoadQueue()
{
if (loadQueueRunning)
return;
loadQueueRunning = true;
forever {
mutex.lock();
@ -113,7 +115,7 @@ void PictureLoader::processLoadQueue()
QString correctedName = ptl.getCard()->getCorrectedName();
QString picsPath = _picsPath;
QString setName = ptl.getSetName();
QImage image;
if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg(setName).arg(correctedName)))
if (!image.load(QString("%1/%2/%3%4.full.jpg").arg(picsPath).arg(setName).arg(correctedName).arg(1)))
@ -130,11 +132,25 @@ void PictureLoader::processLoadQueue()
}
continue;
}
emit imageLoaded(ptl.getCard(), image);
}
}
QString PictureLoader::getPicUrl(CardInfo *card)
{
if (!picDownload) return 0;
QString picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl();
picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName()));
CardSet *set = card->getPreferredSet();
picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName()));
picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName()));
picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(card->getPreferredMuId())));
return picUrl;
}
void PictureLoader::startNextPicDownload()
{
if (cardsToDownload.isEmpty()) {
@ -142,31 +158,28 @@ void PictureLoader::startNextPicDownload()
downloadRunning = false;
return;
}
downloadRunning = true;
cardBeingDownloaded = cardsToDownload.takeFirst();
QString picUrl;
if (cardBeingDownloaded.getStripped())
picUrl = cardBeingDownloaded.getCard()->getPicURLSt(cardBeingDownloaded.getSetName());
else if (cardBeingDownloaded.getHq()) {
picUrl = cardBeingDownloaded.getCard()->getPicURLHq(cardBeingDownloaded.getSetName());
if (picUrl.isEmpty()) {
picUrl = cardBeingDownloaded.getCard()->getPicURL(cardBeingDownloaded.getSetName());
cardBeingDownloaded.setHq(false);
}
} else
picUrl = cardBeingDownloaded.getCard()->getPicURL(cardBeingDownloaded.getSetName());
// TODO: Do something useful when picUrl is 0 or empty, etc
QString picUrl = getPicUrl(cardBeingDownloaded.getCard());
QUrl url(picUrl);
QNetworkRequest req(url);
qDebug() << "starting picture download:" << req.url();
qDebug() << "starting picture download:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url();
networkManager->get(req);
}
void PictureLoader::picDownloadFinished(QNetworkReply *reply)
{
QString picsPath = _picsPath;
if (reply->error()) {
qDebug() << "Download failed:" << reply->errorString();
}
const QByteArray &picData = reply->readAll();
QImage testImage;
if (testImage.loadFromData(picData)) {
@ -180,17 +193,17 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply)
QDir dir(QString(picsPath + "/downloadedPics"));
dir.mkdir(cardBeingDownloaded.getSetName());
}
QString suffix;
if (!cardBeingDownloaded.getStripped())
suffix = ".full";
QFile newPic(picsPath + "/downloadedPics/" + cardBeingDownloaded.getSetName() + "/" + cardBeingDownloaded.getCard()->getCorrectedName() + suffix + ".jpg");
if (!newPic.open(QIODevice::WriteOnly))
return;
newPic.write(picData);
newPic.close();
emit imageLoaded(cardBeingDownloaded.getCard(), testImage);
} else if (cardBeingDownloaded.getHq()) {
qDebug() << "HQ: received invalid picture. URL:" << reply->request().url();
@ -207,7 +220,7 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply)
} else
emit imageLoaded(cardBeingDownloaded.getCard(), QImage());
}
reply->deleteLater();
startNextPicDownload();
}
@ -215,7 +228,7 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply)
void PictureLoader::loadImage(CardInfo *card, bool stripped)
{
QMutexLocker locker(&mutex);
loadQueue.append(PictureToLoad(card, stripped));
emit startLoadQueue();
}
@ -232,6 +245,12 @@ void PictureLoader::setPicDownload(bool _picDownload)
picDownload = _picDownload;
}
void PictureLoader::setPicDownloadHq(bool _picDownloadHq)
{
QMutexLocker locker(&mutex);
picDownloadHq = _picDownloadHq;
}
CardInfo::CardInfo(CardDatabase *_db,
const QString &_name,
bool _isToken,
@ -244,22 +263,18 @@ CardInfo::CardInfo(CardDatabase *_db,
bool _cipt,
int _tableRow,
const SetList &_sets,
const QMap<QString, QString> &_picURLs,
const QMap<QString, QString> &_picURLsHq,
const QMap<QString, QString> &_picURLsSt)
QMap<QString, int> _muIds)
: db(_db),
name(_name),
isToken(_isToken),
sets(_sets),
muIds(_muIds),
manacost(_manacost),
cardtype(_cardtype),
powtough(_powtough),
text(_text),
colors(_colors),
loyalty(_loyalty),
picURLs(_picURLs),
picURLsHq(_picURLsHq),
picURLsSt(_picURLsSt),
cipt(_cipt),
tableRow(_tableRow),
pixmap(NULL)
@ -284,6 +299,8 @@ QString CardInfo::getMainCardType() const
int pos;
if ((pos = result.indexOf('-')) != -1)
result.remove(pos, result.length());
if ((pos = result.indexOf("")) != -1)
result.remove(pos, result.length());
if ((pos = result.indexOf("//")) != -1)
result.remove(pos, result.length());
result = result.simplified();
@ -315,19 +332,12 @@ void CardInfo::addToSet(CardSet *set)
sets << set;
}
QString CardInfo::getPicURL() const
{
SetList sortedSets = sets;
sortedSets.sortByKey();
return picURLs.value(sortedSets.first()->getShortName());
}
QPixmap *CardInfo::loadPixmap()
{
if (pixmap)
return pixmap;
pixmap = new QPixmap();
if (getName().isEmpty()) {
pixmap->load(settingsCache->getCardBackPicturePath());
return pixmap;
@ -400,18 +410,33 @@ void CardInfo::updatePixmapCache()
emit pixmapUpdated();
}
CardSet* CardInfo::getPreferredSet()
{
SetList sortedSets = sets;
sortedSets.sortByKey();
return sortedSets.first();
}
int CardInfo::getPreferredMuId()
{
return muIds[getPreferredSet()->getShortName()];
}
QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info)
{
xml.writeStartElement("card");
xml.writeTextElement("name", info->getName());
const SetList &sets = info->getSets();
QString tmpString;
QString tmpSet;
for (int i = 0; i < sets.size(); i++) {
xml.writeStartElement("set");
xml.writeAttribute("picURL", info->getPicURL(sets[i]->getShortName()));
xml.writeAttribute("picURLHq", info->getPicURLHq(sets[i]->getShortName()));
xml.writeAttribute("picURLSt", info->getPicURLSt(sets[i]->getShortName()));
xml.writeCharacters(sets[i]->getShortName());
tmpSet=sets[i]->getShortName();
xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet)));
xml.writeCharacters(tmpSet);
xml.writeEndElement();
}
const QStringList &colors = info->getColors();
@ -436,18 +461,19 @@ QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info)
}
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(cardDatabasePathChanged()), this, SLOT(loadCardDatabase()));
connect(settingsCache, SIGNAL(tokenDatabasePathChanged()), this, SLOT(loadTokenDatabase()));
connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
connect(settingsCache, SIGNAL(picDownloadHqChanged()), this, SLOT(picDownloadHqChanged()));
loadCardDatabase();
loadTokenDatabase();
pictureLoaderThread = new QThread;
pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload());
pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload(), settingsCache->getPicDownloadHq());
pictureLoader->moveToThread(pictureLoaderThread);
connect(pictureLoader, SIGNAL(imageLoaded(CardInfo *, const QImage &)), this, SLOT(imageLoaded(CardInfo *, const QImage &)));
pictureLoaderThread->start(QThread::LowPriority);
@ -572,7 +598,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
if (xml.name() == "card") {
QString name, manacost, type, pt, text;
QStringList colors;
QMap<QString, QString> picURLs, picURLsHq, picURLsSt;
QMap<QString, int> muids;
SetList sets;
int tableRow = 0;
int loyalty = 0;
@ -592,14 +618,12 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
else if (xml.name() == "text")
text = xml.readElementText();
else if (xml.name() == "set") {
QString picURL = xml.attributes().value("picURL").toString();
QString picURLHq = xml.attributes().value("picURLHq").toString();
QString picURLSt = xml.attributes().value("picURLSt").toString();
QXmlStreamAttributes attrs = xml.attributes();
QString setName = xml.readElementText();
sets.append(getSet(setName));
picURLs.insert(setName, picURL);
picURLsHq.insert(setName, picURLHq);
picURLsSt.insert(setName, picURLSt);
if (attrs.hasAttribute("muId")) {
muids[setName] = attrs.value("muId").toString().toInt();
}
} else if (xml.name() == "color")
colors << xml.readElementText();
else if (xml.name() == "tablerow")
@ -611,18 +635,18 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
else if (xml.name() == "token")
isToken = xml.readElementText().toInt();
}
cardHash.insert(name, new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, picURLs, picURLsHq, picURLsSt));
cardHash.insert(name, new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, muids));
}
}
}
bool CardDatabase::loadFromFile(const QString &fileName, bool tokens)
LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens)
{
QFile file(fileName);
file.open(QIODevice::ReadOnly);
if (!file.isOpen())
return false;
return FileError;
if (tokens) {
QMutableHashIterator<QString, CardInfo *> i(cardHash);
while (i.hasNext()) {
@ -639,7 +663,7 @@ bool CardDatabase::loadFromFile(const QString &fileName, bool tokens)
delete setIt.value();
}
setHash.clear();
QMutableHashIterator<QString, CardInfo *> i(cardHash);
while (i.hasNext()) {
i.next();
@ -655,9 +679,12 @@ bool CardDatabase::loadFromFile(const QString &fileName, bool tokens)
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) {
if (xml.name() != "cockatrice_carddatabase")
return false;
if (xml.attributes().value("version").toString().toInt() < versionNeeded)
return false;
return Invalid;
int version = xml.attributes().value("version").toString().toInt();
if (version < versionNeeded) {
qDebug() << "loadFromFile(): Version too old: " << version;
return VersionTooOld;
}
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement)
break;
@ -669,7 +696,10 @@ bool CardDatabase::loadFromFile(const QString &fileName, bool tokens)
}
}
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)
@ -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 (settingsCache->getPicDownloadHq()) {
QHashIterator<QString, CardInfo *> cardIterator(cardHash);
while (cardIterator.hasNext())
cardIterator.next().value()->clearPixmapCacheMiss();
}
}
LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens)
{
LoadStatus tempLoadStatus = NotLoaded;
if (!path.isEmpty())
tempLoadSuccess = loadFromFile(path, tokens);
if (tempLoadSuccess) {
tempLoadStatus = loadFromFile(path, tokens);
if (tempLoadStatus == Ok) {
SetList allSets;
QHashIterator<QString, CardSet *> setsIterator(setHash);
while (setsIterator.hasNext())
@ -731,14 +771,17 @@ bool CardDatabase::loadCardDatabase(const QString &path, bool tokens)
allSets.sortByKey();
for (int i = 0; i < allSets.size(); ++i)
allSets[i]->setSortKey(i);
emit cardListChanged();
}
if (!tokens)
loadSuccess = tempLoadSuccess;
return tempLoadSuccess;
if (!tokens) {
loadStatus = tempLoadStatus;
qDebug() << "loadCardDatabase(): Status = " << loadStatus;
}
return tempLoadStatus;
}
void CardDatabase::loadCardDatabase()

View file

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

View file

@ -1,5 +1,6 @@
#include <QStringList>
#include <QFile>
#include <QDebug>
#include "deck_loader.h"
#include "decklist.h"
@ -44,18 +45,28 @@ bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt)
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
bool result = false;
switch (fmt) {
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) {
lastFileName = fileName;
lastFileFormat = fmt;
emit deckLoaded();
}
qDebug() << "Deck was loaded -" << result;
return result;
}
@ -66,7 +77,7 @@ bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId)
lastFileName = QString();
lastFileFormat = CockatriceFormat;
lastRemoteDeckId = remoteDeckId;
emit deckLoaded();
}
return result;

View file

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

View file

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

View file

@ -18,6 +18,7 @@
#include <QInputDialog>
#include <QSpinBox>
#include <QDialogButtonBox>
#include <QDebug>
#include "carddatabase.h"
#include "dlg_settings.h"
#include "main.h"
@ -27,7 +28,7 @@ GeneralSettingsPage::GeneralSettingsPage()
{
languageLabel = new QLabel;
languageBox = new QComboBox;
QString setLanguage = settingsCache->getLang();
QStringList qmFiles = findQmFiles();
for (int i = 0; i < qmFiles.size(); i++) {
@ -36,27 +37,32 @@ GeneralSettingsPage::GeneralSettingsPage()
if ((qmFiles[i] == setLanguage) || (setLanguage.isEmpty() && langName == tr("English")))
languageBox->setCurrentIndex(i);
}
picDownloadCheckBox = new QCheckBox;
picDownloadCheckBox->setChecked(settingsCache->getPicDownload());
picDownloadHqCheckBox = new QCheckBox;
picDownloadHqCheckBox->setChecked(settingsCache->getPicDownloadHq());
connect(languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int)));
connect(picDownloadCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownload(int)));
connect(picDownloadHqCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownloadHq(int)));
QGridLayout *personalGrid = new QGridLayout;
personalGrid->addWidget(languageLabel, 0, 0);
personalGrid->addWidget(languageBox, 0, 1);
personalGrid->addWidget(picDownloadCheckBox, 1, 0, 1, 2);
personalGrid->addWidget(picDownloadHqCheckBox, 2, 0, 1, 2);
personalGroupBox = new QGroupBox;
personalGroupBox->setLayout(personalGrid);
deckPathLabel = new QLabel;
deckPathEdit = new QLineEdit(settingsCache->getDeckPath());
deckPathEdit->setReadOnly(true);
QPushButton *deckPathButton = new QPushButton("...");
connect(deckPathButton, SIGNAL(clicked()), this, SLOT(deckPathButtonClicked()));
replaysPathLabel = new QLabel;
replaysPathEdit = new QLineEdit(settingsCache->getReplaysPath());
replaysPathEdit->setReadOnly(true);
@ -183,6 +189,7 @@ void GeneralSettingsPage::retranslateUi()
personalGroupBox->setTitle(tr("Personal settings"));
languageLabel->setText(tr("Language:"));
picDownloadCheckBox->setText(tr("Download card pictures on the fly"));
picDownloadHqCheckBox->setText(tr("Download high-quality card pictures"));
pathsGroupBox->setTitle(tr("Paths"));
deckPathLabel->setText(tr("Decks directory:"));
replaysPathLabel->setText(tr("Replays directory:"));
@ -698,17 +705,65 @@ void DlgSettings::changeEvent(QEvent *event)
void DlgSettings::closeEvent(QCloseEvent *event)
{
if (!db->getLoadSuccess())
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) {
bool showLoadError = true;
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();
return;
}
if (!QDir(settingsCache->getDeckPath()).exists() || settingsCache->getDeckPath().isEmpty())
// TODO: Prompt to create it
if (QMessageBox::critical(this, tr("Error"), tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
event->ignore();
return;
}
if (!QDir(settingsCache->getPicsPath()).exists() || settingsCache->getPicsPath().isEmpty())
// TODO: Prompt to create it
if (QMessageBox::critical(this, tr("Error"), tr("The path to your card pictures directory is invalid. Would you like to go back and set the correct path?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
event->ignore();
return;

View file

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

View file

@ -74,10 +74,18 @@ void installNewTranslator()
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[])
{
QApplication app(argc, argv);
if (app.arguments().contains("--debug-output"))
qInstallMsgHandler(myMessageOutput);
#ifdef Q_OS_MAC
@ -97,7 +105,7 @@ int main(int argc, char *argv[])
QCoreApplication::setOrganizationName("Cockatrice");
QCoreApplication::setOrganizationDomain("cockatrice.de");
QCoreApplication::setApplicationName("Cockatrice");
if (translationPath.isEmpty()) {
#ifdef Q_OS_MAC
QDir translationsDir = baseDir;
@ -108,7 +116,7 @@ int main(int argc, char *argv[])
translationPath = app.applicationDirPath() + "/translations";
#endif
}
rng = new RNG_SFMT;
settingsCache = new SettingsCache;
db = new CardDatabase;
@ -116,7 +124,7 @@ int main(int argc, char *argv[])
qtTranslator = new QTranslator;
translator = new QTranslator;
installNewTranslator();
qsrand(QDateTime::currentDateTime().toTime_t());
bool startMainProgram = true;
@ -142,30 +150,30 @@ int main(int argc, char *argv[])
QDir().mkpath(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.show();
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");
soundEngine = new SoundEngine;
qDebug("main(): SoundEngine constructor finished");
MainWindow ui;
qDebug("main(): MainWindow constructor finished");
QIcon icon(":/resources/appicon.svg");
ui.setWindowIcon(icon);
ui.show();
qDebug("main(): ui.show() finished");
app.exec();
}
qDebug("Event loop finished, terminating...");
delete db;
delete settingsCache;
@ -173,6 +181,6 @@ int main(int argc, char *argv[])
PingPixmapGenerator::clear();
CountryPixmapGenerator::clear();
UserLevelPixmapGenerator::clear();
return 0;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -132,6 +132,8 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
deckView = new QTreeView();
deckView->setModel(deckModel);
deckView->setUniformRowHeights(true);
deckView->setSortingEnabled(true);
deckView->sortByColumn(1, Qt::AscendingOrder);
deckView->header()->setResizeMode(QHeaderView::ResizeToContents);
deckView->installEventFilter(&deckViewKeySignals);
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()));
if (!settingsCache->getPriceTagFeature())
aUpdatePrices->setVisible(false);
connect(settingsCache, SIGNAL(priceTagFeatureChanged(int)), this, SLOT(setPriceTagFeatureEnabled(int)));
QToolBar *deckToolBar = new QToolBar;
deckToolBar->setOrientation(Qt::Vertical);
@ -180,7 +183,7 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
deckToolbarLayout->addStretch();
deckToolbarLayout->addWidget(deckToolBar);
deckToolbarLayout->addStretch();
QVBoxLayout *rightFrame = new QVBoxLayout;
rightFrame->addLayout(grid);
rightFrame->addWidget(deckView, 10);
@ -191,7 +194,7 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
mainLayout->addLayout(middleFrame);
mainLayout->addLayout(rightFrame);
setLayout(mainLayout);
aNewDeck = new QAction(QString(), this);
aNewDeck->setShortcuts(QKeySequence::New);
connect(aNewDeck, SIGNAL(triggered()), this, SLOT(actNewDeck()));
@ -633,6 +636,11 @@ void TabDeckEditor::actDecrement()
offsetCountAtIndex(currentIndex, -1);
}
void TabDeckEditor::setPriceTagFeatureEnabled(int enabled)
{
aUpdatePrices->setVisible(enabled);
}
void TabDeckEditor::actUpdatePrices()
{
aUpdatePrices->setDisabled(true);
@ -655,10 +663,10 @@ void TabDeckEditor::setDeck(DeckLoader *_deck)
nameEdit->setText(deckModel->getDeckList()->getName());
commentsEdit->setText(deckModel->getDeckList()->getComments());
updateHash();
deckModel->sort(1);
deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
deckView->expandAll();
setModified(false);
db->cacheCardPixmaps(deckModel->getDeckList()->getCardList());
deckView->expandAll();
setModified(false);

View file

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

View file

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

View file

@ -2,6 +2,7 @@
#include <QTextStream>
#include <QVariant>
#include <QCryptographicHash>
#include <QDebug>
#include "decklist.h"
SideboardPlan::SideboardPlan(const QString &_name, const QList<MoveCard_ToZone> &_moveList)
@ -104,6 +105,13 @@ QString InnerDecklistNode::visibleNameFromName(const QString &_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
{
return visibleNameFromName(name);
@ -163,21 +171,95 @@ float InnerDecklistNode::recursivePrice(bool countTotalCards) 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);
if (other2)
return (getName() > other->getName());
else
if (other2) {
int n1 = recursiveCount(true);
int n2 = other2->recursiveCount(true);
return (n1 != n2) ? (n1 > n2) : compareName(other);
} else {
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
{
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);
if (other2)
return (getName() > other->getName());
else
if (other2) {
int n1 = getNumber();
int n2 = other2->getNumber();
return (n1 != n2) ? (n1 > n2) : compareName(other);
} else {
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 {
@ -360,6 +442,11 @@ void DeckList::write(QXmlStreamWriter *xml)
bool DeckList::loadFromXml(QXmlStreamReader *xml)
{
if (xml->error()) {
qDebug() << "Error loading deck from xml: " << xml->errorString();
return false;
}
cleanList();
while (!xml->atEnd()) {
xml->readNext();
@ -374,6 +461,10 @@ bool DeckList::loadFromXml(QXmlStreamReader *xml)
}
}
updateDeckHash();
if (xml->error()) {
qDebug() << "Error loading deck from xml: " << xml->errorString();
return false;
}
return true;
}
@ -396,8 +487,7 @@ QString DeckList::writeToString_Native()
bool DeckList::loadFromFile_Native(QIODevice *device)
{
QXmlStreamReader xml(device);
loadFromXml(&xml);
return true;
return loadFromXml(&xml);
}
bool DeckList::saveToFile_Native(QIODevice *device)
@ -415,7 +505,7 @@ bool DeckList::saveToFile_Native(QIODevice *device)
bool DeckList::loadFromStream_Plain(QTextStream &in)
{
cleanList();
InnerDecklistNode *main = 0, *side = 0;
bool inSideboard = false;
@ -449,7 +539,7 @@ bool DeckList::loadFromStream_Plain(QTextStream &in)
line.remove(rx);
rx.setPattern("\\(.*\\)");
line.remove(rx);
//Filter out post card name editions
//Filter out post card name editions
rx.setPattern("\\|.*$");
line.remove(rx);
line = line.simplified();
@ -462,10 +552,10 @@ bool DeckList::loadFromStream_Plain(QTextStream &in)
continue;
QString cardName = line.mid(i + 1);
// Common differences between cockatrice's card names
// and what's commonly used in decklists
// Common differences between cockatrice's card names
// and what's commonly used in decklists
rx.setPattern("");
cardName.replace(rx, "'");
cardName.replace(rx, "'");
rx.setPattern("Æ");
cardName.replace(rx, "AE");
rx.setPattern("^Aether");

View file

@ -29,24 +29,28 @@ public:
SideboardPlan(const QString &_name = QString(), const QList<MoveCard_ToZone> &_moveList = QList<MoveCard_ToZone>());
bool readElement(QXmlStreamReader *xml);
void write(QXmlStreamWriter *xml);
QString getName() const { return name; }
const QList<MoveCard_ToZone> &getMoveList() const { return moveList; }
void setMoveList(const QList<MoveCard_ToZone> &_moveList);
};
enum DeckSortMethod { ByNumber, ByName, ByPrice };
class AbstractDecklistNode {
protected:
InnerDecklistNode *parent;
DeckSortMethod sortMethod;
public:
AbstractDecklistNode(InnerDecklistNode *_parent = 0);
virtual ~AbstractDecklistNode() { }
virtual void setSortMethod(DeckSortMethod method) { sortMethod = method; }
virtual QString getName() const = 0;
InnerDecklistNode *getParent() const { return parent; }
int depth() const;
virtual int height() const = 0;
virtual bool compare(AbstractDecklistNode *other) const = 0;
virtual bool readElement(QXmlStreamReader *xml) = 0;
virtual void writeElement(QXmlStreamWriter *xml) = 0;
};
@ -59,6 +63,7 @@ public:
InnerDecklistNode(const QString &_name = QString(), InnerDecklistNode *_parent = 0) : AbstractDecklistNode(_parent), name(_name) { }
InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent = 0);
virtual ~InnerDecklistNode();
void setSortMethod(DeckSortMethod method);
QString getName() const { return name; }
void setName(const QString &_name) { name = _name; }
static QString visibleNameFromName(const QString &_name);
@ -69,8 +74,11 @@ public:
int recursiveCount(bool countTotalCards = false) const;
float recursivePrice(bool countTotalCards = false) 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);
bool readElement(QXmlStreamReader *xml);
void writeElement(QXmlStreamWriter *xml);
};
@ -87,7 +95,10 @@ public:
float getTotalPrice() const { return getNumber() * getPrice(); }
int height() const { return 0; }
bool compare(AbstractDecklistNode *other) const;
bool compareNumber(AbstractDecklistNode *other) const;
bool compareName(AbstractDecklistNode *other) const;
bool compareTotalPrice(AbstractDecklistNode *other) const;
bool readElement(QXmlStreamReader *xml);
void writeElement(QXmlStreamWriter *xml);
};

View file

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

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}
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.
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,
@ -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}
\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[OS X] \shellcmd{brew install qt cmake protobuf}
\end{description}
\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:\\
@ -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}.
\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}
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:
@ -706,6 +704,8 @@ USE AT YOUR OWN RISK.
\begin{enumerate}
\item Open a command shell and install the prerequisites
\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{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}

View file

@ -91,7 +91,7 @@ SectionEnd
Section "Start menu item" SecStartMenu
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\Usermanual.lnk" "$INSTDIR\Usermanual.pdf"
SectionEnd

View file

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

View file

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

View file

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

View file

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

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

@ -0,0 +1,398 @@
#include <QtGui>
#include <QGridLayout>
#include <QDesktopServices>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include "oraclewizard.h"
#include "oracleimporter.h"
#define ALLSETS_URL "http://mtgjson.com/json/AllSets.json"
OracleWizard::OracleWizard(QWidget *parent)
: QWizard(parent)
{
settings = new QSettings(this);
importer = new OracleImporter(QDesktopServices::storageLocation(QDesktopServices::DataLocation), this);
addPage(new IntroPage);
addPage(new LoadSetsPage);
addPage(new ChooseSetsPage);
addPage(new SaveSetsPage);
setWindowTitle(tr("Oracle Importer"));
QWizard::setButtonText(QWizard::FinishButton, tr("Save"));
}
void OracleWizard::accept()
{
QDialog::accept();
}
void OracleWizard::enableButtons()
{
button(QWizard::NextButton)->setDisabled(false);
button(QWizard::BackButton)->setDisabled(false);
}
void OracleWizard::disableButtons()
{
button(QWizard::NextButton)->setDisabled(true);
button(QWizard::BackButton)->setDisabled(true);
}
IntroPage::IntroPage(QWidget *parent)
: OracleWizardPage(parent)
{
setTitle(tr("Introduction"));
label = new QLabel(tr("This wizard will import the list of sets and cards "
"that will be used by Cockatrice. You will need to "
"specify an url or a filename that will be used as a "
"source, and then choose the wanted sets from the list "
"of the available ones."),
this);
label->setWordWrap(true);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(label);
setLayout(layout);
}
LoadSetsPage::LoadSetsPage(QWidget *parent)
: OracleWizardPage(parent), nam(0)
{
setTitle(tr("Source selection"));
setSubTitle(tr("Please specify a source for the list of sets and cards. "
"You can specify an url address that will be download or "
"use an existing file from your computer."));
urlRadioButton = new QRadioButton(tr("Download url:"), this);
fileRadioButton = new QRadioButton(tr("Local file:"), this);
urlLineEdit = new QLineEdit(this);
fileLineEdit = new QLineEdit(this);
progressLabel = new QLabel(this);
progressBar = new QProgressBar(this);
urlRadioButton->setChecked(true);
fileButton = new QPushButton(tr("Choose file..."), this);
connect(fileButton, SIGNAL(clicked()), this, SLOT(actLoadSetsFile()));
QGridLayout *layout = new QGridLayout(this);
layout->addWidget(urlRadioButton, 0, 0);
layout->addWidget(urlLineEdit, 0, 1);
layout->addWidget(fileRadioButton, 1, 0);
layout->addWidget(fileLineEdit, 1, 1);
layout->addWidget(fileButton, 2, 1, Qt::AlignRight);
layout->addWidget(progressLabel, 3, 0);
layout->addWidget(progressBar, 3, 1);
connect(&watcher, SIGNAL(finished()), this, SLOT(importFinished()));
setLayout(layout);
}
void LoadSetsPage::initializePage()
{
urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString());
progressLabel->hide();
progressBar->hide();
}
void LoadSetsPage::actLoadSetsFile()
{
QFileDialog dialog(this, tr("Load sets file"));
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter("Sets JSON file (*.json)");
if(!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text()))
dialog.selectFile(fileLineEdit->text());
if (!dialog.exec())
return;
fileLineEdit->setText(dialog.selectedFiles().at(0));
}
bool LoadSetsPage::validatePage()
{
// once the import is finished, we call next(); skip validation
if(wizard()->importer->getSets().count() > 0)
return true;
// else, try to import sets
if(urlRadioButton->isChecked())
{
QUrl url = QUrl::fromUserInput(urlLineEdit->text());
if(!url.isValid())
{
QMessageBox::critical(this, tr("Error"), tr("The provided url is not valid."));
return false;
}
progressLabel->setText(tr("Downloading (0MB)"));
// show an infinite progressbar
progressBar->setMaximum(0);
progressBar->setMinimum(0);
progressBar->setValue(0);
progressLabel->show();
progressBar->show();
wizard()->disableButtons();
setEnabled(false);
if(!nam)
nam = new QNetworkAccessManager(this);
QNetworkReply *reply = nam->get(QNetworkRequest(url));
connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSetsFile()));
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(actDownloadProgressSetsFile(qint64, qint64)));
} else if(fileRadioButton->isChecked()) {
QFile setsFile(fileLineEdit->text());
if(!setsFile.exists())
{
QMessageBox::critical(this, tr("Error"), tr("Please choose a file."));
return false;
}
if (!setsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::critical(0, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text()));
return false;
}
wizard()->disableButtons();
setEnabled(false);
readSetsFromByteArray(setsFile.readAll());
}
return false;
}
void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total)
{
if(total > 0 && progressBar->maximum()==0)
{
progressBar->setMaximum(total);
progressBar->setValue(received);
}
progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / 1048576));
}
void LoadSetsPage::actDownloadFinishedSetsFile()
{
progressLabel->hide();
progressBar->hide();
// check for a reply
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
QNetworkReply::NetworkError errorCode = reply->error();
if (errorCode != QNetworkReply::NoError) {
QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString()));
wizard()->enableButtons();
setEnabled(true);
reply->deleteLater();
return;
}
// save allsets.json url, but only if the user customized it and download was successfull
if(urlLineEdit->text() != QString(ALLSETS_URL))
wizard()->settings->setValue("allsetsurl", urlLineEdit->text());
else
wizard()->settings->remove("allsetsurl");
readSetsFromByteArray(reply->readAll());
reply->deleteLater();
}
void LoadSetsPage::readSetsFromByteArray(QByteArray data)
{
// show an infinite progressbar
progressBar->setMaximum(0);
progressBar->setMinimum(0);
progressBar->setValue(0);
progressLabel->setText(tr("Parsing file"));
progressLabel->show();
progressBar->show();
// Start the computation.
future = QtConcurrent::run(wizard()->importer, &OracleImporter::readSetsFromByteArray, data);
watcher.setFuture(future);
}
void LoadSetsPage::importFinished()
{
wizard()->enableButtons();
setEnabled(true);
progressLabel->hide();
progressBar->hide();
if(watcher.future().result())
{
wizard()->next();
} else {
QMessageBox::critical(this, tr("Error"), tr("The file was retrieved successfully, but it does not contain any sets data."));
}
}
ChooseSetsPage::ChooseSetsPage(QWidget *parent)
: OracleWizardPage(parent)
{
setTitle(tr("Sets selection"));
setSubTitle(tr("The following sets has been found in the source file. "
"Please mark the sets that will be imported."));
checkBoxLayout = new QVBoxLayout;
QWidget *checkboxFrame = new QWidget(this);
checkboxFrame->setLayout(checkBoxLayout);
QScrollArea *checkboxArea = new QScrollArea(this);
checkboxArea->setWidget(checkboxFrame);
checkboxArea->setWidgetResizable(true);
checkAllButton = new QPushButton(tr("&Check all"));
connect(checkAllButton, SIGNAL(clicked()), this, SLOT(actCheckAll()));
uncheckAllButton = new QPushButton(tr("&Uncheck all"));
connect(uncheckAllButton, SIGNAL(clicked()), this, SLOT(actUncheckAll()));
QGridLayout *layout = new QGridLayout(this);
layout->addWidget(checkboxArea, 0, 0, 1, 2);
layout->addWidget(checkAllButton, 1, 0);
layout->addWidget(uncheckAllButton, 1, 1);
setLayout(layout);
}
void ChooseSetsPage::initializePage()
{
// populate checkbox list
for (int i = 0; i < checkBoxList.size(); ++i)
delete checkBoxList[i];
checkBoxList.clear();
QList<SetToDownload> &sets = wizard()->importer->getSets();
for (int i = 0; i < sets.size(); ++i) {
QCheckBox *checkBox = new QCheckBox(sets[i].getLongName());
checkBox->setChecked(sets[i].getImport());
connect(checkBox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxChanged(int)));
checkBoxLayout->addWidget(checkBox);
checkBoxList << checkBox;
}
}
void ChooseSetsPage::checkBoxChanged(int state)
{
QCheckBox *checkBox = qobject_cast<QCheckBox *>(sender());
QList<SetToDownload> &sets = wizard()->importer->getSets();
for (int i = 0; i < sets.size(); ++i)
if (sets[i].getLongName() == checkBox->text()) {
sets[i].setImport(state);
break;
}
}
void ChooseSetsPage::actCheckAll()
{
for (int i = 0; i < checkBoxList.size(); ++i)
checkBoxList[i]->setChecked(true);
}
void ChooseSetsPage::actUncheckAll()
{
for (int i = 0; i < checkBoxList.size(); ++i)
checkBoxList[i]->setChecked(false);
}
bool ChooseSetsPage::validatePage()
{
for (int i = 0; i < checkBoxList.size(); ++i)
{
if(checkBoxList[i]->isChecked())
return true;
}
QMessageBox::critical(this, tr("Error"), tr("Please mark at least one set."));
return false;
}
SaveSetsPage::SaveSetsPage(QWidget *parent)
: OracleWizardPage(parent)
{
setTitle(tr("Sets imported"));
setSubTitle(tr("The following sets has been imported. "
"Press \"Save\" to save the imported cards to the Cockatrice database."));
defaultPathCheckBox = new QCheckBox(this);
defaultPathCheckBox->setText(tr("Save to the default path (recommended)"));
defaultPathCheckBox->setChecked(true);
messageLog = new QTextEdit(this);
messageLog->setReadOnly(true);
QGridLayout *layout = new QGridLayout(this);
layout->addWidget(defaultPathCheckBox, 0, 0);
layout->addWidget(messageLog, 1, 0);
setLayout(layout);
}
void SaveSetsPage::cleanupPage()
{
disconnect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), 0, 0);
}
void SaveSetsPage::initializePage()
{
messageLog->clear();
connect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), this, SLOT(updateTotalProgress(int, int, const QString &)));
if (!wizard()->importer->startImport())
QMessageBox::critical(this, tr("Error"), tr("No set has been imported."));
}
void SaveSetsPage::updateTotalProgress(int cardsImported, int setIndex, const QString &setName)
{
if (setName.isEmpty()) {
messageLog->append("<b>" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + "</b>");
} else {
messageLog->append(tr("%1: %2 cards imported").arg(setName).arg(cardsImported));
}
messageLog->verticalScrollBar()->setValue(messageLog->verticalScrollBar()->maximum());
}
bool SaveSetsPage::validatePage()
{
bool ok = false;
const QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
QDir dir(dataDir);
if (!dir.exists())
dir.mkpath(dataDir);
QString savePath = dataDir + "/cards.xml";
do {
QString fileName;
if (savePath.isEmpty() || !defaultPathCheckBox->isChecked())
fileName = QFileDialog::getSaveFileName(this, tr("Save card database"), dataDir + "/cards.xml", tr("XML card database (*.xml)"));
else {
fileName = savePath;
savePath.clear();
}
if (fileName.isEmpty()) {
return false;
}
if (wizard()->importer->saveToFile(fileName))
ok = true;
else
QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to the desired location."));
} while (!ok);
return true;
}

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

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

View file

@ -1,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