Merge branch 'master' into master_feature_prices

This commit is contained in:
Fabio Bas 2014-07-23 21:37:37 +02:00
commit b0ee2e6d4b
21 changed files with 272 additions and 120 deletions

View file

@ -33,8 +33,9 @@ To compile:
The following flags can be passed to `cmake`: The following flags can be passed to `cmake`:
- `-DWITH_SERVER=1` build the server - `-DWITH_SERVER=1` Build the server
- `-DWITHOUT_CLIENT=1` do not build the client - `-DWITHOUT_CLIENT=1` Do not build the client
- `-DWITH_QT4=1` Force compilation to use Qt4 instead of Qt5.
# Running # Running

View file

@ -1,3 +1 @@
!define NSIS_PROJECT_NAME "@PROJECT_NAME@" !define NSIS_SOURCE_PATH "@PROJECT_SOURCE_DIR@"
!define NSIS_SOURCE_PATH "@PROJECT_SOURCE_DIR@"
!define NSIS_BINARY_PATH "@PROJECT_BINARY_DIR@"

View file

@ -2,10 +2,11 @@
!include "MUI2.nsh" !include "MUI2.nsh"
!include "FileFunc.nsh" !include "FileFunc.nsh"
Name "${NSIS_PROJECT_NAME}" Name "@CPACK_PACKAGE_NAME@"
OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@"
SetCompressor /SOLID lzma SetCompressor /SOLID lzma
InstallDir "$PROGRAMFILES\Cockatrice" InstallDir "$PROGRAMFILES\Cockatrice"
!define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@"
!define MUI_ABORTWARNING !define MUI_ABORTWARNING
!define MUI_WELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp" !define MUI_WELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp"
@ -35,7 +36,8 @@ Section "Application" SecApplication
SetShellVarContext all SetShellVarContext all
SetOutPath "$INSTDIR" SetOutPath "$INSTDIR"
File /r "${NSIS_BINARY_PATH}\Release\*.*" @CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS@
@CPACK_NSIS_FULL_INSTALL@
WriteUninstaller "$INSTDIR\uninstall.exe" WriteUninstaller "$INSTDIR\uninstall.exe"
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2

View file

@ -2,6 +2,7 @@ find_package(Git)
if(GIT_FOUND) if(GIT_FOUND)
execute_process( execute_process(
COMMAND ${GIT_EXECUTABLE} describe --long --always COMMAND ${GIT_EXECUTABLE} describe --long --always
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE res_var RESULT_VARIABLE res_var
OUTPUT_VARIABLE GIT_COM_ID OUTPUT_VARIABLE GIT_COM_ID
) )

View file

