Filter Strings for Deck Editor search (#3582)
* Add MagicCards.info like fitler parser. * Use FilterString whenever one of [:=<>] is in the edit box. * Opts * Opt * - Capture errors - Allow querying any property by full name * clang format * Update cockatrice/src/filter_string.cpp Co-Authored-By: basicer <basicer@basicer.com> * - Some refactoring for clarity - More filters - Add filter help * Clangify * Add icon * Fix test name * Remove stay debug * - Add Rarity filter - Make " trigger filter string mode * You have to pass both filter types * clangify * - Allow filtering by legality - Import legality into card.xml * Add format filter to filtertree * More color search options * RIP extended * More fixes * Fix c:m * set syntax help parent * Fix warning * add additional explanations to syntax help * Allow multiple ands/ors to be chained * Cleanup and refactor Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com> * Move utility into guards Signed-off-by: Zach Halpern <ZaHalpern+github@gmail.com> * I heard you like refactors so I put a refactor inside your refactor (#3594) * I heard you like refactors so I put a refactor inside your refactor so you can refactor while you refactor * clangify * Update tab_deck_editor.h
This commit is contained in:
parent
4427ad1451
commit
eb60fec8e2
24 changed files with 780 additions and 122 deletions
|
@ -123,6 +123,7 @@ SET(cockatrice_SOURCES
|
||||||
src/carddbparser/carddatabaseparser.cpp
|
src/carddbparser/carddatabaseparser.cpp
|
||||||
src/carddbparser/cockatricexml3.cpp
|
src/carddbparser/cockatricexml3.cpp
|
||||||
src/carddbparser/cockatricexml4.cpp
|
src/carddbparser/cockatricexml4.cpp
|
||||||
|
src/filter_string.cpp
|
||||||
${VERSION_STRING_CPP}
|
${VERSION_STRING_CPP}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
<file>resources/icons/delete.svg</file>
|
<file>resources/icons/delete.svg</file>
|
||||||
<file>resources/icons/forgot_password.svg</file>
|
<file>resources/icons/forgot_password.svg</file>
|
||||||
<file>resources/icons/increment.svg</file>
|
<file>resources/icons/increment.svg</file>
|
||||||
|
<file>resources/icons/info.svg</file>
|
||||||
<file>resources/icons/lock.svg</file>
|
<file>resources/icons/lock.svg</file>
|
||||||
<file>resources/icons/not_ready_start.svg</file>
|
<file>resources/icons/not_ready_start.svg</file>
|
||||||
<file>resources/icons/pencil.svg</file>
|
<file>resources/icons/pencil.svg</file>
|
||||||
|
@ -360,5 +361,6 @@
|
||||||
<file>resources/tips/images/themes.png</file>
|
<file>resources/tips/images/themes.png</file>
|
||||||
<file>resources/tips/images/tip_of_the_day.png</file>
|
<file>resources/tips/images/tip_of_the_day.png</file>
|
||||||
|
|
||||||
|
<file>resources/help/search.md</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
60
cockatrice/resources/help/search.md
Normal file
60
cockatrice/resources/help/search.md
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
## Syntax Help
|
||||||
|
-----
|
||||||
|
The search bar recognizes a set of special commands similar to some other card databases. Here is a list with examples. Each entry can be clicked to test the query and has a small explanation. Note that all searches are case insensitive.
|
||||||
|
<dl>
|
||||||
|
<dt>Name:</dt>
|
||||||
|
<dd>[birds of paradise](#birds of paradise) <small>(Any card name containing the words birds, of, and paradise)</small></dd>
|
||||||
|
<dd>["birds of paradise"](#%22birds of paradise%22) <small>(Any card name containing the exact phrase "birds of paradise")</small></dd>
|
||||||
|
|
||||||
|
<dt>Rules Text (<u>O</u>racle):</dt>
|
||||||
|
<dd>[o:flying](#o:flying) <small>(Any card text that has the word flying)</small></dd>
|
||||||
|
<dd>[o:"first strike"](#o:%22first strike%22) <small>(Any card text that has the exact phrase "first strike")</small></dd>
|
||||||
|
<dd>[o:"{T}" o:"add one mana of any color"](#o:%22{T}%22 o:%22add one mana of any color%22) <small>(Any card text that has a tap symbol and the phrase "add one mana of any color")</small></dd>
|
||||||
|
|
||||||
|
<dt><u>T</u>ypes:</dt>
|
||||||
|
<dd>[t:angel](#t:angel) <small>(Any card with the type angel)</small></dd>
|
||||||
|
<dd>[t:angel t:legendary](#t:angel t:legendary) <small>(Any angel that's also legendary)</small></dd>
|
||||||
|
<dd>[t:basic](#t:basic) <small>(Any card with the type basic)</small></dd>
|
||||||
|
<dd>[t:arcane t:instant](#t:arcane t:instant) <small>(Any card with the types arcane and instant)</small></dd>
|
||||||
|
|
||||||
|
<dt><u>C</u>olors:</dt>
|
||||||
|
<dd>[c:w](#c:w) <small>(Any card that is white)</small></dd>
|
||||||
|
<dd>[c:wu](#c:wu) <small>(Any card that is white or blue)</small></dd>
|
||||||
|
<dd>[c:wum](#c:wum) <small>(Any card that is white or blue, and multicolored)</small></dd>
|
||||||
|
<!--
|
||||||
|
<dd>[c!w](#c!w) <small>(Cards that are only white)</small></dd>
|
||||||
|
<dd>[c!wu](#c!wu) <small>(Cards that are only white or blue, or both)</small></dd>
|
||||||
|
<dd>[c!wum](#c!wum) <small>(Cards that are only white and blue, and multicolored)</small></dd>
|
||||||
|
<dd>[c=wubrg](#c%3Dwubrg) <small>(Cards that are all five colors)</small></dd>
|
||||||
|
-->
|
||||||
|
<dd>[c:c](#c:c) <small>(Any colorless card)</small></dd>
|
||||||
|
|
||||||
|
<dt><u>Pow</u>er, <u>Tou</u>ghness, <u>C</u>onverted <u>M</u>ana <u>C</u>ost:</dt>
|
||||||
|
<dd>[tou:1](#tou:1) <small>(Any card with a toughness of 1)</small></dd>
|
||||||
|
<dd>[pow>=8](#pow>=8) <small>(Any card with a power greater than or equal to 8)</small></dd>
|
||||||
|
<dd>[cmc=7](#cmc=7) <small>(Any card with a converted mana cost equal to 7)</small></dd>
|
||||||
|
|
||||||
|
<dt><u>R</u>arity:</dt>
|
||||||
|
<dd>[r:mythic](#r:mythic) <small>(Any card that has the mythic-rare rarity)</small></dd>
|
||||||
|
|
||||||
|
<dt><u>F</u>ormat:</dt>
|
||||||
|
<dd>[f:standard](#f:standard) <small>(Any card that can be played in standard)</small></dd>
|
||||||
|
<dd>[banned:modern](#banned:modern) <small>(Any card that is banned in modern)</small></dd>
|
||||||
|
<dd>[restricted:vintage](#restricted:vintage) <small>(Any card that is restricted in vintage)</small></dd>
|
||||||
|
<dd>[legal:pauper](#legal:pauper) <small>(Any card that is legal in pauper)</small></dd>
|
||||||
|
|
||||||
|
<dt><u>E</u>dition:</dt>
|
||||||
|
<dd>[set:lea](#set:lea) <small>(Cards that appear in Alpha, which has the set code LEA)</small></dd>
|
||||||
|
<dd>[e:lea,leb](#e:lea,leb) <small>(Cards that appear in Alpha or Beta)</small></dd>
|
||||||
|
<dd><a href="#e:lea,leb -(e:lea e:leb)">e:lea,leb -(e:lea e:leb)</a> <small>(Cards that appear in Alpha or Beta but not in both editions)</small></dd>
|
||||||
|
|
||||||
|
<dt>Inverse:</dt>
|
||||||
|
<dd>[c:wu -c:m](#c:wu -c:m) <small>(Any card that is white or blue, but not multicolored)</small></dd>
|
||||||
|
|
||||||
|
<dt>Branching:</dt>
|
||||||
|
<dd>[t:sliver or o:changeling](#t:sliver or o:changeling) <small>(Any card that is either a sliver or has changeling)</small></dd>
|
||||||
|
|
||||||
|
<dt>Grouping:</dt>
|
||||||
|
<dd><a href="#t:angel -(angel or c:w)">t:angel -(angel or c:w)</a> <small>(Any angel that doesn't have angel in its name and isn't white)</small></dd>
|
||||||
|
|
||||||
|
</dl>
|
43
cockatrice/resources/icons/info.svg
Normal file
43
cockatrice/resources/icons/info.svg
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="100"
|
||||||
|
height="100"
|
||||||
|
id="svg2858"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.5 r10040"
|
||||||
|
sodipodi:docname="info.svg">
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="Triangle"
|
||||||
|
style="display:inline">
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:#aaaaaa;fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
|
||||||
|
id="path3758-1"
|
||||||
|
sodipodi:cx="53.900002"
|
||||||
|
sodipodi:cy="78.5"
|
||||||
|
sodipodi:rx="46.5"
|
||||||
|
sodipodi:ry="46.5"
|
||||||
|
d="M 100.4,78.5 A 46.5,46.5 0 1 1 7.4000015,78.5 46.5,46.5 0 1 1 100.4,78.5 z"
|
||||||
|
transform="matrix(1.05866,0,0,1.05866,-7.0617752,-32.704809)" />
|
||||||
|
<g
|
||||||
|
style="font-size:133.49534607px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.06498075;stroke-opacity:1;font-family:'Magic:the Gathering';-inkscape-font-specification:'Magic:the Gathering'"
|
||||||
|
id="text3759">
|
||||||
|
<path
|
||||||
|
d="m 46.854005,16.085692 c 0.977733,-2.411704 1.683885,-4.193378 2.118456,-5.345028 0.369355,-1.1514901 0.771318,-1.9988718 1.205891,-2.5421482 0.456264,-0.4344718 1.347101,-0.825571 2.672514,-1.1732989 1.23846,-0.2823765 2.705082,-0.4670623 4.399871,-0.5540578 0.369346,8.46e-5 0.727854,8.46e-5 1.075524,0 1.694737,8.46e-5 2.672485,0.5758696 2.933248,1.7273567 -3e-5,0.1956326 -3e-5,0.3803183 0,0.5540579 -3e-5,1.3254743 -0.662726,2.9224633 -1.98809,4.7909703 -1.520969,2.238035 -3.43301,4.24785 -5.736128,6.029453 -2.32489,1.760018 -4.551983,2.781222 -6.681286,3.063614 -1.260224,-0.543125 -1.977239,-1.075454 -2.151048,-1.59699 -1.3e-5,-0.08684 -1.3e-5,-0.184616 0,-0.293325 -1.3e-5,-0.434484 0.228128,-1.010269 0.684424,-1.727357 0.521453,-0.890765 1.010327,-1.868513 1.466624,-2.933247 z M 31.372977,37.856906 c 2.650785,-0.716962 6.159814,-1.651255 10.527099,-2.802881 4.280354,-1.173242 7.398284,-1.803346 9.3538,-1.890315 0.08689,5.8e-5 0.173803,5.8e-5 0.260733,0 1.06464,5.8e-5 1.82511,0.358566 2.281415,1.075524 0.260711,0.630161 0.391077,1.520998 0.391099,2.672514 -2.2e-5,0.260787 -2.2e-5,0.532384 0,0.814791 -0.173844,1.586178 -0.260755,2.781204 -0.260733,3.58508 l 0,39.533656 c 1.238459,1e-5 2.976678,-0.673549 5.214662,-2.020681 1.673009,-0.977736 2.868034,-1.46661 3.58508,-1.466624 0.173791,1.4e-5 0.304158,0.04347 0.3911,0.130367 0.543162,0.369385 0.814759,0.771348 0.814791,1.20589 -3.2e-5,1.151583 -1.868617,2.66166 -5.605762,4.530238 -1.238507,0.717023 -3.063637,1.922912 -5.475395,3.617671 -2.49871,1.781679 -4.769259,3.204846 -6.811652,4.269505 -0.347658,0.347644 -0.923443,0.662696 -1.727357,0.945157 -0.260745,0.08691 -0.532342,0.130366 -0.814791,0.130367 -0.521477,-10e-7 -1.010351,-0.271598 -1.466623,-0.814791 C 41.856611,91.111641 41.7697,81.594892 41.76971,62.8221 c -1e-5,-1.955468 -1e-5,-4.008739 0,-6.15982 -1e-5,-2.129283 -0.08692,-4.128235 -0.260734,-5.996861 -0.260742,-1.781634 -0.706161,-3.204801 -1.336257,-4.269504 -0.717023,-1.347075 -1.60786,-2.368279 -2.672514,-3.063614 -1.151576,-0.630057 -2.357465,-1.260161 -3.617672,-1.890315 -1.325394,-0.521416 -2.161911,-1.053746 -2.509556,-1.59699 -0.195549,-0.173771 -0.293324,-0.434504 -0.293324,-0.7822 -0.08691,-0.369319 0.01086,-0.771282 0.293324,-1.20589 z"
|
||||||
|
style=""
|
||||||
|
id="path2993" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -21,7 +21,6 @@ class CardRelation;
|
||||||
class ICardDatabaseParser;
|
class ICardDatabaseParser;
|
||||||
|
|
||||||
typedef QMap<QString, QString> QStringMap;
|
typedef QMap<QString, QString> QStringMap;
|
||||||
typedef QMap<QString, int> MuidMap;
|
|
||||||
typedef QSharedPointer<CardInfo> CardInfoPtr;
|
typedef QSharedPointer<CardInfo> CardInfoPtr;
|
||||||
typedef QSharedPointer<CardSet> CardSetPtr;
|
typedef QSharedPointer<CardSet> CardSetPtr;
|
||||||
typedef QMap<QString, CardInfoPerSet> CardInfoPerSetMap;
|
typedef QMap<QString, CardInfoPerSet> CardInfoPerSetMap;
|
||||||
|
@ -248,6 +247,10 @@ public:
|
||||||
properties.insert(_name, _value);
|
properties.insert(_name, _value);
|
||||||
emit cardInfoChanged(smartThis);
|
emit cardInfoChanged(smartThis);
|
||||||
}
|
}
|
||||||
|
bool hasProperty(const QString &propertyName) const
|
||||||
|
{
|
||||||
|
return properties.contains(propertyName);
|
||||||
|
}
|
||||||
const CardInfoPerSetMap &getSets() const
|
const CardInfoPerSetMap &getSets() const
|
||||||
{
|
{
|
||||||
return sets;
|
return sets;
|
||||||
|
|
|
@ -143,12 +143,16 @@ void CardDatabaseModel::cardRemoved(CardInfoPtr card)
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent) : QSortFilterProxyModel(parent), isToken(ShowAll)
|
CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent)
|
||||||
|
: QSortFilterProxyModel(parent), isToken(ShowAll), filterString(nullptr)
|
||||||
{
|
{
|
||||||
filterTree = nullptr;
|
filterTree = nullptr;
|
||||||
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||||
setSortCaseSensitivity(Qt::CaseInsensitive);
|
setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||||
|
|
||||||
|
dirtyTimer.setSingleShot(true);
|
||||||
|
connect(&dirtyTimer, &QTimer::timeout, this, &CardDatabaseDisplayModel::invalidate);
|
||||||
|
|
||||||
loadedRowCount = 0;
|
loadedRowCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +289,13 @@ bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex
|
||||||
if (((isToken == ShowTrue) && !info->getIsToken()) || ((isToken == ShowFalse) && info->getIsToken()))
|
if (((isToken == ShowTrue) && !info->getIsToken()) || ((isToken == ShowFalse) && info->getIsToken()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (filterString != nullptr) {
|
||||||
|
if (filterTree != nullptr && !filterTree->acceptsCard(info)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return filterString->check(info);
|
||||||
|
}
|
||||||
|
|
||||||
return rowMatchesCardName(info);
|
return rowMatchesCardName(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
#define CARDDATABASEMODEL_H
|
#define CARDDATABASEMODEL_H
|
||||||
|
|
||||||
#include "carddatabase.h"
|
#include "carddatabase.h"
|
||||||
|
#include "filter_string.h"
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
class FilterTree;
|
class FilterTree;
|
||||||
|
|
||||||
|
@ -67,11 +69,12 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FilterBool isToken;
|
FilterBool isToken;
|
||||||
QString cardNameBeginning, cardName, cardText;
|
QString cardName, cardText;
|
||||||
QString searchTerm;
|
|
||||||
QSet<QString> cardNameSet, cardTypes, cardColors;
|
QSet<QString> cardNameSet, cardTypes, cardColors;
|
||||||
FilterTree *filterTree;
|
FilterTree *filterTree;
|
||||||
|
FilterString *filterString;
|
||||||
int loadedRowCount;
|
int loadedRowCount;
|
||||||
|
QTimer dirtyTimer;
|
||||||
|
|
||||||
/** The translation table that will be used for sanitizeCardName. */
|
/** The translation table that will be used for sanitizeCardName. */
|
||||||
static QMap<wchar_t, wchar_t> characterTranslation;
|
static QMap<wchar_t, wchar_t> characterTranslation;
|
||||||
|
@ -82,41 +85,33 @@ public:
|
||||||
void setIsToken(FilterBool _isToken)
|
void setIsToken(FilterBool _isToken)
|
||||||
{
|
{
|
||||||
isToken = _isToken;
|
isToken = _isToken;
|
||||||
invalidate();
|
dirty();
|
||||||
}
|
|
||||||
void setCardNameBeginning(const QString &_beginning)
|
|
||||||
{
|
|
||||||
cardNameBeginning = _beginning;
|
|
||||||
invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCardName(const QString &_cardName)
|
void setCardName(const QString &_cardName)
|
||||||
{
|
{
|
||||||
|
if (filterString != nullptr) {
|
||||||
|
delete filterString;
|
||||||
|
filterString = nullptr;
|
||||||
|
}
|
||||||
cardName = sanitizeCardName(_cardName, characterTranslation);
|
cardName = sanitizeCardName(_cardName, characterTranslation);
|
||||||
invalidate();
|
dirty();
|
||||||
|
}
|
||||||
|
void setStringFilter(const QString &_src)
|
||||||
|
{
|
||||||
|
delete filterString;
|
||||||
|
filterString = new FilterString(_src);
|
||||||
|
dirty();
|
||||||
}
|
}
|
||||||
void setCardNameSet(const QSet<QString> &_cardNameSet)
|
void setCardNameSet(const QSet<QString> &_cardNameSet)
|
||||||
{
|
{
|
||||||
cardNameSet = _cardNameSet;
|
cardNameSet = _cardNameSet;
|
||||||
invalidate();
|
dirty();
|
||||||
}
|
}
|
||||||
void setSearchTerm(const QString &_searchTerm)
|
|
||||||
|
void dirty()
|
||||||
{
|
{
|
||||||
searchTerm = _searchTerm;
|
dirtyTimer.start(20);
|
||||||
}
|
|
||||||
void setCardText(const QString &_cardText)
|
|
||||||
{
|
|
||||||
cardText = _cardText;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
void setCardTypes(const QSet<QString> &_cardTypes)
|
|
||||||
{
|
|
||||||
cardTypes = _cardTypes;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
void setCardColors(const QSet<QString> &_cardColors)
|
|
||||||
{
|
|
||||||
cardColors = _cardColors;
|
|
||||||
invalidate();
|
|
||||||
}
|
}
|
||||||
void clearFilterAll();
|
void clearFilterAll();
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
|
@ -41,6 +41,8 @@ const QString CardFilter::attrName(Attr a)
|
||||||
return tr("Toughness");
|
return tr("Toughness");
|
||||||
case AttrLoyalty:
|
case AttrLoyalty:
|
||||||
return tr("Loyalty");
|
return tr("Loyalty");
|
||||||
|
case AttrFormat:
|
||||||
|
return tr("Format");
|
||||||
default:
|
default:
|
||||||
return QString("");
|
return QString("");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
class CardFilter : public QObject
|
class CardFilter : public QObject
|
||||||
{
|
{
|
||||||
|
@ -18,7 +19,7 @@ public:
|
||||||
TypeEnd
|
TypeEnd
|
||||||
};
|
};
|
||||||
|
|
||||||
/* if you add an atribute here you also need to
|
/* if you add an attribute here you also need to
|
||||||
* add its string representation in attrName */
|
* add its string representation in attrName */
|
||||||
enum Attr
|
enum Attr
|
||||||
{
|
{
|
||||||
|
@ -33,6 +34,7 @@ public:
|
||||||
AttrText,
|
AttrText,
|
||||||
AttrTough,
|
AttrTough,
|
||||||
AttrType,
|
AttrType,
|
||||||
|
AttrFormat,
|
||||||
AttrEnd
|
AttrEnd
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,7 +44,7 @@ private:
|
||||||
enum Attr a;
|
enum Attr a;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CardFilter(QString term, Type type, Attr attr) : trm(term), t(type), a(attr){};
|
CardFilter(QString &term, Type type, Attr attr) : trm(term), t(type), a(attr){};
|
||||||
|
|
||||||
Type type() const
|
Type type() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,7 +16,7 @@ CardInfoText::CardInfoText(QWidget *parent) : QFrame(parent), info(nullptr)
|
||||||
textLabel = new QTextEdit();
|
textLabel = new QTextEdit();
|
||||||
textLabel->setReadOnly(true);
|
textLabel->setReadOnly(true);
|
||||||
|
|
||||||
QGridLayout *grid = new QGridLayout(this);
|
auto *grid = new QGridLayout(this);
|
||||||
grid->addWidget(nameLabel, 0, 0);
|
grid->addWidget(nameLabel, 0, 0);
|
||||||
grid->addWidget(textLabel, 1, 0, -1, 2);
|
grid->addWidget(textLabel, 1, 0, -1, 2);
|
||||||
grid->setRowStretch(1, 1);
|
grid->setRowStretch(1, 1);
|
||||||
|
@ -39,6 +39,8 @@ void CardInfoText::setCard(CardInfoPtr card)
|
||||||
|
|
||||||
QStringList cardProps = card->getProperties();
|
QStringList cardProps = card->getProperties();
|
||||||
foreach (QString key, cardProps) {
|
foreach (QString key, cardProps) {
|
||||||
|
if (key.contains("-"))
|
||||||
|
continue;
|
||||||
QString keyText = Mtg::getNicePropertyName(key).toHtmlEscaped() + ":";
|
QString keyText = Mtg::getNicePropertyName(key).toHtmlEscaped() + ":";
|
||||||
text +=
|
text +=
|
||||||
QString("<tr><td>%1</td><td></td><td>%2</td></tr>").arg(keyText, card->getProperty(key).toHtmlEscaped());
|
QString("<tr><td>%1</td><td></td><td>%2</td></tr>").arg(keyText, card->getProperty(key).toHtmlEscaped());
|
||||||
|
@ -46,16 +48,16 @@ void CardInfoText::setCard(CardInfoPtr card)
|
||||||
|
|
||||||
auto relatedCards = card->getRelatedCards();
|
auto relatedCards = card->getRelatedCards();
|
||||||
auto reverserelatedCards2Me = card->getReverseRelatedCards2Me();
|
auto reverserelatedCards2Me = card->getReverseRelatedCards2Me();
|
||||||
if (relatedCards.size() || reverserelatedCards2Me.size()) {
|
if (!relatedCards.empty() || !reverserelatedCards2Me.empty()) {
|
||||||
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>").arg(tr("Related cards:"));
|
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>").arg(tr("Related cards:"));
|
||||||
|
|
||||||
for (int i = 0; i < relatedCards.size(); ++i) {
|
for (auto *relatedCard : relatedCards) {
|
||||||
QString tmp = relatedCards.at(i)->getName().toHtmlEscaped();
|
QString tmp = relatedCard->getName().toHtmlEscaped();
|
||||||
text += "<a href=\"" + tmp + "\">" + tmp + "</a><br>";
|
text += "<a href=\"" + tmp + "\">" + tmp + "</a><br>";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < reverserelatedCards2Me.size(); ++i) {
|
for (auto *i : reverserelatedCards2Me) {
|
||||||
QString tmp = reverserelatedCards2Me.at(i)->getName().toHtmlEscaped();
|
QString tmp = i->getName().toHtmlEscaped();
|
||||||
text += "<a href=\"" + tmp + "\">" + tmp + "</a><br>";
|
text += "<a href=\"" + tmp + "\">" + tmp + "</a><br>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ private:
|
||||||
CardInfoPtr info;
|
CardInfoPtr info;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CardInfoText(QWidget *parent = 0);
|
explicit CardInfoText(QWidget *parent = nullptr);
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
void setInvalidCardName(const QString &cardName);
|
void setInvalidCardName(const QString &cardName);
|
||||||
|
|
||||||
|
|
352
cockatrice/src/filter_string.cpp
Normal file
352
cockatrice/src/filter_string.cpp
Normal file
|
@ -0,0 +1,352 @@
|
||||||
|
#include "filter_string.h"
|
||||||
|
#include "../../common/lib/peglib.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
#include <cmath>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
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 / ["] <NonQuote*> ["]
|
||||||
|
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<Filter>() : 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<Filter>()(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<Filter>()(x))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
search["SomewhatComplexQueryPart"] = passthru;
|
||||||
|
search["QueryPart"] = passthru;
|
||||||
|
search["NotQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||||
|
Filter dependent = sv[0].get<Filter>();
|
||||||
|
return [=](CardData x) -> bool { return !dependent(x); };
|
||||||
|
};
|
||||||
|
search["TypeQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||||
|
StringMatcher matcher = sv[0].get<StringMatcher>();
|
||||||
|
return [=](CardData x) -> bool { return matcher(x->getCardType()); };
|
||||||
|
};
|
||||||
|
search["SetQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||||
|
StringMatcher matcher = sv[0].get<StringMatcher>();
|
||||||
|
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<StringMatcher>();
|
||||||
|
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<QString>();
|
||||||
|
return [=](CardData x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == "legal"; };
|
||||||
|
} else {
|
||||||
|
QString format = sv[1].get<QString>();
|
||||||
|
QString legality = sv[0].get<QString>();
|
||||||
|
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<QString>();
|
||||||
|
return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); };
|
||||||
|
} else {
|
||||||
|
auto target = sv[0].get<QStringList>();
|
||||||
|
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<QStringList>();
|
||||||
|
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<QString>();
|
||||||
|
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<QString>());
|
||||||
|
}
|
||||||
|
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<int>();
|
||||||
|
auto op = sv[0].get<QString>();
|
||||||
|
|
||||||
|
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<QString>();
|
||||||
|
return [=](const QString &s) { return s.QString::contains(target, Qt::CaseInsensitive); };
|
||||||
|
};
|
||||||
|
|
||||||
|
search["OracleQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||||
|
StringMatcher matcher = sv[0].get<StringMatcher>();
|
||||||
|
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<char>();
|
||||||
|
}
|
||||||
|
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<NumberMatcher>();
|
||||||
|
return [=](CardData x) -> bool { return matcher(x->getProperty("cmc").toInt()); };
|
||||||
|
};
|
||||||
|
search["PowerQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||||
|
NumberMatcher matcher = sv[0].get<NumberMatcher>();
|
||||||
|
return [=](CardData x) -> bool { return matcher(x->getPowTough().split("/")[0].toInt()); };
|
||||||
|
};
|
||||||
|
search["ToughnessQuery"] = [](const peg::SemanticValues &sv) -> Filter {
|
||||||
|
NumberMatcher matcher = sv[0].get<NumberMatcher>();
|
||||||
|
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<QString>();
|
||||||
|
if (sv.choice() == 0) {
|
||||||
|
StringMatcher matcher = sv[1].get<StringMatcher>();
|
||||||
|
return [=](CardData x) -> bool { return x->hasProperty(field) ? matcher(x->getProperty(field)) : false; };
|
||||||
|
} else {
|
||||||
|
NumberMatcher matcher = sv[1].get<NumberMatcher>();
|
||||||
|
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<StringMatcher>();
|
||||||
|
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<char>() : *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; };
|
||||||
|
}
|
||||||
|
}
|
48
cockatrice/src/filter_string.h
Normal file
48
cockatrice/src/filter_string.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef FILTER_STRING_H
|
||||||
|
#define FILTER_STRING_H
|
||||||
|
|
||||||
|
#include "carddatabase.h"
|
||||||
|
#include "filtertree.h"
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
|
#include <QString>
|
||||||
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
typedef CardInfoPtr CardData;
|
||||||
|
typedef std::function<bool(const CardData &)> Filter;
|
||||||
|
typedef std::function<bool(const QString &)> StringMatcher;
|
||||||
|
typedef std::function<bool(int)> NumberMatcher;
|
||||||
|
|
||||||
|
namespace peg
|
||||||
|
{
|
||||||
|
template <typename Annotation> struct AstBase;
|
||||||
|
struct EmptyType;
|
||||||
|
typedef AstBase<EmptyType> 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
|
|
@ -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
|
bool FilterItem::acceptLoyalty(const CardInfoPtr info) const
|
||||||
{
|
{
|
||||||
if (info->getLoyalty().isEmpty()) {
|
if (info->getLoyalty().isEmpty()) {
|
||||||
|
@ -400,6 +405,8 @@ bool FilterItem::acceptCardAttr(const CardInfoPtr info, CardFilter::Attr attr) c
|
||||||
return acceptPowerToughness(info, attr);
|
return acceptPowerToughness(info, attr);
|
||||||
case CardFilter::AttrLoyalty:
|
case CardFilter::AttrLoyalty:
|
||||||
return acceptLoyalty(info);
|
return acceptLoyalty(info);
|
||||||
|
case CardFilter::AttrFormat:
|
||||||
|
return acceptFormat(info);
|
||||||
default:
|
default:
|
||||||
return true; /* ignore this attribute */
|
return true; /* ignore this attribute */
|
||||||
}
|
}
|
||||||
|
@ -439,16 +446,6 @@ FilterItemList *FilterTree::attrTypeList(CardFilter::Attr attr, CardFilter::Type
|
||||||
return attrLogicMap(attr)->typeList(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)
|
FilterTreeNode *FilterTree::termNode(CardFilter::Attr attr, CardFilter::Type type, const QString &term)
|
||||||
{
|
{
|
||||||
return attrTypeList(attr, type)->termNode(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());
|
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
|
bool FilterTree::testAttr(const CardInfoPtr info, const LogicMap *lm) const
|
||||||
{
|
{
|
||||||
const FilterItemList *fil;
|
const FilterItemList *fil;
|
||||||
|
|
|
@ -208,6 +208,7 @@ public:
|
||||||
bool acceptLoyalty(CardInfoPtr info) const;
|
bool acceptLoyalty(CardInfoPtr info) const;
|
||||||
bool acceptRarity(CardInfoPtr info) const;
|
bool acceptRarity(CardInfoPtr info) const;
|
||||||
bool acceptCardAttr(CardInfoPtr info, CardFilter::Attr attr) const;
|
bool acceptCardAttr(CardInfoPtr info, CardFilter::Attr attr) const;
|
||||||
|
bool acceptFormat(CardInfoPtr info) const;
|
||||||
bool relationCheck(int cardInfo) const;
|
bool relationCheck(int cardInfo) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -252,11 +253,10 @@ private:
|
||||||
public:
|
public:
|
||||||
FilterTree();
|
FilterTree();
|
||||||
~FilterTree() override;
|
~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(CardFilter::Attr attr, CardFilter::Type type, const QString &term);
|
||||||
FilterTreeNode *termNode(const CardFilter *f);
|
FilterTreeNode *termNode(const CardFilter *f);
|
||||||
FilterTreeNode *attrTypeNode(CardFilter::Attr attr, CardFilter::Type type);
|
|
||||||
const QString text() const override
|
const QString text() const override
|
||||||
{
|
{
|
||||||
return QString("root");
|
return QString("root");
|
||||||
|
|
|
@ -21,6 +21,7 @@ QString const ManaCost("manacost");
|
||||||
QString const PowTough("pt");
|
QString const PowTough("pt");
|
||||||
QString const Side("side");
|
QString const Side("side");
|
||||||
QString const Layout("layout");
|
QString const Layout("layout");
|
||||||
|
QString const ColorIdentity("coloridentity");
|
||||||
|
|
||||||
inline static const QString getNicePropertyName(QString key)
|
inline static const QString getNicePropertyName(QString key)
|
||||||
{
|
{
|
||||||
|
@ -42,6 +43,8 @@ inline static const QString getNicePropertyName(QString key)
|
||||||
return QCoreApplication::translate("Mtg", "Side");
|
return QCoreApplication::translate("Mtg", "Side");
|
||||||
if (key == Layout)
|
if (key == Layout)
|
||||||
return QCoreApplication::translate("Mtg", "Layout");
|
return QCoreApplication::translate("Mtg", "Layout");
|
||||||
|
if (key == ColorIdentity)
|
||||||
|
return QCoreApplication::translate("Mtg", "Color Identity");
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
}; // namespace Mtg
|
}; // namespace Mtg
|
||||||
|
|
|
@ -33,8 +33,10 @@
|
||||||
#include <QPrintPreviewDialog>
|
#include <QPrintPreviewDialog>
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QSignalMapper>
|
#include <QSignalMapper>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
|
#include <QTextBrowser>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
@ -349,6 +351,7 @@ void TabDeckEditor::createCentralFrame()
|
||||||
searchEdit->setPlaceholderText(tr("Search by card name"));
|
searchEdit->setPlaceholderText(tr("Search by card name"));
|
||||||
searchEdit->setClearButtonEnabled(true);
|
searchEdit->setClearButtonEnabled(true);
|
||||||
searchEdit->addAction(QPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
searchEdit->addAction(QPixmap("theme:icons/search"), QLineEdit::LeadingPosition);
|
||||||
|
auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition);
|
||||||
searchEdit->installEventFilter(&searchKeySignals);
|
searchEdit->installEventFilter(&searchKeySignals);
|
||||||
|
|
||||||
setFocusProxy(searchEdit);
|
setFocusProxy(searchEdit);
|
||||||
|
@ -363,6 +366,7 @@ void TabDeckEditor::createCentralFrame()
|
||||||
connect(&searchKeySignals, SIGNAL(onCtrlAltLBracket()), this, SLOT(actDecrementCardFromSideboard()));
|
connect(&searchKeySignals, SIGNAL(onCtrlAltLBracket()), this, SLOT(actDecrementCardFromSideboard()));
|
||||||
connect(&searchKeySignals, SIGNAL(onCtrlAltEnter()), this, SLOT(actAddCardToSideboard()));
|
connect(&searchKeySignals, SIGNAL(onCtrlAltEnter()), this, SLOT(actAddCardToSideboard()));
|
||||||
connect(&searchKeySignals, SIGNAL(onCtrlEnter()), this, SLOT(actAddCardToSideboard()));
|
connect(&searchKeySignals, SIGNAL(onCtrlEnter()), this, SLOT(actAddCardToSideboard()));
|
||||||
|
connect(help, &QAction::triggered, this, &TabDeckEditor::showSearchSyntaxHelp);
|
||||||
|
|
||||||
databaseModel = new CardDatabaseModel(db, true, this);
|
databaseModel = new CardDatabaseModel(db, true, this);
|
||||||
databaseModel->setObjectName("databaseModel");
|
databaseModel->setObjectName("databaseModel");
|
||||||
|
@ -700,7 +704,7 @@ void TabDeckEditor::updateCardInfoRight(const QModelIndex ¤t, const QModel
|
||||||
|
|
||||||
void TabDeckEditor::updateSearch(const QString &search)
|
void TabDeckEditor::updateSearch(const QString &search)
|
||||||
{
|
{
|
||||||
databaseDisplayModel->setCardName(search);
|
databaseDisplayModel->setStringFilter(search);
|
||||||
QModelIndexList sel = databaseView->selectionModel()->selectedRows();
|
QModelIndexList sel = databaseView->selectionModel()->selectedRows();
|
||||||
if (sel.isEmpty() && databaseDisplayModel->rowCount())
|
if (sel.isEmpty() && databaseDisplayModel->rowCount())
|
||||||
databaseView->selectionModel()->setCurrentIndex(databaseDisplayModel->index(0, 0),
|
databaseView->selectionModel()->setCurrentIndex(databaseDisplayModel->index(0, 0),
|
||||||
|
@ -1212,3 +1216,35 @@ void TabDeckEditor::setSaveStatus(bool newStatus)
|
||||||
aPrintDeck->setEnabled(newStatus);
|
aPrintDeck->setEnabled(newStatus);
|
||||||
analyzeDeckMenu->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), "<h3>\\2</h3>")
|
||||||
|
.replace(QRegularExpression("^(##)(.*)", opts), "<h2>\\2</h2>")
|
||||||
|
.replace(QRegularExpression("^(#)(.*)", opts), "<h1>\\2</h1>")
|
||||||
|
.replace(QRegularExpression("^------*", opts), "<hr />")
|
||||||
|
.replace(QRegularExpression("\\[([^\[]+)\\]\\(([^\\)]+)\\)", opts), "<a href=\'\\2\'>\\1</a>");
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ class CardDatabaseModel;
|
||||||
class CardDatabaseDisplayModel;
|
class CardDatabaseDisplayModel;
|
||||||
class DeckListModel;
|
class DeckListModel;
|
||||||
class QTreeView;
|
class QTreeView;
|
||||||
class QTableView;
|
|
||||||
class CardFrame;
|
class CardFrame;
|
||||||
class QTextEdit;
|
class QTextEdit;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
|
@ -33,10 +33,10 @@ private:
|
||||||
QTreeView *treeView;
|
QTreeView *treeView;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *event);
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SearchLineEdit() : QLineEdit(), treeView(0)
|
SearchLineEdit() : QLineEdit(), treeView(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void setTreeView(QTreeView *_treeView)
|
void setTreeView(QTreeView *_treeView)
|
||||||
|
@ -90,12 +90,13 @@ private slots:
|
||||||
void freeDocksSize();
|
void freeDocksSize();
|
||||||
void refreshShortcuts();
|
void refreshShortcuts();
|
||||||
|
|
||||||
bool eventFilter(QObject *o, QEvent *e);
|
bool eventFilter(QObject *o, QEvent *e) override;
|
||||||
void dockVisibleTriggered();
|
void dockVisibleTriggered();
|
||||||
void dockFloatingTriggered();
|
void dockFloatingTriggered();
|
||||||
void dockTopLevelChanged(bool topLevel);
|
void dockTopLevelChanged(bool topLevel);
|
||||||
void saveDbHeaderState();
|
void saveDbHeaderState();
|
||||||
void setSaveStatus(bool newStatus);
|
void setSaveStatus(bool newStatus);
|
||||||
|
void showSearchSyntaxHelp();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CardInfoPtr currentCardInfo() const;
|
CardInfoPtr currentCardInfo() const;
|
||||||
|
@ -146,10 +147,10 @@ private:
|
||||||
QWidget *centralWidget;
|
QWidget *centralWidget;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent = 0);
|
explicit TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent = nullptr);
|
||||||
~TabDeckEditor();
|
~TabDeckEditor() override;
|
||||||
void retranslateUi();
|
void retranslateUi() override;
|
||||||
QString getTabText() const;
|
QString getTabText() const override;
|
||||||
void setDeck(DeckLoader *_deckLoader);
|
void setDeck(DeckLoader *_deckLoader);
|
||||||
void setModified(bool _windowModified);
|
void setModified(bool _windowModified);
|
||||||
bool confirmClose();
|
bool confirmClose();
|
||||||
|
@ -160,7 +161,7 @@ public:
|
||||||
void createCentralFrame();
|
void createCentralFrame();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void closeRequest();
|
void closeRequest() override;
|
||||||
signals:
|
signals:
|
||||||
void deckEditorClosing(TabDeckEditor *tab);
|
void deckEditorClosing(TabDeckEditor *tab);
|
||||||
};
|
};
|
||||||
|
|
|
@ -98,6 +98,11 @@ CardInfoPtr OracleImporter::addCard(QString name,
|
||||||
sortAndReduceColors(allColors);
|
sortAndReduceColors(allColors);
|
||||||
properties.insert("colors", 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
|
// DETECT CARD POSITIONING INFO
|
||||||
|
|
||||||
|
@ -178,7 +183,7 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
|
||||||
QMap<QString, SplitCardPart> splitCards;
|
QMap<QString, SplitCardPart> splitCards;
|
||||||
QString ptSeparator("/");
|
QString ptSeparator("/");
|
||||||
QVariantMap card;
|
QVariantMap card;
|
||||||
QString layout, name, text, colors, maintype, power, toughness;
|
QString layout, name, text, colors, colorIdentity, maintype, power, toughness;
|
||||||
bool isToken;
|
bool isToken;
|
||||||
QStringList additionalNames;
|
QStringList additionalNames;
|
||||||
QVariantHash properties;
|
QVariantHash properties;
|
||||||
|
@ -232,6 +237,11 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
|
||||||
if (!colors.isEmpty())
|
if (!colors.isEmpty())
|
||||||
properties.insert("colors", colors);
|
properties.insert("colors", colors);
|
||||||
|
|
||||||
|
// special handling properties
|
||||||
|
colorIdentity = card.value("colorIdentity").toStringList().join("");
|
||||||
|
if (!colorIdentity.isEmpty())
|
||||||
|
properties.insert("coloridentity", colorIdentity);
|
||||||
|
|
||||||
maintype = card.value("types").toStringList().first();
|
maintype = card.value("types").toStringList().first();
|
||||||
if (!maintype.isEmpty())
|
if (!maintype.isEmpty())
|
||||||
properties.insert("maintype", maintype);
|
properties.insert("maintype", maintype);
|
||||||
|
@ -242,6 +252,12 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
|
||||||
properties.insert("pt", power + ptSeparator + toughness);
|
properties.insert("pt", power + ptSeparator + toughness);
|
||||||
|
|
||||||
additionalNames = card.value("names").toStringList();
|
additionalNames = card.value("names").toStringList();
|
||||||
|
|
||||||
|
auto legalities = card.value("legalities").toMap();
|
||||||
|
for (const QString &fmtName : legalities.keys()) {
|
||||||
|
properties.insert(QString("format-%1").arg(fmtName), legalities.value(fmtName).toString().toLower());
|
||||||
|
}
|
||||||
|
|
||||||
// split cards are considered a single card, enqueue for later merging
|
// split cards are considered a single card, enqueue for later merging
|
||||||
if (layout == "split") {
|
if (layout == "split") {
|
||||||
// get the position of this card part
|
// get the position of this card part
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
ADD_DEFINITIONS("-DCARDDB_DATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/data/\"")
|
ADD_DEFINITIONS("-DCARDDB_DATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/data/\"")
|
||||||
add_executable(carddatabase_test
|
add_executable(carddatabase_test
|
||||||
carddatabase_test.cpp
|
carddatabase_test.cpp
|
||||||
|
mocks.cpp
|
||||||
|
../../cockatrice/src/carddatabase.cpp
|
||||||
|
../../cockatrice/src/carddbparser/carddatabaseparser.cpp
|
||||||
|
../../cockatrice/src/carddbparser/cockatricexml3.cpp
|
||||||
|
../../cockatrice/src/carddbparser/cockatricexml4.cpp
|
||||||
|
)
|
||||||
|
add_executable(filter_string_test
|
||||||
|
filter_string_test.cpp
|
||||||
|
mocks.cpp
|
||||||
|
../../cockatrice/src/filter_string.cpp
|
||||||
|
../../cockatrice/src/cardfilter.cpp
|
||||||
|
../../cockatrice/src/filtertree.cpp
|
||||||
../../cockatrice/src/carddatabase.cpp
|
../../cockatrice/src/carddatabase.cpp
|
||||||
../../cockatrice/src/carddbparser/carddatabaseparser.cpp
|
../../cockatrice/src/carddbparser/carddatabaseparser.cpp
|
||||||
../../cockatrice/src/carddbparser/cockatricexml3.cpp
|
../../cockatrice/src/carddbparser/cockatricexml3.cpp
|
||||||
|
@ -8,10 +20,15 @@ add_executable(carddatabase_test
|
||||||
)
|
)
|
||||||
if(NOT GTEST_FOUND)
|
if(NOT GTEST_FOUND)
|
||||||
add_dependencies(carddatabase_test gtest)
|
add_dependencies(carddatabase_test gtest)
|
||||||
|
add_dependencies(filter_string_test gtest)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Qt5 COMPONENTS Concurrent Network Widgets REQUIRED)
|
find_package(Qt5 COMPONENTS Concurrent Network Widgets Svg REQUIRED)
|
||||||
set(TEST_QT_MODULES Qt5::Concurrent Qt5::Network Qt5::Widgets)
|
set(TEST_QT_MODULES Qt5::Concurrent Qt5::Network Qt5::Widgets Qt5::Svg)
|
||||||
|
|
||||||
target_link_libraries(carddatabase_test ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES})
|
target_link_libraries(carddatabase_test ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES})
|
||||||
add_test(NAME carddatabase_test COMMAND carddatabase_test)
|
target_link_libraries(filter_string_test ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES})
|
||||||
|
|
||||||
|
|
||||||
|
add_test(NAME carddatabase_test COMMAND carddatabase_test)
|
||||||
|
add_test(NAME filter_string_test COMMAND filter_string_test)
|
|
@ -1,57 +1,6 @@
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#include "carddatabase_test.h"
|
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsCache *settingsCache;
|
|
||||||
|
|
||||||
void PictureLoader::clearPixmapCache(CardInfoPtr /* card */)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
66
tests/carddatabase/filter_string_test.cpp
Normal file
66
tests/carddatabase/filter_string_test.cpp
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "../cockatrice/src/filter_string.h"
|
||||||
|
#include "mocks.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
54
tests/carddatabase/mocks.cpp
Normal file
54
tests/carddatabase/mocks.cpp
Normal file
|
@ -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;
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#define SETTINGSCACHE_H
|
#define SETTINGSCACHE_H
|
||||||
|
|
||||||
|
|
||||||
class CardDatabaseSettings
|
class CardDatabaseSettings
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -40,6 +41,8 @@ signals:
|
||||||
void cardDatabasePathChanged();
|
void cardDatabasePathChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern SettingsCache *settingsCache;
|
||||||
|
|
||||||
#define PICTURELOADER_H
|
#define PICTURELOADER_H
|
||||||
|
|
||||||
class PictureLoader
|
class PictureLoader
|
Loading…
Reference in a new issue