From 7cbe4101728123e6deb2cf1795fa2babb004ee53 Mon Sep 17 00:00:00 2001 From: arxanas Date: Mon, 30 Jun 2014 01:18:05 -0400 Subject: [PATCH 01/11] Fix #45: don't send tokens to deckstats. --- cockatrice/src/carddatabase.cpp | 35 ++--------------------- cockatrice/src/carddatabase.h | 2 +- cockatrice/src/deckstats_interface.cpp | 39 ++++++++++++++++++++++++-- cockatrice/src/deckstats_interface.h | 14 ++++++++- cockatrice/src/tab_deck_editor.cpp | 5 +++- common/decklist.cpp | 30 ++++++++++++++------ common/decklist.h | 23 +++++++++++++++ oracle/src/oracleimporter.cpp | 4 ++- 8 files changed, 105 insertions(+), 47 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index eff101f9..c57ac792 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -652,41 +652,13 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml) } } -LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) +LoadStatus CardDatabase::loadFromFile(const QString &fileName) { QFile file(fileName); file.open(QIODevice::ReadOnly); if (!file.isOpen()) return FileError; - if (tokens) { - QMutableHashIterator i(cardHash); - while (i.hasNext()) { - i.next(); - if (i.value()->getIsToken()) { - delete i.value(); - i.remove(); - } - } - } else { - QHashIterator setIt(setHash); - while (setIt.hasNext()) { - setIt.next(); - delete setIt.value(); - } - setHash.clear(); - - QMutableHashIterator i(cardHash); - while (i.hasNext()) { - i.next(); - if (!i.value()->getIsToken()) { - delete i.value(); - i.remove(); - } - } - cardHash.clear(); - } - QXmlStreamReader xml(&file); while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::StartElement) { @@ -738,8 +710,7 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens) QHashIterator cardIterator(cardHash); while (cardIterator.hasNext()) { CardInfo *card = cardIterator.next().value(); - if (card->getIsToken() == tokens) - xml << card; + xml << card; } xml.writeEndElement(); // cards @@ -773,7 +744,7 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens) { LoadStatus tempLoadStatus = NotLoaded; if (!path.isEmpty()) - tempLoadStatus = loadFromFile(path, tokens); + tempLoadStatus = loadFromFile(path); if (tempLoadStatus == Ok) { SetList allSets; diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 09edeff4..be82099d 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -186,7 +186,7 @@ public: CardSet *getSet(const QString &setName); QList getCardList() const { return cardHash.values(); } SetList getSetList() const; - LoadStatus loadFromFile(const QString &fileName, bool tokens = false); + LoadStatus loadFromFile(const QString &fileName); bool saveToFile(const QString &fileName, bool tokens = false); QStringList getAllColors() const; QStringList getAllMainCardTypes() const; diff --git a/cockatrice/src/deckstats_interface.cpp b/cockatrice/src/deckstats_interface.cpp index eae4caae..d3c96d8b 100644 --- a/cockatrice/src/deckstats_interface.cpp +++ b/cockatrice/src/deckstats_interface.cpp @@ -7,8 +7,10 @@ #include #include -DeckStatsInterface::DeckStatsInterface(QObject *parent) - : QObject(parent) +DeckStatsInterface::DeckStatsInterface( + CardDatabase &_cardDatabase, + QObject *parent +) : QObject(parent), cardDatabase(_cardDatabase) { manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *))); @@ -42,7 +44,11 @@ void DeckStatsInterface::queryFinished(QNetworkReply *reply) void DeckStatsInterface::analyzeDeck(DeckList *deck) { QUrl params; - params.addQueryItem("deck", deck->writeToString_Plain()); + + DeckList deckWithoutTokens; + copyDeckWithoutTokens(*deck, deckWithoutTokens); + + params.addQueryItem("deck", deckWithoutTokens.writeToString_Plain()); QByteArray data; data.append(params.encodedQuery()); @@ -51,3 +57,30 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck) 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); +} diff --git a/cockatrice/src/deckstats_interface.h b/cockatrice/src/deckstats_interface.h index e8454dd5..928d5220 100644 --- a/cockatrice/src/deckstats_interface.h +++ b/cockatrice/src/deckstats_interface.h @@ -1,6 +1,8 @@ #ifndef DECKSTATS_INTERFACE_H #define DECKSTATS_INTERFACE_H +#include "carddatabase.h" +#include "decklist.h" #include class QNetworkAccessManager; @@ -11,10 +13,20 @@ class DeckStatsInterface : public QObject { Q_OBJECT private: 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: void queryFinished(QNetworkReply *reply); public: - DeckStatsInterface(QObject *parent = 0); + DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = 0); void analyzeDeck(DeckList *deck); }; diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 0c1d44e1..96fb9a90 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -508,7 +508,10 @@ void TabDeckEditor::actPrintDeck() 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()); } diff --git a/common/decklist.cpp b/common/decklist.cpp index 90b5cb7e..1dd9acf3 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -579,16 +579,30 @@ bool DeckList::loadFromFile_Plain(QIODevice *device) 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) { - // Support for this is only possible if the internal structure doesn't get more complicated. - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName()); - } - } + WriteToStream writeToStream(out); + forEachCard(writeToStream); return true; } diff --git a/common/decklist.h b/common/decklist.h index 2eed52fc..4dfd2bbd 100644 --- a/common/decklist.h +++ b/common/decklist.h @@ -117,6 +117,7 @@ public: QString getName() const { return name; } void setName(const QString &_name) { name = _name; } float getPrice() const { return price; } + void setPrice(const float _price) { price = _price; } }; @@ -169,6 +170,28 @@ public: InnerDecklistNode *getRoot() const { return root; } DecklistCardNode *addCard(const QString &cardName, const QString &zoneName); 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 + 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(root->at(i)); + for (int j = 0; j < node->size(); j++) { + const DecklistCardNode *card = + dynamic_cast( + node->at(j) + ); + callback(node, card); + } + } + } }; #endif diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 70972d92..0da35abe 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -137,6 +137,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) QString cardText; int cardId; int cardLoyalty; + bool cardIsToken = false; QMap splitCards; while (it.hasNext()) { @@ -197,9 +198,10 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) 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; + cardIsToken = map.value("layout") == "token"; } - 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)) { card->addToSet(set); From aead0843af9207a170282cd21a84df9c9870e6a6 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 6 Jul 2014 14:27:35 +0200 Subject: [PATCH 02/11] Room and Message tabs: focus input line when convenient; Fix #180 As suggest by Daenyth in #185 --- cockatrice/src/tab.h | 1 + cockatrice/src/tab_message.cpp | 10 ++++++---- cockatrice/src/tab_message.h | 1 + cockatrice/src/tab_room.cpp | 11 ++++++----- cockatrice/src/tab_room.h | 1 + cockatrice/src/tab_supervisor.cpp | 1 + 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cockatrice/src/tab.h b/cockatrice/src/tab.h index 65473121..1b8b7704 100644 --- a/cockatrice/src/tab.h +++ b/cockatrice/src/tab.h @@ -31,6 +31,7 @@ public: virtual QString getTabText() const = 0; virtual void retranslateUi() = 0; virtual void closeRequest() { } + virtual void tabActivated() { } }; #endif diff --git a/cockatrice/src/tab_message.cpp b/cockatrice/src/tab_message.cpp index eec46bb9..6369f2a6 100644 --- a/cockatrice/src/tab_message.cpp +++ b/cockatrice/src/tab_message.cpp @@ -34,10 +34,6 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor, AbstractClient *_client, c retranslateUi(); setLayout(vbox); - - setFocusProxy(sayEdit); - chatView->setFocusProxy(sayEdit); - sayEdit->setFocus(); } TabMessage::~TabMessage() @@ -53,6 +49,12 @@ void TabMessage::retranslateUi() aLeave->setText(tr("&Leave")); } +void TabMessage::tabActivated() +{ + if(!sayEdit->hasFocus()) + sayEdit->setFocus(); +} + QString TabMessage::getUserName() const { return QString::fromStdString(otherUserInfo->name()); diff --git a/cockatrice/src/tab_message.h b/cockatrice/src/tab_message.h index b8f98959..80263975 100644 --- a/cockatrice/src/tab_message.h +++ b/cockatrice/src/tab_message.h @@ -34,6 +34,7 @@ public: ~TabMessage(); void retranslateUi(); void closeRequest(); + void tabActivated(); QString getUserName() const; QString getTabText() const; diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index a7514e79..a4bb9ce0 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include "tab_supervisor.h" #include "tab_room.h" #include "tab_userlists.h" @@ -98,10 +97,6 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI const int gameListSize = info.game_list_size(); for (int i = 0; i < gameListSize; ++i) gameSelector->processGameInfo(info.game_list(i)); - - setFocusProxy(sayEdit); - chatView->setFocusProxy(sayEdit); - QTimer::singleShot(0, sayEdit, SLOT(setFocus())); } TabRoom::~TabRoom() @@ -126,6 +121,12 @@ void TabRoom::closeRequest() actLeaveRoom(); } +void TabRoom::tabActivated() +{ + if(!sayEdit->hasFocus()) + sayEdit->setFocus(); +} + QString TabRoom::sanitizeHtml(QString dirty) const { return dirty diff --git a/cockatrice/src/tab_room.h b/cockatrice/src/tab_room.h index ada3aca8..857f37a2 100644 --- a/cockatrice/src/tab_room.h +++ b/cockatrice/src/tab_room.h @@ -64,6 +64,7 @@ public: ~TabRoom(); void retranslateUi(); void closeRequest(); + void tabActivated(); void processRoomEvent(const RoomEvent &event); int getRoomId() const { return roomId; } const QMap &getGameTypes() const { return gameTypes; } diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index e85efc6a..dda918a0 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -479,6 +479,7 @@ void TabSupervisor::updateCurrent(int index) tab->setContentsChanged(false); } emit setMenu(static_cast(widget(index))->getTabMenus()); + tab->tabActivated(); } else emit setMenu(); } From 810029ce15cf1854b28fd99df150303ccdefe659 Mon Sep 17 00:00:00 2001 From: arxanas Date: Sun, 6 Jul 2014 20:04:09 -0400 Subject: [PATCH 03/11] Fix #117: Card tags now match disregarding case and punctuation. --- cockatrice/src/carddatabase.cpp | 110 ++++++++++++++++++------------ cockatrice/src/carddatabase.h | 44 +++++++++++- cockatrice/src/cardinfowidget.cpp | 12 ++-- cockatrice/src/cardinfowidget.h | 5 ++ oracle/src/oracleimporter.cpp | 10 +-- 5 files changed, 127 insertions(+), 54 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index eff101f9..c55148aa 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -434,6 +434,13 @@ int CardInfo::getPreferredMuId() return muIds[getPreferredSet()->getShortName()]; } +QString CardInfo::simplifyName(const QString &name) { + QString simpleName(name); + simpleName.remove(QRegExp("[^a-zA-Z0-9 ]")); + simpleName = simpleName.toLower(); + return simpleName; +} + static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) { xml.writeStartElement("card"); @@ -507,55 +514,57 @@ CardDatabase::~CardDatabase() void CardDatabase::clear() { - QHashIterator setIt(setHash); + QHashIterator setIt(sets); while (setIt.hasNext()) { setIt.next(); delete setIt.value(); } - setHash.clear(); + sets.clear(); - QHashIterator i(cardHash); + QHashIterator i(cards); while (i.hasNext()) { i.next(); 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) { - cardHash.insert(card->getName(), card); + cards.insert(card->getName(), card); + simpleNameCards.insert(CardInfo::simplifyName(card->getName()), card); emit cardAdded(card); } void CardDatabase::removeCard(CardInfo *card) { - cardHash.remove(card->getName()); + cards.remove(card->getName()); + simpleNameCards.remove(CardInfo::simplifyName(card->getName())); emit cardRemoved(card); } -CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) -{ - if (cardName.isEmpty()) - return noCard; - else if (cardHash.contains(cardName)) - return cardHash.value(cardName); - else if (createIfNotFound) { - CardInfo *newCard = new CardInfo(this, cardName, true); - newCard->addToSet(getSet("TK")); - cardHash.insert(cardName, newCard); - return newCard; - } else - return 0; +CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) { + return getCardFromMap(cards, cardName, createIfNotFound); +} + +CardInfo *CardDatabase::getCardBySimpleName(const QString &cardName, bool createIfNotFound) { + QString simpleName = CardInfo::simplifyName(cardName); + qDebug() << "Getting card by name " << simpleName << "\n"; + qDebug() << "Cards available: " << simpleNameCards.size() << "\n"; + return getCardFromMap(simpleNameCards, simpleName, createIfNotFound); } CardSet *CardDatabase::getSet(const QString &setName) { - if (setHash.contains(setName)) - return setHash.value(setName); + if (sets.contains(setName)) + return sets.value(setName); else { CardSet *newSet = new CardSet(setName); - setHash.insert(setName, newSet); + sets.insert(setName, newSet); return newSet; } } @@ -563,7 +572,7 @@ CardSet *CardDatabase::getSet(const QString &setName) SetList CardDatabase::getSetList() const { SetList result; - QHashIterator i(setHash); + QHashIterator i(sets); while (i.hasNext()) { i.next(); result << i.value(); @@ -573,7 +582,9 @@ SetList CardDatabase::getSetList() const void CardDatabase::clearPixmapCache() { - QHashIterator i(cardHash); + // This also clears the cards in simpleNameCards since they point to the + // same object. + QHashIterator i(cards); while (i.hasNext()) { i.next(); i.value()->clearPixmapCache(); @@ -597,7 +608,7 @@ void CardDatabase::loadSetsFromXml(QXmlStreamReader &xml) else if (xml.name() == "longname") longName = xml.readElementText(); } - setHash.insert(shortName, new CardSet(shortName, longName)); + sets.insert(shortName, new CardSet(shortName, longName)); } } } @@ -647,11 +658,25 @@ 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, muids)); + addCard(new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, muids)); } } } +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, bool tokens) { QFile file(fileName); @@ -660,31 +685,32 @@ LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) return FileError; if (tokens) { - QMutableHashIterator i(cardHash); + QMutableHashIterator i(cards); while (i.hasNext()) { i.next(); if (i.value()->getIsToken()) { + removeCard(i.value()); delete i.value(); - i.remove(); } } } else { - QHashIterator setIt(setHash); + QHashIterator setIt(sets); while (setIt.hasNext()) { setIt.next(); delete setIt.value(); } - setHash.clear(); + sets.clear(); - QMutableHashIterator i(cardHash); + QMutableHashIterator i(cards); while (i.hasNext()) { i.next(); if (!i.value()->getIsToken()) { + removeCard(i.value()); delete i.value(); - i.remove(); } } - cardHash.clear(); + cards.clear(); + simpleNameCards.clear(); } QXmlStreamReader xml(&file); @@ -707,9 +733,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; } @@ -728,14 +754,14 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens) if (!tokens) { xml.writeStartElement("sets"); - QHashIterator setIterator(setHash); + QHashIterator setIterator(sets); while (setIterator.hasNext()) xml << setIterator.next().value(); xml.writeEndElement(); // sets } xml.writeStartElement("cards"); - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) { CardInfo *card = cardIterator.next().value(); if (card->getIsToken() == tokens) @@ -753,7 +779,7 @@ void CardDatabase::picDownloadChanged() { pictureLoader->setPicDownload(settingsCache->getPicDownload()); if (settingsCache->getPicDownload()) { - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) cardIterator.next().value()->clearPixmapCacheMiss(); } @@ -763,7 +789,7 @@ void CardDatabase::picDownloadHqChanged() { pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq()); if (settingsCache->getPicDownloadHq()) { - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) cardIterator.next().value()->clearPixmapCacheMiss(); } @@ -777,7 +803,7 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens) if (tempLoadStatus == Ok) { SetList allSets; - QHashIterator setsIterator(setHash); + QHashIterator setsIterator(sets); while (setsIterator.hasNext()) allSets.append(setsIterator.next().value()); allSets.sortByKey(); @@ -809,7 +835,7 @@ void CardDatabase::loadTokenDatabase() QStringList CardDatabase::getAllColors() const { QSet colors; - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) { const QStringList &cardColors = cardIterator.next().value()->getColors(); if (cardColors.isEmpty()) @@ -824,7 +850,7 @@ QStringList CardDatabase::getAllColors() const QStringList CardDatabase::getAllMainCardTypes() const { QSet types; - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) types.insert(cardIterator.next().value()->getMainCardType()); return types.toList(); diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 09edeff4..50a4d1b7 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -95,6 +95,13 @@ private: CardDatabase *db; QString name; + + /* + * The name without punctuation or capitalization, for better card tag name + * recognition. + */ + QString simpleName; + bool isToken; SetList sets; QString manacost; @@ -153,6 +160,12 @@ public: void imageLoaded(const QImage &image); CardSet *getPreferredSet(); 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: void updatePixmapCache(); signals: @@ -162,11 +175,27 @@ signals: enum LoadStatus { Ok, VersionTooOld, Invalid, NotLoaded, FileError, NoCards }; +typedef QHash CardNameMap; +typedef QHash SetNameMap; + class CardDatabase : public QObject { Q_OBJECT protected: - QHash cardHash; - QHash 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; QThread *pictureLoaderThread; @@ -176,6 +205,8 @@ private: static const int versionNeeded; void loadCardsFromXml(QXmlStreamReader &xml); void loadSetsFromXml(QXmlStreamReader &xml); + + CardInfo *getCardFromMap(CardNameMap &cardMap, const QString &cardName, bool createIfNotFound); public: CardDatabase(QObject *parent = 0); ~CardDatabase(); @@ -183,8 +214,15 @@ public: void addCard(CardInfo *card); void removeCard(CardInfo *card); 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); - QList getCardList() const { return cardHash.values(); } + QList getCardList() const { return cards.values(); } SetList getSetList() const; LoadStatus loadFromFile(const QString &fileName, bool tokens = false); bool saveToFile(const QString &fileName, bool tokens = false); diff --git a/cockatrice/src/cardinfowidget.cpp b/cockatrice/src/cardinfowidget.cpp index 6b5e995a..21b00a13 100644 --- a/cockatrice/src/cardinfowidget.cpp +++ b/cockatrice/src/cardinfowidget.cpp @@ -81,7 +81,7 @@ CardInfoWidget::CardInfoWidget(ResizeMode _mode, const QString &cardName, QWidge } else setFixedWidth(250); - setCard(db->getCard(cardName)); + setCard(getCard(cardName)); setMinimized(settingsCache->getCardInfoMinimized()); } @@ -166,7 +166,7 @@ void CardInfoWidget::setCard(CardInfo *card) void CardInfoWidget::setCard(const QString &cardName) { - setCard(db->getCard(cardName)); + setCard(getCard(cardName)); } void CardInfoWidget::setCard(AbstractCardItem *card) @@ -176,7 +176,11 @@ void CardInfoWidget::setCard(AbstractCardItem *card) void CardInfoWidget::clear() { - setCard(db->getCard()); + setCard(getCard()); +} + +CardInfo *CardInfoWidget::getCard(const QString &cardName) { + return db->getCardBySimpleName(cardName); } void CardInfoWidget::updatePixmap() @@ -188,7 +192,7 @@ void CardInfoWidget::updatePixmap() if (resizedPixmap) cardPicture->setPixmap(*resizedPixmap); else - cardPicture->setPixmap(*(db->getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio)))); + cardPicture->setPixmap(*(getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio)))); } void CardInfoWidget::retranslateUi() diff --git a/cockatrice/src/cardinfowidget.h b/cockatrice/src/cardinfowidget.h index ecfd6eca..b9988a7a 100644 --- a/cockatrice/src/cardinfowidget.h +++ b/cockatrice/src/cardinfowidget.h @@ -42,6 +42,11 @@ private: CardInfo *info; void setMinimized(int _minimized); + /* + * Wrapper around db->getCardBySimpleName. + */ + CardInfo *getCard(const QString &cardName = QString()); + public: CardInfoWidget(ResizeMode _mode, const QString &cardName = QString(), QWidget *parent = 0, Qt::WindowFlags f = 0); void retranslateUi(); diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 70972d92..cdc51095 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -76,8 +76,8 @@ CardInfo *OracleImporter::addCard(const QString &setName, cardCost.remove(QChar('}')); CardInfo *card; - if (cardHash.contains(cardName)) { - card = cardHash.value(cardName); + if (cards.contains(cardName)) { + card = cards.value(cardName); if (splitCard && !card->getText().contains(fullCardText)) card->setText(card->getText() + "\n---\n" + fullCardText); } else { @@ -117,7 +117,7 @@ CardInfo *OracleImporter::addCard(const QString &setName, tableRow = 2; card->setTableRow(tableRow); - cardHash.insert(cardName, card); + cards.insert(cardName, card); } card->setMuId(setName, cardId); @@ -225,8 +225,8 @@ int OracleImporter::startImport() continue; CardSet *set = new CardSet(curSet->getShortName(), curSet->getLongName()); - if (!setHash.contains(set->getShortName())) - setHash.insert(set->getShortName(), set); + if (!sets.contains(set->getShortName())) + sets.insert(set->getShortName(), set); int setCards = importTextSpoiler(set, curSet->getCards()); From c07b53999ddf5563465bbe0aeb492a06bdd15b97 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sat, 12 Jul 2014 00:11:48 +0200 Subject: [PATCH 04/11] Fix: ensure git is always run from the project directory When trying to obtain the version hash --- cmake/getversion.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/getversion.cmake b/cmake/getversion.cmake index 9db61040..e434e992 100644 --- a/cmake/getversion.cmake +++ b/cmake/getversion.cmake @@ -2,6 +2,7 @@ find_package(Git) if(GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} describe --long --always + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} RESULT_VARIABLE res_var OUTPUT_VARIABLE GIT_COM_ID ) From 23b0a6c0dfd6d4a2055f87ae2a4e7efbe1d18bed Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sat, 12 Jul 2014 01:38:00 +0200 Subject: [PATCH 05/11] Windows + CPack: fix packaging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Package the correct cpack’s temporary directory --- cmake/NSIS.definitions.nsh.in | 4 +--- cmake/NSIS.template.in | 6 ++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/NSIS.definitions.nsh.in b/cmake/NSIS.definitions.nsh.in index 0d98b666..cc4b014c 100644 --- a/cmake/NSIS.definitions.nsh.in +++ b/cmake/NSIS.definitions.nsh.in @@ -1,3 +1 @@ -!define NSIS_PROJECT_NAME "@PROJECT_NAME@" -!define NSIS_SOURCE_PATH "@PROJECT_SOURCE_DIR@" -!define NSIS_BINARY_PATH "@PROJECT_BINARY_DIR@" \ No newline at end of file +!define NSIS_SOURCE_PATH "@PROJECT_SOURCE_DIR@" \ No newline at end of file diff --git a/cmake/NSIS.template.in b/cmake/NSIS.template.in index b2c60d11..c04169c8 100644 --- a/cmake/NSIS.template.in +++ b/cmake/NSIS.template.in @@ -2,10 +2,11 @@ !include "MUI2.nsh" !include "FileFunc.nsh" -Name "${NSIS_PROJECT_NAME}" +Name "@CPACK_PACKAGE_NAME@" OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" SetCompressor /SOLID lzma InstallDir "$PROGRAMFILES\Cockatrice" +!define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" !define MUI_ABORTWARNING !define MUI_WELCOMEFINISHPAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\leftimage.bmp" @@ -35,7 +36,8 @@ Section "Application" SecApplication SetShellVarContext all SetOutPath "$INSTDIR" - File /r "${NSIS_BINARY_PATH}\Release\*.*" + @CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS@ + @CPACK_NSIS_FULL_INSTALL@ WriteUninstaller "$INSTDIR\uninstall.exe" ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 From 1137179b8828adc6a8e4d5aa8b3293c45b5eaf64 Mon Sep 17 00:00:00 2001 From: Gavin Bisesi Date: Sun, 13 Jul 2014 09:08:32 -0400 Subject: [PATCH 06/11] Add WITH_QT4 note to README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ca8796b4..61b44ed4 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,9 @@ To compile: The following flags can be passed to `cmake`: -- `-DWITH_SERVER=1` build the server -- `-DWITHOUT_CLIENT=1` do not build the client +- `-DWITH_SERVER=1` Build the server +- `-DWITHOUT_CLIENT=1` Do not build the client +- `-DWITH_QT4=1` Force compilation to use Qt4 instead of Qt5. # Running From 88e6d0d3eb5419b0abe0fcf4046df296d62d4735 Mon Sep 17 00:00:00 2001 From: arxanas Date: Sat, 19 Jul 2014 14:28:46 -0400 Subject: [PATCH 07/11] Tabs to spaces. --- common/decklist.cpp | 870 ++++++++++++++++++++++---------------------- 1 file changed, 435 insertions(+), 435 deletions(-) diff --git a/common/decklist.cpp b/common/decklist.cpp index 90b5cb7e..8e877ba9 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -6,155 +6,155 @@ #include "decklist.h" SideboardPlan::SideboardPlan(const QString &_name, const QList &_moveList) - : name(_name), moveList(_moveList) + : name(_name), moveList(_moveList) { } void SideboardPlan::setMoveList(const QList &_moveList) { - moveList = _moveList; + moveList = _moveList; } bool SideboardPlan::readElement(QXmlStreamReader *xml) { - while (!xml->atEnd()) { - xml->readNext(); - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "name") - name = xml->readElementText(); - else if (childName == "move_card_to_zone") { - MoveCard_ToZone m; - while (!xml->atEnd()) { - xml->readNext(); - const QString childName2 = xml->name().toString(); - if (xml->isStartElement()) { - if (childName2 == "card_name") - m.set_card_name(xml->readElementText().toStdString()); - else if (childName2 == "start_zone") - m.set_start_zone(xml->readElementText().toStdString()); - else if (childName2 == "target_zone") - m.set_target_zone(xml->readElementText().toStdString()); - } else if (xml->isEndElement() && (childName2 == "move_card_to_zone")) { - moveList.append(m); - break; - } - } - } - } else if (xml->isEndElement() && (childName == "sideboard_plan")) - return true; - } - return false; + while (!xml->atEnd()) { + xml->readNext(); + const QString childName = xml->name().toString(); + if (xml->isStartElement()) { + if (childName == "name") + name = xml->readElementText(); + else if (childName == "move_card_to_zone") { + MoveCard_ToZone m; + while (!xml->atEnd()) { + xml->readNext(); + const QString childName2 = xml->name().toString(); + if (xml->isStartElement()) { + if (childName2 == "card_name") + m.set_card_name(xml->readElementText().toStdString()); + else if (childName2 == "start_zone") + m.set_start_zone(xml->readElementText().toStdString()); + else if (childName2 == "target_zone") + m.set_target_zone(xml->readElementText().toStdString()); + } else if (xml->isEndElement() && (childName2 == "move_card_to_zone")) { + moveList.append(m); + break; + } + } + } + } else if (xml->isEndElement() && (childName == "sideboard_plan")) + return true; + } + return false; } void SideboardPlan::write(QXmlStreamWriter *xml) { - xml->writeStartElement("sideboard_plan"); - xml->writeTextElement("name", name); - for (int i = 0; i < moveList.size(); ++i) { - xml->writeStartElement("move_card_to_zone"); - xml->writeTextElement("card_name", QString::fromStdString(moveList[i].card_name())); - xml->writeTextElement("start_zone", QString::fromStdString(moveList[i].start_zone())); - xml->writeTextElement("target_zone", QString::fromStdString(moveList[i].target_zone())); - xml->writeEndElement(); - } - xml->writeEndElement(); + xml->writeStartElement("sideboard_plan"); + xml->writeTextElement("name", name); + for (int i = 0; i < moveList.size(); ++i) { + xml->writeStartElement("move_card_to_zone"); + xml->writeTextElement("card_name", QString::fromStdString(moveList[i].card_name())); + xml->writeTextElement("start_zone", QString::fromStdString(moveList[i].start_zone())); + xml->writeTextElement("target_zone", QString::fromStdString(moveList[i].target_zone())); + xml->writeEndElement(); + } + xml->writeEndElement(); } AbstractDecklistNode::AbstractDecklistNode(InnerDecklistNode *_parent) - : parent(_parent) + : parent(_parent) { - if (parent) - parent->append(this); + if (parent) + parent->append(this); } int AbstractDecklistNode::depth() const { - if (parent) - return parent->depth() + 1; - else - return 0; + if (parent) + return parent->depth() + 1; + else + return 0; } InnerDecklistNode::InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent) - : AbstractDecklistNode(_parent), name(other->getName()) + : AbstractDecklistNode(_parent), name(other->getName()) { - for (int i = 0; i < other->size(); ++i) { - InnerDecklistNode *inner = dynamic_cast(other->at(i)); - if (inner) - new InnerDecklistNode(inner, this); - else - new DecklistCardNode(dynamic_cast(other->at(i)), this); - } + for (int i = 0; i < other->size(); ++i) { + InnerDecklistNode *inner = dynamic_cast(other->at(i)); + if (inner) + new InnerDecklistNode(inner, this); + else + new DecklistCardNode(dynamic_cast(other->at(i)), this); + } } InnerDecklistNode::~InnerDecklistNode() { - clearTree(); + clearTree(); } QString InnerDecklistNode::visibleNameFromName(const QString &_name) { - if (_name == "main") - return QObject::tr("Maindeck"); - else if (_name == "side") - return QObject::tr("Sideboard"); - else if (_name == "tokens") - return QObject::tr("Tokens"); - else - return _name; + if (_name == "main") + return QObject::tr("Maindeck"); + else if (_name == "side") + return QObject::tr("Sideboard"); + else if (_name == "tokens") + return QObject::tr("Tokens"); + else + return _name; } void InnerDecklistNode::setSortMethod(DeckSortMethod method) { - sortMethod = method; - for (int i = 0; i < size(); i++) - at(i)->setSortMethod(method); + sortMethod = method; + for (int i = 0; i < size(); i++) + at(i)->setSortMethod(method); } QString InnerDecklistNode::getVisibleName() const { - return visibleNameFromName(name); + return visibleNameFromName(name); } void InnerDecklistNode::clearTree() { - for (int i = 0; i < size(); i++) - delete at(i); - clear(); + for (int i = 0; i < size(); i++) + delete at(i); + clear(); } DecklistCardNode::DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent) - : AbstractDecklistCardNode(_parent), name(other->getName()), number(other->getNumber()), price(other->getPrice()) + : AbstractDecklistCardNode(_parent), name(other->getName()), number(other->getNumber()), price(other->getPrice()) { } AbstractDecklistNode *InnerDecklistNode::findChild(const QString &name) { - for (int i = 0; i < size(); i++) - if (at(i)->getName() == name) - return at(i); - return 0; + for (int i = 0; i < size(); i++) + if (at(i)->getName() == name) + return at(i); + return 0; } int InnerDecklistNode::height() const { - return at(0)->height() + 1; + return at(0)->height() + 1; } int InnerDecklistNode::recursiveCount(bool countTotalCards) const { - int result = 0; - for (int i = 0; i < size(); i++) { - InnerDecklistNode *node = dynamic_cast(at(i)); - if (node) - result += node->recursiveCount(countTotalCards); - else if (countTotalCards) - result += dynamic_cast(at(i))->getNumber(); - else - result += 1; - } - return result; + int result = 0; + for (int i = 0; i < size(); i++) { + InnerDecklistNode *node = dynamic_cast(at(i)); + if (node) + result += node->recursiveCount(countTotalCards); + else if (countTotalCards) + result += dynamic_cast(at(i))->getNumber(); + else + result += 1; + } + return result; } float InnerDecklistNode::recursivePrice(bool countTotalCards) const @@ -172,275 +172,275 @@ 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); - } + switch (sortMethod) { + case 0: + return compareNumber(other); + case 1: + return compareName(other); + case 2: + return comparePrice(other); + } return 0; } bool InnerDecklistNode::compareNumber(AbstractDecklistNode *other) const { - InnerDecklistNode *other2 = dynamic_cast(other); - if (other2) { - int n1 = recursiveCount(true); - int n2 = other2->recursiveCount(true); - return (n1 != n2) ? (n1 > n2) : compareName(other); - } else { - return false; - } + InnerDecklistNode *other2 = dynamic_cast(other); + 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(other); - if (other2) { - return (getName() > other2->getName()); - } else { - return false; - } + InnerDecklistNode *other2 = dynamic_cast(other); + if (other2) { + return (getName() > other2->getName()); + } else { + return false; + } } bool InnerDecklistNode::comparePrice(AbstractDecklistNode *other) const { - InnerDecklistNode *other2 = dynamic_cast(other); - if (other2) { - int p1 = 100*recursivePrice(true); - int p2 = 100*other2->recursivePrice(true); - return (p1 != p2) ? (p1 > p2) : compareName(other); - } else { - return false; - } + InnerDecklistNode *other2 = dynamic_cast(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); - } + switch (sortMethod) { + case ByNumber: + return compareNumber(other); + case ByName: + return compareName(other); + case ByPrice: + return compareTotalPrice(other); + } return 0; } bool AbstractDecklistCardNode::compareNumber(AbstractDecklistNode *other) const { - AbstractDecklistCardNode *other2 = dynamic_cast(other); - if (other2) { - int n1 = getNumber(); - int n2 = other2->getNumber(); - return (n1 != n2) ? (n1 > n2) : compareName(other); - } else { - return true; - } + AbstractDecklistCardNode *other2 = dynamic_cast(other); + 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(other); - if (other2) { - return (getName() > other2->getName()); - } else { - return true; - } + AbstractDecklistCardNode *other2 = dynamic_cast(other); + if (other2) { + return (getName() > other2->getName()); + } else { + return true; + } } bool AbstractDecklistCardNode::compareTotalPrice(AbstractDecklistNode *other) const { - AbstractDecklistCardNode *other2 = dynamic_cast(other); - if (other2) { - int p1 = 100*getTotalPrice(); - int p2 = 100*other2->getTotalPrice(); - return (p1 != p2) ? (p1 > p2) : compareName(other); - } else { - return true; - } + AbstractDecklistCardNode *other2 = dynamic_cast(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 { private: - Qt::SortOrder order; + Qt::SortOrder order; public: - compareFunctor(Qt::SortOrder _order) : order(_order) { } - inline bool operator()(QPair a, QPair b) const - { - return (order == Qt::AscendingOrder) ^ (a.second->compare(b.second)); - } + compareFunctor(Qt::SortOrder _order) : order(_order) { } + inline bool operator()(QPair a, QPair b) const + { + return (order == Qt::AscendingOrder) ^ (a.second->compare(b.second)); + } }; bool InnerDecklistNode::readElement(QXmlStreamReader *xml) { - while (!xml->atEnd()) { - xml->readNext(); - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "zone") { - InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this); - newZone->readElement(xml); - } else if (childName == "card") { - float price = (xml->attributes().value("price") != NULL) ? xml->attributes().value("price").toString().toFloat() : 0; - DecklistCardNode *newCard = new DecklistCardNode(xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(), price, this); - newCard->readElement(xml); - } - } else if (xml->isEndElement() && (childName == "zone")) - return false; - } - return true; + while (!xml->atEnd()) { + xml->readNext(); + const QString childName = xml->name().toString(); + if (xml->isStartElement()) { + if (childName == "zone") { + InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this); + newZone->readElement(xml); + } else if (childName == "card") { + float price = (xml->attributes().value("price") != NULL) ? xml->attributes().value("price").toString().toFloat() : 0; + DecklistCardNode *newCard = new DecklistCardNode(xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(), price, this); + newCard->readElement(xml); + } + } else if (xml->isEndElement() && (childName == "zone")) + return false; + } + return true; } void InnerDecklistNode::writeElement(QXmlStreamWriter *xml) { - xml->writeStartElement("zone"); - xml->writeAttribute("name", name); - for (int i = 0; i < size(); i++) - at(i)->writeElement(xml); - xml->writeEndElement(); // zone + xml->writeStartElement("zone"); + xml->writeAttribute("name", name); + for (int i = 0; i < size(); i++) + at(i)->writeElement(xml); + xml->writeEndElement(); // zone } bool AbstractDecklistCardNode::readElement(QXmlStreamReader *xml) { - while (!xml->atEnd()) { - xml->readNext(); - if (xml->isEndElement() && xml->name() == "card") - return false; - } - return true; + while (!xml->atEnd()) { + xml->readNext(); + if (xml->isEndElement() && xml->name() == "card") + return false; + } + return true; } void AbstractDecklistCardNode::writeElement(QXmlStreamWriter *xml) { - xml->writeEmptyElement("card"); - xml->writeAttribute("number", QString::number(getNumber())); + xml->writeEmptyElement("card"); + xml->writeAttribute("number", QString::number(getNumber())); xml->writeAttribute("price", QString::number(getPrice())); - xml->writeAttribute("name", getName()); + xml->writeAttribute("name", getName()); } QVector > InnerDecklistNode::sort(Qt::SortOrder order) { - QVector > result(size()); - - // Initialize temporary list with contents of current list - QVector > tempList(size()); - for (int i = size() - 1; i >= 0; --i) { - tempList[i].first = i; - tempList[i].second = at(i); - } - - // Sort temporary list - compareFunctor cmp(order); - qSort(tempList.begin(), tempList.end(), cmp); - - // Map old indexes to new indexes and - // copy temporary list to the current one - for (int i = size() - 1; i >= 0; --i) { - result[i].first = tempList[i].first; - result[i].second = i; - replace(i, tempList[i].second); - } + QVector > result(size()); + + // Initialize temporary list with contents of current list + QVector > tempList(size()); + for (int i = size() - 1; i >= 0; --i) { + tempList[i].first = i; + tempList[i].second = at(i); + } + + // Sort temporary list + compareFunctor cmp(order); + qSort(tempList.begin(), tempList.end(), cmp); + + // Map old indexes to new indexes and + // copy temporary list to the current one + for (int i = size() - 1; i >= 0; --i) { + result[i].first = tempList[i].first; + result[i].second = i; + replace(i, tempList[i].second); + } - return result; + return result; } DeckList::DeckList() { - root = new InnerDecklistNode; + root = new InnerDecklistNode; } // TODO: http://qt-project.org/doc/qt-4.8/qobject.html#no-copy-constructor-or-assignment-operator DeckList::DeckList(const DeckList &other) - : name(other.name), - comments(other.comments), - deckHash(other.deckHash) + : name(other.name), + comments(other.comments), + deckHash(other.deckHash) { - root = new InnerDecklistNode(other.getRoot()); - - QMapIterator spIterator(other.getSideboardPlans()); - while (spIterator.hasNext()) { - spIterator.next(); - sideboardPlans.insert(spIterator.key(), new SideboardPlan(spIterator.key(), spIterator.value()->getMoveList())); - } - updateDeckHash(); + root = new InnerDecklistNode(other.getRoot()); + + QMapIterator spIterator(other.getSideboardPlans()); + while (spIterator.hasNext()) { + spIterator.next(); + sideboardPlans.insert(spIterator.key(), new SideboardPlan(spIterator.key(), spIterator.value()->getMoveList())); + } + updateDeckHash(); } DeckList::DeckList(const QString &nativeString) { - root = new InnerDecklistNode; - loadFromString_Native(nativeString); + root = new InnerDecklistNode; + loadFromString_Native(nativeString); } DeckList::~DeckList() { - delete root; - - QMapIterator i(sideboardPlans); - while (i.hasNext()) - delete i.next().value(); + delete root; + + QMapIterator i(sideboardPlans); + while (i.hasNext()) + delete i.next().value(); } QList DeckList::getCurrentSideboardPlan() { - SideboardPlan *current = sideboardPlans.value(QString(), 0); - if (!current) - return QList(); - else - return current->getMoveList(); + SideboardPlan *current = sideboardPlans.value(QString(), 0); + if (!current) + return QList(); + else + return current->getMoveList(); } void DeckList::setCurrentSideboardPlan(const QList &plan) { - SideboardPlan *current = sideboardPlans.value(QString(), 0); - if (!current) { - current = new SideboardPlan; - sideboardPlans.insert(QString(), current); - } - - current->setMoveList(plan); + SideboardPlan *current = sideboardPlans.value(QString(), 0); + if (!current) { + current = new SideboardPlan; + sideboardPlans.insert(QString(), current); + } + + current->setMoveList(plan); } bool DeckList::readElement(QXmlStreamReader *xml) { - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "deckname") - name = xml->readElementText(); - else if (childName == "comments") - comments = xml->readElementText(); - else if (childName == "zone") { - InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), root); - newZone->readElement(xml); - } else if (childName == "sideboard_plan") { - SideboardPlan *newSideboardPlan = new SideboardPlan; - if (newSideboardPlan->readElement(xml)) - sideboardPlans.insert(newSideboardPlan->getName(), newSideboardPlan); - else - delete newSideboardPlan; - } - } else if (xml->isEndElement() && (childName == "cockatrice_deck")) - return false; - return true; + const QString childName = xml->name().toString(); + if (xml->isStartElement()) { + if (childName == "deckname") + name = xml->readElementText(); + else if (childName == "comments") + comments = xml->readElementText(); + else if (childName == "zone") { + InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), root); + newZone->readElement(xml); + } else if (childName == "sideboard_plan") { + SideboardPlan *newSideboardPlan = new SideboardPlan; + if (newSideboardPlan->readElement(xml)) + sideboardPlans.insert(newSideboardPlan->getName(), newSideboardPlan); + else + delete newSideboardPlan; + } + } else if (xml->isEndElement() && (childName == "cockatrice_deck")) + return false; + return true; } void DeckList::write(QXmlStreamWriter *xml) { - xml->writeStartElement("cockatrice_deck"); - xml->writeAttribute("version", "1"); - xml->writeTextElement("deckname", name); - xml->writeTextElement("comments", comments); + xml->writeStartElement("cockatrice_deck"); + xml->writeAttribute("version", "1"); + xml->writeTextElement("deckname", name); + xml->writeTextElement("comments", comments); - for (int i = 0; i < root->size(); i++) - root->at(i)->writeElement(xml); - - QMapIterator i(sideboardPlans); - while (i.hasNext()) - i.next().value()->write(xml); - xml->writeEndElement(); + for (int i = 0; i < root->size(); i++) + root->at(i)->writeElement(xml); + + QMapIterator i(sideboardPlans); + while (i.hasNext()) + i.next().value()->write(xml); + xml->writeEndElement(); } bool DeckList::loadFromXml(QXmlStreamReader *xml) @@ -450,187 +450,187 @@ bool DeckList::loadFromXml(QXmlStreamReader *xml) return false; } - cleanList(); - while (!xml->atEnd()) { - xml->readNext(); - if (xml->isStartElement()) { - if (xml->name() != "cockatrice_deck") - return false; - while (!xml->atEnd()) { - xml->readNext(); - if (!readElement(xml)) - break; - } - } - } - updateDeckHash(); + cleanList(); + while (!xml->atEnd()) { + xml->readNext(); + if (xml->isStartElement()) { + if (xml->name() != "cockatrice_deck") + return false; + while (!xml->atEnd()) { + xml->readNext(); + if (!readElement(xml)) + break; + } + } + } + updateDeckHash(); if (xml->error()) { qDebug() << "Error loading deck from xml: " << xml->errorString(); return false; } - return true; + return true; } bool DeckList::loadFromString_Native(const QString &nativeString) { - QXmlStreamReader xml(nativeString); - return loadFromXml(&xml); + QXmlStreamReader xml(nativeString); + return loadFromXml(&xml); } QString DeckList::writeToString_Native() { - QString result; - QXmlStreamWriter xml(&result); - xml.writeStartDocument(); - write(&xml); - xml.writeEndDocument(); - return result; + QString result; + QXmlStreamWriter xml(&result); + xml.writeStartDocument(); + write(&xml); + xml.writeEndDocument(); + return result; } bool DeckList::loadFromFile_Native(QIODevice *device) { - QXmlStreamReader xml(device); - return loadFromXml(&xml); + QXmlStreamReader xml(device); + return loadFromXml(&xml); } bool DeckList::saveToFile_Native(QIODevice *device) { - QXmlStreamWriter xml(device); - xml.setAutoFormatting(true); - xml.writeStartDocument(); + QXmlStreamWriter xml(device); + xml.setAutoFormatting(true); + xml.writeStartDocument(); - write(&xml); + write(&xml); - xml.writeEndDocument(); - return true; + xml.writeEndDocument(); + return true; } bool DeckList::loadFromStream_Plain(QTextStream &in) { - cleanList(); + cleanList(); - InnerDecklistNode *main = 0, *side = 0; - bool inSideboard = false; + InnerDecklistNode *main = 0, *side = 0; + bool inSideboard = false; - int okRows = 0; - while (!in.atEnd()) { - QString line = in.readLine().simplified(); - if (line.startsWith("//")) - continue; + int okRows = 0; + while (!in.atEnd()) { + QString line = in.readLine().simplified(); + if (line.startsWith("//")) + continue; - InnerDecklistNode *zone; - if (line.startsWith("Sideboard", Qt::CaseInsensitive)) { - inSideboard = true; - continue; - } else if (line.startsWith("SB:", Qt::CaseInsensitive)) { - line = line.mid(3).trimmed(); - if (!side) - side = new InnerDecklistNode("side", root); - zone = side; - } else if (inSideboard) { - if (!side) - side = new InnerDecklistNode("side", root); - zone = side; - } else { - if (!main) - main = new InnerDecklistNode("main", root); - zone = main; - } + InnerDecklistNode *zone; + if (line.startsWith("Sideboard", Qt::CaseInsensitive)) { + inSideboard = true; + continue; + } else if (line.startsWith("SB:", Qt::CaseInsensitive)) { + line = line.mid(3).trimmed(); + if (!side) + side = new InnerDecklistNode("side", root); + zone = side; + } else if (inSideboard) { + if (!side) + side = new InnerDecklistNode("side", root); + zone = side; + } else { + if (!main) + main = new InnerDecklistNode("main", root); + zone = main; + } - // Filter out MWS edition symbols and basic land extras - QRegExp rx("\\[.*\\]"); - line.remove(rx); - rx.setPattern("\\(.*\\)"); - line.remove(rx); - //Filter out post card name editions - rx.setPattern("\\|.*$"); - line.remove(rx); - line = line.simplified(); + // Filter out MWS edition symbols and basic land extras + QRegExp rx("\\[.*\\]"); + line.remove(rx); + rx.setPattern("\\(.*\\)"); + line.remove(rx); + //Filter out post card name editions + rx.setPattern("\\|.*$"); + line.remove(rx); + line = line.simplified(); - int i = line.indexOf(' '); - bool ok; - int number = line.left(i).toInt(&ok); - if (!ok) - continue; + int i = line.indexOf(' '); + bool ok; + int number = line.left(i).toInt(&ok); + if (!ok) + continue; - QString cardName = line.mid(i + 1); + QString cardName = line.mid(i + 1); // Common differences between cockatrice's card names // and what's commonly used in decklists - rx.setPattern("’"); + rx.setPattern("’"); cardName.replace(rx, "'"); - rx.setPattern("Æ"); - cardName.replace(rx, "AE"); - rx.setPattern("^Aether"); - cardName.replace(rx, "AEther"); - rx.setPattern("\\s*[&|/]{1,2}\\s*"); - cardName.replace(rx, " // "); + rx.setPattern("Æ"); + cardName.replace(rx, "AE"); + rx.setPattern("^Aether"); + cardName.replace(rx, "AEther"); + rx.setPattern("\\s*[&|/]{1,2}\\s*"); + cardName.replace(rx, " // "); - ++okRows; - new DecklistCardNode(cardName, number, zone); - } - updateDeckHash(); - return (okRows > 0); + ++okRows; + new DecklistCardNode(cardName, number, zone); + } + updateDeckHash(); + return (okRows > 0); } bool DeckList::loadFromFile_Plain(QIODevice *device) { - QTextStream in(device); - return loadFromStream_Plain(in); + QTextStream in(device); + return loadFromStream_Plain(in); } bool DeckList::saveToStream_Plain(QTextStream &out) { - // Support for this is only possible if the internal structure doesn't get more complicated. - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName()); - } - } - return true; + // Support for this is only possible if the internal structure doesn't get more complicated. + for (int i = 0; i < root->size(); i++) { + InnerDecklistNode *node = dynamic_cast(root->at(i)); + for (int j = 0; j < node->size(); j++) { + DecklistCardNode *card = dynamic_cast(node->at(j)); + out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName()); + } + } + return true; } bool DeckList::saveToFile_Plain(QIODevice *device) { - QTextStream out(device); - return saveToStream_Plain(out); + QTextStream out(device); + return saveToStream_Plain(out); } QString DeckList::writeToString_Plain() { - QString result; - QTextStream out(&result); - saveToStream_Plain(out); - return result; + QString result; + QTextStream out(&result); + saveToStream_Plain(out); + return result; } void DeckList::cleanList() { - root->clearTree(); - setName(); - setComments(); - deckHash = QString(); - emit deckHashChanged(); + root->clearTree(); + setName(); + setComments(); + deckHash = QString(); + emit deckHashChanged(); } void DeckList::getCardListHelper(InnerDecklistNode *item, QSet &result) const { - for (int i = 0; i < item->size(); ++i) { - DecklistCardNode *node = dynamic_cast(item->at(i)); - if (node) - result.insert(node->getName()); - else - getCardListHelper(dynamic_cast(item->at(i)), result); - } + for (int i = 0; i < item->size(); ++i) { + DecklistCardNode *node = dynamic_cast(item->at(i)); + if (node) + result.insert(node->getName()); + else + getCardListHelper(dynamic_cast(item->at(i)), result); + } } QStringList DeckList::getCardList() const { - QSet result; - getCardListHelper(root, result); - return result.toList(); + QSet result; + getCardListHelper(root, result); + return result.toList(); } int DeckList::getSideboardSize() const @@ -640,7 +640,7 @@ int DeckList::getSideboardSize() const InnerDecklistNode *node = dynamic_cast(root->at(i)); if (node->getName() != "side") continue; - for (int j = 0; j < node->size(); j++) { + for (int j = 0; j < node->size(); j++) { DecklistCardNode *card = dynamic_cast(node->at(j)); size += card->getNumber(); } @@ -650,65 +650,65 @@ int DeckList::getSideboardSize() const DecklistCardNode *DeckList::addCard(const QString &cardName, const QString &zoneName) { - InnerDecklistNode *zoneNode = dynamic_cast(root->findChild(zoneName)); - if (!zoneNode) - zoneNode = new InnerDecklistNode(zoneName, root); + InnerDecklistNode *zoneNode = dynamic_cast(root->findChild(zoneName)); + if (!zoneNode) + zoneNode = new InnerDecklistNode(zoneName, root); - DecklistCardNode *node = new DecklistCardNode(cardName, 1, zoneNode); - updateDeckHash(); - return node; + DecklistCardNode *node = new DecklistCardNode(cardName, 1, zoneNode); + updateDeckHash(); + return node; } bool DeckList::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode) { - if (node == root) - return true; - bool updateHash = false; - if (!rootNode) { - rootNode = root; - updateHash = true; - } - - int index = rootNode->indexOf(node); - if (index != -1) { - delete rootNode->takeAt(index); - if (!rootNode->size()) - deleteNode(rootNode, rootNode->getParent()); - if (updateHash) - updateDeckHash(); - return true; - } - for (int i = 0; i < rootNode->size(); i++) { - InnerDecklistNode *inner = dynamic_cast(rootNode->at(i)); - if (inner) - if (deleteNode(node, inner)) { - if (updateHash) - updateDeckHash(); - return true; - } - } - return false; + if (node == root) + return true; + bool updateHash = false; + if (!rootNode) { + rootNode = root; + updateHash = true; + } + + int index = rootNode->indexOf(node); + if (index != -1) { + delete rootNode->takeAt(index); + if (!rootNode->size()) + deleteNode(rootNode, rootNode->getParent()); + if (updateHash) + updateDeckHash(); + return true; + } + for (int i = 0; i < rootNode->size(); i++) { + InnerDecklistNode *inner = dynamic_cast(rootNode->at(i)); + if (inner) + if (deleteNode(node, inner)) { + if (updateHash) + updateDeckHash(); + return true; + } + } + return false; } void DeckList::updateDeckHash() { - QStringList cardList; - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - for (int k = 0; k < card->getNumber(); ++k) - cardList.append((node->getName() == "side" ? "SB:" : "") + card->getName().toLower()); - } - } - cardList.sort(); - QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1); - quint64 number = (((quint64) (unsigned char) deckHashArray[0]) << 32) - + (((quint64) (unsigned char) deckHashArray[1]) << 24) - + (((quint64) (unsigned char) deckHashArray[2] << 16)) - + (((quint64) (unsigned char) deckHashArray[3]) << 8) - + (quint64) (unsigned char) deckHashArray[4]; - deckHash = QString::number(number, 32).rightJustified(8, '0'); - - emit deckHashChanged(); + QStringList cardList; + for (int i = 0; i < root->size(); i++) { + InnerDecklistNode *node = dynamic_cast(root->at(i)); + for (int j = 0; j < node->size(); j++) { + DecklistCardNode *card = dynamic_cast(node->at(j)); + for (int k = 0; k < card->getNumber(); ++k) + cardList.append((node->getName() == "side" ? "SB:" : "") + card->getName().toLower()); + } + } + cardList.sort(); + QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1); + quint64 number = (((quint64) (unsigned char) deckHashArray[0]) << 32) + + (((quint64) (unsigned char) deckHashArray[1]) << 24) + + (((quint64) (unsigned char) deckHashArray[2] << 16)) + + (((quint64) (unsigned char) deckHashArray[3]) << 8) + + (quint64) (unsigned char) deckHashArray[4]; + deckHash = QString::number(number, 32).rightJustified(8, '0'); + + emit deckHashChanged(); } From 0eb97c91b38f0096cc32044c4e5973d76155cfb9 Mon Sep 17 00:00:00 2001 From: arxanas Date: Sat, 19 Jul 2014 14:47:19 -0400 Subject: [PATCH 08/11] Fix #183: Deck parser doesn't too eagerly replace ampersands. --- common/decklist.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/common/decklist.cpp b/common/decklist.cpp index 8e877ba9..f2808337 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -563,9 +563,20 @@ bool DeckList::loadFromStream_Plain(QTextStream &in) cardName.replace(rx, "AE"); rx.setPattern("^Aether"); cardName.replace(rx, "AEther"); - rx.setPattern("\\s*[&|/]{1,2}\\s*"); + rx.setPattern("\\s*[|/]{1,2}\\s*"); 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; new DecklistCardNode(cardName, number, zone); } From 4df46b13e6ea9ee13b7854eb1f0ab8f9d5fd8f8d Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Sat, 19 Jul 2014 18:27:44 -0400 Subject: [PATCH 09/11] Removed cmake from Travis's OS X dependencies. --- travis-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/travis-dependencies.sh b/travis-dependencies.sh index 0c6c62e6..10af11cd 100755 --- a/travis-dependencies.sh +++ b/travis-dependencies.sh @@ -2,7 +2,7 @@ if [[ $TRAVIS_OS_NAME == "osx" ]] ; then brew update - brew install qt cmake protobuf libgcrypt + brew install qt protobuf libgcrypt else sudo apt-get update -qq sudo apt-get install -y qtmobility-dev libprotobuf-dev protobuf-compiler libqt4-dev From aff32e0f567a7e07664bf3e31c6a06506d2ffeaf Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Sun, 20 Jul 2014 21:09:58 -0400 Subject: [PATCH 10/11] Ignore diacritics as well. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So [card]jotun grunt[/card] works as well as [card]Jötun Grunt[/card]. Also I removed some debugging information I accidentally left in. --- cockatrice/src/carddatabase.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index c55148aa..5083e6ed 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -436,6 +436,10 @@ int CardInfo::getPreferredMuId() QString CardInfo::simplifyName(const QString &name) { QString simpleName(name); + + // 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; @@ -553,8 +557,6 @@ CardInfo *CardDatabase::getCard(const QString &cardName, bool createIfNotFound) CardInfo *CardDatabase::getCardBySimpleName(const QString &cardName, bool createIfNotFound) { QString simpleName = CardInfo::simplifyName(cardName); - qDebug() << "Getting card by name " << simpleName << "\n"; - qDebug() << "Cards available: " << simpleNameCards.size() << "\n"; return getCardFromMap(simpleNameCards, simpleName, createIfNotFound); } From 2a5aa06881326423decdea3326f37afb9e537b2c Mon Sep 17 00:00:00 2001 From: Waleed Khan Date: Tue, 22 Jul 2014 20:58:38 -0400 Subject: [PATCH 11/11] =?UTF-8?q?=C3=86=20now=20actually=20works=20with=20?= =?UTF-8?q?the=20case-insensitive=20tags.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cockatrice/src/carddatabase.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 5083e6ed..df449610 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -437,6 +437,11 @@ int CardInfo::getPreferredMuId() 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);