@ -434,6 +434,22 @@ int CardInfo::getPreferredMuId()
return muIds[getPreferredSet()->getShortName()]; return muIds[getPreferredSet()->getShortName()];
} }
QString CardInfo::simplifyName(const QString &name) {
QString simpleName(name);
// So Aetherling would work, but not Ætherling since 'Æ' would get replaced
// with nothing.
simpleName.replace("æ", "ae");
simpleName.replace("Æ", "AE");
// Replace Jötun Grunt with Jotun Grunt.
simpleName = simpleName.normalized(QString::NormalizationForm_KD);
simpleName.remove(QRegExp("[^a-zA-Z0-9 ]"));
simpleName = simpleName.toLower();
return simpleName;
}
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info)
{ {
xml.writeStartElement("card"); xml.writeStartElement("card");
@ -507,55 +523,55 @@ CardDatabase::~CardDatabase()
void CardDatabase::clear() void CardDatabase::clear()
{ {
QHashIterator<QString, CardSet *> setIt(setHash); QHashIterator<QString, CardSet *> setIt(sets);
while (setIt.hasNext()) { while (setIt.hasNext()) {
setIt.next(); setIt.next();
delete setIt.value(); delete setIt.value();
} }
setHash.clear(); sets.clear();
QHashIterator<QString, CardInfo *> i(cardHash); QHashIterator<QString, CardInfo *> i(cards);
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
delete i.value(); delete i.value();
} }
cardHash.clear(); cards.clear();
// The pointers themselves were already deleted, so we don't delete them
// again.
simpleNameCards.clear();
} }
void CardDatabase::addCard(CardInfo *card) void CardDatabase::addCard(CardInfo *card)
{ {
cardHash.insert(card->getName(), card); cards.insert(card->getName(), card);
simpleNameCards.insert(CardInfo::simplifyName(card->getName()), card);
emit cardAdded(card); emit cardAdded(card);
} }
void CardDatabase::removeCard(CardInfo *card) void CardDatabase::removeCard(CardInfo *card)
{ {
cardHash.remove(card->getName()); cards.remove(card->getName());
simpleNameCards.remove(CardInfo::simplifyName(card->getName()));
emit cardRemoved(card); emit cardRemoved(card);
} }
CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) {
{ return getCardFromMap(cards, cardName, createIfNotFound);
if (cardName.isEmpty()) }
return noCard;
else if (cardHash.contains(cardName)) CardInfo *CardDatabase::getCardBySimpleName(const QString &cardName, bool createIfNotFound) {
return cardHash.value(cardName); QString simpleName = CardInfo::simplifyName(cardName);
else if (createIfNotFound) { return getCardFromMap(simpleNameCards, simpleName, createIfNotFound);
CardInfo *newCard = new CardInfo(this, cardName, true);
newCard->addToSet(getSet("TK"));
cardHash.insert(cardName, newCard);
return newCard;
} else
return 0;
} }
CardSet *CardDatabase::getSet(const QString &setName) CardSet *CardDatabase::getSet(const QString &setName)
{ {
if (setHash.contains(setName)) if (sets.contains(setName))
return setHash.value(setName); return sets.value(setName);
else { else {
CardSet *newSet = new CardSet(setName); CardSet *newSet = new CardSet(setName);
setHash.insert(setName, newSet); sets.insert(setName, newSet);
return newSet; return newSet;
} }
} }
@ -563,7 +579,7 @@ CardSet *CardDatabase::getSet(const QString &setName)
SetList CardDatabase::getSetList() const SetList CardDatabase::getSetList() const
{ {
SetList result; SetList result;
QHashIterator<QString, CardSet *> i(setHash); QHashIterator<QString, CardSet *> i(sets);
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
result << i.value(); result << i.value();
@ -573,7 +589,9 @@ SetList CardDatabase::getSetList() const
void CardDatabase::clearPixmapCache() void CardDatabase::clearPixmapCache()
{ {
QHashIterator<QString, CardInfo *> i(cardHash); // This also clears the cards in simpleNameCards since they point to the
// same object.
QHashIterator<QString, CardInfo *> i(cards);
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
i.value()->clearPixmapCache(); i.value()->clearPixmapCache();
@ -597,7 +615,7 @@ void CardDatabase::loadSetsFromXml(QXmlStreamReader &xml)
else if (xml.name() == "longname") else if (xml.name() == "longname")
longName = xml.readElementText(); longName = xml.readElementText();
} }
setHash.insert(shortName, new CardSet(shortName, longName)); sets.insert(shortName, new CardSet(shortName, longName));
} }
} }
} }
@ -647,46 +665,32 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
else if (xml.name() == "token") else if (xml.name() == "token")
isToken = xml.readElementText().toInt(); isToken = xml.readElementText().toInt();
} }
cardHash.insert(name, new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, muids)); addCard(new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, muids));
} }
} }
} }
LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) CardInfo *CardDatabase::getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound) {
if (cardName.isEmpty())
return noCard;
else if (cardMap.contains(cardName))
return cardMap.value(cardName);
else if (createIfNotFound) {
CardInfo *newCard = new CardInfo(this, cardName, true);
newCard->addToSet(getSet("TK"));
cardMap.insert(cardName, newCard);
return newCard;
} else
return 0;
}
LoadStatus CardDatabase::loadFromFile(const QString &fileName)
{ {
QFile file(fileName); QFile file(fileName);
file.open(QIODevice::ReadOnly); file.open(QIODevice::ReadOnly);
if (!file.isOpen()) if (!file.isOpen())
return FileError; return FileError;
if (tokens) {
QMutableHashIterator<QString, CardInfo *> i(cardHash);
while (i.hasNext()) {
i.next();
if (i.value()->getIsToken()) {
delete i.value();
i.remove();
}
}
} else {
QHashIterator<QString, CardSet *> setIt(setHash);
while (setIt.hasNext()) {
setIt.next();
delete setIt.value();
}
setHash.clear();
QMutableHashIterator<QString, CardInfo *> i(cardHash);
while (i.hasNext()) {
i.next();
if (!i.value()->getIsToken()) {
delete i.value();
i.remove();
}
}
cardHash.clear();
}
QXmlStreamReader xml(&file); QXmlStreamReader xml(&file);
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) { if (xml.readNext() == QXmlStreamReader::StartElement) {
@ -707,9 +711,9 @@ LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens)
} }
} }
} }
qDebug() << cardHash.size() << "cards in" << setHash.size() << "sets loaded"; qDebug() << cards.size() << "cards in" << sets.size() << "sets loaded";
if (cardHash.isEmpty()) return NoCards; if (cards.isEmpty()) return NoCards;
return Ok; return Ok;
} }
@ -728,18 +732,17 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens)
if (!tokens) { if (!tokens) {
xml.writeStartElement("sets"); xml.writeStartElement("sets");
QHashIterator<QString, CardSet *> setIterator(setHash); QHashIterator<QString, CardSet *> setIterator(sets);
while (setIterator.hasNext()) while (setIterator.hasNext())
xml << setIterator.next().value(); xml << setIterator.next().value();
xml.writeEndElement(); // sets xml.writeEndElement(); // sets
} }
xml.writeStartElement("cards"); xml.writeStartElement("cards");
QHashIterator<QString, CardInfo *> cardIterator(cardHash); QHashIterator<QString, CardInfo *> cardIterator(cards);
while (cardIterator.hasNext()) { while (cardIterator.hasNext()) {
CardInfo *card = cardIterator.next().value(); CardInfo *card = cardIterator.next().value();
if (card->getIsToken() == tokens) xml << card;
xml << card;
} }
xml.writeEndElement(); // cards xml.writeEndElement(); // cards
@ -753,7 +756,7 @@ void CardDatabase::picDownloadChanged()
{ {
pictureLoader->setPicDownload(settingsCache->getPicDownload()); pictureLoader->setPicDownload(settingsCache->getPicDownload());
if (settingsCache->getPicDownload()) { if (settingsCache->getPicDownload()) {
QHashIterator<QString, CardInfo *> cardIterator(cardHash); QHashIterator<QString, CardInfo *> cardIterator(cards);
while (cardIterator.hasNext()) while (cardIterator.hasNext())
cardIterator.next().value()->clearPixmapCacheMiss(); cardIterator.next().value()->clearPixmapCacheMiss();
} }
@ -763,7 +766,7 @@ void CardDatabase::picDownloadHqChanged()
{ {
pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq()); pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq());
if (settingsCache->getPicDownloadHq()) { if (settingsCache->getPicDownloadHq()) {
QHashIterator<QString, CardInfo *> cardIterator(cardHash); QHashIterator<QString, CardInfo *> cardIterator(cards);
while (cardIterator.hasNext()) while (cardIterator.hasNext())
cardIterator.next().value()->clearPixmapCacheMiss(); cardIterator.next().value()->clearPixmapCacheMiss();
} }
@ -773,11 +776,11 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens)
{ {
LoadStatus tempLoadStatus = NotLoaded; LoadStatus tempLoadStatus = NotLoaded;
if (!path.isEmpty()) if (!path.isEmpty())
tempLoadStatus = loadFromFile(path, tokens); tempLoadStatus = loadFromFile(path);
if (tempLoadStatus == Ok) { if (tempLoadStatus == Ok) {
SetList allSets; SetList allSets;
QHashIterator<QString, CardSet *> setsIterator(setHash); QHashIterator<QString, CardSet *> setsIterator(sets);
while (setsIterator.hasNext()) while (setsIterator.hasNext())
allSets.append(setsIterator.next().value()); allSets.append(setsIterator.next().value());
allSets.sortByKey(); allSets.sortByKey();
@ -809,7 +812,7 @@ void CardDatabase::loadTokenDatabase()
QStringList CardDatabase::getAllColors() const QStringList CardDatabase::getAllColors() const
{ {
QSet<QString> colors; QSet<QString> colors;
QHashIterator<QString, CardInfo *> cardIterator(cardHash); QHashIterator<QString, CardInfo *> cardIterator(cards);
while (cardIterator.hasNext()) { while (cardIterator.hasNext()) {
const QStringList &cardColors = cardIterator.next().value()->getColors(); const QStringList &cardColors = cardIterator.next().value()->getColors();
if (cardColors.isEmpty()) if (cardColors.isEmpty())
@ -824,7 +827,7 @@ QStringList CardDatabase::getAllColors() const
QStringList CardDatabase::getAllMainCardTypes() const QStringList CardDatabase::getAllMainCardTypes() const
{ {
QSet<QString> types; QSet<QString> types;
QHashIterator<QString, CardInfo *> cardIterator(cardHash); QHashIterator<QString, CardInfo *> cardIterator(cards);
while (cardIterator.hasNext()) while (cardIterator.hasNext())
types.insert(cardIterator.next().value()->getMainCardType()); types.insert(cardIterator.next().value()->getMainCardType());
return types.toList(); return types.toList();

View file

@ -95,6 +95,13 @@ private:
CardDatabase *db; CardDatabase *db;
QString name; QString name;
/*
* The name without punctuation or capitalization, for better card tag name
* recognition.
*/
QString simpleName;
bool isToken; bool isToken;
SetList sets; SetList sets;
QString manacost; QString manacost;
@ -153,6 +160,12 @@ public:
void imageLoaded(const QImage &image); void imageLoaded(const QImage &image);
CardSet *getPreferredSet(); CardSet *getPreferredSet();
int getPreferredMuId(); int getPreferredMuId();
/**
* Simplify a name to have no punctuation and lowercase all letters, for
* less strict name-matching.
*/
static QString simplifyName(const QString &name);
public slots: public slots:
void updatePixmapCache(); void updatePixmapCache();
signals: signals:
@ -162,11 +175,27 @@ signals:
enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards }; enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards };
typedef QHash<QString, CardInfo *> CardNameMap;
typedef QHash<QString, CardSet *> SetNameMap;
class CardDatabase : public QObject { class CardDatabase : public QObject {
Q_OBJECT Q_OBJECT
protected: protected:
QHash<QString, CardInfo *> cardHash; /*
QHash<QString, CardSet *> setHash; * The cards, indexed by name.
*/
CardNameMap cards;
/**
* The cards, indexed by their simple name.
*/
CardNameMap simpleNameCards;
/*
* The sets, indexed by short name.
*/
SetNameMap sets;
CardInfo *noCard; CardInfo *noCard;
QThread *pictureLoaderThread; QThread *pictureLoaderThread;
@ -176,6 +205,8 @@ private:
static const int versionNeeded; static const int versionNeeded;
void loadCardsFromXml(QXmlStreamReader &xml); void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml); void loadSetsFromXml(QXmlStreamReader &xml);
CardInfo *getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound);
public: public:
CardDatabase(QObject *parent = 0); CardDatabase(QObject *parent = 0);
~CardDatabase(); ~CardDatabase();
@ -183,10 +214,17 @@ public:
void addCard(CardInfo *card); void addCard(CardInfo *card);
void removeCard(CardInfo *card); void removeCard(CardInfo *card);
CardInfo *getCard(const QString &cardName = QString(), bool createIfNotFound = true); CardInfo *getCard(const QString &cardName = QString(), bool createIfNotFound = true);
/*
* Get a card by its simple name. The name will be simplified in this
* function, so you don't need to simplify it beforehand.
*/
CardInfo *getCardBySimpleName(const QString &cardName = QString(), bool createIfNotFound = true);
CardSet *getSet(const QString &setName); CardSet *getSet(const QString &setName);
QList<CardInfo *> getCardList() const { return cardHash.values(); } QList<CardInfo *> getCardList() const { return cards.values(); }
SetList getSetList() const; SetList getSetList() const;
LoadStatus loadFromFile(const QString &fileName, bool tokens = false); LoadStatus loadFromFile(const QString &fileName);
bool saveToFile(const QString &fileName, bool tokens = false); bool saveToFile(const QString &fileName, bool tokens = false);
QStringList getAllColors() const; QStringList getAllColors() const;
QStringList getAllMainCardTypes() const; QStringList getAllMainCardTypes() const;

View file

@ -81,7 +81,7 @@ CardInfoWidget::CardInfoWidget(ResizeMode _mode, const QString &cardName, QWidge
} else } else
setFixedWidth(250); setFixedWidth(250);
setCard(db->getCard(cardName)); setCard(getCard(cardName));
setMinimized(settingsCache->getCardInfoMinimized()); setMinimized(settingsCache->getCardInfoMinimized());
} }
@ -166,7 +166,7 @@ void CardInfoWidget::setCard(CardInfo *card)
void CardInfoWidget::setCard(const QString &cardName) void CardInfoWidget::setCard(const QString &cardName)
{ {
setCard(db->getCard(cardName)); setCard(getCard(cardName));
} }
void CardInfoWidget::setCard(AbstractCardItem *card) void CardInfoWidget::setCard(AbstractCardItem *card)
@ -176,7 +176,11 @@ void CardInfoWidget::setCard(AbstractCardItem *card)
void CardInfoWidget::clear() void CardInfoWidget::clear()
{ {
setCard(db->getCard()); setCard(getCard());
}
CardInfo *CardInfoWidget::getCard(const QString &cardName) {
return db->getCardBySimpleName(cardName);
} }
void CardInfoWidget::updatePixmap() void CardInfoWidget::updatePixmap()
@ -188,7 +192,7 @@ void CardInfoWidget::updatePixmap()
if (resizedPixmap) if (resizedPixmap)
cardPicture->setPixmap(*resizedPixmap); cardPicture->setPixmap(*resizedPixmap);
else else
cardPicture->setPixmap(*(db->getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio)))); cardPicture->setPixmap(*(getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio))));
} }
void CardInfoWidget::retranslateUi() void CardInfoWidget::retranslateUi()

