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 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 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 ) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index eff101f9..51120ee0 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -434,6 +434,22 @@ int CardInfo::getPreferredMuId() 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) { xml.writeStartElement("card"); @@ -507,55 +523,55 @@ 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); + 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 +579,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 +589,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 +615,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,46 +665,32 @@ 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)); } } } -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); 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) { @@ -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; } @@ -728,18 +732,17 @@ 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) - xml << card; + xml << card; } xml.writeEndElement(); // cards @@ -753,7 +756,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 +766,7 @@ void CardDatabase::picDownloadHqChanged() { pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq()); if (settingsCache->getPicDownloadHq()) { - QHashIterator cardIterator(cardHash); + QHashIterator cardIterator(cards); while (cardIterator.hasNext()) cardIterator.next().value()->clearPixmapCacheMiss(); } @@ -773,11 +776,11 @@ 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; - QHashIterator setsIterator(setHash); + QHashIterator setsIterator(sets); while (setsIterator.hasNext()) allSets.append(setsIterator.next().value()); allSets.sortByKey(); @@ -809,7 +812,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 +827,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..3d1da9a2 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,10 +214,17 @@ 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); + LoadStatus loadFromFile(const QString &fileName); bool saveToFile(const QString &fileName, bool tokens = false); QStringList getAllColors() const; QStringList getAllMainCardTypes() const; 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/cockatrice/src/deckstats_interface.cpp b/cockatrice/src/deckstats_interface.cpp index 245e04c2..c70624bf 100644 --- a/cockatrice/src/deckstats_interface.cpp +++ b/cockatrice/src/deckstats_interface.cpp @@ -11,8 +11,10 @@ #include #endif -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 *))); @@ -43,24 +45,23 @@ void DeckStatsInterface::queryFinished(QNetworkReply *reply) deleteLater(); } +void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data) +{ + DeckList deckWithoutTokens; + copyDeckWithoutTokens(*deck, deckWithoutTokens); + #if QT_VERSION < 0x050000 -void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data) -{ QUrl params; - params.addQueryItem("deck", deck->writeToString_Plain()); + params.addQueryItem("deck", deckWithoutTokens.writeToString_Plain()); data->append(params.encodedQuery()); -} #else -void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data) -{ QUrl params; QUrlQuery urlQuery; - urlQuery.addQueryItem("deck", deck->writeToString_Plain()); + urlQuery.addQueryItem("deck", deckWithoutTokens.writeToString_Plain()); params.setQuery(urlQuery); data->append(params.query(QUrl::EncodeReserved)); -} #endif - +} void DeckStatsInterface::analyzeDeck(DeckList *deck) { @@ -72,3 +73,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 756f917d..c3a59c9b 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 QByteArray; @@ -12,11 +14,21 @@ 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); void getAnalyzeRequestData(DeckList *deck, QByteArray *data); public: - DeckStatsInterface(QObject *parent = 0); + DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = 0); void analyzeDeck(DeckList *deck); }; 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_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index a54186e1..e402ea11 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -512,7 +512,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/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(); } diff --git a/common/decklist.cpp b/common/decklist.cpp index 52ef6415..af2f6270 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -570,9 +570,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); } @@ -586,16 +597,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 8b71a4b7..c2037205 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 3562d5cc..6a099eb4 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -80,8 +80,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 { @@ -121,7 +121,7 @@ CardInfo *OracleImporter::addCard(const QString &setName, tableRow = 2; card->setTableRow(tableRow); - cardHash.insert(cardName, card); + cards.insert(cardName, card); } card->setMuId(setName, cardId); @@ -141,6 +141,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) QString cardText; int cardId; int cardLoyalty; + bool cardIsToken = false; QMap splitCards; while (it.hasNext()) { @@ -201,6 +202,7 @@ 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"; // Distinguish Vanguard cards from regular cards of the same name. 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)) { card->addToSet(set); @@ -234,8 +236,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()); 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