diff --git a/cockatrice/src/game_graphics/dialogs/dlg_create_token.cpp b/cockatrice/src/game_graphics/dialogs/dlg_create_token.cpp index b311d2ebd..47985cf29 100644 --- a/cockatrice/src/game_graphics/dialogs/dlg_create_token.cpp +++ b/cockatrice/src/game_graphics/dialogs/dlg_create_token.cpp @@ -16,11 +16,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -88,6 +90,15 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa cardDatabaseDisplayModel = new TokenDisplayModel(this); cardDatabaseDisplayModel->setSourceModel(cardDatabaseModel); + CardsDisplaySettings *cardsDisplay = &SettingsCache::instance().cardsDisplay(); + const auto applyCardSearchLanguage = [this, cardsDisplay]() { + cardDatabaseDisplayModel->setSearchLanguage( + cardsDisplay->getCardLang(), static_cast(cardsDisplay->getCardSearchLanguage())); + }; + applyCardSearchLanguage(); + connect(cardsDisplay, &CardsDisplaySettings::cardLangChanged, this, applyCardSearchLanguage); + connect(cardsDisplay, &CardsDisplaySettings::cardSearchLanguageChanged, this, applyCardSearchLanguage); + chooseTokenFromAllRadioButton = new QRadioButton(tr("Show &all tokens")); connect(chooseTokenFromAllRadioButton, &QRadioButton::toggled, this, &DlgCreateToken::actChooseTokenFromAll); chooseTokenFromDeckRadioButton = new QRadioButton(tr("Show tokens from this &deck")); diff --git a/cockatrice/src/interface/card_picture_loader/card_picture_to_load.cpp b/cockatrice/src/interface/card_picture_loader/card_picture_to_load.cpp index 7cb502e92..4f933207e 100644 --- a/cockatrice/src/interface/card_picture_loader/card_picture_to_load.cpp +++ b/cockatrice/src/interface/card_picture_loader/card_picture_to_load.cpp @@ -94,8 +94,7 @@ void CardPictureToLoad::populateSetUrls() } } - const QStringList orderedTemplates = urlTemplates; - for (const QString &urlTemplate : orderedTemplates) { + for (const QString &urlTemplate : urlTemplates) { QString transformedUrl = transformUrl(urlTemplate); if (!transformedUrl.isEmpty()) { diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp index c8bdacf0d..29c18c821 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent, CardDatabaseModel *databaseModel) : QWidget(parent) @@ -40,6 +42,15 @@ DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent databaseDisplayModel->setSourceModel(databaseModel); databaseDisplayModel->setFilterKeyColumn(0); + CardsDisplaySettings *cardsDisplay = &SettingsCache::instance().cardsDisplay(); + const auto applyCardSearchLanguage = [this, cardsDisplay]() { + databaseDisplayModel->setSearchLanguage(cardsDisplay->getCardLang(), + static_cast(cardsDisplay->getCardSearchLanguage())); + }; + applyCardSearchLanguage(); + connect(cardsDisplay, &CardsDisplaySettings::cardLangChanged, this, applyCardSearchLanguage); + connect(cardsDisplay, &CardsDisplaySettings::cardSearchLanguageChanged, this, applyCardSearchLanguage); + databaseView = new CardDatabaseView(this, databaseDisplayModel); databaseView->setObjectName("databaseView"); databaseView->setFocusProxy(searchEdit); diff --git a/cockatrice/src/interface/widgets/settings_page/general_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/general_settings_page.cpp index 36436f8a3..40f84215f 100644 --- a/cockatrice/src/interface/widgets/settings_page/general_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/general_settings_page.cpp @@ -63,13 +63,26 @@ GeneralSettingsPage::GeneralSettingsPage() connect(&cardLanguageBox, qOverload(&QComboBox::currentIndexChanged), this, &GeneralSettingsPage::cardLanguageBoxChanged); + // card search language, independent of the card display language + cardSearchLanguageBox.addItem(""); // texts set in retranslateUi + cardSearchLanguageBox.addItem(""); + cardSearchLanguageBox.addItem(""); + const int cardSearchLanguageIndex = SettingsCache::instance().cardsDisplay().getCardSearchLanguage(); + cardSearchLanguageBox.setCurrentIndex(cardSearchLanguageIndex < 0 ? static_cast(CardSearchLanguage::English) + : cardSearchLanguageIndex); + + connect(&cardSearchLanguageBox, qOverload(&QComboBox::currentIndexChanged), this, + &GeneralSettingsPage::cardSearchLanguageBoxChanged); + auto *languageGrid = new QGridLayout; languageGrid->addWidget(&languageLabel, 0, 0); languageGrid->addWidget(&languageBox, 0, 1); languageGrid->addWidget(&cardLanguageLabel, 1, 0); languageGrid->addWidget(&cardLanguageBox, 1, 1); languageGrid->addWidget(&cardLanguageNoteLabel, 2, 1); - languageGrid->addWidget(&advertiseTranslationPageLabel, 3, 1, Qt::AlignRight); + languageGrid->addWidget(&cardSearchLanguageLabel, 3, 0); + languageGrid->addWidget(&cardSearchLanguageBox, 3, 1); + languageGrid->addWidget(&advertiseTranslationPageLabel, 4, 1, Qt::AlignRight); cardLanguageNoteLabel.setWordWrap(true); cardLanguageNoteLabel.setAlignment(Qt::AlignLeft | Qt::AlignVCenter); @@ -481,6 +494,11 @@ void GeneralSettingsPage::cardLanguageBoxChanged(int index) } } +void GeneralSettingsPage::cardSearchLanguageBoxChanged(int index) +{ + SettingsCache::instance().cardsDisplay().setCardSearchLanguage(index); +} + void GeneralSettingsPage::updateStartupServerControlsVisibility() { const int index = startupTabSelector.currentIndex(); @@ -502,6 +520,12 @@ void GeneralSettingsPage::retranslateUi() cardLanguageLabel.setText(tr("Card text & images language:")); cardLanguageNoteLabel.setText( tr("Foreign card names, text and art apply after you update the card database (Oracle).")); + cardSearchLanguageLabel.setText(tr("Language used in card search:")); + cardSearchLanguageBox.setItemText(static_cast(CardSearchLanguage::English), tr("English")); + cardSearchLanguageBox.setItemText(static_cast(CardSearchLanguage::Selected), + tr("Selected card language (untranslated cards still match in English)")); + cardSearchLanguageBox.setItemText(static_cast(CardSearchLanguage::Both), + tr("English and selected card language")); advertiseTranslationPageLabel.setText( QString("%2").arg(WIKI_TRANSLATION_FAQ).arg(tr("How to help with translations"))); diff --git a/cockatrice/src/interface/widgets/settings_page/general_settings_page.h b/cockatrice/src/interface/widgets/settings_page/general_settings_page.h index 7afba158f..07e0ba3a9 100644 --- a/cockatrice/src/interface/widgets/settings_page/general_settings_page.h +++ b/cockatrice/src/interface/widgets/settings_page/general_settings_page.h @@ -38,6 +38,7 @@ private slots: void resetAllPathsClicked(); void languageBoxChanged(int index); void cardLanguageBoxChanged(int index); + void cardSearchLanguageBoxChanged(int index); void updateStartupServerControlsVisibility(); private: @@ -55,6 +56,9 @@ private: QComboBox cardLanguageBox; QLabel cardLanguageNoteLabel; + QLabel cardSearchLanguageLabel; + QComboBox cardSearchLanguageBox; + QLabel updateReleaseChannelLabel; QComboBox updateReleaseChannelBox; QCheckBox startupUpdateCheckCheckBox; diff --git a/cockatrice/src/interface/widgets/utility/completer_utils.cpp b/cockatrice/src/interface/widgets/utility/completer_utils.cpp index 16d5cfd13..1f7111960 100644 --- a/cockatrice/src/interface/widgets/utility/completer_utils.cpp +++ b/cockatrice/src/interface/widgets/utility/completer_utils.cpp @@ -1,5 +1,6 @@ #include "completer_utils.h" +#include "../../../client/settings/cache_settings.h" #include "card_completer_styler.h" #include @@ -7,13 +8,26 @@ #include #include #include +#include #include #include #include +#include + +namespace +{ +void applyCardSearchLanguage(CardSearchModel *searchModel) +{ + const auto &cardsDisplay = SettingsCache::instance().cardsDisplay(); + searchModel->setSearchLanguage(cardsDisplay.getCardLang(), + static_cast(cardsDisplay.getCardSearchLanguage())); +} +} // namespace CardCompleterSetup createCardCompleter(CardDatabaseDisplayModel *displayModel, QObject *parent, int maxVisibleItems) { auto *searchModel = new CardSearchModel(displayModel, parent); + applyCardSearchLanguage(searchModel); auto *proxyModel = new CardCompleterProxyModel(parent); proxyModel->setSourceModel(searchModel); @@ -27,6 +41,12 @@ CardCompleterSetup createCardCompleter(CardDatabaseDisplayModel *displayModel, Q completer->setMaxVisibleItems(maxVisibleItems); CardCompleterStyler::apply(completer); + auto *cardsDisplay = &SettingsCache::instance().cardsDisplay(); + QObject::connect(cardsDisplay, &CardsDisplaySettings::cardLangChanged, searchModel, + [searchModel] { applyCardSearchLanguage(searchModel); }); + QObject::connect(cardsDisplay, &CardsDisplaySettings::cardSearchLanguageChanged, searchModel, + [searchModel] { applyCardSearchLanguage(searchModel); }); + return {searchModel, proxyModel, completer}; } diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp index f4530a12b..fb43dddcd 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_widget.cpp @@ -41,6 +41,15 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent, databaseDisplayModel->setSourceModel(database_model); databaseDisplayModel->setFilterKeyColumn(0); + CardsDisplaySettings *cardsDisplay = &SettingsCache::instance().cardsDisplay(); + const auto applyCardSearchLanguage = [this, cardsDisplay]() { + databaseDisplayModel->setSearchLanguage(cardsDisplay->getCardLang(), + static_cast(cardsDisplay->getCardSearchLanguage())); + }; + applyCardSearchLanguage(); + connect(cardsDisplay, &CardsDisplaySettings::cardLangChanged, this, applyCardSearchLanguage); + connect(cardsDisplay, &CardsDisplaySettings::cardSearchLanguageChanged, this, applyCardSearchLanguage); + cards = new QList; connect(databaseDisplayModel, &CardDatabaseDisplayModel::modelDirty, this, &VisualDatabaseDisplayWidget::modelDirty); diff --git a/libcockatrice_card/libcockatrice/card/card_localization.h b/libcockatrice_card/libcockatrice/card/card_localization.h index a9c8d28ea..450ee1805 100644 --- a/libcockatrice_card/libcockatrice/card/card_localization.h +++ b/libcockatrice_card/libcockatrice/card/card_localization.h @@ -15,6 +15,16 @@ * imports the selected language's card data) and the client settings UI (which * offers the language choice). */ +/** + * @brief The card languages card search should run against. + */ +enum class CardSearchLanguage +{ + English, ///< Only search the English card names and texts. + Selected, ///< Search the selected card language (untranslated cards still match in English). + Both ///< Search both the English and the selected card language names and texts. +}; + namespace CardLocalization { /** diff --git a/libcockatrice_filters/libcockatrice/filters/filter_string.cpp b/libcockatrice_filters/libcockatrice/filters/filter_string.cpp index aaf391c03..bb9f00cc2 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_string.cpp +++ b/libcockatrice_filters/libcockatrice/filters/filter_string.cpp @@ -74,6 +74,38 @@ NumericValue <- [0-9]+ static std::once_flag init; +// The peg parser rules are set up once per process, which means the GenericQuery/ +// OracleQuery lambdas cannot capture per-instance state. The card language the +// plain-text name and text queries search in is therefore kept here and applied +// by those lambdas; every FilterString shares it because it reflects a single +// global user setting. +static QString globalSearchLanguage; +static CardSearchLanguage globalSearchLanguageMode = CardSearchLanguage::English; + +namespace +{ +bool matchesInSearchLanguage(const QString &english, + const QString &localized, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode, + const StringMatcher &matcher) +{ + if (searchLanguageMode == CardSearchLanguage::English) { + return matcher(english); + } + + if (searchLanguageMode == CardSearchLanguage::Both && matcher(english)) { + return true; + } + + if (searchLanguage.isEmpty() || searchLanguage == "en") { + return matcher(english); + } + + return matcher(localized); +} +} // namespace + static void setupParserRules() { auto passthru = [](const peg::SemanticValues &sv) -> Filter { @@ -333,7 +365,10 @@ static void setupParserRules() search["OracleQuery"] = [](const peg::SemanticValues &sv) -> Filter { const auto matcher = std::any_cast(sv[0]); - return [=](const CardData &x) { return matcher(x->getText()); }; + return [=](const CardData &x) { + return matchesInSearchLanguage(x->getText(), x->getLocalizedText(globalSearchLanguage), + globalSearchLanguage, globalSearchLanguageMode, matcher); + }; }; search["ColorQuery"] = [](const peg::SemanticValues &sv) -> Filter { @@ -410,7 +445,10 @@ static void setupParserRules() }; search["GenericQuery"] = [](const peg::SemanticValues &sv) -> Filter { const auto matcher = std::any_cast(sv[0]); - return [=](const CardData &x) { return matcher(x->getName()); }; + return [=](const CardData &x) { + return matchesInSearchLanguage(x->getName(), x->getLocalizedName(globalSearchLanguage), + globalSearchLanguage, globalSearchLanguageMode, matcher); + }; }; search["Color"] = [](const peg::SemanticValues &sv) -> char { return "WUBRGU"[sv.choice()]; }; @@ -447,3 +485,9 @@ FilterString::FilterString(const QString &expr) result = [](const CardData &) -> bool { return false; }; } } + +void FilterString::setSearchLanguage(const QString &searchLanguage, CardSearchLanguage searchLanguageMode) +{ + globalSearchLanguage = searchLanguage; + globalSearchLanguageMode = searchLanguageMode; +} diff --git a/libcockatrice_filters/libcockatrice/filters/filter_string.h b/libcockatrice_filters/libcockatrice/filters/filter_string.h index a058f7d07..7bcab9c2e 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_string.h +++ b/libcockatrice_filters/libcockatrice/filters/filter_string.h @@ -14,6 +14,7 @@ #include #include #include +#include #include inline Q_LOGGING_CATEGORY(FilterStringLog, "filter_string"); @@ -55,6 +56,17 @@ public: return _error; } + /** + * @brief Sets the card language plain-text name and text queries run against. + * + * The peg parser rules are set up once per process, so this propagates to + * every FilterString instance created through the shared parser. + * + * @param searchLanguage Empty string for English only, otherwise the card language code. + * @param searchLanguageMode The search language mode from CardSearchLanguage. + */ + void setSearchLanguage(const QString &searchLanguage, CardSearchLanguage searchLanguageMode); + private: QString _error; Filter result; diff --git a/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp b/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp index 8502db50b..1731f834d 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp +++ b/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp @@ -100,14 +100,17 @@ FilterTreeNode *FilterItemList::termNode(const QString &term) return childNodes.at(i); } -bool FilterItemList::testTypeAnd(const CardInfoPtr info, CardFilter::Attr attr) const +bool FilterItemList::testTypeAnd(const CardInfoPtr info, + CardFilter::Attr attr, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const { for (auto i = childNodes.constBegin(); i != childNodes.constEnd(); i++) { if (!(*i)->isEnabled()) { continue; } - if (!(*i)->acceptCardAttr(info, attr)) { + if (!(*i)->acceptCardAttr(info, attr, searchLanguage, searchLanguageMode)) { return false; } } @@ -115,13 +118,19 @@ bool FilterItemList::testTypeAnd(const CardInfoPtr info, CardFilter::Attr attr) return true; } -bool FilterItemList::testTypeAndNot(const CardInfoPtr info, CardFilter::Attr attr) const +bool FilterItemList::testTypeAndNot(const CardInfoPtr info, + CardFilter::Attr attr, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const { // if any one in the list is true, return false - return !testTypeOr(info, attr); + return !testTypeOr(info, attr, searchLanguage, searchLanguageMode); } -bool FilterItemList::testTypeOr(const CardInfoPtr info, CardFilter::Attr attr) const +bool FilterItemList::testTypeOr(const CardInfoPtr info, + CardFilter::Attr attr, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const { bool noChildEnabledChild = true; @@ -134,7 +143,7 @@ bool FilterItemList::testTypeOr(const CardInfoPtr info, CardFilter::Attr attr) c noChildEnabledChild = false; } - if ((*i)->acceptCardAttr(info, attr)) { + if ((*i)->acceptCardAttr(info, attr, searchLanguage, searchLanguageMode)) { return true; } } @@ -142,20 +151,51 @@ bool FilterItemList::testTypeOr(const CardInfoPtr info, CardFilter::Attr attr) c return noChildEnabledChild; } -bool FilterItemList::testTypeOrNot(const CardInfoPtr info, CardFilter::Attr attr) const +bool FilterItemList::testTypeOrNot(const CardInfoPtr info, + CardFilter::Attr attr, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const { // if any one in the list is false, return true - return !testTypeAnd(info, attr); + return !testTypeAnd(info, attr, searchLanguage, searchLanguageMode); } -bool FilterItem::acceptName(const CardInfoPtr info) const +bool FilterItem::acceptName(const CardInfoPtr info, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const { - return info->getName().contains(term, Qt::CaseInsensitive); + if (searchLanguageMode == CardSearchLanguage::English) { + return info->getName().contains(term, Qt::CaseInsensitive); + } + + if (searchLanguageMode == CardSearchLanguage::Both && info->getName().contains(term, Qt::CaseInsensitive)) { + return true; + } + + if (searchLanguage.isEmpty() || searchLanguage == "en") { + return info->getName().contains(term, Qt::CaseInsensitive); + } + + return info->getLocalizedName(searchLanguage).contains(term, Qt::CaseInsensitive); } -bool FilterItem::acceptNameExact(const CardInfoPtr info) const +bool FilterItem::acceptNameExact(const CardInfoPtr info, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const { - return info->getName() == term; + if (searchLanguageMode == CardSearchLanguage::English) { + return info->getName() == term; + } + + if (searchLanguageMode == CardSearchLanguage::Both && info->getName() == term) { + return true; + } + + if (searchLanguage.isEmpty() || searchLanguage == "en") { + return info->getName() == term; + } + + return info->getLocalizedName(searchLanguage) == term; } bool FilterItem::acceptType(const CardInfoPtr info) const @@ -213,9 +253,23 @@ bool FilterItem::acceptColor(const CardInfoPtr info) const return match_count == converted_term.length(); } -bool FilterItem::acceptText(const CardInfoPtr info) const +bool FilterItem::acceptText(const CardInfoPtr info, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const { - return info->getText().contains(term, Qt::CaseInsensitive); + if (searchLanguageMode == CardSearchLanguage::English) { + return info->getText().contains(term, Qt::CaseInsensitive); + } + + if (searchLanguageMode == CardSearchLanguage::Both && info->getText().contains(term, Qt::CaseInsensitive)) { + return true; + } + + if (searchLanguage.isEmpty() || searchLanguage == "en") { + return info->getText().contains(term, Qt::CaseInsensitive); + } + + return info->getLocalizedText(searchLanguage).contains(term, Qt::CaseInsensitive); } bool FilterItem::acceptSet(const CardInfoPtr info) const @@ -402,19 +456,22 @@ bool FilterItem::relationCheck(int cardInfo) const return result; } -bool FilterItem::acceptCardAttr(const CardInfoPtr info, CardFilter::Attr attr) const +bool FilterItem::acceptCardAttr(const CardInfoPtr info, + CardFilter::Attr attr, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const { switch (attr) { case CardFilter::AttrName: - return acceptName(info); + return acceptName(info, searchLanguage, searchLanguageMode); case CardFilter::AttrNameExact: - return acceptNameExact(info); + return acceptNameExact(info, searchLanguage, searchLanguageMode); case CardFilter::AttrType: return acceptType(info); case CardFilter::AttrColor: return acceptColor(info); case CardFilter::AttrText: - return acceptText(info); + return acceptText(info, searchLanguage, searchLanguageMode); case CardFilter::AttrSet: return acceptSet(info); case CardFilter::AttrManaCost: @@ -484,18 +541,21 @@ FilterTreeNode *FilterTree::termNode(const CardFilter *f) return termNode(f->attr(), f->type(), f->term()); } -bool FilterTree::testAttr(const CardInfoPtr info, const LogicMap *lm) const +bool FilterTree::testAttr(const CardInfoPtr info, + const LogicMap *lm, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const { const FilterItemList *fil; bool status = true; fil = lm->findTypeList(CardFilter::TypeAnd); - if (fil && fil->isEnabled() && !fil->testTypeAnd(info, lm->attr)) { + if (fil && fil->isEnabled() && !fil->testTypeAnd(info, lm->attr, searchLanguage, searchLanguageMode)) { return false; } fil = lm->findTypeList(CardFilter::TypeAndNot); - if (fil && fil->isEnabled() && !fil->testTypeAndNot(info, lm->attr)) { + if (fil && fil->isEnabled() && !fil->testTypeAndNot(info, lm->attr, searchLanguage, searchLanguageMode)) { return false; } @@ -504,23 +564,25 @@ bool FilterTree::testAttr(const CardInfoPtr info, const LogicMap *lm) const status = false; // if this is true we can return because it is OR'd with the OrNot list - if (fil->testTypeOr(info, lm->attr)) { + if (fil->testTypeOr(info, lm->attr, searchLanguage, searchLanguageMode)) { return true; } } fil = lm->findTypeList(CardFilter::TypeOrNot); - if (fil && fil->isEnabled() && fil->testTypeOrNot(info, lm->attr)) { + if (fil && fil->isEnabled() && fil->testTypeOrNot(info, lm->attr, searchLanguage, searchLanguageMode)) { return true; } return status; } -bool FilterTree::acceptsCard(const CardInfoPtr info) const +bool FilterTree::acceptsCard(const CardInfoPtr info, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const { for (auto i = childNodes.constBegin(); i != childNodes.constEnd(); i++) { - if ((*i)->isEnabled() && !testAttr(info, *i)) { + if ((*i)->isEnabled() && !testAttr(info, *i, searchLanguage, searchLanguageMode)) { return false; } } diff --git a/libcockatrice_filters/libcockatrice/filters/filter_tree.h b/libcockatrice_filters/libcockatrice/filters/filter_tree.h index aac1777e0..e98106b53 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_tree.h +++ b/libcockatrice_filters/libcockatrice/filters/filter_tree.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -167,10 +168,22 @@ public: return CardFilter::typeName(type); } - [[nodiscard]] bool testTypeAnd(CardInfoPtr info, CardFilter::Attr attr) const; - [[nodiscard]] bool testTypeAndNot(CardInfoPtr info, CardFilter::Attr attr) const; - [[nodiscard]] bool testTypeOr(CardInfoPtr info, CardFilter::Attr attr) const; - [[nodiscard]] bool testTypeOrNot(CardInfoPtr info, CardFilter::Attr attr) const; + [[nodiscard]] bool testTypeAnd(CardInfoPtr info, + CardFilter::Attr attr, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const; + [[nodiscard]] bool testTypeAndNot(CardInfoPtr info, + CardFilter::Attr attr, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const; + [[nodiscard]] bool testTypeOr(CardInfoPtr info, + CardFilter::Attr attr, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const; + [[nodiscard]] bool testTypeOrNot(CardInfoPtr info, + CardFilter::Attr attr, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const; }; class FilterItem : public FilterTreeNode @@ -207,20 +220,26 @@ public: return true; } - [[nodiscard]] bool acceptName(CardInfoPtr info) const; - [[nodiscard]] bool acceptNameExact(CardInfoPtr info) const; + [[nodiscard]] bool + acceptName(CardInfoPtr info, const QString &searchLanguage, CardSearchLanguage searchLanguageMode) const; + [[nodiscard]] bool + acceptNameExact(CardInfoPtr info, const QString &searchLanguage, CardSearchLanguage searchLanguageMode) const; [[nodiscard]] bool acceptType(CardInfoPtr info) const; [[nodiscard]] bool acceptMainType(CardInfoPtr info) const; [[nodiscard]] bool acceptSubType(CardInfoPtr info) const; [[nodiscard]] bool acceptColor(CardInfoPtr info) const; - [[nodiscard]] bool acceptText(CardInfoPtr info) const; + [[nodiscard]] bool + acceptText(CardInfoPtr info, const QString &searchLanguage, CardSearchLanguage searchLanguageMode) const; [[nodiscard]] bool acceptSet(CardInfoPtr info) const; [[nodiscard]] bool acceptManaCost(CardInfoPtr info) const; [[nodiscard]] bool acceptCmc(CardInfoPtr info) const; [[nodiscard]] bool acceptPowerToughness(CardInfoPtr info, CardFilter::Attr attr) const; [[nodiscard]] bool acceptLoyalty(CardInfoPtr info) const; [[nodiscard]] bool acceptRarity(CardInfoPtr info) const; - [[nodiscard]] bool acceptCardAttr(CardInfoPtr info, CardFilter::Attr attr) const; + [[nodiscard]] bool acceptCardAttr(CardInfoPtr info, + CardFilter::Attr attr, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const; [[nodiscard]] bool acceptFormat(CardInfoPtr info) const; [[nodiscard]] bool relationCheck(int cardInfo) const; }; @@ -240,7 +259,10 @@ private: LogicMap *attrLogicMap(CardFilter::Attr attr); FilterItemList *attrTypeList(CardFilter::Attr attr, CardFilter::Type type); - bool testAttr(CardInfoPtr info, const LogicMap *lm) const; + bool testAttr(CardInfoPtr info, + const LogicMap *lm, + const QString &searchLanguage, + CardSearchLanguage searchLanguageMode) const; void nodeChanged() const override { @@ -279,7 +301,8 @@ public: return 0; } - [[nodiscard]] bool acceptsCard(CardInfoPtr info) const; + [[nodiscard]] bool + acceptsCard(CardInfoPtr info, const QString &searchLanguage, CardSearchLanguage searchLanguageMode) const; void removeFiltersByAttr(CardFilter::Attr filterType); void removeFilter(const CardFilter *toRemove); void clear(); diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_cards_display_settings_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_cards_display_settings_provider.h index 304ab3cb7..900f51f2b 100644 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_cards_display_settings_provider.h +++ b/libcockatrice_interfaces/libcockatrice/interfaces/interface_cards_display_settings_provider.h @@ -29,6 +29,7 @@ public: [[nodiscard]] virtual int getArchidektPreviewSize() const = 0; [[nodiscard]] virtual int getSampleHandSize() const = 0; [[nodiscard]] virtual QString getCardLang() const = 0; + [[nodiscard]] virtual int getCardSearchLanguage() const = 0; }; #endif // COCKATRICE_INTERFACE_CARDS_DISPLAY_SETTINGS_PROVIDER_H diff --git a/libcockatrice_models/libcockatrice/models/database/card/card_search_model.cpp b/libcockatrice_models/libcockatrice/models/database/card/card_search_model.cpp index 621f28983..274719b3a 100644 --- a/libcockatrice_models/libcockatrice/models/database/card/card_search_model.cpp +++ b/libcockatrice_models/libcockatrice/models/database/card/card_search_model.cpp @@ -65,7 +65,13 @@ void CardSearchModel::updateSearchResults(const QString &query) continue; } - const QString lowerName = card->getName().toLower(); + // The completer suggestions match against the same languages the card + // search uses, so typing a localized name finds the card. + QString matchName = card->getName(); + if (searchLanguageMode != CardSearchLanguage::English && !searchLanguage.isEmpty() && searchLanguage != "en") { + matchName = card->getLocalizedName(searchLanguage); + } + const QString lowerName = matchName.toLower(); if (!lowerName.contains(lowerQuery)) { continue; } @@ -79,11 +85,17 @@ void CardSearchModel::updateSearchResults(const QString &query) } } - auto sortByDistanceThenLength = [](const SearchResult &a, const SearchResult &b) { + auto sortByDistanceThenLength = [this](const SearchResult &a, const SearchResult &b) { + QString nameA = a.card->getName(); + QString nameB = b.card->getName(); + if (searchLanguageMode != CardSearchLanguage::English && !searchLanguage.isEmpty() && searchLanguage != "en") { + nameA = a.card->getLocalizedName(searchLanguage); + nameB = b.card->getLocalizedName(searchLanguage); + } if (a.distance != b.distance) { return a.distance < b.distance; } - return a.card->getName().size() < b.card->getName().size(); + return nameA.size() < nameB.size(); }; std::sort(prefixMatches.begin(), prefixMatches.end(), sortByDistanceThenLength); diff --git a/libcockatrice_models/libcockatrice/models/database/card/card_search_model.h b/libcockatrice_models/libcockatrice/models/database/card/card_search_model.h index 646bf7e61..dcd61e37c 100644 --- a/libcockatrice_models/libcockatrice/models/database/card/card_search_model.h +++ b/libcockatrice_models/libcockatrice/models/database/card/card_search_model.h @@ -27,6 +27,15 @@ public: void updateSearchResults(const QString &query); // Update results based on input + void setSearchLanguage(const QString &searchLang, CardSearchLanguage mode) + { + if (searchLanguage == searchLang && searchLanguageMode == mode) { + return; + } + searchLanguage = searchLang; + searchLanguageMode = mode; + } + private: struct SearchResult { @@ -36,6 +45,8 @@ private: CardDatabaseDisplayModel *sourceModel; QList searchResults; + QString searchLanguage; + CardSearchLanguage searchLanguageMode = CardSearchLanguage::English; }; #endif // CARD_SEARCH_MODEL_H diff --git a/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp b/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp index 724ee61f2..acef20440 100644 --- a/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp +++ b/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp @@ -179,7 +179,7 @@ bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex } if (filterString != nullptr) { - if (filterTree != nullptr && !filterTree->acceptsCard(info)) { + if (filterTree != nullptr && !filterTree->acceptsCard(info, searchLanguage, searchLanguageMode)) { return false; } return filterString->check(info); @@ -190,7 +190,9 @@ bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex bool CardDatabaseDisplayModel::rowMatchesCardName(CardInfoPtr info) const { - if (!cardName.isEmpty() && !info->getName().contains(cardName, Qt::CaseInsensitive)) { + if (!cardName.isEmpty() && !info->getName().contains(cardName, Qt::CaseInsensitive) && + searchLanguageMode != CardSearchLanguage::English && !searchLanguage.isEmpty() && searchLanguage != "en" && + !info->getLocalizedName(searchLanguage).contains(cardName, Qt::CaseInsensitive)) { return false; } @@ -199,7 +201,7 @@ bool CardDatabaseDisplayModel::rowMatchesCardName(CardInfoPtr info) const } if (filterTree != nullptr) { - return filterTree->acceptsCard(info); + return filterTree->acceptsCard(info, searchLanguage, searchLanguageMode); } return true; @@ -235,6 +237,29 @@ void CardDatabaseDisplayModel::setFilterTree(FilterTree *_filterTree) invalidate(); } +void CardDatabaseDisplayModel::setStringFilter(const QString &_src) +{ + delete filterString; + filterString = new FilterString(_src); + filterString->setSearchLanguage(searchLanguage, searchLanguageMode); + dirty(); +} + +void CardDatabaseDisplayModel::setSearchLanguage(const QString &searchLang, CardSearchLanguage mode) +{ + if (searchLanguage == searchLang && searchLanguageMode == mode) { + return; + } + + searchLanguage = searchLang; + searchLanguageMode = mode; + + if (filterString != nullptr) { + filterString->setSearchLanguage(searchLanguage, searchLanguageMode); + } + dirty(); +} + void CardDatabaseDisplayModel::filterTreeChanged() { invalidate(); diff --git a/libcockatrice_models/libcockatrice/models/database/card_database_display_model.h b/libcockatrice_models/libcockatrice/models/database/card_database_display_model.h index c3145c356..5eba2ae0a 100644 --- a/libcockatrice_models/libcockatrice/models/database/card_database_display_model.h +++ b/libcockatrice_models/libcockatrice/models/database/card_database_display_model.h @@ -10,6 +10,7 @@ #include #include +#include #include class FilterTree; @@ -32,6 +33,8 @@ private: FilterString *filterString; int loadedRowCount; QTimer dirtyTimer; + QString searchLanguage; + CardSearchLanguage searchLanguageMode = CardSearchLanguage::English; /** The translation table that will be used for sanitizeCardName. */ static QMap characterTranslation; @@ -55,17 +58,13 @@ public: cardName = sanitizeCardName(_cardName, characterTranslation); dirty(); } - void setStringFilter(const QString &_src) - { - delete filterString; - filterString = new FilterString(_src); - dirty(); - } + void setStringFilter(const QString &_src); void setCardNameSet(const QSet &_cardNameSet) { cardNameSet = _cardNameSet; dirty(); } + void setSearchLanguage(const QString &searchLang, CardSearchLanguage mode); void dirty() { diff --git a/libcockatrice_settings/libcockatrice/settings/cards_display_settings.cpp b/libcockatrice_settings/libcockatrice/settings/cards_display_settings.cpp index b8eafca6c..0996dc92e 100644 --- a/libcockatrice_settings/libcockatrice/settings/cards_display_settings.cpp +++ b/libcockatrice_settings/libcockatrice/settings/cards_display_settings.cpp @@ -110,6 +110,11 @@ QString CardsDisplaySettings::getCardLang() const return getValue("cardLang", QString(), QString(), "en").toString(); } +int CardsDisplaySettings::getCardSearchLanguage() const +{ + return getValue("cardSearchLanguage", QString(), QString(), static_cast(CardSearchLanguage::English)).toInt(); +} + void CardsDisplaySettings::setDisplayCardNames(bool _displayCardNames) { setValue(_displayCardNames, "displayCardNames"); @@ -242,3 +247,12 @@ void CardsDisplaySettings::setCardLang(const QString &_cardLang) sync(); emit cardLangChanged(_cardLang); } + +void CardsDisplaySettings::setCardSearchLanguage(int _cardSearchLanguage) +{ + if (_cardSearchLanguage == getCardSearchLanguage()) { + return; + } + setValue(_cardSearchLanguage, "cardSearchLanguage"); + emit cardSearchLanguageChanged(_cardSearchLanguage); +} diff --git a/libcockatrice_settings/libcockatrice/settings/cards_display_settings.h b/libcockatrice_settings/libcockatrice/settings/cards_display_settings.h index 0b47ce490..85eb5adbd 100644 --- a/libcockatrice_settings/libcockatrice/settings/cards_display_settings.h +++ b/libcockatrice_settings/libcockatrice/settings/cards_display_settings.h @@ -3,6 +3,7 @@ #include "settings_manager.h" +#include #include class CardsDisplaySettings : public SettingsManager, public ICardsDisplaySettingsProvider @@ -32,6 +33,7 @@ public: [[nodiscard]] int getArchidektPreviewSize() const override; [[nodiscard]] int getSampleHandSize() const override; [[nodiscard]] QString getCardLang() const override; + [[nodiscard]] int getCardSearchLanguage() const override; void setDisplayCardNames(bool _displayCardNames); void setRoundCardCorners(bool _roundCardCorners); @@ -54,6 +56,7 @@ public: void setArchidektPreviewCardSize(int _archidektPreviewCardSize); void setSampleHandSize(int _sampleHandSize); void setCardLang(const QString &_cardLang); + void setCardSearchLanguage(int _cardSearchLanguage); signals: void displayCardNamesChanged(); @@ -71,6 +74,7 @@ signals: void archidektPreviewSizeChanged(); void sampleHandSizeChanged(int amount); void cardLangChanged(const QString &lang); + void cardSearchLanguageChanged(int cardSearchLanguage); public: explicit CardsDisplaySettings(const QString &settingPath, QObject *parent = nullptr); diff --git a/tests/settings/settings_defaults_test.cpp b/tests/settings/settings_defaults_test.cpp index 340859323..fdf1c3fa2 100644 --- a/tests/settings/settings_defaults_test.cpp +++ b/tests/settings/settings_defaults_test.cpp @@ -562,6 +562,19 @@ TEST_F(SettingsDefaultsTest, CardsDisplay_CardLang_SetAndGet) ASSERT_EQ(s.getCardLang(), QString("de")); } +TEST_F(SettingsDefaultsTest, CardsDisplay_CardSearchLanguage_Default) +{ + CardsDisplaySettings s(settingsPath, nullptr); + ASSERT_EQ(s.getCardSearchLanguage(), static_cast(CardSearchLanguage::English)); +} + +TEST_F(SettingsDefaultsTest, CardsDisplay_CardSearchLanguage_SetAndGet) +{ + CardsDisplaySettings s(settingsPath, nullptr); + s.setCardSearchLanguage(static_cast(CardSearchLanguage::Selected)); + ASSERT_EQ(s.getCardSearchLanguage(), static_cast(CardSearchLanguage::Selected)); +} + // --- VisualDeckStorageSettings --- TEST_F(SettingsDefaultsTest, VisualDeckStorage_SortingOrder_Default)