View file

@ -42,6 +42,11 @@ private:
CardInfo *info; CardInfo *info;
void setMinimized(int _minimized); void setMinimized(int _minimized);
/*
* Wrapper around db->getCardBySimpleName.
*/
CardInfo *getCard(const QString &cardName = QString());
public: public:
CardInfoWidget(ResizeMode _mode, const QString &cardName = QString(), QWidget *parent = 0, Qt::WindowFlags f = 0); CardInfoWidget(ResizeMode _mode, const QString &cardName = QString(), QWidget *parent = 0, Qt::WindowFlags f = 0);
void retranslateUi(); void retranslateUi();

View file

@ -11,8 +11,10 @@
#include <QUrlQuery> #include <QUrlQuery>
#endif #endif
DeckStatsInterface::DeckStatsInterface(QObject *parent) DeckStatsInterface::DeckStatsInterface(
: QObject(parent) CardDatabase &_cardDatabase,
QObject *parent
) : QObject(parent), cardDatabase(_cardDatabase)
{ {
manager = new QNetworkAccessManager(this); manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *))); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *)));
@ -43,24 +45,23 @@ void DeckStatsInterface::queryFinished(QNetworkReply *reply)
deleteLater(); deleteLater();
} }
void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data)
{
DeckList deckWithoutTokens;
copyDeckWithoutTokens(*deck, deckWithoutTokens);
#if QT_VERSION < 0x050000 #if QT_VERSION < 0x050000
void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data)
{
QUrl params; QUrl params;
params.addQueryItem("deck", deck->writeToString_Plain()); params.addQueryItem("deck", deckWithoutTokens.writeToString_Plain());
data->append(params.encodedQuery()); data->append(params.encodedQuery());
}
#else #else
void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data)
{
QUrl params; QUrl params;
QUrlQuery urlQuery; QUrlQuery urlQuery;
urlQuery.addQueryItem("deck", deck->writeToString_Plain()); urlQuery.addQueryItem("deck", deckWithoutTokens.writeToString_Plain());
params.setQuery(urlQuery); params.setQuery(urlQuery);
data->append(params.query(QUrl::EncodeReserved)); data->append(params.query(QUrl::EncodeReserved));
}
#endif #endif
}
void DeckStatsInterface::analyzeDeck(DeckList *deck) void DeckStatsInterface::analyzeDeck(DeckList *deck)
{ {
@ -72,3 +73,30 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck)
manager->post(request, data); manager->post(request, data);
} }
struct CopyIfNotAToken {
CardDatabase &cardDatabase;
DeckList &destination;
CopyIfNotAToken(
CardDatabase &_cardDatabase,
DeckList &_destination
) : cardDatabase(_cardDatabase), destination(_destination) {};
void operator()(
const InnerDecklistNode *node,
const DecklistCardNode *card
) const {
if (!cardDatabase.getCard(card->getName())->getIsToken()) {
destination.addCard(card->getName(), node->getName());
}
}
};
void DeckStatsInterface::copyDeckWithoutTokens(
const DeckList &source,
DeckList &destination
) {
CopyIfNotAToken copyIfNotAToken(cardDatabase, destination);
source.forEachCard(copyIfNotAToken);
}

