%1 | | ").arg(tr("Related cards:"));
- for (int i = 0; i < relatedCards.size(); ++i) {
- QString tmp = relatedCards.at(i)->getName().toHtmlEscaped();
+ for (auto *relatedCard : relatedCards) {
+ QString tmp = relatedCard->getName().toHtmlEscaped();
text += "" + tmp + " ";
}
- for (int i = 0; i < reverserelatedCards2Me.size(); ++i) {
- QString tmp = reverserelatedCards2Me.at(i)->getName().toHtmlEscaped();
+ for (auto *i : reverserelatedCards2Me) {
+ QString tmp = i->getName().toHtmlEscaped();
text += "" + tmp + " ";
}
diff --git a/cockatrice/src/cardinfotext.h b/cockatrice/src/cardinfotext.h
index 7ee0a890..882e542a 100644
--- a/cockatrice/src/cardinfotext.h
+++ b/cockatrice/src/cardinfotext.h
@@ -17,7 +17,7 @@ private:
CardInfoPtr info;
public:
- CardInfoText(QWidget *parent = 0);
+ explicit CardInfoText(QWidget *parent = nullptr);
void retranslateUi();
void setInvalidCardName(const QString &cardName);
diff --git a/cockatrice/src/filter_string.cpp b/cockatrice/src/filter_string.cpp
new file mode 100644
index 00000000..b514789b
--- /dev/null
+++ b/cockatrice/src/filter_string.cpp
@@ -0,0 +1,352 @@
+#include "filter_string.h"
+#include "../../common/lib/peglib.h"
+
+#include
+#include
+#include
+#include
+
+peg::parser search(R"(
+Start <- QueryPartList
+~ws <- [ ]+
+QueryPartList <- ComplexQueryPart ( ws ("and" ws)? ComplexQueryPart)* ws*
+
+ComplexQueryPart <- SomewhatComplexQueryPart ws $or<[oO][rR]> ws ComplexQueryPart / SomewhatComplexQueryPart
+
+SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart
+
+QueryPart <- NotQuery / SetQuery / RarityQuery / CMCQuery / FormatQuery / PowerQuery / ToughnessQuery / ColorQuery / TypeQuery / OracleQuery / FieldQuery / GenericQuery
+
+NotQuery <- ('not' ws/'-') SomewhatComplexQueryPart
+SetQuery <- ('e'/'set') [:] FlexStringValue
+OracleQuery <- 'o' [:] RegexString
+
+
+CMCQuery <- 'cmc' ws? NumericExpression
+PowerQuery <- [Pp] 'ow' 'er'? ws? NumericExpression
+ToughnessQuery <- [Tt] 'ou' 'ghness'? ws? NumericExpression
+RarityQuery <- [rR] ':' RegexString
+
+FormatQuery <- 'f' ':' Format / Legality ':' Format
+Format <- [Mm] 'odern'? / [Ss] 'tandard'? / [Vv] 'intage'? / [Ll] 'egacy'? / [Cc] 'ommander'?
+Legality <- [Ll] 'egal'? / [Bb] 'anned'? / [Rr] 'estricted'
+
+
+TypeQuery <- [tT] 'ype'? [:] StringValue
+
+Color <- < [Ww] 'hite'? / [Uu] / [Bb] 'lack'? / [Rr] 'ed'? / [Gg] 'reen'? / [Bb] 'lue'? >
+ColorEx <- Color / [mc]
+
+ColorQuery <- [cC] 'olor'? <[iI]?> <[:!]> ColorEx*
+
+FieldQuery <- String [:] RegexString / String ws? NumericExpression
+
+NonQuote <- !["].
+UnescapedStringListPart <- [a-zA-Z0-9']+
+String <- UnescapedStringListPart / ["] ["]
+StringValue <- String / [(] StringList [)]
+StringList <- StringListString (ws? [,] ws? StringListString)*
+StringListString <- UnescapedStringListPart
+GenericQuery <- RegexString
+RegexString <- String
+
+FlexStringValue <- CompactStringSet / String / [(] StringList [)]
+CompactStringSet <- StringListString ([,+] StringListString)+
+
+NumericExpression <- NumericOperator ws? NumericValue
+NumericOperator <- [=:] / <[>
+NumericValue <- [0-9]+
+
+)");
+
+std::once_flag init;
+
+static void setupParserRules()
+{
+ auto passthru = [](const peg::SemanticValues &sv) -> Filter { return !sv.empty() ? sv[0].get() : nullptr; };
+
+ search["Start"] = passthru;
+ search["QueryPartList"] = [](const peg::SemanticValues &sv) -> Filter {
+ return [=](CardData x) {
+ for (int i = 0; i < sv.size(); ++i) {
+ if (!sv[i].get()(x))
+ return false;
+ }
+ return true;
+ };
+ };
+ search["ComplexQueryPart"] = [](const peg::SemanticValues &sv) -> Filter {
+ return [=](CardData x) {
+ for (int i = 0; i < sv.size(); ++i) {
+ if (sv[i].get()(x))
+ return true;
+ }
+ return false;
+ };
+ };
+ search["SomewhatComplexQueryPart"] = passthru;
+ search["QueryPart"] = passthru;
+ search["NotQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ Filter dependent = sv[0].get();
+ return [=](CardData x) -> bool { return !dependent(x); };
+ };
+ search["TypeQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ StringMatcher matcher = sv[0].get();
+ return [=](CardData x) -> bool { return matcher(x->getCardType()); };
+ };
+ search["SetQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ StringMatcher matcher = sv[0].get();
+ return [=](CardData x) -> bool {
+ for (const auto &set : x->getSets().keys()) {
+ if (matcher(set))
+ return true;
+ }
+ return false;
+ };
+ };
+ search["RarityQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ StringMatcher matcher = sv[0].get();
+ return [=](CardData x) -> bool {
+ for (const auto &set : x->getSets().values()) {
+ if (matcher(set.getProperty("rarity")))
+ return true;
+ }
+ return false;
+ };
+ };
+ search["FormatQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ if (sv.choice() == 0) {
+ QString format = sv[0].get();
+ return [=](CardData x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == "legal"; };
+ } else {
+ QString format = sv[1].get();
+ QString legality = sv[0].get();
+ return [=](CardData x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == legality; };
+ }
+ };
+ search["Legality"] = [](const peg::SemanticValues &sv) -> QString {
+ switch (tolower(sv.str()[0])) {
+ case 'l':
+ return "legal";
+ case 'b':
+ return "banned";
+ case 'r':
+ return "restricted";
+ default:
+ return "";
+ }
+ };
+
+ search["Format"] = [](const peg::SemanticValues &sv) -> QString {
+ switch (tolower(sv.str()[0])) {
+ case 'm':
+ return "modern";
+ case 's':
+ return "standard";
+ case 'v':
+ return "vintage";
+ case 'l':
+ return "legacy";
+ case 'c':
+ return "commander";
+ default:
+ return "";
+ }
+ };
+ search["StringValue"] = [](const peg::SemanticValues &sv) -> StringMatcher {
+ if (sv.choice() == 0) {
+ auto target = sv[0].get();
+ return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); };
+ } else {
+ auto target = sv[0].get();
+ return [=](const QString &s) {
+ for (const QString &str : target) {
+ if (s.split(" ").contains(str, Qt::CaseInsensitive)) {
+ return true;
+ }
+ }
+ return false;
+ };
+ }
+ };
+
+ search["String"] = [](const peg::SemanticValues &sv) -> QString {
+ if (sv.choice() == 0) {
+ return QString::fromStdString(sv.str());
+ } else {
+ return QString::fromStdString(sv.token(0));
+ }
+ };
+ search["FlexStringValue"] = [](const peg::SemanticValues &sv) -> StringMatcher {
+ if (sv.choice() != 1) {
+ auto target = sv[0].get();
+ return [=](const QString &s) {
+ for (const QString &str : target) {
+ if (s.split(" ").contains(str, Qt::CaseInsensitive)) {
+ return true;
+ }
+ }
+ return false;
+ };
+ } else {
+ auto target = sv[0].get();
+ return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); };
+ }
+ };
+ search["CompactStringSet"] = search["StringList"] = [](const peg::SemanticValues &sv) -> QStringList {
+ QStringList result;
+ for (int i = 0; i < sv.size(); ++i) {
+ result.append(sv[i].get());
+ }
+ return result;
+ };
+ search["StringListString"] = [](const peg::SemanticValues &sv) -> QString {
+ return QString::fromStdString(sv.str());
+ };
+
+ search["NumericExpression"] = [](const peg::SemanticValues &sv) -> NumberMatcher {
+ auto arg = sv[1].get();
+ auto op = sv[0].get();
+
+ if (op == ">")
+ return [=](int s) { return s > arg; };
+ if (op == ">=")
+ return [=](int s) { return s >= arg; };
+ if (op == "<")
+ return [=](int s) { return s < arg; };
+ if (op == "<=")
+ return [=](int s) { return s <= arg; };
+ if (op == "=")
+ return [=](int s) { return s == arg; };
+ if (op == ":")
+ return [=](int s) { return s == arg; };
+ if (op == "!=")
+ return [=](int s) { return s != arg; };
+ return [](int) { return false; };
+ };
+
+ search["NumericValue"] = [](const peg::SemanticValues &sv) -> int {
+ return QString::fromStdString(sv.str()).toInt();
+ };
+
+ search["NumericOperator"] = [](const peg::SemanticValues &sv) -> QString {
+ return QString::fromStdString(sv.str());
+ };
+
+ search["RegexString"] = [](const peg::SemanticValues &sv) -> StringMatcher {
+ auto target = sv[0].get();
+ return [=](const QString &s) { return s.QString::contains(target, Qt::CaseInsensitive); };
+ };
+
+ search["OracleQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ StringMatcher matcher = sv[0].get();
+ return [=](CardData x) { return matcher(x->getText()); };
+ };
+
+ search["ColorQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ QString parts;
+ for (int i = 0; i < sv.size(); ++i) {
+ parts += sv[i].get();
+ }
+ bool idenity = sv.tokens[0].first[0] != 'i';
+ if (sv.tokens[1].first[0] == ':') {
+ return [=](CardData x) {
+ QString match = idenity ? x->getColors() : x->getProperty("coloridentity");
+ if (parts.contains("m") && match.length() < 2) {
+ return false;
+ } else if (parts == "m") {
+ return true;
+ }
+
+ if (parts.contains("c") && match.length() == 0)
+ return true;
+
+ for (const auto &i : match) {
+ if (parts.contains(i))
+ return true;
+ }
+ return false;
+ };
+ } else {
+ return [=](CardData x) {
+ QString match = idenity ? x->getColors() : x->getProperty("colorIdentity");
+ if (parts.contains("m") && match.length() < 2)
+ return false;
+
+ if (parts.contains("c") && match.length() != 0)
+ return false;
+
+ for (const auto &part : parts) {
+ if (!match.contains(part))
+ return false;
+ }
+
+ for (const auto &i : match) {
+ if (!parts.contains(i))
+ return false;
+ }
+ return true;
+ };
+ }
+ };
+
+ search["CMCQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ NumberMatcher matcher = sv[0].get();
+ return [=](CardData x) -> bool { return matcher(x->getProperty("cmc").toInt()); };
+ };
+ search["PowerQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ NumberMatcher matcher = sv[0].get();
+ return [=](CardData x) -> bool { return matcher(x->getPowTough().split("/")[0].toInt()); };
+ };
+ search["ToughnessQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ NumberMatcher matcher = sv[0].get();
+ return [=](CardData x) -> bool {
+ auto parts = x->getPowTough().split("/");
+ return matcher(parts.length() == 2 ? parts[1].toInt() : 0);
+ };
+ };
+ search["FieldQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ QString field = sv[0].get();
+ if (sv.choice() == 0) {
+ StringMatcher matcher = sv[1].get();
+ return [=](CardData x) -> bool { return x->hasProperty(field) ? matcher(x->getProperty(field)) : false; };
+ } else {
+ NumberMatcher matcher = sv[1].get();
+ return [=](CardData x) -> bool {
+ return x->hasProperty(field) ? matcher(x->getProperty(field).toInt()) : false;
+ };
+ }
+ };
+ search["GenericQuery"] = [](const peg::SemanticValues &sv) -> Filter {
+ StringMatcher matcher = sv[0].get();
+ return [=](CardData x) { return matcher(x->getName()); };
+ };
+
+ search["Color"] = [](const peg::SemanticValues &sv) -> char { return "WUBRGU"[sv.choice()]; };
+ search["ColorEx"] = [](const peg::SemanticValues &sv) -> char {
+ return sv.choice() == 0 ? sv[0].get() : *sv.c_str();
+ };
+}
+
+FilterString::FilterString(const QString &expr)
+{
+ QByteArray ba = expr.toLocal8Bit();
+
+ std::call_once(init, setupParserRules);
+
+ _error = QString();
+
+ if (ba.isEmpty()) {
+ result = [](CardData) -> bool { return true; };
+ return;
+ }
+
+ search.log = [&](size_t ln, size_t col, const std::string &msg) {
+ _error = QString("%1:%2: %3").arg(ln).arg(col).arg(QString::fromStdString(msg));
+ };
+
+ if (!search.parse(ba.data(), result)) {
+ std::cout << "Error!" << _error.toStdString() << std::endl;
+ result = [](CardData) -> bool { return false; };
+ }
+}
diff --git a/cockatrice/src/filter_string.h b/cockatrice/src/filter_string.h
new file mode 100644
index 00000000..690ff2d7
--- /dev/null
+++ b/cockatrice/src/filter_string.h
@@ -0,0 +1,48 @@
+#ifndef FILTER_STRING_H
+#define FILTER_STRING_H
+
+#include "carddatabase.h"
+#include "filtertree.h"
+
+#include
+#include
+#include
+#include
+
+typedef CardInfoPtr CardData;
+typedef std::function Filter;
+typedef std::function StringMatcher;
+typedef std::function NumberMatcher;
+
+namespace peg
+{
+template struct AstBase;
+struct EmptyType;
+typedef AstBase Ast;
+} // namespace peg
+
+class FilterString
+{
+public:
+ explicit FilterString(const QString &exp);
+ bool check(const CardData &card)
+ {
+ return result(card);
+ }
+
+ bool valid()
+ {
+ return _error.isEmpty();
+ }
+
+ QString error()
+ {
+ return _error;
+ }
+
+private:
+ QString _error;
+ Filter result;
+};
+
+#endif
diff --git a/cockatrice/src/filtertree.cpp b/cockatrice/src/filtertree.cpp
index 1dcb37cf..7bb76b2b 100644
--- a/cockatrice/src/filtertree.cpp
+++ b/cockatrice/src/filtertree.cpp
@@ -253,6 +253,11 @@ bool FilterItem::acceptCmc(const CardInfoPtr info) const
}
}
+bool FilterItem::acceptFormat(const CardInfoPtr info) const
+{
+ return info->getProperty(QString("format-%1").arg(term.toLower())) == "legal";
+}
+
bool FilterItem::acceptLoyalty(const CardInfoPtr info) const
{
if (info->getLoyalty().isEmpty()) {
@@ -400,6 +405,8 @@ bool FilterItem::acceptCardAttr(const CardInfoPtr info, CardFilter::Attr attr) c
return acceptPowerToughness(info, attr);
case CardFilter::AttrLoyalty:
return acceptLoyalty(info);
+ case CardFilter::AttrFormat:
+ return acceptFormat(info);
default:
return true; /* ignore this attribute */
}
@@ -439,16 +446,6 @@ FilterItemList *FilterTree::attrTypeList(CardFilter::Attr attr, CardFilter::Type
return attrLogicMap(attr)->typeList(type);
}
-int FilterTree::findTermIndex(CardFilter::Attr attr, CardFilter::Type type, const QString &term)
-{
- return attrTypeList(attr, type)->termIndex(term);
-}
-
-int FilterTree::findTermIndex(const CardFilter *f)
-{
- return findTermIndex(f->attr(), f->type(), f->term());
-}
-
FilterTreeNode *FilterTree::termNode(CardFilter::Attr attr, CardFilter::Type type, const QString &term)
{
return attrTypeList(attr, type)->termNode(term);
@@ -459,11 +456,6 @@ FilterTreeNode *FilterTree::termNode(const CardFilter *f)
return termNode(f->attr(), f->type(), f->term());
}
-FilterTreeNode *FilterTree::attrTypeNode(CardFilter::Attr attr, CardFilter::Type type)
-{
- return attrTypeList(attr, type);
-}
-
bool FilterTree::testAttr(const CardInfoPtr info, const LogicMap *lm) const
{
const FilterItemList *fil;
diff --git a/cockatrice/src/filtertree.h b/cockatrice/src/filtertree.h
index 7227f944..a837c221 100644
--- a/cockatrice/src/filtertree.h
+++ b/cockatrice/src/filtertree.h
@@ -208,6 +208,7 @@ public:
bool acceptLoyalty(CardInfoPtr info) const;
bool acceptRarity(CardInfoPtr info) const;
bool acceptCardAttr(CardInfoPtr info, CardFilter::Attr attr) const;
+ bool acceptFormat(CardInfoPtr info) const;
bool relationCheck(int cardInfo) const;
};
@@ -252,11 +253,10 @@ private:
public:
FilterTree();
~FilterTree() override;
- int findTermIndex(CardFilter::Attr attr, CardFilter::Type type, const QString &term);
- int findTermIndex(const CardFilter *f);
+
FilterTreeNode *termNode(CardFilter::Attr attr, CardFilter::Type type, const QString &term);
FilterTreeNode *termNode(const CardFilter *f);
- FilterTreeNode *attrTypeNode(CardFilter::Attr attr, CardFilter::Type type);
+
const QString text() const override
{
return QString("root");
diff --git a/cockatrice/src/game_specific_terms.h b/cockatrice/src/game_specific_terms.h
index 9e3d0386..9bae8fd7 100644
--- a/cockatrice/src/game_specific_terms.h
+++ b/cockatrice/src/game_specific_terms.h
@@ -21,6 +21,7 @@ QString const ManaCost("manacost");
QString const PowTough("pt");
QString const Side("side");
QString const Layout("layout");
+QString const ColorIdentity("coloridentity");
inline static const QString getNicePropertyName(QString key)
{
@@ -42,6 +43,8 @@ inline static const QString getNicePropertyName(QString key)
return QCoreApplication::translate("Mtg", "Side");
if (key == Layout)
return QCoreApplication::translate("Mtg", "Layout");
+ if (key == ColorIdentity)
+ return QCoreApplication::translate("Mtg", "Color Identity");
return key;
}
}; // namespace Mtg
diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp
index 79556bda..743ef948 100644
--- a/cockatrice/src/tab_deck_editor.cpp
+++ b/cockatrice/src/tab_deck_editor.cpp
@@ -33,8 +33,10 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
@@ -349,6 +351,7 @@ void TabDeckEditor::createCentralFrame()
searchEdit->setPlaceholderText(tr("Search by card name"));
searchEdit->setClearButtonEnabled(true);
searchEdit->addAction(QPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
+ auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition);
searchEdit->installEventFilter(&searchKeySignals);
setFocusProxy(searchEdit);
@@ -363,6 +366,7 @@ void TabDeckEditor::createCentralFrame()
connect(&searchKeySignals, SIGNAL(onCtrlAltLBracket()), this, SLOT(actDecrementCardFromSideboard()));
connect(&searchKeySignals, SIGNAL(onCtrlAltEnter()), this, SLOT(actAddCardToSideboard()));
connect(&searchKeySignals, SIGNAL(onCtrlEnter()), this, SLOT(actAddCardToSideboard()));
+ connect(help, &QAction::triggered, this, &TabDeckEditor::showSearchSyntaxHelp);
databaseModel = new CardDatabaseModel(db, true, this);
databaseModel->setObjectName("databaseModel");
@@ -700,7 +704,7 @@ void TabDeckEditor::updateCardInfoRight(const QModelIndex ¤t, const QModel
void TabDeckEditor::updateSearch(const QString &search)
{
- databaseDisplayModel->setCardName(search);
+ databaseDisplayModel->setStringFilter(search);
QModelIndexList sel = databaseView->selectionModel()->selectedRows();
if (sel.isEmpty() && databaseDisplayModel->rowCount())
databaseView->selectionModel()->setCurrentIndex(databaseDisplayModel->index(0, 0),
@@ -1212,3 +1216,35 @@ void TabDeckEditor::setSaveStatus(bool newStatus)
aPrintDeck->setEnabled(newStatus);
analyzeDeckMenu->setEnabled(newStatus);
}
+
+void TabDeckEditor::showSearchSyntaxHelp()
+{
+
+ QFile file("theme:help/search.md");
+
+ if (!file.open(QFile::ReadOnly | QFile::Text)) {
+ return;
+ }
+
+ QTextStream in(&file);
+ QString text = in.readAll();
+ file.close();
+
+ // Poor Markdown Converter
+ auto opts = QRegularExpression::MultilineOption;
+ text = text.replace(QRegularExpression("^(###)(.*)", opts), "\\2")
+ .replace(QRegularExpression("^(##)(.*)", opts), "\\2")
+ .replace(QRegularExpression("^(#)(.*)", opts), "\\2")
+ .replace(QRegularExpression("^------*", opts), " ")
+ .replace(QRegularExpression("\\[([^\[]+)\\]\\(([^\\)]+)\\)", opts), "\\1");
+
+ auto browser = new QTextBrowser;
+ browser->setParent(this, Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint |
+ Qt::WindowCloseButtonHint | Qt::WindowFullscreenButtonHint);
+ browser->setWindowTitle("Search Help");
+ browser->setReadOnly(true);
+ browser->setMinimumSize({500, 600});
+ browser->setHtml(text);
+ connect(browser, &QTextBrowser::anchorClicked, [=](QUrl link) { searchEdit->setText(link.fragment()); });
+ browser->show();
+}
diff --git a/cockatrice/src/tab_deck_editor.h b/cockatrice/src/tab_deck_editor.h
index 5aec4f89..b64684f6 100644
--- a/cockatrice/src/tab_deck_editor.h
+++ b/cockatrice/src/tab_deck_editor.h
@@ -13,7 +13,7 @@ class CardDatabaseModel;
class CardDatabaseDisplayModel;
class DeckListModel;
class QTreeView;
-class QTableView;
+
class CardFrame;
class QTextEdit;
class QLabel;
@@ -33,10 +33,10 @@ private:
QTreeView *treeView;
protected:
- void keyPressEvent(QKeyEvent *event);
+ void keyPressEvent(QKeyEvent *event) override;
public:
- SearchLineEdit() : QLineEdit(), treeView(0)
+ SearchLineEdit() : QLineEdit(), treeView(nullptr)
{
}
void setTreeView(QTreeView *_treeView)
@@ -90,12 +90,13 @@ private slots:
void freeDocksSize();
void refreshShortcuts();
- bool eventFilter(QObject *o, QEvent *e);
+ bool eventFilter(QObject *o, QEvent *e) override;
void dockVisibleTriggered();
void dockFloatingTriggered();
void dockTopLevelChanged(bool topLevel);
void saveDbHeaderState();
void setSaveStatus(bool newStatus);
+ void showSearchSyntaxHelp();
private:
CardInfoPtr currentCardInfo() const;
@@ -146,10 +147,10 @@ private:
QWidget *centralWidget;
public:
- TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent = 0);
- ~TabDeckEditor();
- void retranslateUi();
- QString getTabText() const;
+ explicit TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent = nullptr);
+ ~TabDeckEditor() override;
+ void retranslateUi() override;
+ QString getTabText() const override;
void setDeck(DeckLoader *_deckLoader);
void setModified(bool _windowModified);
bool confirmClose();
@@ -160,7 +161,7 @@ public:
void createCentralFrame();
public slots:
- void closeRequest();
+ void closeRequest() override;
signals:
void deckEditorClosing(TabDeckEditor *tab);
};
diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp
index e080a915..8015e41f 100644
--- a/oracle/src/oracleimporter.cpp
+++ b/oracle/src/oracleimporter.cpp
@@ -98,6 +98,11 @@ CardInfoPtr OracleImporter::addCard(QString name,
sortAndReduceColors(allColors);
properties.insert("colors", allColors);
}
+ QString allColorIdent = properties.value("colorIdenity").toString();
+ if (allColorIdent.size() > 1) {
+ sortAndReduceColors(allColorIdent);
+ properties.insert("coloridentity", allColorIdent);
+ }
// DETECT CARD POSITIONING INFO
@@ -178,7 +183,7 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList splitCards;
QString ptSeparator("/");
QVariantMap card;
- QString layout, name, text, colors, maintype, power, toughness;
+ QString layout, name, text, colors, colorIdentity, maintype, power, toughness;
bool isToken;
QStringList additionalNames;
QVariantHash properties;
@@ -232,6 +237,11 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList
+
+CardDatabase *db;
+
+#define Query(name, card, query, match) \
+TEST_F(CardQuery, name) {\
+ ASSERT_EQ(FilterString(query).check(card), match);\
+}
+
+
+namespace
+{
+
+class CardQuery : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ cat = db->getCardBySimpleName("Cat");
+ }
+
+ // void TearDown() override {}
+
+ CardData cat;
+};
+
+ Query(Empty, cat, "", true)
+ Query(Typing, cat, "t", true)
+
+ Query(NonMatchingType, cat, "t:kithkin", false)
+ Query(MatchingType, cat, "t:creature", true)
+ Query(Not1, cat, "not t:kithkin", true)
+ Query(Not2, cat, "not t:creature", false)
+ Query(Case, cat, "t:cReAtUrE", true)
+
+ Query(And, cat, "t:creature t:creature", true)
+ Query(And2, cat, "t:creature t:sorcery", false)
+
+ Query(Or, cat, "t:bat or t:creature", true)
+
+ Query(Cmc1, cat, "cmc=2", true)
+ Query(Cmc2, cat, "cmc>3", false)
+ Query(Cmc3, cat, "cmc>1", true)
+
+ Query(Quotes, cat, "t:\"creature\"", true);
+
+ Query(Field, cat, "pt:\"3/3\"", true)
+
+ Query(Color1, cat, "c:g", true);
+ Query(Color2, cat, "c:gw", true);
+ Query(Color3, cat, "c!g", true);
+ Query(Color4, cat, "c!gw", false);
+
+} // namespace
+
+int main(int argc, char **argv)
+{
+ settingsCache = new SettingsCache;
+ db = new CardDatabase;
+ db->loadCardDatabases();
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp
new file mode 100644
index 00000000..64c8be32
--- /dev/null
+++ b/tests/carddatabase/mocks.cpp
@@ -0,0 +1,54 @@
+
+#include "mocks.h"
+
+void CardDatabaseSettings::setSortKey(QString /* shortName */, unsigned int /* sortKey */){};
+void CardDatabaseSettings::setEnabled(QString /* shortName */, bool /* enabled */){};
+void CardDatabaseSettings::setIsKnown(QString /* shortName */, bool /* isknown */){};
+unsigned int CardDatabaseSettings::getSortKey(QString /* shortName */)
+{
+ return 0;
+};
+bool CardDatabaseSettings::isEnabled(QString /* shortName */)
+{
+ return true;
+};
+bool CardDatabaseSettings::isKnown(QString /* shortName */)
+{
+ return true;
+};
+
+SettingsCache::SettingsCache()
+{
+ cardDatabaseSettings = new CardDatabaseSettings();
+};
+SettingsCache::~SettingsCache()
+{
+ delete cardDatabaseSettings;
+};
+QString SettingsCache::getCustomCardDatabasePath() const
+{
+ return QString("%1/customsets/").arg(CARDDB_DATADIR);
+}
+QString SettingsCache::getCardDatabasePath() const
+{
+ return QString("%1/cards.xml").arg(CARDDB_DATADIR);
+}
+QString SettingsCache::getTokenDatabasePath() const
+{
+ return QString("%1/tokens.xml").arg(CARDDB_DATADIR);
+}
+QString SettingsCache::getSpoilerCardDatabasePath() const
+{
+ return QString("%1/spoiler.xml").arg(CARDDB_DATADIR);
+}
+CardDatabaseSettings &SettingsCache::cardDatabase() const
+{
+ return *cardDatabaseSettings;
+}
+
+
+void PictureLoader::clearPixmapCache(CardInfoPtr /* card */)
+{
+}
+
+SettingsCache *settingsCache;
diff --git a/tests/carddatabase/carddatabase_test.h b/tests/carddatabase/mocks.h
similarity index 96%
rename from tests/carddatabase/carddatabase_test.h
rename to tests/carddatabase/mocks.h
index 07793b85..c6286fc1 100644
--- a/tests/carddatabase/carddatabase_test.h
+++ b/tests/carddatabase/mocks.h
@@ -10,6 +10,7 @@
#define SETTINGSCACHE_H
+
class CardDatabaseSettings
{
public:
@@ -40,6 +41,8 @@ signals:
void cardDatabasePathChanged();
};
+extern SettingsCache *settingsCache;
+
#define PICTURELOADER_H
class PictureLoader
|