View file

@ -1,6 +1,8 @@
#ifndef DECKSTATS_INTERFACE_H #ifndef DECKSTATS_INTERFACE_H
#define DECKSTATS_INTERFACE_H #define DECKSTATS_INTERFACE_H
#include "carddatabase.h"
#include "decklist.h"
#include <QObject> #include <QObject>
class QByteArray; class QByteArray;
@ -12,11 +14,21 @@ class DeckStatsInterface : public QObject {
Q_OBJECT Q_OBJECT
private: private:
QNetworkAccessManager *manager; QNetworkAccessManager *manager;
CardDatabase &cardDatabase;
/**
* Deckstats doesn't recognize token cards, and instead tries to find the
* closest non-token card instead. So we construct a new deck which has no
* tokens.
*/
void copyDeckWithoutTokens(const DeckList &source, DeckList& destination);
private slots: private slots:
void queryFinished(QNetworkReply *reply); void queryFinished(QNetworkReply *reply);
void getAnalyzeRequestData(DeckList *deck, QByteArray *data); void getAnalyzeRequestData(DeckList *deck, QByteArray *data);
public: public:
DeckStatsInterface(QObject *parent = 0); DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = 0);
void analyzeDeck(DeckList *deck); void analyzeDeck(DeckList *deck);
}; };

View file

@ -31,6 +31,7 @@ public:
virtual QString getTabText() const = 0; virtual QString getTabText() const = 0;
virtual void retranslateUi() = 0; virtual void retranslateUi() = 0;
virtual void closeRequest() { } virtual void closeRequest() { }
virtual void tabActivated() { }
}; };
#endif #endif

View file

@ -512,7 +512,10 @@ void TabDeckEditor::actPrintDeck()
void TabDeckEditor::actAnalyzeDeck() void TabDeckEditor::actAnalyzeDeck()
{ {
DeckStatsInterface *interface = new DeckStatsInterface(this); // it deletes itself when done DeckStatsInterface *interface = new DeckStatsInterface(
*databaseModel->getDatabase(),
this
); // it deletes itself when done
interface->analyzeDeck(deckModel->getDeckList()); interface->analyzeDeck(deckModel->getDeckList());
} }

View file

@ -34,10 +34,6 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor, AbstractClient *_client, c
retranslateUi(); retranslateUi();
setLayout(vbox); setLayout(vbox);
setFocusProxy(sayEdit);
chatView->setFocusProxy(sayEdit);
sayEdit->setFocus();
} }
TabMessage::~TabMessage() TabMessage::~TabMessage()
@ -53,6 +49,12 @@ void TabMessage::retranslateUi()
aLeave->setText(tr("&Leave")); aLeave->setText(tr("&Leave"));
} }
void TabMessage::tabActivated()
{
if(!sayEdit->hasFocus())
sayEdit->setFocus();
}
QString TabMessage::getUserName() const QString TabMessage::getUserName() const
{ {
return QString::fromStdString(otherUserInfo->name()); return QString::fromStdString(otherUserInfo->name());

View file

@ -34,6 +34,7 @@ public:
~TabMessage(); ~TabMessage();
void retranslateUi(); void retranslateUi();
void closeRequest(); void closeRequest();
void tabActivated();
QString getUserName() const; QString getUserName() const;
QString getTabText() const; QString getTabText() const;

View file

@ -9,7 +9,6 @@
#include <QLabel> #include <QLabel>
#include <QToolButton> #include <QToolButton>
#include <QSplitter> #include <QSplitter>
#include <QTimer>
#include "tab_supervisor.h" #include "tab_supervisor.h"
#include "tab_room.h" #include "tab_room.h"
#include "tab_userlists.h" #include "tab_userlists.h"
@ -98,10 +97,6 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI
const int gameListSize = info.game_list_size(); const int gameListSize = info.game_list_size();
for (int i = 0; i < gameListSize; ++i) for (int i = 0; i < gameListSize; ++i)
gameSelector->processGameInfo(info.game_list(i)); gameSelector->processGameInfo(info.game_list(i));
setFocusProxy(sayEdit);
chatView->setFocusProxy(sayEdit);
QTimer::singleShot(0, sayEdit, SLOT(setFocus()));
} }
TabRoom::~TabRoom() TabRoom::~TabRoom()
@ -126,6 +121,12 @@ void TabRoom::closeRequest()
actLeaveRoom(); actLeaveRoom();
} }
void TabRoom::tabActivated()
{
if(!sayEdit->hasFocus())
sayEdit->setFocus();
}
QString TabRoom::sanitizeHtml(QString dirty) const QString TabRoom::sanitizeHtml(QString dirty) const
{ {
return dirty return dirty

View file

@ -64,6 +64,7 @@ public:
~TabRoom(); ~TabRoom();
void retranslateUi(); void retranslateUi();
void closeRequest(); void closeRequest();
void tabActivated();
void processRoomEvent(const RoomEvent &event); void processRoomEvent(const RoomEvent &event);
int getRoomId() const { return roomId; } int getRoomId() const { return roomId; }
const QMap<int, QString> &getGameTypes() const { return gameTypes; } const QMap<int, QString> &getGameTypes() const { return gameTypes; }

View file

@ -479,6 +479,7 @@ void TabSupervisor::updateCurrent(int index)
tab->setContentsChanged(false); tab->setContentsChanged(false);
} }
emit setMenu(static_cast<Tab *>(widget(index))->getTabMenus()); emit setMenu(static_cast<Tab *>(widget(index))->getTabMenus());
tab->tabActivated();
} else } else
emit setMenu(); emit setMenu();
} }

View file

@ -570,9 +570,20 @@ bool DeckList::loadFromStream_Plain(QTextStream &in)
cardName.replace(rx, "AE"); cardName.replace(rx, "AE");
rx.setPattern("^Aether"); rx.setPattern("^Aether");
cardName.replace(rx, "AEther"); cardName.replace(rx, "AEther");
rx.setPattern("\\s*[&|/]{1,2}\\s*"); rx.setPattern("\\s*[|/]{1,2}\\s*");
cardName.replace(rx, " // "); cardName.replace(rx, " // ");
// Replace only if the ampersand is preceded by a non-capital letter,
// as would happen with acronyms. So 'Fire & Ice' is replaced but not
// 'R&D' or 'R & D'.
//
// Qt regexes don't support lookbehind so we capture and replace
// instead.
rx.setPattern("([^A-Z])\\s*&\\s*");
if (rx.indexIn(cardName) != -1) {
cardName.replace(rx, QString("%1 // ").arg(rx.cap(1)));
}
++okRows; ++okRows;
new DecklistCardNode(cardName, number, zone); new DecklistCardNode(cardName, number, zone);
} }
@ -586,16 +597,30 @@ bool DeckList::loadFromFile_Plain(QIODevice *device)
return loadFromStream_Plain(in); return loadFromStream_Plain(in);
} }
struct WriteToStream {
QTextStream &stream;
WriteToStream(QTextStream &_stream) : stream(_stream) {}
void operator()(
const InnerDecklistNode *node,
const DecklistCardNode *card
) {
if (node->getName() == "side") {
stream << "SB: ";
}
stream << QString("%1 %2\n").arg(
card->getNumber()
).arg(
card->getName()
);
}
};
bool DeckList::saveToStream_Plain(QTextStream &out) bool DeckList::saveToStream_Plain(QTextStream &out)
{ {
// Support for this is only possible if the internal structure doesn't get more complicated. WriteToStream writeToStream(out);
for (int i = 0; i < root->size(); i++) { forEachCard(writeToStream);
InnerDecklistNode *node = dynamic_cast<InnerDecklistNode *>(root->at(i));
for (int j = 0; j < node->size(); j++) {
DecklistCardNode *card = dynamic_cast<DecklistCardNode *>(node->at(j));
out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName());
}
}
return true; return true;
} }

View file

@ -117,6 +117,7 @@ public:
QString getName() const { return name; } QString getName() const { return name; }
void setName(const QString &_name) { name = _name; } void setName(const QString &_name) { name = _name; }
float getPrice() const { return price; } float getPrice() const { return price; }
void setPrice(const float _price) { price = _price; } void setPrice(const float _price) { price = _price; }
}; };
@ -169,6 +170,28 @@ public:
InnerDecklistNode *getRoot() const { return root; } InnerDecklistNode *getRoot() const { return root; }
DecklistCardNode *addCard(const QString &cardName, const QString &zoneName); DecklistCardNode *addCard(const QString &cardName, const QString &zoneName);
bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = 0); bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = 0);
/**
* Calls a given function object for each card in the deck. It must
* take a InnerDecklistNode* as its first argument and a
* DecklistCardNode* as its second.
*/
template <typename Callback>
void forEachCard(Callback &callback) const {
// Support for this is only possible if the internal structure
// doesn't get more complicated.
for (int i = 0; i < root->size(); i++) {
const InnerDecklistNode *node =
dynamic_cast<InnerDecklistNode *>(root->at(i));
for (int j = 0; j < node->size(); j++) {
const DecklistCardNode *card =
dynamic_cast<DecklistCardNode *>(
node->at(j)
);
callback(node, card);
}
}
}
}; };
#endif #endif

View file

@ -80,8 +80,8 @@ CardInfo *OracleImporter::addCard(const QString &setName,
cardCost.remove(QChar('}')); cardCost.remove(QChar('}'));
CardInfo *card; CardInfo *card;
if (cardHash.contains(cardName)) { if (cards.contains(cardName)) {
card = cardHash.value(cardName); card = cards.value(cardName);
if (splitCard && !card->getText().contains(fullCardText)) if (splitCard && !card->getText().contains(fullCardText))
card->setText(card->getText() + "\n---\n" + fullCardText); card->setText(card->getText() + "\n---\n" + fullCardText);
} else { } else {
@ -121,7 +121,7 @@ CardInfo *OracleImporter::addCard(const QString &setName,
tableRow = 2; tableRow = 2;
card->setTableRow(tableRow); card->setTableRow(tableRow);
cardHash.insert(cardName, card); cards.insert(cardName, card);
} }
card->setMuId(setName, cardId); card->setMuId(setName, cardId);
@ -141,6 +141,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data)
QString cardText; QString cardText;
int cardId; int cardId;
int cardLoyalty; int cardLoyalty;
bool cardIsToken = false;
QMap<int, QVariantMap> splitCards; QMap<int, QVariantMap> splitCards;
while (it.hasNext()) { while (it.hasNext()) {
@ -201,6 +202,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data)
cardText = map.contains("text") ? map.value("text").toString() : QString(""); cardText = map.contains("text") ? map.value("text").toString() : QString("");
cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0;
cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0; cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0;
cardIsToken = map.value("layout") == "token";
// Distinguish Vanguard cards from regular cards of the same name. // Distinguish Vanguard cards from regular cards of the same name.
if (map.value("layout") == "vanguard") { if (map.value("layout") == "vanguard") {
@ -208,7 +210,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data)
} }
} }
CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n")); CardInfo *card = addCard(set->getShortName(), cardName, cardIsToken, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n"));
if (!set->contains(card)) { if (!set->contains(card)) {
card->addToSet(set); card->addToSet(set);
@ -234,8 +236,8 @@ int OracleImporter::startImport()
continue; continue;
CardSet *set = new CardSet(curSet->getShortName(), curSet->getLongName()); CardSet *set = new CardSet(curSet->getShortName(), curSet->getLongName());
if (!setHash.contains(set->getShortName())) if (!sets.contains(set->getShortName()))
setHash.insert(set->getShortName(), set); sets.insert(set->getShortName(), set);
int setCards = importTextSpoiler(set, curSet->getCards()); int setCards = importTextSpoiler(set, curSet->getCards());

View file

@ -2,7 +2,7 @@
if [[ $TRAVIS_OS_NAME == "osx" ]] ; then if [[ $TRAVIS_OS_NAME == "osx" ]] ; then
brew update brew update
brew install qt cmake protobuf libgcrypt brew install qt protobuf libgcrypt
else else
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -y qtmobility-dev libprotobuf-dev protobuf-compiler libqt4-dev sudo apt-get install -y qtmobility-dev libprotobuf-dev protobuf-compiler libqt4-dev