From 5d9d7d3aa55222471a629ecbeb3b18957a1e26b4 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 14 Dec 2025 14:26:06 -0800 Subject: [PATCH 01/43] [DeckLoader] remove unused private methods (#6417) --- .../src/interface/deck_loader/deck_loader.cpp | 23 ------------------- .../src/interface/deck_loader/deck_loader.h | 3 --- 2 files changed, 26 deletions(-) diff --git a/cockatrice/src/interface/deck_loader/deck_loader.cpp b/cockatrice/src/interface/deck_loader/deck_loader.cpp index a3f0ff619..bd5fe3ef2 100644 --- a/cockatrice/src/interface/deck_loader/deck_loader.cpp +++ b/cockatrice/src/interface/deck_loader/deck_loader.cpp @@ -485,29 +485,6 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName) return result; } -QString DeckLoader::getCardZoneFromName(const QString &cardName, QString currentZoneName) -{ - CardInfoPtr card = CardDatabaseManager::query()->getCardInfo(cardName); - - if (card && card->getIsToken()) { - return DECK_ZONE_TOKENS; - } - - return currentZoneName; -} - -QString DeckLoader::getCompleteCardName(const QString &cardName) -{ - if (CardDatabaseManager::getInstance()) { - ExactCard temp = CardDatabaseManager::query()->guessCard({cardName}); - if (temp) { - return temp.getName(); - } - } - - return cardName; -} - void DeckLoader::printDeckListNode(QTextCursor *cursor, const InnerDecklistNode *node) { const int totalColumns = 2; diff --git a/cockatrice/src/interface/deck_loader/deck_loader.h b/cockatrice/src/interface/deck_loader/deck_loader.h index ae3088ff8..75eb38423 100644 --- a/cockatrice/src/interface/deck_loader/deck_loader.h +++ b/cockatrice/src/interface/deck_loader/deck_loader.h @@ -106,9 +106,6 @@ private: QList cards, bool addComments = true, bool addSetNameAndNumber = true); - - [[nodiscard]] static QString getCardZoneFromName(const QString &cardName, QString currentZoneName); - [[nodiscard]] static QString getCompleteCardName(const QString &cardName); }; #endif From 8485bbe575bc9c6f06cd3cebfb2416b08c3d5d36 Mon Sep 17 00:00:00 2001 From: tooomm Date: Mon, 15 Dec 2025 00:13:16 +0100 Subject: [PATCH 02/43] Docs: Use Doxygen \todo comments (#6421) * format todo comments for doxygen * Mention Todo List & link search --- README.md | 2 ++ cockatrice/src/game/player/player.cpp | 2 +- .../interface/widgets/dialogs/dlg_edit_password.cpp | 2 +- .../widgets/dialogs/dlg_forgot_password_reset.cpp | 2 +- .../src/interface/widgets/dialogs/dlg_register.cpp | 2 +- .../src/interface/widgets/dialogs/dlg_settings.cpp | 4 ++-- .../interface/widgets/server/chat_view/chat_view.cpp | 4 ++-- .../api_response/card/archidekt_api_response_card.cpp | 10 +++++----- .../libcockatrice/deck_list/deck_list.h | 2 +- .../network/server/remote/game/server_game.cpp | 2 +- libcockatrice_utility/libcockatrice/utility/peglib.h | 6 +++--- oracle/src/zip/unzip.cpp | 2 +- servatrice/src/serversocketinterface.cpp | 4 ++-- servatrice/src/smtp/qxtmailmessage.cpp | 3 +-- 14 files changed, 24 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index b1233f749..87629d2c9 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ This tag is used for issues that we are looking for somebody to pick up. Often t For both tags, we're willing to provide help to contributors in showing them where and how they can make changes, as well as code reviews for submitted changes.
We'll happily advice on how best to implement a feature, or we can show you where the codebase is doing something similar before you get too far along - put a note on an issue you want to discuss more on! +You can also have a look at our `Todo List` in our [Code Documentation](https://cockatrice.github.io/docs) or search the repo for [`\todo` comments](https://github.com/search?q=repo%3ACockatrice%2FCockatrice%20%5Ctodo&type=code). + Cockatrice tries to use the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide.
diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp index efce40cd7..a61bb5c00 100644 --- a/cockatrice/src/game/player/player.cpp +++ b/cockatrice/src/game/player/player.cpp @@ -263,7 +263,7 @@ void Player::deleteCard(CardItem *card) } } -// TODO: Does a player need a DeckLoader? +//! \todo Does a player need a DeckLoader? void Player::setDeck(DeckLoader &_deck) { deck = new DeckLoader(this, _deck.getDeckList()); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_edit_password.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_edit_password.cpp index 998dafb66..e3bbc7435 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_edit_password.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_edit_password.cpp @@ -59,7 +59,7 @@ DlgEditPassword::DlgEditPassword(QWidget *parent) : QDialog(parent) void DlgEditPassword::actOk() { - // TODO this stuff should be using qvalidators + //! \todo this stuff should be using qvalidators if (newPasswordEdit->text().length() < 8) { QMessageBox::critical(this, tr("Error"), tr("Your password is too short.")); return; diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp index e0d855d7e..c9c41722e 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp @@ -121,7 +121,7 @@ void DlgForgotPasswordReset::actOk() return; } - // TODO this stuff should be using qvalidators + //! \todo this stuff should be using qvalidators if (newpasswordEdit->text().length() < 8) { QMessageBox::critical(this, tr("Error"), tr("Your password is too short.")); return; diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp index 4be4cdf78..a3f232d9b 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp @@ -356,7 +356,7 @@ DlgRegister::DlgRegister(QWidget *parent) : QDialog(parent) void DlgRegister::actOk() { - // TODO this stuff should be using qvalidators + //! \todo this stuff should be using qvalidators if (passwordEdit->text().length() < 8) { QMessageBox::critical(this, tr("Registration Warning"), tr("Your password is too short.")); return; diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp index c0b80c6c1..191cc5d0b 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp @@ -1913,7 +1913,7 @@ void DlgSettings::closeEvent(QCloseEvent *event) } if (!QDir(SettingsCache::instance().getDeckPath()).exists() || SettingsCache::instance().getDeckPath().isEmpty()) { - // TODO: Prompt to create it + //! \todo Prompt to create it if (QMessageBox::critical( this, tr("Error"), tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"), @@ -1924,7 +1924,7 @@ void DlgSettings::closeEvent(QCloseEvent *event) } if (!QDir(SettingsCache::instance().getPicsPath()).exists() || SettingsCache::instance().getPicsPath().isEmpty()) { - // TODO: Prompt to create it + //! \todo Prompt to create it if (QMessageBox::critical(this, tr("Error"), tr("The path to your card pictures directory is invalid. Would you like to go back " "and set the correct path?"), diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp index c97d32d44..694085771 100644 --- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp +++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp @@ -206,7 +206,7 @@ void ChatView::appendMessage(QString message, defaultFormat = QTextCharFormat(); if (!isUserMessage) { if (messageType == Event_RoomSay::ChatHistory) { - defaultFormat.setForeground(Qt::gray); // FIXME : hardcoded color + defaultFormat.setForeground(Qt::gray); //! \todo hardcoded color defaultFormat.setFontWeight(QFont::Light); defaultFormat.setFontItalic(true); static const QRegularExpression userNameRegex("^(\\[[^\\]]*\\]\\s)(\\S+):\\s"); @@ -229,7 +229,7 @@ void ChatView::appendMessage(QString message, message.remove(0, pos.relativePosition - 2); // do not remove semicolon } } else { - defaultFormat.setForeground(Qt::darkGreen); // FIXME : hardcoded color + defaultFormat.setForeground(Qt::darkGreen); //! \todo hardcoded color defaultFormat.setFontWeight(QFont::Bold); } } diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp index 909c4d9eb..5b879f8ae 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp @@ -21,16 +21,16 @@ void ArchidektApiResponseCard::fromJson(const QJsonObject &json) edition.fromJson(json.value("edition").toObject()); flavor = json.value("flavor").toString(); - // TODO but not really important - // games = {""}; - // options = {""}; + //! \todo but not really important + //! \todo games = {""}; + //! \todo options = {""}; scryfallImageHash = json.value("scryfallImageHash").toString(); oracleCard = json.value("oracleCard").toObject(); owned = json.value("owned").toInt(); pinnedStatus = json.value("pinnedStatus").toInt(); rarity = json.value("rarity").toString(); - // TODO but not really important - // globalCategories = {""}; + //! \todo but not really important + //! \todo globalCategories = {""}; } void ArchidektApiResponseCard::debugPrint() const diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h index e4206ebc6..0325b029a 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h @@ -198,8 +198,8 @@ public: /// @name Metadata getters /// The individual metadata getters still exist for backwards compatibility. - /// TODO: Figure out when we can remove them. ///@{ + //! \todo Figure out when we can remove them. const Metadata &getMetadata() const { return metadata; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp index 5ec7b3c5d..e53838695 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp @@ -501,7 +501,7 @@ void Server_Game::addPlayer(Server_AbstractUserInterface *userInterface, allPlayersEver.insert(playerName); // if the original creator of the game joins, give them host status back - // FIXME: transferring host to spectators has side effects + //! \todo transferring host to spectators has side effects if (newParticipant->getUserInfo()->name() == creatorInfo->name()) { hostId = newParticipant->getPlayerId(); sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); diff --git a/libcockatrice_utility/libcockatrice/utility/peglib.h b/libcockatrice_utility/libcockatrice/utility/peglib.h index 459fa5a27..6a5b87b2d 100644 --- a/libcockatrice_utility/libcockatrice/utility/peglib.h +++ b/libcockatrice_utility/libcockatrice/utility/peglib.h @@ -441,8 +441,8 @@ private: size_t id; }; - // TODO: Use unordered_map when heterogeneous lookup is supported in C++20 - // std::unordered_map dic_; + //! \todo Use unordered_map when heterogeneous lookup is supported in C++20 + //! \todo std::unordered_map dic_; std::map> dic_; bool ignore_case_; @@ -3068,7 +3068,7 @@ inline size_t Recovery::parse_core(const char *s, size_t n, c.cut_stack.back() = true; if (c.cut_stack.size() == 1) { - // TODO: Remove unneeded entries in packrat memoise table + //! \todo Remove unneeded entries in packrat memoise table } } diff --git a/oracle/src/zip/unzip.cpp b/oracle/src/zip/unzip.cpp index 1e5910051..8e52ece18 100755 --- a/oracle/src/zip/unzip.cpp +++ b/oracle/src/zip/unzip.cpp @@ -245,7 +245,6 @@ UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev) \internal Parses a local header record and makes some consistency check with the information stored in the Central Directory record for this entry that has been previously parsed. - \todo Optional consistency check (as a ExtractionOptions flag) local file header signature 4 bytes (0x04034b50) version needed to extract 2 bytes @@ -262,6 +261,7 @@ UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev) file name (variable size) extra field (variable size) */ +//! \todo Optional consistency check (as a ExtractionOptions flag) UnZip::ErrorCode UnzipPrivate::parseLocalHeaderRecord(const QString& path, const ZipEntryP& entry) { Q_ASSERT(device); diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 4b33aad12..bc686ad28 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -1207,7 +1207,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C return Response::RespEmailBlackListed; } - // TODO: Move this method outside of the db interface + //! \todo Move this method outside of the db interface QString errorString; if (!sqlInterface->usernameIsValid(userName, errorString)) { if (servatrice->getEnableRegistrationAudit()) @@ -1330,7 +1330,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C bool AbstractServerSocketInterface::tooManyRegistrationAttempts(const QString &ipAddress) { - // TODO: implement + //! \todo implement Q_UNUSED(ipAddress); return false; } diff --git a/servatrice/src/smtp/qxtmailmessage.cpp b/servatrice/src/smtp/qxtmailmessage.cpp index ca003d875..ff376c1a9 100644 --- a/servatrice/src/smtp/qxtmailmessage.cpp +++ b/servatrice/src/smtp/qxtmailmessage.cpp @@ -27,9 +27,8 @@ * \class QxtMailMessage * \inmodule QxtNetwork * \brief The QxtMailMessage class encapsulates an e-mail according to RFC 2822 and related specifications - * TODO: {implicitshared} */ - +//! \todo {implicitshared} #include "qxtmailmessage.h" #include "qxtmail_p.h" From c218a66bcd295d9013e6899c1fd9694587bbe254 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 14 Dec 2025 15:14:11 -0800 Subject: [PATCH 03/43] [DeckFilterString] Rename file search expression (#6413) --- cockatrice/resources/help/deck_search.md | 8 ++++---- cockatrice/src/filters/deck_filter_string.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cockatrice/resources/help/deck_search.md b/cockatrice/resources/help/deck_search.md index 6d58ee590..9ae7164e2 100644 --- a/cockatrice/resources/help/deck_search.md +++ b/cockatrice/resources/help/deck_search.md @@ -15,10 +15,10 @@ searches are case insensitive.
[n:red n:deck n:wins](#n:red n:deck n:wins) (Any deck with a name containing the words red, deck, and wins)
[n:"red deck wins"](#n:%22red deck wins%22) (Any deck with a name containing the exact phrase "red deck wins")
-
File Name:
-
[f:aggro](#f:aggro) (Any deck with a filename containing the word aggro)
-
[f:red f:deck f:wins](#f:red f:deck f:wins) (Any deck with a filename containing the words red, deck, and wins)
-
[f:"red deck wins"](#f:%22red deck wins%22) (Any deck with a filename containing the exact phrase "red deck wins")
+
File Name:
+
[fn:aggro](#fn:aggro) (Any deck with a filename containing the word aggro)
+
[fn:red fn:deck fn:wins](#fn:red fn:deck fn:wins) (Any deck with a filename containing the words red, deck, and wins)
+
[fn:"red deck wins"](#fn:%22red deck wins%22) (Any deck with a filename containing the exact phrase "red deck wins")
Relative Path (starting from the deck folder):
[p:aggro](#p:aggro) (Any deck that has "aggro" somewhere in its relative path)
diff --git a/cockatrice/src/filters/deck_filter_string.cpp b/cockatrice/src/filters/deck_filter_string.cpp index dfc54afe0..186ee0b90 100644 --- a/cockatrice/src/filters/deck_filter_string.cpp +++ b/cockatrice/src/filters/deck_filter_string.cpp @@ -22,7 +22,7 @@ CardSearch <- '[[' CardFilterString ']]' CardFilterString <- (!']]'.)* DeckNameQuery <- ([Dd] 'eck')? [Nn] 'ame'? [:] String -FileNameQuery <- [Ff] ('ile' 'name'?)? [:] String +FileNameQuery <- [Ff] ([Nn] / 'ile' ([Nn] 'ame')?) [:] String PathQuery <- [Pp] 'ath'? [:] String GenericQuery <- String From 589e9a15a6b1f23f1a094cec66542124e957884f Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 14 Dec 2025 15:14:19 -0800 Subject: [PATCH 04/43] [DeckFilterString] Add search query for format (#6414) * [DeckFilterString] Rename file search expression * [DeckFilterString] Add search query for format --- cockatrice/resources/help/deck_search.md | 3 +++ cockatrice/src/filters/deck_filter_string.cpp | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/cockatrice/resources/help/deck_search.md b/cockatrice/resources/help/deck_search.md index 9ae7164e2..c42e41c42 100644 --- a/cockatrice/resources/help/deck_search.md +++ b/cockatrice/resources/help/deck_search.md @@ -24,6 +24,9 @@ searches are case insensitive.
[p:aggro](#p:aggro) (Any deck that has "aggro" somewhere in its relative path)
[p:edh/](#p:edh/) (Any deck with "edh/" in its relative path, A.K.A. decks in the "edh" folder)
+
Format:
+
[f:standard](#f:standard) (Any deck with format set to standard)
+
Deck Contents (Uses [card search expressions](#cardSearchSyntaxHelp)):
[[plains]] (Any deck that contains at least one card with "plains" in its name)
[[t:legendary]] (Any deck that contains at least one legendary)
diff --git a/cockatrice/src/filters/deck_filter_string.cpp b/cockatrice/src/filters/deck_filter_string.cpp index 186ee0b90..ed1a24322 100644 --- a/cockatrice/src/filters/deck_filter_string.cpp +++ b/cockatrice/src/filters/deck_filter_string.cpp @@ -13,7 +13,7 @@ QueryPartList <- ComplexQueryPart ( ws ("AND" ws)? ComplexQueryPart)* ws* ComplexQueryPart <- SomewhatComplexQueryPart ws "OR" ws ComplexQueryPart / SomewhatComplexQueryPart SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart -QueryPart <- NotQuery / DeckContentQuery / DeckNameQuery / FileNameQuery / PathQuery / GenericQuery +QueryPart <- NotQuery / DeckContentQuery / DeckNameQuery / FileNameQuery / PathQuery / FormatQuery / GenericQuery NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart @@ -24,6 +24,7 @@ CardFilterString <- (!']]'.)* DeckNameQuery <- ([Dd] 'eck')? [Nn] 'ame'? [:] String FileNameQuery <- [Ff] ([Nn] / 'ile' ([Nn] 'ame')?) [:] String PathQuery <- [Pp] 'ath'? [:] String +FormatQuery <- [Ff] 'ormat'? [:] String GenericQuery <- String @@ -157,6 +158,14 @@ static void setupParserRules() }; }; + search["FormatQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { + auto format = std::any_cast(sv[0]); + return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { + auto gameFormat = deck->deckLoader->getDeckList()->getGameFormat(); + return QString::compare(format, gameFormat, Qt::CaseInsensitive) == 0; + }; + }; + search["GenericQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { auto name = std::any_cast(sv[0]); return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { From b29909bdbec03d1cb9566bc0b73e0026e908f427 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 14 Dec 2025 15:56:58 -0800 Subject: [PATCH 05/43] [DeckList] Refactor: Create class to RAII underlying tree (#6412) * [DeckList] Create class to RAII underlying tree * Update usages * fixes after rebase * update docs --- libcockatrice_deck_list/CMakeLists.txt | 2 + .../libcockatrice/deck_list/deck_list.cpp | 170 +++------------- .../libcockatrice/deck_list/deck_list.h | 38 ++-- .../deck_list/deck_list_node_tree.cpp | 186 ++++++++++++++++++ .../deck_list/deck_list_node_tree.h | 87 ++++++++ .../models/deck_list/deck_list_model.cpp | 6 +- .../models/deck_list/deck_list_model.h | 4 +- 7 files changed, 325 insertions(+), 168 deletions(-) create mode 100644 libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.cpp create mode 100644 libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.h diff --git a/libcockatrice_deck_list/CMakeLists.txt b/libcockatrice_deck_list/CMakeLists.txt index 5dffff3f7..a7dd01702 100644 --- a/libcockatrice_deck_list/CMakeLists.txt +++ b/libcockatrice_deck_list/CMakeLists.txt @@ -27,6 +27,8 @@ add_library( libcockatrice/deck_list/tree/inner_deck_list_node.cpp libcockatrice/deck_list/deck_list.cpp libcockatrice/deck_list/deck_list_history_manager.cpp + libcockatrice/deck_list/deck_list_node_tree.cpp + libcockatrice/deck_list/deck_list_node_tree.h ) add_dependencies(libcockatrice_deck_list libcockatrice_protocol) diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp index 09326e619..54eaa3096 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp @@ -83,26 +83,23 @@ bool DeckList::Metadata::isEmpty() const } DeckList::DeckList() -{ - root = new InnerDecklistNode; -} - -DeckList::DeckList(const DeckList &other) - : metadata(other.metadata), sideboardPlans(other.sideboardPlans), root(new InnerDecklistNode(other.getRoot())), - cachedDeckHash(other.cachedDeckHash) { } DeckList::DeckList(const QString &nativeString) { - root = new InnerDecklistNode; loadFromString_Native(nativeString); } +DeckList::DeckList(const Metadata &metadata, + const DecklistNodeTree &tree, + const QMap &sideboardPlans) + : metadata(metadata), sideboardPlans(sideboardPlans), tree(tree) +{ +} + DeckList::~DeckList() { - delete root; - QMapIterator i(sideboardPlans); while (i.hasNext()) delete i.next().value(); @@ -152,8 +149,7 @@ bool DeckList::readElement(QXmlStreamReader *xml) } } } else if (childName == "zone") { - InnerDecklistNode *newZone = getZoneObjFromName(xml->attributes().value("name").toString()); - newZone->readElement(xml); + tree.readZoneElement(xml); } else if (childName == "sideboard_plan") { SideboardPlan *newSideboardPlan = new SideboardPlan; if (newSideboardPlan->readElement(xml)) { @@ -195,9 +191,7 @@ void DeckList::write(QXmlStreamWriter *xml) const writeMetadata(xml, metadata); // Write zones - for (int i = 0; i < root->size(); i++) { - root->at(i)->writeElement(xml); - } + tree.write(xml); // Write sideboard plans QMapIterator i(sideboardPlans); @@ -456,25 +450,13 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) QString zoneName = sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; // make new entry in decklist - new DecklistCardNode(cardName, amount, getZoneObjFromName(zoneName), -1, setCode, collectorNumber); + tree.addCard(cardName, amount, zoneName, -1, setCode, collectorNumber); } refreshDeckHash(); return true; } -InnerDecklistNode *DeckList::getZoneObjFromName(const QString &zoneName) -{ - for (int i = 0; i < root->size(); i++) { - auto *node = dynamic_cast(root->at(i)); - if (node->getName() == zoneName) { - return node; - } - } - - return new InnerDecklistNode(zoneName, root); -} - bool DeckList::loadFromFile_Plain(QIODevice *device) { QTextStream in(device); @@ -519,7 +501,7 @@ QString DeckList::writeToString_Plain(bool prefixSideboardCards, bool slashTappe */ void DeckList::cleanList(bool preserveMetadata) { - root->clearTree(); + tree.clear(); if (!preserveMetadata) { metadata = {}; } @@ -528,7 +510,7 @@ void DeckList::cleanList(bool preserveMetadata) QStringList DeckList::getCardList() const { - auto nodes = getCardNodes(); + auto nodes = tree.getCardNodes(); QStringList result; std::transform(nodes.cbegin(), nodes.cend(), std::back_inserter(result), [](auto node) { return node->getName(); }); @@ -538,7 +520,7 @@ QStringList DeckList::getCardList() const QList DeckList::getCardRefList() const { - auto nodes = getCardNodes(); + auto nodes = tree.getCardNodes(); QList result; std::transform(nodes.cbegin(), nodes.cend(), std::back_inserter(result), @@ -547,42 +529,19 @@ QList DeckList::getCardRefList() const return result; } -QList DeckList::getCardNodes(const QStringList &restrictToZones) const +QList DeckList::getCardNodes(const QSet &restrictToZones) const { - QList result; - - auto zoneNodes = getZoneNodes(); - for (auto *zoneNode : zoneNodes) { - if (!restrictToZones.isEmpty() && !restrictToZones.contains(zoneNode->getName())) { - continue; - } - for (auto *cardNode : *zoneNode) { - auto *cardCardNode = dynamic_cast(cardNode); - if (cardCardNode != nullptr) { - result.append(cardCardNode); - } - } - } - - return result; + return tree.getCardNodes(restrictToZones); } QList DeckList::getZoneNodes() const { - QList zones; - for (auto *node : *root) { - InnerDecklistNode *currentZone = dynamic_cast(node); - if (!currentZone) - continue; - zones.append(currentZone); - } - - return zones; + return tree.getZoneNodes(); } int DeckList::getSideboardSize() const { - auto cards = getCardNodes({DECK_ZONE_SIDE}); + auto cards = tree.getCardNodes({DECK_ZONE_SIDE}); int size = 0; for (auto card : cards) { @@ -594,93 +553,18 @@ int DeckList::getSideboardSize() const DecklistCardNode *DeckList::addCard(const QString &cardName, const QString &zoneName, - const int position, + int position, const QString &cardSetName, const QString &cardSetCollectorNumber, const QString &cardProviderId, - const bool formatLegal) + bool formatLegal) { - auto *zoneNode = dynamic_cast(root->findChild(zoneName)); - if (zoneNode == nullptr) { - zoneNode = new InnerDecklistNode(zoneName, root); - } - - auto *node = new DecklistCardNode(cardName, 1, zoneNode, position, cardSetName, cardSetCollectorNumber, - cardProviderId, formatLegal); + auto node = + tree.addCard(cardName, 1, zoneName, position, cardSetName, cardSetCollectorNumber, cardProviderId, formatLegal); refreshDeckHash(); - return node; } -bool DeckList::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode) -{ - if (node == root) { - return true; - } - - bool updateHash = false; - if (rootNode == nullptr) { - rootNode = root; - updateHash = true; - } - - int index = rootNode->indexOf(node); - if (index != -1) { - delete rootNode->takeAt(index); - - if (rootNode->empty()) { - deleteNode(rootNode, rootNode->getParent()); - } - - if (updateHash) { - refreshDeckHash(); - } - - return true; - } - - for (int i = 0; i < rootNode->size(); i++) { - auto *inner = dynamic_cast(rootNode->at(i)); - if (inner) { - if (deleteNode(node, inner)) { - if (updateHash) { - refreshDeckHash(); - } - - return true; - } - } - } - - return false; -} - -static QString computeDeckHash(const DeckList &deckList) -{ - auto mainDeckNodes = deckList.getCardNodes({DECK_ZONE_MAIN}); - auto sideDeckNodes = deckList.getCardNodes({DECK_ZONE_SIDE}); - - static auto nodesToCardList = [](const QList &nodes, const QString &prefix = {}) { - QStringList result; - for (auto node : nodes) { - for (int i = 0; i < node->getNumber(); ++i) { - result.append(prefix + node->getName().toLower()); - } - } - return result; - }; - - QStringList cardList = nodesToCardList(mainDeckNodes) + nodesToCardList(sideDeckNodes, "SB:"); - - cardList.sort(); - QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1); - quint64 number = (((quint64)(unsigned char)deckHashArray[0]) << 32) + - (((quint64)(unsigned char)deckHashArray[1]) << 24) + - (((quint64)(unsigned char)deckHashArray[2] << 16)) + - (((quint64)(unsigned char)deckHashArray[3]) << 8) + (quint64)(unsigned char)deckHashArray[4]; - return QString::number(number, 32).rightJustified(8, '0'); -} - /** * Gets the deck hash. * The hash is computed on the first call to this method, and is cached until the decklist is modified. @@ -693,7 +577,7 @@ QString DeckList::getDeckHash() const return cachedDeckHash; } - cachedDeckHash = computeDeckHash(*this); + cachedDeckHash = tree.computeDeckHash(); return cachedDeckHash; } @@ -710,15 +594,7 @@ void DeckList::refreshDeckHash() */ void DeckList::forEachCard(const std::function &func) const { - // Support for this is only possible if the internal structure - // doesn't get more complicated. - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - func(node, card); - } - } + tree.forEachCard(func); } DeckListMemento DeckList::createMemento(const QString &reason) const diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h index 0325b029a..e8c57be67 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h @@ -11,6 +11,7 @@ #define DECKLIST_H #include "deck_list_memento.h" +#include "deck_list_node_tree.h" #include "tree/inner_deck_list_node.h" #include @@ -107,14 +108,14 @@ public: * - Provide hashing for deck identity (deck hash). * * ### Ownership: - * - Owns the root `InnerDecklistNode` tree. + * - Owns the `DecklistNodeTree`. * - Owns `SideboardPlan` instances stored in `sideboardPlans`. * * ### Example workflow: * ``` * DeckList deck; * deck.setName("Mono Red Aggro"); - * deck.addCard("Lightning Bolt", "main", -1); + * deck.addCard("Lightning Bolt", "main"); * deck.addTag("Aggro"); * deck.saveToFile_Native(device); * ``` @@ -140,7 +141,7 @@ public: private: Metadata metadata; ///< Deck metadata that is stored in the deck file QMap sideboardPlans; ///< Named sideboard plans. - InnerDecklistNode *root; ///< Root of the deck tree (zones + cards). + DecklistNodeTree tree; ///< The deck tree (zones + cards). /** * @brief Cached deck hash, recalculated lazily. @@ -148,9 +149,6 @@ private: */ mutable QString cachedDeckHash; - // Helpers for traversing the tree - InnerDecklistNode *getZoneObjFromName(const QString &zoneName); - public: /// @name Metadata setters ///@{ @@ -190,12 +188,24 @@ public: /// @brief Construct an empty deck. explicit DeckList(); - /// @brief Copy constructor (deep copies the node tree) - DeckList(const DeckList &other); /// @brief Construct from a serialized native-format string. explicit DeckList(const QString &nativeString); + /// @brief Construct from components + DeckList(const Metadata &metadata, + const DecklistNodeTree &tree, + const QMap &sideboardPlans = {}); virtual ~DeckList(); + /** + * @brief Gets a pointer to the underlying node tree. + * Note: DO NOT call this method unless the object needs to have access to the underlying model. + * For now, only the DeckListModel should be calling this. + */ + DecklistNodeTree *getTree() + { + return &tree; + } + /// @name Metadata getters /// The individual metadata getters still exist for backwards compatibility. ///@{ @@ -270,25 +280,21 @@ public: void cleanList(bool preserveMetadata = false); bool isEmpty() const { - return root->isEmpty() && metadata.isEmpty() && sideboardPlans.isEmpty(); + return tree.isEmpty() && metadata.isEmpty() && sideboardPlans.isEmpty(); } QStringList getCardList() const; QList getCardRefList() const; - QList getCardNodes(const QStringList &restrictToZones = QStringList()) const; + QList getCardNodes(const QSet &restrictToZones = {}) const; QList getZoneNodes() const; int getSideboardSize() const; - InnerDecklistNode *getRoot() const - { - return root; - } + DecklistCardNode *addCard(const QString &cardName, const QString &zoneName, - int position, + int position = -1, const QString &cardSetName = QString(), const QString &cardSetCollectorNumber = QString(), const QString &cardProviderId = QString(), const bool formatLegal = true); - bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr); ///@} /// @name Deck identity diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.cpp new file mode 100644 index 000000000..8f651a061 --- /dev/null +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.cpp @@ -0,0 +1,186 @@ +#include "deck_list_node_tree.h" + +#include "tree/deck_list_card_node.h" + +#include +#include + +DecklistNodeTree::DecklistNodeTree() : root(new InnerDecklistNode()) +{ +} + +DecklistNodeTree::DecklistNodeTree(const DecklistNodeTree &other) : root(new InnerDecklistNode(other.root)) +{ +} + +DecklistNodeTree &DecklistNodeTree::operator=(const DecklistNodeTree &other) +{ + if (this != &other) { + delete root; + root = new InnerDecklistNode(other.root); + } + return *this; +} + +DecklistNodeTree::~DecklistNodeTree() +{ + delete root; +} + +bool DecklistNodeTree::isEmpty() const +{ + return root->isEmpty(); +} + +void DecklistNodeTree::clear() +{ + root->clearTree(); +} + +QList DecklistNodeTree::getCardNodes(const QSet &restrictToZones) const +{ + QList result; + + for (auto *zoneNode : getZoneNodes()) { + if (!restrictToZones.isEmpty() && !restrictToZones.contains(zoneNode->getName())) { + continue; + } + for (auto *cardNode : *zoneNode) { + auto *cardCardNode = dynamic_cast(cardNode); + if (cardCardNode) { + result.append(cardCardNode); + } + } + } + + return result; +} + +QList DecklistNodeTree::getZoneNodes() const +{ + QList zones; + for (auto *node : *root) { + InnerDecklistNode *currentZone = dynamic_cast(node); + if (!currentZone) + continue; + zones.append(currentZone); + } + + return zones; +} + +QString DecklistNodeTree::computeDeckHash() const +{ + auto mainDeckNodes = getCardNodes({DECK_ZONE_MAIN}); + auto sideDeckNodes = getCardNodes({DECK_ZONE_SIDE}); + + static auto nodesToCardList = [](const QList &nodes, const QString &prefix = {}) { + QStringList result; + for (auto node : nodes) { + for (int i = 0; i < node->getNumber(); ++i) { + result.append(prefix + node->getName().toLower()); + } + } + return result; + }; + + QStringList cardList = nodesToCardList(mainDeckNodes) + nodesToCardList(sideDeckNodes, "SB:"); + + cardList.sort(); + QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1); + quint64 number = (((quint64)(unsigned char)deckHashArray[0]) << 32) + + (((quint64)(unsigned char)deckHashArray[1]) << 24) + + (((quint64)(unsigned char)deckHashArray[2] << 16)) + + (((quint64)(unsigned char)deckHashArray[3]) << 8) + (quint64)(unsigned char)deckHashArray[4]; + return QString::number(number, 32).rightJustified(8, '0'); +} + +void DecklistNodeTree::write(QXmlStreamWriter *xml) const +{ + for (int i = 0; i < root->size(); i++) { + root->at(i)->writeElement(xml); + } +} + +void DecklistNodeTree::readZoneElement(QXmlStreamReader *xml) +{ + QString zoneName = xml->attributes().value("name").toString(); + InnerDecklistNode *newZone = getZoneObjFromName(zoneName); + newZone->readElement(xml); +} + +DecklistCardNode *DecklistNodeTree::addCard(const QString &cardName, + int amount, + const QString &zoneName, + int position, + const QString &cardSetName, + const QString &cardSetCollectorNumber, + const QString &cardProviderId, + const bool formatLegal) +{ + auto *zoneNode = getZoneObjFromName(zoneName); + auto *node = new DecklistCardNode(cardName, amount, zoneNode, position, cardSetName, cardSetCollectorNumber, + cardProviderId, formatLegal); + return node; +} + +bool DecklistNodeTree::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode) +{ + if (node == root) { + return true; + } + + if (rootNode == nullptr) { + rootNode = root; + } + + int index = rootNode->indexOf(node); + if (index != -1) { + delete rootNode->takeAt(index); + + if (rootNode->empty()) { + deleteNode(rootNode, rootNode->getParent()); + } + + return true; + } + + for (int i = 0; i < rootNode->size(); i++) { + auto *inner = dynamic_cast(rootNode->at(i)); + if (inner) { + if (deleteNode(node, inner)) { + return true; + } + } + } + + return false; +} + +void DecklistNodeTree::forEachCard(const std::function &func) const +{ + // Support for this is only possible if the internal structure + // doesn't get more complicated. + for (int i = 0; i < root->size(); i++) { + InnerDecklistNode *node = dynamic_cast(root->at(i)); + for (int j = 0; j < node->size(); j++) { + DecklistCardNode *card = dynamic_cast(node->at(j)); + func(node, card); + } + } +} + +/** + * Gets the InnerDecklistNode that is the root node for the given zone, creating a new node if it doesn't exist. + */ +InnerDecklistNode *DecklistNodeTree::getZoneObjFromName(const QString &zoneName) const +{ + for (int i = 0; i < root->size(); i++) { + auto *node = dynamic_cast(root->at(i)); + if (node->getName() == zoneName) { + return node; + } + } + + return new InnerDecklistNode(zoneName, root); +} diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.h new file mode 100644 index 000000000..5cfd4944d --- /dev/null +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.h @@ -0,0 +1,87 @@ +#ifndef COCKATRICE_DECKLIST_NODE_TREE_H +#define COCKATRICE_DECKLIST_NODE_TREE_H + +#include "libcockatrice/utility/card_ref.h" +#include "tree/deck_list_card_node.h" +#include "tree/inner_deck_list_node.h" + +#include + +class DecklistNodeTree +{ + InnerDecklistNode *root; ///< Root of the deck tree (zones + cards). + +public: + /// @brief Constructs an empty DecklistNodeTree + explicit DecklistNodeTree(); + /// @brief Copy constructor. Deep copies the tree + explicit DecklistNodeTree(const DecklistNodeTree &other); + /// @brief Copy-assignment operator. Deep copies the tree + DecklistNodeTree &operator=(const DecklistNodeTree &other); + + virtual ~DecklistNodeTree(); + + /** + * @brief Gets a pointer to the underlying root node. + * Note: DO NOT call this method unless the object needs to have access to the underlying model. + * For now, only the DeckListModel should be calling this. + */ + InnerDecklistNode *getRoot() const + { + return root; + } + + bool isEmpty() const; + + /** + * @brief Deletes all nodes except the root. + */ + void clear(); + + /** + * Gets all card nodes in the tree + * @param restrictToZones Only get the nodes in these zones + * @return A QList containing all the card nodes in the zone. + */ + QList getCardNodes(const QSet &restrictToZones = {}) const; + + QList getZoneNodes() const; + + /** + * @brief Computes the deck hash + */ + QString computeDeckHash() const; + + /** + *@brief Writes the contents of the deck to xml + */ + void write(QXmlStreamWriter *xml) const; + + /** + * @brief Reads a "zone" section of the xml to this tree + */ + void readZoneElement(QXmlStreamReader *xml); + + DecklistCardNode *addCard(const QString &cardName, + int amount, + const QString &zoneName, + int position, + const QString &cardSetName = QString(), + const QString &cardSetCollectorNumber = QString(), + const QString &cardProviderId = QString(), + const bool formatLegal = true); + bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr); + + /** + * @brief Apply a function to every card in the deck tree. This can modify the cards. + * + * @param func Function taking (zone node, card node). + */ + void forEachCard(const std::function &func) const; + +private: + // Helpers for traversing the tree + InnerDecklistNode *getZoneObjFromName(const QString &zoneName) const; +}; + +#endif // COCKATRICE_DECKLIST_NODE_TREE_H diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index 05ee480a5..c06fb8268 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -41,7 +41,7 @@ void DeckListModel::rebuildTree() beginResetModel(); root->clearTree(); - InnerDecklistNode *listRoot = deckList->getRoot(); + InnerDecklistNode *listRoot = deckList->getTree()->getRoot(); for (int i = 0; i < listRoot->size(); i++) { auto *currentZone = dynamic_cast(listRoot->at(i)); @@ -313,7 +313,7 @@ bool DeckListModel::removeRows(int row, int count, const QModelIndex &parent) for (int i = 0; i < count; i++) { AbstractDecklistNode *toDelete = node->takeAt(row); if (auto *temp = dynamic_cast(toDelete)) { - deckList->deleteNode(temp->getDataNode()); + deckList->getTree()->deleteNode(temp->getDataNode()); } delete toDelete; } @@ -668,7 +668,7 @@ bool DeckListModel::isCardQuantityLegalForCurrentFormat(const CardInfoPtr cardIn void DeckListModel::refreshCardFormatLegalities() { - InnerDecklistNode *listRoot = deckList->getRoot(); + InnerDecklistNode *listRoot = deckList->getTree()->getRoot(); for (int i = 0; i < listRoot->size(); i++) { auto *currentZone = static_cast(listRoot->at(i)); diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h index c03497bd5..7d8265d7a 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h @@ -202,7 +202,7 @@ public: * affects its hash. * * Slots: - * - rebuildTree(): rebuilds the model structure from the underlying DeckLoader. + * - rebuildTree(): rebuilds the model structure from the underlying node tree. */ class DeckListModel : public QAbstractItemModel { @@ -210,7 +210,7 @@ class DeckListModel : public QAbstractItemModel public slots: /** - * @brief Rebuilds the model tree from the underlying DeckLoader. + * @brief Rebuilds the model tree from the underlying node tree. * * This updates all indices and ensures the model reflects the current * state of the deck. From 9471adb4f7a4ec9de73a4430b2a20f5c1d8f5dfc Mon Sep 17 00:00:00 2001 From: tooomm Date: Mon, 15 Dec 2025 22:55:57 +0100 Subject: [PATCH 06/43] Add repo activity with top contributors acknowledgment (#6420) --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 87629d2c9..47fb3a159 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,11 @@ You can also have a look at our `Todo List` in our [Code Documentation](https:// Cockatrice tries to use the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide. +#### Repository Activity +![Cockatrice Repo Analytics](https://repobeats.axiom.co/api/embed/c7cec938789a5bbaeb4182a028b4dbb96db8f181.svg "Cockatrice Repo Analytics by Repobeats") +
-Kudos to our amazing contributors ❤️ +Kudos to all our amazing contributors ❤️
From 1198db889109ff9d83df9f074b8a43807e6692a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:35:18 +0100 Subject: [PATCH 07/43] Bump actions/cache from 4 to 5 (#6424) --- .github/workflows/desktop-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 58b246071..06549702d 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -166,7 +166,7 @@ jobs: - name: Restore compiler cache (ccache) id: ccache_restore - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} with: @@ -205,7 +205,7 @@ jobs: - name: Save compiler cache (ccache) if: github.ref == 'refs/heads/master' - uses: actions/cache/save@v4 + uses: actions/cache/save@v5 with: path: ${{env.CACHE}} key: ${{ steps.ccache_restore.outputs.cache-primary-key }} From 64bb5355ff5da70b788d0203988a1faf192c8efc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:38:49 +0100 Subject: [PATCH 08/43] Bump peter-evans/create-pull-request from 7 to 8 (#6423) --- .github/workflows/translations-pull.yml | 2 +- .github/workflows/translations-push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/translations-pull.yml b/.github/workflows/translations-pull.yml index 81d6f4e75..ed61e3b19 100644 --- a/.github/workflows/translations-pull.yml +++ b/.github/workflows/translations-pull.yml @@ -33,7 +33,7 @@ jobs: - name: Create pull request if: github.event_name != 'pull_request' id: create_pr - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 with: add-paths: | cockatrice/translations/*.ts diff --git a/.github/workflows/translations-push.yml b/.github/workflows/translations-push.yml index d201e7ad8..777e9e6ac 100644 --- a/.github/workflows/translations-push.yml +++ b/.github/workflows/translations-push.yml @@ -57,7 +57,7 @@ jobs: - name: Create pull request if: github.event_name != 'pull_request' id: create_pr - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 with: add-paths: | cockatrice/cockatrice_en@source.ts From cd443928663cdc7d7b05894af0959d41f44e5255 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 16 Dec 2025 13:03:14 +0100 Subject: [PATCH 09/43] Static helpers. (#6425) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Took 2 minutes Took 29 seconds Co-authored-by: Lukas Brübach --- .../archidekt_api_response_deck_entry_display_widget.cpp | 2 +- libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp | 2 +- .../libcockatrice/models/deck_list/deck_list_model.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp index 7a838d1ff..2998a03bc 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp @@ -16,7 +16,7 @@ #define ARCHIDEKT_DEFAULT_IMAGE "https://storage.googleapis.com/topdekt-user/images/archidekt_deck_card_shadow.jpg" -QString timeAgo(const QString ×tamp) +static QString timeAgo(const QString ×tamp) { QDateTime dt = QDateTime::fromString(timestamp, Qt::ISODate); diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp index 54eaa3096..b453a57ef 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp @@ -164,7 +164,7 @@ bool DeckList::readElement(QXmlStreamReader *xml) return true; } -void writeMetadata(QXmlStreamWriter *xml, const DeckList::Metadata &metadata) +static void writeMetadata(QXmlStreamWriter *xml, const DeckList::Metadata &metadata) { xml->writeTextElement("lastLoadedTimestamp", metadata.lastLoadedTimestamp); xml->writeTextElement("deckname", metadata.name); diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index c06fb8268..992c33961 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -622,7 +622,7 @@ bool DeckListModel::isCardLegalForCurrentFormat(const CardInfoPtr cardInfo) return true; } -int maxAllowedForLegality(const FormatRules &format, const QString &legality) +static int maxAllowedForLegality(const FormatRules &format, const QString &legality) { for (const AllowedCount &c : format.allowedCounts) { if (c.label == legality) { From 41aca8467ab6898864c279eff9f0db9f14125b6d Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 16 Dec 2025 13:12:51 +0100 Subject: [PATCH 10/43] [CardInfoPicture] Defer enlargedPixmap creation until needed (#6426) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [CardInfoPicture] Defer enlargedPixmap creation until needed Took 4 minutes Took 1 minute * Disregard const_cast Took 2 minutes --------- Co-authored-by: Lukas Brübach --- .../cards/card_info_picture_widget.cpp | 30 ++++++++++++------- .../widgets/cards/card_info_picture_widget.h | 3 +- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/cockatrice/src/interface/widgets/cards/card_info_picture_widget.cpp b/cockatrice/src/interface/widgets/cards/card_info_picture_widget.cpp index fc1b9ebe2..85d210e1a 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_picture_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_info_picture_widget.cpp @@ -39,10 +39,6 @@ CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool _hoverT setMouseTracking(true); } - enlargedPixmapWidget = new CardInfoPictureEnlargedWidget(this->window()); - enlargedPixmapWidget->hide(); - connect(this, &QObject::destroyed, enlargedPixmapWidget, &CardInfoPictureEnlargedWidget::deleteLater); - hoverTimer = new QTimer(this); hoverTimer->setSingleShot(true); connect(hoverTimer, &QTimer::timeout, this, &CardInfoPictureWidget::showEnlargedPixmap); @@ -277,7 +273,7 @@ void CardInfoPictureWidget::leaveEvent(QEvent *event) if (hoverToZoomEnabled) { hoverTimer->stop(); - enlargedPixmapWidget->hide(); + destroyEnlargedPixmapWidget(); } if (raiseOnEnter) { @@ -294,7 +290,7 @@ void CardInfoPictureWidget::moveEvent(QMoveEvent *event) QWidget::moveEvent(event); hoverTimer->stop(); - enlargedPixmapWidget->hide(); + destroyEnlargedPixmapWidget(); if (animation->state() == QAbstractAnimation::Running) { return; @@ -310,7 +306,7 @@ void CardInfoPictureWidget::mouseMoveEvent(QMouseEvent *event) { QWidget::mouseMoveEvent(event); - if (hoverToZoomEnabled && enlargedPixmapWidget->isVisible()) { + if (hoverToZoomEnabled && enlargedPixmapWidget && enlargedPixmapWidget->isVisible()) { const QPoint cursorPos = QCursor::pos(); const QRect screenGeometry = QGuiApplication::screenAt(cursorPos)->geometry(); const QSize widgetSize = enlargedPixmapWidget->size(); @@ -344,7 +340,7 @@ void CardInfoPictureWidget::mousePressEvent(QMouseEvent *event) void CardInfoPictureWidget::hideEvent(QHideEvent *event) { - enlargedPixmapWidget->hide(); + destroyEnlargedPixmapWidget(); QWidget::hideEvent(event); } @@ -444,12 +440,19 @@ QMenu *CardInfoPictureWidget::createAddToOpenDeckMenu() * If card information is available, the enlarged pixmap is loaded, positioned near the cursor, * and displayed. */ -void CardInfoPictureWidget::showEnlargedPixmap() const +void CardInfoPictureWidget::showEnlargedPixmap() { if (!exactCard) { return; } + // Lazy creation of the enlarged widget + if (!enlargedPixmapWidget) { + enlargedPixmapWidget = new CardInfoPictureEnlargedWidget(const_cast(this)->window()); + enlargedPixmapWidget->hide(); + connect(this, &QObject::destroyed, enlargedPixmapWidget, &CardInfoPictureEnlargedWidget::deleteLater); + } + const QSize enlargedSize(static_cast(size().width() * 2), static_cast(size().width() * aspectRatio * 2)); enlargedPixmapWidget->setCardPixmap(exactCard, enlargedSize); @@ -460,7 +463,6 @@ void CardInfoPictureWidget::showEnlargedPixmap() const int newX = cursorPos.x() + enlargedPixmapOffset; int newY = cursorPos.y() + enlargedPixmapOffset; - // Adjust if out of bounds if (newX + widgetSize.width() > screenGeometry.right()) { newX = cursorPos.x() - widgetSize.width() - enlargedPixmapOffset; } @@ -472,3 +474,11 @@ void CardInfoPictureWidget::showEnlargedPixmap() const enlargedPixmapWidget->show(); } + +void CardInfoPictureWidget::destroyEnlargedPixmapWidget() +{ + if (enlargedPixmapWidget) { + enlargedPixmapWidget->deleteLater(); + enlargedPixmapWidget = nullptr; + } +} diff --git a/cockatrice/src/interface/widgets/cards/card_info_picture_widget.h b/cockatrice/src/interface/widgets/cards/card_info_picture_widget.h index 980d700f6..c9dbc47ab 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_picture_widget.h +++ b/cockatrice/src/interface/widgets/cards/card_info_picture_widget.h @@ -63,7 +63,8 @@ protected: { return resizedPixmap; } - void showEnlargedPixmap() const; + void showEnlargedPixmap(); + void destroyEnlargedPixmapWidget(); private: ExactCard exactCard; From d47dc358856d21f26af246378badd5a52b5c5ce1 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 16 Dec 2025 13:39:19 +0100 Subject: [PATCH 11/43] [TabArchidekt] Set game format when importing (#6416) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [TabArchidekt] Set game format when importing Took 5 minutes * Move formats to file. Took 9 minutes Took 4 seconds --------- Co-authored-by: Lukas Brübach --- cockatrice/CMakeLists.txt | 1 + .../api_response/archidekt_formats.h | 227 ++++++++++++++++++ .../deck/archidekt_api_response_deck.h | 5 + ...idekt_api_response_deck_display_widget.cpp | 3 + 4 files changed, 236 insertions(+) create mode 100644 cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index c9fa80e6b..4c3679011 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -230,6 +230,7 @@ set(cockatrice_SOURCES src/interface/widgets/tabs/abstract_tab_deck_editor.cpp src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp src/interface/widgets/tabs/api/archidekt/api_response/archidekt_deck_listing_api_response.cpp + src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card_entry.cpp src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_edition.cpp diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h new file mode 100644 index 000000000..ac1b66e14 --- /dev/null +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h @@ -0,0 +1,227 @@ +#ifndef COCKATRICE_ARCHIDEKT_FORMATS_H +#define COCKATRICE_ARCHIDEKT_FORMATS_H +#include +#include + +namespace ArchidektFormats +{ +enum class DeckFormat +{ + Standard = 0, + Modern = 1, + Commander = 2, + Legacy = 3, + Vintage = 4, + Pauper = 5, + Custom = 6, + Frontier = 7, + FutureStandard = 8, + PennyDreadful = 9, + Commander1v1 = 10, + DualCommander = 11, + Brawl = 12, + + // Values outside Archidekt range + Alchemy = 1000, + Historic = 1001, + Gladiator = 1002, + Oathbreaker = 1003, + OldSchool = 1004, + PauperCommander = 1005, + Pioneer = 1006, + PreDH = 1007, + Premodern = 1008, + StandardBrawl = 1009, + Timeless = 1010, + Unknown = 1011 +}; + +inline static QString formatToApiName(DeckFormat format) +{ + switch (format) { + case DeckFormat::Standard: + return "Standard"; + case DeckFormat::Modern: + return "Modern"; + case DeckFormat::Commander: + return "Commander"; + case DeckFormat::Legacy: + return "Legacy"; + case DeckFormat::Vintage: + return "Vintage"; + case DeckFormat::Pauper: + return "Pauper"; + case DeckFormat::Custom: + return "Custom"; + case DeckFormat::Frontier: + return "Frontier"; + case DeckFormat::FutureStandard: + return "Future Std"; + case DeckFormat::PennyDreadful: + return "Penny Dreadful"; + case DeckFormat::Commander1v1: + return "1v1 Commander"; + case DeckFormat::DualCommander: + return "Dual Commander"; + case DeckFormat::Brawl: + return "Brawl"; + + case DeckFormat::Alchemy: + return "Alchemy"; + case DeckFormat::Historic: + return "Historic"; + case DeckFormat::Gladiator: + return "Gladiator"; + case DeckFormat::Oathbreaker: + return "Oathbreaker"; + case DeckFormat::OldSchool: + return "Old School"; + case DeckFormat::PauperCommander: + return "Pauper Commander"; + case DeckFormat::Pioneer: + return "Pioneer"; + case DeckFormat::PreDH: + return "PreDH"; + case DeckFormat::Premodern: + return "Premodern"; + case DeckFormat::StandardBrawl: + return "Standard Brawl"; + case DeckFormat::Timeless: + return "Timeless"; + + default: + return "Unknown"; + } +} + +inline static DeckFormat apiNameToFormat(const QString &name) +{ + const QString n = name.trimmed(); + + if (n.compare("Standard", Qt::CaseInsensitive) == 0) + return DeckFormat::Standard; + if (n.compare("Modern", Qt::CaseInsensitive) == 0) + return DeckFormat::Modern; + if (n.compare("Commander", Qt::CaseInsensitive) == 0) + return DeckFormat::Commander; + if (n.compare("Legacy", Qt::CaseInsensitive) == 0) + return DeckFormat::Legacy; + if (n.compare("Vintage", Qt::CaseInsensitive) == 0) + return DeckFormat::Vintage; + if (n.compare("Pauper", Qt::CaseInsensitive) == 0) + return DeckFormat::Pauper; + if (n.compare("Custom", Qt::CaseInsensitive) == 0) + return DeckFormat::Custom; + if (n.compare("Frontier", Qt::CaseInsensitive) == 0) + return DeckFormat::Frontier; + if (n.compare("Future Std", Qt::CaseInsensitive) == 0) + return DeckFormat::FutureStandard; + if (n.compare("Penny Dreadful", Qt::CaseInsensitive) == 0) + return DeckFormat::PennyDreadful; + if (n.compare("1v1 Commander", Qt::CaseInsensitive) == 0) + return DeckFormat::Commander1v1; + if (n.compare("Dual Commander", Qt::CaseInsensitive) == 0) + return DeckFormat::DualCommander; + if (n.compare("Brawl", Qt::CaseInsensitive) == 0) + return DeckFormat::Brawl; + + if (n.compare("Alchemy", Qt::CaseInsensitive) == 0) + return DeckFormat::Alchemy; + if (n.compare("Historic", Qt::CaseInsensitive) == 0) + return DeckFormat::Historic; + if (n.compare("Gladiator", Qt::CaseInsensitive) == 0) + return DeckFormat::Gladiator; + if (n.compare("Oathbreaker", Qt::CaseInsensitive) == 0) + return DeckFormat::Oathbreaker; + if (n.compare("Old School", Qt::CaseInsensitive) == 0) + return DeckFormat::OldSchool; + if (n.compare("Pauper Commander", Qt::CaseInsensitive) == 0) + return DeckFormat::PauperCommander; + if (n.compare("Pioneer", Qt::CaseInsensitive) == 0) + return DeckFormat::Pioneer; + if (n.compare("PreDH", Qt::CaseInsensitive) == 0) + return DeckFormat::PreDH; + if (n.compare("Premodern", Qt::CaseInsensitive) == 0) + return DeckFormat::Premodern; + if (n.compare("Standard Brawl", Qt::CaseInsensitive) == 0) + return DeckFormat::StandardBrawl; + if (n.compare("Timeless", Qt::CaseInsensitive) == 0) + return DeckFormat::Timeless; + + return DeckFormat::Unknown; +} + +inline static QString formatToCockatriceName(DeckFormat format) +{ + switch (format) { + case DeckFormat::Standard: + return "standard"; + case DeckFormat::Modern: + return "modern"; + case DeckFormat::Commander: + return "commander"; + case DeckFormat::Legacy: + return "legacy"; + case DeckFormat::Vintage: + return "vintage"; + case DeckFormat::Pauper: + return "pauper"; + case DeckFormat::Brawl: + return "brawl"; + case DeckFormat::PennyDreadful: + return "penny"; + case DeckFormat::FutureStandard: + return "future"; + case DeckFormat::Commander1v1: + return "duel"; + case DeckFormat::DualCommander: + return "duel"; + case DeckFormat::Alchemy: + return "alchemy"; + case DeckFormat::Historic: + return "historic"; + case DeckFormat::Gladiator: + return "gladiator"; + case DeckFormat::Oathbreaker: + return "oathbreaker"; + case DeckFormat::OldSchool: + return "oldschool"; + case DeckFormat::PauperCommander: + return "paupercommander"; + case DeckFormat::Pioneer: + return "pioneer"; + case DeckFormat::PreDH: + return "predh"; + case DeckFormat::Premodern: + return "premodern"; + case DeckFormat::StandardBrawl: + return "standardbrawl"; + case DeckFormat::Timeless: + return "timeless"; + + default: + return {}; + } +} + +inline static DeckFormat cockatriceNameToFormat(const QString &apiName) +{ + static const QHash map = { + {"standard", DeckFormat::Standard}, {"modern", DeckFormat::Modern}, + {"commander", DeckFormat::Commander}, {"legacy", DeckFormat::Legacy}, + {"vintage", DeckFormat::Vintage}, {"pauper", DeckFormat::Pauper}, + {"brawl", DeckFormat::Brawl}, {"penny", DeckFormat::PennyDreadful}, + {"future", DeckFormat::FutureStandard}, {"duel", DeckFormat::Commander1v1}, + {"alchemy", DeckFormat::Alchemy}, {"historic", DeckFormat::Historic}, + {"gladiator", DeckFormat::Gladiator}, {"oathbreaker", DeckFormat::Oathbreaker}, + {"oldschool", DeckFormat::OldSchool}, {"paupercommander", DeckFormat::PauperCommander}, + {"pioneer", DeckFormat::Pioneer}, {"predh", DeckFormat::PreDH}, + {"premodern", DeckFormat::Premodern}, {"standardbrawl", DeckFormat::StandardBrawl}, + {"timeless", DeckFormat::Timeless}}; + + return map.value(apiName.toLower(), DeckFormat::Unknown); +} + +} // namespace ArchidektFormats + +#endif // COCKATRICE_ARCHIDEKT_FORMATS_H diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.h b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.h index 8cb81cac3..fce437751 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.h +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.h @@ -39,6 +39,11 @@ public: return name; }; + int getDeckFormat() const + { + return deckFormat; + } + private: int id; QString name; diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp index 63d732c9a..8745ca175 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp @@ -6,6 +6,7 @@ #include "../../../../cards/card_size_widget.h" #include "../../../../cards/deck_card_zone_display_widget.h" #include "../../../../visual_deck_editor/visual_deck_display_options_widget.h" +#include "../api_response/archidekt_formats.h" #include "../api_response/deck/archidekt_api_response_deck.h" #include @@ -93,6 +94,8 @@ void ArchidektApiResponseDeckDisplayWidget::actOpenInDeckEditor() loader->getDeckList()->loadFromString_Native(model->getDeckList()->writeToString_Native()); loader->getDeckList()->setName(response.getDeckName()); + loader->getDeckList()->setGameFormat( + ArchidektFormats::formatToCockatriceName(ArchidektFormats::DeckFormat(response.getDeckFormat() - 1))); emit openInDeckEditor(loader); } From ebb02b27b27fe579449502615b24dd58faa2b49c Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Thu, 18 Dec 2025 09:01:06 +0100 Subject: [PATCH 12/43] [Card DB] Properly pass along set priority controller to parsers (#6430) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Card DB] Properly pass along set priority controller to parsers Took 16 minutes Took 35 seconds * More adjustments. Took 13 minutes --------- Co-authored-by: Lukas Brübach --- dbconverter/src/main.h | 4 +++- .../libcockatrice/card/database/card_database.cpp | 2 +- .../card/database/card_database_loader.cpp | 7 ++++--- .../card/database/card_database_loader.h | 4 +++- .../card/database/parser/card_database_parser.cpp | 6 +++++- .../card/database/parser/card_database_parser.h | 2 ++ .../card/database/parser/cockatrice_xml_3.cpp | 5 +++++ .../card/database/parser/cockatrice_xml_3.h | 2 +- .../card/database/parser/cockatrice_xml_4.cpp | 5 +++-- .../card/database/parser/cockatrice_xml_4.h | 3 ++- .../interface_card_set_priority_controller.h | 2 ++ .../interfaces/noop_card_set_priority_controller.h | 12 ++++++------ oracle/src/oracleimporter.cpp | 2 +- 13 files changed, 38 insertions(+), 18 deletions(-) diff --git a/dbconverter/src/main.h b/dbconverter/src/main.h index d6e734316..72dbe939d 100644 --- a/dbconverter/src/main.h +++ b/dbconverter/src/main.h @@ -1,6 +1,8 @@ #ifndef MAIN_H #define MAIN_H +#include "libcockatrice/interfaces/noop_card_set_priority_controller.h" + #include #include #include @@ -26,7 +28,7 @@ public: bool saveCardDatabase(const QString &fileName) { - CockatriceXml4Parser parser(new NoopCardPreferenceProvider()); + CockatriceXml4Parser parser(new NoopCardPreferenceProvider(), new NoopCardSetPriorityController()); return parser.saveToFile(createDefaultMagicFormats(), sets, cards, fileName); } diff --git a/libcockatrice_card/libcockatrice/card/database/card_database.cpp b/libcockatrice_card/libcockatrice/card/database/card_database.cpp index de84ad814..5c4b408b3 100644 --- a/libcockatrice_card/libcockatrice/card/database/card_database.cpp +++ b/libcockatrice_card/libcockatrice/card/database/card_database.cpp @@ -21,7 +21,7 @@ CardDatabase::CardDatabase(QObject *parent, qRegisterMetaType("CardSetPtr"); // create loader and wire it up - loader = new CardDatabaseLoader(this, this, pathProvider, prefs); + loader = new CardDatabaseLoader(this, this, pathProvider, prefs, setPriorityController); // re-emit loader signals (so other code doesn't need to know about internals) connect(loader, &CardDatabaseLoader::loadingFinished, this, &CardDatabase::cardDatabaseLoadingFinished); connect(loader, &CardDatabaseLoader::loadingFailed, this, &CardDatabase::cardDatabaseLoadingFailed); diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp b/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp index 91bb4c741..716477a59 100644 --- a/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp +++ b/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp @@ -12,12 +12,13 @@ CardDatabaseLoader::CardDatabaseLoader(QObject *parent, CardDatabase *db, ICardDatabasePathProvider *_pathProvider, - ICardPreferenceProvider *_preferenceProvider) + ICardPreferenceProvider *_preferenceProvider, + ICardSetPriorityController *_priorityController) : QObject(parent), database(db), pathProvider(_pathProvider) { // instantiate available parsers here and connect them to the database - availableParsers << new CockatriceXml4Parser(_preferenceProvider); - availableParsers << new CockatriceXml3Parser; + availableParsers << new CockatriceXml4Parser(_preferenceProvider, _priorityController); + availableParsers << new CockatriceXml3Parser(_priorityController); for (auto *p : availableParsers) { // connect parser outputs to the database adders diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_loader.h b/libcockatrice_card/libcockatrice/card/database/card_database_loader.h index f4e690428..861cc95b0 100644 --- a/libcockatrice_card/libcockatrice/card/database/card_database_loader.h +++ b/libcockatrice_card/libcockatrice/card/database/card_database_loader.h @@ -6,6 +6,7 @@ #include #include #include +#include inline Q_LOGGING_CATEGORY(CardDatabaseLoadingLog, "card_database.loading"); inline Q_LOGGING_CATEGORY(CardDatabaseLoadingSuccessOrFailureLog, "card_database.loading.success_or_failure"); @@ -52,7 +53,8 @@ public: explicit CardDatabaseLoader(QObject *parent, CardDatabase *db, ICardDatabasePathProvider *pathProvider, - ICardPreferenceProvider *preferenceProvider); + ICardPreferenceProvider *preferenceProvider, + ICardSetPriorityController *_priorityController); /** @brief Destructor cleans up allocated parsers. */ ~CardDatabaseLoader() override; diff --git a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp b/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp index e9686f816..f7e6bbfcf 100644 --- a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp +++ b/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp @@ -4,6 +4,10 @@ SetNameMap ICardDatabaseParser::sets; +ICardDatabaseParser::ICardDatabaseParser(ICardSetPriorityController *_cardSetPriorityController) + : cardSetPriorityController(_cardSetPriorityController) +{ +} void ICardDatabaseParser::clearSetlist() { sets.clear(); @@ -19,7 +23,7 @@ CardSetPtr ICardDatabaseParser::internalAddSet(const QString &setName, return sets.value(setName); } - CardSetPtr newSet = CardSet::newInstance(new NoopCardSetPriorityController(), setName); + CardSetPtr newSet = CardSet::newInstance(cardSetPriorityController, setName); newSet->setLongName(longName); newSet->setSetType(setType); newSet->setReleaseDate(releaseDate); diff --git a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h b/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h index 93d46a3cc..a8eceab5a 100644 --- a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h +++ b/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h @@ -20,6 +20,7 @@ class ICardDatabaseParser : public QObject { Q_OBJECT public: + ICardDatabaseParser(ICardSetPriorityController *cardSetPriorityController); ~ICardDatabaseParser() override = default; /** @@ -59,6 +60,7 @@ public: protected: /** @brief Cached global list of sets shared between all parsers. */ static SetNameMap sets; + ICardSetPriorityController *cardSetPriorityController; /** * @brief Internal helper to add a set to the global set cache. diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp index 9df396c74..ba27d63c4 100644 --- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp +++ b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp @@ -14,6 +14,11 @@ #define COCKATRICE_XML3_SCHEMALOCATION \ "https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v3/cards.xsd" +CockatriceXml3Parser::CockatriceXml3Parser(ICardSetPriorityController *_cardSetPriorityController) + : ICardDatabaseParser(_cardSetPriorityController) +{ +} + bool CockatriceXml3Parser::getCanParseFile(const QString &fileName, QIODevice &device) { qCInfo(CockatriceXml3Log) << "Trying to parse: " << fileName; diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h index c3a261739..a01b705aa 100644 --- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h +++ b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h @@ -26,7 +26,7 @@ class CockatriceXml3Parser : public ICardDatabaseParser { Q_OBJECT public: - CockatriceXml3Parser() = default; + CockatriceXml3Parser(ICardSetPriorityController *cardSetPriorityController); ~CockatriceXml3Parser() override = default; /** diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp index 0d46aad0f..cc0220526 100644 --- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp +++ b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp @@ -14,8 +14,9 @@ #define COCKATRICE_XML4_SCHEMALOCATION \ "https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v4/cards.xsd" -CockatriceXml4Parser::CockatriceXml4Parser(ICardPreferenceProvider *_cardPreferenceProvider) - : cardPreferenceProvider(_cardPreferenceProvider) +CockatriceXml4Parser::CockatriceXml4Parser(ICardPreferenceProvider *_cardPreferenceProvider, + ICardSetPriorityController *_cardSetPriorityController) + : ICardDatabaseParser(_cardSetPriorityController), cardPreferenceProvider(_cardPreferenceProvider) { } diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h index 5b47e9090..189e79535 100644 --- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h +++ b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h @@ -29,7 +29,8 @@ class CockatriceXml4Parser : public ICardDatabaseParser { Q_OBJECT public: - explicit CockatriceXml4Parser(ICardPreferenceProvider *cardPreferenceProvider); + explicit CockatriceXml4Parser(ICardPreferenceProvider *cardPreferenceProvider, + ICardSetPriorityController *cardSetPriorityController); ~CockatriceXml4Parser() override = default; /** diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h index a4f3184cc..46a5897f7 100644 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h +++ b/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h @@ -1,6 +1,8 @@ #ifndef COCKATRICE_INTERFACE_CARD_SET_PRIORITY_CONTROLLER_H #define COCKATRICE_INTERFACE_CARD_SET_PRIORITY_CONTROLLER_H +#include + class ICardSetPriorityController { public: diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h b/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h index b2410781c..949ab5a91 100644 --- a/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h +++ b/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h @@ -6,25 +6,25 @@ class NoopCardSetPriorityController : public ICardSetPriorityController { public: - void setSortKey(QString /* shortName */, unsigned int /* sortKey */) + void setSortKey(QString /* shortName */, unsigned int /* sortKey */) override { } - void setEnabled(QString /* shortName */, bool /* enabled */) + void setEnabled(QString /* shortName */, bool /* enabled */) override { } - void setIsKnown(QString /* shortName */, bool /* isknown */) + void setIsKnown(QString /* shortName */, bool /* isknown */) override { } - unsigned int getSortKey(QString /* shortName */) + unsigned int getSortKey(QString /* shortName */) override { return 0; } - bool isEnabled(QString /* shortName */) + bool isEnabled(QString /* shortName */) override { return true; } - bool isKnown(QString /* shortName */) + bool isKnown(QString /* shortName */) override { return true; } diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 813df49ae..4b089846f 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -565,7 +565,7 @@ int OracleImporter::startImport() bool OracleImporter::saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion) { - CockatriceXml4Parser parser(new NoopCardPreferenceProvider()); + CockatriceXml4Parser parser(new NoopCardPreferenceProvider(), new NoopCardSetPriorityController()); return parser.saveToFile(createDefaultMagicFormats(), sets, cards, fileName, sourceUrl, sourceVersion); } From ad06a81765397368dffceb4e7993b6cf5b2cb405 Mon Sep 17 00:00:00 2001 From: tooomm Date: Fri, 19 Dec 2025 23:51:56 +0100 Subject: [PATCH 13/43] Enable internal documentation in Doxyfile (#6432) --- Doxyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doxyfile b/Doxyfile index d5a3a4b7a..a2a5fb522 100644 --- a/Doxyfile +++ b/Doxyfile @@ -636,7 +636,7 @@ HIDE_IN_BODY_DOCS = NO # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. -INTERNAL_DOCS = NO +INTERNAL_DOCS = YES # With the correct setting of option CASE_SENSE_NAMES Doxygen will better be # able to match the capabilities of the underlying filesystem. In case the From 715ee1d6fed8298b1dd19dd40817114ccf03ab2d Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Fri, 19 Dec 2025 23:55:19 +0100 Subject: [PATCH 14/43] Revert "Enable internal documentation in Doxyfile (#6432)" (#6433) This reverts commit ad06a81765397368dffceb4e7993b6cf5b2cb405. --- Doxyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doxyfile b/Doxyfile index a2a5fb522..d5a3a4b7a 100644 --- a/Doxyfile +++ b/Doxyfile @@ -636,7 +636,7 @@ HIDE_IN_BODY_DOCS = NO # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. -INTERNAL_DOCS = YES +INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES Doxygen will better be # able to match the capabilities of the underlying filesystem. In case the From 367507e0548d1a941368a9f619275d1e092d6e34 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:25:30 -0800 Subject: [PATCH 15/43] [DeckListModel] Refactor: Don't access underlying decklist for iteration (#6427) * [DeckListModel] Refactor: Don't access underlying decklist for iteration * add docs * extract method --- .../deck_list_statistics_analyzer.cpp | 40 +++++++---------- .../dialogs/dlg_select_set_for_cards.cpp | 44 +++++++------------ .../printing_selector/card_amount_widget.cpp | 15 ++----- ...al_database_display_name_filter_widget.cpp | 10 ++--- .../visual_deck_editor_sample_hand_widget.cpp | 16 +------ .../models/deck_list/deck_list_model.cpp | 32 +++++++------- .../models/deck_list/deck_list_model.h | 16 +++++++ 7 files changed, 73 insertions(+), 100 deletions(-) diff --git a/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp b/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp index a5ddf37a1..3ca18d21c 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp @@ -21,32 +21,26 @@ void DeckListStatisticsAnalyzer::update() manaCurveMap.clear(); manaDevotionMap.clear(); - auto nodes = model->getDeckList()->getCardNodes(); + QList cards = model->getCards(); - for (auto *node : nodes) { - CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(node->getName()); - if (!info) - continue; + for (const ExactCard &card : cards) { + // ---- Mana curve ---- + if (config.computeManaCurve) { + manaCurveMap[card.getInfo().getCmc().toInt()]++; + } - for (int i = 0; i < node->getNumber(); ++i) { - // ---- Mana curve ---- - if (config.computeManaCurve) { - manaCurveMap[info->getCmc().toInt()]++; - } + // ---- Mana base ---- + if (config.computeManaBase) { + auto mana = determineManaProduction(card.getInfo().getText()); + for (auto it = mana.begin(); it != mana.end(); ++it) + manaBaseMap[it.key()] += it.value(); + } - // ---- Mana base ---- - if (config.computeManaBase) { - auto mana = determineManaProduction(info->getText()); - for (auto it = mana.begin(); it != mana.end(); ++it) - manaBaseMap[it.key()] += it.value(); - } - - // ---- Devotion ---- - if (config.computeDevotion) { - auto devo = countManaSymbols(info->getManaCost()); - for (auto &d : devo) - manaDevotionMap[d.first] += d.second; - } + // ---- Devotion ---- + if (config.computeDevotion) { + auto devo = countManaSymbols(card.getInfo().getManaCost()); + for (auto &d : devo) + manaDevotionMap[d.first] += d.second; } } diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp index 2bfe650c3..52768e0e7 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp @@ -224,14 +224,10 @@ QMap DlgSelectSetForCards::getSetsForCards() if (!model) return setCounts; - DeckList *decklist = model->getDeckList(); - if (!decklist) - return setCounts; + QList cardNames = model->getCardNames(); - QList cardsInDeck = decklist->getCardNodes(); - - for (auto currentCard : cardsInDeck) { - CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(currentCard->getName()); + for (auto cardName : cardNames) { + CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName); if (!infoPtr) continue; @@ -267,19 +263,15 @@ void DlgSelectSetForCards::updateCardLists() } } - DeckList *decklist = model->getDeckList(); - if (!decklist) - return; + QList cardNames = model->getCardNames(); - QList cardsInDeck = decklist->getCardNodes(); - - for (auto currentCard : cardsInDeck) { + for (auto cardName : cardNames) { bool found = false; QString foundSetName; // Check across all sets if the card is present for (auto it = selectedCardsBySet.begin(); it != selectedCardsBySet.end(); ++it) { - if (it.value().contains(currentCard->getName())) { + if (it.value().contains(cardName)) { found = true; foundSetName = it.key(); // Store the set name where it was found break; // Stop at the first match @@ -288,16 +280,16 @@ void DlgSelectSetForCards::updateCardLists() if (!found) { // The card was not in any selected set - ExactCard card = CardDatabaseManager::query()->getCard({currentCard->getName()}); + ExactCard card = CardDatabaseManager::query()->getCard({cardName}); CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(uneditedCardsFlowWidget); picture_widget->setCard(card); uneditedCardsFlowWidget->addWidget(picture_widget); } else { - ExactCard card = CardDatabaseManager::query()->getCard( - {currentCard->getName(), CardDatabaseManager::getInstance() - ->query() - ->getSpecificPrinting(currentCard->getName(), foundSetName, "") - .getUuid()}); + ExactCard card = + CardDatabaseManager::query()->getCard({cardName, CardDatabaseManager::getInstance() + ->query() + ->getSpecificPrinting(cardName, foundSetName, "") + .getUuid()}); CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(modifiedCardsFlowWidget); picture_widget->setCard(card); modifiedCardsFlowWidget->addWidget(picture_widget); @@ -356,20 +348,16 @@ QMap DlgSelectSetForCards::getCardsForSets() if (!model) return setCards; - DeckList *decklist = model->getDeckList(); - if (!decklist) - return setCards; + QList cardNames = model->getCardNames(); - QList cardsInDeck = decklist->getCardNodes(); - - for (auto currentCard : cardsInDeck) { - CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(currentCard->getName()); + for (auto cardName : cardNames) { + CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName); if (!infoPtr) continue; SetToPrintingsMap setMap = infoPtr->getSets(); for (auto it = setMap.begin(); it != setMap.end(); ++it) { - setCards[it.key()].append(currentCard->getName()); + setCards[it.key()].append(cardName); } } diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp index 0d0195d34..3d519e595 100644 --- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp @@ -306,19 +306,12 @@ int CardAmountWidget::countCardsInZone(const QString &deckZone) return -1; } - DeckList *decklist = deckModel->getDeckList(); - if (!decklist) { - return -1; - } - - QList cardsInDeck = decklist->getCardNodes({deckZone}); + QList cards = deckModel->getCardsForZone(deckZone); int count = 0; - for (auto currentCard : cardsInDeck) { - for (int k = 0; k < currentCard->getNumber(); ++k) { - if (currentCard->getCardProviderId() == rootCard.getPrinting().getProperty("uuid")) { - count++; - } + for (auto currentCard : cards) { + if (currentCard.getPrinting().getUuid() == rootCard.getPrinting().getProperty("uuid")) { + count++; } } diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp index 9bc506056..b43b11f11 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp @@ -64,14 +64,10 @@ void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck() if (!deckListModel) return; - DeckList *decklist = deckListModel->getDeckList(); - if (!decklist) - return; - QList cardsInDeck = decklist->getCardNodes(); - - for (auto currentCard : cardsInDeck) { - createNameFilter(currentCard->getName()); + QList cardNames = deckListModel->getCardNames(); + for (auto cardName : cardNames) { + createNameFilter(cardName); } updateFilterModel(); diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp index 778706f9a..0badb76ff 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp @@ -76,25 +76,11 @@ void VisualDeckEditorSampleHandWidget::updateDisplay() QList VisualDeckEditorSampleHandWidget::getRandomCards(int amountToGet) { - QList mainDeckCards; QList randomCards; if (!deckListModel) return randomCards; - DeckList *decklist = deckListModel->getDeckList(); - if (!decklist) - return randomCards; - QList cardsInDeck = decklist->getCardNodes({DECK_ZONE_MAIN}); - - // Collect all cards in the main deck, allowing duplicates based on their count - for (auto currentCard : cardsInDeck) { - for (int k = 0; k < currentCard->getNumber(); ++k) { - ExactCard card = CardDatabaseManager::query()->getCard(currentCard->toCardRef()); - if (card) { - mainDeckCards.append(card); - } - } - } + QList mainDeckCards = deckListModel->getCardsForZone(DECK_ZONE_MAIN); if (mainDeckCards.isEmpty()) return randomCards; diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index 992c33961..8859a31fe 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -561,10 +561,8 @@ void DeckListModel::setDeckList(DeckList *_deck) rebuildTree(); } -QList DeckListModel::getCards() const +static QList cardNodesToExactCards(QList nodes) { - auto nodes = deckList->getCardNodes(); - QList cards; for (auto node : nodes) { ExactCard card = CardDatabaseManager::query()->getCard(node->toCardRef()); @@ -580,23 +578,26 @@ QList DeckListModel::getCards() const return cards; } +QList DeckListModel::getCards() const +{ + auto nodes = deckList->getCardNodes(); + return cardNodesToExactCards(nodes); +} + QList DeckListModel::getCardsForZone(const QString &zoneName) const { auto nodes = deckList->getCardNodes({zoneName}); + return cardNodesToExactCards(nodes); +} - QList cards; - for (auto node : nodes) { - ExactCard card = CardDatabaseManager::query()->getCard(node->toCardRef()); - if (card) { - for (int k = 0; k < node->getNumber(); ++k) { - cards.append(card); - } - } else { - qDebug() << "Card not found in database!"; - } - } +QList DeckListModel::getCardNames() const +{ + auto nodes = deckList->getCardNodes(); - return cards; + QList names; + std::transform(nodes.cbegin(), nodes.cend(), std::back_inserter(names), [](auto node) { return node->getName(); }); + + return names; } QList DeckListModel::getZones() const @@ -632,7 +633,6 @@ static int maxAllowedForLegality(const FormatRules &format, const QString &legal return -1; // unknown legality → treat as illegal } - bool DeckListModel::isCardQuantityLegalForCurrentFormat(const CardInfoPtr cardInfo, int quantity) { auto formatRules = CardDatabaseManager::query()->getFormat(deckList->getGameFormat()); diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h index 7d8265d7a..6e8882084 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h @@ -309,9 +309,25 @@ public: } void setDeckList(DeckList *_deck); + /** + * @brief Creates a list consisting of the entries of the model mapped into ExactCards (with each entry looked up + * in the card database). + * If a card node has number > 1, it will be added that many times to the list. + * If an entry's card is not found in the card database, that entry will be left out of the list. + * @return An ordered list of ExactCards + */ [[nodiscard]] QList getCards() const; [[nodiscard]] QList getCardsForZone(const QString &zoneName) const; + + /** + * @brief Gets a deduplicated list of all card names that appear in the model + */ + [[nodiscard]] QList getCardNames() const; + /** + * @brief Gets a list of all zone names that appear in the model + */ [[nodiscard]] QList getZones() const; + bool isCardLegalForCurrentFormat(CardInfoPtr cardInfo); bool isCardQuantityLegalForCurrentFormat(CardInfoPtr cardInfo, int quantity); void refreshCardFormatLegalities(); From d6db21419c5e8d08dafbbcd4e19aab38f58f13af Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:39:00 -0800 Subject: [PATCH 16/43] [Refactor] Pass around LoadedDeck instead of DeckLoader (#6422) --- .../parsers/interface_json_deck_parser.h | 34 +++++------ cockatrice/src/filters/deck_filter_string.cpp | 6 +- .../src/game/deckview/deck_view_container.cpp | 23 ++++---- .../src/game/deckview/deck_view_container.h | 4 +- .../src/game/player/menu/utility_menu.cpp | 6 +- cockatrice/src/game/player/player.cpp | 7 +-- cockatrice/src/game/player/player.h | 10 ++-- cockatrice/src/game/player/player_actions.cpp | 2 +- .../src/interface/deck_loader/deck_loader.cpp | 46 +++++++-------- .../src/interface/deck_loader/deck_loader.h | 28 ++++----- .../src/interface/deck_loader/loaded_deck.h | 4 +- .../deck_preview_card_picture_widget.cpp | 1 - .../deck_editor_deck_dock_widget.cpp | 15 +++-- .../deck_editor_deck_dock_widget.h | 2 +- .../dialogs/dlg_load_deck_from_clipboard.cpp | 31 +++++----- .../dialogs/dlg_load_deck_from_clipboard.h | 23 ++++---- .../dialogs/dlg_load_deck_from_website.cpp | 8 +-- .../dialogs/dlg_load_deck_from_website.h | 4 +- .../interface/widgets/general/home_widget.cpp | 19 +++--- .../interface/widgets/general/home_widget.h | 4 +- .../widgets/tabs/abstract_tab_deck_editor.cpp | 58 +++++++++---------- .../widgets/tabs/abstract_tab_deck_editor.h | 8 +-- ...idekt_api_response_deck_display_widget.cpp | 12 ++-- ...chidekt_api_response_deck_display_widget.h | 8 +-- .../average_deck/edhrec_deck_api_response.cpp | 4 +- .../average_deck/edhrec_deck_api_response.h | 2 +- .../tabs/api/edhrec/tab_edhrec_main.cpp | 2 +- .../interface/widgets/tabs/tab_deck_editor.h | 1 - .../widgets/tabs/tab_deck_storage.cpp | 26 +++++---- .../interface/widgets/tabs/tab_deck_storage.h | 4 +- .../src/interface/widgets/tabs/tab_game.cpp | 9 ++- .../src/interface/widgets/tabs/tab_game.h | 3 +- .../interface/widgets/tabs/tab_supervisor.cpp | 20 +++---- .../interface/widgets/tabs/tab_supervisor.h | 6 +- .../tab_deck_storage_visual.cpp | 6 +- .../tab_deck_storage_visual.h | 4 +- ...al_database_display_name_filter_widget.cpp | 2 +- .../deck_preview_deck_tags_display_widget.cpp | 4 +- .../deck_preview/deck_preview_widget.cpp | 42 +++++++------- .../deck_preview/deck_preview_widget.h | 2 +- ...ual_deck_storage_folder_display_widget.cpp | 2 +- .../visual_deck_storage_sort_widget.cpp | 9 ++- .../visual_deck_storage_tag_filter_widget.cpp | 4 +- .../visual_deck_storage_widget.h | 2 +- 44 files changed, 253 insertions(+), 264 deletions(-) diff --git a/cockatrice/src/client/network/parsers/interface_json_deck_parser.h b/cockatrice/src/client/network/parsers/interface_json_deck_parser.h index e0fe4967a..e19e4f4e4 100644 --- a/cockatrice/src/client/network/parsers/interface_json_deck_parser.h +++ b/cockatrice/src/client/network/parsers/interface_json_deck_parser.h @@ -18,21 +18,21 @@ class IJsonDeckParser public: virtual ~IJsonDeckParser() = default; - virtual DeckLoader *parse(const QJsonObject &obj) = 0; + virtual DeckList parse(const QJsonObject &obj) = 0; }; class ArchidektJsonParser : public IJsonDeckParser { public: - DeckLoader *parse(const QJsonObject &obj) override + DeckList parse(const QJsonObject &obj) override { - DeckLoader *loader = new DeckLoader(nullptr); + DeckList deckList; QString deckName = obj.value("name").toString(); QString deckDescription = obj.value("description").toString(); - loader->getDeckList()->setName(deckName); - loader->getDeckList()->setComments(deckDescription); + deckList.setName(deckName); + deckList.setComments(deckDescription); QString outputText; QTextStream outStream(&outputText); @@ -49,25 +49,25 @@ public: outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n'; } - loader->getDeckList()->loadFromStream_Plain(outStream, false); - loader->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId()); + deckList.loadFromStream_Plain(outStream, false); + deckList.forEachCard(CardNodeFunction::ResolveProviderId()); - return loader; + return deckList; } }; class MoxfieldJsonParser : public IJsonDeckParser { public: - DeckLoader *parse(const QJsonObject &obj) override + DeckList parse(const QJsonObject &obj) override { - DeckLoader *loader = new DeckLoader(nullptr); + DeckList deckList; QString deckName = obj.value("name").toString(); QString deckDescription = obj.value("description").toString(); - loader->getDeckList()->setName(deckName); - loader->getDeckList()->setComments(deckDescription); + deckList.setName(deckName); + deckList.setComments(deckDescription); QString outputText; QTextStream outStream(&outputText); @@ -96,8 +96,8 @@ public: outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n'; } - loader->getDeckList()->loadFromStream_Plain(outStream, false); - loader->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId()); + deckList.loadFromStream_Plain(outStream, false); + deckList.forEachCard(CardNodeFunction::ResolveProviderId()); QJsonObject commandersObj = obj.value("commanders").toObject(); if (!commandersObj.isEmpty()) { @@ -108,12 +108,12 @@ public: QString collectorNumber = cardData.value("cn").toString(); QString providerId = cardData.value("scryfall_id").toString(); - loader->getDeckList()->setBannerCard({commanderName, providerId}); - loader->getDeckList()->addCard(commanderName, DECK_ZONE_MAIN, -1, setName, collectorNumber, providerId); + deckList.setBannerCard({commanderName, providerId}); + deckList.addCard(commanderName, DECK_ZONE_MAIN, -1, setName, collectorNumber, providerId); } } - return loader; + return deckList; } }; diff --git a/cockatrice/src/filters/deck_filter_string.cpp b/cockatrice/src/filters/deck_filter_string.cpp index ed1a24322..5cce5d323 100644 --- a/cockatrice/src/filters/deck_filter_string.cpp +++ b/cockatrice/src/filters/deck_filter_string.cpp @@ -119,7 +119,7 @@ static void setupParserRules() return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) -> bool { int count = 0; - auto cardNodes = deck->deckLoader->getDeckList()->getCardNodes(); + auto cardNodes = deck->deckLoader->getDeck().deckList.getCardNodes(); for (auto node : cardNodes) { auto cardInfoPtr = CardDatabaseManager::query()->getCardInfo(node->getName()); if (!cardInfoPtr.isNull() && cardFilter.check(cardInfoPtr)) { @@ -139,7 +139,7 @@ static void setupParserRules() search["DeckNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { auto name = std::any_cast(sv[0]); return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { - return deck->deckLoader->getDeckList()->getName().contains(name, Qt::CaseInsensitive); + return deck->deckLoader->getDeck().deckList.getName().contains(name, Qt::CaseInsensitive); }; }; @@ -161,7 +161,7 @@ static void setupParserRules() search["FormatQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { auto format = std::any_cast(sv[0]); return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { - auto gameFormat = deck->deckLoader->getDeckList()->getGameFormat(); + auto gameFormat = deck->deckLoader->getDeck().deckList.getGameFormat(); return QString::compare(format, gameFormat, Qt::CaseInsensitive) == 0; }; }; diff --git a/cockatrice/src/game/deckview/deck_view_container.cpp b/cockatrice/src/game/deckview/deck_view_container.cpp index 5d4a07bdf..c7876a12f 100644 --- a/cockatrice/src/game/deckview/deck_view_container.cpp +++ b/cockatrice/src/game/deckview/deck_view_container.cpp @@ -269,12 +269,12 @@ void DeckViewContainer::loadDeckFromFile(const QString &filePath) return; } - loadDeckFromDeckLoader(&deck); + loadDeckFromDeckList(deck.getDeck().deckList); } -void DeckViewContainer::loadDeckFromDeckLoader(DeckLoader *deck) +void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck) { - QString deckString = deck->getDeckList()->writeToString_Native(); + QString deckString = deck.writeToString_Native(); if (deckString.length() > MAX_FILE_LENGTH) { QMessageBox::critical(this, tr("Error"), tr("Deck is greater than maximum file size.")); @@ -308,8 +308,8 @@ void DeckViewContainer::loadFromClipboard() return; } - DeckLoader *deck = dlg.getDeckList(); - loadDeckFromDeckLoader(deck); + DeckList deck = dlg.getDeckList(); + loadDeckFromDeckList(deck); } void DeckViewContainer::loadFromWebsite() @@ -320,16 +320,15 @@ void DeckViewContainer::loadFromWebsite() return; } - DeckLoader *deck = dlg.getDeck(); - loadDeckFromDeckLoader(deck); + DeckList deck = dlg.getDeck(); + loadDeckFromDeckList(deck); } void DeckViewContainer::deckSelectFinished(const Response &r) { const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext); - DeckLoader newDeck(this, new DeckList(QString::fromStdString(resp.deck()))); - CardPictureLoader::cacheCardPixmaps( - CardDatabaseManager::query()->getCards(newDeck.getDeckList()->getCardRefList())); + DeckList newDeck = DeckList(QString::fromStdString(resp.deck())); + CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(newDeck.getCardRefList())); setDeck(newDeck); switchToDeckLoadedView(); } @@ -410,8 +409,8 @@ void DeckViewContainer::setSideboardLocked(bool locked) deckView->resetSideboardPlan(); } -void DeckViewContainer::setDeck(DeckLoader &deck) +void DeckViewContainer::setDeck(const DeckList &deck) { - deckView->setDeck(*deck.getDeckList()); + deckView->setDeck(deck); switchToDeckLoadedView(); } \ No newline at end of file diff --git a/cockatrice/src/game/deckview/deck_view_container.h b/cockatrice/src/game/deckview/deck_view_container.h index 620929789..6d685cd79 100644 --- a/cockatrice/src/game/deckview/deck_view_container.h +++ b/cockatrice/src/game/deckview/deck_view_container.h @@ -85,12 +85,12 @@ public: void setReadyStart(bool ready); void readyAndUpdate(); void setSideboardLocked(bool locked); - void setDeck(DeckLoader &deck); + void setDeck(const DeckList &deck); void setVisualDeckStorageExists(bool exists); public slots: void loadDeckFromFile(const QString &filePath); - void loadDeckFromDeckLoader(DeckLoader *deck); + void loadDeckFromDeckList(const DeckList &deck); }; #endif // DECK_VIEW_CONTAINER_H diff --git a/cockatrice/src/game/player/menu/utility_menu.cpp b/cockatrice/src/game/player/menu/utility_menu.cpp index ab14d629a..37bdcbbaf 100644 --- a/cockatrice/src/game/player/menu/utility_menu.cpp +++ b/cockatrice/src/game/player/menu/utility_menu.cpp @@ -60,13 +60,13 @@ void UtilityMenu::populatePredefinedTokensMenu() clear(); setEnabled(false); predefinedTokens.clear(); - DeckLoader *_deck = player->getDeck(); + const DeckList &deckList = player->getDeck(); - if (!_deck) { + if (deckList.isEmpty()) { return; } - auto tokenCardNodes = _deck->getDeckList()->getCardNodes({DECK_ZONE_TOKENS}); + auto tokenCardNodes = deckList.getCardNodes({DECK_ZONE_TOKENS}); if (!tokenCardNodes.isEmpty()) { setEnabled(true); diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp index a61bb5c00..0723ae6bc 100644 --- a/cockatrice/src/game/player/player.cpp +++ b/cockatrice/src/game/player/player.cpp @@ -32,7 +32,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent) : QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)), playerEventHandler(new PlayerEventHandler(this)), playerActions(new PlayerActions(this)), active(false), - conceded(false), deck(nullptr), zoneId(0), dialogSemaphore(false) + conceded(false), zoneId(0), dialogSemaphore(false) { initializeZones(); @@ -263,10 +263,9 @@ void Player::deleteCard(CardItem *card) } } -//! \todo Does a player need a DeckLoader? -void Player::setDeck(DeckLoader &_deck) +void Player::setDeck(const DeckList &_deck) { - deck = new DeckLoader(this, _deck.getDeckList()); + deck = _deck; emit deckChanged(); } diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h index 0a4fc9e69..0a03b3abe 100644 --- a/cockatrice/src/game/player/player.h +++ b/cockatrice/src/game/player/player.h @@ -9,6 +9,7 @@ #include "../../game_graphics/board/abstract_graphics_item.h" #include "../../interface/widgets/menus/tearoff_menu.h" +#include "../interface/deck_loader/loaded_deck.h" #include "../zones/logic/hand_zone_logic.h" #include "../zones/logic/pile_zone_logic.h" #include "../zones/logic/stack_zone_logic.h" @@ -44,7 +45,6 @@ class ArrowTarget; class CardDatabase; class CardZone; class CommandContainer; -class DeckLoader; class GameCommand; class GameEvent; class PlayerInfo; @@ -66,7 +66,7 @@ class Player : public QObject Q_OBJECT signals: - void openDeckEditor(DeckLoader *deck); + void openDeckEditor(const LoadedDeck &deck); void deckChanged(); void newCardAdded(AbstractCardItem *card); void rearrangeCounters(); @@ -130,9 +130,9 @@ public: return playerMenu; } - void setDeck(DeckLoader &_deck); + void setDeck(const DeckList &_deck); - [[nodiscard]] DeckLoader *getDeck() const + [[nodiscard]] const DeckList &getDeck() const { return deck; } @@ -241,7 +241,7 @@ private: bool active; bool conceded; - DeckLoader *deck; + DeckList deck; int zoneId; QMap zones; diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index 058fdb510..444eecb0d 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -218,7 +218,7 @@ void PlayerActions::actAlwaysLookAtTopCard() void PlayerActions::actOpenDeckInDeckEditor() { - emit player->openDeckEditor(player->getDeck()); + emit player->openDeckEditor({.deckList = player->getDeck()}); } void PlayerActions::actViewGraveyard() diff --git a/cockatrice/src/interface/deck_loader/deck_loader.cpp b/cockatrice/src/interface/deck_loader/deck_loader.cpp index bd5fe3ef2..e567e45fd 100644 --- a/cockatrice/src/interface/deck_loader/deck_loader.cpp +++ b/cockatrice/src/interface/deck_loader/deck_loader.cpp @@ -25,11 +25,7 @@ const QStringList DeckLoader::ACCEPTED_FILE_EXTENSIONS = {"*.cod", "*.dec", "*.d const QStringList DeckLoader::FILE_NAME_FILTERS = { tr("Common deck formats (%1)").arg(ACCEPTED_FILE_EXTENSIONS.join(" ")), tr("All files (*.*)")}; -DeckLoader::DeckLoader(QObject *parent) : QObject(parent), deckList(new DeckList()) -{ -} - -DeckLoader::DeckLoader(QObject *parent, DeckList *_deckList) : QObject(parent), deckList(_deckList) +DeckLoader::DeckLoader(QObject *parent) : QObject(parent) { } @@ -41,17 +37,18 @@ bool DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fm } bool result = false; + DeckList deckList = DeckList(); switch (fmt) { case DeckFileFormat::PlainText: - result = deckList->loadFromFile_Plain(&file); + result = deckList.loadFromFile_Plain(&file); break; case DeckFileFormat::Cockatrice: { - result = deckList->loadFromFile_Native(&file); + result = deckList.loadFromFile_Native(&file); qCInfo(DeckLoaderLog) << "Loaded from" << fileName << "-" << result; if (!result) { qCInfo(DeckLoaderLog) << "Retrying as plain format"; file.seek(0); - result = deckList->loadFromFile_Plain(&file); + result = deckList.loadFromFile_Plain(&file); fmt = DeckFileFormat::PlainText; } break; @@ -62,7 +59,8 @@ bool DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fm } if (result) { - lastLoadInfo = { + loadedDeck.deckList = deckList; + loadedDeck.lastLoadInfo = { .fileName = fileName, .fileFormat = fmt, }; @@ -86,7 +84,7 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, DeckFileFormat::Form watcher->deleteLater(); if (result) { - lastLoadInfo = { + loadedDeck.lastLoadInfo = { .fileName = fileName, .fileFormat = fmt, }; @@ -107,13 +105,13 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, DeckFileFormat::Form switch (fmt) { case DeckFileFormat::PlainText: - return deckList->loadFromFile_Plain(&file); + return loadedDeck.deckList.loadFromFile_Plain(&file); case DeckFileFormat::Cockatrice: { bool result = false; - result = deckList->loadFromFile_Native(&file); + result = loadedDeck.deckList.loadFromFile_Native(&file); if (!result) { file.seek(0); - return deckList->loadFromFile_Plain(&file); + return loadedDeck.deckList.loadFromFile_Plain(&file); } return result; } @@ -129,9 +127,9 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, DeckFileFormat::Form bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId) { - bool result = deckList->loadFromString_Native(nativeString); + bool result = loadedDeck.deckList.loadFromString_Native(nativeString); if (result) { - lastLoadInfo = { + loadedDeck.lastLoadInfo = { .remoteDeckId = remoteDeckId, }; @@ -150,16 +148,16 @@ bool DeckLoader::saveToFile(const QString &fileName, DeckFileFormat::Format fmt) bool result = false; switch (fmt) { case DeckFileFormat::PlainText: - result = deckList->saveToFile_Plain(&file); + result = loadedDeck.deckList.saveToFile_Plain(&file); break; case DeckFileFormat::Cockatrice: - result = deckList->saveToFile_Native(&file); + result = loadedDeck.deckList.saveToFile_Native(&file); qCInfo(DeckLoaderLog) << "Saving to " << fileName << "-" << result; break; } if (result) { - lastLoadInfo = { + loadedDeck.lastLoadInfo = { .fileName = fileName, .fileFormat = fmt, }; @@ -194,18 +192,18 @@ bool DeckLoader::updateLastLoadedTimestamp(const QString &fileName, DeckFileForm // Perform file modifications switch (fmt) { case DeckFileFormat::PlainText: - result = deckList->saveToFile_Plain(&file); + result = loadedDeck.deckList.saveToFile_Plain(&file); break; case DeckFileFormat::Cockatrice: - deckList->setLastLoadedTimestamp(QDateTime::currentDateTime().toString()); - result = deckList->saveToFile_Native(&file); + loadedDeck.deckList.setLastLoadedTimestamp(QDateTime::currentDateTime().toString()); + result = loadedDeck.deckList.saveToFile_Native(&file); break; } file.close(); // Close the file to ensure changes are flushed if (result) { - lastLoadInfo = { + loadedDeck.lastLoadInfo = { .fileName = fileName, .fileFormat = fmt, }; @@ -455,7 +453,7 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName) switch (DeckFileFormat::getFormatFromName(fileName)) { case DeckFileFormat::PlainText: // Save in Cockatrice's native format - result = deckList->saveToFile_Native(&file); + result = loadedDeck.deckList.saveToFile_Native(&file); break; case DeckFileFormat::Cockatrice: qCInfo(DeckLoaderLog) << "File is already in Cockatrice format. No conversion needed."; @@ -476,7 +474,7 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName) } else { qCInfo(DeckLoaderLog) << "Original file deleted successfully:" << fileName; } - lastLoadInfo = { + loadedDeck.lastLoadInfo = { .fileName = newFileName, .fileFormat = DeckFileFormat::Cockatrice, }; diff --git a/cockatrice/src/interface/deck_loader/deck_loader.h b/cockatrice/src/interface/deck_loader/deck_loader.h index 75eb38423..4ceb9006b 100644 --- a/cockatrice/src/interface/deck_loader/deck_loader.h +++ b/cockatrice/src/interface/deck_loader/deck_loader.h @@ -41,28 +41,16 @@ public: }; private: - DeckList *deckList; - LoadedDeck::LoadInfo lastLoadInfo; + LoadedDeck loadedDeck; public: DeckLoader(QObject *parent); - DeckLoader(QObject *parent, DeckList *_deckList); DeckLoader(const DeckLoader &) = delete; DeckLoader &operator=(const DeckLoader &) = delete; - const LoadedDeck::LoadInfo &getLastLoadInfo() const - { - return lastLoadInfo; - } - - void setLastLoadInfo(const LoadedDeck::LoadInfo &info) - { - lastLoadInfo = info; - } - [[nodiscard]] bool hasNotBeenLoaded() const { - return lastLoadInfo.isEmpty(); + return loadedDeck.lastLoadInfo.isEmpty(); } bool loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bool userRequest = false); @@ -88,9 +76,17 @@ public: bool convertToCockatriceFormat(QString fileName); - DeckList *getDeckList() + LoadedDeck &getDeck() { - return deckList; + return loadedDeck; + } + const LoadedDeck &getDeck() const + { + return loadedDeck; + } + void setDeck(const LoadedDeck &deck) + { + loadedDeck = deck; } private: diff --git a/cockatrice/src/interface/deck_loader/loaded_deck.h b/cockatrice/src/interface/deck_loader/loaded_deck.h index 90f3853cf..e07cd0db6 100644 --- a/cockatrice/src/interface/deck_loader/loaded_deck.h +++ b/cockatrice/src/interface/deck_loader/loaded_deck.h @@ -30,8 +30,8 @@ struct LoadedDeck bool isEmpty() const; }; - DeckList deckList; ///< The decklist itself - LoadInfo lastLoadInfo; ///< info about where the deck was loaded from + DeckList deckList; ///< The decklist itself + LoadInfo lastLoadInfo = {}; ///< info about where the deck was loaded from bool isEmpty() const; }; diff --git a/cockatrice/src/interface/widgets/cards/deck_preview_card_picture_widget.cpp b/cockatrice/src/interface/widgets/cards/deck_preview_card_picture_widget.cpp index 486b3302d..b27fe274f 100644 --- a/cockatrice/src/interface/widgets/cards/deck_preview_card_picture_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/deck_preview_card_picture_widget.cpp @@ -17,7 +17,6 @@ * @param outlineColor The color of the outline around the text. * @param fontSize The font size of the overlay text. * @param alignment The alignment of the text within the overlay. - * @param _deckLoader The Deck Loader holding the Deck associated with this preview. * * Sets the widget's size policy and default border style. */ diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 268a13d5f..a9e19195d 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -56,7 +56,7 @@ void DeckEditorDeckDockWidget::createDeckDock() deckModel->setObjectName("deckModel"); connect(deckModel, &DeckListModel::deckHashChanged, this, &DeckEditorDeckDockWidget::updateHash); - deckLoader = new DeckLoader(this, deckModel->getDeckList()); + deckLoader = new DeckLoader(this); proxy = new DeckListStyleProxy(this); proxy->setSourceModel(deckModel); @@ -347,7 +347,7 @@ void DeckEditorDeckDockWidget::updateCard(const QModelIndex /*¤t*/, const void DeckEditorDeckDockWidget::updateName(const QString &name) { emit requestDeckHistorySave( - QString(tr("Rename deck to \"%1\" from \"%2\"")).arg(name).arg(deckLoader->getDeckList()->getName())); + QString(tr("Rename deck to \"%1\" from \"%2\"")).arg(name).arg(deckLoader->getDeck().deckList.getName())); deckModel->getDeckList()->setName(name); deckEditor->setModified(name.isEmpty()); emit nameChanged(); @@ -357,7 +357,7 @@ void DeckEditorDeckDockWidget::updateName(const QString &name) void DeckEditorDeckDockWidget::updateComments() { emit requestDeckHistorySave(tr("Updated comments (was %1 chars, now %2 chars)") - .arg(deckLoader->getDeckList()->getComments().size()) + .arg(deckLoader->getDeck().deckList.getComments().size()) .arg(commentsEdit->toPlainText().size())); deckModel->getDeckList()->setComments(commentsEdit->toPlainText()); @@ -474,13 +474,12 @@ void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck() /** * Sets the currently active deck for this tab - * @param _deck The deck. Takes ownership of the object + * @param _deck The deck. */ -void DeckEditorDeckDockWidget::setDeck(DeckLoader *_deck) +void DeckEditorDeckDockWidget::setDeck(const LoadedDeck &_deck) { - deckLoader = _deck; - deckLoader->setParent(this); - deckModel->setDeckList(deckLoader->getDeckList()); + deckLoader->setDeck(_deck); + deckModel->setDeckList(&deckLoader->getDeck().deckList); connect(deckLoader, &DeckLoader::deckLoaded, deckModel, &DeckListModel::rebuildTree); emit requestDeckHistoryClear(); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h index 08aa38e5e..da175d417 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -57,7 +57,7 @@ public: public slots: void cleanDeck(); void updateBannerCardComboBox(); - void setDeck(DeckLoader *_deck); + void setDeck(const LoadedDeck &_deck); void syncDisplayWidgetsToModel(); void sortDeckModelToDeckView(); DeckLoader *getDeckLoader(); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp index 952364e37..9024b3bdc 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp @@ -66,26 +66,26 @@ void AbstractDlgDeckTextEdit::setText(const QString &text) } /** - * Tries to load the current contents of the contentsEdit into the DeckLoader + * Tries to load the current contents of the contentsEdit into the deckList * - * @param deckLoader The DeckLoader to load the deck into + * @param deckList The deckList to load the deck into * @return Whether the loading was successful */ -bool AbstractDlgDeckTextEdit::loadIntoDeck(DeckLoader *deckLoader) const +bool AbstractDlgDeckTextEdit::loadIntoDeck(DeckList &deckList) const { QString buffer = contentsEdit->toPlainText(); if (buffer.contains("")) { - return deckLoader->getDeckList()->loadFromString_Native(buffer); + return deckList.loadFromString_Native(buffer); } QTextStream stream(&buffer); - if (deckLoader->getDeckList()->loadFromStream_Plain(stream, true)) { + if (deckList.loadFromStream_Plain(stream, true)) { if (loadSetNameAndNumberCheckBox->isChecked()) { - deckLoader->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId()); + deckList.forEachCard(CardNodeFunction::ResolveProviderId()); } else { - deckLoader->getDeckList()->forEachCard(CardNodeFunction::ClearPrintingData()); + deckList.forEachCard(CardNodeFunction::ClearPrintingData()); } return true; } @@ -108,7 +108,7 @@ void AbstractDlgDeckTextEdit::keyPressEvent(QKeyEvent *event) * * @param parent The parent widget */ -DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : AbstractDlgDeckTextEdit(parent), deckList(nullptr) +DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : AbstractDlgDeckTextEdit(parent) { setWindowTitle(tr("Load deck from clipboard")); @@ -122,8 +122,6 @@ void DlgLoadDeckFromClipboard::actRefresh() void DlgLoadDeckFromClipboard::actOK() { - deckList = new DeckLoader(this); - if (loadIntoDeck(deckList)) { accept(); } else { @@ -134,18 +132,15 @@ void DlgLoadDeckFromClipboard::actOK() /** * Creates the dialog window for the "Edit deck in clipboard" action * - * @param _deckLoader The existing deck in the deck editor. Copies the instance + * @param _deckList The existing deck in the deck editor. * @param _annotated Whether to add annotations to the text that is loaded from the deck * @param parent The parent widget */ -DlgEditDeckInClipboard::DlgEditDeckInClipboard(DeckLoader *_deckLoader, bool _annotated, QWidget *parent) - : AbstractDlgDeckTextEdit(parent), annotated(_annotated) +DlgEditDeckInClipboard::DlgEditDeckInClipboard(const DeckList &_deckList, bool _annotated, QWidget *parent) + : AbstractDlgDeckTextEdit(parent), deckList(_deckList), annotated(_annotated) { setWindowTitle(tr("Edit deck in clipboard")); - deckLoader = new DeckLoader(this, _deckLoader->getDeckList()); - deckLoader->setParent(this); - DlgEditDeckInClipboard::actRefresh(); } @@ -165,12 +160,12 @@ static QString deckListToString(const DeckList *deckList, bool addComments) void DlgEditDeckInClipboard::actRefresh() { - setText(deckListToString(deckLoader->getDeckList(), annotated)); + setText(deckListToString(&deckList, annotated)); } void DlgEditDeckInClipboard::actOK() { - if (loadIntoDeck(deckLoader)) { + if (loadIntoDeck(deckList)) { accept(); } else { QMessageBox::critical(this, tr("Error"), tr("Invalid deck list.")); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.h b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.h index 982840228..0e59653ea 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.h +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.h @@ -8,10 +8,11 @@ #ifndef DLG_LOAD_DECK_FROM_CLIPBOARD_H #define DLG_LOAD_DECK_FROM_CLIPBOARD_H +#include "../../deck_loader/loaded_deck.h" + #include #include -class DeckLoader; class QPlainTextEdit; class QPushButton; @@ -35,15 +36,13 @@ public: /** * Gets the loaded deck. Only call this method after this dialog window has been successfully exec'd. * - * The returned DeckLoader is parented to this object; make sure to take ownership of the DeckLoader if you intend - * to use it, since otherwise it will get destroyed once this dlg is destroyed - * @return The DeckLoader + * @return The loaded decklist */ - [[nodiscard]] virtual DeckLoader *getDeckList() const = 0; + [[nodiscard]] virtual const DeckList &getDeckList() = 0; protected: void setText(const QString &text); - bool loadIntoDeck(DeckLoader *deckLoader) const; + bool loadIntoDeck(DeckList &deckList) const; void keyPressEvent(QKeyEvent *event) override; protected slots: @@ -62,12 +61,12 @@ protected slots: void actRefresh() override; private: - DeckLoader *deckList; + DeckList deckList; public: explicit DlgLoadDeckFromClipboard(QWidget *parent = nullptr); - [[nodiscard]] DeckLoader *getDeckList() const override + [[nodiscard]] const DeckList &getDeckList() override { return deckList; } @@ -84,15 +83,15 @@ protected slots: void actRefresh() override; private: - DeckLoader *deckLoader; + DeckList deckList; bool annotated; public: - explicit DlgEditDeckInClipboard(DeckLoader *_deckLoader, bool _annotated, QWidget *parent = nullptr); + explicit DlgEditDeckInClipboard(const DeckList &_deckList, bool _annotated, QWidget *parent = nullptr); - [[nodiscard]] DeckLoader *getDeckList() const override + [[nodiscard]] const DeckList &getDeckList() override { - return deckLoader; + return deckList; } }; diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp index 2b63af1a4..da4135246 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp @@ -97,11 +97,11 @@ void DlgLoadDeckFromWebsite::accept() } // Parse the plain text deck here - DeckLoader *loader = new DeckLoader(this); + DeckList deckList; QTextStream stream(&deckText); - loader->getDeckList()->loadFromStream_Plain(stream, false); - loader->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId()); - deck = loader; + deckList.loadFromStream_Plain(stream, false); + deckList.forEachCard(CardNodeFunction::ResolveProviderId()); + deck = deckList; QDialog::accept(); return; diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h index 84adb760f..fe6f0a7e9 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h @@ -26,9 +26,9 @@ public: explicit DlgLoadDeckFromWebsite(QWidget *parent); void retranslateUi(); bool testValidUrl(); - DeckLoader *deck; + DeckList deck; - DeckLoader *getDeck() + const DeckList &getDeck() const { return deck; } diff --git a/cockatrice/src/interface/widgets/general/home_widget.cpp b/cockatrice/src/interface/widgets/general/home_widget.cpp index 7aa6b40c7..f35f90256 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.cpp +++ b/cockatrice/src/interface/widgets/general/home_widget.cpp @@ -20,10 +20,6 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor) layout = new QGridLayout(this); backgroundSourceCard = new CardInfoPictureArtCropWidget(this); - backgroundSourceDeck = new DeckLoader(this); - - backgroundSourceDeck->loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod", - DeckFileFormat::Cockatrice, false); gradientColors = extractDominantColors(background); @@ -72,13 +68,20 @@ void HomeWidget::initializeBackgroundFromSource() cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); break; case BackgroundSources::DeckFileArt: - backgroundSourceDeck->loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod", - DeckFileFormat::Cockatrice, false); + loadBackgroundSourceDeck(); cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); break; } } +void HomeWidget::loadBackgroundSourceDeck() +{ + DeckLoader deckLoader = DeckLoader(this); + deckLoader.loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod", DeckFileFormat::Cockatrice, + false); + backgroundSourceDeck = deckLoader.getDeck().deckList; +} + void HomeWidget::updateRandomCard() { auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource()); @@ -95,7 +98,7 @@ void HomeWidget::updateRandomCard() newCard.getCardPtr()->getProperty("layout") != "normal"); break; case BackgroundSources::DeckFileArt: - QList cardRefs = backgroundSourceDeck->getDeckList()->getCardRefList(); + QList cardRefs = backgroundSourceDeck.getCardRefList(); ExactCard oldCard = backgroundSourceCard->getCard(); if (!cardRefs.empty()) { @@ -183,7 +186,7 @@ QGroupBox *HomeWidget::createButtons() auto visualDeckEditorButton = new HomeStyledButton(tr("Create New Deck"), gradientColors); connect(visualDeckEditorButton, &QPushButton::clicked, tabSupervisor, - [this] { tabSupervisor->openDeckInNewTab(nullptr); }); + [this] { tabSupervisor->openDeckInNewTab(LoadedDeck()); }); boxLayout->addWidget(visualDeckEditorButton); auto visualDeckStorageButton = new HomeStyledButton(tr("Browse Decks"), gradientColors); connect(visualDeckStorageButton, &QPushButton::clicked, tabSupervisor, diff --git a/cockatrice/src/interface/widgets/general/home_widget.h b/cockatrice/src/interface/widgets/general/home_widget.h index 1db33446a..233fda543 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.h +++ b/cockatrice/src/interface/widgets/general/home_widget.h @@ -40,10 +40,12 @@ private: TabSupervisor *tabSupervisor; QPixmap background; CardInfoPictureArtCropWidget *backgroundSourceCard = nullptr; - DeckLoader *backgroundSourceDeck; + DeckList backgroundSourceDeck; QPixmap overlay; QPair gradientColors; HomeStyledButton *connectButton; + + void loadBackgroundSourceDeck(); }; #endif // HOME_WIDGET_H diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp index 55fa0b95c..cdfa5b529 100644 --- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp +++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp @@ -213,22 +213,22 @@ void AbstractTabDeckEditor::actSwapCard(const ExactCard &card, const QString &zo /** * @brief Opens a deck in this tab. - * @param deck DeckLoader object (takes ownership). + * @param deck The deck */ -void AbstractTabDeckEditor::openDeck(DeckLoader *deck) +void AbstractTabDeckEditor::openDeck(const LoadedDeck &deck) { setDeck(deck); - if (!deck->getLastLoadInfo().fileName.isEmpty()) { - SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(deck->getLastLoadInfo().fileName); + if (!deck.lastLoadInfo.fileName.isEmpty()) { + SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(deck.lastLoadInfo.fileName); } } /** * @brief Sets the currently active deck. - * @param _deck DeckLoader object. + * @param _deck The deck */ -void AbstractTabDeckEditor::setDeck(DeckLoader *_deck) +void AbstractTabDeckEditor::setDeck(const LoadedDeck &_deck) { deckDockWidget->setDeck(_deck); CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(getDeckList()->getCardRefList())); @@ -265,8 +265,8 @@ void AbstractTabDeckEditor::setModified(bool _modified) */ bool AbstractTabDeckEditor::isBlankNewDeck() const { - DeckLoader *deck = deckDockWidget->getDeckLoader(); - return !modified && deck->getDeckList()->isBlankDeck() && deck->hasNotBeenLoaded(); + const LoadedDeck &loadedDeck = deckDockWidget->getDeckLoader()->getDeck(); + return !modified && loadedDeck.isEmpty(); } /** @brief Creates a new deck. Handles opening in new tab if needed. */ @@ -277,7 +277,7 @@ void AbstractTabDeckEditor::actNewDeck() return; if (deckOpenLocation == NEW_TAB) { - emit openDeckEditor(nullptr); + emit openDeckEditor(LoadedDeck()); return; } @@ -382,17 +382,15 @@ void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLo { DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(fileName); - auto *l = new DeckLoader(this); - if (l->loadFromFile(fileName, fmt, true)) { + auto l = DeckLoader(this); + if (l.loadFromFile(fileName, fmt, true)) { if (deckOpenLocation == NEW_TAB) { - emit openDeckEditor(l); - l->deleteLater(); + emit openDeckEditor(l.getDeck()); } else { deckMenu->setSaveStatus(false); - openDeck(l); + openDeck(l.getDeck()); } } else { - l->deleteLater(); QMessageBox::critical(this, tr("Error"), tr("Could not open deck at %1").arg(fileName)); } deckMenu->setSaveStatus(true); @@ -405,16 +403,16 @@ void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLo */ bool AbstractTabDeckEditor::actSaveDeck() { - DeckLoader *const deck = getDeckLoader(); - if (deck->getLastLoadInfo().remoteDeckId != LoadedDeck::LoadInfo::NON_REMOTE_ID) { - QString deckString = deck->getDeckList()->writeToString_Native(); + const LoadedDeck &loadedDeck = getDeckLoader()->getDeck(); + if (loadedDeck.lastLoadInfo.remoteDeckId != LoadedDeck::LoadInfo::NON_REMOTE_ID) { + QString deckString = loadedDeck.deckList.writeToString_Native(); if (deckString.length() > MAX_FILE_LENGTH) { QMessageBox::critical(this, tr("Error"), tr("Could not save remote deck")); return false; } Command_DeckUpload cmd; - cmd.set_deck_id(static_cast(deck->getLastLoadInfo().remoteDeckId)); + cmd.set_deck_id(static_cast(loadedDeck.lastLoadInfo.remoteDeckId)); cmd.set_deck_list(deckString.toStdString()); PendingCommand *pend = AbstractClient::prepareSessionCommand(cmd); @@ -422,9 +420,11 @@ bool AbstractTabDeckEditor::actSaveDeck() tabSupervisor->getClient()->sendCommand(pend); return true; - } else if (deck->getLastLoadInfo().fileName.isEmpty()) + } + if (loadedDeck.lastLoadInfo.fileName.isEmpty()) return actSaveDeckAs(); - else if (deck->saveToFile(deck->getLastLoadInfo().fileName, deck->getLastLoadInfo().fileFormat)) { + + if (getDeckLoader()->saveToFile(loadedDeck.lastLoadInfo.fileName, loadedDeck.lastLoadInfo.fileFormat)) { setModified(false); return true; } @@ -493,9 +493,9 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard() return; if (deckOpenLocation == NEW_TAB) { - emit openDeckEditor(dlg.getDeckList()); + emit openDeckEditor({.deckList = dlg.getDeckList()}); } else { - setDeck(dlg.getDeckList()); + setDeck({.deckList = dlg.getDeckList()}); setModified(true); } @@ -508,11 +508,11 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard() */ void AbstractTabDeckEditor::editDeckInClipboard(bool annotated) { - DlgEditDeckInClipboard dlg(getDeckLoader(), annotated, this); + DlgEditDeckInClipboard dlg(getDeckLoader()->getDeck().deckList, annotated, this); if (!dlg.exec()) return; - setDeck(dlg.getDeckList()); + setDeck({dlg.getDeckList(), getDeckLoader()->getDeck().lastLoadInfo}); setModified(true); deckMenu->setSaveStatus(true); } @@ -576,9 +576,9 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite() return; if (deckOpenLocation == NEW_TAB) { - emit openDeckEditor(dlg.getDeck()); + emit openDeckEditor({.deckList = dlg.getDeck()}); } else { - setDeck(dlg.getDeck()); + setDeck({.deckList = dlg.getDeck()}); setModified(true); } @@ -591,8 +591,8 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite() */ void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite website) { - if (DeckLoader *const deck = getDeckLoader()) { - QString decklistUrlString = deck->exportDeckToDecklist(getDeckList(), website); + if (DeckList *deckList = getDeckList()) { + QString decklistUrlString = DeckLoader::exportDeckToDecklist(deckList, website); // Check to make sure the string isn't empty. if (decklistUrlString.isEmpty()) { // Show an error if the deck is empty, and return. diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h index 8544e6c00..e1ffa45de 100644 --- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h +++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h @@ -114,9 +114,9 @@ public: virtual void retranslateUi() override = 0; /** @brief Opens a deck in this tab. - * @param deck Pointer to a DeckLoader object. + * @param deck The deck to open */ - void openDeck(DeckLoader *deck); + void openDeck(const LoadedDeck &deck); /** @brief Returns the currently active deck loader. */ DeckLoader *getDeckLoader() const; @@ -198,7 +198,7 @@ public slots: signals: /** @brief Emitted when a deck should be opened in a new editor tab. */ - void openDeckEditor(DeckLoader *deckLoader); + void openDeckEditor(const LoadedDeck &deck); /** @brief Emitted before the tab is closed. */ void deckEditorClosing(AbstractTabDeckEditor *tab); @@ -286,7 +286,7 @@ private: /** @brief Sets the deck for this tab. * @param _deck The deck object. */ - virtual void setDeck(DeckLoader *_deck); + virtual void setDeck(const LoadedDeck &_deck); /** @brief Helper for editing decks from the clipboard. */ void editDeckInClipboard(bool annotated); diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp index 8745ca175..ef29a1732 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp @@ -90,14 +90,14 @@ void ArchidektApiResponseDeckDisplayWidget::onGroupCriteriaChange(const QString void ArchidektApiResponseDeckDisplayWidget::actOpenInDeckEditor() { - auto loader = new DeckLoader(this); - loader->getDeckList()->loadFromString_Native(model->getDeckList()->writeToString_Native()); - - loader->getDeckList()->setName(response.getDeckName()); - loader->getDeckList()->setGameFormat( + DeckList deckList(*model->getDeckList()); + deckList.setName(response.getDeckName()); + deckList.setGameFormat( ArchidektFormats::formatToCockatriceName(ArchidektFormats::DeckFormat(response.getDeckFormat() - 1))); - emit openInDeckEditor(loader); + LoadedDeck loadedDeck = {deckList, {}}; + + emit openInDeckEditor(loadedDeck); } void ArchidektApiResponseDeckDisplayWidget::clearAllDisplayWidgets() diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.h b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.h index 3d624d293..58aca429e 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.h +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.h @@ -31,7 +31,7 @@ * * ### Signals * - `requestNavigation(QString url)` — triggered when navigation to a deck URL is requested. - * - `openInDeckEditor(DeckLoader *loader)` — emitted when the user chooses to open the deck + * - `openInDeckEditor(const LoadedDeck &deck)` — emitted when the user chooses to open the deck * in the deck editor. * * ### Features @@ -52,9 +52,9 @@ signals: /** * @brief Emitted when the deck should be opened in the deck editor. - * @param loader Initialized DeckLoader containing the deck data. + * @param deck LoadedDeck containing the deck data. */ - void openInDeckEditor(DeckLoader *loader); + void openInDeckEditor(const LoadedDeck &deck); public: /** @@ -75,7 +75,7 @@ public: void retranslateUi(); /** - * @brief Opens the deck in the deck editor via DeckLoader. + * @brief Opens the deck in the deck editor. */ void actOpenInDeckEditor(); diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp index ceafb719b..a744a9b14 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp @@ -14,10 +14,8 @@ void EdhrecDeckApiResponse::fromJson(const QJsonArray &json) deckList += cardlistValue.toString() + "\n"; } - deckLoader = new DeckLoader(nullptr); - QTextStream stream(&deckList); - deckLoader->getDeckList()->loadFromStream_Plain(stream, true); + deck.loadFromStream_Plain(stream, true); } void EdhrecDeckApiResponse::debugPrint() const diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h index 96205e149..cd4f9d99c 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h @@ -21,7 +21,7 @@ public: // Debug method for logging void debugPrint() const; - DeckLoader *deckLoader; + DeckList deck; }; #endif // EDHREC_DECK_API_RESPONSE_H diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp index 92163997c..fb26732f6 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp @@ -363,7 +363,7 @@ void TabEdhRecMain::processAverageDeckResponse(QJsonObject reply) { EdhrecAverageDeckApiResponse deckData; deckData.fromJson(reply); - tabSupervisor->openDeckInNewTab(deckData.deck.deckLoader); + tabSupervisor->openDeckInNewTab({deckData.deck.deck, {}}); } void TabEdhRecMain::prettyPrintJson(const QJsonValue &value, int indentLevel) diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_editor.h b/cockatrice/src/interface/widgets/tabs/tab_deck_editor.h index 9e00099a0..b3ffb8d90 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_deck_editor.h +++ b/cockatrice/src/interface/widgets/tabs/tab_deck_editor.h @@ -12,7 +12,6 @@ class CardDatabaseDisplayModel; class DeckListModel; class QLabel; -class DeckLoader; /** * @class TabDeckEditor diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp index a54c16258..73b3fc0ac 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp @@ -245,7 +245,7 @@ void TabDeckStorage::actOpenLocalDeck() if (!deckLoader->loadFromFile(filePath, DeckFileFormat::Cockatrice, true)) continue; - emit openDeckEditor(deckLoader); + emit openDeckEditor(deckLoader->getDeck()); } } @@ -307,13 +307,15 @@ void TabDeckStorage::uploadDeck(const QString &filePath, const QString &targetPa QFile deckFile(filePath); QFileInfo deckFileInfo(deckFile); - DeckLoader deck(this); - if (!deck.loadFromFile(filePath, DeckFileFormat::Cockatrice)) { + DeckLoader deckLoader(this); + if (!deckLoader.loadFromFile(filePath, DeckFileFormat::Cockatrice)) { QMessageBox::critical(this, tr("Error"), tr("Invalid deck file")); return; } - if (deck.getDeckList()->getName().isEmpty()) { + DeckList deck = deckLoader.getDeck().deckList; + + if (deck.getName().isEmpty()) { bool ok; QString deckName = getTextWithMax(this, tr("Enter deck name"), tr("This decklist does not have a name.\nPlease enter a name:"), @@ -322,12 +324,12 @@ void TabDeckStorage::uploadDeck(const QString &filePath, const QString &targetPa return; if (deckName.isEmpty()) deckName = tr("Unnamed deck"); - deck.getDeckList()->setName(deckName); + deck.setName(deckName); } else { - deck.getDeckList()->setName(deck.getDeckList()->getName().left(MAX_NAME_LENGTH)); + deck.setName(deck.getName().left(MAX_NAME_LENGTH)); } - QString deckString = deck.getDeckList()->writeToString_Native(); + QString deckString = deck.writeToString_Native(); if (deckString.length() > MAX_FILE_LENGTH) { QMessageBox::critical(this, tr("Error"), tr("Invalid deck file")); return; @@ -436,7 +438,7 @@ void TabDeckStorage::openRemoteDeckFinished(const Response &r, const CommandCont if (!loader.loadFromRemote(QString::fromStdString(resp.deck()), cmd.deck_id())) return; - emit openDeckEditor(&loader); + emit openDeckEditor(loader.getDeck()); } void TabDeckStorage::actDownload() @@ -492,8 +494,12 @@ void TabDeckStorage::downloadFinished(const Response &r, const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext); QString filePath = extraData.toString(); - DeckLoader deck(this, new DeckList(QString::fromStdString(resp.deck()))); - deck.saveToFile(filePath, DeckFileFormat::Cockatrice); + DeckList deckList = DeckList(QString::fromStdString(resp.deck())); + + DeckLoader deckLoader(this); + deckLoader.setDeck({deckList, {}}); + + deckLoader.saveToFile(filePath, DeckFileFormat::Cockatrice); } void TabDeckStorage::actNewFolder() diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h index 0226785e3..f8c0497f7 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h +++ b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h @@ -13,6 +13,7 @@ #include +struct LoadedDeck; class ServerInfo_User; class AbstractClient; class QTreeView; @@ -23,7 +24,6 @@ class QTreeWidgetItem; class QGroupBox; class CommandContainer; class Response; -class DeckLoader; class TabDeckStorage : public Tab { @@ -87,7 +87,7 @@ public: return tr("Deck Storage"); } signals: - void openDeckEditor(DeckLoader *deckLoader); + void openDeckEditor(const LoadedDeck &deck); }; #endif diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.cpp b/cockatrice/src/interface/widgets/tabs/tab_game.cpp index 2f83ba2e6..5b6c8d52a 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_game.cpp @@ -749,11 +749,10 @@ void TabGame::loadDeckForLocalPlayer(Player *localPlayer, int playerId, ServerIn { TabbedDeckViewContainer *deckViewContainer = deckViewContainers.value(playerId); if (playerInfo.has_deck_list()) { - DeckLoader newDeck(this, new DeckList(QString::fromStdString(playerInfo.deck_list()))); - CardPictureLoader::cacheCardPixmaps( - CardDatabaseManager::query()->getCards(newDeck.getDeckList()->getCardRefList())); - deckViewContainer->playerDeckView->setDeck(newDeck); - localPlayer->setDeck(newDeck); + DeckList deckList = DeckList(QString::fromStdString(playerInfo.deck_list())); + CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(deckList.getCardRefList())); + deckViewContainer->playerDeckView->setDeck(deckList); + localPlayer->setDeck(deckList); } } diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.h b/cockatrice/src/interface/widgets/tabs/tab_game.h index b6fe5b51c..27374ef2a 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.h +++ b/cockatrice/src/interface/widgets/tabs/tab_game.h @@ -45,7 +45,6 @@ class ReplayTimelineWidget; class CardZone; class AbstractCardItem; class CardItem; -class DeckLoader; class QVBoxLayout; class QHBoxLayout; class GameReplay; @@ -119,7 +118,7 @@ signals: void containerProcessingStarted(const GameEventContext &context); void containerProcessingDone(); void openMessageDialog(const QString &userName, bool focus); - void openDeckEditor(DeckLoader *deck); + void openDeckEditor(const LoadedDeck &deck); void notIdle(); void phaseChanged(int phase); diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp index df9387a6c..b10d615ff 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp @@ -133,10 +133,10 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget * // create tabs menu actions aTabDeckEditor = new QAction(this); - connect(aTabDeckEditor, &QAction::triggered, this, [this] { addDeckEditorTab(nullptr); }); + connect(aTabDeckEditor, &QAction::triggered, this, [this] { addDeckEditorTab(LoadedDeck()); }); aTabVisualDeckEditor = new QAction(this); - connect(aTabVisualDeckEditor, &QAction::triggered, this, [this] { addVisualDeckEditorTab(nullptr); }); + connect(aTabVisualDeckEditor, &QAction::triggered, this, [this] { addVisualDeckEditorTab(LoadedDeck()); }); aTabEdhRec = new QAction(this); connect(aTabEdhRec, &QAction::triggered, this, [this] { addEdhrecMainTab(); }); @@ -846,9 +846,9 @@ void TabSupervisor::talkLeft(TabMessage *tab) /** * Creates a new deck editor tab and loads the deck into it. * Creates either a classic or visual deck editor tab depending on settings - * @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance. + * @param deckToOpen The deck to open in the tab. */ -void TabSupervisor::openDeckInNewTab(DeckLoader *deckToOpen) +void TabSupervisor::openDeckInNewTab(const LoadedDeck &deckToOpen) { int type = SettingsCache::instance().getDefaultDeckEditorType(); switch (type) { @@ -868,13 +868,12 @@ void TabSupervisor::openDeckInNewTab(DeckLoader *deckToOpen) /** * Creates a new deck editor tab - * @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance. + * @param deckToOpen The deck to open in the tab. */ -TabDeckEditor *TabSupervisor::addDeckEditorTab(DeckLoader *deckToOpen) +TabDeckEditor *TabSupervisor::addDeckEditorTab(const LoadedDeck &deckToOpen) { auto *tab = new TabDeckEditor(this); - if (deckToOpen) - tab->openDeck(deckToOpen); + tab->openDeck(deckToOpen); connect(tab, &AbstractTabDeckEditor::deckEditorClosing, this, &TabSupervisor::deckEditorClosed); connect(tab, &AbstractTabDeckEditor::openDeckEditor, this, &TabSupervisor::addDeckEditorTab); myAddTab(tab); @@ -883,11 +882,10 @@ TabDeckEditor *TabSupervisor::addDeckEditorTab(DeckLoader *deckToOpen) return tab; } -TabDeckEditorVisual *TabSupervisor::addVisualDeckEditorTab(DeckLoader *deckToOpen) +TabDeckEditorVisual *TabSupervisor::addVisualDeckEditorTab(const LoadedDeck &deckToOpen) { auto *tab = new TabDeckEditorVisual(this); - if (deckToOpen) - tab->openDeck(deckToOpen); + tab->openDeck(deckToOpen); connect(tab, &AbstractTabDeckEditor::deckEditorClosing, this, &TabSupervisor::deckEditorClosed); connect(tab, &AbstractTabDeckEditor::openDeckEditor, this, &TabSupervisor::addVisualDeckEditorTab); myAddTab(tab); diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h index 948ac6d7e..0c4428f83 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h @@ -168,9 +168,9 @@ signals: void showWindowIfHidden(); public slots: - void openDeckInNewTab(DeckLoader *deckToOpen); - TabDeckEditor *addDeckEditorTab(DeckLoader *deckToOpen); - TabDeckEditorVisual *addVisualDeckEditorTab(DeckLoader *deckToOpen); + void openDeckInNewTab(const LoadedDeck &deckToOpen); + TabDeckEditor *addDeckEditorTab(const LoadedDeck &deckToOpen); + TabDeckEditorVisual *addVisualDeckEditorTab(const LoadedDeck &deckToOpen); TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab(); TabEdhRecMain *addEdhrecMainTab(); TabArchidekt *addArchidektTab(); diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp b/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp index 69c80bb35..9570702ed 100644 --- a/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp +++ b/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp @@ -24,11 +24,11 @@ TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor) void TabDeckStorageVisual::actOpenLocalDeck(const QString &filePath) { - auto deckLoader = new DeckLoader(this); - if (!deckLoader->loadFromFile(filePath, DeckFileFormat::getFormatFromName(filePath), true)) { + auto deckLoader = DeckLoader(this); + if (!deckLoader.loadFromFile(filePath, DeckFileFormat::getFormatFromName(filePath), true)) { QMessageBox::critical(this, tr("Error"), tr("Could not open deck at %1").arg(filePath)); return; } - emit openDeckEditor(deckLoader); + emit openDeckEditor(deckLoader.getDeck()); } diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.h b/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.h index a56ce4c24..ccf752e67 100644 --- a/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.h +++ b/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.h @@ -9,9 +9,9 @@ #include "../tab.h" +struct LoadedDeck; class AbstractClient; class CommandContainer; -class DeckLoader; class DeckPreviewWidget; class QFileSystemModel; class QGroupBox; @@ -39,7 +39,7 @@ public slots: void actOpenLocalDeck(const QString &filePath); signals: - void openDeckEditor(DeckLoader *deckLoader); + void openDeckEditor(const LoadedDeck &deck); private: VisualDeckStorageWidget *visualDeckStorageWidget; diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp index b43b11f11..7551954f3 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp @@ -79,7 +79,7 @@ void VisualDatabaseDisplayNameFilterWidget::actLoadFromClipboard() if (!dlg.exec()) return; - QStringList cardsInClipboard = dlg.getDeckList()->getDeckList()->getCardList(); + QStringList cardsInClipboard = dlg.getDeckList().getCardList(); for (QString cardName : cardsInClipboard) { createNameFilter(cardName); } diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp index d3858a135..d57cda1c6 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp @@ -80,7 +80,7 @@ static QStringList findAllKnownTags() auto loader = DeckLoader(nullptr); for (const QString &file : allFiles) { loader.loadFromFile(file, DeckFileFormat::getFormatFromName(file), false); - QStringList tags = loader.getDeckList()->getTags(); + QStringList tags = loader.getDeck().deckList.getTags(); knownTags.append(tags); knownTags.removeDuplicates(); } @@ -125,7 +125,7 @@ static bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath) static void convertFileToCockatriceFormat(DeckPreviewWidget *deckPreviewWidget) { deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath); - deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastLoadInfo().fileName; + deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getDeck().lastLoadInfo.fileName; deckPreviewWidget->refreshBannerCardText(); } diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp index 68243100c..7177879a8 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp @@ -74,16 +74,16 @@ void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess) if (!deckLoadSuccess) { return; } - auto bannerCard = deckLoader->getDeckList()->getBannerCard().name.isEmpty() + auto bannerCard = deckLoader->getDeck().deckList.getBannerCard().name.isEmpty() ? ExactCard() - : CardDatabaseManager::query()->getCard(deckLoader->getDeckList()->getBannerCard()); + : CardDatabaseManager::query()->getCard(deckLoader->getDeck().deckList.getBannerCard()); bannerCardDisplayWidget->setCard(bannerCard); bannerCardDisplayWidget->setFontSize(24); - setFilePath(deckLoader->getLastLoadInfo().fileName); + setFilePath(deckLoader->getDeck().lastLoadInfo.fileName); colorIdentityWidget = new ColorIdentityWidget(this, getColorIdentity()); - deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckLoader->getDeckList()->getTags()); + deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckLoader->getDeck().deckList.getTags()); connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, this, &DeckPreviewWidget::setTags); bannerCardLabel = new QLabel(this); @@ -91,7 +91,7 @@ void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess) bannerCardComboBox = new QComboBox(this); bannerCardComboBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); bannerCardComboBox->setObjectName("bannerCardComboBox"); - bannerCardComboBox->setCurrentText(deckLoader->getDeckList()->getBannerCard().name); + bannerCardComboBox->setCurrentText(deckLoader->getDeck().deckList.getBannerCard().name); bannerCardComboBox->installEventFilter(new NoScrollFilter()); connect(bannerCardComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &DeckPreviewWidget::setBannerCard); @@ -153,7 +153,7 @@ void DeckPreviewWidget::updateTagsVisibility(bool visible) QString DeckPreviewWidget::getColorIdentity() { - QStringList cardList = deckLoader->getDeckList()->getCardList(); + QStringList cardList = deckLoader->getDeck().deckList.getCardList(); if (cardList.isEmpty()) { return {}; } @@ -187,8 +187,8 @@ QString DeckPreviewWidget::getColorIdentity() */ QString DeckPreviewWidget::getDisplayName() const { - return deckLoader->getDeckList()->getName().isEmpty() ? QFileInfo(deckLoader->getLastLoadInfo().fileName).fileName() - : deckLoader->getDeckList()->getName(); + QString deckName = deckLoader->getDeck().deckList.getName(); + return !deckName.isEmpty() ? deckName : QFileInfo(deckLoader->getDeck().lastLoadInfo.fileName).fileName(); } void DeckPreviewWidget::setFilePath(const QString &_filePath) @@ -235,7 +235,7 @@ void DeckPreviewWidget::updateBannerCardComboBox() // Prepare the new items with deduplication QSet> bannerCardSet; - QList cardsInDeck = deckLoader->getDeckList()->getCardNodes(); + QList cardsInDeck = deckLoader->getDeck().deckList.getCardNodes(); for (auto currentCard : cardsInDeck) { for (int k = 0; k < currentCard->getNumber(); ++k) { @@ -269,7 +269,7 @@ void DeckPreviewWidget::updateBannerCardComboBox() bannerCardComboBox->setCurrentIndex(restoredIndex); } else { // Add a placeholder "-" and set it as the current selection - int bannerIndex = bannerCardComboBox->findText(deckLoader->getDeckList()->getBannerCard().name); + int bannerIndex = bannerCardComboBox->findText(deckLoader->getDeck().deckList.getBannerCard().name); if (bannerIndex != -1) { bannerCardComboBox->setCurrentIndex(bannerIndex); } else { @@ -287,7 +287,7 @@ void DeckPreviewWidget::setBannerCard(int /* changedIndex */) { auto [name, id] = bannerCardComboBox->currentData().value>(); CardRef cardRef = {name, id}; - deckLoader->getDeckList()->setBannerCard(cardRef); + deckLoader->getDeck().deckList.setBannerCard(cardRef); deckLoader->saveToFile(filePath, DeckFileFormat::getFormatFromName(filePath)); bannerCardDisplayWidget->setCard(CardDatabaseManager::query()->getCard(cardRef)); } @@ -310,7 +310,7 @@ void DeckPreviewWidget::imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewC void DeckPreviewWidget::setTags(const QStringList &tags) { - deckLoader->getDeckList()->setTags(tags); + deckLoader->getDeck().deckList.setTags(tags); deckLoader->saveToFile(filePath, DeckFileFormat::Cockatrice); } @@ -320,7 +320,7 @@ QMenu *DeckPreviewWidget::createRightClickMenu() menu->setAttribute(Qt::WA_DeleteOnClose); connect(menu->addAction(tr("Open in deck editor")), &QAction::triggered, this, - [this] { emit openDeckEditor(deckLoader); }); + [this] { emit openDeckEditor(deckLoader->getDeck()); }); connect(menu->addAction(tr("Edit Tags")), &QAction::triggered, deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::openTagEditDlg); @@ -334,13 +334,13 @@ QMenu *DeckPreviewWidget::createRightClickMenu() auto saveToClipboardMenu = menu->addMenu(tr("Save Deck to Clipboard")); connect(saveToClipboardMenu->addAction(tr("Annotated")), &QAction::triggered, this, - [this] { DeckLoader::saveToClipboard(deckLoader->getDeckList(), true, true); }); + [this] { DeckLoader::saveToClipboard(&deckLoader->getDeck().deckList, true, true); }); connect(saveToClipboardMenu->addAction(tr("Annotated (No set info)")), &QAction::triggered, this, - [this] { DeckLoader::saveToClipboard(deckLoader->getDeckList(), true, false); }); + [this] { DeckLoader::saveToClipboard(&deckLoader->getDeck().deckList, true, false); }); connect(saveToClipboardMenu->addAction(tr("Not Annotated")), &QAction::triggered, this, - [this] { DeckLoader::saveToClipboard(deckLoader->getDeckList(), false, true); }); + [this] { DeckLoader::saveToClipboard(&deckLoader->getDeck().deckList, false, true); }); connect(saveToClipboardMenu->addAction(tr("Not Annotated (No set info)")), &QAction::triggered, this, - [this] { DeckLoader::saveToClipboard(deckLoader->getDeckList(), false, false); }); + [this] { DeckLoader::saveToClipboard(&deckLoader->getDeck().deckList, false, false); }); menu->addSeparator(); @@ -376,7 +376,7 @@ void DeckPreviewWidget::addSetBannerCardMenu(QMenu *menu) void DeckPreviewWidget::actRenameDeck() { // read input - const QString oldName = deckLoader->getDeckList()->getName(); + const QString oldName = deckLoader->getDeck().deckList.getName(); bool ok; QString newName = QInputDialog::getText(this, "Rename deck", tr("New name:"), QLineEdit::Normal, oldName, &ok); @@ -385,7 +385,7 @@ void DeckPreviewWidget::actRenameDeck() } // write change - deckLoader->getDeckList()->setName(newName); + deckLoader->getDeck().deckList.setName(newName); deckLoader->saveToFile(filePath, DeckFileFormat::getFormatFromName(filePath)); // update VDS @@ -416,9 +416,7 @@ void DeckPreviewWidget::actRenameFile() return; } - LoadedDeck::LoadInfo lastLoadInfo = deckLoader->getLastLoadInfo(); - lastLoadInfo.fileName = newFilePath; - deckLoader->setLastLoadInfo(lastLoadInfo); + deckLoader->getDeck().lastLoadInfo.fileName = newFilePath; // update VDS setFilePath(newFilePath); diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h index 6e6fdb9af..e5975db71 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h +++ b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h @@ -51,7 +51,7 @@ public: signals: void deckLoadRequested(const QString &filePath); - void openDeckEditor(DeckLoader *deck); + void openDeckEditor(const LoadedDeck &deck); public slots: void setFilePath(const QString &filePath); diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp index 883682749..fdb44dff4 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp @@ -211,7 +211,7 @@ QStringList VisualDeckStorageFolderDisplayWidget::gatherAllTagsFromFlowWidget() // Iterate through all DeckPreviewWidgets for (DeckPreviewWidget *display : flowWidget->findChildren()) { // Get tags from each DeckPreviewWidget - QStringList tags = display->deckLoader->getDeckList()->getTags(); + QStringList tags = display->deckLoader->getDeck().deckList.getTags(); // Add tags to the list while avoiding duplicates allTags.append(tags); diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp index cbc78ba50..0eb45cdcc 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp @@ -95,14 +95,17 @@ QList VisualDeckStorageSortWidget::filterFiles(QListdeckLoader->getDeckList()->getName() < widget2->deckLoader->getDeckList()->getName(); + return widget1->deckLoader->getDeck().deckList.getName() < + widget2->deckLoader->getDeck().deckList.getName(); case Alphabetical: return QString::localeAwareCompare(info1.fileName(), info2.fileName()) <= 0; case ByLastModified: return info1.lastModified() > info2.lastModified(); case ByLastLoaded: { - QDateTime time1 = QDateTime::fromString(widget1->deckLoader->getDeckList()->getLastLoadedTimestamp()); - QDateTime time2 = QDateTime::fromString(widget2->deckLoader->getDeckList()->getLastLoadedTimestamp()); + QDateTime time1 = + QDateTime::fromString(widget1->deckLoader->getDeck().deckList.getLastLoadedTimestamp()); + QDateTime time2 = + QDateTime::fromString(widget2->deckLoader->getDeck().deckList.getLastLoadedTimestamp()); return time1 > time2; } } diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp index 1686de054..28fd7a5ca 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp @@ -57,7 +57,7 @@ void VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QListdeckLoader->getDeckList()->getTags(); + QStringList deckTags = deckPreview->deckLoader->getDeck().deckList.getTags(); bool hasAllSelected = std::all_of(selectedTags.begin(), selectedTags.end(), [&deckTags](const QString &tag) { return deckTags.contains(tag); }); @@ -153,7 +153,7 @@ QSet VisualDeckStorageTagFilterWidget::gatherAllTags() const for (DeckPreviewWidget *widget : deckWidgets) { if (widget->checkVisibility()) { - for (const QString &tag : widget->deckLoader->getDeckList()->getTags()) { + for (const QString &tag : widget->deckLoader->getDeck().deckList.getTags()) { allTags.insert(tag); } } diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.h b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.h index b205cb67e..b13c51700 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.h +++ b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.h @@ -53,7 +53,7 @@ public slots: signals: void bannerCardsRefreshed(); void deckLoadRequested(const QString &filePath); - void openDeckEditor(DeckLoader *deck); + void openDeckEditor(const LoadedDeck &deck); private: QVBoxLayout *layout; From 7f1d891e268fe344283f05e11964f3a0306c4cee Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Sat, 20 Dec 2025 15:25:13 +0100 Subject: [PATCH 17/43] [Deprecation] Remove DBConverter from sources. (#6431) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Took 10 minutes Co-authored-by: Lukas Brübach --- .ci/compile.sh | 4 +- CMakeLists.txt | 7 - Dockerfile | 2 +- Doxyfile | 1 - cmake/CMakeDMGSetup.script | 1 - cmake/FindQtRuntime.cmake | 9 +- cmake/NSIS.template.in | 1 - cmake/SignMacApplications.cmake | 2 +- dbconverter/CMakeLists.txt | 133 ---------- dbconverter/src/main.cpp | 66 ----- dbconverter/src/main.h | 102 -------- dbconverter/src/mocks.cpp | 451 -------------------------------- dbconverter/src/mocks.h | 24 -- format.sh | 1 - 14 files changed, 6 insertions(+), 798 deletions(-) delete mode 100644 dbconverter/CMakeLists.txt delete mode 100644 dbconverter/src/main.cpp delete mode 100644 dbconverter/src/main.h delete mode 100644 dbconverter/src/mocks.cpp delete mode 100644 dbconverter/src/mocks.h diff --git a/.ci/compile.sh b/.ci/compile.sh index a7c349fb0..0af79868b 100755 --- a/.ci/compile.sh +++ b/.ci/compile.sh @@ -122,7 +122,7 @@ if [[ $MAKE_SERVER ]]; then flags+=("-DWITH_SERVER=1") fi if [[ $MAKE_NO_CLIENT ]]; then - flags+=("-DWITH_CLIENT=0" "-DWITH_ORACLE=0" "-DWITH_DBCONVERTER=0") + flags+=("-DWITH_CLIENT=0" "-DWITH_ORACLE=0") fi if [[ $MAKE_TEST ]]; then flags+=("-DTEST=1") @@ -246,7 +246,7 @@ fi if [[ $RUNNER_OS == macOS ]]; then echo "::group::Inspect Mach-O binaries" - for app in cockatrice oracle servatrice dbconverter; do + for app in cockatrice oracle servatrice; do binary="$GITHUB_WORKSPACE/build/$app/$app.app/Contents/MacOS/$app" echo "Inspecting $app..." vtool -show-build "$binary" diff --git a/CMakeLists.txt b/CMakeLists.txt index affc6472d..574ccecb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,8 +20,6 @@ option(WITH_SERVER "build servatrice" OFF) option(WITH_CLIENT "build cockatrice" ON) # Compile oracle option(WITH_ORACLE "build oracle" ON) -# Compile dbconverter -option(WITH_DBCONVERTER "build dbconverter" ON) # Compile tests option(TEST "build tests" OFF) # Use vcpkg regardless of OS @@ -356,11 +354,6 @@ if(WITH_ORACLE) set(CPACK_INSTALL_CMAKE_PROJECTS "Oracle;Oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) endif() -if(WITH_DBCONVERTER) - add_subdirectory(dbconverter) - set(CPACK_INSTALL_CMAKE_PROJECTS "Dbconverter;Dbconverter;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) -endif() - if(TEST) include(CTest) add_subdirectory(tests) diff --git a/Dockerfile b/Dockerfile index 80f9f69f2..d185b746a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ WORKDIR /src COPY . . RUN mkdir build && cd build && \ - cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 && \ + cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 && \ make -j$(nproc) && \ make install diff --git a/Doxyfile b/Doxyfile index d5a3a4b7a..ecacc6b8f 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1068,7 +1068,6 @@ RECURSIVE = YES EXCLUDE = build/ \ cmake/ \ - dbconverter/ \ vcpkg/ \ webclient/ diff --git a/cmake/CMakeDMGSetup.script b/cmake/CMakeDMGSetup.script index fe02eea13..e9a826eb8 100644 --- a/cmake/CMakeDMGSetup.script +++ b/cmake/CMakeDMGSetup.script @@ -42,7 +42,6 @@ tell disk image_name set position of item "Cockatrice.app" to { 139, 214 } set position of item "Oracle.app" to { 139, 414 } set position of item "Servatrice.app" to { 139, 614 } - set position of item "dbconverter.app" to { 1400, 1400 } set position of item "Applications" to { 861, 414 } end tell update without registering applications diff --git a/cmake/FindQtRuntime.cmake b/cmake/FindQtRuntime.cmake index 769f9b463..6be08a694 100644 --- a/cmake/FindQtRuntime.cmake +++ b/cmake/FindQtRuntime.cmake @@ -1,12 +1,11 @@ # Find a compatible Qt version -# Inputs: WITH_SERVER, WITH_CLIENT, WITH_ORACLE, WITH_DBCONVERTER, FORCE_USE_QT5 +# Inputs: WITH_SERVER, WITH_CLIENT, WITH_ORACLE, FORCE_USE_QT5 # Optional Input: QT6_DIR -- Hint as to where Qt6 lives on the system # Optional Input: QT5_DIR -- Hint as to where Qt5 lives on the system # Output: COCKATRICE_QT_VERSION_NAME -- Example values: Qt5, Qt6 # Output: SERVATRICE_QT_MODULES # Output: COCKATRICE_QT_MODULES # Output: ORACLE_QT_MODULES -# Output: DBCONVERTER_QT_MODULES # Output: TEST_QT_MODULES set(REQUIRED_QT_COMPONENTS Core) @@ -29,15 +28,12 @@ endif() if(WITH_ORACLE) set(_ORACLE_NEEDED Concurrent Network Svg Widgets) endif() -if(WITH_DBCONVERTER) - set(_DBCONVERTER_NEEDED Network Widgets) -endif() if(TEST) set(_TEST_NEEDED Widgets) endif() set(REQUIRED_QT_COMPONENTS ${REQUIRED_QT_COMPONENTS} ${_SERVATRICE_NEEDED} ${_COCKATRICE_NEEDED} ${_ORACLE_NEEDED} - ${_DBCONVERTER_NEEDED} ${_TEST_NEEDED} + ${_TEST_NEEDED} ) list(REMOVE_DUPLICATES REQUIRED_QT_COMPONENTS) @@ -112,7 +108,6 @@ message(DEBUG "QT_LIBRARY_DIR = ${QT_LIBRARY_DIR}") string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" SERVATRICE_QT_MODULES "${_SERVATRICE_NEEDED}") string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" COCKATRICE_QT_MODULES "${_COCKATRICE_NEEDED}") string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" ORACLE_QT_MODULES "${_ORACLE_NEEDED}") -string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" DB_CONVERTER_QT_MODULES "${_DBCONVERTER_NEEDED}") string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" TEST_QT_MODULES "${_TEST_NEEDED}") # Core-only export (useful for headless libs) diff --git a/cmake/NSIS.template.in b/cmake/NSIS.template.in index fbdde0b86..a0d51fa2b 100644 --- a/cmake/NSIS.template.in +++ b/cmake/NSIS.template.in @@ -213,7 +213,6 @@ ${AndIf} ${FileExists} "$INSTDIR\portable.dat" Delete "$INSTDIR\uninstall.exe" Delete "$INSTDIR\cockatrice.exe" Delete "$INSTDIR\oracle.exe" - Delete "$INSTDIR\dbconverter.exe" Delete "$INSTDIR\servatrice.exe" Delete "$INSTDIR\Qt*.dll" Delete "$INSTDIR\libmysql.dll" diff --git a/cmake/SignMacApplications.cmake b/cmake/SignMacApplications.cmake index b91e53725..c08c30c1e 100644 --- a/cmake/SignMacApplications.cmake +++ b/cmake/SignMacApplications.cmake @@ -3,7 +3,7 @@ string(LENGTH "$ENV{MACOS_CERTIFICATE_NAME}" MACOS_CERTIFICATE_NAME_LEN) if(APPLE AND MACOS_CERTIFICATE_NAME_LEN GREATER 0) - set(APPLICATIONS "cockatrice" "servatrice" "oracle" "dbconverter") + set(APPLICATIONS "cockatrice" "servatrice" "oracle") foreach(app_name IN LISTS APPLICATIONS) set(FULL_APP_PATH "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/${app_name}.app") diff --git a/dbconverter/CMakeLists.txt b/dbconverter/CMakeLists.txt deleted file mode 100644 index b2de9dcb8..000000000 --- a/dbconverter/CMakeLists.txt +++ /dev/null @@ -1,133 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(Dbconverter VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") - -# ------------------------ -# Sources -# ------------------------ -set(dbconverter_SOURCES src/main.cpp src/mocks.cpp ${VERSION_STRING_CPP}) - -# ------------------------ -# Qt configuration -# ------------------------ -set(QT_DONT_USE_QTGUI TRUE) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -# ------------------------ -# Build executable -# ------------------------ -add_executable(dbconverter MACOSX_BUNDLE ${dbconverter_SOURCES}) - -# ------------------------ -# Link libraries -# ------------------------ -target_link_libraries( - dbconverter - PRIVATE libcockatrice_card - PRIVATE libcockatrice_interfaces - PRIVATE libcockatrice_settings - PRIVATE ${DB_CONVERTER_QT_MODULES} -) - -# ------------------------ -# Install rules -# ------------------------ -if(UNIX) - if(APPLE) - set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}") - set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}") - set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}") - set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) - set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) - set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) - - install(TARGETS dbconverter BUNDLE DESTINATION ./) - else() - # Linux - install(TARGETS dbconverter RUNTIME DESTINATION bin/) - endif() -elseif(WIN32) - install(TARGETS dbconverter RUNTIME DESTINATION ./) -endif() - -# ------------------------ -# Qt plugin handling -# ------------------------ -if(APPLE) - set(plugin_dest_dir dbconverter.app/Contents/Plugins) - set(qtconf_dest_dir dbconverter.app/Contents/Resources) - - install( - DIRECTORY "${QT_PLUGINS_DIR}/" - DESTINATION ${plugin_dest_dir} - COMPONENT Runtime - FILES_MATCHING - PATTERN "*.dSYM" EXCLUDE - PATTERN "*_debug.dylib" EXCLUDE - PATTERN "platforms/*.dylib" - ) - - install( - CODE " - file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] -Plugins = Plugins -Translations = Resources/translations\") - " - COMPONENT Runtime - ) - - install( - CODE " - file(GLOB_RECURSE QTPLUGINS - \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\") - set(BU_CHMOD_BUNDLE_ITEMS ON) - include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/dbconverter.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") - " - COMPONENT Runtime - ) -endif() - -if(WIN32) - set(plugin_dest_dir Plugins) - set(qtconf_dest_dir .) - - install( - DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" - DESTINATION ./ - FILES_MATCHING - PATTERN "*.dll" - ) - - install( - DIRECTORY "${QT_PLUGINS_DIR}/" - DESTINATION ${plugin_dest_dir} - COMPONENT Runtime - FILES_MATCHING - PATTERN "platforms/qdirect2d.dll" - PATTERN "platforms/qminimal.dll" - PATTERN "platforms/qoffscreen.dll" - PATTERN "platforms/qwindows.dll" - ) - - install( - CODE " - file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] -Plugins = Plugins -Translations = Resources/translations\") - " - COMPONENT Runtime - ) - - install( - CODE " - file(GLOB_RECURSE QTPLUGINS - \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dll\") - set(BU_CHMOD_BUNDLE_ITEMS ON) - include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/dbconverter.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") - " - COMPONENT Runtime - ) -endif() diff --git a/dbconverter/src/main.cpp b/dbconverter/src/main.cpp deleted file mode 100644 index 67748fa45..000000000 --- a/dbconverter/src/main.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2019 by Fabio Bas * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include "main.h" - -#include "mocks.h" -#include "version_string.h" - -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - QCoreApplication app(argc, argv); - app.setOrganizationName("Cockatrice"); - app.setApplicationName("Dbconverter"); - app.setApplicationVersion(VERSION_STRING); - - QCommandLineParser parser; - parser.addPositionalArgument("olddb", "Read existing card database from "); - parser.addPositionalArgument("newdb", "Write new card database to "); - parser.addHelpOption(); - parser.addVersionOption(); - parser.process(app); - - QString oldDbPath; - QString newDbPath; - QStringList args = parser.positionalArguments(); - if (args.count() == 2) { - oldDbPath = QFileInfo(args.at(0)).absoluteFilePath(); - newDbPath = QFileInfo(args.at(1)).absoluteFilePath(); - } else { - qCritical() << "Usage: dbconverter "; - parser.showHelp(1); - exit(0); - } - - CardDatabaseConverter *db = new CardDatabaseConverter; - - qInfo() << "---------------------------------------------"; - qInfo() << "Loading cards from" << oldDbPath; - db->loadCardDatabase(oldDbPath); - qInfo() << "---------------------------------------------"; - qInfo() << "Saving cards to" << newDbPath; - db->saveCardDatabase(newDbPath); - qInfo() << "---------------------------------------------"; -} diff --git a/dbconverter/src/main.h b/dbconverter/src/main.h deleted file mode 100644 index 72dbe939d..000000000 --- a/dbconverter/src/main.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef MAIN_H -#define MAIN_H - -#include "libcockatrice/interfaces/noop_card_set_priority_controller.h" - -#include -#include -#include - -static const QList kConstructedCounts = {{4, "legal"}, {0, "banned"}}; - -static const QList kSingletonCounts = {{1, "legal"}, {0, "banned"}}; - -class CardDatabaseConverter : public CardDatabase -{ -public: - explicit CardDatabaseConverter() - { - // Replace querier with one that ignores SettingsCache - delete querier; - querier = new CardDatabaseQuerier(this, this, new NoopCardPreferenceProvider()); - }; - - LoadStatus loadCardDatabase(const QString &path) - { - return loader->loadCardDatabase(path); - } - - bool saveCardDatabase(const QString &fileName) - { - CockatriceXml4Parser parser(new NoopCardPreferenceProvider(), new NoopCardSetPriorityController()); - - return parser.saveToFile(createDefaultMagicFormats(), sets, cards, fileName); - } - - FormatRulesNameMap createDefaultMagicFormats() - { - // Predefined common exceptions - CardCondition superTypeIsBasic; - superTypeIsBasic.field = "type"; - superTypeIsBasic.matchType = "contains"; - superTypeIsBasic.value = "Basic Land"; - - ExceptionRule basicLands; - basicLands.conditions.append(superTypeIsBasic); - - CardCondition anyNumberAllowed; - anyNumberAllowed.field = "text"; - anyNumberAllowed.matchType = "contains"; - anyNumberAllowed.value = "A deck can have any number of"; - - ExceptionRule mayContainAnyNumber; - mayContainAnyNumber.conditions.append(anyNumberAllowed); - - // Map to store default rules - FormatRulesNameMap defaultFormatRulesNameMap; - - // ----------------- Helper lambda to create format ----------------- - auto makeFormat = [&](const QString &name, int minDeck = 60, int maxDeck = -1, int maxSideboardSize = 15, - const QList &allowedCounts = kConstructedCounts) -> FormatRulesPtr { - FormatRulesPtr f(new FormatRules); - f->formatName = name; - f->allowedCounts = allowedCounts; - f->minDeckSize = minDeck; - f->maxDeckSize = maxDeck; - f->maxSideboardSize = maxSideboardSize; - f->exceptions.append(basicLands); - f->exceptions.append(mayContainAnyNumber); - defaultFormatRulesNameMap.insert(name.toLower(), f); - return f; - }; - - // ----------------- Standard formats ----------------- - makeFormat("Standard"); - makeFormat("Modern"); - makeFormat("Legacy"); - makeFormat("Pioneer"); - makeFormat("Historic"); - makeFormat("Timeless"); - makeFormat("Future"); - makeFormat("OldSchool"); - makeFormat("Premodern"); - makeFormat("Pauper"); - makeFormat("Penny"); - - // ----------------- Singleton formats ----------------- - makeFormat("Commander", 100, 100, 15, kSingletonCounts); - makeFormat("Duel", 100, 100, 15, kSingletonCounts); - makeFormat("Brawl", 60, 60, 15, kSingletonCounts); - makeFormat("StandardBrawl", 60, 60, 15, kSingletonCounts); - makeFormat("Oathbreaker", 60, 60, 15, kSingletonCounts); - makeFormat("PauperCommander", 100, 100, 15, kSingletonCounts); - makeFormat("Predh", 100, 100, 15, kSingletonCounts); - - // ----------------- Restricted formats ----------------- - makeFormat("Vintage", 60, -1, 15, {{4, "legal"}, {1, "restricted"}, {0, "banned"}}); - - return defaultFormatRulesNameMap; - } -}; - -#endif diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp deleted file mode 100644 index b97bd09c7..000000000 --- a/dbconverter/src/mocks.cpp +++ /dev/null @@ -1,451 +0,0 @@ - -#include "mocks.h" - -CardDatabaseSettings::CardDatabaseSettings(const QString &settingPath, QObject *parent) - : SettingsManager(settingPath + "cardDatabase.ini", QString(), QString(), parent) -{ -} -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; -}; - -QString SettingsCache::getDataPath() -{ - return ""; -} -QString SettingsCache::getSettingsPath() -{ - return ""; -} -void SettingsCache::translateLegacySettings() -{ -} -QString SettingsCache::getSafeConfigPath(QString /* configEntry */, QString defaultPath) const -{ - return defaultPath; -} -QString SettingsCache::getSafeConfigFilePath(QString /* configEntry */, QString defaultPath) const -{ - return defaultPath; -} - -void SettingsCache::setUseTearOffMenus(bool /* _useTearOffMenus */) -{ -} -void SettingsCache::setCardViewInitialRowsMax(int /* _cardViewInitialRowsMax */) -{ -} -void SettingsCache::setCardViewExpandedRowsMax(int /* value */) -{ -} -void SettingsCache::setCloseEmptyCardView(const QT_STATE_CHANGED_T /* value */) -{ -} -void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T /* value */) -{ -} -void SettingsCache::setKnownMissingFeatures(const QString & /* _knownMissingFeatures */) -{ -} -void SettingsCache::setCardInfoViewMode(const int /* _viewMode */) -{ -} -void SettingsCache::setHighlightWords(const QString & /* _highlightWords */) -{ -} -void SettingsCache::setMasterVolume(int /* _masterVolume */) -{ -} -void SettingsCache::setLeftJustified(const QT_STATE_CHANGED_T /* _leftJustified */) -{ -} -void SettingsCache::setCardScaling(const QT_STATE_CHANGED_T /* _scaleCards */) -{ -} -void SettingsCache::setStackCardOverlapPercent(const int /* _verticalCardOverlapPercent */) -{ -} -void SettingsCache::setShowMessagePopups(const QT_STATE_CHANGED_T /* _showMessagePopups */) -{ -} -void SettingsCache::setShowMentionPopups(const QT_STATE_CHANGED_T /* _showMentionPopus */) -{ -} -void SettingsCache::setRoomHistory(const QT_STATE_CHANGED_T /* _roomHistory */) -{ -} -void SettingsCache::setLang(const QString & /* _lang */) -{ -} -void SettingsCache::setShowTipsOnStartup(bool /* _showTipsOnStartup */) -{ -} -void SettingsCache::setSeenTips(const QList & /* _seenTips */) -{ -} -void SettingsCache::setDeckPath(const QString & /* _deckPath */) -{ -} -void SettingsCache::setFiltersPath(const QString & /*_filtersPath */) -{ -} -void SettingsCache::setReplaysPath(const QString & /* _replaysPath */) -{ -} -void SettingsCache::setThemesPath(const QString & /* _themesPath */) -{ -} -void SettingsCache::setPicsPath(const QString & /* _picsPath */) -{ -} -void SettingsCache::setCardDatabasePath(const QString & /* _cardDatabasePath */) -{ -} -void SettingsCache::setCustomCardDatabasePath(const QString & /* _customCardDatabasePath */) -{ -} -void SettingsCache::setSpoilerDatabasePath(const QString & /* _spoilerDatabasePath */) -{ -} -void SettingsCache::setTokenDatabasePath(const QString & /* _tokenDatabasePath */) -{ -} -void SettingsCache::setThemeName(const QString & /* _themeName */) -{ -} -void SettingsCache::setHomeTabBackgroundSource(const QString & /* _backgroundSource */) -{ -} -void SettingsCache::setHomeTabBackgroundShuffleFrequency(int /* frequency */) -{ -} -void SettingsCache::setTabVisualDeckStorageOpen(bool /*value*/) -{ -} -void SettingsCache::setTabServerOpen(bool /*value*/) -{ -} -void SettingsCache::setTabAccountOpen(bool /*value*/) -{ -} -void SettingsCache::setTabDeckStorageOpen(bool /*value*/) -{ -} -void SettingsCache::setTabReplaysOpen(bool /*value*/) -{ -} -void SettingsCache::setTabAdminOpen(bool /*value*/) -{ -} -void SettingsCache::setTabLogOpen(bool /*value*/) -{ -} -void SettingsCache::setPicDownload(QT_STATE_CHANGED_T /* _picDownload */) -{ -} -void SettingsCache::setShowStatusBar(bool /* value */) -{ -} -void SettingsCache::setNotificationsEnabled(QT_STATE_CHANGED_T /* _notificationsEnabled */) -{ -} -void SettingsCache::setSpectatorNotificationsEnabled(QT_STATE_CHANGED_T /* _spectatorNotificationsEnabled */) -{ -} -void SettingsCache::setBuddyConnectNotificationsEnabled(QT_STATE_CHANGED_T /* _buddyConnectNotificationsEnabled */) -{ -} -void SettingsCache::setDoubleClickToPlay(QT_STATE_CHANGED_T /* _doubleClickToPlay */) -{ -} -void SettingsCache::setClickPlaysAllSelected(QT_STATE_CHANGED_T /* _clickPlaysAllSelected */) -{ -} -void SettingsCache::setPlayToStack(QT_STATE_CHANGED_T /* _playToStack */) -{ -} -void SettingsCache::setDoNotDeleteArrowsInSubPhases(QT_STATE_CHANGED_T /* _doNotDeleteArrowsInSubPhases */) -{ -} -void SettingsCache::setStartingHandSize(int /* _startingHandSize */) -{ -} -void SettingsCache::setAnnotateTokens(QT_STATE_CHANGED_T /* _annotateTokens */) -{ -} -void SettingsCache::setTabGameSplitterSizes(const QByteArray & /* _tabGameSplitterSizes */) -{ -} -void SettingsCache::setShowShortcuts(QT_STATE_CHANGED_T /* _showShortcuts */) -{ -} -void SettingsCache::setDisplayCardNames(QT_STATE_CHANGED_T /* _displayCardNames */) -{ -} -void SettingsCache::setOverrideAllCardArtWithPersonalPreference(QT_STATE_CHANGED_T /* _displayCardNames */) -{ -} -void SettingsCache::setBumpSetsWithCardsInDeckToTop(QT_STATE_CHANGED_T /* _bumpSetsWithCardsInDeckToTop */) -{ -} -void SettingsCache::setPrintingSelectorSortOrder(int /* _printingSelectorSortOrder */) -{ -} -void SettingsCache::setPrintingSelectorCardSize(int /* _printingSelectorCardSize */) -{ -} -void SettingsCache::setIncludeRebalancedCards(bool /* _includeRebalancedCards */) -{ -} -void SettingsCache::setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED_T /* _navigationButtonsVisible */) -{ -} -void SettingsCache::setDeckEditorBannerCardComboBoxVisible( - QT_STATE_CHANGED_T /* _deckEditorBannerCardComboBoxVisible */) -{ -} -void SettingsCache::setDeckEditorTagsWidgetVisible(QT_STATE_CHANGED_T /* _deckEditorTagsWidgetVisible */) -{ -} -void SettingsCache::setVisualDeckStorageSortingOrder(int /* _visualDeckStorageSortingOrder */) -{ -} -void SettingsCache::setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T /* value */) -{ -} -void SettingsCache::setVisualDeckStorageShowTagFilter(QT_STATE_CHANGED_T /* _showTags */) -{ -} -void SettingsCache::setVisualDeckStorageDefaultTagsList(QStringList /* _defaultTagsList */) -{ -} -void SettingsCache::setVisualDeckStorageSearchFolderNames(QT_STATE_CHANGED_T /* value */) -{ -} -void SettingsCache::setVisualDeckStorageShowBannerCardComboBox(QT_STATE_CHANGED_T /* _showBannerCardComboBox */) -{ -} -void SettingsCache::setVisualDeckStorageShowTagsOnDeckPreviews(QT_STATE_CHANGED_T /* _showTags */) -{ -} -void SettingsCache::setVisualDeckStorageCardSize(int /* _visualDeckStorageCardSize */) -{ -} -void SettingsCache::setVisualDeckStorageDrawUnusedColorIdentities( - QT_STATE_CHANGED_T /* _visualDeckStorageDrawUnusedColorIdentities */) -{ -} -void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity( - int /* _visualDeckStorageUnusedColorIdentitiesOpacity */) -{ -} -void SettingsCache::setVisualDeckStorageTooltipType(int /* value */) -{ -} -void SettingsCache::setVisualDeckStoragePromptForConversion(bool /* _visualDeckStoragePromptForConversion */) -{ -} -void SettingsCache::setVisualDeckStorageAlwaysConvert(bool /* _visualDeckStorageAlwaysConvert */) -{ -} -void SettingsCache::setVisualDeckStorageInGame(QT_STATE_CHANGED_T /* value */) -{ -} -void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T /* value */) -{ -} -void SettingsCache::setDefaultDeckEditorType(int /* value */) -{ -} -void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T /* _enabled */) -{ -} -void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsAmount(int /* _amount */) -{ -} -void SettingsCache::setVisualDeckEditorSampleHandSize(int /* _amount */) -{ -} -void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T /* _horizontalHand */) -{ -} -void SettingsCache::setInvertVerticalCoordinate(QT_STATE_CHANGED_T /* _invertVerticalCoordinate */) -{ -} -void SettingsCache::setMinPlayersForMultiColumnLayout(int /* _minPlayersForMultiColumnLayout */) -{ -} -void SettingsCache::setTapAnimation(QT_STATE_CHANGED_T /* _tapAnimation */) -{ -} -void SettingsCache::setAutoRotateSidewaysLayoutCards(QT_STATE_CHANGED_T /* _autoRotateSidewaysLayoutCards */) -{ -} -void SettingsCache::setOpenDeckInNewTab(QT_STATE_CHANGED_T /* _openDeckInNewTab */) -{ -} -void SettingsCache::setRewindBufferingMs(int /* _rewindBufferingMs */) -{ -} -void SettingsCache::setChatMention(QT_STATE_CHANGED_T /* _chatMention */) -{ -} -void SettingsCache::setChatMentionCompleter(const QT_STATE_CHANGED_T /* _enableMentionCompleter */) -{ -} -void SettingsCache::setChatMentionForeground(QT_STATE_CHANGED_T /* _chatMentionForeground */) -{ -} -void SettingsCache::setChatHighlightForeground(QT_STATE_CHANGED_T /* _chatHighlightForeground */) -{ -} -void SettingsCache::setChatMentionColor(const QString & /* _chatMentionColor */) -{ -} -void SettingsCache::setChatHighlightColor(const QString & /* _chatHighlightColor */) -{ -} -void SettingsCache::setZoneViewGroupByIndex(int /* _zoneViewGroupByIndex */) -{ -} -void SettingsCache::setZoneViewSortByIndex(int /* _zoneViewSortByIndex */) -{ -} -void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T /* _zoneViewPileView */) -{ -} -void SettingsCache::setSoundEnabled(QT_STATE_CHANGED_T /* _soundEnabled */) -{ -} -void SettingsCache::setSoundThemeName(const QString & /* _soundThemeName */) -{ -} -void SettingsCache::setIgnoreUnregisteredUsers(QT_STATE_CHANGED_T /* _ignoreUnregisteredUsers */) -{ -} -void SettingsCache::setIgnoreUnregisteredUserMessages(QT_STATE_CHANGED_T /* _ignoreUnregisteredUserMessages */) -{ -} -void SettingsCache::setMainWindowGeometry(const QByteArray & /* _mainWindowGeometry */) -{ -} -void SettingsCache::setTokenDialogGeometry(const QByteArray & /* _tokenDialogGeometry */) -{ -} -void SettingsCache::setSetsDialogGeometry(const QByteArray & /* _setsDialogGeometry */) -{ -} -void SettingsCache::setPixmapCacheSize(const int /* _pixmapCacheSize */) -{ -} -void SettingsCache::setNetworkCacheSizeInMB(const int /* _networkCacheSize */) -{ -} -void SettingsCache::setNetworkRedirectCacheTtl(const int /* _redirectCacheTtl */) -{ -} -void SettingsCache::setClientID(const QString & /* _clientID */) -{ -} -void SettingsCache::setClientVersion(const QString & /* _clientVersion */) -{ -} -QStringList SettingsCache::getCountries() const -{ - static QStringList countries = QStringList() << "us"; - return countries; -} -void SettingsCache::setGameDescription(const QString /* _gameDescription */) -{ -} -void SettingsCache::setMaxPlayers(const int /* _maxPlayers */) -{ -} -void SettingsCache::setGameTypes(const QString /* _gameTypes */) -{ -} -void SettingsCache::setOnlyBuddies(const bool /* _onlyBuddies */) -{ -} -void SettingsCache::setOnlyRegistered(const bool /* _onlyRegistered */) -{ -} -void SettingsCache::setSpectatorsAllowed(const bool /* _spectatorsAllowed */) -{ -} -void SettingsCache::setSpectatorsNeedPassword(const bool /* _spectatorsNeedPassword */) -{ -} -void SettingsCache::setSpectatorsCanTalk(const bool /* _spectatorsCanTalk */) -{ -} -void SettingsCache::setSpectatorsCanSeeEverything(const bool /* _spectatorsCanSeeEverything */) -{ -} -void SettingsCache::setCreateGameAsSpectator(const bool /* _createGameAsSpectator */) -{ -} -void SettingsCache::setDefaultStartingLifeTotal(const int /* _startingLifeTotal */) -{ -} -void SettingsCache::setShareDecklistsOnLoad(const bool /* _shareDecklistsOnLoad */) -{ -} -void SettingsCache::setRememberGameSettings(const bool /* _rememberGameSettings */) -{ -} -void SettingsCache::setCheckUpdatesOnStartup(QT_STATE_CHANGED_T /* value */) -{ -} -void SettingsCache::setStartupCardUpdateCheckPromptForUpdate(bool /* value */) -{ -} -void SettingsCache::setStartupCardUpdateCheckAlwaysUpdate(bool /* value */) -{ -} -void SettingsCache::setCardUpdateCheckInterval(int /* value */) -{ -} -void SettingsCache::setLastCardUpdateCheck(QDate /* value */) -{ -} -void SettingsCache::setNotifyAboutUpdate(QT_STATE_CHANGED_T /* _notifyaboutupdate */) -{ -} -void SettingsCache::setNotifyAboutNewVersion(QT_STATE_CHANGED_T /* _notifyaboutnewversion */) -{ -} -void SettingsCache::setDownloadSpoilerStatus(bool /* _spoilerStatus */) -{ -} -void SettingsCache::setUpdateReleaseChannelIndex(int /* value */) -{ -} -void SettingsCache::setMaxFontSize(int /* _max */) -{ -} -void SettingsCache::setRoundCardCorners(bool /* _roundCardCorners */) -{ -} - -void CardPictureLoader::clearPixmapCache(CardInfoPtr /* card */) -{ -} diff --git a/dbconverter/src/mocks.h b/dbconverter/src/mocks.h deleted file mode 100644 index 929092bd7..000000000 --- a/dbconverter/src/mocks.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Beware of this preprocessor hack used to redefine the settingCache class - * instead of including it and all of its dependencies. - * Always set header guards of mocked objects before including any headers - * with mocked objects. - */ - -#include -#include - -#define PICTURELOADER_H - -#include "../../cockatrice/src/client/settings/cache_settings.h" - -#include -#include - -extern SettingsCache *settingsCache; - -class CardPictureLoader -{ -public: - static void clearPixmapCache(CardInfoPtr card); -}; diff --git a/format.sh b/format.sh index 51fccbdcf..f8c183dfc 100755 --- a/format.sh +++ b/format.sh @@ -14,7 +14,6 @@ cd "${BASH_SOURCE%/*}/" || exit 2 # could not find path, this could happen with # defaults include=("cockatrice/src" \ -"dbconverter/src" \ "libcockatrice_card" \ "libcockatrice_deck_list" \ "libcockatrice_network" \ From 73a90bdf38f47e9cb2bbf6a2ce3ff652a1b37476 Mon Sep 17 00:00:00 2001 From: tooomm Date: Sat, 20 Dec 2025 17:46:13 +0100 Subject: [PATCH 18/43] Doxygen: Add bullet points to subpages lists & link webpage on welcome page (#6377) * add bullet points to subpages * Link to webpage from welcome page * Add bullet points to subpages * grouping * Add TODO note to card database documentation Added a TODO note for future updates. * Fix GH alerts commands to be doxygen compatible --- README.md | 8 ++++---- .../card_database_schema_and_parsing.md | 4 +++- doc/doxygen/extra-pages/index.md | 4 +++- .../deck_management/editing_decks.md | 7 +++---- .../extra-pages/user_documentation/index.md | 18 +++++++----------- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 47fb3a159..3d01e9f3d 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,8 @@ Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other p - [Official Discord](https://discord.gg/3Z9yzmA) - [reddit r/Cockatrice](https://reddit.com/r/cockatrice) ->[!IMPORTANT] ->For support regarding specific servers, please contact that server's admin/mods and use their dedicated communication channels rather than contacting the team building the software. +> [!IMPORTANT] +> For support regarding specific servers, please contact that server's admin/mods and use their dedicated communication channels rather than contacting the team building the software. # Contribute @@ -131,8 +131,8 @@ You can then make package ``` ->[!NOTE] ->Detailed compiling instructions can be found in the Cockatrice wiki at [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice) +> [!NOTE] +> Detailed compiling instructions can be found in the Cockatrice wiki at [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)
diff --git a/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md b/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md index 6c5e3512b..1de6fda4f 100644 --- a/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md +++ b/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md @@ -1 +1,3 @@ -@page card_database_schema_and_parsing Card Database Schema and Parsing \ No newline at end of file +@page card_database_schema_and_parsing Card Database Schema and Parsing + +TODO diff --git a/doc/doxygen/extra-pages/index.md b/doc/doxygen/extra-pages/index.md index 66113117f..c370a6fc2 100644 --- a/doc/doxygen/extra-pages/index.md +++ b/doc/doxygen/extra-pages/index.md @@ -5,4 +5,6 @@ This is the **main landing page** of the Cockatrice documentation. - Go to the @subpage user_reference page -- Or check out the @subpage developer_reference \ No newline at end of file +- Review the @subpage developer_reference + +Or check out the [Cockatrice Webpage](https://cockatrice.github.io/). diff --git a/doc/doxygen/extra-pages/user_documentation/deck_management/editing_decks.md b/doc/doxygen/extra-pages/user_documentation/deck_management/editing_decks.md index 030811306..37b613461 100644 --- a/doc/doxygen/extra-pages/user_documentation/deck_management/editing_decks.md +++ b/doc/doxygen/extra-pages/user_documentation/deck_management/editing_decks.md @@ -1,7 +1,6 @@ @page editing_decks Editing Decks -@subpage editing_decks_classic + - @subpage editing_decks_classic + - @subpage editing_decks_visual -@subpage editing_decks_visual - -@subpage editing_decks_printings \ No newline at end of file + - @subpage editing_decks_printings diff --git a/doc/doxygen/extra-pages/user_documentation/index.md b/doc/doxygen/extra-pages/user_documentation/index.md index 8a5c88c37..33104991a 100644 --- a/doc/doxygen/extra-pages/user_documentation/index.md +++ b/doc/doxygen/extra-pages/user_documentation/index.md @@ -1,13 +1,9 @@ @page user_reference User Reference -@subpage search_syntax_help - -@subpage deck_search_syntax_help - -@subpage creating_decks - -@subpage importing_decks - -@subpage editing_decks - -@subpage exporting_decks \ No newline at end of file +- @subpage search_syntax_help +- @subpage deck_search_syntax_help + +- @subpage creating_decks +- @subpage importing_decks +- @subpage editing_decks +- @subpage exporting_decks From a0f977e80c9e3513fb288899bb5c18b9cc02ea75 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:19:33 -0800 Subject: [PATCH 19/43] [DeckList] refactor: pass DeckList by const ref (#6437) * [DeckList] refactor: pass DeckList by const ref * Change getDeckList to return a const ref --- .../interfaces/deck_stats_interface.cpp | 14 +++---- .../network/interfaces/deck_stats_interface.h | 6 +-- .../interfaces/tapped_out_interface.cpp | 14 +++---- .../network/interfaces/tapped_out_interface.h | 6 +-- .../src/interface/deck_loader/deck_loader.cpp | 32 +++++++-------- .../src/interface/deck_loader/deck_loader.h | 12 +++--- .../deck_editor_deck_dock_widget.cpp | 4 +- .../deck_editor_deck_dock_widget.h | 2 +- .../dialogs/dlg_load_deck_from_clipboard.cpp | 4 +- .../widgets/tabs/abstract_tab_deck_editor.cpp | 41 ++++++++----------- .../widgets/tabs/abstract_tab_deck_editor.h | 2 +- .../deck_preview/deck_preview_widget.cpp | 8 ++-- 12 files changed, 70 insertions(+), 75 deletions(-) diff --git a/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp b/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp index 5e9b874d0..0298daa6b 100644 --- a/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp +++ b/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp @@ -43,23 +43,23 @@ void DeckStatsInterface::queryFinished(QNetworkReply *reply) deleteLater(); } -void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data) +void DeckStatsInterface::getAnalyzeRequestData(const DeckList &deck, QByteArray &data) { DeckList deckWithoutTokens; - copyDeckWithoutTokens(*deck, deckWithoutTokens); + copyDeckWithoutTokens(deck, deckWithoutTokens); QUrl params; QUrlQuery urlQuery; urlQuery.addQueryItem("deck", deckWithoutTokens.writeToString_Plain()); - urlQuery.addQueryItem("decktitle", deck->getName()); + urlQuery.addQueryItem("decktitle", deck.getName()); params.setQuery(urlQuery); - data->append(params.query(QUrl::EncodeReserved).toUtf8()); + data.append(params.query(QUrl::EncodeReserved).toUtf8()); } -void DeckStatsInterface::analyzeDeck(DeckList *deck) +void DeckStatsInterface::analyzeDeck(const DeckList &deck) { QByteArray data; - getAnalyzeRequestData(deck, &data); + getAnalyzeRequestData(deck, data); QNetworkRequest request(QUrl("https://deckstats.net/index.php")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -68,7 +68,7 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck) manager->post(request, data); } -void DeckStatsInterface::copyDeckWithoutTokens(DeckList &source, DeckList &destination) +void DeckStatsInterface::copyDeckWithoutTokens(const DeckList &source, DeckList &destination) { auto copyIfNotAToken = [this, &destination](const auto node, const auto card) { CardInfoPtr dbCard = cardDatabase.query()->getCardInfo(card->getName()); diff --git a/cockatrice/src/client/network/interfaces/deck_stats_interface.h b/cockatrice/src/client/network/interfaces/deck_stats_interface.h index fd1691a8b..7dc841027 100644 --- a/cockatrice/src/client/network/interfaces/deck_stats_interface.h +++ b/cockatrice/src/client/network/interfaces/deck_stats_interface.h @@ -28,15 +28,15 @@ private: * closest non-token card instead. So we construct a new deck which has no * tokens. */ - void copyDeckWithoutTokens(DeckList &source, DeckList &destination); + void copyDeckWithoutTokens(const DeckList &source, DeckList &destination); private slots: void queryFinished(QNetworkReply *reply); - void getAnalyzeRequestData(DeckList *deck, QByteArray *data); + void getAnalyzeRequestData(const DeckList &deck, QByteArray &data); public: explicit DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = nullptr); - void analyzeDeck(DeckList *deck); + void analyzeDeck(const DeckList &deck); }; #endif diff --git a/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp b/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp index 137c7728c..a30a7f531 100644 --- a/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp +++ b/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp @@ -67,24 +67,24 @@ void TappedOutInterface::queryFinished(QNetworkReply *reply) deleteLater(); } -void TappedOutInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data) +void TappedOutInterface::getAnalyzeRequestData(const DeckList &deck, QByteArray &data) { DeckList mainboard, sideboard; - copyDeckSplitMainAndSide(*deck, mainboard, sideboard); + copyDeckSplitMainAndSide(deck, mainboard, sideboard); QUrl params; QUrlQuery urlQuery; - urlQuery.addQueryItem("name", deck->getName()); + urlQuery.addQueryItem("name", deck.getName()); urlQuery.addQueryItem("mainboard", mainboard.writeToString_Plain(false, true)); urlQuery.addQueryItem("sideboard", sideboard.writeToString_Plain(false, true)); params.setQuery(urlQuery); - data->append(params.query(QUrl::EncodeReserved).toUtf8()); + data.append(params.query(QUrl::EncodeReserved).toUtf8()); } -void TappedOutInterface::analyzeDeck(DeckList *deck) +void TappedOutInterface::analyzeDeck(const DeckList &deck) { QByteArray data; - getAnalyzeRequestData(deck, &data); + getAnalyzeRequestData(deck, data); QNetworkRequest request(QUrl("https://tappedout.net/mtg-decks/paste/")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -93,7 +93,7 @@ void TappedOutInterface::analyzeDeck(DeckList *deck) manager->post(request, data); } -void TappedOutInterface::copyDeckSplitMainAndSide(DeckList &source, DeckList &mainboard, DeckList &sideboard) +void TappedOutInterface::copyDeckSplitMainAndSide(const DeckList &source, DeckList &mainboard, DeckList &sideboard) { auto copyMainOrSide = [this, &mainboard, &sideboard](const auto node, const auto card) { CardInfoPtr dbCard = cardDatabase.query()->getCardInfo(card->getName()); diff --git a/cockatrice/src/client/network/interfaces/tapped_out_interface.h b/cockatrice/src/client/network/interfaces/tapped_out_interface.h index 056d7fd9a..0ea9c8358 100644 --- a/cockatrice/src/client/network/interfaces/tapped_out_interface.h +++ b/cockatrice/src/client/network/interfaces/tapped_out_interface.h @@ -30,14 +30,14 @@ private: QNetworkAccessManager *manager; CardDatabase &cardDatabase; - void copyDeckSplitMainAndSide(DeckList &source, DeckList &mainboard, DeckList &sideboard); + void copyDeckSplitMainAndSide(const DeckList &source, DeckList &mainboard, DeckList &sideboard); private slots: void queryFinished(QNetworkReply *reply); - void getAnalyzeRequestData(DeckList *deck, QByteArray *data); + void getAnalyzeRequestData(const DeckList &deck, QByteArray &data); public: explicit TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent = nullptr); - void analyzeDeck(DeckList *deck); + void analyzeDeck(const DeckList &deck); }; #endif diff --git a/cockatrice/src/interface/deck_loader/deck_loader.cpp b/cockatrice/src/interface/deck_loader/deck_loader.cpp index e567e45fd..45c3ec1ba 100644 --- a/cockatrice/src/interface/deck_loader/deck_loader.cpp +++ b/cockatrice/src/interface/deck_loader/deck_loader.cpp @@ -287,14 +287,14 @@ static QString toDecklistExportString(const QList &car * @param deckList The decklist to export * @param website The website we're sending the deck to */ -QString DeckLoader::exportDeckToDecklist(const DeckList *deckList, DecklistWebsite website) +QString DeckLoader::exportDeckToDecklist(const DeckList &deckList, DecklistWebsite website) { // Add the base url QString deckString = "https://" + getDomainForWebsite(website) + "/?"; // export all cards in zone - QString mainBoardCards = toDecklistExportString(deckList->getCardNodes({DECK_ZONE_MAIN})); - QString sideBoardCards = toDecklistExportString(deckList->getCardNodes({DECK_ZONE_SIDE})); + QString mainBoardCards = toDecklistExportString(deckList.getCardNodes({DECK_ZONE_MAIN})); + QString sideBoardCards = toDecklistExportString(deckList.getCardNodes({DECK_ZONE_SIDE})); // Remove the extra return at the end of the last cards mainBoardCards.chop(3); @@ -310,7 +310,7 @@ QString DeckLoader::exportDeckToDecklist(const DeckList *deckList, DecklistWebsi return deckString; } -void DeckLoader::saveToClipboard(const DeckList *deckList, bool addComments, bool addSetNameAndNumber) +void DeckLoader::saveToClipboard(const DeckList &deckList, bool addComments, bool addSetNameAndNumber) { QString buffer; QTextStream stream(&buffer); @@ -320,7 +320,7 @@ void DeckLoader::saveToClipboard(const DeckList *deckList, bool addComments, boo } bool DeckLoader::saveToStream_Plain(QTextStream &out, - const DeckList *deckList, + const DeckList &deckList, bool addComments, bool addSetNameAndNumber) { @@ -329,7 +329,7 @@ bool DeckLoader::saveToStream_Plain(QTextStream &out, } // loop zones - for (auto zoneNode : deckList->getZoneNodes()) { + for (auto zoneNode : deckList.getZoneNodes()) { saveToStream_DeckZone(out, zoneNode, addComments, addSetNameAndNumber); // end of zone @@ -339,14 +339,14 @@ bool DeckLoader::saveToStream_Plain(QTextStream &out, return true; } -void DeckLoader::saveToStream_DeckHeader(QTextStream &out, const DeckList *deckList) +void DeckLoader::saveToStream_DeckHeader(QTextStream &out, const DeckList &deckList) { - if (!deckList->getName().isEmpty()) { - out << "// " << deckList->getName() << "\n\n"; + if (!deckList.getName().isEmpty()) { + out << "// " << deckList.getName() << "\n\n"; } - if (!deckList->getComments().isEmpty()) { - QStringList commentRows = deckList->getComments().split(QRegularExpression("\n|\r\n|\r")); + if (!deckList.getComments().isEmpty()) { + QStringList commentRows = deckList.getComments().split(QRegularExpression("\n|\r\n|\r")); for (const QString &row : commentRows) { out << "// " << row << "\n"; } @@ -434,7 +434,7 @@ void DeckLoader::saveToStream_DeckZoneCards(QTextStream &out, } } -bool DeckLoader::convertToCockatriceFormat(QString fileName) +bool DeckLoader::convertToCockatriceFormat(const QString &fileName) { // Change the file extension to .cod QFileInfo fileInfo(fileName); @@ -543,7 +543,7 @@ void DeckLoader::printDeckListNode(QTextCursor *cursor, const InnerDecklistNode cursor->movePosition(QTextCursor::End); } -void DeckLoader::printDeckList(QPrinter *printer, const DeckList *deckList) +void DeckLoader::printDeckList(QPrinter *printer, const DeckList &deckList) { QTextDocument doc; @@ -559,14 +559,14 @@ void DeckLoader::printDeckList(QPrinter *printer, const DeckList *deckList) headerCharFormat.setFontWeight(QFont::Bold); cursor.insertBlock(headerBlockFormat, headerCharFormat); - cursor.insertText(deckList->getName()); + cursor.insertText(deckList.getName()); headerCharFormat.setFontPointSize(12); cursor.insertBlock(headerBlockFormat, headerCharFormat); - cursor.insertText(deckList->getComments()); + cursor.insertText(deckList.getComments()); cursor.insertBlock(headerBlockFormat, headerCharFormat); - for (auto zoneNode : deckList->getZoneNodes()) { + for (auto zoneNode : deckList.getZoneNodes()) { cursor.insertHtml("
"); cursor.insertBlock(headerBlockFormat, headerCharFormat); diff --git a/cockatrice/src/interface/deck_loader/deck_loader.h b/cockatrice/src/interface/deck_loader/deck_loader.h index 4ceb9006b..ec5636995 100644 --- a/cockatrice/src/interface/deck_loader/deck_loader.h +++ b/cockatrice/src/interface/deck_loader/deck_loader.h @@ -59,11 +59,11 @@ public: bool saveToFile(const QString &fileName, DeckFileFormat::Format fmt); bool updateLastLoadedTimestamp(const QString &fileName, DeckFileFormat::Format fmt); - static QString exportDeckToDecklist(const DeckList *deckList, DecklistWebsite website); + static QString exportDeckToDecklist(const DeckList &deckList, DecklistWebsite website); - static void saveToClipboard(const DeckList *deckList, bool addComments = true, bool addSetNameAndNumber = true); + static void saveToClipboard(const DeckList &deckList, bool addComments = true, bool addSetNameAndNumber = true); static bool saveToStream_Plain(QTextStream &out, - const DeckList *deckList, + const DeckList &deckList, bool addComments = true, bool addSetNameAndNumber = true); @@ -72,9 +72,9 @@ public: * @param printer The printer to render the decklist to. * @param deckList */ - static void printDeckList(QPrinter *printer, const DeckList *deckList); + static void printDeckList(QPrinter *printer, const DeckList &deckList); - bool convertToCockatriceFormat(QString fileName); + bool convertToCockatriceFormat(const QString &fileName); LoadedDeck &getDeck() { @@ -91,7 +91,7 @@ public: private: static void printDeckListNode(QTextCursor *cursor, const InnerDecklistNode *node); - static void saveToStream_DeckHeader(QTextStream &out, const DeckList *deckList); + static void saveToStream_DeckHeader(QTextStream &out, const DeckList &deckList); static void saveToStream_DeckZone(QTextStream &out, const InnerDecklistNode *zoneNode, diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index a9e19195d..facf0c98b 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -527,9 +527,9 @@ DeckLoader *DeckEditorDeckDockWidget::getDeckLoader() return deckLoader; } -DeckList *DeckEditorDeckDockWidget::getDeckList() +const DeckList &DeckEditorDeckDockWidget::getDeckList() const { - return deckModel->getDeckList(); + return *deckModel->getDeckList(); } /** diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h index da175d417..60e1d36d2 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -61,7 +61,7 @@ public slots: void syncDisplayWidgetsToModel(); void sortDeckModelToDeckView(); DeckLoader *getDeckLoader(); - DeckList *getDeckList(); + const DeckList &getDeckList() const; void actIncrement(); bool swapCard(const QModelIndex &idx); void actDecrementCard(const ExactCard &card, QString zoneName); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp index 9024b3bdc..b72130c81 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp @@ -150,7 +150,7 @@ DlgEditDeckInClipboard::DlgEditDeckInClipboard(const DeckList &_deckList, bool _ * @param addComments Whether to add annotations * @return A QString */ -static QString deckListToString(const DeckList *deckList, bool addComments) +static QString deckListToString(const DeckList &deckList, bool addComments) { QString buffer; QTextStream stream(&buffer); @@ -160,7 +160,7 @@ static QString deckListToString(const DeckList *deckList, bool addComments) void DlgEditDeckInClipboard::actRefresh() { - setText(deckListToString(&deckList, annotated)); + setText(deckListToString(deckList, annotated)); } void DlgEditDeckInClipboard::actOK() diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp index cdfa5b529..398208993 100644 --- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp +++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp @@ -124,7 +124,7 @@ void AbstractTabDeckEditor::onDeckModified() */ void AbstractTabDeckEditor::onDeckHistorySaveRequested(const QString &modificationReason) { - historyManager->save(deckDockWidget->getDeckList()->createMemento(modificationReason)); + historyManager->save(deckDockWidget->getDeckList().createMemento(modificationReason)); } /** @@ -231,7 +231,7 @@ void AbstractTabDeckEditor::openDeck(const LoadedDeck &deck) void AbstractTabDeckEditor::setDeck(const LoadedDeck &_deck) { deckDockWidget->setDeck(_deck); - CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(getDeckList()->getCardRefList())); + CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(getDeckList().getCardRefList())); setModified(false); aDeckDockVisible->setChecked(true); @@ -245,7 +245,7 @@ DeckLoader *AbstractTabDeckEditor::getDeckLoader() const } /** @brief Returns the currently loaded deck list. */ -DeckList *AbstractTabDeckEditor::getDeckList() const +const DeckList &AbstractTabDeckEditor::getDeckList() const { return deckDockWidget->getDeckList(); } @@ -446,7 +446,7 @@ bool AbstractTabDeckEditor::actSaveDeckAs() dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setDefaultSuffix("cod"); dialog.setNameFilters(DeckLoader::FILE_NAME_FILTERS); - dialog.selectFile(getDeckList()->getName().trimmed()); + dialog.selectFile(getDeckList().getName().trimmed()); if (!dialog.exec()) return false; @@ -591,26 +591,21 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite() */ void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite website) { - if (DeckList *deckList = getDeckList()) { - QString decklistUrlString = DeckLoader::exportDeckToDecklist(deckList, website); - // Check to make sure the string isn't empty. - if (decklistUrlString.isEmpty()) { - // Show an error if the deck is empty, and return. - QMessageBox::critical(this, tr("Error"), tr("There are no cards in your deck to be exported")); - return; - } - - // Encode the string recieved from the model to make sure all characters are encoded. - // first we put it into a qurl object - QUrl decklistUrl = QUrl(decklistUrlString); - // we get the correctly encoded url. - decklistUrlString = decklistUrl.toEncoded(); - // We open the url in the user's default browser - QDesktopServices::openUrl(decklistUrlString); - } else { - // if there's no deck loader object, return an error - QMessageBox::critical(this, tr("Error"), tr("No deck was selected to be exported.")); + QString decklistUrlString = DeckLoader::exportDeckToDecklist(getDeckList(), website); + // Check to make sure the string isn't empty. + if (decklistUrlString.isEmpty()) { + // Show an error if the deck is empty, and return. + QMessageBox::critical(this, tr("Error"), tr("There are no cards in your deck to be exported")); + return; } + + // Encode the string recieved from the model to make sure all characters are encoded. + // first we put it into a qurl object + QUrl decklistUrl = QUrl(decklistUrlString); + // we get the correctly encoded url. + decklistUrlString = decklistUrl.toEncoded(); + // We open the url in the user's default browser + QDesktopServices::openUrl(decklistUrlString); } /** @brief Exports deck to www.decklist.org. */ diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h index e1ffa45de..10675dd39 100644 --- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h +++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h @@ -122,7 +122,7 @@ public: DeckLoader *getDeckLoader() const; /** @brief Returns the currently active deck list. */ - DeckList *getDeckList() const; + const DeckList &getDeckList() const; /** @brief Sets the modified state of the tab. * @param _windowModified Whether the tab is modified. diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp index 7177879a8..4c782c989 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp @@ -334,13 +334,13 @@ QMenu *DeckPreviewWidget::createRightClickMenu() auto saveToClipboardMenu = menu->addMenu(tr("Save Deck to Clipboard")); connect(saveToClipboardMenu->addAction(tr("Annotated")), &QAction::triggered, this, - [this] { DeckLoader::saveToClipboard(&deckLoader->getDeck().deckList, true, true); }); + [this] { DeckLoader::saveToClipboard(deckLoader->getDeck().deckList, true, true); }); connect(saveToClipboardMenu->addAction(tr("Annotated (No set info)")), &QAction::triggered, this, - [this] { DeckLoader::saveToClipboard(&deckLoader->getDeck().deckList, true, false); }); + [this] { DeckLoader::saveToClipboard(deckLoader->getDeck().deckList, true, false); }); connect(saveToClipboardMenu->addAction(tr("Not Annotated")), &QAction::triggered, this, - [this] { DeckLoader::saveToClipboard(&deckLoader->getDeck().deckList, false, true); }); + [this] { DeckLoader::saveToClipboard(deckLoader->getDeck().deckList, false, true); }); connect(saveToClipboardMenu->addAction(tr("Not Annotated (No set info)")), &QAction::triggered, this, - [this] { DeckLoader::saveToClipboard(&deckLoader->getDeck().deckList, false, false); }); + [this] { DeckLoader::saveToClipboard(deckLoader->getDeck().deckList, false, false); }); menu->addSeparator(); From c12f4e9d2a0cf81ef457221ccc4d80f1fb06f059 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 21 Dec 2025 16:19:57 -0800 Subject: [PATCH 20/43] [DeckListModel] remove more access to underlying decklist for iteration (#6436) * [DeckListModel] remove more access to underlying decklist for iteration * remove one last direct iteration of decklist --- .../deck_editor/deck_editor_deck_dock_widget.cpp | 8 ++++---- .../widgets/dialogs/dlg_select_set_for_cards.cpp | 6 +++--- ...rchidekt_api_response_deck_display_widget.cpp | 4 +--- .../models/deck_list/deck_list_model.cpp | 16 ++++++++++++++++ .../models/deck_list/deck_list_model.h | 11 +++++++++++ 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index facf0c98b..93286191f 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -386,15 +386,15 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox() // Collect unique (name, providerId) pairs QSet> bannerCardSet; - QList cardsInDeck = deckModel->getDeckList()->getCardNodes(); + QList cardsInDeck = deckModel->getCardRefs(); - for (auto currentCard : cardsInDeck) { - if (!CardDatabaseManager::query()->getCard(currentCard->toCardRef())) { + for (auto cardRef : cardsInDeck) { + if (!CardDatabaseManager::query()->getCard(cardRef)) { continue; } // Insert one entry per distinct card, ignore copies - bannerCardSet.insert({currentCard->getName(), currentCard->getCardProviderId()}); + bannerCardSet.insert({cardRef.name, cardRef.providerId}); } // Convert to sorted list diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp index 52768e0e7..656abbfdc 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp @@ -178,7 +178,7 @@ void DlgSelectSetForCards::actOK() void DlgSelectSetForCards::actClear() { emit deckAboutToBeModified(tr("Cleared all printing information.")); - model->getDeckList()->forEachCard(CardNodeFunction::ClearPrintingData()); + model->forEachCard(CardNodeFunction::ClearPrintingData()); emit deckModified(); accept(); } @@ -186,8 +186,8 @@ void DlgSelectSetForCards::actClear() void DlgSelectSetForCards::actSetAllToPreferred() { emit deckAboutToBeModified(tr("Set all printings to preferred.")); - model->getDeckList()->forEachCard(CardNodeFunction::ClearPrintingData()); - model->getDeckList()->forEachCard(CardNodeFunction::SetProviderIdToPreferred()); + model->forEachCard(CardNodeFunction::ClearPrintingData()); + model->forEachCard(CardNodeFunction::SetProviderIdToPreferred()); emit deckModified(); accept(); } diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp index ef29a1732..288cdc425 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp @@ -70,9 +70,7 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset); model->getDeckList()->loadFromStream_Plain(deckStream, false); - model->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId()); - - model->rebuildTree(); + model->forEachCard(CardNodeFunction::ResolveProviderId()); retranslateUi(); } diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index 8859a31fe..5fd4d71e8 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -561,6 +561,11 @@ void DeckListModel::setDeckList(DeckList *_deck) rebuildTree(); } +void DeckListModel::forEachCard(const std::function &func) +{ + deckList->forEachCard(func); +} + static QList cardNodesToExactCards(QList nodes) { QList cards; @@ -600,6 +605,17 @@ QList DeckListModel::getCardNames() const return names; } +QList DeckListModel::getCardRefs() const +{ + auto nodes = deckList->getCardNodes(); + + QList cardRefs; + std::transform(nodes.cbegin(), nodes.cend(), std::back_inserter(cardRefs), + [](auto node) { return node->toCardRef(); }); + + return cardRefs; +} + QList DeckListModel::getZones() const { auto zoneNodes = deckList->getZoneNodes(); diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h index 6e8882084..80a519297 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h @@ -309,6 +309,13 @@ public: } void setDeckList(DeckList *_deck); + /** + * @brief Apply a function to every card in the deck tree. + * + * @param func Function taking (zone node, card node). + */ + void forEachCard(const std::function &func); + /** * @brief Creates a list consisting of the entries of the model mapped into ExactCards (with each entry looked up * in the card database). @@ -323,6 +330,10 @@ public: * @brief Gets a deduplicated list of all card names that appear in the model */ [[nodiscard]] QList getCardNames() const; + /** + * @brief Gets a deduplicated list of all CardRefs that appear in the model + */ + [[nodiscard]] QList getCardRefs() const; /** * @brief Gets a list of all zone names that appear in the model */ From e80f13b78e25ca90cdb88e14ababeb2c22e6aa52 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Mon, 22 Dec 2025 05:48:55 -0800 Subject: [PATCH 21/43] [DeckDockWidget] Refactor to move down some methods in AbstractTabDeckEditor (#6444) * move actSwapCard down * rename method * move actAddCard down --- .../deck_editor_deck_dock_widget.cpp | 56 ++++++++++++++++--- .../deck_editor_deck_dock_widget.h | 10 ++-- .../widgets/tabs/abstract_tab_deck_editor.cpp | 36 +----------- .../widgets/tabs/abstract_tab_deck_editor.h | 6 +- .../tab_deck_editor_visual.cpp | 2 +- 5 files changed, 57 insertions(+), 53 deletions(-) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 93286191f..3ee7f647a 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -76,14 +76,14 @@ void DeckEditorDeckDockWidget::createDeckDock() deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(deckView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &DeckEditorDeckDockWidget::updateCard); - connect(deckView, &QTreeView::doubleClicked, this, &DeckEditorDeckDockWidget::actSwapCard); + connect(deckView, &QTreeView::doubleClicked, this, &DeckEditorDeckDockWidget::actSwapSelection); deckView->setContextMenuPolicy(Qt::CustomContextMenu); connect(deckView, &QTreeView::customContextMenuRequested, this, &DeckEditorDeckDockWidget::decklistCustomMenu); - connect(&deckViewKeySignals, &KeySignals::onShiftS, this, &DeckEditorDeckDockWidget::actSwapCard); - connect(&deckViewKeySignals, &KeySignals::onEnter, this, &DeckEditorDeckDockWidget::actIncrement); - connect(&deckViewKeySignals, &KeySignals::onCtrlAltEqual, this, &DeckEditorDeckDockWidget::actIncrement); + connect(&deckViewKeySignals, &KeySignals::onShiftS, this, &DeckEditorDeckDockWidget::actSwapSelection); + connect(&deckViewKeySignals, &KeySignals::onEnter, this, &DeckEditorDeckDockWidget::actIncrementSelection); + connect(&deckViewKeySignals, &KeySignals::onCtrlAltEqual, this, &DeckEditorDeckDockWidget::actIncrementSelection); connect(&deckViewKeySignals, &KeySignals::onCtrlAltMinus, this, &DeckEditorDeckDockWidget::actDecrementSelection); - connect(&deckViewKeySignals, &KeySignals::onShiftRight, this, &DeckEditorDeckDockWidget::actIncrement); + connect(&deckViewKeySignals, &KeySignals::onShiftRight, this, &DeckEditorDeckDockWidget::actIncrementSelection); connect(&deckViewKeySignals, &KeySignals::onShiftLeft, this, &DeckEditorDeckDockWidget::actDecrementSelection); connect(&deckViewKeySignals, &KeySignals::onDelete, this, &DeckEditorDeckDockWidget::actRemoveCard); @@ -181,7 +181,7 @@ void DeckEditorDeckDockWidget::createDeckDock() aIncrement = new QAction(QString(), this); aIncrement->setIcon(QPixmap("theme:icons/increment")); - connect(aIncrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actIncrement); + connect(aIncrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actIncrementSelection); auto *tbIncrement = new QToolButton(this); tbIncrement->setDefaultAction(aIncrement); @@ -199,7 +199,7 @@ void DeckEditorDeckDockWidget::createDeckDock() aSwapCard = new QAction(QString(), this); aSwapCard->setIcon(QPixmap("theme:icons/swap")); - connect(aSwapCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actSwapCard); + connect(aSwapCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actSwapSelection); auto *tbSwapCard = new QToolButton(this); tbSwapCard->setDefaultAction(aSwapCard); @@ -582,7 +582,32 @@ QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodes() const return selectedRows; } -void DeckEditorDeckDockWidget::actIncrement() +void DeckEditorDeckDockWidget::actAddCard(const ExactCard &card, const QString &_zoneName) +{ + if (!card) { + return; + } + + QString zoneName = card.getInfo().getIsToken() ? DECK_ZONE_TOKENS : _zoneName; + + emit requestDeckHistorySave(tr("Added (%1): %2 (%3) %4") + .arg(zoneName, card.getName(), card.getPrinting().getSet()->getCorrectedShortName(), + card.getPrinting().getProperty("num"))); + + QModelIndex newCardIndex = deckModel->addCard(card, zoneName); + + if (!newCardIndex.isValid()) { + return; + } + + expandAll(); + deckView->clearSelection(); + deckView->setCurrentIndex(newCardIndex); + + emit deckModified(); +} + +void DeckEditorDeckDockWidget::actIncrementSelection() { auto selectedRows = getSelectedCardNodes(); @@ -591,7 +616,20 @@ void DeckEditorDeckDockWidget::actIncrement() } } -void DeckEditorDeckDockWidget::actSwapCard() +void DeckEditorDeckDockWidget::actSwapCard(const ExactCard &card, const QString &zoneName) +{ + QString providerId = card.getPrinting().getUuid(); + QString collectorNumber = card.getPrinting().getProperty("num"); + + QModelIndex foundCard = deckModel->findCard(card.getName(), zoneName, providerId, collectorNumber); + if (!foundCard.isValid()) { + foundCard = deckModel->findCard(card.getName(), zoneName); + } + + swapCard(foundCard); +} + +void DeckEditorDeckDockWidget::actSwapSelection() { auto selectedRows = getSelectedCardNodes(); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h index 60e1d36d2..6c88cafff 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -62,13 +62,13 @@ public slots: void sortDeckModelToDeckView(); DeckLoader *getDeckLoader(); const DeckList &getDeckList() const; - void actIncrement(); - bool swapCard(const QModelIndex &idx); + void actAddCard(const ExactCard &card, const QString &zoneName); + void actIncrementSelection(); void actDecrementCard(const ExactCard &card, QString zoneName); void actDecrementSelection(); - void actSwapCard(); + void actSwapCard(const ExactCard &card, const QString &zoneName); + void actSwapSelection(); void actRemoveCard(); - void offsetCountAtIndex(const QModelIndex &idx, int offset); void initializeFormats(); void expandAll(); @@ -108,9 +108,11 @@ private: void recursiveExpand(const QModelIndex &index); [[nodiscard]] QModelIndexList getSelectedCardNodes() const; + void offsetCountAtIndex(const QModelIndex &idx, int offset); private slots: void decklistCustomMenu(QPoint point); + bool swapCard(const QModelIndex ¤tIndex); void updateCard(QModelIndex, const QModelIndex ¤t); void updateName(const QString &name); void updateComments(); diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp index 398208993..8eb1477f1 100644 --- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp +++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp @@ -140,23 +140,9 @@ void AbstractTabDeckEditor::onDeckHistoryClearRequested() * @param card Card to add. * @param zoneName Zone to add the card to. */ -void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, QString zoneName) +void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, const QString &zoneName) { - if (!card) - return; - - if (card.getInfo().getIsToken()) - zoneName = DECK_ZONE_TOKENS; - - onDeckHistorySaveRequested(QString(tr("Added (%1): %2 (%3) %4")) - .arg(zoneName, card.getName(), card.getPrinting().getSet()->getCorrectedShortName(), - card.getPrinting().getProperty("num"))); - - QModelIndex newCardIndex = deckDockWidget->deckModel->addCard(card, zoneName); - deckDockWidget->expandAll(); - deckDockWidget->deckView->clearSelection(); - deckDockWidget->deckView->setCurrentIndex(newCardIndex); - setModified(true); + deckDockWidget->actAddCard(card, zoneName); databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length()); } @@ -193,24 +179,6 @@ void AbstractTabDeckEditor::actDecrementCardFromSideboard(const ExactCard &card) emit decrementCard(card, DECK_ZONE_SIDE); } -/** - * @brief Swaps a card in a deck zone. - * @param card Card to swap. - * @param zoneName Zone to swap in. - */ -void AbstractTabDeckEditor::actSwapCard(const ExactCard &card, const QString &zoneName) -{ - QString providerId = card.getPrinting().getUuid(); - QString collectorNumber = card.getPrinting().getProperty("num"); - - QModelIndex foundCard = deckDockWidget->deckModel->findCard(card.getName(), zoneName, providerId, collectorNumber); - if (!foundCard.isValid()) { - foundCard = deckDockWidget->deckModel->findCard(card.getName(), zoneName); - } - - deckDockWidget->swapCard(foundCard); -} - /** * @brief Opens a deck in this tab. * @param deck The deck diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h index 10675dd39..bfbda778c 100644 --- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h +++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h @@ -77,7 +77,6 @@ class QAction; * * - actAddCard(const ExactCard &card) — Adds a card to the deck. * - actDecrementCard(const ExactCard &card) — Removes a single instance of a card from the deck. - * - actSwapCard(const ExactCard &card, const QString &zone) — Swaps a card between zones. * - actRemoveCard() — Removes the currently selected card from the deck. * - actSaveDeckAs() — Performs a "Save As" action for the deck. * - updateCard(const ExactCard &card) — Updates the currently displayed card info in the dock. @@ -320,10 +319,7 @@ protected: bool isBlankNewDeck() const; /** @brief Helper function to add a card to a specific deck zone. */ - void addCardHelper(const ExactCard &card, QString zoneName); - - /** @brief Swaps a card in the deck view. */ - void actSwapCard(const ExactCard &card, const QString &zoneName); + void addCardHelper(const ExactCard &card, const QString &zoneName); /** @brief Opens a deck from a file. */ virtual void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation); diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp index 6dd55d545..57615df94 100644 --- a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp +++ b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp @@ -191,7 +191,7 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event, // Double click = swap if (event->type() == QEvent::MouseButtonDblClick && event->button() == Qt::LeftButton) { - actSwapCard(card, zoneName); + deckDockWidget->actSwapCard(card, zoneName); idx = deckDockWidget->deckModel->findCard(card.getName(), zoneName); sel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); return; From e557ae0f2a9177961b86299701e4f3dcbde0b286 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:09:50 +0100 Subject: [PATCH 22/43] Bump actions/upload-artifact from 5 to 6 (#6445) --- .github/workflows/desktop-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 06549702d..b89190c8d 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -213,7 +213,7 @@ jobs: - name: Upload artifact id: upload_artifact if: matrix.package != 'skip' - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ${{matrix.distro}}${{matrix.version}}-package path: ${{steps.build.outputs.path}} @@ -450,7 +450,7 @@ jobs: - name: Upload artifact id: upload_artifact if: matrix.make_package - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ${{matrix.artifact_name}} path: ${{steps.build.outputs.path}} @@ -458,7 +458,7 @@ jobs: - name: Upload pdb database if: matrix.os == 'Windows' - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: Windows${{matrix.target}}-debug-pdbs path: | From be17ee190222c763588bd88d27b413f330ae251b Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 23 Dec 2025 06:07:39 -0800 Subject: [PATCH 23/43] [DeckListModel] Refactor to use column num constants (#6441) --- .../card_group_display_widget.cpp | 7 ++--- .../cards/deck_card_zone_display_widget.cpp | 5 ++-- .../deck_editor_database_display_widget.cpp | 8 +++--- .../deck_editor_deck_dock_widget.cpp | 27 ++++++++++--------- .../printing_selector/card_amount_widget.cpp | 14 +++++----- ...idekt_api_response_deck_display_widget.cpp | 12 +++++---- .../visual_deck_editor_widget.cpp | 7 ++--- .../deck_list_sort_filter_proxy_model.cpp | 4 +-- 8 files changed, 46 insertions(+), 38 deletions(-) diff --git a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp index cb83768f4..fdac9d0f0 100644 --- a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp @@ -91,8 +91,9 @@ QWidget *CardGroupDisplayWidget::constructWidgetForIndex(QPersistentModelIndex i if (indexToWidgetMap.contains(index)) { return indexToWidgetMap[index]; } - auto cardName = deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString(); - auto cardProviderId = deckListModel->data(index.sibling(index.row(), 4), Qt::EditRole).toString(); + auto cardName = index.sibling(index.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); + auto cardProviderId = + index.sibling(index.row(), DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::EditRole).toString(); auto widget = new CardInfoPictureWithTextOverlayWidget(getLayoutParent(), true); widget->setScaleFactor(cardSizeWidget->getSlider()->value()); @@ -114,7 +115,7 @@ void CardGroupDisplayWidget::updateCardDisplays() // This doesn't really matter since overwrite the whole lessThan function to just compare dynamically anyway. proxy.setSortRole(Qt::EditRole); - proxy.sort(1, Qt::AscendingOrder); + proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder); // 1. trackedIndex is a source index → map it to proxy space QModelIndex proxyParent = proxy.mapFromSource(trackedIndex); diff --git a/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp b/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp index 132964f13..4742467b1 100644 --- a/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp @@ -82,10 +82,11 @@ void DeckCardZoneDisplayWidget::cleanupInvalidCardGroup(CardGroupDisplayWidget * void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex index) { - auto categoryName = deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString(); if (indexToWidgetMap.contains(index)) { return; } + + auto categoryName = index.sibling(index.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); if (displayType == DisplayType::Overlap) { auto *displayWidget = new OverlappedCardGroupDisplayWidget( cardGroupContainer, deckListModel, selectionModel, index, zoneName, categoryName, activeGroupCriteria, @@ -120,7 +121,7 @@ void DeckCardZoneDisplayWidget::displayCards() QSortFilterProxyModel proxy; proxy.setSourceModel(deckListModel); proxy.setSortRole(Qt::EditRole); - proxy.sort(1, Qt::AscendingOrder); + proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder); // 1. trackedIndex is a source index → map it to proxy space QModelIndex proxyParent = proxy.mapFromSource(trackedIndex); 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 b4b27e48a..f5549a98c 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 @@ -134,13 +134,13 @@ void DeckEditorDatabaseDisplayWidget::clearAllDatabaseFilters() void DeckEditorDatabaseDisplayWidget::updateCard(const QModelIndex ¤t, const QModelIndex & /*previous*/) { - const QString cardName = current.sibling(current.row(), 0).data().toString(); - if (!current.isValid()) { return; } - if (!current.model()->hasChildren(current.sibling(current.row(), 0))) { + const QString cardName = current.siblingAtColumn(CardDatabaseModel::NameColumn).data().toString(); + + if (!current.model()->hasChildren(current.siblingAtColumn(CardDatabaseModel::NameColumn))) { emit cardChanged(CardDatabaseManager::query()->getPreferredCard(cardName)); } } @@ -172,7 +172,7 @@ ExactCard DeckEditorDatabaseDisplayWidget::currentCard() const return {}; } - const QString cardName = currentIndex.sibling(currentIndex.row(), 0).data().toString(); + const QString cardName = currentIndex.siblingAtColumn(CardDatabaseModel::NameColumn).data().toString(); return CardDatabaseManager::query()->getPreferredCard(cardName); } diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 3ee7f647a..1e7319aa3 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -319,17 +319,17 @@ ExactCard DeckEditorDeckDockWidget::getCurrentCard() QModelIndex current = deckView->selectionModel()->currentIndex(); if (!current.isValid()) return {}; - const QString cardName = current.sibling(current.row(), 1).data().toString(); - const QString cardProviderID = current.sibling(current.row(), 4).data().toString(); + const QString cardName = current.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString(); + const QString cardProviderID = current.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data().toString(); const QModelIndex gparent = current.parent().parent(); if (!gparent.isValid()) { return {}; } - const QString zoneName = gparent.sibling(gparent.row(), 1).data(Qt::EditRole).toString(); + const QString zoneName = gparent.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); - if (!current.model()->hasChildren(current.sibling(current.row(), 0))) { + if (!current.model()->hasChildren(current.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT))) { if (ExactCard selectedCard = CardDatabaseManager::query()->getCard({cardName, cardProviderID})) { return selectedCard; } @@ -665,14 +665,15 @@ bool DeckEditorDeckDockWidget::swapCard(const QModelIndex ¤tIndex) { if (!currentIndex.isValid()) return false; - const QString cardName = currentIndex.sibling(currentIndex.row(), 1).data().toString(); - const QString cardProviderID = currentIndex.sibling(currentIndex.row(), 4).data().toString(); + const QString cardName = currentIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString(); + const QString cardProviderID = + currentIndex.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data().toString(); const QModelIndex gparent = currentIndex.parent().parent(); if (!gparent.isValid()) return false; - const QString zoneName = gparent.sibling(gparent.row(), 1).data(Qt::EditRole).toString(); + const QString zoneName = gparent.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); offsetCountAtIndex(currentIndex, -1); const QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; @@ -738,7 +739,7 @@ void DeckEditorDeckDockWidget::actRemoveCard() continue; } QModelIndex sourceIndex = proxy->mapToSource(index); - QString cardName = sourceIndex.sibling(sourceIndex.row(), 1).data().toString(); + QString cardName = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString(); emit requestDeckHistorySave(QString(tr("Removed \"%1\" (all copies)")).arg(cardName)); @@ -761,11 +762,11 @@ void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, int of QModelIndex sourceIndex = proxy->mapToSource(idx); - const QModelIndex numberIndex = sourceIndex.sibling(sourceIndex.row(), 0); - const QModelIndex nameIndex = sourceIndex.sibling(sourceIndex.row(), 1); + const QModelIndex numberIndex = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT); + const QModelIndex nameIndex = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME); - const QString cardName = deckModel->data(nameIndex, Qt::EditRole).toString(); - const int count = deckModel->data(numberIndex, Qt::EditRole).toInt(); + const QString cardName = nameIndex.data(Qt::EditRole).toString(); + const int count = numberIndex.data(Qt::EditRole).toInt(); const int new_count = count + offset; const auto reason = @@ -773,7 +774,7 @@ void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, int of .arg(offset > 0 ? tr("Added") : tr("Removed")) .arg(qAbs(offset)) .arg(cardName) - .arg(deckModel->data(sourceIndex.sibling(sourceIndex.row(), 4), Qt::DisplayRole).toString()); + .arg(sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString()); emit requestDeckHistorySave(reason); diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp index 3d519e595..e979637f8 100644 --- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp @@ -151,9 +151,10 @@ void CardAmountWidget::addPrinting(const QString &zone) bool replacingProviderless = false; if (existing.isValid()) { - QString providerId = deckModel->data(existing.sibling(existing.row(), 4), Qt::DisplayRole).toString(); + QString providerId = + existing.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString(); if (providerId.isEmpty()) { - int amount = deckModel->data(existing, Qt::DisplayRole).toInt(); + int amount = existing.data(Qt::DisplayRole).toInt(); extraCopies = amount - 1; // One less because we *always* add one replacingProviderless = true; } @@ -177,9 +178,10 @@ void CardAmountWidget::addPrinting(const QString &zone) recursiveExpand(newCardIndex); // Check if a card without a providerId already exists in the deckModel and replace it, if so. - QString foundProviderId = deckModel->data(existing.sibling(existing.row(), 4), Qt::DisplayRole).toString(); + QString foundProviderId = + existing.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString(); if (existing.isValid() && existing != newCardIndex && foundProviderId == "") { - auto amount = deckModel->data(existing, Qt::DisplayRole); + auto amount = existing.data(Qt::DisplayRole); for (int i = 0; i < amount.toInt() - 1; i++) { deckModel->addCard(rootCard, zone); } @@ -252,8 +254,8 @@ void CardAmountWidget::offsetCountAtIndex(const QModelIndex &idx, int offset) return; } - const QModelIndex numberIndex = idx.sibling(idx.row(), 0); - const int count = deckModel->data(numberIndex, Qt::EditRole).toInt(); + const QModelIndex numberIndex = idx.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT); + const int count = numberIndex.data(Qt::EditRole).toInt(); const int new_count = count + offset; deckView->setCurrentIndex(numberIndex); diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp index 288cdc425..1547cca74 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp @@ -83,7 +83,7 @@ void ArchidektApiResponseDeckDisplayWidget::retranslateUi() void ArchidektApiResponseDeckDisplayWidget::onGroupCriteriaChange(const QString &activeGroupCriteria) { model->setActiveGroupCriteria(DeckListModelGroupCriteria::fromString(activeGroupCriteria)); - model->sort(1, Qt::AscendingOrder); + model->sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder); } void ArchidektApiResponseDeckDisplayWidget::actOpenInDeckEditor() @@ -120,7 +120,7 @@ void ArchidektApiResponseDeckDisplayWidget::constructZoneWidgetsFromDeckListMode QSortFilterProxyModel proxy; proxy.setSourceModel(model); proxy.setSortRole(Qt::EditRole); - proxy.sort(1, Qt::AscendingOrder); + proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder); for (int i = 0; i < proxy.rowCount(); ++i) { QModelIndex proxyIndex = proxy.index(i, 0); @@ -133,10 +133,12 @@ void ArchidektApiResponseDeckDisplayWidget::constructZoneWidgetsFromDeckListMode continue; } + QString zoneName = + persistent.sibling(persistent.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); + DeckCardZoneDisplayWidget *zoneDisplayWidget = - new DeckCardZoneDisplayWidget(zoneContainer, model, nullptr, persistent, - model->data(persistent.sibling(persistent.row(), 1), Qt::EditRole).toString(), - "maintype", {"name"}, DisplayType::Overlap, 20, 10, cardSizeSlider); + new DeckCardZoneDisplayWidget(zoneContainer, model, nullptr, persistent, zoneName, "maintype", {"name"}, + DisplayType::Overlap, 20, 10, cardSizeSlider); connect(displayOptionsWidget, &VisualDeckDisplayOptionsWidget::sortCriteriaChanged, zoneDisplayWidget, &DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged); diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp index 76cfe8c8e..7b70e2b6d 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp @@ -264,9 +264,10 @@ void VisualDeckEditorWidget::onCardRemoval(const QModelIndex &parent, int first, void VisualDeckEditorWidget::constructZoneWidgetForIndex(QPersistentModelIndex persistent) { + QString zoneName = + persistent.sibling(persistent.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); DeckCardZoneDisplayWidget *zoneDisplayWidget = new DeckCardZoneDisplayWidget( - zoneContainer, deckListModel, selectionModel, persistent, - deckListModel->data(persistent.sibling(persistent.row(), 1), Qt::EditRole).toString(), + zoneContainer, deckListModel, selectionModel, persistent, zoneName, displayOptionsWidget->getActiveGroupCriteria(), displayOptionsWidget->getActiveSortCriteria(), displayOptionsWidget->getDisplayType(), 20, 10, cardSizeWidget); connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover); @@ -290,7 +291,7 @@ void VisualDeckEditorWidget::constructZoneWidgetsFromDeckListModel() QSortFilterProxyModel proxy; proxy.setSourceModel(deckListModel); proxy.setSortRole(Qt::EditRole); - proxy.sort(1, Qt::AscendingOrder); + proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder); for (int i = 0; i < proxy.rowCount(); ++i) { QModelIndex proxyIndex = proxy.index(i, 0); diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.cpp index 35fd4d9f8..0ec159737 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.cpp @@ -11,8 +11,8 @@ bool DeckListSortFilterProxyModel::lessThan(const QModelIndex &left, const QMode bool rightIsCard = src->data(right, Qt::UserRole + 1).toBool(); if (!leftIsCard || !rightIsCard) { - QString lName = src->data(left.siblingAtColumn(1), Qt::EditRole).toString(); - QString rName = src->data(right.siblingAtColumn(1), Qt::EditRole).toString(); + QString lName = src->data(left.siblingAtColumn(DeckListModelColumns::CARD_NAME), Qt::EditRole).toString(); + QString rName = src->data(right.siblingAtColumn(DeckListModelColumns::CARD_NAME), Qt::EditRole).toString(); return lName.localeAwareCompare(rName) < 0; } From 01e8e4d5895408a5eee9a0300988467c5c506db6 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 23 Dec 2025 06:45:27 -0800 Subject: [PATCH 24/43] [DeckDockWidget] Fix swap not auto-expanding tree (#6443) --- .../widgets/deck_editor/deck_editor_deck_dock_widget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 1e7319aa3..625af4dfa 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -681,7 +681,7 @@ bool DeckEditorDeckDockWidget::swapCard(const QModelIndex ¤tIndex) QModelIndex newCardIndex = card ? deckModel->addCard(card, otherZoneName) // Third argument (true) says create the card no matter what, even if not in DB : deckModel->addPreferredPrintingCard(cardName, otherZoneName, true); - recursiveExpand(proxy->mapToSource(newCardIndex)); + recursiveExpand(proxy->mapFromSource(newCardIndex)); return true; } From e7af1bbec9425146de1d3f92ba942f093d302379 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 23 Dec 2025 16:00:07 +0100 Subject: [PATCH 25/43] [EDHRec] New layout for commander details (#6405) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Stuff Took 22 minutes * New layout for commander details. Took 1 hour 18 minutes * Update plate to encompass everything, update font sizes. Took 10 minutes * Include map. Took 2 minutes * Include QSet Took 5 minutes --------- Co-authored-by: Lukas Brübach --- cockatrice/CMakeLists.txt | 4 + ...i_response_card_details_display_widget.cpp | 6 +- ...ponse_commander_details_display_widget.cpp | 33 ++++- ...esponse_commander_details_display_widget.h | 6 +- ...api_response_bracket_navigation_widget.cpp | 99 ++++++++++++++ ...r_api_response_bracket_navigation_widget.h | 37 ++++++ ..._api_response_budget_navigation_widget.cpp | 101 ++++++++++++++ ...er_api_response_budget_navigation_widget.h | 37 ++++++ ...mmander_api_response_navigation_widget.cpp | 123 +++--------------- ...commander_api_response_navigation_widget.h | 17 +-- 10 files changed, 336 insertions(+), 127 deletions(-) create mode 100644 cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp create mode 100644 cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h create mode 100644 cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp create mode 100644 cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 4c3679011..b2d387391 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -287,6 +287,10 @@ set(cockatrice_SOURCES src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp src/interface/key_signals.cpp src/interface/logger.cpp + src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp + src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h + src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp + src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h ) add_subdirectory(sounds) diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp index 26ade96dd..c01c7fa43 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp @@ -19,17 +19,17 @@ EdhrecApiResponseCardDetailsDisplayWidget::EdhrecApiResponseCardDetailsDisplayWi nameLabel = new QLabel(this); nameLabel->setText(toDisplay.name); nameLabel->setAlignment(Qt::AlignHCenter); + nameLabel->setStyleSheet("font-size: 20px; font-weight: bold"); inclusionDisplayWidget = new EdhrecApiResponseCardInclusionDisplayWidget(this, toDisplay); synergyDisplayWidget = new EdhrecApiResponseCardSynergyDisplayWidget(this, toDisplay); - layout->addWidget(nameLabel); - layout->addWidget(cardPictureWidget); - backgroundPlateWidget = new BackgroundPlateWidget(this); auto plateLayout = new QVBoxLayout(backgroundPlateWidget); + plateLayout->addWidget(nameLabel); + plateLayout->addWidget(cardPictureWidget); plateLayout->addWidget(inclusionDisplayWidget); plateLayout->addWidget(synergyDisplayWidget); diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp index b15d559f5..515475f3e 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp @@ -3,6 +3,7 @@ #include "../../../../../cards/card_info_picture_widget.h" #include "../../tab_edhrec_main.h" #include "../card_prices/edhrec_api_response_card_prices_display_widget.h" +#include "edhrec_commander_api_response_bracket_navigation_widget.h" #include @@ -12,9 +13,14 @@ EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCom QString baseUrl) : QWidget(parent), commanderDetails(_commanderDetails) { - layout = new QVBoxLayout(this); + layout = new QHBoxLayout(this); setLayout(layout); + commanderLayout = new QHBoxLayout(); + commanderDetailsLayout = new QVBoxLayout(); + commanderDetailsLayout->setAlignment(Qt::AlignCenter); + navigationAndPricesLayout = new QVBoxLayout(); + commanderPicture = new CardInfoPictureWidget(this); commanderPicture->setCard(CardDatabaseManager::query()->getCard({commanderDetails.getName()})); @@ -36,20 +42,35 @@ EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCom commanderDetails.debugPrint(); + commanderName = new QLabel(this); + commanderName->setText(commanderDetails.getName()); + commanderName->setAlignment(Qt::AlignCenter); + commanderName->setStyleSheet("font-size: 28px; font-weight: bold"); + label = new QLabel(this); label->setAlignment(Qt::AlignCenter); + label->setStyleSheet("font-size: 16px"); + salt = new QLabel(this); salt->setAlignment(Qt::AlignCenter); + salt->setStyleSheet("font-size: 16px"); cardPricesDisplayWidget = new EdhrecApiResponseCardPricesDisplayWidget(this, commanderDetails.getPrices()); navigationWidget = new EdhrecCommanderApiResponseNavigationWidget(this, commanderDetails, baseUrl); - layout->addWidget(commanderPicture); - layout->addWidget(label); - layout->addWidget(salt); - layout->addWidget(cardPricesDisplayWidget); - layout->addWidget(navigationWidget); + commanderLayout->addWidget(commanderPicture); + commanderDetailsLayout->addWidget(commanderName); + commanderDetailsLayout->addSpacing(1); + commanderDetailsLayout->addWidget(label); + commanderDetailsLayout->addWidget(salt); + commanderDetailsLayout->addWidget(cardPricesDisplayWidget); + commanderLayout->addLayout(commanderDetailsLayout); + navigationAndPricesLayout->addWidget(navigationWidget); + // navigationAndPricesLayout->addWidget(cardPricesDisplayWidget); + + layout->addLayout(commanderLayout); + layout->addLayout(navigationAndPricesLayout); retranslateUi(); } diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.h b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.h index 295e4228b..8e74588e2 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.h +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.h @@ -29,8 +29,12 @@ public: private: EdhrecCommanderApiResponseCommanderDetails commanderDetails; - QVBoxLayout *layout; + QHBoxLayout *layout; + QHBoxLayout *commanderLayout; + QVBoxLayout *commanderDetailsLayout; + QVBoxLayout *navigationAndPricesLayout; CardInfoPictureWidget *commanderPicture; + QLabel *commanderName; QLabel *label; QLabel *salt; EdhrecApiResponseCardPricesDisplayWidget *cardPricesDisplayWidget; diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp new file mode 100644 index 000000000..2bc727bd2 --- /dev/null +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp @@ -0,0 +1,99 @@ +#include "edhrec_commander_api_response_bracket_navigation_widget.h" + +#include + +EdhrecCommanderApiResponseBracketNavigationWidget::EdhrecCommanderApiResponseBracketNavigationWidget( + QWidget *parent, + const QString &baseUrl) + : QWidget(parent) +{ + layout = new QGridLayout(this); + setLayout(layout); + + gameChangerLabel = new QLabel(this); + + layout->addWidget(gameChangerLabel, 1, 0, 1, 2); + + for (int i = 0; i < gameChangerOptions.length(); i++) { + QString option = gameChangerOptions.at(i); + QString label = option.isEmpty() ? "All" : option.at(0).toUpper() + option.mid(1); + QPushButton *optionButton = new QPushButton(label, this); + optionButton->setMinimumHeight(84); + optionButton->setStyleSheet("font-size: 24px"); + gameChangerButtons[option] = optionButton; + layout->addWidget(optionButton, 2, i); + connect(optionButton, &QPushButton::clicked, this, [=, this]() { + selectedGameChanger = option; + updateOptionButtonSelection(gameChangerButtons, option); + emit requestNavigation(); + }); + } + + updateOptionButtonSelection(gameChangerButtons, ""); + + retranslateUi(); + applyOptionsFromUrl(baseUrl); +} + +void EdhrecCommanderApiResponseBracketNavigationWidget::retranslateUi() +{ + gameChangerLabel->setText(tr("Game Changers")); +} + +void EdhrecCommanderApiResponseBracketNavigationWidget::applyOptionsFromUrl(const QString &url) +{ + QString cleanedUrl = url; + + // Remove base and file extension + if (cleanedUrl.startsWith("https://json.edhrec.com/pages/")) { + cleanedUrl = cleanedUrl.mid(QString("https://json.edhrec.com/pages/").length()); + } + if (cleanedUrl.endsWith(".json")) { + cleanedUrl.chop(5); + } + + // Expecting something like: "commanders/the-ur-dragon/core/expensive" +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts); +#else + QStringList parts = cleanedUrl.split('/', QString::SkipEmptyParts); +#endif + + if (parts.size() < 2) { + return; + } + + QString commanderName = parts[1]; + QString gameChangerOpt; + + // Define valid sets + QSet validGameChangers = {"exhibition", "core", "upgraded", "optimized", "cedh"}; + + // Check remaining parts after commander + for (int i = 2; i < parts.size(); ++i) { + QString part = parts[i].toLower(); + if (validGameChangers.contains(part)) { + gameChangerOpt = part; + } + } + + // Validate and apply + if (!gameChangerButtons.contains(gameChangerOpt)) { + gameChangerOpt.clear(); + } + + selectedGameChanger = gameChangerOpt; + + updateOptionButtonSelection(gameChangerButtons, selectedGameChanger); +} + +void EdhrecCommanderApiResponseBracketNavigationWidget::updateOptionButtonSelection( + QMap &buttons, + const QString &selectedKey) +{ + for (auto it = buttons.begin(); it != buttons.end(); ++it) { + it.value()->setStyleSheet(it.key() == selectedKey + ? "background-color: lightblue; font-weight: bold; font-size: 24px;" + : "font-size: 24px"); + } +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h new file mode 100644 index 000000000..713ef2791 --- /dev/null +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h @@ -0,0 +1,37 @@ +#ifndef COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BRACKET_NAVIGATION_WIDGET_H +#define COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BRACKET_NAVIGATION_WIDGET_H + +#include +#include +#include +#include +#include + +class EdhrecCommanderApiResponseBracketNavigationWidget : public QWidget +{ + Q_OBJECT +public: + explicit EdhrecCommanderApiResponseBracketNavigationWidget(QWidget *parent, const QString &baseUrl); + void retranslateUi(); + void applyOptionsFromUrl(const QString &url); + QString getSelectedGameChanger() const + { + return selectedGameChanger; + } + +signals: + void requestNavigation(); + +private: + QGridLayout *layout; + QLabel *gameChangerLabel; + + QStringList gameChangerOptions = {"", "exhibition", "core", "upgraded", "optimized", "cedh"}; + QString selectedGameChanger; + + QMap gameChangerButtons; + + void updateOptionButtonSelection(QMap &buttons, const QString &selectedKey); +}; + +#endif // COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BRACKET_NAVIGATION_WIDGET_H diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp new file mode 100644 index 000000000..8470e6b3f --- /dev/null +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp @@ -0,0 +1,101 @@ +#include "edhrec_commander_api_response_budget_navigation_widget.h" + +#include + +EdhrecCommanderApiResponseBudgetNavigationWidget::EdhrecCommanderApiResponseBudgetNavigationWidget( + QWidget *parent, + const QString &baseUrl) + : QWidget(parent) +{ + layout = new QGridLayout(this); + setLayout(layout); + + budgetLabel = new QLabel(this); + + layout->addWidget(budgetLabel, 3, 0, 1, 2); + + for (int i = 0; i < budgetOptions.length(); i++) { + QString option = budgetOptions.at(i); + QString label = option.isEmpty() ? "Any" : option.at(0).toUpper() + option.mid(1); + QPushButton *btn = new QPushButton(label, this); + btn->setMinimumHeight(84); + btn->setStyleSheet("font-size: 24px"); + budgetButtons[option] = btn; + layout->addWidget(btn, 4, i); + connect(btn, &QPushButton::clicked, this, [=, this]() { + selectedBudget = option; + updateOptionButtonSelection(budgetButtons, option); + emit requestNavigation(); + }); + } + + updateOptionButtonSelection(budgetButtons, ""); + + retranslateUi(); + applyOptionsFromUrl(baseUrl); +} + +void EdhrecCommanderApiResponseBudgetNavigationWidget::retranslateUi() +{ + budgetLabel->setText(tr("Budget")); +} + +void EdhrecCommanderApiResponseBudgetNavigationWidget::applyOptionsFromUrl(const QString &url) +{ + QString cleanedUrl = url; + + // Remove base and file extension + if (cleanedUrl.startsWith("https://json.edhrec.com/pages/")) { + cleanedUrl = cleanedUrl.mid(QString("https://json.edhrec.com/pages/").length()); + } + if (cleanedUrl.endsWith(".json")) { + cleanedUrl.chop(5); + } + + // Expecting something like: "commanders/the-ur-dragon/core/expensive" +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts); +#else + QStringList parts = cleanedUrl.split('/', QString::SkipEmptyParts); +#endif + + if (parts.size() < 2) { + return; + } + + QString commanderName = parts[1]; + QString gameChangerOpt, budgetOpt; + + // Define valid sets + QSet validGameChangers = {"exhibition", "core", "upgraded", "optimized", "cedh"}; + QSet validBudgets = {"budget", "expensive"}; + + // Check remaining parts after commander + for (int i = 2; i < parts.size(); ++i) { + QString part = parts[i].toLower(); + if (validGameChangers.contains(part)) { + gameChangerOpt = part; + } else if (validBudgets.contains(part)) { + budgetOpt = part; + } + } + + if (!budgetButtons.contains(budgetOpt)) { + budgetOpt.clear(); + } + + selectedBudget = budgetOpt; + + updateOptionButtonSelection(budgetButtons, selectedBudget); +} + +void EdhrecCommanderApiResponseBudgetNavigationWidget::updateOptionButtonSelection( + QMap &buttons, + const QString &selectedKey) +{ + for (auto it = buttons.begin(); it != buttons.end(); ++it) { + it.value()->setStyleSheet(it.key() == selectedKey + ? "background-color: lightblue; font-weight: bold; font-size: 24px;" + : "font-size: 24px"); + } +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h new file mode 100644 index 000000000..666edba16 --- /dev/null +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h @@ -0,0 +1,37 @@ +#ifndef COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BUDGET_NAVIGATION_WIDGET_H +#define COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BUDGET_NAVIGATION_WIDGET_H + +#include +#include +#include +#include +#include + +class EdhrecCommanderApiResponseBudgetNavigationWidget : public QWidget +{ + Q_OBJECT +public: + explicit EdhrecCommanderApiResponseBudgetNavigationWidget(QWidget *parent, const QString &baseUrl); + void retranslateUi(); + void applyOptionsFromUrl(const QString &url); + QString getSelectedBudget() const + { + return selectedBudget; + } + +signals: + void requestNavigation(); + +private: + QGridLayout *layout; + QLabel *budgetLabel; + + QStringList budgetOptions = {"", "budget", "expensive"}; + QString selectedBudget; + + QMap budgetButtons; + + void updateOptionButtonSelection(QMap &buttons, const QString &selectedKey); +}; + +#endif // COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BUDGET_NAVIGATION_WIDGET_H diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp index 3f3b9f1ba..c42d2ed90 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp @@ -11,47 +11,28 @@ EdhrecCommanderApiResponseNavigationWidget::EdhrecCommanderApiResponseNavigation layout = new QGridLayout(this); setLayout(layout); - gameChangerLabel = new QLabel(this); - budgetLabel = new QLabel(this); + bracketNavigationWidget = new EdhrecCommanderApiResponseBracketNavigationWidget(this, baseUrl); + + connect(bracketNavigationWidget, &EdhrecCommanderApiResponseBracketNavigationWidget::requestNavigation, this, + &EdhrecCommanderApiResponseNavigationWidget::actRequestCommanderNavigation); + + budgetNavigationWidget = new EdhrecCommanderApiResponseBudgetNavigationWidget(this, baseUrl); + + connect(budgetNavigationWidget, &EdhrecCommanderApiResponseBudgetNavigationWidget::requestNavigation, this, + &EdhrecCommanderApiResponseNavigationWidget::actRequestCommanderNavigation); comboPushButton = new QPushButton(this); + comboPushButton->setMinimumHeight(84); + comboPushButton->setStyleSheet("font-size: 24px"); averageDeckPushButton = new QPushButton(this); + averageDeckPushButton->setMinimumHeight(84); + averageDeckPushButton->setStyleSheet("font-size: 24px"); layout->addWidget(comboPushButton, 0, 0, 1, 1); layout->addWidget(averageDeckPushButton, 0, 1, 1, 1); - layout->addWidget(gameChangerLabel, 1, 0, 1, 2); - - for (int i = 0; i < gameChangerOptions.length(); i++) { - QString option = gameChangerOptions.at(i); - QString label = option.isEmpty() ? "All" : option.at(0).toUpper() + option.mid(1); - QPushButton *optionButton = new QPushButton(label, this); - gameChangerButtons[option] = optionButton; - layout->addWidget(optionButton, 2, i); - connect(optionButton, &QPushButton::clicked, this, [=, this]() { - selectedGameChanger = option; - updateOptionButtonSelection(gameChangerButtons, option); - actRequestCommanderNavigation(); - }); - } - - layout->addWidget(budgetLabel, 3, 0, 1, 2); - - for (int i = 0; i < budgetOptions.length(); i++) { - QString option = budgetOptions.at(i); - QString label = option.isEmpty() ? "Any" : option.at(0).toUpper() + option.mid(1); - QPushButton *btn = new QPushButton(label, this); - budgetButtons[option] = btn; - layout->addWidget(btn, 4, i); - connect(btn, &QPushButton::clicked, this, [=, this]() { - selectedBudget = option; - updateOptionButtonSelection(budgetButtons, option); - actRequestCommanderNavigation(); - }); - } - - updateOptionButtonSelection(gameChangerButtons, ""); - updateOptionButtonSelection(budgetButtons, ""); + layout->addWidget(bracketNavigationWidget, 1, 0, 1, 2); + layout->addWidget(budgetNavigationWidget, 2, 0, 1, 2); QWidget *currentParent = parentWidget(); TabEdhRecMain *parentTab = nullptr; @@ -73,87 +54,21 @@ EdhrecCommanderApiResponseNavigationWidget::EdhrecCommanderApiResponseNavigation } retranslateUi(); - applyOptionsFromUrl(baseUrl); } void EdhrecCommanderApiResponseNavigationWidget::retranslateUi() { comboPushButton->setText(tr("Combos")); averageDeckPushButton->setText(tr("Average Deck")); - gameChangerLabel->setText(tr("Game Changers")); - budgetLabel->setText(tr("Budget")); -} - -void EdhrecCommanderApiResponseNavigationWidget::applyOptionsFromUrl(const QString &url) -{ - QString cleanedUrl = url; - - // Remove base and file extension - if (cleanedUrl.startsWith("https://json.edhrec.com/pages/")) { - cleanedUrl = cleanedUrl.mid(QString("https://json.edhrec.com/pages/").length()); - } - if (cleanedUrl.endsWith(".json")) { - cleanedUrl.chop(5); - } - - // Expecting something like: "commanders/the-ur-dragon/core/expensive" -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts); -#else - QStringList parts = cleanedUrl.split('/', QString::SkipEmptyParts); -#endif - - if (parts.size() < 2) { - return; - } - - QString commanderName = parts[1]; - QString gameChangerOpt, budgetOpt; - - // Define valid sets - QSet validGameChangers = {"core", "upgraded", "optimized"}; - QSet validBudgets = {"budget", "expensive"}; - - // Check remaining parts after commander - for (int i = 2; i < parts.size(); ++i) { - QString part = parts[i].toLower(); - if (validGameChangers.contains(part)) { - gameChangerOpt = part; - } else if (validBudgets.contains(part)) { - budgetOpt = part; - } - } - - // Validate and apply - if (!gameChangerButtons.contains(gameChangerOpt)) { - gameChangerOpt.clear(); - } - if (!budgetButtons.contains(budgetOpt)) { - budgetOpt.clear(); - } - - selectedGameChanger = gameChangerOpt; - selectedBudget = budgetOpt; - - updateOptionButtonSelection(gameChangerButtons, selectedGameChanger); - updateOptionButtonSelection(budgetButtons, selectedBudget); -} - -void EdhrecCommanderApiResponseNavigationWidget::updateOptionButtonSelection(QMap &buttons, - const QString &selectedKey) -{ - for (auto it = buttons.begin(); it != buttons.end(); ++it) { - it.value()->setStyleSheet(it.key() == selectedKey ? "background-color: lightblue; font-weight: bold;" : ""); - } } QString EdhrecCommanderApiResponseNavigationWidget::addNavigationOptionsToUrl(QString baseUrl) { - if (!selectedGameChanger.isEmpty()) { - baseUrl += "/" + selectedGameChanger; + if (!bracketNavigationWidget->getSelectedGameChanger().isEmpty()) { + baseUrl += "/" + bracketNavigationWidget->getSelectedGameChanger(); } - if (!selectedBudget.isEmpty()) { - baseUrl += "/" + selectedBudget; + if (!budgetNavigationWidget->getSelectedBudget().isEmpty()) { + baseUrl += "/" + budgetNavigationWidget->getSelectedBudget(); } return baseUrl; } diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h index b97e095f7..10dfa8223 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h @@ -8,6 +8,8 @@ #define EDHREC_COMMANDER_API_RESPONSE_NAVIGATION_WIDGET_H #include "edhrec_api_response_commander_details_display_widget.h" +#include "edhrec_commander_api_response_bracket_navigation_widget.h" +#include "edhrec_commander_api_response_budget_navigation_widget.h" #include #include @@ -23,7 +25,6 @@ public: const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails, QString baseUrl); void retranslateUi(); - void applyOptionsFromUrl(const QString &url); public slots: void actRequestCommanderNavigation(); @@ -35,24 +36,14 @@ signals: private: QGridLayout *layout; - QLabel *gameChangerLabel; - QLabel *budgetLabel; - - QStringList gameChangerOptions = {"", "core", "upgraded", "optimized"}; - QStringList budgetOptions = {"", "budget", "expensive"}; - - QString selectedGameChanger; - QString selectedBudget; - - QMap gameChangerButtons; - QMap budgetButtons; + EdhrecCommanderApiResponseBracketNavigationWidget *bracketNavigationWidget; + EdhrecCommanderApiResponseBudgetNavigationWidget *budgetNavigationWidget; QPushButton *comboPushButton; QPushButton *averageDeckPushButton; EdhrecCommanderApiResponseCommanderDetails commanderDetails; - void updateOptionButtonSelection(QMap &buttons, const QString &selectedKey); QString addNavigationOptionsToUrl(QString baseUrl); QString buildComboUrl() const; }; From 421d6b334abf57951ab6c6e8ad28a5010936458e Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 23 Dec 2025 07:21:47 -0800 Subject: [PATCH 26/43] [DeckDockWidget] Correctly handle auto-expanding tree (#6446) * move method * remove expandAll calls * update recursiveExpand * Refactor DeckModel access * [DeckDockWidget] Correctly handle auto-expand --- .../deck_editor_deck_dock_widget.cpp | 82 ++++++++++--------- .../deck_editor_deck_dock_widget.h | 6 +- .../printing_selector/card_amount_widget.cpp | 43 +--------- .../printing_selector/card_amount_widget.h | 2 - .../models/deck_list/deck_list_model.cpp | 47 ++++++++++- .../models/deck_list/deck_list_model.h | 37 ++++++++- 6 files changed, 131 insertions(+), 86 deletions(-) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 625af4dfa..57d34be00 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -156,6 +156,9 @@ void DeckEditorDeckDockWidget::createDeckDock() // Delay the update to avoid race conditions QTimer::singleShot(100, this, &DeckEditorDeckDockWidget::updateBannerCardComboBox); }); + connect(deckModel, &DeckListModel::cardAddedAt, this, &DeckEditorDeckDockWidget::recursiveExpand); + connect(deckModel, &DeckListModel::deckReplaced, this, &DeckEditorDeckDockWidget::expandAll); + connect(bannerCardComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &DeckEditorDeckDockWidget::setBannerCard); bannerCardComboBox->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible()); @@ -175,8 +178,6 @@ void DeckEditorDeckDockWidget::createDeckDock() deckModel->setActiveGroupCriteria(static_cast( activeGroupCriteriaComboBox->currentData(Qt::UserRole).toInt())); deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); - deckView->expandAll(); - deckView->expandAll(); }); aIncrement = new QAction(QString(), this); @@ -506,7 +507,6 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel() bannerCardComboBox->blockSignals(false); updateHash(); sortDeckModelToDeckView(); - expandAll(); deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags()); } @@ -516,8 +516,6 @@ void DeckEditorDeckDockWidget::sortDeckModelToDeckView() deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat()); formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat())); - deckView->expandAll(); - deckView->expandAll(); emit deckChanged(); } @@ -550,17 +548,26 @@ void DeckEditorDeckDockWidget::cleanDeck() deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags()); } -void DeckEditorDeckDockWidget::recursiveExpand(const QModelIndex &index) +/** + * @brief Expands all parents of the given index. + * @param sourceIndex The index to expand (model source index) + */ +void DeckEditorDeckDockWidget::recursiveExpand(const QModelIndex &sourceIndex) { - if (index.parent().isValid()) - recursiveExpand(index.parent()); - deckView->expand(index); + auto index = proxy->mapFromSource(sourceIndex); + + while (index.parent().isValid()) { + index = index.parent(); + deckView->expand(index); + } } +/** + * @brief Fully expands all levels of the deck view + */ void DeckEditorDeckDockWidget::expandAll() { - deckView->expandAll(); - deckView->expandAll(); + deckView->expandRecursively(deckView->rootIndex()); } /** @@ -600,7 +607,6 @@ void DeckEditorDeckDockWidget::actAddCard(const ExactCard &card, const QString & return; } - expandAll(); deckView->clearSelection(); deckView->setCurrentIndex(newCardIndex); @@ -612,7 +618,7 @@ void DeckEditorDeckDockWidget::actIncrementSelection() auto selectedRows = getSelectedCardNodes(); for (const auto &index : selectedRows) { - offsetCountAtIndex(index, 1); + offsetCountAtIndex(index, true); } } @@ -674,14 +680,15 @@ bool DeckEditorDeckDockWidget::swapCard(const QModelIndex ¤tIndex) return false; const QString zoneName = gparent.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); - offsetCountAtIndex(currentIndex, -1); + offsetCountAtIndex(currentIndex, false); const QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; - ExactCard card = CardDatabaseManager::query()->getCard({cardName, cardProviderID}); - QModelIndex newCardIndex = card ? deckModel->addCard(card, otherZoneName) - // Third argument (true) says create the card no matter what, even if not in DB - : deckModel->addPreferredPrintingCard(cardName, otherZoneName, true); - recursiveExpand(proxy->mapFromSource(newCardIndex)); + if (ExactCard card = CardDatabaseManager::query()->getCard({cardName, cardProviderID})) { + deckModel->addCard(card, otherZoneName); + } else { + // Third argument (true) says create the card no matter what, even if not in DB + deckModel->addPreferredPrintingCard(cardName, otherZoneName, true); + } return true; } @@ -703,7 +710,7 @@ void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString z deckView->clearSelection(); deckView->setCurrentIndex(proxy->mapToSource(idx)); - offsetCountAtIndex(idx, -1); + offsetCountAtIndex(idx, false); } void DeckEditorDeckDockWidget::actDecrementSelection() @@ -717,7 +724,7 @@ void DeckEditorDeckDockWidget::actDecrementSelection() } for (const auto &index : selectedRows) { - offsetCountAtIndex(index, -1); + offsetCountAtIndex(index, false); } deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -754,7 +761,12 @@ void DeckEditorDeckDockWidget::actRemoveCard() } } -void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, int offset) +/** + * @brief Increments or decrements the amount of the card node at the index by 1. + * @param idx The proxy index + * @param isIncrement If true, increments the count. If false, decrements the count + */ +void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, bool isIncrement) { if (!idx.isValid() || deckModel->hasChildren(idx)) { return; @@ -762,26 +774,22 @@ void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, int of QModelIndex sourceIndex = proxy->mapToSource(idx); - const QModelIndex numberIndex = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT); - const QModelIndex nameIndex = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME); + QString cardName = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); + QString providerId = + sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString(); - const QString cardName = nameIndex.data(Qt::EditRole).toString(); - const int count = numberIndex.data(Qt::EditRole).toInt(); - const int new_count = count + offset; - - const auto reason = - QString(tr("%1 %2 × \"%3\" (%4)")) - .arg(offset > 0 ? tr("Added") : tr("Removed")) - .arg(qAbs(offset)) - .arg(cardName) - .arg(sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString()); + const auto reason = QString(tr("%1 %2 × \"%3\" (%4)")) + .arg(isIncrement ? tr("Added") : tr("Removed")) + .arg(1) + .arg(cardName) + .arg(providerId); emit requestDeckHistorySave(reason); - if (new_count <= 0) { - deckModel->removeRow(sourceIndex.row(), sourceIndex.parent()); + if (isIncrement) { + deckModel->incrementAmountAtIndex(sourceIndex); } else { - deckModel->setData(numberIndex, new_count, Qt::EditRole); + deckModel->decrementAmountAtIndex(sourceIndex); } emit deckModified(); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h index 6c88cafff..30f752203 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -70,7 +70,6 @@ public slots: void actSwapSelection(); void actRemoveCard(); void initializeFormats(); - void expandAll(); signals: void nameChanged(); @@ -106,9 +105,8 @@ private: QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard; - void recursiveExpand(const QModelIndex &index); [[nodiscard]] QModelIndexList getSelectedCardNodes() const; - void offsetCountAtIndex(const QModelIndex &idx, int offset); + void offsetCountAtIndex(const QModelIndex &idx, bool isIncrement); private slots: void decklistCustomMenu(QPoint point); @@ -124,6 +122,8 @@ private slots: void updateShowBannerCardComboBox(bool visible); void updateShowTagsWidget(bool visible); void syncBannerCardComboBoxSelectionWithDeck(); + void recursiveExpand(const QModelIndex &parent); + void expandAll(); }; #endif // DECK_EDITOR_DECK_DOCK_WIDGET_H diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp index e979637f8..7c5804c37 100644 --- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp @@ -175,7 +175,6 @@ void CardAmountWidget::addPrinting(const QString &zone) // Add the card and expand the list UI auto newCardIndex = deckModel->addCard(rootCard, zone); - recursiveExpand(newCardIndex); // Check if a card without a providerId already exists in the deckModel and replace it, if so. QString foundProviderId = @@ -229,46 +228,6 @@ void CardAmountWidget::removePrintingSideboard() decrementCardHelper(DECK_ZONE_SIDE); } -/** - * @brief Recursively expands the card in the deck view starting from the given index. - * - * @param index The model index of the card to expand. - */ -void CardAmountWidget::recursiveExpand(const QModelIndex &index) -{ - if (index.parent().isValid()) { - recursiveExpand(index.parent()); - } - deckView->expand(index); -} - -/** - * @brief Offsets the card count at the specified index by the given amount. - * - * @param idx The model index of the card. - * @param offset The amount to add or subtract from the card count. - */ -void CardAmountWidget::offsetCountAtIndex(const QModelIndex &idx, int offset) -{ - if (!idx.isValid() || offset == 0) { - return; - } - - const QModelIndex numberIndex = idx.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT); - const int count = numberIndex.data(Qt::EditRole).toInt(); - const int new_count = count + offset; - - deckView->setCurrentIndex(numberIndex); - - if (new_count <= 0) { - deckModel->removeRow(idx.row(), idx.parent()); - } else { - deckModel->setData(numberIndex, new_count, Qt::EditRole); - } - - deckEditor->setModified(true); -} - /** * @brief Helper function to decrement the card count for a given zone. * @@ -288,7 +247,7 @@ void CardAmountWidget::decrementCardHelper(const QString &zone) QModelIndex idx = deckModel->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(), rootCard.getPrinting().getProperty("num")); - offsetCountAtIndex(idx, -1); + deckModel->decrementAmountAtIndex(idx); deckEditor->setModified(true); } diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h index 6d059bc04..b4704cede 100644 --- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h +++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h @@ -57,9 +57,7 @@ private: bool hovered; - void offsetCountAtIndex(const QModelIndex &idx, int offset); void decrementCardHelper(const QString &zoneName); - void recursiveExpand(const QModelIndex &index); private slots: void addPrintingMainboard(); diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index 5fd4d71e8..53d08cd9d 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -436,7 +436,51 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam } sort(lastKnownColumn, lastKnownOrder); emitRecursiveUpdates(parentIndex); - return nodeToIndex(cardNode); + auto index = nodeToIndex(cardNode); + + emit cardAddedAt(index); + + return index; +} + +bool DeckListModel::incrementAmountAtIndex(const QModelIndex &idx) +{ + return offsetAmountAtIndex(idx, 1); +} + +bool DeckListModel::decrementAmountAtIndex(const QModelIndex &idx) +{ + return offsetAmountAtIndex(idx, -1); +} + +bool DeckListModel::offsetAmountAtIndex(const QModelIndex &idx, int offset) +{ + if (!idx.isValid()) { + return false; + } + + auto *node = static_cast(idx.internalPointer()); + auto *card = dynamic_cast(node); + + if (!card) { + return false; + } + + const QModelIndex numberIndex = idx.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT); + const int count = numberIndex.data(Qt::EditRole).toInt(); + const int newCount = count + offset; + + if (newCount <= 0) { + removeRow(idx.row(), idx.parent()); + } else { + setData(numberIndex, newCount, Qt::EditRole); + } + + if (offset > 0) { + emit cardAddedAt(idx); + } + + return true; } int DeckListModel::findSortedInsertRow(InnerDecklistNode *parent, CardInfoPtr cardInfo) const @@ -559,6 +603,7 @@ void DeckListModel::setDeckList(DeckList *_deck) deckList = _deck; } rebuildTree(); + emit deckReplaced(); } void DeckListModel::forEachCard(const std::function &func) diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h index 80a519297..a85542d97 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h @@ -226,6 +226,18 @@ signals: */ void deckHashChanged(); + /** + * @brief Emitted whenever a card is added to the deck, regardless of whether it's an entirely new card or an + * existing card that got incremented. + * @param index The index of the card that got added. + */ + void cardAddedAt(const QModelIndex &index); + + /** + * @brief Emitted whenever the deck in the model has been replaced with a new one + */ + void deckReplaced(); + public: explicit DeckListModel(QObject *parent = nullptr); ~DeckListModel() override; @@ -250,7 +262,6 @@ public: [[nodiscard]] int rowCount(const QModelIndex &parent) const override; [[nodiscard]] int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override; [[nodiscard]] QVariant data(const QModelIndex &index, int role) const override; - void emitBackgroundUpdates(const QModelIndex &parent); [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role) const override; [[nodiscard]] QModelIndex index(int row, int column, const QModelIndex &parent) const override; [[nodiscard]] QModelIndex parent(const QModelIndex &index) const override; @@ -258,6 +269,12 @@ public: bool setData(const QModelIndex &index, const QVariant &value, int role) override; bool removeRows(int row, int count, const QModelIndex &parent) override; + /** + * Recursively emits the dataChanged signal for all child nodes. + * @param parent The parent node + */ + void emitBackgroundUpdates(const QModelIndex &parent); + /** * @brief Finds a card by name, zone, and optional identifiers. * @param cardName The card's name. @@ -289,6 +306,21 @@ public: */ QModelIndex addCard(const ExactCard &card, const QString &zoneName); + /** + * @brief Increments the `amount` field of the card node at the index by 1. + * @param idx The index of a card node. No-ops if the index is invalid or not a card node + * @return Whether the operation was successful + */ + bool incrementAmountAtIndex(const QModelIndex &idx); + + /** + * @brief Decrements the `amount` field of the card node at the index by 1. + * Removes the node if it causes the amount to fall to 0. + * @param idx The index of a card node. No-ops if the index is invalid or not a card node + * @return Whether the operation was successful + */ + bool decrementAmountAtIndex(const QModelIndex &idx); + /** * @brief Determines the sorted insertion row for a card. * @param parent The parent node where the card will be inserted. @@ -362,6 +394,9 @@ private: const QString &zoneName, const QString &providerId = "", const QString &cardNumber = "") const; + + bool offsetAmountAtIndex(const QModelIndex &idx, int offset); + void emitRecursiveUpdates(const QModelIndex &index); void sortHelper(InnerDecklistNode *node, Qt::SortOrder order); From 521046fb0941bb130e2b122d15a2282d8994c7cf Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Tue, 23 Dec 2025 17:48:10 +0100 Subject: [PATCH 27/43] Hashing tests (#5026) * add deck hashing tests * format * fix header * fix cmakelists * fix test * add 5 second timeout to test let the optimising begin * expand tests * remove debug message * manually format * I installed cmake format from the aur * use decklist library * format --- tests/CMakeLists.txt | 11 ++- tests/deck_hash_performance_test.cpp | 81 +++++++++++++++++++ .../clipboard_testing.cpp | 22 +++-- .../clipboard_testing.h | 2 +- .../loading_from_clipboard_test.cpp | 16 ++++ 5 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 tests/deck_hash_performance_test.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6a5eacf54..d80ccce4f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,16 +1,20 @@ enable_testing() + add_test(NAME dummy_test COMMAND dummy_test) add_test(NAME expression_test COMMAND expression_test) - add_test(NAME test_age_formatting COMMAND test_age_formatting) add_test(NAME password_hash_test COMMAND password_hash_test) +add_test(NAME deck_hash_performance_test COMMAND deck_hash_performance_test) +set_tests_properties(deck_hash_performance_test PROPERTIES TIMEOUT 5) + # Find GTest add_executable(dummy_test dummy_test.cpp) add_executable(expression_test expression_test.cpp) add_executable(test_age_formatting test_age_formatting.cpp) add_executable(password_hash_test password_hash_test.cpp) +add_executable(deck_hash_performance_test deck_hash_performance_test.cpp) find_package(GTest) @@ -41,6 +45,7 @@ if(NOT GTEST_FOUND) add_dependencies(expression_test gtest) add_dependencies(test_age_formatting gtest) add_dependencies(password_hash_test gtest) + add_dependencies(deck_hash_performance_test gtest) endif() include_directories(${GTEST_INCLUDE_DIRS}) @@ -50,6 +55,10 @@ target_link_libraries(test_age_formatting Threads::Threads ${GTEST_BOTH_LIBRARIE target_link_libraries( password_hash_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} ) +target_link_libraries( + deck_hash_performance_test libcockatrice_deck_list libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} + ${TEST_QT_MODULES} +) add_subdirectory(carddatabase) add_subdirectory(loading_from_clipboard) diff --git a/tests/deck_hash_performance_test.cpp b/tests/deck_hash_performance_test.cpp new file mode 100644 index 000000000..154283e8e --- /dev/null +++ b/tests/deck_hash_performance_test.cpp @@ -0,0 +1,81 @@ +#include "gtest/gtest.h" +#include +#include + +static constexpr int amount = 1e5; +QString repeatDeck; +QString numberDeck; +QString uniquesDeck; +QString uniquesXorDeck; +QString duplicatesDeck; + +TEST(DeckHashTest, RepeatTest) +{ + DeckList decklist(repeatDeck); + for (int i = 0; i < amount; ++i) { + decklist.getDeckHash(); + decklist.refreshDeckHash(); + } + auto hash = decklist.getDeckHash().toStdString(); + ASSERT_EQ(hash, "5cac19qm") << "The hash does not match!"; +} + +TEST(DeckHashTest, NumberTest) +{ + DeckList decklist(numberDeck); + auto hash = decklist.getDeckHash().toStdString(); + ASSERT_EQ(hash, "e0m38p19") << "The hash does not match!"; +} + +TEST(DeckHashTest, UniquesTest) +{ + DeckList decklist(uniquesDeck); + auto hash = decklist.getDeckHash().toStdString(); + ASSERT_EQ(hash, "88prk025") << "The hash does not match!"; +} + +TEST(DeckHashTest, UniquesTestXor) +{ + DeckList decklist(uniquesXorDeck); + auto hash = decklist.getDeckHash().toStdString(); + ASSERT_EQ(hash, "hkn6q4pf") << "The hash does not match!"; +} + +TEST(DeckHashTest, DuplicatesTest) +{ + DeckList decklist(duplicatesDeck); + auto hash = decklist.getDeckHash().toStdString(); + ASSERT_EQ(hash, "ekt6tg1h") << "The hash does not match!"; +} + +int main(int argc, char **argv) +{ + const QString deckStart = + R"()"; + const QString deckEnd = R"()"; + + repeatDeck = + deckStart + + R"()" + + deckEnd; + numberDeck = deckStart + QString(R"()").arg(amount) + deckEnd; + + QStringList deckString{deckStart}; + QStringList deckStringXor = deckString; + int len = QString::number(amount).length(); + for (int i = 0; i < amount; ++i) { + // creates already sorted list + deckString << R"()"; + // xor in order to mess with sorting + deckStringXor << R"()"; + } + deckString << deckEnd; + deckStringXor << deckEnd; + uniquesDeck = deckString.join(""); + uniquesXorDeck = deckStringXor.join(""); + + duplicatesDeck = deckStart + QString(R"()").repeated(amount) + deckEnd; + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/loading_from_clipboard/clipboard_testing.cpp b/tests/loading_from_clipboard/clipboard_testing.cpp index 2342bc088..29535c1dc 100644 --- a/tests/loading_from_clipboard/clipboard_testing.cpp +++ b/tests/loading_from_clipboard/clipboard_testing.cpp @@ -3,22 +3,32 @@ #include #include -void testEmpty(const QString &clipboard) +DeckList getDeckList(const QString &clipboard) { - QString cp(clipboard); DeckList deckList; + QString cp(clipboard); QTextStream stream(&cp); // text stream requires local copy deckList.loadFromStream_Plain(stream, false); + return deckList; +} + +void testEmpty(const QString &clipboard) +{ + DeckList deckList = getDeckList(clipboard); ASSERT_TRUE(deckList.getCardList().isEmpty()); } +void testHash(const QString &clipboard, const std::string &hash) +{ + DeckList deckList = getDeckList(clipboard); + + ASSERT_EQ(deckList.getDeckHash().toStdString(), hash); +} + void testDeck(const QString &clipboard, const Result &result) { - QString cp(clipboard); - DeckList deckList; - QTextStream stream(&cp); // text stream requires local copy - deckList.loadFromStream_Plain(stream, false); + DeckList deckList = getDeckList(clipboard); ASSERT_EQ(result.name, deckList.getName().toStdString()); ASSERT_EQ(result.comments, deckList.getComments().toStdString()); diff --git a/tests/loading_from_clipboard/clipboard_testing.h b/tests/loading_from_clipboard/clipboard_testing.h index 5e9cff915..41d8ea794 100644 --- a/tests/loading_from_clipboard/clipboard_testing.h +++ b/tests/loading_from_clipboard/clipboard_testing.h @@ -21,7 +21,7 @@ struct Result }; void testEmpty(const QString &clipboard); - +void testHash(const QString &clipboard, const std::string &hash); void testDeck(const QString &clipboard, const Result &result); #endif // CLIPBOARD_TESTING_H diff --git a/tests/loading_from_clipboard/loading_from_clipboard_test.cpp b/tests/loading_from_clipboard/loading_from_clipboard_test.cpp index 6f9762be9..fcfbb22db 100644 --- a/tests/loading_from_clipboard/loading_from_clipboard_test.cpp +++ b/tests/loading_from_clipboard/loading_from_clipboard_test.cpp @@ -203,6 +203,22 @@ TEST(LoadingFromClipboardTest, emptyMainBoard) testEmpty(clipboard); } +TEST(LoadingFromClipboardTest, emptyHash) +{ + QString clipboard(""); + + testHash(clipboard, "r8sq7riu"); +} + +TEST(LoadingFromClipboardTest, deckHash) +{ + QString clipboard("1 Mountain\n" + "2 Island\n" + "SB: 3 Forest\n"); + + testHash(clipboard, "5cac19qm"); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); From 70f9982c29d0951eb911cab43a3b13c6b4de8cf9 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 23 Dec 2025 09:58:23 -0800 Subject: [PATCH 28/43] Bump minimum Qt version from 5.8 to 5.15 (#6442) * Bump minimum Qt version from 5.8 to 5.15 * remove version check * remove version checks --- cmake/FindQtRuntime.cmake | 2 +- .../src/client/settings/shortcut_treeview.cpp | 7 +------ cockatrice/src/game/dialogs/dlg_create_token.cpp | 8 -------- .../src/game/player/player_event_handler.cpp | 7 ------- .../board/abstract_graphics_item.cpp | 7 +------ .../card_picture_loader_worker.cpp | 2 -- cockatrice/src/interface/logger.cpp | 15 --------------- .../widgets/server/chat_view/chat_view.cpp | 4 ---- .../widgets/server/chat_view/chat_view.h | 3 --- .../src/interface/widgets/server/games_model.cpp | 4 +--- ...api_response_deck_listings_display_widget.cpp | 3 --- .../widgets/tabs/api/archidekt/tab_archidekt.cpp | 3 --- ...er_api_response_bracket_navigation_widget.cpp | 4 ---- ...der_api_response_budget_navigation_widget.cpp | 4 ---- .../widgets/tabs/api/edhrec/tab_edhrec.cpp | 3 --- .../widgets/tabs/api/edhrec/tab_edhrec_main.cpp | 3 --- cockatrice/src/interface/window_main.cpp | 4 ---- cockatrice/src/main.cpp | 2 -- .../card/database/card_database_querier.cpp | 4 ---- .../models/database/card_set/card_sets_model.cpp | 4 ---- .../network/client/remote/remote_client.cpp | 4 ---- .../server/remote/game/server_cardzone.cpp | 4 ---- .../network/server/remote/game/server_game.cpp | 4 ---- .../network/server/remote/game/server_game.h | 4 ---- servatrice/src/isl_interface.cpp | 8 -------- servatrice/src/servatrice.cpp | 9 --------- servatrice/src/servatrice_database_interface.cpp | 4 ---- servatrice/src/server_logger.cpp | 4 ---- servatrice/src/serversocketinterface.cpp | 16 ---------------- servatrice/src/settingscache.cpp | 7 +------ servatrice/src/smtp/qxtsmtp.cpp | 4 ---- 31 files changed, 5 insertions(+), 156 deletions(-) diff --git a/cmake/FindQtRuntime.cmake b/cmake/FindQtRuntime.cmake index 6be08a694..c205ebdcf 100644 --- a/cmake/FindQtRuntime.cmake +++ b/cmake/FindQtRuntime.cmake @@ -59,7 +59,7 @@ if(Qt6_FOUND) endif() else() find_package( - Qt5 5.8.0 + Qt5 5.15.2 COMPONENTS ${REQUIRED_QT_COMPONENTS} QUIET HINTS ${Qt5_DIR} ) diff --git a/cockatrice/src/client/settings/shortcut_treeview.cpp b/cockatrice/src/client/settings/shortcut_treeview.cpp index 6b329b23d..b909d47ac 100644 --- a/cockatrice/src/client/settings/shortcut_treeview.cpp +++ b/cockatrice/src/client/settings/shortcut_treeview.cpp @@ -150,12 +150,7 @@ void ShortcutTreeView::currentChanged(const QModelIndex ¤t, const QModelIn */ void ShortcutTreeView::updateSearchString(const QString &searchString) { -#if QT_VERSION > QT_VERSION_CHECK(5, 14, 0) - const auto skipEmptyParts = Qt::SkipEmptyParts; -#else - const auto skipEmptyParts = QString::SkipEmptyParts; -#endif - QStringList searchWords = searchString.split(" ", skipEmptyParts); + QStringList searchWords = searchString.split(" ", Qt::SkipEmptyParts); auto escapeRegex = [](const QString &s) { return QRegularExpression::escape(s); }; std::transform(searchWords.begin(), searchWords.end(), searchWords.begin(), escapeRegex); diff --git a/cockatrice/src/game/dialogs/dlg_create_token.cpp b/cockatrice/src/game/dialogs/dlg_create_token.cpp index 1f6f9b08c..836c0c16a 100644 --- a/cockatrice/src/game/dialogs/dlg_create_token.cpp +++ b/cockatrice/src/game/dialogs/dlg_create_token.cpp @@ -117,11 +117,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa chooseTokenFromDeckRadioButton->setDisabled(true); // No tokens in deck = no need for option } else { chooseTokenFromDeckRadioButton->setChecked(true); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) cardDatabaseDisplayModel->setCardNameSet(QSet(predefinedTokens.begin(), predefinedTokens.end())); -#else - cardDatabaseDisplayModel->setCardNameSet(QSet::fromList(predefinedTokens)); -#endif } auto *tokenChooseLayout = new QVBoxLayout; @@ -223,11 +219,7 @@ void DlgCreateToken::actChooseTokenFromAll(bool checked) void DlgCreateToken::actChooseTokenFromDeck(bool checked) { if (checked) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) cardDatabaseDisplayModel->setCardNameSet(QSet(predefinedTokens.begin(), predefinedTokens.end())); -#else - cardDatabaseDisplayModel->setCardNameSet(QSet::fromList(predefinedTokens)); -#endif } } diff --git a/cockatrice/src/game/player/player_event_handler.cpp b/cockatrice/src/game/player/player_event_handler.cpp index 4cf814d86..331605918 100644 --- a/cockatrice/src/game/player/player_event_handler.cpp +++ b/cockatrice/src/game/player/player_event_handler.cpp @@ -78,14 +78,7 @@ void PlayerEventHandler::eventShuffle(const Event_Shuffle &event) void PlayerEventHandler::eventRollDie(const Event_RollDie &event) { if (!event.values().empty()) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QList rolls(event.values().begin(), event.values().end()); -#else - QList rolls; - for (const auto &value : event.values()) { - rolls.append(value); - } -#endif std::sort(rolls.begin(), rolls.end()); emit logRollDie(player, static_cast(event.sides()), rolls); } else if (event.value()) { diff --git a/cockatrice/src/game_graphics/board/abstract_graphics_item.cpp b/cockatrice/src/game_graphics/board/abstract_graphics_item.cpp index 8ced8d5bc..05f4a41ab 100644 --- a/cockatrice/src/game_graphics/board/abstract_graphics_item.cpp +++ b/cockatrice/src/game_graphics/board/abstract_graphics_item.cpp @@ -17,12 +17,7 @@ void AbstractGraphicsItem::paintNumberEllipse(int number, font.setWeight(QFont::Bold); QFontMetrics fm(font); - double w = 1.3 * -#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) - fm.horizontalAdvance(numStr); -#else - fm.width(numStr); -#endif + double w = 1.3 * fm.horizontalAdvance(numStr); double h = fm.height() * 1.3; if (w < h) w = h; diff --git a/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp b/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp index 77fddc9de..128b03c95 100644 --- a/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp +++ b/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp @@ -21,9 +21,7 @@ CardPictureLoaderWorker::CardPictureLoaderWorker() // We need a timeout to ensure requests don't hang indefinitely in case of // cache corruption, see related Qt bug: https://bugreports.qt.io/browse/QTBUG-111397 // Use Qt's default timeout (30s, as of 2023-02-22) -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) networkManager->setTransferTimeout(); -#endif cache = new QNetworkDiskCache(this); cache->setCacheDirectory(SettingsCache::instance().getNetworkCachePath()); cache->setMaximumCacheSize(1024L * 1024L * diff --git a/cockatrice/src/interface/logger.cpp b/cockatrice/src/interface/logger.cpp index 37e07acec..d6df065e8 100644 --- a/cockatrice/src/interface/logger.cpp +++ b/cockatrice/src/interface/logger.cpp @@ -57,17 +57,10 @@ void Logger::openLogfileSession() return; } fileStream.setDevice(&fileHandle); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) fileStream << "Log session started at " << QDateTime::currentDateTime().toString() << Qt::endl; fileStream << getClientVersion() << Qt::endl; fileStream << getSystemArchitecture() << Qt::endl; fileStream << getClientInstallInfo() << Qt::endl; -#else - fileStream << "Log session started at " << QDateTime::currentDateTime().toString() << endl; - fileStream << getClientVersion() << endl; - fileStream << getSystemArchitecture() << endl; - fileStream << getClientInstallInfo() << endl; -#endif logToFileEnabled = true; } @@ -77,11 +70,7 @@ void Logger::closeLogfileSession() return; logToFileEnabled = false; -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) fileStream << "Log session closed at " << QDateTime::currentDateTime().toString() << Qt::endl; -#else - fileStream << "Log session closed at " << QDateTime::currentDateTime().toString() << endl; -#endif fileHandle.close(); } @@ -103,11 +92,7 @@ void Logger::internalLog(const QString &message) std::cerr << message.toStdString() << std::endl; // Print to stdout if (logToFileEnabled) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) fileStream << message << Qt::endl; // Print to fileStream -#else - fileStream << message << endl; // Print to fileStream -#endif } } diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp index 694085771..50375c936 100644 --- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp +++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp @@ -236,11 +236,7 @@ void ChatView::appendMessage(QString message, cursor.setCharFormat(defaultFormat); bool mentionEnabled = SettingsCache::instance().getChatMention(); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) highlightedWords = SettingsCache::instance().getHighlightWords().split(' ', Qt::SkipEmptyParts); -#else - highlightedWords = SettingsCache::instance().getHighlightWords().split(' ', QString::SkipEmptyParts); -#endif // parse the message while (message.size()) { diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.h b/cockatrice/src/interface/widgets/server/chat_view/chat_view.h index 00fcc4f5e..6cf8370ed 100644 --- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.h +++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.h @@ -28,9 +28,6 @@ class UserListProxy; class UserMessagePosition { public: -#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0)) - UserMessagePosition() = default; // older qt versions require a default constructor to use in containers -#endif UserMessagePosition(QTextCursor &cursor); int relativePosition; QTextBlock block; diff --git a/cockatrice/src/interface/widgets/server/games_model.cpp b/cockatrice/src/interface/widgets/server/games_model.cpp index cdac71b28..05d363fee 100644 --- a/cockatrice/src/interface/widgets/server/games_model.cpp +++ b/cockatrice/src/interface/widgets/server/games_model.cpp @@ -421,10 +421,8 @@ bool GamesProxyModel::filterAcceptsRow(int sourceRow) const { #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) static const QDate epochDate = QDateTime::fromSecsSinceEpoch(0, QTimeZone::UTC).date(); -#elif (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) - static const QDate epochDate = QDateTime::fromSecsSinceEpoch(0, Qt::UTC).date(); #else - static const QDate epochDate = QDateTime::fromTime_t(0, Qt::UTC).date(); + static const QDate epochDate = QDateTime::fromSecsSinceEpoch(0, Qt::UTC).date(); #endif auto *model = qobject_cast(sourceModel()); if (!model) diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp index 5747ce90d..8746093c7 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp @@ -16,10 +16,7 @@ ArchidektApiResponseDeckListingsDisplayWidget::ArchidektApiResponseDeckListingsD flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded); imageNetworkManager = new QNetworkAccessManager(this); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) imageNetworkManager->setTransferTimeout(); // Use Qt's default timeout -#endif - imageNetworkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); // Add widgets for deck listings diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp index e6615fa7b..3769cd9a2 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp @@ -27,10 +27,7 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) { networkManager = new QNetworkAccessManager(this); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) networkManager->setTransferTimeout(); // Use Qt's default timeout -#endif - networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(processApiJson(QNetworkReply *))); diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp index 2bc727bd2..c3ab23e41 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp @@ -53,11 +53,7 @@ void EdhrecCommanderApiResponseBracketNavigationWidget::applyOptionsFromUrl(cons } // Expecting something like: "commanders/the-ur-dragon/core/expensive" -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts); -#else - QStringList parts = cleanedUrl.split('/', QString::SkipEmptyParts); -#endif if (parts.size() < 2) { return; diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp index 8470e6b3f..1845c020d 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp @@ -53,11 +53,7 @@ void EdhrecCommanderApiResponseBudgetNavigationWidget::applyOptionsFromUrl(const } // Expecting something like: "commanders/the-ur-dragon/core/expensive" -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts); -#else - QStringList parts = cleanedUrl.split('/', QString::SkipEmptyParts); -#endif if (parts.size() < 2) { return; diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp index 93dbd6bc5..10389f4eb 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp @@ -15,10 +15,7 @@ TabEdhRec::TabEdhRec(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) { networkManager = new QNetworkAccessManager(this); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) networkManager->setTransferTimeout(); // Use Qt's default timeout -#endif - networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); connect(networkManager, &QNetworkAccessManager::finished, this, &TabEdhRec::processApiJson); } diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp index fb26732f6..f861d8afd 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp @@ -37,10 +37,7 @@ static bool canBeCommander(const CardInfoPtr &cardInfo) TabEdhRecMain::TabEdhRecMain(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) { networkManager = new QNetworkAccessManager(this); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) networkManager->setTransferTimeout(); // Use Qt's default timeout -#endif - networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(processApiJson(QNetworkReply *))); diff --git a/cockatrice/src/interface/window_main.cpp b/cockatrice/src/interface/window_main.cpp index 4c9922c80..41113185c 100644 --- a/cockatrice/src/interface/window_main.cpp +++ b/cockatrice/src/interface/window_main.cpp @@ -499,11 +499,7 @@ QString MainWindow::extractInvalidUsernameMessage(QString &in) if (words.startsWith("\n")) { out += tr("no unacceptable language as specified by these server rules:", "note that the following lines will not be translated"); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) for (QString &line : words.split("\n", Qt::SkipEmptyParts)) { -#else - for (QString &line : words.split("\n", QString::SkipEmptyParts)) { -#endif out += "
  • " + line + "
  • "; } } else { diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 29dacee74..7092a3fd7 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -260,10 +260,8 @@ int main(int argc, char *argv[]) qCInfo(MainLog) << "MainWindow constructor finished"; ui.setWindowIcon(QPixmap("theme:cockatrice")); -#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) // set name of the app desktop file; used by wayland to load the window icon QGuiApplication::setDesktopFileName("cockatrice"); -#endif SettingsCache::instance().setClientID(generateClientID()); diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp b/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp index b2a675b99..26e515a2d 100644 --- a/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp +++ b/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp @@ -328,11 +328,7 @@ QMap CardDatabaseQuerier::getAllSubCardTypesWithCount() const QStringList parts = type.split(" — "); if (parts.size() > 1) { // Ensure there are subtypes -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList subtypes = parts[1].split(" ", Qt::SkipEmptyParts); -#else - QStringList subtypes = parts[1].split(" ", QString::SkipEmptyParts); -#endif for (const QString &subtype : subtypes) { typeCounts[subtype]++; diff --git a/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp b/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp index 2af815246..b678e8276 100644 --- a/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp +++ b/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp @@ -283,11 +283,7 @@ bool SetsDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex &source auto nameIndex = sourceModel()->index(sourceRow, SetsModel::LongNameCol, sourceParent); auto shortNameIndex = sourceModel()->index(sourceRow, SetsModel::ShortNameCol, sourceParent); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) const auto filter = filterRegularExpression(); -#else - const auto filter = filterRegExp(); -#endif return (sourceModel()->data(typeIndex).toString().contains(filter) || sourceModel()->data(nameIndex).toString().contains(filter) || diff --git a/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp b/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp index c0167a875..4b5d3f1b8 100644 --- a/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp +++ b/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp @@ -42,11 +42,7 @@ RemoteClient::RemoteClient(QObject *parent, INetworkSettingsProvider *_networkSe connect(socket, &QTcpSocket::connected, this, &RemoteClient::slotConnected); connect(socket, &QTcpSocket::readyRead, this, &RemoteClient::readData); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) connect(socket, &QTcpSocket::errorOccurred, this, &RemoteClient::slotSocketError); -#else - connect(socket, qOverload(&QTcpSocket::error), this, &RemoteClient::slotSocketError); -#endif websocket = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this); connect(websocket, &QWebSocket::binaryMessageReceived, this, &RemoteClient::websocketMessageReceived); diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_cardzone.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_cardzone.cpp index 68dcadd35..f2a35e548 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_cardzone.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_cardzone.cpp @@ -63,11 +63,7 @@ void Server_CardZone::shuffle(int start, int end) for (int i = end; i > start; i--) { int j = rng->rand(start, i); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) cards.swapItemsAt(j, i); -#else - cards.swap(j, i); -#endif } playersWithWritePermission.clear(); } diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp index e53838695..5cd6c8bf8 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp @@ -73,11 +73,7 @@ Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, spectatorsSeeEverything(_spectatorsSeeEverything), startingLifeTotal(_startingLifeTotal), shareDecklistsOnLoad(_shareDecklistsOnLoad), inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0), firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()), pingClock(nullptr), -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) gameMutex() -#else - gameMutex(QMutex::Recursive) -#endif { currentReplay = new GameReplay; currentReplay->set_replay_id(room->getServer()->getDatabaseInterface()->getNextReplayId()); diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h index 033542fad..64374019c 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h @@ -90,11 +90,7 @@ private slots: void doStartGameIfReady(bool forceStartGame = false); public: -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) mutable QRecursiveMutex gameMutex; -#else - mutable QMutex gameMutex; -#endif Server_Game(const ServerInfo_User &_creatorInfo, int _gameId, const QString &_description, diff --git a/servatrice/src/isl_interface.cpp b/servatrice/src/isl_interface.cpp index 2269ff314..bcf71a98a 100644 --- a/servatrice/src/isl_interface.cpp +++ b/servatrice/src/isl_interface.cpp @@ -112,11 +112,7 @@ void IslInterface::initServer() socket->startServerEncryption(); if (!socket->waitForEncrypted(5000)) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) QList sslErrors(socket->sslHandshakeErrors()); -#else - QList sslErrors(socket->sslErrors()); -#endif if (sslErrors.isEmpty()) qDebug() << "[ISL] SSL handshake timeout, terminating connection"; else @@ -193,11 +189,7 @@ void IslInterface::initClient() return; } if (!socket->waitForEncrypted(5000)) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) QList sslErrors(socket->sslHandshakeErrors()); -#else - QList sslErrors(socket->sslErrors()); -#endif if (sslErrors.isEmpty()) qDebug() << "[ISL] SSL handshake timeout, terminating connection"; else diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index ea3d783bd..410bf4ed9 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -254,13 +254,8 @@ bool Servatrice::initServer() qDebug() << "Accept registered users only:" << getRegOnlyServerEnabled(); qDebug() << "Registration enabled:" << getRegistrationEnabled(); if (getRegistrationEnabled()) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList emailBlackListFilters = getEmailBlackList().split(",", Qt::SkipEmptyParts); QStringList emailWhiteListFilters = getEmailWhiteList().split(",", Qt::SkipEmptyParts); -#else - QStringList emailBlackListFilters = getEmailBlackList().split(",", QString::SkipEmptyParts); - QStringList emailWhiteListFilters = getEmailWhiteList().split(",", QString::SkipEmptyParts); -#endif qDebug() << "Email blacklist:" << emailBlackListFilters; qDebug() << "Email whitelist:" << emailWhiteListFilters; qDebug() << "Require email address to register:" << getRequireEmailForRegistrationEnabled(); @@ -564,11 +559,7 @@ void Servatrice::setRequiredFeatures(const QString &featureList) FeatureSet features; serverRequiredFeatureList.clear(); features.initalizeFeatureList(serverRequiredFeatureList); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList listReqFeatures = featureList.split(",", Qt::SkipEmptyParts); -#else - QStringList listReqFeatures = featureList.split(",", QString::SkipEmptyParts); -#endif if (!listReqFeatures.isEmpty()) for (const QString &reqFeature : listReqFeatures) { features.enableRequiredFeature(serverRequiredFeatureList, reqFeature); diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index bad16bec3..fa9b14f31 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -163,11 +163,7 @@ bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString bool allowPunctuationPrefix = settingsCache->value("users/allowpunctuationprefix", false).toBool(); QString allowedPunctuation = settingsCache->value("users/allowedpunctuation", "_").toString(); QString disallowedWordsStr = settingsCache->value("users/disallowedwords", "").toString(); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList disallowedWords = disallowedWordsStr.split(",", Qt::SkipEmptyParts); -#else - QStringList disallowedWords = disallowedWordsStr.split(",", QString::SkipEmptyParts); -#endif disallowedWords.removeDuplicates(); QVariant displayDisallowedWords = settingsCache->value("users/displaydisallowedwords"); QString disallowedRegExpStr; diff --git a/servatrice/src/server_logger.cpp b/servatrice/src/server_logger.cpp index 9e40ac7b4..79d8cdfe0 100644 --- a/servatrice/src/server_logger.cpp +++ b/servatrice/src/server_logger.cpp @@ -57,11 +57,7 @@ void ServerLogger::logMessage(const QString &message, void *caller) // filter out all log entries based on values in configuration file bool shouldWeWriteLog = settingsCache->value("server/writelog", 1).toBool(); QString logFilters = settingsCache->value("server/logfilters").toString(); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList listlogFilters = logFilters.split(",", Qt::SkipEmptyParts); -#else - QStringList listlogFilters = logFilters.split(",", QString::SkipEmptyParts); -#endif bool shouldWeSkipLine = false; if (!shouldWeWriteLog) diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index bc686ad28..619d36d3a 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -972,11 +972,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdGetWarnList(const Comma Response_WarnList *re = new Response_WarnList; QString officialWarnings = settingsCache->value("server/officialwarnings").toString(); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList warningsList = officialWarnings.split(",", Qt::SkipEmptyParts); -#else - QStringList warningsList = officialWarnings.split(",", QString::SkipEmptyParts); -#endif for (const QString &warning : warningsList) { re->add_warning(warning.toStdString()); } @@ -1172,13 +1168,8 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C const auto parsedEmailParts = EmailParser::parseEmailAddress(nameFromStdString(cmd.email())); const auto emailUser = parsedEmailParts.first; const auto emailDomain = parsedEmailParts.second; -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) const QStringList emailBlackListFilters = emailBlackList.split(",", Qt::SkipEmptyParts); const QStringList emailWhiteListFilters = emailWhiteList.split(",", Qt::SkipEmptyParts); -#else - const QStringList emailBlackListFilters = emailBlackList.split(",", QString::SkipEmptyParts); - const QStringList emailWhiteListFilters = emailWhiteList.split(",", QString::SkipEmptyParts); -#endif bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); if (requireEmailForRegistration && emailUser.isEmpty()) { @@ -1930,13 +1921,8 @@ TcpServerSocketInterface::TcpServerSocketInterface(Servatrice *_server, socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); connect(socket, SIGNAL(readyRead()), this, SLOT(readClient())); connect(socket, SIGNAL(disconnected()), this, SLOT(catchSocketDisconnected())); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(catchSocketError(QAbstractSocket::SocketError))); -#else - connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, - SLOT(catchSocketError(QAbstractSocket::SocketError))); -#endif } TcpServerSocketInterface::~TcpServerSocketInterface() @@ -2109,10 +2095,8 @@ void WebsocketServerSocketInterface::initConnection(void *_socket) } socket = (QWebSocket *)_socket; socket->setParent(this); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) // https://bugreports.qt.io/browse/QTBUG-70693 socket->setMaxAllowedIncomingMessageSize(1500000); // 1.5MB -#endif address = socket->peerAddress(); diff --git a/servatrice/src/settingscache.cpp b/servatrice/src/settingscache.cpp index 14bfaebfb..f6dcd5fc8 100644 --- a/servatrice/src/settingscache.cpp +++ b/servatrice/src/settingscache.cpp @@ -11,12 +11,7 @@ SettingsCache::SettingsCache(const QString &fileName, QSettings::Format format, // first, figure out if we are running in portable mode isPortableBuild = QFile::exists(qApp->applicationDirPath() + "/portable.dat"); - QStringList disallowedRegExpStr = -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - value("users/disallowedregexp", "").toString().split(",", Qt::SkipEmptyParts); -#else - value("users/disallowedregexp", "").toString().split(",", QString::SkipEmptyParts); -#endif + QStringList disallowedRegExpStr = value("users/disallowedregexp", "").toString().split(",", Qt::SkipEmptyParts); disallowedRegExpStr.removeDuplicates(); for (const QString ®ExpStr : disallowedRegExpStr) { disallowedRegExp.append(QRegularExpression(QString("\\A%1\\z").arg(regExpStr))); diff --git a/servatrice/src/smtp/qxtsmtp.cpp b/servatrice/src/smtp/qxtsmtp.cpp index 951492d7a..6326b101d 100644 --- a/servatrice/src/smtp/qxtsmtp.cpp +++ b/servatrice/src/smtp/qxtsmtp.cpp @@ -334,11 +334,7 @@ void QxtSmtpPrivate::authenticate() state = Authenticated; emit qxt_p().authenticated(); } else { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList auth = extensions["AUTH"].toUpper().split(' ', Qt::SkipEmptyParts); -#else - QStringList auth = extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts); -#endif if (auth.contains("CRAM-MD5")) { authCramMD5(); } else if (auth.contains("PLAIN")) { From ca3f6bba0234ba098d49cd7cd91c0659bcd813dd Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Fri, 26 Dec 2025 04:29:35 -0800 Subject: [PATCH 29/43] [Refactor] Move prev/next card logic out of PrintingSelector (#6450) --- .../deck_editor_deck_dock_widget.cpp | 46 ++++++++++++++++ .../deck_editor_deck_dock_widget.h | 3 ++ ...k_editor_printing_selector_dock_widget.cpp | 4 ++ .../printing_selector/printing_selector.cpp | 52 ------------------- .../printing_selector/printing_selector.h | 13 +++-- ...rinting_selector_card_selection_widget.cpp | 4 +- 6 files changed, 65 insertions(+), 57 deletions(-) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 57d34be00..8ded2c50a 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -548,6 +548,52 @@ void DeckEditorDeckDockWidget::cleanDeck() deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags()); } +void DeckEditorDeckDockWidget::selectPrevCard() +{ + changeSelectedCard(-1); +} + +void DeckEditorDeckDockWidget::selectNextCard() +{ + changeSelectedCard(1); +} + +/** + * @brief Selects a card based on the change direction. + * + * @param changeBy The direction to change, -1 for previous, 1 for next. + */ +void DeckEditorDeckDockWidget::changeSelectedCard(int changeBy) +{ + if (changeBy == 0) { + return; + } + + // Get the current index of the selected item + auto deckViewCurrentIndex = deckView->currentIndex(); + + auto nextIndex = deckViewCurrentIndex.siblingAtRow(deckViewCurrentIndex.row() + changeBy); + if (!nextIndex.isValid()) { + nextIndex = deckViewCurrentIndex; + + // Increment to the next valid index, skipping header rows + AbstractDecklistNode *node; + do { + if (changeBy > 0) { + nextIndex = deckView->indexBelow(nextIndex); + } else { + nextIndex = deckView->indexAbove(nextIndex); + } + node = static_cast(nextIndex.internalPointer()); + } while (node && node->isDeckHeader()); + } + + if (nextIndex.isValid()) { + deckView->setCurrentIndex(nextIndex); + deckView->setFocus(Qt::FocusReason::MouseFocusReason); + } +} + /** * @brief Expands all parents of the given index. * @param sourceIndex The index to expand (model source index) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h index 30f752203..a50434f0b 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -56,6 +56,8 @@ public: public slots: void cleanDeck(); + void selectPrevCard(); + void selectNextCard(); void updateBannerCardComboBox(); void setDeck(const LoadedDeck &_deck); void syncDisplayWidgetsToModel(); @@ -122,6 +124,7 @@ private slots: void updateShowBannerCardComboBox(bool visible); void updateShowTagsWidget(bool visible); void syncBannerCardComboBoxSelectionWithDeck(); + void changeSelectedCard(int changeBy); void recursiveExpand(const QModelIndex &parent); void expandAll(); }; diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp index 0c068e043..03760c22d 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp @@ -33,6 +33,10 @@ void DeckEditorPrintingSelectorDockWidget::createPrintingSelectorDock() installEventFilter(deckEditor); connect(this, &QDockWidget::topLevelChanged, deckEditor, &AbstractTabDeckEditor::dockTopLevelChanged); + connect(printingSelector, &PrintingSelector::prevCardRequested, deckEditor->getDeckDockWidget(), + &DeckEditorDeckDockWidget::selectPrevCard); + connect(printingSelector, &PrintingSelector::nextCardRequested, deckEditor->getDeckDockWidget(), + &DeckEditorDeckDockWidget::selectNextCard); } void DeckEditorPrintingSelectorDockWidget::retranslateUi() diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp index f27101b93..f6851a9c7 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp @@ -138,58 +138,6 @@ void PrintingSelector::setCard(const CardInfoPtr &newCard, const QString &_curre flowWidget->repaint(); } -/** - * @brief Selects the previous card in the list. - */ -void PrintingSelector::selectPreviousCard() -{ - selectCard(-1); -} - -/** - * @brief Selects the next card in the list. - */ -void PrintingSelector::selectNextCard() -{ - selectCard(1); -} - -/** - * @brief Selects a card based on the change direction. - * - * @param changeBy The direction to change, -1 for previous, 1 for next. - */ -void PrintingSelector::selectCard(const int changeBy) -{ - if (changeBy == 0) { - return; - } - - // Get the current index of the selected item - auto deckViewCurrentIndex = deckView->currentIndex(); - - auto nextIndex = deckViewCurrentIndex.siblingAtRow(deckViewCurrentIndex.row() + changeBy); - if (!nextIndex.isValid()) { - nextIndex = deckViewCurrentIndex; - - // Increment to the next valid index, skipping header rows - AbstractDecklistNode *node; - do { - if (changeBy > 0) { - nextIndex = deckView->indexBelow(nextIndex); - } else { - nextIndex = deckView->indexAbove(nextIndex); - } - node = static_cast(nextIndex.internalPointer()); - } while (node && node->isDeckHeader()); - } - - if (nextIndex.isValid()) { - deckView->setCurrentIndex(nextIndex); - deckView->setFocus(Qt::FocusReason::MouseFocusReason); - } -} - /** * @brief Loads and displays all sets for the current selected card. */ diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector.h index fbe3b5b06..0de67619f 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector.h +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector.h @@ -48,13 +48,21 @@ public: public slots: void retranslateUi(); void updateDisplay(); - void selectPreviousCard(); - void selectNextCard(); void toggleVisibilityNavigationButtons(bool _state); private slots: void printingsInDeckChanged(); +signals: + /** + * Requests the previous card in the list + */ + void prevCardRequested(); + /** + * Requests the next card in the list + */ + void nextCardRequested(); + private: QVBoxLayout *layout; SettingsButtonWidget *displayOptionsWidget; @@ -73,7 +81,6 @@ private: QString currentZone; QTimer *widgetLoadingBufferTimer; int currentIndex = 0; - void selectCard(int changeBy); }; #endif // PRINTING_SELECTOR_H diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp index fc17cecd0..317ce83b6 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp @@ -42,8 +42,8 @@ PrintingSelectorCardSelectionWidget::PrintingSelectorCardSelectionWidget(Printin */ void PrintingSelectorCardSelectionWidget::connectSignals() { - connect(previousCardButton, &QPushButton::clicked, parent, &PrintingSelector::selectPreviousCard); - connect(nextCardButton, &QPushButton::clicked, parent, &PrintingSelector::selectNextCard); + connect(previousCardButton, &QPushButton::clicked, parent, &PrintingSelector::prevCardRequested); + connect(nextCardButton, &QPushButton::clicked, parent, &PrintingSelector::nextCardRequested); } void PrintingSelectorCardSelectionWidget::selectSetForCards() From 96c82a03771b1e74b686a7e8e8a896a08f86ecff Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Mon, 29 Dec 2025 03:03:44 -0800 Subject: [PATCH 30/43] [Refactor] Clean up some PrintingSelector widgets (#6451) * remove currentZone from PrintingSelector * don't store constructor args in fields if they're just passed * simplify some methods * refactor * clean up initializeFormats * more refactoring in CardAmountWidget --- .../deck_editor_deck_dock_widget.cpp | 7 +- .../dialogs/dlg_select_set_for_cards.cpp | 30 +++++--- .../all_zones_card_amount_widget.cpp | 3 +- .../all_zones_card_amount_widget.h | 4 - .../printing_selector/card_amount_widget.cpp | 76 ++++++++++--------- .../printing_selector/printing_selector.cpp | 8 +- .../printing_selector/printing_selector.h | 3 +- .../printing_selector_card_display_widget.cpp | 25 +++--- .../printing_selector_card_display_widget.h | 17 ++--- .../printing_selector_card_overlay_widget.cpp | 15 ++-- .../printing_selector_card_overlay_widget.h | 3 - ...e_and_collectors_number_display_widget.cpp | 3 +- .../widgets/tabs/abstract_tab_deck_editor.cpp | 2 +- .../widgets/tabs/tab_deck_editor.cpp | 3 +- .../tab_deck_editor_visual.cpp | 3 +- 15 files changed, 92 insertions(+), 110 deletions(-) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 8ded2c50a..409d43e64 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -284,16 +284,15 @@ void DeckEditorDeckDockWidget::createDeckDock() void DeckEditorDeckDockWidget::initializeFormats() { - QMap allFormats = CardDatabaseManager::query()->getAllFormatsWithCount(); + QStringList allFormats = CardDatabaseManager::query()->getAllFormatsWithCount().keys(); formatComboBox->clear(); // Remove "Loading Database..." formatComboBox->setEnabled(true); // Populate with formats formatComboBox->addItem("", ""); - for (auto it = allFormats.constBegin(); it != allFormats.constEnd(); ++it) { - QString displayText = QString("%1").arg(it.key()); - formatComboBox->addItem(displayText, it.key()); // store the raw key in itemData + for (auto formatName : allFormats) { + formatComboBox->addItem(formatName, formatName); // store the raw key in itemData } if (!deckModel->getDeckList()->getGameFormat().isEmpty()) { diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp index 656abbfdc..587407065 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp @@ -143,6 +143,22 @@ void DlgSelectSetForCards::retranslateUi() setAllToPreferredButton->setText(tr("Set all to preferred")); } +static bool swapPrinting(DeckListModel *model, const QString &modifiedSet, const QString &cardName) +{ + QModelIndex idx = model->findCard(cardName, DECK_ZONE_MAIN); + if (!idx.isValid()) { + return false; + } + int amount = model->data(idx.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT), Qt::DisplayRole).toInt(); + model->removeRow(idx.row(), idx.parent()); + CardInfoPtr cardInfo = CardDatabaseManager::query()->getCardInfo(cardName); + PrintingInfo printing = CardDatabaseManager::query()->getSpecificPrinting(cardName, modifiedSet, ""); + for (int i = 0; i < amount; i++) { + model->addCard(ExactCard(cardInfo, printing), DECK_ZONE_MAIN); + } + return true; +} + void DlgSelectSetForCards::actOK() { QMap modifiedSetsAndCardsMap = getModifiedCards(); @@ -155,20 +171,10 @@ void DlgSelectSetForCards::actOK() for (QString modifiedSet : modifiedSetsAndCardsMap.keys()) { for (QString card : modifiedSetsAndCardsMap.value(modifiedSet)) { - QModelIndex find_card = model->findCard(card, DECK_ZONE_MAIN); - if (!find_card.isValid()) { - continue; - } - int amount = - model->data(find_card.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT), Qt::DisplayRole).toInt(); - model->removeRow(find_card.row(), find_card.parent()); - CardInfoPtr cardInfo = CardDatabaseManager::query()->getCardInfo(card); - PrintingInfo printing = CardDatabaseManager::query()->getSpecificPrinting(card, modifiedSet, ""); - for (int i = 0; i < amount; i++) { - model->addCard(ExactCard(cardInfo, printing), DECK_ZONE_MAIN); - } + swapPrinting(model, modifiedSet, card); } } + if (!modifiedSetsAndCardsMap.isEmpty()) { emit deckModified(); } diff --git a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp index 5b67ae727..d8bd88b37 100644 --- a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp @@ -23,8 +23,7 @@ AllZonesCardAmountWidget::AllZonesCardAmountWidget(QWidget *parent, QTreeView *deckView, QSlider *cardSizeSlider, const ExactCard &rootCard) - : QWidget(parent), deckEditor(deckEditor), deckModel(deckModel), deckView(deckView), cardSizeSlider(cardSizeSlider), - rootCard(rootCard) + : QWidget(parent), cardSizeSlider(cardSizeSlider) { layout = new QVBoxLayout(this); layout->setAlignment(Qt::AlignHCenter); diff --git a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h index 1a8ec8bfd..6ce10cf2e 100644 --- a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h +++ b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h @@ -36,11 +36,7 @@ public slots: private: QVBoxLayout *layout; - AbstractTabDeckEditor *deckEditor; - DeckListModel *deckModel; - QTreeView *deckView; QSlider *cardSizeSlider; - ExactCard rootCard; QLabel *zoneLabelMainboard; CardAmountWidget *buttonBoxMainboard; QLabel *zoneLabelSideboard; diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp index 7c5804c37..7371976eb 100644 --- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp @@ -137,6 +137,31 @@ void CardAmountWidget::updateCardCount() layout->activate(); } +static QModelIndex addAndReplacePrintings(DeckListModel *model, + const QModelIndex &existing, + const ExactCard &rootCard, + const QString &zone, + int extraCopies, + bool replaceProviderless) +{ + auto newCardIndex = model->addCard(rootCard, zone); + if (!newCardIndex.isValid()) { + return {}; + } + + // Check if a card without a providerId already exists in the deckModel and replace it, if so. + if (existing.isValid() && existing != newCardIndex && replaceProviderless) { + for (int i = 0; i < extraCopies; i++) { + model->addCard(rootCard, zone); + } + model->removeRow(existing.row(), existing.parent()); + } + + // Set Index and Focus as if the user had just clicked the new card and modify the deckEditor saveState + return model->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(), + rootCard.getPrinting().getProperty("num")); +} + /** * @brief Adds a printing of the card to the specified zone (Mainboard or Sideboard). * @@ -144,26 +169,24 @@ void CardAmountWidget::updateCardCount() */ void CardAmountWidget::addPrinting(const QString &zone) { - int addedCount = 1; // Check if we will need to add extra copies due to replacing copies without providerIds QModelIndex existing = deckModel->findCard(rootCard.getName(), zone); + int extraCopies = 0; bool replacingProviderless = false; if (existing.isValid()) { - QString providerId = + QString foundProviderId = existing.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString(); - if (providerId.isEmpty()) { + if (foundProviderId.isEmpty()) { int amount = existing.data(Qt::DisplayRole).toInt(); extraCopies = amount - 1; // One less because we *always* add one replacingProviderless = true; } } - addedCount += extraCopies; - QString reason = QString("Added %1 copies of '%2 (%3) %4' to %5 [ProviderID: %6]%7") - .arg(addedCount) + .arg(1 + extraCopies) .arg(rootCard.getName()) .arg(rootCard.getPrinting().getSet()->getShortName()) .arg(rootCard.getPrinting().getProperty("num")) @@ -174,26 +197,13 @@ void CardAmountWidget::addPrinting(const QString &zone) emit deckModified(reason); // Add the card and expand the list UI - auto newCardIndex = deckModel->addCard(rootCard, zone); + auto newCardIndex = addAndReplacePrintings(deckModel, existing, rootCard, zone, extraCopies, replacingProviderless); - // Check if a card without a providerId already exists in the deckModel and replace it, if so. - QString foundProviderId = - existing.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString(); - if (existing.isValid() && existing != newCardIndex && foundProviderId == "") { - auto amount = existing.data(Qt::DisplayRole); - for (int i = 0; i < amount.toInt() - 1; i++) { - deckModel->addCard(rootCard, zone); - } - deckModel->removeRow(existing.row(), existing.parent()); + if (newCardIndex.isValid()) { + deckView->setCurrentIndex(newCardIndex); + deckView->setFocus(Qt::FocusReason::MouseFocusReason); + deckEditor->setModified(true); } - - // Set Index and Focus as if the user had just clicked the new card and modify the deckEditor saveState - newCardIndex = deckModel->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(), - rootCard.getPrinting().getProperty("num")); - - deckView->setCurrentIndex(newCardIndex); - deckView->setFocus(Qt::FocusReason::MouseFocusReason); - deckEditor->setModified(true); } /** @@ -259,22 +269,14 @@ void CardAmountWidget::decrementCardHelper(const QString &zone) */ int CardAmountWidget::countCardsInZone(const QString &deckZone) { - if (rootCard.getPrinting().getUuid().isEmpty()) { - return 0; // Cards without uuids/providerIds CANNOT match another card, they are undefined for us. - } + QString uuid = rootCard.getPrinting().getUuid(); - if (!deckModel) { - return -1; + if (uuid.isEmpty()) { + return 0; // Cards without uuids/providerIds CANNOT match another card, they are undefined for us. } QList cards = deckModel->getCardsForZone(deckZone); - int count = 0; - for (auto currentCard : cards) { - if (currentCard.getPrinting().getUuid() == rootCard.getPrinting().getProperty("uuid")) { - count++; - } - } - - return count; + return std::count_if(cards.cbegin(), cards.cend(), + [&uuid](const ExactCard &card) { return card.getPrinting().getUuid() == uuid; }); } \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp index f6851a9c7..e64d6a009 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp @@ -115,9 +115,8 @@ void PrintingSelector::updateDisplay() * @brief Sets the current card for the selector and updates the display. * * @param newCard The new card to set. - * @param _currentZone The current zone the card is in. */ -void PrintingSelector::setCard(const CardInfoPtr &newCard, const QString &_currentZone) +void PrintingSelector::setCard(const CardInfoPtr &newCard) { if (newCard.isNull()) { return; @@ -129,7 +128,6 @@ void PrintingSelector::setCard(const CardInfoPtr &newCard, const QString &_curre } selectedCard = newCard; - currentZone = _currentZone; if (isVisible()) { updateDisplay(); } @@ -166,8 +164,8 @@ void PrintingSelector::getAllSetsForCurrentCard() connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=, this]() mutable { for (int i = 0; i < BATCH_SIZE && currentIndex < printingsToUse.size(); ++i, ++currentIndex) { auto card = ExactCard(selectedCard, printingsToUse[currentIndex]); - auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget( - this, deckEditor, deckModel, deckView, cardSizeWidget->getSlider(), card, currentZone); + auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(this, deckEditor, deckModel, deckView, + cardSizeWidget->getSlider(), card); flowWidget->addWidget(cardDisplayWidget); cardDisplayWidget->clampSetNameToPicture(); connect(cardDisplayWidget, &PrintingSelectorCardDisplayWidget::cardPreferenceChanged, this, diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector.h index 0de67619f..e34ce3fe6 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector.h +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector.h @@ -33,7 +33,7 @@ class PrintingSelector : public QWidget public: PrintingSelector(QWidget *parent, AbstractTabDeckEditor *deckEditor); - void setCard(const CardInfoPtr &newCard, const QString &_currentZone); + void setCard(const CardInfoPtr &newCard); void getAllSetsForCurrentCard(); [[nodiscard]] DeckListModel *getDeckModel() const { @@ -78,7 +78,6 @@ private: DeckListModel *deckModel; QTreeView *deckView; CardInfoPtr selectedCard; - QString currentZone; QTimer *widgetLoadingBufferTimer; int currentIndex = 0; }; diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp index 24c45a7e3..86d6659a8 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp @@ -17,22 +17,19 @@ * display. * * @param parent The parent widget for this display. - * @param _deckEditor The TabDeckEditor instance for deck management. - * @param _deckModel The DeckListModel instance providing deck data. - * @param _deckView The QTreeView instance displaying the deck. - * @param _cardSizeSlider The slider controlling the size of the displayed card. - * @param _rootCard The root card object, representing the card to be displayed. - * @param _currentZone The current zone in which the card is located. + * @param deckEditor The TabDeckEditor instance for deck management. + * @param deckModel The DeckListModel instance providing deck data. + * @param deckView The QTreeView instance displaying the deck. + * @param cardSizeSlider The slider controlling the size of the displayed card. + * @param rootCard The root card object, representing the card to be displayed. */ PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *parent, - AbstractTabDeckEditor *_deckEditor, - DeckListModel *_deckModel, - QTreeView *_deckView, - QSlider *_cardSizeSlider, - const ExactCard &_rootCard, - QString &_currentZone) - : QWidget(parent), deckEditor(_deckEditor), deckModel(_deckModel), deckView(_deckView), - cardSizeSlider(_cardSizeSlider), rootCard(_rootCard), currentZone(_currentZone) + AbstractTabDeckEditor *deckEditor, + DeckListModel *deckModel, + QTreeView *deckView, + QSlider *cardSizeSlider, + const ExactCard &rootCard) + : QWidget(parent) { layout = new QVBoxLayout(this); setLayout(layout); diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h index 2f6bd6920..608c2df5c 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h @@ -20,12 +20,11 @@ class PrintingSelectorCardDisplayWidget : public QWidget public: PrintingSelectorCardDisplayWidget(QWidget *parent, - AbstractTabDeckEditor *_deckEditor, - DeckListModel *_deckModel, - QTreeView *_deckView, - QSlider *_cardSizeSlider, - const ExactCard &_rootCard, - QString &_currentZone); + AbstractTabDeckEditor *deckEditor, + DeckListModel *deckModel, + QTreeView *deckView, + QSlider *cardSizeSlider, + const ExactCard &rootCard); public slots: void clampSetNameToPicture(); @@ -36,12 +35,6 @@ signals: private: QVBoxLayout *layout; SetNameAndCollectorsNumberDisplayWidget *setNameAndCollectorsNumberDisplayWidget; - AbstractTabDeckEditor *deckEditor; - DeckListModel *deckModel; - QTreeView *deckView; - QSlider *cardSizeSlider; - ExactCard rootCard; - QString currentZone; PrintingSelectorCardOverlayWidget *overlayWidget; }; diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp index 5f3a44b30..04ab07a59 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp @@ -22,19 +22,18 @@ * * @param parent The parent widget for this overlay. * @param _deckEditor The TabDeckEditor instance for deck management. - * @param _deckModel The DeckListModel instance providing deck data. - * @param _deckView The QTreeView instance displaying the deck. - * @param _cardSizeSlider The slider controlling the size of the card. + * @param deckModel The DeckListModel instance providing deck data. + * @param deckView The QTreeView instance displaying the deck. + * @param cardSizeSlider The slider controlling the size of the card. * @param _rootCard The root card object that contains information about the card. */ PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *parent, AbstractTabDeckEditor *_deckEditor, - DeckListModel *_deckModel, - QTreeView *_deckView, - QSlider *_cardSizeSlider, + DeckListModel *deckModel, + QTreeView *deckView, + QSlider *cardSizeSlider, const ExactCard &_rootCard) - : QWidget(parent), deckEditor(_deckEditor), deckModel(_deckModel), deckView(_deckView), - cardSizeSlider(_cardSizeSlider), rootCard(_rootCard) + : QWidget(parent), deckEditor(_deckEditor), rootCard(_rootCard) { // Set up the main layout auto *mainLayout = new QVBoxLayout(this); diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h index 10df9d53e..3bd5ce247 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h @@ -48,9 +48,6 @@ private: AllZonesCardAmountWidget *allZonesCardAmountWidget; QLabel *pinBadge = nullptr; AbstractTabDeckEditor *deckEditor; - DeckListModel *deckModel; - QTreeView *deckView; - QSlider *cardSizeSlider; ExactCard rootCard; }; diff --git a/cockatrice/src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp index b1a6a61c1..5962680cd 100644 --- a/cockatrice/src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp @@ -13,7 +13,7 @@ SetNameAndCollectorsNumberDisplayWidget::SetNameAndCollectorsNumberDisplayWidget const QString &_setName, const QString &_collectorsNumber, QSlider *_cardSizeSlider) - : QWidget(parent) + : QWidget(parent), cardSizeSlider(_cardSizeSlider) { // Set up the layout for the widget layout = new QVBoxLayout(this); @@ -35,7 +35,6 @@ SetNameAndCollectorsNumberDisplayWidget::SetNameAndCollectorsNumberDisplayWidget collectorsNumber->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Store the card size slider and connect its signal to the font size adjustment slot - cardSizeSlider = _cardSizeSlider; connect(cardSizeSlider, &QSlider::valueChanged, this, &SetNameAndCollectorsNumberDisplayWidget::adjustFontSize); // Add labels to the layout diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp index 8eb1477f1..cb2002199 100644 --- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp +++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp @@ -101,7 +101,7 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta void AbstractTabDeckEditor::updateCard(const ExactCard &card) { cardInfoDockWidget->updateCard(card); - printingSelectorDockWidget->printingSelector->setCard(card.getCardPtr(), DECK_ZONE_MAIN); + printingSelectorDockWidget->printingSelector->setCard(card.getCardPtr()); } /** @brief Placeholder: called when the deck changes. */ diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp b/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp index 4fa12259c..4e3d0c57d 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp @@ -158,8 +158,7 @@ void TabDeckEditor::refreshShortcuts() */ void TabDeckEditor::showPrintingSelector() { - printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getCard().getCardPtr(), - DECK_ZONE_MAIN); + printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getCard().getCardPtr()); printingSelectorDockWidget->printingSelector->updateDisplay(); aPrintingSelectorDockVisible->setChecked(true); printingSelectorDockWidget->setVisible(true); diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp index 57615df94..a124eaa01 100644 --- a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp +++ b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp @@ -266,8 +266,7 @@ bool TabDeckEditorVisual::actSaveDeckAs() /** @brief Shows the printing selector dock and updates it with the current card. */ void TabDeckEditorVisual::showPrintingSelector() { - printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getCard().getCardPtr(), - DECK_ZONE_MAIN); + printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getCard().getCardPtr()); printingSelectorDockWidget->printingSelector->updateDisplay(); aPrintingSelectorDockVisible->setChecked(true); printingSelectorDockWidget->setVisible(true); From 296866a67525cc3e524c04420afea00729be8799 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Mon, 29 Dec 2025 08:19:03 -0800 Subject: [PATCH 31/43] [DeckListModel] Refactor api for offset count (#6454) --- .../deck_editor_deck_dock_widget.cpp | 7 ++----- .../printing_selector/card_amount_widget.cpp | 2 +- .../models/deck_list/deck_list_model.cpp | 12 +----------- .../models/deck_list/deck_list_model.h | 18 +++++------------- 4 files changed, 9 insertions(+), 30 deletions(-) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 409d43e64..543f1a1c0 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -831,11 +831,8 @@ void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, bool i emit requestDeckHistorySave(reason); - if (isIncrement) { - deckModel->incrementAmountAtIndex(sourceIndex); - } else { - deckModel->decrementAmountAtIndex(sourceIndex); - } + int offset = isIncrement ? 1 : -1; + deckModel->offsetCountAtIndex(sourceIndex, offset); emit deckModified(); } diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp index 7371976eb..623b797a7 100644 --- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp @@ -257,7 +257,7 @@ void CardAmountWidget::decrementCardHelper(const QString &zone) QModelIndex idx = deckModel->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(), rootCard.getPrinting().getProperty("num")); - deckModel->decrementAmountAtIndex(idx); + deckModel->offsetCountAtIndex(idx, -1); deckEditor->setModified(true); } diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index 53d08cd9d..130f05c8a 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -443,17 +443,7 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam return index; } -bool DeckListModel::incrementAmountAtIndex(const QModelIndex &idx) -{ - return offsetAmountAtIndex(idx, 1); -} - -bool DeckListModel::decrementAmountAtIndex(const QModelIndex &idx) -{ - return offsetAmountAtIndex(idx, -1); -} - -bool DeckListModel::offsetAmountAtIndex(const QModelIndex &idx, int offset) +bool DeckListModel::offsetCountAtIndex(const QModelIndex &idx, int offset) { if (!idx.isValid()) { return false; diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h index a85542d97..7bb504e60 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h @@ -307,19 +307,13 @@ public: QModelIndex addCard(const ExactCard &card, const QString &zoneName); /** - * @brief Increments the `amount` field of the card node at the index by 1. - * @param idx The index of a card node. No-ops if the index is invalid or not a card node + * @brief Changes the `amount` field in the card node at the index by the amount. + * Removes the node if it causes the amount to fall to 0 or below. + * @param idx The index of a card node. No-ops if the index is invalid or not a card node. + * @param offset The amount to change the amount field by. * @return Whether the operation was successful */ - bool incrementAmountAtIndex(const QModelIndex &idx); - - /** - * @brief Decrements the `amount` field of the card node at the index by 1. - * Removes the node if it causes the amount to fall to 0. - * @param idx The index of a card node. No-ops if the index is invalid or not a card node - * @return Whether the operation was successful - */ - bool decrementAmountAtIndex(const QModelIndex &idx); + bool offsetCountAtIndex(const QModelIndex &idx, int offset); /** * @brief Determines the sorted insertion row for a card. @@ -395,8 +389,6 @@ private: const QString &providerId = "", const QString &cardNumber = "") const; - bool offsetAmountAtIndex(const QModelIndex &idx, int offset); - void emitRecursiveUpdates(const QModelIndex &index); void sortHelper(InnerDecklistNode *node, Qt::SortOrder order); From 9d0bb0d51a0cbfb89a3d350561d9bfbae28a1b13 Mon Sep 17 00:00:00 2001 From: Bruno Alexandre Rosa <1791393+brunoalr@users.noreply.github.com> Date: Mon, 29 Dec 2025 13:21:59 -0300 Subject: [PATCH 32/43] fix: manage ccache caches manually on macos (#6449) * fix: manage ccache caches manually on macos * install ccache * fix issues shown by bugbot * readd cache size limit --- .github/workflows/desktop-build.yml | 32 ++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index b89190c8d..a26e82ee5 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -342,6 +342,11 @@ jobs: name: ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} needs: configure runs-on: ${{matrix.runner}} + env: + CCACHE_DIR: ${{github.workspace}}/.cache/ + # Cache size over the entire repo is 10Gi: + # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy + CCACHE_SIZE: 500M steps: - name: Checkout @@ -356,16 +361,20 @@ jobs: with: msbuild-architecture: x64 - # Using jianmingyong/ccache-action to setup ccache without using brew - # It tries to download a binary of ccache from GitHub Release and falls back to building from source if it fails - name: Setup ccache + if: matrix.use_ccache == 1 && matrix.os == 'macOS' + run: brew install ccache + + - name: Restore compiler cache (ccache) if: matrix.use_ccache == 1 - uses: jianmingyong/ccache-action@v1 + id: ccache_restore + uses: actions/cache/restore@v5 + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} with: - install-type: "binary" - ccache-key-prefix: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}} - max-size: 500M - gh-token: ${{ secrets.GITHUB_TOKEN }} + path: ${{env.CCACHE_DIR}} + key: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-${{env.BRANCH_NAME}} + restore-keys: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}- - name: Install Qt ${{matrix.qt_version}} uses: jurplel/install-qt-action@v4 @@ -403,6 +412,15 @@ jobs: TARGET_MACOS_VERSION: ${{ matrix.override_target }} run: .ci/compile.sh --server --test --vcpkg + - name: Save compiler cache (ccache) + if: github.ref == 'refs/heads/master' && matrix.use_ccache == 1 + uses: actions/cache/save@v5 + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + with: + path: ${{env.CCACHE_DIR}} + key: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-${{env.BRANCH_NAME}} + - name: Sign app bundle if: matrix.os == 'macOS' && matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null) env: From ce4a3bf11825ae01ce7225e6100d0533c0b8ff1f Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Tue, 30 Dec 2025 06:16:02 +0100 Subject: [PATCH 33/43] compile in debug mode on ubuntu 22.04 (#6418) * compile in debug mode on ubuntu 22.04 * Update card_info_display_widget.cpp Use c++ instead of c-style cast --------- Co-authored-by: BruebachL <44814898+BruebachL@users.noreply.github.com> --- .github/workflows/desktop-build.yml | 1 - .../src/interface/widgets/cards/card_info_display_widget.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index a26e82ee5..9f4ee4984 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -142,7 +142,6 @@ jobs: - distro: Ubuntu version: 22.04 package: DEB - test: skip # Running tests on all distros is superfluous - distro: Ubuntu version: 24.04 diff --git a/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp b/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp index 819bc57d0..f8b581e33 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp @@ -27,7 +27,7 @@ CardInfoDisplayWidget::CardInfoDisplayWidget(const CardRef &cardRef, QWidget *pa layout->addWidget(text, 0, Qt::AlignCenter); setLayout(layout); - setFrameStyle(QFrame::Panel | QFrame::Raised); + setFrameStyle(static_cast(QFrame::Panel) | QFrame::Raised); int pixmapHeight = QGuiApplication::primaryScreen()->geometry().height() / 3; int pixmapWidth = static_cast(pixmapHeight / aspectRatio); From cb2cf31cecf3588159bd8ef31d9f608579ad6632 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Mon, 29 Dec 2025 22:13:34 -0800 Subject: [PATCH 34/43] [DeckListModel] Clean up recursive updates (#6457) --- .../models/deck_list/deck_list_model.cpp | 33 +++++++++---------- .../models/deck_list/deck_list_model.h | 18 ++++++---- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index 130f05c8a..2ccb95690 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -178,22 +178,6 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const } } -void DeckListModel::emitBackgroundUpdates(const QModelIndex &parent) -{ - int rows = rowCount(parent); - if (rows == 0) - return; - - QModelIndex topLeft = index(0, 0, parent); - QModelIndex bottomRight = index(rows - 1, columnCount() - 1, parent); - emit dataChanged(topLeft, bottomRight, {Qt::BackgroundRole}); - - for (int r = 0; r < rows; ++r) { - QModelIndex child = index(r, 0, parent); - emitBackgroundUpdates(child); - } -} - QVariant DeckListModel::headerData(const int section, const Qt::Orientation orientation, const int role) const { if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal)) { @@ -252,6 +236,22 @@ Qt::ItemFlags DeckListModel::flags(const QModelIndex &index) const return result; } +void DeckListModel::emitBackgroundUpdates(const QModelIndex &parent) +{ + int rows = rowCount(parent); + if (rows == 0) + return; + + QModelIndex topLeft = index(0, 0, parent); + QModelIndex bottomRight = index(rows - 1, columnCount() - 1, parent); + emit dataChanged(topLeft, bottomRight, {Qt::BackgroundRole}); + + for (int r = 0; r < rows; ++r) { + QModelIndex child = index(r, 0, parent); + emitBackgroundUpdates(child); + } +} + void DeckListModel::emitRecursiveUpdates(const QModelIndex &index) { if (!index.isValid()) { @@ -294,7 +294,6 @@ bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, con deckList->refreshDeckHash(); emit deckHashChanged(); - emit dataChanged(index, index); return true; } diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h index 7bb504e60..724a4d9d8 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h @@ -269,12 +269,6 @@ public: bool setData(const QModelIndex &index, const QVariant &value, int role) override; bool removeRows(int row, int count, const QModelIndex &parent) override; - /** - * Recursively emits the dataChanged signal for all child nodes. - * @param parent The parent node - */ - void emitBackgroundUpdates(const QModelIndex &parent); - /** * @brief Finds a card by name, zone, and optional identifiers. * @param cardName The card's name. @@ -389,7 +383,19 @@ private: const QString &providerId = "", const QString &cardNumber = "") const; + /** + * @brief Recursively emits the dataChanged signal with role as Qt::BackgroundRole for all indices that are children + * of the given node. This is used to update the background color when changing formats. + * @param parent The parent node + */ + void emitBackgroundUpdates(const QModelIndex &parent); + + /** + * @brief Recursively emits the dataChanged signal for the given node and all parent nodes. + * @param index The parent node + */ void emitRecursiveUpdates(const QModelIndex &index); + void sortHelper(InnerDecklistNode *node, Qt::SortOrder order); template T getNode(const QModelIndex &index) const From daa7db7ce310e4a66b57221e636d9ab78c7c5cbd Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Mon, 29 Dec 2025 22:43:34 -0800 Subject: [PATCH 35/43] [DeckDockWidget] Fix tree unexpanding when changing group by (#6458) --- .../widgets/deck_editor/deck_editor_deck_dock_widget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 543f1a1c0..3ca171a15 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -157,7 +157,7 @@ void DeckEditorDeckDockWidget::createDeckDock() QTimer::singleShot(100, this, &DeckEditorDeckDockWidget::updateBannerCardComboBox); }); connect(deckModel, &DeckListModel::cardAddedAt, this, &DeckEditorDeckDockWidget::recursiveExpand); - connect(deckModel, &DeckListModel::deckReplaced, this, &DeckEditorDeckDockWidget::expandAll); + connect(deckModel, &DeckListModel::modelReset, this, &DeckEditorDeckDockWidget::expandAll); connect(bannerCardComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &DeckEditorDeckDockWidget::setBannerCard); From 968be8a06f6d2eb50f140992f7214428f16d2f9b Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Wed, 31 Dec 2025 03:00:23 -0800 Subject: [PATCH 36/43] Fix bug with next/prev buttons in PrintingSelector (#6453) * Hacky fix and debug messages * remove debug * add todo --- .../widgets/deck_editor/deck_editor_deck_dock_widget.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index 3ca171a15..b8ebc1419 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -571,6 +571,15 @@ void DeckEditorDeckDockWidget::changeSelectedCard(int changeBy) // Get the current index of the selected item auto deckViewCurrentIndex = deckView->currentIndex(); + // For some reason, if the deckModel is modified but the view is not manually reselected, + // currentIndex will return an index for the underlying deckModel instead of the proxy. + // That index will return an invalid index when indexBelow/indexAbove crosses a header node, + // causing the selection to fail to move down. + /// \todo Figure out why it's happening so we can do a proper fix instead of a hacky workaround + if (deckViewCurrentIndex.model() == proxy->sourceModel()) { + deckViewCurrentIndex = proxy->mapFromSource(deckViewCurrentIndex); + } + auto nextIndex = deckViewCurrentIndex.siblingAtRow(deckViewCurrentIndex.row() + changeBy); if (!nextIndex.isValid()) { nextIndex = deckViewCurrentIndex; From d722b2569c157735632a9df9425b9a067839b630 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Wed, 31 Dec 2025 03:01:49 -0800 Subject: [PATCH 37/43] [DeckListModel] Refactor: general code cleanup (#6460) * change one usage * move method * move format check code * make group criteria method static * move method * make method private * more comments --- .../printing_selector/card_amount_widget.cpp | 4 +- .../libcockatrice/card/card_info.cpp | 10 ++++ .../libcockatrice/card/card_info.h | 9 ++++ .../models/deck_list/deck_list_model.cpp | 53 +++++++++---------- .../models/deck_list/deck_list_model.h | 49 +++++++---------- 5 files changed, 66 insertions(+), 59 deletions(-) diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp index 623b797a7..d01725cc4 100644 --- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp @@ -151,9 +151,7 @@ static QModelIndex addAndReplacePrintings(DeckListModel *model, // Check if a card without a providerId already exists in the deckModel and replace it, if so. if (existing.isValid() && existing != newCardIndex && replaceProviderless) { - for (int i = 0; i < extraCopies; i++) { - model->addCard(rootCard, zone); - } + model->offsetCountAtIndex(newCardIndex, extraCopies); model->removeRow(existing.row(), existing.parent()); } diff --git a/libcockatrice_card/libcockatrice/card/card_info.cpp b/libcockatrice_card/libcockatrice/card/card_info.cpp index 3054a10cf..acfaea8c8 100644 --- a/libcockatrice_card/libcockatrice/card/card_info.cpp +++ b/libcockatrice_card/libcockatrice/card/card_info.cpp @@ -75,6 +75,16 @@ QString CardInfo::getCorrectedName() const return result.remove(rmrx).replace(spacerx, space); } +bool CardInfo::isLegalInFormat(const QString &format) const +{ + if (format.isEmpty()) { + return true; + } + + QString formatLegality = getProperty("format-" + format); + return formatLegality == "legal" || formatLegality == "restricted"; +} + void CardInfo::addToSet(const CardSetPtr &_set, const PrintingInfo _info) { if (!_set->contains(smartThis)) { diff --git a/libcockatrice_card/libcockatrice/card/card_info.h b/libcockatrice_card/libcockatrice/card/card_info.h index 00e8fec37..13b2b8a49 100644 --- a/libcockatrice_card/libcockatrice/card/card_info.h +++ b/libcockatrice_card/libcockatrice/card/card_info.h @@ -291,6 +291,15 @@ public: */ [[nodiscard]] QString getCorrectedName() const; + /** + * @brief Checks if the card is legal in the given format. + * A card is considered legal in a format if its properties map contains an entry for "format-", with value + * "legal" or "restricted". + * @param format The format's name. If empty, will always return true. + * @return Whether the card is legal in the given format. + */ + [[nodiscard]] bool isLegalInFormat(const QString &format) const; + /** * @brief Adds a printing to a specific set. * diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index 2ccb95690..6f479616e 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -18,13 +18,19 @@ DeckListModel::~DeckListModel() delete root; } -QString DeckListModel::getGroupCriteriaForCard(CardInfoPtr info) const +/** + * @brief Extract the value from the card that is used for the group criteria. + * @param info Pointer to card information. + * @param criteria The group criteria + * @return String representing the value of the criteria. + */ +static QString extractGroupCriteriaValue(const CardInfoPtr &info, DeckListModelGroupCriteria::Type criteria) { if (!info) { return "unknown"; } - switch (activeGroupCriteria) { + switch (criteria) { case DeckListModelGroupCriteria::MAIN_TYPE: return info->getMainCardType(); case DeckListModelGroupCriteria::MANA_COST: @@ -56,7 +62,7 @@ void DeckListModel::rebuildTree() } CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(currentCard->getName()); - QString groupCriteria = getGroupCriteriaForCard(info); + QString groupCriteria = extractGroupCriteriaValue(info, activeGroupCriteria); auto *groupNode = dynamic_cast(node->findChild(groupCriteria)); @@ -353,7 +359,7 @@ DecklistModelCardNode *DeckListModel::findCardNode(const QString &cardName, return nullptr; } - QString groupCriteria = getGroupCriteriaForCard(info); + QString groupCriteria = extractGroupCriteriaValue(info, activeGroupCriteria); InnerDecklistNode *groupNode = dynamic_cast(zoneNode->findChild(groupCriteria)); if (!groupNode) { return nullptr; @@ -406,7 +412,7 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam CardInfoPtr cardInfo = card.getCardPtr(); PrintingInfo printingInfo = card.getPrinting(); - QString groupCriteria = getGroupCriteriaForCard(cardInfo); + QString groupCriteria = extractGroupCriteriaValue(cardInfo, activeGroupCriteria); InnerDecklistNode *groupNode = createNodeIfNeeded(groupCriteria, zoneNode); const QModelIndex parentIndex = nodeToIndex(groupNode); @@ -420,7 +426,7 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam auto *decklistCard = deckList->addCard(cardInfo->getName(), zoneName, insertRow, cardSetName, printingInfo.getProperty("num"), - printingInfo.getProperty("uuid"), isCardLegalForCurrentFormat(cardInfo)); + printingInfo.getProperty("uuid"), cardInfo->isLegalInFormat(deckList->getGameFormat())); beginInsertRows(parentIndex, insertRow, insertRow); cardNode = new DecklistModelCardNode(decklistCard, groupNode, insertRow); @@ -472,7 +478,7 @@ bool DeckListModel::offsetCountAtIndex(const QModelIndex &idx, int offset) return true; } -int DeckListModel::findSortedInsertRow(InnerDecklistNode *parent, CardInfoPtr cardInfo) const +int DeckListModel::findSortedInsertRow(const InnerDecklistNode *parent, const CardInfoPtr &cardInfo) const { if (!cardInfo) { return parent->size(); // fallback: append at end @@ -661,18 +667,6 @@ QList DeckListModel::getZones() const return zones; } -bool DeckListModel::isCardLegalForCurrentFormat(const CardInfoPtr cardInfo) -{ - if (!deckList->getGameFormat().isEmpty()) { - if (cardInfo->getProperties().contains("format-" + deckList->getGameFormat())) { - QString formatLegality = cardInfo->getProperty("format-" + deckList->getGameFormat()); - return formatLegality == "legal" || formatLegality == "restricted"; - } - return false; - } - return true; -} - static int maxAllowedForLegality(const FormatRules &format, const QString &legality) { for (const AllowedCount &c : format.allowedCounts) { @@ -683,25 +677,29 @@ static int maxAllowedForLegality(const FormatRules &format, const QString &legal return -1; // unknown legality → treat as illegal } -bool DeckListModel::isCardQuantityLegalForCurrentFormat(const CardInfoPtr cardInfo, int quantity) +static bool isCardQuantityLegalForFormat(const QString &format, const CardInfo &cardInfo, int quantity) { - auto formatRules = CardDatabaseManager::query()->getFormat(deckList->getGameFormat()); + if (format.isEmpty()) { + return true; + } + + auto formatRules = CardDatabaseManager::query()->getFormat(format); if (!formatRules) { return true; } // Exceptions always win - if (cardHasAnyException(*cardInfo, *formatRules)) { + if (cardHasAnyException(cardInfo, *formatRules)) { return true; } - const QString legalityProp = "format-" + deckList->getGameFormat(); - if (!cardInfo->getProperties().contains(legalityProp)) { + const QString legalityProp = "format-" + format; + if (!cardInfo.getProperties().contains(legalityProp)) { return false; } - const QString legality = cardInfo->getProperty(legalityProp); + const QString legality = cardInfo.getProperty(legalityProp); int maxAllowed = maxAllowedForLegality(*formatRules, legality); @@ -735,10 +733,11 @@ void DeckListModel::refreshCardFormatLegalities() continue; } - bool legal = isCardLegalForCurrentFormat(exactCard.getCardPtr()); + QString format = deckList->getGameFormat(); + bool legal = exactCard.getInfo().isLegalInFormat(format); if (legal) { - legal = isCardQuantityLegalForCurrentFormat(exactCard.getCardPtr(), currentCard->getNumber()); + legal = isCardQuantityLegalForFormat(format, exactCard.getInfo(), currentCard->getNumber()); } currentCard->setFormatLegality(legal); diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h index 724a4d9d8..b6292d689 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h @@ -217,7 +217,12 @@ public slots: */ void rebuildTree(); -public slots: + /** + * @brief Sets the criteria used to group cards in the model. + * @param newCriteria The new grouping criteria. + */ + void setActiveGroupCriteria(DeckListModelGroupCriteria::Type newCriteria); + void setActiveFormat(const QString &_format); signals: @@ -251,14 +256,8 @@ public: return nodeToIndex(root); } - /** - * @brief Returns the value of the grouping category for a card based on the current criteria. - * @param info Pointer to card information. - * @return String representing the value of the current grouping criteria for the card. - */ - [[nodiscard]] QString getGroupCriteriaForCard(CardInfoPtr info) const; - - // Qt model overrides + /// @name Qt model overrides + ///@{ [[nodiscard]] int rowCount(const QModelIndex &parent) const override; [[nodiscard]] int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override; [[nodiscard]] QVariant data(const QModelIndex &index, int role) const override; @@ -268,6 +267,8 @@ public: [[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; bool removeRows(int row, int count, const QModelIndex &parent) override; + void sort(int column, Qt::SortOrder order) override; + ///@} /** * @brief Finds a card by name, zone, and optional identifiers. @@ -309,16 +310,6 @@ public: */ bool offsetCountAtIndex(const QModelIndex &idx, int offset); - /** - * @brief Determines the sorted insertion row for a card. - * @param parent The parent node where the card will be inserted. - * @param cardInfo The card info to insert. - * @return Row index where the card should be inserted to maintain sort order. - */ - int findSortedInsertRow(InnerDecklistNode *parent, CardInfoPtr cardInfo) const; - - void sort(int column, Qt::SortOrder order) override; - /** * @brief Removes all cards and resets the model. */ @@ -359,16 +350,6 @@ public: */ [[nodiscard]] QList getZones() const; - bool isCardLegalForCurrentFormat(CardInfoPtr cardInfo); - bool isCardQuantityLegalForCurrentFormat(CardInfoPtr cardInfo, int quantity); - void refreshCardFormatLegalities(); - - /** - * @brief Sets the criteria used to group cards in the model. - * @param newCriteria The new grouping criteria. - */ - void setActiveGroupCriteria(DeckListModelGroupCriteria::Type newCriteria); - private: DeckList *deckList; /**< Pointer to the deck loader providing the underlying data. */ InnerDecklistNode *root; /**< Root node of the model tree. */ @@ -383,6 +364,14 @@ private: const QString &providerId = "", const QString &cardNumber = "") const; + /** + * @brief Determines the sorted insertion row for a card. + * @param parent The parent node where the card will be inserted. + * @param cardInfo The card info to insert. + * @return Row index where the card should be inserted to maintain sort order. + */ + int findSortedInsertRow(const InnerDecklistNode *parent, const CardInfoPtr &cardInfo) const; + /** * @brief Recursively emits the dataChanged signal with role as Qt::BackgroundRole for all indices that are children * of the given node. This is used to update the background color when changing formats. @@ -404,6 +393,8 @@ private: return dynamic_cast(root); return dynamic_cast(static_cast(index.internalPointer())); } + + void refreshCardFormatLegalities(); }; #endif From db3bdb586b63bc1ef79f6f6d41b54b31fac14031 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Wed, 31 Dec 2025 04:13:32 -0800 Subject: [PATCH 38/43] [CardInfo] clean up signatures (#6462) --- .../libcockatrice/card/card_info.cpp | 18 +++++++++--------- .../libcockatrice/card/card_info.h | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/libcockatrice_card/libcockatrice/card/card_info.cpp b/libcockatrice_card/libcockatrice/card/card_info.cpp index acfaea8c8..ae73ef2db 100644 --- a/libcockatrice_card/libcockatrice/card/card_info.cpp +++ b/libcockatrice_card/libcockatrice/card/card_info.cpp @@ -85,7 +85,7 @@ bool CardInfo::isLegalInFormat(const QString &format) const return formatLegality == "legal" || formatLegality == "restricted"; } -void CardInfo::addToSet(const CardSetPtr &_set, const PrintingInfo _info) +void CardInfo::addToSet(const CardSetPtr &_set, const PrintingInfo &_info) { if (!_set->contains(smartThis)) { _set->append(smartThis); @@ -166,7 +166,7 @@ QString CardInfo::simplifyName(const QString &name) return simpleName; } -const QChar CardInfo::getColorChar() const +QChar CardInfo::getColorChar() const { QString colors = getColors(); switch (colors.size()) { @@ -188,7 +188,7 @@ void CardInfo::resetReverseRelatedCards2Me() } // Back-compatibility methods. Remove ASAP -const QString CardInfo::getCardType() const +QString CardInfo::getCardType() const { return getProperty(Mtg::CardType); } @@ -196,11 +196,11 @@ void CardInfo::setCardType(const QString &value) { setProperty(Mtg::CardType, value); } -const QString CardInfo::getCmc() const +QString CardInfo::getCmc() const { return getProperty(Mtg::ConvertedManaCost); } -const QString CardInfo::getColors() const +QString CardInfo::getColors() const { return getProperty(Mtg::Colors); } @@ -208,19 +208,19 @@ void CardInfo::setColors(const QString &value) { setProperty(Mtg::Colors, value); } -const QString CardInfo::getLoyalty() const +QString CardInfo::getLoyalty() const { return getProperty(Mtg::Loyalty); } -const QString CardInfo::getMainCardType() const +QString CardInfo::getMainCardType() const { return getProperty(Mtg::MainCardType); } -const QString CardInfo::getManaCost() const +QString CardInfo::getManaCost() const { return getProperty(Mtg::ManaCost); } -const QString CardInfo::getPowTough() const +QString CardInfo::getPowTough() const { return getProperty(Mtg::PowTough); } diff --git a/libcockatrice_card/libcockatrice/card/card_info.h b/libcockatrice_card/libcockatrice/card/card_info.h index 13b2b8a49..b81cf51dc 100644 --- a/libcockatrice_card/libcockatrice/card/card_info.h +++ b/libcockatrice_card/libcockatrice/card/card_info.h @@ -267,18 +267,18 @@ public: } //@} - [[nodiscard]] const QChar getColorChar() const; + [[nodiscard]] QChar getColorChar() const; /** @name Legacy/Convenience Property Accessors */ //@{ - [[nodiscard]] const QString getCardType() const; + [[nodiscard]] QString getCardType() const; void setCardType(const QString &value); - [[nodiscard]] const QString getCmc() const; - [[nodiscard]] const QString getColors() const; + [[nodiscard]] QString getCmc() const; + [[nodiscard]] QString getColors() const; void setColors(const QString &value); - [[nodiscard]] const QString getLoyalty() const; - [[nodiscard]] const QString getMainCardType() const; - [[nodiscard]] const QString getManaCost() const; - [[nodiscard]] const QString getPowTough() const; + [[nodiscard]] QString getLoyalty() const; + [[nodiscard]] QString getMainCardType() const; + [[nodiscard]] QString getManaCost() const; + [[nodiscard]] QString getPowTough() const; void setPowTough(const QString &value); //@} @@ -308,7 +308,7 @@ public: * @param _set The set to which the card should be added. * @param _info Optional printing information. */ - void addToSet(const CardSetPtr &_set, PrintingInfo _info = PrintingInfo()); + void addToSet(const CardSetPtr &_set, const PrintingInfo &_info = PrintingInfo()); /** * @brief Combines legality properties from a provided map. From 0085015ebe59d3b0942ebf11f6dd32b64529ab28 Mon Sep 17 00:00:00 2001 From: Alex Okonechnikov <36140593+okonech@users.noreply.github.com> Date: Wed, 31 Dec 2025 11:48:30 -0500 Subject: [PATCH 39/43] manual drag override (#6461) * manual drag override * fix styles * pr comments * better close button rect calc --- .../src/game/zones/view_zone_widget.cpp | 194 ++++++++++++++++-- cockatrice/src/game/zones/view_zone_widget.h | 36 +++- 2 files changed, 210 insertions(+), 20 deletions(-) diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index 91abb4663..e15a466c3 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -13,12 +13,20 @@ #include #include #include +#include #include #include #include +#include #include #include +namespace +{ +constexpr qreal kTitleBarHeight = 24.0; +constexpr qreal kMinVisibleWidth = 100.0; +} // namespace + /** * @param _player the player the cards were revealed to. * @param _origZone the zone the cards were revealed from. @@ -241,33 +249,182 @@ void ZoneViewWidget::retranslateUi() pileViewCheckBox.setText(tr("pile view")); } -void ZoneViewWidget::moveEvent(QGraphicsSceneMoveEvent * /* event */) +void ZoneViewWidget::stopWindowDrag() { - if (!scene()) + if (!draggingWindow) return; - int titleBarHeight = 24; + draggingWindow = false; + ungrabMouse(); +} - QPointF scenePos = pos(); +void ZoneViewWidget::startWindowDrag(QGraphicsSceneMouseEvent *event) +{ + draggingWindow = true; + dragStartItemPos = pos(); + dragStartScreenPos = event->screenPos(); + dragView = findDragView(event->widget()); - if (scenePos.x() < 0) { - scenePos.setX(0); - } else { - qreal maxw = scene()->sceneRect().width() - 100; - if (scenePos.x() > maxw) - scenePos.setX(maxw); + // need to grab mouse to receive events and not miss initial movement + grabMouse(); +} + +QRectF ZoneViewWidget::closeButtonRect(QWidget *styleWidget) const +{ + const QRectF frameRectF = windowFrameRect(); + const QRect titleBarRect(frameRectF.toRect().x(), frameRectF.toRect().y(), frameRectF.toRect().width(), + static_cast(kTitleBarHeight)); + + // query the style for the close button position (handles macOS top-left placement) + if (styleWidget) { + QStyleOptionTitleBar opt; + opt.initFrom(styleWidget); + opt.rect = titleBarRect; + opt.text = windowTitle(); + opt.icon = styleWidget->windowIcon(); + opt.titleBarFlags = Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; + opt.subControls = QStyle::SC_TitleBarCloseButton; + opt.activeSubControls = QStyle::SC_TitleBarCloseButton; + opt.titleBarState = styleWidget->isActiveWindow() ? Qt::WindowActive : Qt::WindowNoState; + if (styleWidget->isActiveWindow()) + opt.state |= QStyle::State_Active; + const QRect r = styleWidget->style()->subControlRect(QStyle::CC_TitleBar, &opt, QStyle::SC_TitleBarCloseButton, + styleWidget); + if (r.isValid() && !r.isEmpty()) { + return QRectF(r); + } } - if (scenePos.y() < titleBarHeight) { - scenePos.setY(titleBarHeight); - } else { - qreal maxh = scene()->sceneRect().height() - titleBarHeight; - if (scenePos.y() > maxh) - scenePos.setY(maxh); + // fallback: square at right end of titlebar (Windows/Linux style) + return QRectF(frameRectF.right() - kTitleBarHeight, frameRectF.top(), kTitleBarHeight, kTitleBarHeight); +} + +QGraphicsView *ZoneViewWidget::findDragView(QWidget *eventWidget) const +{ + QWidget *current = eventWidget; + while (current) { + if (auto *view = qobject_cast(current)) + return view; + current = current->parentWidget(); } - if (scenePos != pos()) - setPos(scenePos); + if (scene() && !scene()->views().isEmpty()) + return scene()->views().constFirst(); + + return nullptr; +} + +QPointF ZoneViewWidget::calcDraggedWindowPos(const QPoint &screenPos, + const QPointF &scenePos, + const QPointF &buttonDownScenePos) const +{ + if (dragView && dragView->viewport()) { + const QPoint vpStart = dragView->viewport()->mapFromGlobal(dragStartScreenPos); + const QPoint vpNow = dragView->viewport()->mapFromGlobal(screenPos); + const QPointF sceneStart = dragView->mapToScene(vpStart); + const QPointF sceneNow = dragView->mapToScene(vpNow); + return dragStartItemPos + (sceneNow - sceneStart); + } + + return dragStartItemPos + (scenePos - buttonDownScenePos); +} + +bool ZoneViewWidget::windowFrameEvent(QEvent *event) +{ + if (event->type() == QEvent::UngrabMouse) { + stopWindowDrag(); + return QGraphicsWidget::windowFrameEvent(event); + } + + auto *me = dynamic_cast(event); + if (!me) + return QGraphicsWidget::windowFrameEvent(event); + + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + if (me->button() == Qt::LeftButton && windowFrameSectionAt(me->pos()) == Qt::TitleBarArea) { + // avoid drag on close button + if (closeButtonRect(me->widget()).contains(me->pos())) { + me->accept(); + close(); + return true; + } + startWindowDrag(me); + me->accept(); + return true; + } + break; + + case QEvent::GraphicsSceneMouseMove: + if (draggingWindow) { + if (!(me->buttons() & Qt::LeftButton)) { + stopWindowDrag(); + } else { + setPos( + calcDraggedWindowPos(me->screenPos(), me->scenePos(), me->buttonDownScenePos(Qt::LeftButton))); + } + me->accept(); + return true; + } + break; + + case QEvent::GraphicsSceneMouseRelease: + if (draggingWindow && me->button() == Qt::LeftButton) { + stopWindowDrag(); + me->accept(); + return true; + } + break; + + default: + break; + } + + return QGraphicsWidget::windowFrameEvent(event); +} + +void ZoneViewWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + // move if the scene routes moves while dragging + if (draggingWindow && (event->buttons() & Qt::LeftButton)) { + setPos(calcDraggedWindowPos(event->screenPos(), event->scenePos(), event->buttonDownScenePos(Qt::LeftButton))); + event->accept(); + return; + } + + QGraphicsWidget::mouseMoveEvent(event); +} + +void ZoneViewWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + if (draggingWindow && event->button() == Qt::LeftButton) { + stopWindowDrag(); + event->accept(); + return; + } + + QGraphicsWidget::mouseReleaseEvent(event); +} + +QVariant ZoneViewWidget::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == QGraphicsItem::ItemPositionChange && scene()) { + // Keep grab area in main view + const QRectF sceneRect = scene()->sceneRect(); + const QPointF requestedPos = value.toPointF(); + QPointF desiredPos = requestedPos; + + const qreal minX = sceneRect.left(); + const qreal maxX = qMax(minX, sceneRect.right() - kMinVisibleWidth); + const qreal minY = sceneRect.top() + kTitleBarHeight; + const qreal maxY = qMax(minY, sceneRect.bottom() - kTitleBarHeight); + + desiredPos.setX(qBound(minX, desiredPos.x(), maxX)); + desiredPos.setY(qBound(minY, desiredPos.y(), maxY)); + return desiredPos; + } + + return QGraphicsWidget::itemChange(change, value); } void ZoneViewWidget::resizeEvent(QGraphicsSceneResizeEvent *event) @@ -350,6 +507,7 @@ void ZoneViewWidget::handleScrollBarChange(int value) void ZoneViewWidget::closeEvent(QCloseEvent *event) { + stopWindowDrag(); disconnect(zone, &ZoneViewZone::closed, this, 0); // manually call zone->close in order to remove it from the origZones views zone->close(); diff --git a/cockatrice/src/game/zones/view_zone_widget.h b/cockatrice/src/game/zones/view_zone_widget.h index 272ff5560..4ed8f74d8 100644 --- a/cockatrice/src/game/zones/view_zone_widget.h +++ b/cockatrice/src/game/zones/view_zone_widget.h @@ -3,7 +3,6 @@ * @ingroup GameGraphicsZones * @brief TODO: Document this. */ - #ifndef ZONEVIEWWIDGET_H #define ZONEVIEWWIDGET_H @@ -14,6 +13,7 @@ #include #include #include +#include #include class QLabel; @@ -28,6 +28,8 @@ class ServerInfo_Card; class QGraphicsSceneMouseEvent; class QGraphicsSceneWheelEvent; class QStyleOption; +class QGraphicsView; +class QWidget; class ScrollableGraphicsProxyWidget : public QGraphicsProxyWidget { @@ -66,6 +68,33 @@ private: int extraHeight; Player *player; + bool draggingWindow = false; + QPoint dragStartScreenPos; + QPointF dragStartItemPos; + QPointer dragView; + + void stopWindowDrag(); + void startWindowDrag(QGraphicsSceneMouseEvent *event); + QRectF closeButtonRect(QWidget *styleWidget) const; + /** + * @brief Resolves the QGraphicsView to use for drag coordinate mapping + * + * @param eventWidget QWidget that originated the mouse event + * @return The resolved QGraphicsView + */ + QGraphicsView *findDragView(QWidget *eventWidget) const; + /** + * @brief Calculates the desired widget position while dragging + * + * @param screenPos Global screen coordinates of the current mouse position + * @param scenePos Scene coordinates of the current mouse position + * @param buttonDownScenePos Scene coordinates of the initial mouse press position + * + * @return The new widget position in scene coordinates + */ + QPointF + calcDraggedWindowPos(const QPoint &screenPos, const QPointF &scenePos, const QPointF &buttonDownScenePos) const; + void resizeScrollbar(qreal newZoneHeight); signals: void closePressed(ZoneViewWidget *zv); @@ -76,7 +105,6 @@ private slots: void resizeToZoneContents(bool forceInitialHeight = false); void handleScrollBarChange(int value); void zoneDeleted(); - void moveEvent(QGraphicsSceneMoveEvent * /* event */) override; void resizeEvent(QGraphicsSceneResizeEvent * /* event */) override; void expandWindow(); @@ -101,6 +129,10 @@ public: protected: void closeEvent(QCloseEvent *event) override; void initStyleOption(QStyleOption *option) const override; + bool windowFrameEvent(QEvent *event) override; + QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; }; From b2dd8eed3fbec6539dfbc8a61ce2d2e70664f229 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Wed, 31 Dec 2025 08:54:47 -0800 Subject: [PATCH 40/43] [TabDeckEditor] Create class to centralize deck state (#6459) * create new file * use QSharedPointer in DeckListModel * [TabDeckEditor] Create class to centralize deck state * delete method * update docs --- cockatrice/CMakeLists.txt | 1 + .../deck_editor_deck_dock_widget.cpp | 298 ++++----------- .../deck_editor_deck_dock_widget.h | 38 +- .../deck_list_history_manager_widget.cpp | 77 ++-- .../deck_list_history_manager_widget.h | 11 +- .../deck_editor/deck_state_manager.cpp | 361 ++++++++++++++++++ .../widgets/deck_editor/deck_state_manager.h | 297 ++++++++++++++ .../dialogs/dlg_select_set_for_cards.cpp | 49 +-- .../dialogs/dlg_select_set_for_cards.h | 6 +- .../all_zones_card_amount_widget.cpp | 14 +- .../all_zones_card_amount_widget.h | 4 +- .../printing_selector/card_amount_widget.cpp | 44 +-- .../printing_selector/card_amount_widget.h | 8 +- .../printing_selector/printing_selector.cpp | 16 +- .../printing_selector/printing_selector.h | 13 +- .../printing_selector_card_display_widget.cpp | 9 +- .../printing_selector_card_display_widget.h | 3 +- .../printing_selector_card_overlay_widget.cpp | 9 +- .../printing_selector_card_overlay_widget.h | 3 +- ...rinting_selector_card_selection_widget.cpp | 14 +- .../printing_selector_card_selection_widget.h | 3 +- .../printing_selector_card_sorting_widget.cpp | 2 +- .../printing_selector_card_sorting_widget.h | 2 +- .../widgets/tabs/abstract_tab_deck_editor.cpp | 135 +++---- .../widgets/tabs/abstract_tab_deck_editor.h | 36 +- ...idekt_api_response_deck_display_widget.cpp | 5 +- .../widgets/tabs/tab_deck_editor.cpp | 5 +- .../tab_deck_editor_visual.cpp | 19 +- ...al_database_display_name_filter_widget.cpp | 3 +- .../models/deck_list/deck_list_model.cpp | 15 +- .../models/deck_list/deck_list_model.h | 10 +- 31 files changed, 933 insertions(+), 577 deletions(-) create mode 100644 cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp create mode 100644 cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index b2d387391..c6a29969f 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -156,6 +156,7 @@ set(cockatrice_SOURCES src/interface/widgets/deck_editor/deck_editor_filter_dock_widget.cpp src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp src/interface/widgets/deck_editor/deck_list_style_proxy.cpp + src/interface/widgets/deck_editor/deck_state_manager.cpp src/interface/widgets/general/background_sources.cpp src/interface/widgets/general/display/background_plate_widget.cpp src/interface/widgets/general/display/banner_widget.cpp diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index b8ebc1419..62195974b 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -1,8 +1,8 @@ #include "deck_editor_deck_dock_widget.h" #include "../../../client/settings/cache_settings.h" -#include "../../deck_loader/deck_loader.h" #include "deck_list_style_proxy.h" +#include "deck_state_manager.h" #include #include @@ -38,7 +38,7 @@ static int findRestoreIndex(const CardRef &wanted, const QComboBox *combo) } DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent) - : QDockWidget(parent), deckEditor(parent) + : QDockWidget(parent), deckEditor(parent), deckStateManager(parent->deckStateManager) { setObjectName("deckDock"); @@ -52,19 +52,19 @@ DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent void DeckEditorDeckDockWidget::createDeckDock() { - deckModel = new DeckListModel(this); - deckModel->setObjectName("deckModel"); - connect(deckModel, &DeckListModel::deckHashChanged, this, &DeckEditorDeckDockWidget::updateHash); - - deckLoader = new DeckLoader(this); + connect(getModel(), &DeckListModel::deckHashChanged, this, &DeckEditorDeckDockWidget::updateHash); proxy = new DeckListStyleProxy(this); - proxy->setSourceModel(deckModel); + proxy->setSourceModel(getModel()); - historyManagerWidget = new DeckListHistoryManagerWidget(deckModel, proxy, deckEditor->getHistoryManager(), this); + historyManagerWidget = new DeckListHistoryManagerWidget(deckStateManager, proxy, this); connect(historyManagerWidget, &DeckListHistoryManagerWidget::requestDisplayWidgetSync, this, &DeckEditorDeckDockWidget::syncDisplayWidgetsToModel); + connect(deckStateManager, &DeckStateManager::focusIndexChanged, this, &DeckEditorDeckDockWidget::setSelectedIndex); + connect(deckStateManager, &DeckStateManager::deckReplaced, this, + &DeckEditorDeckDockWidget::syncDisplayWidgetsToModel); + deckView = new QTreeView(); deckView->setObjectName("deckView"); deckView->setModel(proxy); @@ -97,7 +97,7 @@ void DeckEditorDeckDockWidget::createDeckDock() nameDebounceTimer = new QTimer(this); nameDebounceTimer->setSingleShot(true); nameDebounceTimer->setInterval(300); // debounce duration in ms - connect(nameDebounceTimer, &QTimer::timeout, this, [this]() { updateName(nameEdit->text()); }); + connect(nameDebounceTimer, &QTimer::timeout, this, &DeckEditorDeckDockWidget::writeName); connect(nameEdit, &LineEditUnfocusable::textChanged, this, [this]() { nameDebounceTimer->start(); // restart debounce timer @@ -141,7 +141,7 @@ void DeckEditorDeckDockWidget::createDeckDock() commentsDebounceTimer = new QTimer(this); commentsDebounceTimer->setSingleShot(true); commentsDebounceTimer->setInterval(400); // longer debounce for multi-line - connect(commentsDebounceTimer, &QTimer::timeout, this, [this]() { updateComments(); }); + connect(commentsDebounceTimer, &QTimer::timeout, this, &DeckEditorDeckDockWidget::writeComments); connect(commentsEdit, &QTextEdit::textChanged, this, [this]() { commentsDebounceTimer->start(); // restart debounce timer @@ -152,21 +152,21 @@ void DeckEditorDeckDockWidget::createDeckDock() bannerCardLabel->setText(tr("Banner Card")); bannerCardLabel->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible()); bannerCardComboBox = new QComboBox(this); - connect(deckModel, &DeckListModel::dataChanged, this, [this]() { + connect(getModel(), &DeckListModel::dataChanged, this, [this]() { // Delay the update to avoid race conditions QTimer::singleShot(100, this, &DeckEditorDeckDockWidget::updateBannerCardComboBox); }); - connect(deckModel, &DeckListModel::cardAddedAt, this, &DeckEditorDeckDockWidget::recursiveExpand); - connect(deckModel, &DeckListModel::modelReset, this, &DeckEditorDeckDockWidget::expandAll); + connect(getModel(), &DeckListModel::cardAddedAt, this, &DeckEditorDeckDockWidget::recursiveExpand); + connect(getModel(), &DeckListModel::modelReset, this, &DeckEditorDeckDockWidget::expandAll); connect(bannerCardComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, - &DeckEditorDeckDockWidget::setBannerCard); + &DeckEditorDeckDockWidget::writeBannerCard); bannerCardComboBox->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible()); - deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList()->getTags()); + deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, {}); deckTagsDisplayWidget->setHidden(!SettingsCache::instance().getDeckEditorTagsWidgetVisible()); - connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, this, - &DeckEditorDeckDockWidget::setTags); + connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, deckStateManager, + &DeckStateManager::setTags); activeGroupCriteriaLabel = new QLabel(this); @@ -175,9 +175,9 @@ void DeckEditorDeckDockWidget::createDeckDock() activeGroupCriteriaComboBox->addItem(tr("Mana Cost"), DeckListModelGroupCriteria::MANA_COST); activeGroupCriteriaComboBox->addItem(tr("Colors"), DeckListModelGroupCriteria::COLOR); connect(activeGroupCriteriaComboBox, QOverload::of(&QComboBox::currentIndexChanged), [this]() { - deckModel->setActiveGroupCriteria(static_cast( + getModel()->setActiveGroupCriteria(static_cast( activeGroupCriteriaComboBox->currentData(Qt::UserRole).toInt())); - deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); + getModel()->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); }); aIncrement = new QAction(QString(), this); @@ -295,9 +295,10 @@ void DeckEditorDeckDockWidget::initializeFormats() formatComboBox->addItem(formatName, formatName); // store the raw key in itemData } - if (!deckModel->getDeckList()->getGameFormat().isEmpty()) { - deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat()); - formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat())); + QString format = deckStateManager->getMetadata().gameFormat; + if (!format.isEmpty()) { + getModel()->setActiveFormat(format); + formatComboBox->setCurrentIndex(formatComboBox->findData(format)); } else { // Ensure no selection is visible initially formatComboBox->setCurrentIndex(-1); @@ -306,11 +307,10 @@ void DeckEditorDeckDockWidget::initializeFormats() connect(formatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (index >= 0) { QString formatKey = formatComboBox->itemData(index).toString(); - deckModel->setActiveFormat(formatKey); + deckStateManager->setFormat(formatKey); } else { - deckModel->setActiveFormat(QString()); // clear format if deselected + deckStateManager->setFormat(""); // clear format if deselected } - emit deckModified(); }); } @@ -340,43 +340,37 @@ ExactCard DeckEditorDeckDockWidget::getCurrentCard() void DeckEditorDeckDockWidget::updateCard(const QModelIndex /*¤t*/, const QModelIndex & /*previous*/) { if (ExactCard card = getCurrentCard()) { - emit cardChanged(card); + emit selectedCardChanged(card); } } -void DeckEditorDeckDockWidget::updateName(const QString &name) +/** + * @brief Writes the contents of the name textBox to the DeckStateManager + */ +void DeckEditorDeckDockWidget::writeName() { - emit requestDeckHistorySave( - QString(tr("Rename deck to \"%1\" from \"%2\"")).arg(name).arg(deckLoader->getDeck().deckList.getName())); - deckModel->getDeckList()->setName(name); - deckEditor->setModified(name.isEmpty()); - emit nameChanged(); - emit deckModified(); + QString name = nameEdit->text(); + deckStateManager->setName(name); } -void DeckEditorDeckDockWidget::updateComments() +/** + * @brief Writes the contents of the comments textBox to the DeckStateManager + */ +void DeckEditorDeckDockWidget::writeComments() { - emit requestDeckHistorySave(tr("Updated comments (was %1 chars, now %2 chars)") - .arg(deckLoader->getDeck().deckList.getComments().size()) - .arg(commentsEdit->toPlainText().size())); - - deckModel->getDeckList()->setComments(commentsEdit->toPlainText()); - deckEditor->setModified(commentsEdit->toPlainText().isEmpty()); - emit commentsChanged(); - emit deckModified(); + QString comments = commentsEdit->toPlainText(); + deckStateManager->setComments(comments); } void DeckEditorDeckDockWidget::updateHash() { - hashLabel->setText(deckModel->getDeckList()->getDeckHash()); - emit hashChanged(); - emit deckModified(); + hashLabel->setText(deckStateManager->getDeckHash()); } void DeckEditorDeckDockWidget::updateBannerCardComboBox() { // Store current banner card identity - CardRef wanted = deckModel->getDeckList()->getBannerCard(); + CardRef wanted = deckStateManager->getMetadata().bannerCard; // Block signals temporarily bool wasBlocked = bannerCardComboBox->blockSignals(true); @@ -386,7 +380,7 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox() // Collect unique (name, providerId) pairs QSet> bannerCardSet; - QList cardsInDeck = deckModel->getCardRefs(); + QList cardsInDeck = getModel()->getCardRefs(); for (auto cardRef : cardsInDeck) { if (!CardDatabaseManager::query()->getCard(cardRef)) { @@ -415,7 +409,6 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox() // Handle results if (restoreIndex != -1) { bannerCardComboBox->setCurrentIndex(restoreIndex); - syncDeckListBannerCardWithComboBox(); } else { // Add a placeholder "-" and set it as the current selection bannerCardComboBox->insertItem(0, "-"); @@ -426,25 +419,14 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox() bannerCardComboBox->blockSignals(wasBlocked); } -void DeckEditorDeckDockWidget::setBannerCard(int /* changedIndex */) +/** + * @brief Writes the selected bannerCard to the DeckStateManager + */ +void DeckEditorDeckDockWidget::writeBannerCard(int index) { - emit requestDeckHistorySave(tr("Banner card changed")); - syncDeckListBannerCardWithComboBox(); - deckEditor->setModified(true); - emit deckModified(); -} - -void DeckEditorDeckDockWidget::setTags(const QStringList &tags) -{ - deckModel->getDeckList()->setTags(tags); - deckEditor->setModified(true); - emit deckModified(); -} - -void DeckEditorDeckDockWidget::syncDeckListBannerCardWithComboBox() -{ - auto [name, id] = bannerCardComboBox->currentData().value>(); - deckModel->getDeckList()->setBannerCard({name, id}); + auto [name, id] = bannerCardComboBox->itemData(index).value>(); + CardRef bannerCard = {name, id}; + deckStateManager->setBannerCard(bannerCard); } void DeckEditorDeckDockWidget::updateShowBannerCardComboBox(const bool visible) @@ -460,7 +442,7 @@ void DeckEditorDeckDockWidget::updateShowTagsWidget(const bool visible) void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck() { - if (deckModel->getDeckList()->getBannerCard().name == "") { + if (deckStateManager->getMetadata().bannerCard.name == "") { if (bannerCardComboBox->findText("-") != -1) { bannerCardComboBox->setCurrentIndex(bannerCardComboBox->findText("-")); } else { @@ -468,36 +450,26 @@ void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck() bannerCardComboBox->setCurrentIndex(0); } } else { - bannerCardComboBox->setCurrentText(deckModel->getDeckList()->getBannerCard().name); + bannerCardComboBox->setCurrentText(deckStateManager->getMetadata().bannerCard.name); } } -/** - * Sets the currently active deck for this tab - * @param _deck The deck. - */ -void DeckEditorDeckDockWidget::setDeck(const LoadedDeck &_deck) +void DeckEditorDeckDockWidget::setSelectedIndex(const QModelIndex &newCardIndex) { - deckLoader->setDeck(_deck); - deckModel->setDeckList(&deckLoader->getDeck().deckList); - connect(deckLoader, &DeckLoader::deckLoaded, deckModel, &DeckListModel::rebuildTree); - - emit requestDeckHistoryClear(); - historyManagerWidget->setDeckListModel(deckModel); - - syncDisplayWidgetsToModel(); - - emit deckChanged(); + deckView->clearSelection(); + deckView->setCurrentIndex(newCardIndex); + recursiveExpand(newCardIndex); + deckView->setFocus(Qt::FocusReason::MouseFocusReason); } void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel() { nameEdit->blockSignals(true); - nameEdit->setText(deckModel->getDeckList()->getName()); + nameEdit->setText(deckStateManager->getMetadata().name); nameEdit->blockSignals(false); commentsEdit->blockSignals(true); - commentsEdit->setText(deckModel->getDeckList()->getComments()); + commentsEdit->setText(deckStateManager->getMetadata().comments); commentsEdit->blockSignals(false); bannerCardComboBox->blockSignals(true); @@ -507,44 +479,22 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel() updateHash(); sortDeckModelToDeckView(); - deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags()); + deckTagsDisplayWidget->setTags(deckStateManager->getMetadata().tags); } void DeckEditorDeckDockWidget::sortDeckModelToDeckView() { - deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); - deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat()); - formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat())); - - emit deckChanged(); -} - -DeckLoader *DeckEditorDeckDockWidget::getDeckLoader() -{ - return deckLoader; -} - -const DeckList &DeckEditorDeckDockWidget::getDeckList() const -{ - return *deckModel->getDeckList(); + getModel()->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); + getModel()->setActiveFormat(deckStateManager->getMetadata().gameFormat); + formatComboBox->setCurrentIndex(formatComboBox->findData(deckStateManager->getMetadata().gameFormat)); } /** - * Resets the tab to the state for a blank new tab. + * @brief Convenience method to get the underlying model instance from the DeckStateManager */ -void DeckEditorDeckDockWidget::cleanDeck() +DeckListModel *DeckEditorDeckDockWidget::getModel() const { - deckModel->cleanList(); - nameEdit->setText(QString()); - emit nameChanged(); - commentsEdit->setText(QString()); - emit commentsChanged(); - hashLabel->setText(QString()); - emit hashChanged(); - emit deckModified(); - emit deckChanged(); - updateBannerCardComboBox(); - deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags()); + return deckStateManager->getModel(); } void DeckEditorDeckDockWidget::selectPrevCard() @@ -635,7 +585,7 @@ QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodes() const auto selectedRows = deckView->selectionModel()->selectedRows(); const auto notLeafNode = [this](const QModelIndex &index) { - return deckModel->hasChildren(proxy->mapToSource(index)); + return getModel()->hasChildren(proxy->mapToSource(index)); }; selectedRows.erase(std::remove_if(selectedRows.begin(), selectedRows.end(), notLeafNode), selectedRows.end()); @@ -650,21 +600,7 @@ void DeckEditorDeckDockWidget::actAddCard(const ExactCard &card, const QString & } QString zoneName = card.getInfo().getIsToken() ? DECK_ZONE_TOKENS : _zoneName; - - emit requestDeckHistorySave(tr("Added (%1): %2 (%3) %4") - .arg(zoneName, card.getName(), card.getPrinting().getSet()->getCorrectedShortName(), - card.getPrinting().getProperty("num"))); - - QModelIndex newCardIndex = deckModel->addCard(card, zoneName); - - if (!newCardIndex.isValid()) { - return; - } - - deckView->clearSelection(); - deckView->setCurrentIndex(newCardIndex); - - emit deckModified(); + deckStateManager->addCard(card, zoneName); } void DeckEditorDeckDockWidget::actIncrementSelection() @@ -681,12 +617,12 @@ void DeckEditorDeckDockWidget::actSwapCard(const ExactCard &card, const QString QString providerId = card.getPrinting().getUuid(); QString collectorNumber = card.getPrinting().getProperty("num"); - QModelIndex foundCard = deckModel->findCard(card.getName(), zoneName, providerId, collectorNumber); + QModelIndex foundCard = getModel()->findCard(card.getName(), zoneName, providerId, collectorNumber); if (!foundCard.isValid()) { - foundCard = deckModel->findCard(card.getName(), zoneName); + foundCard = getModel()->findCard(card.getName(), zoneName); } - swapCard(foundCard); + deckStateManager->swapCardAtIndex(foundCard); } void DeckEditorDeckDockWidget::actSwapSelection() @@ -699,54 +635,15 @@ void DeckEditorDeckDockWidget::actSwapSelection() deckView->setSelectionMode(QAbstractItemView::SingleSelection); } - bool isModified = false; for (const auto ¤tIndex : selectedRows) { - if (swapCard(currentIndex)) { - isModified = true; - } + deckStateManager->swapCardAtIndex(currentIndex); } deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); - if (isModified) { - emit deckModified(); - } - update(); } -/** - * Swaps the card at the index between the maindeck and sideboard - * - * @param currentIndex The index to swap. - * @return True if the swap was successful - */ -bool DeckEditorDeckDockWidget::swapCard(const QModelIndex ¤tIndex) -{ - if (!currentIndex.isValid()) - return false; - const QString cardName = currentIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString(); - const QString cardProviderID = - currentIndex.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data().toString(); - const QModelIndex gparent = currentIndex.parent().parent(); - - if (!gparent.isValid()) - return false; - - const QString zoneName = gparent.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); - offsetCountAtIndex(currentIndex, false); - const QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; - - if (ExactCard card = CardDatabaseManager::query()->getCard({cardName, cardProviderID})) { - deckModel->addCard(card, otherZoneName); - } else { - // Third argument (true) says create the card no matter what, even if not in DB - deckModel->addPreferredPrintingCard(cardName, otherZoneName, true); - } - - return true; -} - void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString zoneName) { if (!card) @@ -754,17 +651,7 @@ void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString z if (card.getInfo().getIsToken()) zoneName = DECK_ZONE_TOKENS; - QString providerId = card.getPrinting().getUuid(); - QString collectorNumber = card.getPrinting().getProperty("num"); - - QModelIndex idx = deckModel->findCard(card.getName(), zoneName, providerId, collectorNumber); - if (!idx.isValid()) { - return; - } - - deckView->clearSelection(); - deckView->setCurrentIndex(proxy->mapToSource(idx)); - offsetCountAtIndex(idx, false); + deckStateManager->decrementCard(card, zoneName); } void DeckEditorDeckDockWidget::actDecrementSelection() @@ -794,25 +681,11 @@ void DeckEditorDeckDockWidget::actRemoveCard() deckView->setSelectionMode(QAbstractItemView::SingleSelection); } - bool isModified = false; - for (const auto &index : selectedRows) { - if (!index.isValid() || deckModel->hasChildren(index)) { - continue; - } - QModelIndex sourceIndex = proxy->mapToSource(index); - QString cardName = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString(); - - emit requestDeckHistorySave(QString(tr("Removed \"%1\" (all copies)")).arg(cardName)); - - deckModel->removeRow(sourceIndex.row(), sourceIndex.parent()); - isModified = true; + for (const auto &row : selectedRows) { + deckStateManager->removeCardAtIndex(row); } deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); - - if (isModified) { - emit deckModified(); - } } /** @@ -822,28 +695,17 @@ void DeckEditorDeckDockWidget::actRemoveCard() */ void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, bool isIncrement) { - if (!idx.isValid() || deckModel->hasChildren(idx)) { + if (!idx.isValid() || getModel()->hasChildren(idx)) { return; } QModelIndex sourceIndex = proxy->mapToSource(idx); - QString cardName = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); - QString providerId = - sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString(); - - const auto reason = QString(tr("%1 %2 × \"%3\" (%4)")) - .arg(isIncrement ? tr("Added") : tr("Removed")) - .arg(1) - .arg(cardName) - .arg(providerId); - - emit requestDeckHistorySave(reason); - - int offset = isIncrement ? 1 : -1; - deckModel->offsetCountAtIndex(sourceIndex, offset); - - emit deckModified(); + if (isIncrement) { + deckStateManager->incrementCountAtIndex(sourceIndex); + } else { + deckStateManager->decrementCountAtIndex(sourceIndex); + } } void DeckEditorDeckDockWidget::decklistCustomMenu(QPoint point) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h index a50434f0b..1a82b00d1 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -28,22 +28,14 @@ class DeckEditorDeckDockWidget : public QDockWidget Q_OBJECT public: explicit DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent); - DeckLoader *deckLoader; + DeckListStyleProxy *proxy; - DeckListModel *deckModel; QTreeView *deckView; QComboBox *bannerCardComboBox; void createDeckDock(); ExactCard getCurrentCard(); void retranslateUi(); - QString getDeckName() - { - return nameEdit->text(); - } - QString getSimpleDeckName() - { - return nameEdit->text().simplified(); - } + QComboBox *getGroupByComboBox() { return activeGroupCriteriaComboBox; @@ -55,15 +47,11 @@ public: } public slots: - void cleanDeck(); void selectPrevCard(); void selectNextCard(); void updateBannerCardComboBox(); - void setDeck(const LoadedDeck &_deck); void syncDisplayWidgetsToModel(); void sortDeckModelToDeckView(); - DeckLoader *getDeckLoader(); - const DeckList &getDeckList() const; void actAddCard(const ExactCard &card, const QString &zoneName); void actIncrementSelection(); void actDecrementCard(const ExactCard &card, QString zoneName); @@ -74,17 +62,12 @@ public slots: void initializeFormats(); signals: - void nameChanged(); - void commentsChanged(); - void hashChanged(); - void deckChanged(); - void deckModified(); - void requestDeckHistorySave(const QString &modificationReason); - void requestDeckHistoryClear(); - void cardChanged(const ExactCard &_card); + void selectedCardChanged(const ExactCard &card); private: AbstractTabDeckEditor *deckEditor; + DeckStateManager *deckStateManager; + DeckListHistoryManagerWidget *historyManagerWidget; KeySignals deckViewKeySignals; QLabel *nameLabel; @@ -107,18 +90,17 @@ private: QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard; + DeckListModel *getModel() const; [[nodiscard]] QModelIndexList getSelectedCardNodes() const; void offsetCountAtIndex(const QModelIndex &idx, bool isIncrement); private slots: void decklistCustomMenu(QPoint point); - bool swapCard(const QModelIndex ¤tIndex); void updateCard(QModelIndex, const QModelIndex ¤t); - void updateName(const QString &name); - void updateComments(); - void setBannerCard(int); - void setTags(const QStringList &tags); - void syncDeckListBannerCardWithComboBox(); + void writeName(); + void writeComments(); + void writeBannerCard(int); + void setSelectedIndex(const QModelIndex &newCardIndex); void updateHash(); void refreshShortcuts(); void updateShowBannerCardComboBox(bool visible); diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp index c68934ea6..d2d748753 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp @@ -1,10 +1,11 @@ #include "deck_list_history_manager_widget.h" -DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckListModel *_deckListModel, +#include "deck_state_manager.h" + +DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_deckStateManager, DeckListStyleProxy *_styleProxy, - DeckListHistoryManager *manager, QWidget *parent) - : QWidget(parent), deckListModel(_deckListModel), styleProxy(_styleProxy), historyManager(manager) + : QWidget(parent), deckStateManager(_deckStateManager), styleProxy(_styleProxy) { layout = new QHBoxLayout(this); @@ -43,8 +44,7 @@ DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckListModel *_deckL connect(historyList, &QListWidget::itemClicked, this, &DeckListHistoryManagerWidget::onListClicked); - connect(historyManager, &DeckListHistoryManager::undoRedoStateChanged, this, - &DeckListHistoryManagerWidget::refreshList); + connect(deckStateManager, &DeckStateManager::historyChanged, this, &DeckListHistoryManagerWidget::refreshList); refreshList(); retranslateUi(); @@ -58,15 +58,12 @@ void DeckListHistoryManagerWidget::retranslateUi() historyLabel->setText(tr("Click on an entry to revert to that point in the history.")); } -void DeckListHistoryManagerWidget::setDeckListModel(DeckListModel *_deckListModel) -{ - deckListModel = _deckListModel; -} - void DeckListHistoryManagerWidget::refreshList() { historyList->clear(); + DeckListHistoryManager *historyManager = deckStateManager->getHistoryManager(); + // Fill redo section first (oldest redo at top, newest redo closest to divider) const auto redoStack = historyManager->getRedoStack(); for (int i = 0; i < redoStack.size(); ++i) { // iterate forward @@ -98,36 +95,7 @@ void DeckListHistoryManagerWidget::refreshList() redoButton->setEnabled(historyManager->canRedo()); } -void DeckListHistoryManagerWidget::doUndo() -{ - if (!historyManager->canUndo()) { - return; - } - - historyManager->undo(deckListModel->getDeckList()); - deckListModel->rebuildTree(); - emit deckListModel->layoutChanged(); - emit requestDisplayWidgetSync(); - - refreshList(); -} - -void DeckListHistoryManagerWidget::doRedo() -{ - if (!historyManager->canRedo()) { - return; - } - - historyManager->redo(deckListModel->getDeckList()); - deckListModel->rebuildTree(); - - emit deckListModel->layoutChanged(); - emit requestDisplayWidgetSync(); - - refreshList(); -} - -void DeckListHistoryManagerWidget::onListClicked(QListWidgetItem *item) +void DeckListHistoryManagerWidget::onListClicked(const QListWidgetItem *item) { // Ignore non-selectable items (like divider) if (!(item->flags() & Qt::ItemIsSelectable)) { @@ -138,23 +106,24 @@ void DeckListHistoryManagerWidget::onListClicked(QListWidgetItem *item) int index = item->data(Qt::UserRole + 1).toInt(); if (mode == "redo") { - const auto redoStack = historyManager->getRedoStack(); + const auto redoStack = deckStateManager->getHistoryManager()->getRedoStack(); int steps = redoStack.size() - index; - for (int i = 0; i < steps; ++i) { - historyManager->redo(deckListModel->getDeckList()); - } + deckStateManager->redo(steps); } else if (mode == "undo") { - const auto undoStack = historyManager->getUndoStack(); - int steps = undoStack.size() - 1 - index; - for (int i = 0; i < steps + 1; ++i) { - historyManager->undo(deckListModel->getDeckList()); - } + const auto undoStack = deckStateManager->getHistoryManager()->getUndoStack(); + int steps = undoStack.size() - index; + deckStateManager->undo(steps); } - deckListModel->rebuildTree(); - - emit deckListModel->layoutChanged(); - emit requestDisplayWidgetSync(); - refreshList(); +} + +void DeckListHistoryManagerWidget::doUndo() +{ + deckStateManager->undo(); +} + +void DeckListHistoryManagerWidget::doRedo() +{ + deckStateManager->redo(); } \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.h index 0e208ad2b..ab53912e2 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.h @@ -14,6 +14,8 @@ #include #include +class DeckStateManager; + class DeckListHistoryManagerWidget : public QWidget { Q_OBJECT @@ -25,22 +27,19 @@ public slots: void retranslateUi(); public: - explicit DeckListHistoryManagerWidget(DeckListModel *deckListModel, + explicit DeckListHistoryManagerWidget(DeckStateManager *deckStateManager, DeckListStyleProxy *styleProxy, - DeckListHistoryManager *manager, QWidget *parent = nullptr); - void setDeckListModel(DeckListModel *_deckListModel); private slots: void refreshList(); - void onListClicked(QListWidgetItem *item); + void onListClicked(const QListWidgetItem *item); void doUndo(); void doRedo(); private: - DeckListModel *deckListModel; + DeckStateManager *deckStateManager; DeckListStyleProxy *styleProxy; - DeckListHistoryManager *historyManager; QHBoxLayout *layout; QAction *aUndo; diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp new file mode 100644 index 000000000..628d33d51 --- /dev/null +++ b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp @@ -0,0 +1,361 @@ +#include "deck_state_manager.h" + +#include +#include + +DeckStateManager::DeckStateManager(QObject *parent) + : QObject(parent), deckList(QSharedPointer(new DeckList)), + deckListModel(new DeckListModel(this, deckList)), historyManager(new DeckListHistoryManager(this)) +{ + connect(historyManager, &DeckListHistoryManager::undoRedoStateChanged, this, [this] { + setModified(true); + emit historyChanged(); + }); + connect(deckListModel, &DeckListModel::rowsInserted, this, &DeckStateManager::uniqueCardsChanged); + connect(deckListModel, &DeckListModel::rowsRemoved, this, &DeckStateManager::uniqueCardsChanged); +} + +const DeckList &DeckStateManager::getDeckList() const +{ + return *deckList.get(); +} + +LoadedDeck DeckStateManager::toLoadedDeck() const +{ + return {getDeckList(), lastLoadInfo}; +} + +DeckList::Metadata const &DeckStateManager::getMetadata() const +{ + return deckList->getMetadata(); +} + +QString DeckStateManager::getSimpleDeckName() const +{ + return deckList->getMetadata().name.simplified(); +} + +QString DeckStateManager::getDeckHash() const +{ + return deckList->getDeckHash(); +} + +bool DeckStateManager::isModified() const +{ + return modified; +} + +void DeckStateManager::setModified(bool state) +{ + if (state == modified) { + return; + } + + modified = state; + emit isModifiedChanged(modified); +} + +bool DeckStateManager::isBlankNewDeck() const +{ + return !isModified() && deckList->isBlankDeck(); +} + +void DeckStateManager::replaceDeck(const LoadedDeck &deck) +{ + lastLoadInfo = deck.lastLoadInfo; + deckList = QSharedPointer(new DeckList(deck.deckList)); + deckListModel->setDeckList(deckList); + + historyManager->clear(); + + setModified(false); + emit deckReplaced(); +} + +void DeckStateManager::clearDeck() +{ + replaceDeck(LoadedDeck()); +} + +bool DeckStateManager::modifyDeck(const QString &reason, const std::function &operation) +{ + DeckListMemento memento = deckList->createMemento(reason); + bool success = operation(deckListModel); + + if (success) { + historyManager->save(memento); + doCardModified(); + } + + return success; +} + +QModelIndex DeckStateManager::modifyDeck(const QString &reason, + const std::function &operation) +{ + DeckListMemento memento = deckList->createMemento(reason); + QModelIndex idx = operation(deckListModel); + + if (idx.isValid()) { + historyManager->save(memento); + doCardModified(); + } + + return idx; +} + +void DeckStateManager::setName(const QString &name) +{ + QString previous = deckList->getName(); + if (previous == name) { + return; + } + + requestHistorySave(tr("Rename deck to \"%1\" from \"%2\"").arg(name).arg(previous)); + deckList->setName(name); + + doMetadataModified(); +} + +void DeckStateManager::setComments(const QString &comments) +{ + QString previous = deckList->getComments(); + if (previous == comments) { + return; + } + + requestHistorySave(tr("Updated comments (was %1 chars, now %2 chars)").arg(previous.size()).arg(comments.size())); + deckList->setComments(comments); + + doMetadataModified(); +} + +void DeckStateManager::setBannerCard(const CardRef &bannerCard) +{ + CardRef previous = deckList->getBannerCard(); + if (previous == bannerCard) { + return; + } + + requestHistorySave(tr("Set banner card to %1 (%2)").arg(bannerCard.name).arg(bannerCard.providerId)); + deckList->setBannerCard(bannerCard); + + doMetadataModified(); +} + +void DeckStateManager::setTags(const QStringList &tags) +{ + QStringList previous = deckList->getTags(); + if (previous == tags) { + return; + } + + requestHistorySave(tr("Tags changed")); + deckList->setTags(tags); + + doMetadataModified(); +} + +void DeckStateManager::setFormat(const QString &format) +{ + if (deckList->getMetadata().gameFormat == format) { + return; + } + + requestHistorySave(tr("Set format to %1").arg(format)); + deckListModel->setActiveFormat(format); + + doMetadataModified(); +} + +QModelIndex DeckStateManager::addCard(const ExactCard &card, const QString &zoneName) +{ + if (!card) { + return {}; + } + + QString reason = tr("Added (%1): %2 (%3) %4") + .arg(zoneName, card.getName(), card.getPrinting().getSet()->getCorrectedShortName(), + card.getPrinting().getProperty("num")); + + QModelIndex idx = modifyDeck(reason, [&card, &zoneName](auto model) { return model->addCard(card, zoneName); }); + + if (idx.isValid()) { + emit focusIndexChanged(idx); + } + + return idx; +} + +QModelIndex DeckStateManager::decrementCard(const ExactCard &card, const QString &zoneName) +{ + if (!card) + return {}; + + QString providerId = card.getPrinting().getUuid(); + QString collectorNumber = card.getPrinting().getProperty("num"); + + QModelIndex idx = deckListModel->findCard(card.getName(), zoneName, providerId, collectorNumber); + if (!idx.isValid()) { + return {}; + } + + bool success = offsetCountAtIndex(idx, false); + + if (!success) { + return {}; + } + + if (idx.isValid()) { + emit focusIndexChanged(idx); + } + + return idx; +} + +static bool doSwapCard(DeckListModel *model, + const QModelIndex &idx, + const QString &cardName, + const QString &providerId, + const QString &otherZone) +{ + bool success = model->offsetCountAtIndex(idx, -1); + if (!success) { + return false; + } + + if (ExactCard card = CardDatabaseManager::query()->getCard({cardName, providerId})) { + model->addCard(card, otherZone); + } else { + // Third argument (true) says create the card no matter what, even if not in DB + model->addPreferredPrintingCard(cardName, otherZone, true); + } + + return true; +} + +bool DeckStateManager::swapCardAtIndex(const QModelIndex &idx) +{ + if (!idx.isValid()) + return false; + + QString cardName = idx.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString(); + QString providerId = idx.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data().toString(); + QModelIndex gparent = idx.parent().parent(); + + if (!gparent.isValid()) + return false; + + QString zoneName = gparent.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); + QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; + + QString reason = tr("Moved to %1 1 × \"%2\" (%3)") // + .arg(otherZoneName) + .arg(cardName) + .arg(providerId); + + return modifyDeck(reason, [&idx, &cardName, &providerId, &otherZoneName](auto model) { + return doSwapCard(model, idx, cardName, providerId, otherZoneName); + }); +} + +bool DeckStateManager::removeCardAtIndex(const QModelIndex &idx) +{ + if (!idx.isValid() || deckListModel->hasChildren(idx)) { + return false; + } + + QString cardName = idx.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString(); + + QString reason = tr("Removed \"%1\" (all copies)").arg(cardName); + + return modifyDeck(reason, [&idx](auto model) { return model->removeRow(idx.row(), idx.parent()); }); +} + +bool DeckStateManager::incrementCountAtIndex(const QModelIndex &idx) +{ + return offsetCountAtIndex(idx, 1); +} + +bool DeckStateManager::decrementCountAtIndex(const QModelIndex &idx) +{ + return offsetCountAtIndex(idx, -1); +} + +bool DeckStateManager::offsetCountAtIndex(const QModelIndex &idx, int offset) +{ + if (!idx.isValid()) { + return false; + } + + QString cardName = idx.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); + QString providerId = idx.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString(); + + QString reason = tr("%1 1 × \"%2\" (%3)") // + .arg(offset > 0 ? tr("Added") : tr("Removed")) + .arg(cardName) + .arg(providerId); + + return modifyDeck(reason, [&idx, &offset](auto model) { return model->offsetCountAtIndex(idx, offset); }); +} + +void DeckStateManager::undo(int steps) +{ + if (!historyManager->canUndo()) { + return; + } + + for (int i = 0; i < steps; i++) { + if (!historyManager->canUndo()) { + continue; + } + historyManager->undo(deckList.get()); + } + + deckListModel->rebuildTree(); + + emit deckListModel->layoutChanged(); +} + +void DeckStateManager::redo(int steps) +{ + if (!historyManager->canRedo()) { + return; + } + + for (int i = 0; i < steps; i++) { + if (!historyManager->canRedo()) { + continue; + } + historyManager->redo(deckList.get()); + } + + deckListModel->rebuildTree(); + + emit deckListModel->layoutChanged(); +} + +void DeckStateManager::requestHistorySave(const QString &reason) +{ + historyManager->save(deckList->createMemento(reason)); +} + +/** + * @brief Handles updating state and emitting signals whenever the cards are modified + */ +void DeckStateManager::doCardModified() +{ + setModified(true); + emit cardModified(); + emit deckModified(); +} + +/** + * @brief Handles updating state and emitting signals whenever the metadata is modified + */ +void DeckStateManager::doMetadataModified() +{ + setModified(true); + emit metadataModified(); + emit deckModified(); +} diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h new file mode 100644 index 000000000..0f3ba3255 --- /dev/null +++ b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h @@ -0,0 +1,297 @@ +#ifndef COCKATRICE_DECK_STATE_MANAGER_H +#define COCKATRICE_DECK_STATE_MANAGER_H + +#include "../../deck_loader/loaded_deck.h" +#include "deck_list_model.h" + +#include +#include + +class DeckListHistoryManager; + +/** + * @brief This class centralizes the management of the state of the deck in the deck editor tab. + * It is responsible for owning and managing the DeckListModel, underlying DeckList, load info, and edit history. + * + * Although this class provides getters for the underlying DeckListModel, you should generally refrain from directly + * modifying the returned model. Outside modifications to the deck state should be done through @link + * DeckStateManager::modifyDeck and the metadata setters. + * Those methods ensure that the history is recorded and correct signals are emitted. + */ +class DeckStateManager : public QObject +{ + Q_OBJECT + + LoadedDeck::LoadInfo lastLoadInfo; + QSharedPointer deckList; + DeckListModel *deckListModel; + DeckListHistoryManager *historyManager; + + bool modified = false; + +public: + explicit DeckStateManager(QObject *parent = nullptr); + + /** + * Gets the underlying HistoryManager. + * @return The DeckListHistoryManager instance + */ + DeckListHistoryManager *getHistoryManager() const + { + return historyManager; + } + + /** + * @brief Gets the underlying DeckListModel. + * You should generally refrain modifying the returned model directly. + * However, it's fine (and intended) to perform queries on the returned model. + * @return The DeckListModel instance + */ + DeckListModel *getModel() const + { + return deckListModel; + } + + /** + * @brief Gets a view of the current deck. + */ + const DeckList &getDeckList() const; + + /** + * @brief Creates a LoadedDeck containing the contents of the current deck and the current LoadInfo. + * + * @return A new LoadedDeck instance. + */ + LoadedDeck toLoadedDeck() const; + + /** + * @brief Gets a view of the metadata in the DeckList + */ + DeckList::Metadata const &getMetadata() const; + + /** + * @brief Gets the deck's simplified name. + */ + QString getSimpleDeckName() const; + + /** + * @brief Gets the deck hash. + */ + QString getDeckHash() const; + + /** + * @brief Checks if the deck has been modified since it was last saved + */ + bool isModified() const; + + /** + * @brief Sets the new isModified state, emitting a signal if the state changed. + * This class will automatically update its isModified state, but you may need to set it manually to handle, for + * example, saving. + * @param state The state + */ + void setModified(bool state); + + /** + * @brief Checks if the deck state is as if it was a new deck + */ + bool isBlankNewDeck() const; + + /** + * @brief Overwrites the current deck with a new deck, resetting all history + * @param deck The new deck. + */ + void replaceDeck(const LoadedDeck &deck); + + /** + * @brief Resets the deck to a blank new deck, resetting all history. + */ + void clearDeck(); + + /** + * @brief Sets the lastLoadInfo. + * @param loadInfo The lastLoadInfo + */ + void setLastLoadInfo(const LoadedDeck::LoadInfo &loadInfo) + { + lastLoadInfo = loadInfo; + } + + /** + * @brief Modifies the cards in the deck, in a wrapped operation that is saved to the history. + * + * The operation is a function that accepts a DeckListModel that it operates upon, and returns a bool. + * + * This method will pass the underlying DeckListModel into the operation function. The function can call methods on + * the model to modify the deck. + * The function should return a bool to indicate success/failure. + * + * If the operation returns true, the state of the deck before the operation is ran is saved to the history, and the + * isModified state is updated. + * If the operation returns false, the history and isModified state is not updated. + * + * Note that even if the operation fails, any modifications to the model will already have been made. + * It's recommended for the operation to always return true if any modification has already been made to the model, + * as not doing that may cause the state to become desynced. + * + * @param reason The reason to display in the history + * @param operation The modification operation. + * @return The bool returned from the operation + */ + bool modifyDeck(const QString &reason, const std::function &operation); + + /** + * @brief Modifies the cards in the deck, in a wrapped operation that is saved to the history. + * + * The operation is a function that accepts a DeckListModel that it operates upon, and returns a QModelIndex. + * If the index is invalid, then the operation is considered to be a failure. + * + * See the other @link DeckStateManager::modifyDeck for more info about the behavior of this method. + * + * @param reason The reason to display in the history + * @param operation The modification operation. + * @return The QModelIndex returned from the operation + */ + QModelIndex modifyDeck(const QString &reason, const std::function &operation); + + /// @name Metadata setters + /// @brief These methods set the metadata. Will no-op if the new value is the same as the current value. + /// Saves the operation to history if successful. + ///@{ + void setName(const QString &name); + void setComments(const QString &comments); + void setBannerCard(const CardRef &bannerCard); + void setTags(const QStringList &tags); + void setFormat(const QString &format); + ///@} + + /** + * @brief Adds the given card to the given zone. + * Saves the operation to history if successful. + * + * @param card The card to add + * @param zoneName The zone to add the card to + * @return The index of the added card + */ + QModelIndex addCard(const ExactCard &card, const QString &zoneName); + + /** + * @brief Removes 1 copy of the given card from the given zone. + * Saves the operation to history if successful. + * + * @param card The card to remove + * @param zoneName The zone to remove the card from + * @return The index of the removed card. Will be invalid if the last copy was removed. + */ + QModelIndex decrementCard(const ExactCard &card, const QString &zoneName); + + /** + * @brief Swaps one copy of the card at the given index between the maindeck and sideboard. + * No-ops if index is invalid or not a card node. + * Saves the operation to history if successful. + * + * @param idx The model index + * @return Whether the operation was successfully performed + */ + bool swapCardAtIndex(const QModelIndex &idx); + + /** + * @brief Removes all copies of the card at the given index. + * No-ops if index is invalid or not a card node. + * Saves the operation to history if successful. + * + * @param idx The model index + * @return Whether the operation was successfully performed + */ + bool removeCardAtIndex(const QModelIndex &idx); + + /** + * @brief Increments the number of copies of the card at the given index by 1. + * No-ops if index is invalid or not a card node. + * Saves the operation to history if successful. + * + * @param idx The model index + * @return Whether the operation was successfully performed + */ + bool incrementCountAtIndex(const QModelIndex &idx); + + /** + * @brief Decrements the number of copies of the card at the given index by 1. + * No-ops if index is invalid or not a card node. + * Saves the operation to history if successful. + * + * @param idx The model index + * @return Whether the operation was successfully performed + */ + bool decrementCountAtIndex(const QModelIndex &idx); + + /** + * Undoes n steps of the history, setting the decklist state and updating the current step in the historyManager. + * @param steps Number of steps to undo. + */ + void undo(int steps = 1); + + /** + * Redoes n steps of the history, setting the decklist state and updating the current step in the historyManager. + * @param steps Number of steps to redo. + */ + void redo(int steps = 1); + +public slots: + /** + * Saves the current decklist state to history. + * @param reason The reason that is shown in the history. + */ + void requestHistorySave(const QString &reason); + +private: + bool offsetCountAtIndex(const QModelIndex &idx, int offset); + void doCardModified(); + void doMetadataModified(); + +signals: + /** + * A modification has been made to the cards in the deck + */ + void cardModified(); + + /** + * A card that wasn't previously in the deck was added to the deck, or the last copy of a card was removed from the + * deck. + */ + void uniqueCardsChanged(); + + /** + * A modification has been made to the metadata in the deck + */ + void metadataModified(); + + /** + * A modification has been made to the cards or metadata in the deck + */ + void deckModified(); + + /** + * The history has been greatly changed and needs to be reloaded. + */ + void historyChanged(); + + /** + * The deck has been completely changed. + */ + void deckReplaced(); + + /** + * The isModified state of the deck has changed + * @param isModified the new state + */ + void isModifiedChanged(bool isModified); + + /** + * The selected card on any views connected to this deck should be changed to this index. + * @param index The model index + */ + void focusIndexChanged(QModelIndex index); +}; + +#endif // COCKATRICE_DECK_STATE_MANAGER_H \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp index 587407065..6ed6b67a4 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp @@ -2,6 +2,7 @@ #include "../../deck_loader/card_node_function.h" #include "../../deck_loader/deck_loader.h" +#include "../deck_editor/deck_state_manager.h" #include "../interface/widgets/cards/card_info_picture_widget.h" #include "../interface/widgets/general/layout_containers/flow_widget.h" @@ -21,7 +22,8 @@ #include #include -DlgSelectSetForCards::DlgSelectSetForCards(QWidget *parent, DeckListModel *_model) : QDialog(parent), model(_model) +DlgSelectSetForCards::DlgSelectSetForCards(QWidget *parent, DeckStateManager *deckStateManger) + : QDialog(parent), deckStateManager(deckStateManger) { setMinimumSize(500, 500); setAcceptDrops(true); @@ -165,36 +167,39 @@ void DlgSelectSetForCards::actOK() if (modifiedSetsAndCardsMap.isEmpty()) { accept(); // Nothing to do - } else { - emit deckAboutToBeModified(tr("Bulk modified printings.")); + return; } - for (QString modifiedSet : modifiedSetsAndCardsMap.keys()) { - for (QString card : modifiedSetsAndCardsMap.value(modifiedSet)) { - swapPrinting(model, modifiedSet, card); + auto bulkModify = [&modifiedSetsAndCardsMap](DeckListModel *model) { + for (QString modifiedSet : modifiedSetsAndCardsMap.keys()) { + for (QString card : modifiedSetsAndCardsMap.value(modifiedSet)) { + swapPrinting(model, modifiedSet, card); + } } - } + return true; + }; + + deckStateManager->modifyDeck(tr("Bulk modified printings."), bulkModify); - if (!modifiedSetsAndCardsMap.isEmpty()) { - emit deckModified(); - } accept(); } void DlgSelectSetForCards::actClear() { - emit deckAboutToBeModified(tr("Cleared all printing information.")); - model->forEachCard(CardNodeFunction::ClearPrintingData()); - emit deckModified(); + deckStateManager->modifyDeck(tr("Cleared all printing information."), [](auto model) { + model->forEachCard(CardNodeFunction::ClearPrintingData()); + return true; + }); accept(); } void DlgSelectSetForCards::actSetAllToPreferred() { - emit deckAboutToBeModified(tr("Set all printings to preferred.")); - model->forEachCard(CardNodeFunction::ClearPrintingData()); - model->forEachCard(CardNodeFunction::SetProviderIdToPreferred()); - emit deckModified(); + deckStateManager->modifyDeck(tr("Set all printings to preferred."), [](auto model) { + model->forEachCard(CardNodeFunction::ClearPrintingData()); + model->forEachCard(CardNodeFunction::SetProviderIdToPreferred()); + return true; + }); accept(); } @@ -227,10 +232,8 @@ void DlgSelectSetForCards::sortSetsByCount() QMap DlgSelectSetForCards::getSetsForCards() { QMap setCounts; - if (!model) - return setCounts; - QList cardNames = model->getCardNames(); + QList cardNames = deckStateManager->getModel()->getCardNames(); for (auto cardName : cardNames) { CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName); @@ -269,7 +272,7 @@ void DlgSelectSetForCards::updateCardLists() } } - QList cardNames = model->getCardNames(); + QList cardNames = deckStateManager->getModel()->getCardNames(); for (auto cardName : cardNames) { bool found = false; @@ -351,10 +354,8 @@ void DlgSelectSetForCards::dropEvent(QDropEvent *event) QMap DlgSelectSetForCards::getCardsForSets() { QMap setCards; - if (!model) - return setCards; - QList cardNames = model->getCardNames(); + QList cardNames = deckStateManager->getModel()->getCardNames(); for (auto cardName : cardNames) { CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.h b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.h index 5cdef5a30..795366b57 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.h +++ b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.h @@ -18,6 +18,7 @@ #include #include +class DeckStateManager; class SetEntryWidget; // Forward declaration class DlgSelectSetForCards : public QDialog @@ -25,7 +26,7 @@ class DlgSelectSetForCards : public QDialog Q_OBJECT public: - explicit DlgSelectSetForCards(QWidget *parent, DeckListModel *_model); + explicit DlgSelectSetForCards(QWidget *parent, DeckStateManager *deckStateManager); void retranslateUi(); void sortSetsByCount(); QMap getCardsForSets(); @@ -37,7 +38,6 @@ public: signals: void widgetOrderChanged(); void orderChanged(); - void deckAboutToBeModified(const QString &reason); void deckModified(); public slots: @@ -61,7 +61,7 @@ private: QLabel *modifiedCardsLabel; QWidget *listContainer; QListWidget *listWidget; - DeckListModel *model; + DeckStateManager *deckStateManager; QMap setEntries; QPushButton *clearButton; QPushButton *setAllToPreferredButton; diff --git a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp index d8bd88b37..b1346e4fd 100644 --- a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp @@ -11,16 +11,12 @@ * UI elements for managing card counts in both the mainboard and sideboard zones. * * @param parent The parent widget. - * @param deckEditor Pointer to the TabDeckEditor. - * @param deckModel Pointer to the DeckListModel. - * @param deckView Pointer to the QTreeView for the deck display. + * @param deckStateManager Pointer to the DeckStateManager * @param cardSizeSlider Pointer to the QSlider used for dynamic font resizing. * @param rootCard The root card for the widget. */ AllZonesCardAmountWidget::AllZonesCardAmountWidget(QWidget *parent, - AbstractTabDeckEditor *deckEditor, - DeckListModel *deckModel, - QTreeView *deckView, + DeckStateManager *deckStateManager, QSlider *cardSizeSlider, const ExactCard &rootCard) : QWidget(parent), cardSizeSlider(cardSizeSlider) @@ -32,11 +28,9 @@ AllZonesCardAmountWidget::AllZonesCardAmountWidget(QWidget *parent, setContentsMargins(5, 5, 5, 5); // Padding around the text zoneLabelMainboard = new ShadowBackgroundLabel(this, tr("Mainboard")); - buttonBoxMainboard = - new CardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard, DECK_ZONE_MAIN); + buttonBoxMainboard = new CardAmountWidget(this, deckStateManager, cardSizeSlider, rootCard, DECK_ZONE_MAIN); zoneLabelSideboard = new ShadowBackgroundLabel(this, tr("Sideboard")); - buttonBoxSideboard = - new CardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard, DECK_ZONE_SIDE); + buttonBoxSideboard = new CardAmountWidget(this, deckStateManager, cardSizeSlider, rootCard, DECK_ZONE_SIDE); layout->addWidget(zoneLabelMainboard, 0, Qt::AlignHCenter | Qt::AlignBottom); layout->addWidget(buttonBoxMainboard, 0, Qt::AlignHCenter | Qt::AlignTop); diff --git a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h index 6ce10cf2e..5a03c5f4a 100644 --- a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h +++ b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h @@ -18,9 +18,7 @@ class AllZonesCardAmountWidget : public QWidget Q_OBJECT public: explicit AllZonesCardAmountWidget(QWidget *parent, - AbstractTabDeckEditor *deckEditor, - DeckListModel *deckModel, - QTreeView *deckView, + DeckStateManager *deckStateManager, QSlider *cardSizeSlider, const ExactCard &rootCard); int getMainboardAmount(); diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp index d01725cc4..62c8e2d60 100644 --- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp @@ -1,5 +1,7 @@ #include "card_amount_widget.h" +#include "../deck_editor/deck_state_manager.h" + #include #include @@ -7,22 +9,17 @@ * @brief Constructs a widget for displaying and controlling the card count in a specific zone. * * @param parent The parent widget. - * @param deckEditor Pointer to the TabDeckEditor instance. - * @param deckModel Pointer to the DeckListModel instance. - * @param deckView Pointer to the QTreeView displaying the deck. * @param cardSizeSlider Pointer to the QSlider for adjusting font size. * @param rootCard The root card to manage within the widget. * @param zoneName The zone name (e.g., DECK_ZONE_MAIN or DECK_ZONE_SIDE). */ CardAmountWidget::CardAmountWidget(QWidget *parent, - AbstractTabDeckEditor *deckEditor, - DeckListModel *deckModel, - QTreeView *deckView, + DeckStateManager *deckStateManager, QSlider *cardSizeSlider, const ExactCard &rootCard, const QString &zoneName) - : QWidget(parent), deckEditor(deckEditor), deckModel(deckModel), deckView(deckView), cardSizeSlider(cardSizeSlider), - rootCard(rootCard), zoneName(zoneName), hovered(false) + : QWidget(parent), deckStateManager(deckStateManager), cardSizeSlider(cardSizeSlider), rootCard(rootCard), + zoneName(zoneName), hovered(false) { layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -56,15 +53,10 @@ CardAmountWidget::CardAmountWidget(QWidget *parent, layout->addWidget(incrementButton); // React to model changes - connect(deckModel, &DeckListModel::dataChanged, this, &CardAmountWidget::updateCardCount); - connect(deckModel, &QAbstractItemModel::rowsRemoved, this, &CardAmountWidget::updateCardCount); + connect(deckStateManager, &DeckStateManager::cardModified, this, &CardAmountWidget::updateCardCount); // Connect slider for dynamic font size adjustment connect(cardSizeSlider, &QSlider::valueChanged, this, &CardAmountWidget::adjustFontSize); - - if (deckEditor) { - connect(this, &CardAmountWidget::deckModified, deckEditor, &AbstractTabDeckEditor::onDeckHistorySaveRequested); - } } /** @@ -168,7 +160,7 @@ static QModelIndex addAndReplacePrintings(DeckListModel *model, void CardAmountWidget::addPrinting(const QString &zone) { // Check if we will need to add extra copies due to replacing copies without providerIds - QModelIndex existing = deckModel->findCard(rootCard.getName(), zone); + QModelIndex existing = deckStateManager->getModel()->findCard(rootCard.getName(), zone); int extraCopies = 0; bool replacingProviderless = false; @@ -192,15 +184,13 @@ void CardAmountWidget::addPrinting(const QString &zone) .arg(rootCard.getPrinting().getUuid()) .arg(replacingProviderless ? " (replaced providerless printings)" : ""); - emit deckModified(reason); - // Add the card and expand the list UI - auto newCardIndex = addAndReplacePrintings(deckModel, existing, rootCard, zone, extraCopies, replacingProviderless); + QModelIndex newCardIndex = deckStateManager->modifyDeck(reason, [&](auto model) { + return addAndReplacePrintings(model, existing, rootCard, zone, extraCopies, replacingProviderless); + }); if (newCardIndex.isValid()) { - deckView->setCurrentIndex(newCardIndex); - deckView->setFocus(Qt::FocusReason::MouseFocusReason); - deckEditor->setModified(true); + emit deckStateManager->focusIndexChanged(newCardIndex); } } @@ -250,13 +240,11 @@ void CardAmountWidget::decrementCardHelper(const QString &zone) .arg(zone == DECK_ZONE_MAIN ? "mainboard" : "sideboard") .arg(rootCard.getPrinting().getUuid()); - emit deckModified(reason); - - QModelIndex idx = deckModel->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(), + deckStateManager->modifyDeck(reason, [this, &zone](auto model) { + QModelIndex idx = model->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(), rootCard.getPrinting().getProperty("num")); - - deckModel->offsetCountAtIndex(idx, -1); - deckEditor->setModified(true); + return model->offsetCountAtIndex(idx, -1); + }); } /** @@ -273,7 +261,7 @@ int CardAmountWidget::countCardsInZone(const QString &deckZone) return 0; // Cards without uuids/providerIds CANNOT match another card, they are undefined for us. } - QList cards = deckModel->getCardsForZone(deckZone); + QList cards = deckStateManager->getModel()->getCardsForZone(deckZone); return std::count_if(cards.cbegin(), cards.cend(), [&uuid](const ExactCard &card) { return card.getPrinting().getUuid() == uuid; }); diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h index b4704cede..983416782 100644 --- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h +++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h @@ -27,9 +27,7 @@ signals: public: explicit CardAmountWidget(QWidget *parent, - AbstractTabDeckEditor *deckEditor, - DeckListModel *deckModel, - QTreeView *deckView, + DeckStateManager *deckStateManager, QSlider *cardSizeSlider, const ExactCard &rootCard, const QString &zoneName); @@ -44,9 +42,7 @@ protected: void showEvent(QShowEvent *event) override; private: - AbstractTabDeckEditor *deckEditor; - DeckListModel *deckModel; - QTreeView *deckView; + DeckStateManager *deckStateManager; QSlider *cardSizeSlider; ExactCard rootCard; QString zoneName; diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp index e64d6a009..95d6b2cdf 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp @@ -3,6 +3,7 @@ #include "../../../client/settings/cache_settings.h" #include "../../../interface/card_picture_loader/card_picture_loader.h" #include "../../../interface/widgets/dialogs/dlg_select_set_for_cards.h" +#include "../deck_editor/deck_state_manager.h" #include "printing_selector_card_display_widget.h" #include "printing_selector_card_search_widget.h" #include "printing_selector_card_selection_widget.h" @@ -21,12 +22,9 @@ * * @param parent The parent widget for the PrintingSelector. * @param deckEditor The TabDeckEditor instance used for managing the deck. - * @param deckModel The DeckListModel instance that provides data for the deck's contents. - * @param deckView The QTreeView instance used to display the deck and its contents. */ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deckEditor) - : QWidget(parent), deckEditor(_deckEditor), deckModel(deckEditor->deckDockWidget->deckModel), - deckView(deckEditor->deckDockWidget->deckView) + : QWidget(parent), deckEditor(_deckEditor), deckStateManager(_deckEditor->deckStateManager) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); layout = new QVBoxLayout(this); @@ -74,13 +72,12 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck layout->addWidget(flowWidget); - cardSelectionBar = new PrintingSelectorCardSelectionWidget(this); + cardSelectionBar = new PrintingSelectorCardSelectionWidget(this, deckStateManager); cardSelectionBar->setVisible(SettingsCache::instance().getPrintingSelectorNavigationButtonsVisible()); layout->addWidget(cardSelectionBar); // Connect deck model data change signal to update display - connect(deckModel, &DeckListModel::rowsInserted, this, &PrintingSelector::printingsInDeckChanged); - connect(deckModel, &DeckListModel::rowsRemoved, this, &PrintingSelector::printingsInDeckChanged); + connect(deckStateManager, &DeckStateManager::uniqueCardsChanged, this, &PrintingSelector::printingsInDeckChanged); retranslateUi(); } @@ -152,7 +149,8 @@ void PrintingSelector::getAllSetsForCurrentCard() QList printingsToUse; if (SettingsCache::instance().getBumpSetsWithCardsInDeckToTop()) { - printingsToUse = sortToolBar->prependPrintingsInDeck(filteredPrintings, selectedCard, deckModel); + printingsToUse = + sortToolBar->prependPrintingsInDeck(filteredPrintings, selectedCard, deckStateManager->getModel()); } else { printingsToUse = filteredPrintings; } @@ -164,7 +162,7 @@ void PrintingSelector::getAllSetsForCurrentCard() connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=, this]() mutable { for (int i = 0; i < BATCH_SIZE && currentIndex < printingsToUse.size(); ++i, ++currentIndex) { auto card = ExactCard(selectedCard, printingsToUse[currentIndex]); - auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(this, deckEditor, deckModel, deckView, + auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(this, deckEditor, deckStateManager, cardSizeWidget->getSlider(), card); flowWidget->addWidget(cardDisplayWidget); cardDisplayWidget->clampSetNameToPicture(); diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector.h index e34ce3fe6..e1c07addf 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector.h +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector.h @@ -21,6 +21,7 @@ #define BATCH_SIZE 10 +class DeckStateManager; class PrintingSelectorCardSearchWidget; class PrintingSelectorCardSelectionWidget; class PrintingSelectorCardSortingWidget; @@ -35,15 +36,6 @@ public: void setCard(const CardInfoPtr &newCard); void getAllSetsForCurrentCard(); - [[nodiscard]] DeckListModel *getDeckModel() const - { - return deckModel; - } - - [[nodiscard]] AbstractTabDeckEditor *getDeckEditor() const - { - return deckEditor; - } public slots: void retranslateUi(); @@ -75,8 +67,7 @@ private: CardSizeWidget *cardSizeWidget; PrintingSelectorCardSelectionWidget *cardSelectionBar; AbstractTabDeckEditor *deckEditor; - DeckListModel *deckModel; - QTreeView *deckView; + DeckStateManager *deckStateManager; CardInfoPtr selectedCard; QTimer *widgetLoadingBufferTimer; int currentIndex = 0; diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp index 86d6659a8..92cf2437c 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp @@ -18,15 +18,13 @@ * * @param parent The parent widget for this display. * @param deckEditor The TabDeckEditor instance for deck management. - * @param deckModel The DeckListModel instance providing deck data. - * @param deckView The QTreeView instance displaying the deck. + * @param deckStateManager The DeckStateManager instance providing deck data. * @param cardSizeSlider The slider controlling the size of the displayed card. * @param rootCard The root card object, representing the card to be displayed. */ PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *parent, AbstractTabDeckEditor *deckEditor, - DeckListModel *deckModel, - QTreeView *deckView, + DeckStateManager *deckStateManager, QSlider *cardSizeSlider, const ExactCard &rootCard) : QWidget(parent) @@ -36,8 +34,7 @@ PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *pa setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // Create the overlay widget for the card display - overlayWidget = - new PrintingSelectorCardOverlayWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard); + overlayWidget = new PrintingSelectorCardOverlayWidget(this, deckEditor, deckStateManager, cardSizeSlider, rootCard); connect(overlayWidget, &PrintingSelectorCardOverlayWidget::cardPreferenceChanged, this, [this]() { emit cardPreferenceChanged(); }); diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h index 608c2df5c..2637b0e57 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h @@ -21,8 +21,7 @@ class PrintingSelectorCardDisplayWidget : public QWidget public: PrintingSelectorCardDisplayWidget(QWidget *parent, AbstractTabDeckEditor *deckEditor, - DeckListModel *deckModel, - QTreeView *deckView, + DeckStateManager *deckStateManager, QSlider *cardSizeSlider, const ExactCard &rootCard); diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp index 04ab07a59..ac36f2cf4 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp @@ -22,15 +22,13 @@ * * @param parent The parent widget for this overlay. * @param _deckEditor The TabDeckEditor instance for deck management. - * @param deckModel The DeckListModel instance providing deck data. - * @param deckView The QTreeView instance displaying the deck. + * @param deckStateManager The DeckStateManager instance providing deck data. * @param cardSizeSlider The slider controlling the size of the card. * @param _rootCard The root card object that contains information about the card. */ PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *parent, AbstractTabDeckEditor *_deckEditor, - DeckListModel *deckModel, - QTreeView *deckView, + DeckStateManager *deckStateManager, QSlider *cardSizeSlider, const ExactCard &_rootCard) : QWidget(parent), deckEditor(_deckEditor), rootCard(_rootCard) @@ -58,8 +56,7 @@ PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *pa updatePinBadgeVisibility(); // Add AllZonesCardAmountWidget - allZonesCardAmountWidget = - new AllZonesCardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, _rootCard); + allZonesCardAmountWidget = new AllZonesCardAmountWidget(this, deckStateManager, cardSizeSlider, _rootCard); allZonesCardAmountWidget->raise(); // Ensure it's on top of the picture // Set initial visibility based on amounts diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h index 3bd5ce247..8550612bd 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h @@ -20,8 +20,7 @@ class PrintingSelectorCardOverlayWidget : public QWidget public: explicit PrintingSelectorCardOverlayWidget(QWidget *parent, AbstractTabDeckEditor *_deckEditor, - DeckListModel *_deckModel, - QTreeView *_deckView, + DeckStateManager *_deckStateManager, QSlider *_cardSizeSlider, const ExactCard &_rootCard); diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp index 317ce83b6..2eb2ef245 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp @@ -11,7 +11,9 @@ * * @param parent The parent PrintingSelector widget responsible for managing card selection. */ -PrintingSelectorCardSelectionWidget::PrintingSelectorCardSelectionWidget(PrintingSelector *parent) : parent(parent) +PrintingSelectorCardSelectionWidget::PrintingSelectorCardSelectionWidget(PrintingSelector *parent, + DeckStateManager *deckStateManager) + : parent(parent), deckStateManager(deckStateManager) { cardSelectionBarLayout = new QHBoxLayout(this); cardSelectionBarLayout->setContentsMargins(9, 0, 9, 0); @@ -48,12 +50,6 @@ void PrintingSelectorCardSelectionWidget::connectSignals() void PrintingSelectorCardSelectionWidget::selectSetForCards() { - auto *setSelectionDialog = new DlgSelectSetForCards(nullptr, parent->getDeckModel()); - connect(setSelectionDialog, &DlgSelectSetForCards::deckAboutToBeModified, parent->getDeckEditor(), - &AbstractTabDeckEditor::onDeckHistorySaveRequested); - connect(setSelectionDialog, &DlgSelectSetForCards::deckModified, parent->getDeckEditor(), - &AbstractTabDeckEditor::onDeckModified); - if (!setSelectionDialog->exec()) { - return; - } + auto *setSelectionDialog = new DlgSelectSetForCards(nullptr, deckStateManager); + setSelectionDialog->exec(); } diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.h index a1176a76c..ecd5c83e3 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.h +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.h @@ -18,7 +18,7 @@ class PrintingSelectorCardSelectionWidget : public QWidget Q_OBJECT public: - explicit PrintingSelectorCardSelectionWidget(PrintingSelector *parent); + explicit PrintingSelectorCardSelectionWidget(PrintingSelector *parent, DeckStateManager *deckStateManager); void connectSignals(); @@ -27,6 +27,7 @@ public slots: private: PrintingSelector *parent; + DeckStateManager *deckStateManager; QHBoxLayout *cardSelectionBarLayout; QPushButton *previousCardButton; QPushButton *selectSetForCardsButton; diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp index af7cedbeb..725e5df90 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp @@ -183,7 +183,7 @@ QList PrintingSelectorCardSortingWidget::prependPinnedPrintings(co */ QList PrintingSelectorCardSortingWidget::prependPrintingsInDeck(const QList &printings, const CardInfoPtr &selectedCard, - DeckListModel *deckModel) + const DeckListModel *deckModel) { if (!selectedCard) { return {}; diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.h index d0faea4ac..b5a00b81e 100644 --- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.h +++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.h @@ -23,7 +23,7 @@ public: QList prependPinnedPrintings(const QList &printings, const QString &cardName); QList prependPrintingsInDeck(const QList &printings, const CardInfoPtr &selectedCard, - DeckListModel *deckModel); + const DeckListModel *deckModel); public slots: void updateSortOrder(); diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp index cb2002199..29bcc1ab1 100644 --- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp +++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp @@ -12,6 +12,7 @@ #include "../../../client/settings/cache_settings.h" #include "../client/network/interfaces/deck_stats_interface.h" #include "../client/network/interfaces/tapped_out_interface.h" +#include "../deck_editor/deck_state_manager.h" #include "../interface/card_picture_loader/card_picture_loader.h" #include "../interface/pixel_map_generator.h" #include "../interface/widgets/dialogs/dlg_load_deck.h" @@ -52,7 +53,7 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta { setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks); - historyManager = new DeckListHistoryManager(this); + deckStateManager = new DeckStateManager(this); databaseDisplayDockWidget = new DeckEditorDatabaseDisplayWidget(this); deckDockWidget = new DeckEditorDeckDockWidget(this); @@ -64,14 +65,8 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta }); // Connect deck signals to this tab - connect(deckDockWidget, &DeckEditorDeckDockWidget::deckChanged, this, &AbstractTabDeckEditor::onDeckChanged); - connect(deckDockWidget, &DeckEditorDeckDockWidget::deckModified, this, &AbstractTabDeckEditor::onDeckModified); - connect(deckDockWidget, &DeckEditorDeckDockWidget::requestDeckHistorySave, this, - &AbstractTabDeckEditor::onDeckHistorySaveRequested); - connect(deckDockWidget, &DeckEditorDeckDockWidget::requestDeckHistoryClear, this, - &AbstractTabDeckEditor::onDeckHistoryClearRequested); - connect(deckDockWidget, &DeckEditorDeckDockWidget::cardChanged, this, &AbstractTabDeckEditor::updateCard); - connect(this, &AbstractTabDeckEditor::decrementCard, deckDockWidget, &DeckEditorDeckDockWidget::actDecrementCard); + connect(deckStateManager, &DeckStateManager::isModifiedChanged, this, &AbstractTabDeckEditor::onDeckModified); + connect(deckDockWidget, &DeckEditorDeckDockWidget::selectedCardChanged, this, &AbstractTabDeckEditor::updateCard); // Connect database display signals to this tab connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this, @@ -107,7 +102,6 @@ void AbstractTabDeckEditor::updateCard(const ExactCard &card) /** @brief Placeholder: called when the deck changes. */ void AbstractTabDeckEditor::onDeckChanged() { - historyManager->clear(); } /** @@ -115,24 +109,8 @@ void AbstractTabDeckEditor::onDeckChanged() */ void AbstractTabDeckEditor::onDeckModified() { - setModified(!isBlankNewDeck()); - deckMenu->setSaveStatus(!isBlankNewDeck()); -} - -/** - * @brief Marks the tab as modified and updates the save menu status. - */ -void AbstractTabDeckEditor::onDeckHistorySaveRequested(const QString &modificationReason) -{ - historyManager->save(deckDockWidget->getDeckList().createMemento(modificationReason)); -} - -/** - * @brief Marks the tab as modified and updates the save menu status. - */ -void AbstractTabDeckEditor::onDeckHistoryClearRequested() -{ - historyManager->clear(); + deckMenu->setSaveStatus(!deckStateManager->isBlankNewDeck()); + emit tabTextChanged(this, getTabText()); } /** @@ -142,7 +120,7 @@ void AbstractTabDeckEditor::onDeckHistoryClearRequested() */ void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, const QString &zoneName) { - deckDockWidget->actAddCard(card, zoneName); + deckStateManager->addCard(card, zoneName); databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length()); } @@ -170,13 +148,13 @@ void AbstractTabDeckEditor::actAddCardToSideboard(const ExactCard &card) /** @brief Decrements a card from the main deck. */ void AbstractTabDeckEditor::actDecrementCard(const ExactCard &card) { - emit decrementCard(card, DECK_ZONE_MAIN); + deckStateManager->decrementCard(card, DECK_ZONE_MAIN); } /** @brief Decrements a card from the sideboard. */ void AbstractTabDeckEditor::actDecrementCardFromSideboard(const ExactCard &card) { - emit decrementCard(card, DECK_ZONE_SIDE); + deckStateManager->decrementCard(card, DECK_ZONE_SIDE); } /** @@ -198,45 +176,13 @@ void AbstractTabDeckEditor::openDeck(const LoadedDeck &deck) */ void AbstractTabDeckEditor::setDeck(const LoadedDeck &_deck) { - deckDockWidget->setDeck(_deck); - CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(getDeckList().getCardRefList())); - setModified(false); + deckStateManager->replaceDeck(_deck); + CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(_deck.deckList.getCardRefList())); aDeckDockVisible->setChecked(true); deckDockWidget->setVisible(aDeckDockVisible->isChecked()); } -/** @brief Returns the currently loaded deck. */ -DeckLoader *AbstractTabDeckEditor::getDeckLoader() const -{ - return deckDockWidget->getDeckLoader(); -} - -/** @brief Returns the currently loaded deck list. */ -const DeckList &AbstractTabDeckEditor::getDeckList() const -{ - return deckDockWidget->getDeckList(); -} - -/** - * @brief Sets the modified state of the tab. - * @param _modified True if tab is modified, false otherwise. - */ -void AbstractTabDeckEditor::setModified(bool _modified) -{ - modified = _modified; - emit tabTextChanged(this, getTabText()); -} - -/** - * @brief Returns true if the tab is a blank newly created deck. - */ -bool AbstractTabDeckEditor::isBlankNewDeck() const -{ - const LoadedDeck &loadedDeck = deckDockWidget->getDeckLoader()->getDeck(); - return !modified && loadedDeck.isEmpty(); -} - /** @brief Creates a new deck. Handles opening in new tab if needed. */ void AbstractTabDeckEditor::actNewDeck() { @@ -255,9 +201,8 @@ void AbstractTabDeckEditor::actNewDeck() /** @brief Clears the current deck and resets modified flag. */ void AbstractTabDeckEditor::cleanDeckAndResetModified() { + deckStateManager->clearDeck(); deckMenu->setSaveStatus(false); - deckDockWidget->cleanDeck(); - setModified(false); } /** @@ -268,13 +213,13 @@ void AbstractTabDeckEditor::cleanDeckAndResetModified() AbstractTabDeckEditor::DeckOpenLocation AbstractTabDeckEditor::confirmOpen(const bool openInSameTabIfBlank) { if (SettingsCache::instance().getOpenDeckInNewTab()) { - if (openInSameTabIfBlank && isBlankNewDeck()) + if (openInSameTabIfBlank && deckStateManager->isBlankNewDeck()) return SAME_TAB; else return NEW_TAB; } - if (!modified) + if (!deckStateManager->isModified()) return SAME_TAB; tabSupervisor->setCurrentWidget(this); @@ -325,7 +270,6 @@ void AbstractTabDeckEditor::actLoadDeck() QString fileName = dialog.selectedFiles().at(0); openDeckFromFile(fileName, deckOpenLocation); - deckDockWidget->updateBannerCardComboBox(); } /** @@ -371,7 +315,7 @@ void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLo */ bool AbstractTabDeckEditor::actSaveDeck() { - const LoadedDeck &loadedDeck = getDeckLoader()->getDeck(); + const auto loadedDeck = deckStateManager->toLoadedDeck(); if (loadedDeck.lastLoadInfo.remoteDeckId != LoadedDeck::LoadInfo::NON_REMOTE_ID) { QString deckString = loadedDeck.deckList.writeToString_Native(); if (deckString.length() > MAX_FILE_LENGTH) { @@ -392,8 +336,10 @@ bool AbstractTabDeckEditor::actSaveDeck() if (loadedDeck.lastLoadInfo.fileName.isEmpty()) return actSaveDeckAs(); - if (getDeckLoader()->saveToFile(loadedDeck.lastLoadInfo.fileName, loadedDeck.lastLoadInfo.fileFormat)) { - setModified(false); + auto deckLoader = DeckLoader(this); + deckLoader.setDeck(loadedDeck); + if (deckLoader.saveToFile(loadedDeck.lastLoadInfo.fileName, loadedDeck.lastLoadInfo.fileFormat)) { + deckStateManager->setModified(false); return true; } @@ -409,12 +355,14 @@ bool AbstractTabDeckEditor::actSaveDeck() */ bool AbstractTabDeckEditor::actSaveDeckAs() { + LoadedDeck loadedDeck = deckStateManager->toLoadedDeck(); + QFileDialog dialog(this, tr("Save deck")); dialog.setDirectory(SettingsCache::instance().getDeckPath()); dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setDefaultSuffix("cod"); dialog.setNameFilters(DeckLoader::FILE_NAME_FILTERS); - dialog.selectFile(getDeckList().getName().trimmed()); + dialog.selectFile(loadedDeck.deckList.getName().trimmed()); if (!dialog.exec()) return false; @@ -422,14 +370,18 @@ bool AbstractTabDeckEditor::actSaveDeckAs() QString fileName = dialog.selectedFiles().at(0); DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(fileName); - if (!getDeckLoader()->saveToFile(fileName, fmt)) { + DeckLoader deckLoader = DeckLoader(this); + deckLoader.setDeck(loadedDeck); + if (!deckLoader.saveToFile(fileName, fmt)) { QMessageBox::critical( this, tr("Error"), tr("The deck could not be saved.\nPlease check that the directory is writable and try again.")); return false; } - setModified(false); + deckStateManager->setLastLoadInfo({.fileName = fileName, .fileFormat = fmt}); + + deckStateManager->setModified(false); SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName); return true; } @@ -443,7 +395,7 @@ void AbstractTabDeckEditor::saveDeckRemoteFinished(const Response &response) if (response.response_code() != Response::RespOk) QMessageBox::critical(this, tr("Error"), tr("The deck could not be saved.")); else - setModified(false); + deckStateManager->setModified(false); } /** @@ -464,7 +416,7 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard() emit openDeckEditor({.deckList = dlg.getDeckList()}); } else { setDeck({.deckList = dlg.getDeckList()}); - setModified(true); + deckStateManager->setModified(true); } deckMenu->setSaveStatus(true); @@ -476,12 +428,13 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard() */ void AbstractTabDeckEditor::editDeckInClipboard(bool annotated) { - DlgEditDeckInClipboard dlg(getDeckLoader()->getDeck().deckList, annotated, this); + LoadedDeck loadedDeck = deckStateManager->toLoadedDeck(); + DlgEditDeckInClipboard dlg(loadedDeck.deckList, annotated, this); if (!dlg.exec()) return; - setDeck({dlg.getDeckList(), getDeckLoader()->getDeck().lastLoadInfo}); - setModified(true); + setDeck({dlg.getDeckList(), loadedDeck.lastLoadInfo}); + deckStateManager->setModified(true); deckMenu->setSaveStatus(true); } @@ -500,25 +453,25 @@ void AbstractTabDeckEditor::actEditDeckInClipboardRaw() /** @brief Saves deck to clipboard with set info and annotation. */ void AbstractTabDeckEditor::actSaveDeckToClipboard() { - DeckLoader::saveToClipboard(getDeckList(), true, true); + DeckLoader::saveToClipboard(deckStateManager->getDeckList(), true, true); } /** @brief Saves deck to clipboard with annotation, without set info. */ void AbstractTabDeckEditor::actSaveDeckToClipboardNoSetInfo() { - DeckLoader::saveToClipboard(getDeckList(), true, false); + DeckLoader::saveToClipboard(deckStateManager->getDeckList(), true, false); } /** @brief Saves deck to clipboard without annotations, with set info. */ void AbstractTabDeckEditor::actSaveDeckToClipboardRaw() { - DeckLoader::saveToClipboard(getDeckList(), false, true); + DeckLoader::saveToClipboard(deckStateManager->getDeckList(), false, true); } /** @brief Saves deck to clipboard without annotations or set info. */ void AbstractTabDeckEditor::actSaveDeckToClipboardRawNoSetInfo() { - DeckLoader::saveToClipboard(getDeckList(), false, false); + DeckLoader::saveToClipboard(deckStateManager->getDeckList(), false, false); } /** @brief Prints the deck using a QPrintPreviewDialog. */ @@ -526,7 +479,7 @@ void AbstractTabDeckEditor::actPrintDeck() { auto *dlg = new QPrintPreviewDialog(this); connect(dlg, &QPrintPreviewDialog::paintRequested, this, - [this](QPrinter *printer) { DeckLoader::printDeckList(printer, getDeckList()); }); + [this](QPrinter *printer) { DeckLoader::printDeckList(printer, deckStateManager->getDeckList()); }); dlg->exec(); } @@ -547,7 +500,7 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite() emit openDeckEditor({.deckList = dlg.getDeck()}); } else { setDeck({.deckList = dlg.getDeck()}); - setModified(true); + deckStateManager->setModified(true); } deckMenu->setSaveStatus(true); @@ -559,7 +512,7 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite() */ void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite website) { - QString decklistUrlString = DeckLoader::exportDeckToDecklist(getDeckList(), website); + QString decklistUrlString = DeckLoader::exportDeckToDecklist(deckStateManager->getDeckList(), website); // Check to make sure the string isn't empty. if (decklistUrlString.isEmpty()) { // Show an error if the deck is empty, and return. @@ -592,14 +545,14 @@ void AbstractTabDeckEditor::actExportDeckDecklistXyz() void AbstractTabDeckEditor::actAnalyzeDeckDeckstats() { auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this); - interface->analyzeDeck(getDeckList()); + interface->analyzeDeck(deckStateManager->getDeckList()); } /** @brief Analyzes the deck using TappedOut. */ void AbstractTabDeckEditor::actAnalyzeDeckTappedout() { auto *interface = new TappedOutInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this); - interface->analyzeDeck(getDeckList()); + interface->analyzeDeck(deckStateManager->getDeckList()); } /** @brief Applies a new filter tree to the database display. */ @@ -658,7 +611,7 @@ bool AbstractTabDeckEditor::eventFilter(QObject *o, QEvent *e) /** @brief Shows a confirmation dialog before closing. */ bool AbstractTabDeckEditor::confirmClose() { - if (modified) { + if (deckStateManager->isModified()) { tabSupervisor->setCurrentWidget(this); int ret = createSaveConfirmationWindow()->exec(); if (ret == QMessageBox::Save) diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h index bfbda778c..69b2b4a1c 100644 --- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h +++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h @@ -19,6 +19,7 @@ #include +class DeckStateManager; class CardDatabaseModel; class CardDatabaseDisplayModel; @@ -117,30 +118,13 @@ public: */ void openDeck(const LoadedDeck &deck); - /** @brief Returns the currently active deck loader. */ - DeckLoader *getDeckLoader() const; - - /** @brief Returns the currently active deck list. */ - const DeckList &getDeckList() const; - - /** @brief Sets the modified state of the tab. - * @param _windowModified Whether the tab is modified. - */ - void setModified(bool _windowModified); - DeckEditorDeckDockWidget *getDeckDockWidget() const { return deckDockWidget; } - DeckListHistoryManager *getHistoryManager() const - { - return historyManager; - } - - DeckListHistoryManager *historyManager; - // UI Elements + DeckStateManager *deckStateManager; DeckEditorMenu *deckMenu; ///< Menu for deck operations DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget; ///< Database dock DeckEditorCardInfoDockWidget *cardInfoDockWidget; ///< Card info dock @@ -155,14 +139,6 @@ public slots: /** @brief Called when the deck is modified. */ virtual void onDeckModified(); - /** @brief Called when a widget is about to modify the state of the DeckList. - * @param modificationReason The reason for the state modification - */ - virtual void onDeckHistorySaveRequested(const QString &modificationReason); - - /** @brief Called when a widget would like to clear the history. */ - virtual void onDeckHistoryClearRequested(); - /** @brief Updates the card info panel. * @param card The card to display. */ @@ -202,9 +178,6 @@ signals: /** @brief Emitted before the tab is closed. */ void deckEditorClosing(AbstractTabDeckEditor *tab); - /** @brief Emitted when a card should be decremented. */ - void decrementCard(const ExactCard &card, QString zoneName); - protected slots: /** @brief Starts a new deck in this tab. */ virtual void actNewDeck(); @@ -315,9 +288,6 @@ protected: */ QMessageBox *createSaveConfirmationWindow(); - /** @brief Returns true if the tab is a blank newly created deck. */ - bool isBlankNewDeck() const; - /** @brief Helper function to add a card to a specific deck zone. */ void addCardHelper(const ExactCard &card, const QString &zoneName); @@ -330,8 +300,6 @@ protected: QAction *aResetLayout; QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating; QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating; - - bool modified = false; ///< Whether the deck/tab has unsaved changes }; #endif // TAB_GENERIC_DECK_EDITOR_H diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp index 1547cca74..3a2468368 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp @@ -68,7 +68,10 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi model = new DeckListModel(this); connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset); - model->getDeckList()->loadFromStream_Plain(deckStream, false); + + auto decklist = QSharedPointer(new DeckList); + decklist->loadFromStream_Plain(deckStream, false); + model->setDeckList(decklist); model->forEachCard(CardNodeFunction::ResolveProviderId()); diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp b/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp index 4e3d0c57d..286252e13 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp @@ -1,6 +1,7 @@ #include "tab_deck_editor.h" #include "../../../client/settings/cache_settings.h" +#include "../deck_editor/deck_state_manager.h" #include "../filters/filter_builder.h" #include "../interface/pixel_map_generator.h" #include "../interface/widgets/cards/card_info_frame_widget.h" @@ -114,8 +115,8 @@ void TabDeckEditor::createMenus() */ QString TabDeckEditor::getTabText() const { - QString result = tr("Deck: %1").arg(deckDockWidget->getSimpleDeckName()); - if (modified) + QString result = tr("Deck: %1").arg(deckStateManager->getSimpleDeckName()); + if (deckStateManager->isModified()) result.prepend("* "); return result; } diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp index a124eaa01..f3d573d27 100644 --- a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp +++ b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp @@ -1,6 +1,7 @@ #include "tab_deck_editor_visual.h" #include "../../../../client/settings/cache_settings.h" +#include "../../deck_editor/deck_state_manager.h" #include "../../filters/filter_builder.h" #include "../../interface/pixel_map_generator.h" #include "../../interface/widgets/cards/card_info_frame_widget.h" @@ -61,7 +62,7 @@ void TabDeckEditorVisual::createCentralFrame() centralFrame = new QVBoxLayout; centralWidget->setLayout(centralFrame); - tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, deckDockWidget->deckModel, + tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, deckStateManager->getModel(), databaseDisplayDockWidget->databaseModel, databaseDisplayDockWidget->databaseDisplayModel); @@ -85,7 +86,7 @@ void TabDeckEditorVisual::onDeckChanged() AbstractTabDeckEditor::onDeckModified(); tabContainer->visualDeckView->constructZoneWidgetsFromDeckListModel(); tabContainer->deckAnalytics->refreshDisplays(); - tabContainer->sampleHandWidget->setDeckModel(deckDockWidget->deckModel); + tabContainer->sampleHandWidget->setDeckModel(deckStateManager->getModel()); } /** @brief Creates menus for deck editing and view options, including dock actions. */ @@ -149,8 +150,8 @@ void TabDeckEditorVisual::createMenus() /** @brief Returns the tab text, prepending a mark if the deck has unsaved changes. */ QString TabDeckEditorVisual::getTabText() const { - QString result = tr("Visual Deck: %1").arg(deckDockWidget->getSimpleDeckName()); - if (modified) + QString result = tr("Visual Deck: %1").arg(deckStateManager->getSimpleDeckName()); + if (deckStateManager->isModified()) result.prepend("* "); return result; } @@ -166,9 +167,9 @@ void TabDeckEditorVisual::changeModelIndexAndCardInfo(const ExactCard &activeCar void TabDeckEditorVisual::changeModelIndexToCard(const ExactCard &activeCard) { QString cardName = activeCard.getName(); - QModelIndex index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_MAIN); + QModelIndex index = deckStateManager->getModel()->findCard(cardName, DECK_ZONE_MAIN); if (!index.isValid()) { - index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_SIDE); + index = deckStateManager->getModel()->findCard(cardName, DECK_ZONE_SIDE); } if (!deckDockWidget->getSelectionModel()->hasSelection()) { deckDockWidget->getSelectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); @@ -182,7 +183,7 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event, auto card = instance->getCard(); // Get the model index for the card - QModelIndex idx = deckDockWidget->deckModel->findCard(card.getName(), zoneName); + QModelIndex idx = deckStateManager->getModel()->findCard(card.getName(), zoneName); if (!idx.isValid()) { return; } @@ -191,8 +192,8 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event, // Double click = swap if (event->type() == QEvent::MouseButtonDblClick && event->button() == Qt::LeftButton) { - deckDockWidget->actSwapCard(card, zoneName); - idx = deckDockWidget->deckModel->findCard(card.getName(), zoneName); + deckStateManager->swapCardAtIndex(idx); + idx = deckStateManager->getModel()->findCard(card.getName(), zoneName); sel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); return; } diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp index 7551954f3..5098696dd 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp @@ -2,6 +2,7 @@ #include "../../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h" #include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h" +#include "../deck_editor/deck_state_manager.h" #include @@ -60,7 +61,7 @@ void VisualDatabaseDisplayNameFilterWidget::retranslateUi() void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck() { - DeckListModel *deckListModel = deckEditor->deckDockWidget->deckModel; + DeckListModel *deckListModel = deckEditor->deckStateManager->getModel(); if (!deckListModel) return; diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index 6f479616e..ece3bc2f8 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -5,14 +5,15 @@ DeckListModel::DeckListModel(QObject *parent) : QAbstractItemModel(parent), lastKnownColumn(1), lastKnownOrder(Qt::AscendingOrder) { - // This class will leak the decklist object. We cannot safely delete it in the dtor because the deckList field is a - // non-owning pointer and another deckList might have been assigned to it. - // `DeckListModel::cleanList` also leaks for the same reason. - // TODO: fix the leak - deckList = new DeckList; + deckList = QSharedPointer(new DeckList()); root = new InnerDecklistNode; } +DeckListModel::DeckListModel(QObject *parent, const QSharedPointer &deckList) : DeckListModel(parent) +{ + setDeckList(deckList); +} + DeckListModel::~DeckListModel() { delete root; @@ -586,13 +587,13 @@ void DeckListModel::setActiveFormat(const QString &_format) void DeckListModel::cleanList() { - setDeckList(new DeckList); + setDeckList(QSharedPointer(new DeckList())); } /** * @param _deck The deck. */ -void DeckListModel::setDeckList(DeckList *_deck) +void DeckListModel::setDeckList(const QSharedPointer &_deck) { if (deckList != _deck) { deckList = _deck; diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h index b6292d689..e6f10c072 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h @@ -245,6 +245,7 @@ signals: public: explicit DeckListModel(QObject *parent = nullptr); + explicit DeckListModel(QObject *parent, const QSharedPointer &deckList); ~DeckListModel() override; /** @@ -314,11 +315,12 @@ public: * @brief Removes all cards and resets the model. */ void cleanList(); - [[nodiscard]] DeckList *getDeckList() const + + [[nodiscard]] QSharedPointer getDeckList() const { return deckList; } - void setDeckList(DeckList *_deck); + void setDeckList(const QSharedPointer &_deck); /** * @brief Apply a function to every card in the deck tree. @@ -351,8 +353,8 @@ public: [[nodiscard]] QList getZones() const; private: - DeckList *deckList; /**< Pointer to the deck loader providing the underlying data. */ - InnerDecklistNode *root; /**< Root node of the model tree. */ + QSharedPointer deckList; /**< Pointer to the decklist providing the underlying data. */ + InnerDecklistNode *root; /**< Root node of the model tree. */ DeckListModelGroupCriteria::Type activeGroupCriteria = DeckListModelGroupCriteria::MAIN_TYPE; int lastKnownColumn; /**< Last column used for sorting. */ Qt::SortOrder lastKnownOrder; /**< Last known sort order. */ From 9f90de2242ec30e519169a00873ff5fac0f163b6 Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Wed, 31 Dec 2025 17:55:31 +0100 Subject: [PATCH 41/43] change the release channel based on version string (#6447) * change the release channel based on version string * Apply suggestions from code review * format --- .../src/client/settings/cache_settings.cpp | 19 +++++++++++++++++-- cockatrice/src/interface/window_main.cpp | 18 +++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/cockatrice/src/client/settings/cache_settings.cpp b/cockatrice/src/client/settings/cache_settings.cpp index 7a518df67..fde8e9b34 100644 --- a/cockatrice/src/client/settings/cache_settings.cpp +++ b/cockatrice/src/client/settings/cache_settings.cpp @@ -2,6 +2,7 @@ #include "../network/update/client/release_channel.h" #include "card_counter_settings.h" +#include "version_string.h" #include #include @@ -198,7 +199,13 @@ SettingsCache::SettingsCache() mbDownloadSpoilers = settings->value("personal/downloadspoilers", false).toBool(); - checkUpdatesOnStartup = settings->value("personal/startupUpdateCheck", true).toBool(); + if (settings->contains("personal/startupUpdateCheck")) { + checkUpdatesOnStartup = settings->value("personal/startupUpdateCheck", true).toBool(); + } else if (QString(VERSION_STRING).contains("custom", Qt::CaseInsensitive)) { + checkUpdatesOnStartup = false; // do not run auto updater on custom version + } else { + checkUpdatesOnStartup = true; // default to run auto updater + } startupCardUpdateCheckPromptForUpdate = settings->value("personal/startupCardUpdateCheckPromptForUpdate", true).toBool(); startupCardUpdateCheckAlwaysUpdate = settings->value("personal/startupCardUpdateCheckAlwaysUpdate", false).toBool(); @@ -206,7 +213,15 @@ SettingsCache::SettingsCache() lastCardUpdateCheck = settings->value("personal/lastCardUpdateCheck", QDateTime::currentDateTime().date()).toDate(); notifyAboutUpdates = settings->value("personal/updatenotification", true).toBool(); notifyAboutNewVersion = settings->value("personal/newversionnotification", true).toBool(); - updateReleaseChannel = settings->value("personal/updatereleasechannel", 0).toInt(); + + if (settings->contains("personal/updatereleasechannel")) { + updateReleaseChannel = settings->value("personal/updatereleasechannel").toInt(); + } else if (QString(VERSION_STRING).contains("beta", Qt::CaseInsensitive)) { + // default to beta if this is a beta release + updateReleaseChannel = 1; + } else { + updateReleaseChannel = 0; // stable + } lang = settings->value("personal/lang").toString(); keepalive = settings->value("personal/keepalive", 3).toInt(); diff --git a/cockatrice/src/interface/window_main.cpp b/cockatrice/src/interface/window_main.cpp index 41113185c..2e135d170 100644 --- a/cockatrice/src/interface/window_main.cpp +++ b/cockatrice/src/interface/window_main.cpp @@ -938,9 +938,25 @@ void MainWindow::startupConfigCheck() const auto reloadOk0 = QtConcurrent::run([] { CardDatabaseManager::getInstance()->loadCardDatabases(); }); } - qCInfo(WindowMainStartupShortcutsLog) << "[MainWindow] Migrating shortcuts after update detected."; + qCInfo(WindowMainStartupShortcutsLog) << "Migrating shortcuts after update detected."; SettingsCache::instance().shortcuts().migrateShortcuts(); + if (SettingsCache::instance().getCheckUpdatesOnStartup()) { + if (QString(VERSION_STRING).contains("custom", Qt::CaseInsensitive)) { + qCInfo(WindowMainStartupShortcutsLog) << "Update has changed to custom version, disabling auto update"; + SettingsCache::instance().setCheckUpdatesOnStartup(Qt::Unchecked); + } else { + int channel = 0; + if (QString(VERSION_STRING).contains("beta", Qt::CaseInsensitive)) { + channel = 1; + } + if (SettingsCache::instance().getUpdateReleaseChannelIndex() != channel) { + qCInfo(WindowMainStartupShortcutsLog) << "Update has changed beta state, updating release channel."; + SettingsCache::instance().setUpdateReleaseChannelIndex(channel); + } + } + } + SettingsCache::instance().setClientVersion(VERSION_STRING); } else { // previous config from this version found From 28c800dd3795f736d72b86ea3bbe8b6bd420f9ec Mon Sep 17 00:00:00 2001 From: tooomm Date: Wed, 31 Dec 2025 17:56:24 +0100 Subject: [PATCH 42/43] Docs: Add & link xsd schema information (#6439) * Add xsd scheme information * further references as list --- .../card_database_schema_and_parsing.md | 13 +++++++++++++ .../deck_management/creating_decks.md | 8 +++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md b/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md index 1de6fda4f..7b4bdafc3 100644 --- a/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md +++ b/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md @@ -1,3 +1,16 @@ @page card_database_schema_and_parsing Card Database Schema and Parsing +# Card Database Schemas + +Cockatrice uses `XML files` to store information of available cards to be used in the app (`cards.xml`). +The token file follows the schema of the card database (`tokens.xml`), too. + +Saved decks (`.xml`) use a different schema. + +- [XSD Schema for `Card Databases`](https://github.com/Cockatrice/Cockatrice/blob/master/doc/carddatabase_v4/cards.xsd) v4 +- [XSD Schema for `Decks`](https://github.com/Cockatrice/Cockatrice/blob/master/doc/deck.xsd) v1 + + +# Card Database Parsing + TODO diff --git a/doc/doxygen/extra-pages/user_documentation/deck_management/creating_decks.md b/doc/doxygen/extra-pages/user_documentation/deck_management/creating_decks.md index bfe578afd..153320c3d 100644 --- a/doc/doxygen/extra-pages/user_documentation/deck_management/creating_decks.md +++ b/doc/doxygen/extra-pages/user_documentation/deck_management/creating_decks.md @@ -8,10 +8,8 @@ by selecting "Deck Editor" or "Visual Deck Editor" under the "Tabs" application # Further References -See @ref editing_decks for information on how to modify the attributes and contents of a deck in the Deck Editor +- See @ref editing_decks for information on how to modify the attributes and contents of a deck in the Deck Editor widgets. - -See @ref exporting_decks for information on how to store and persist your deck either in-client or to external +- See @ref exporting_decks for information on how to store and persist your deck either in-client or to external services. - -See @ref importing_decks for information on how to import existing decks either in-client or from external services \ No newline at end of file +- See @ref importing_decks for information on how to import existing decks either in-client or from external services From 36d82807652961a80787f1ba939adff04ff76be8 Mon Sep 17 00:00:00 2001 From: tooomm Date: Wed, 31 Dec 2025 17:57:19 +0100 Subject: [PATCH 43/43] Readme: Reorder `Contribute` section (#6435) * Reorder contribute section * fix * Wording updates in related section --- README.md | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 3d01e9f3d..c08aee0e7 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,10 @@ Latest beta version: # Related Repositories -- [Magic-Token](https://github.com/Cockatrice/Magic-Token): MtG token data to use in Cockatrice -- [Magic-Spoiler](https://github.com/Cockatrice/Magic-Spoiler): Script to generate MtG spoiler data from [MTGJSON](https://github.com/mtgjson/mtgjson) to use in Cockatrice +- [Magic-Token](https://github.com/Cockatrice/Magic-Token): File with MtG token data for use in Cockatrice +- [Magic-Spoiler](https://github.com/Cockatrice/Magic-Spoiler): Code to generate MtG spoiler data from [MTGJSON](https://github.com/mtgjson/mtgjson) for use in Cockatrice - [cockatrice.github.io](https://github.com/Cockatrice/cockatrice.github.io): Code of the official Cockatrice webpage -- [Cockatrice @Flathub](https://github.com/flathub/io.github.Cockatrice.cockatrice): Configuration for our Linux `flatpak` package +- [io.github.Cockatrice.cockatrice](https://github.com/flathub/io.github.Cockatrice.cockatrice): Configuration of our Linux `flatpak` package hosted at [Flathub](https://flathub.org/en/apps/io.github.Cockatrice.cockatrice) # Community Resources [![Discord](https://img.shields.io/discord/314987288398659595?label=Discord&logo=discord&logoColor=white)](https://discord.gg/3Z9yzmA) @@ -55,7 +55,6 @@ Latest beta version: Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other projet contributors (`#dev` channel) or fellow users of the app. Come here to talk about the application, features, or just to hang out. - [Official Website](https://cockatrice.github.io) - [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki) -- [Official Code Documentation](https://cockatrice.github.io/docs) - [Official Discord](https://discord.gg/3Z9yzmA) - [reddit r/Cockatrice](https://reddit.com/r/cockatrice) @@ -64,6 +63,23 @@ Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other p # Contribute +

    + Code | + Documentation | + Translation +

    + +#### Repository Activity +![Cockatrice Repo Analytics](https://repobeats.axiom.co/api/embed/c7cec938789a5bbaeb4182a028b4dbb96db8f181.svg "Cockatrice Repo Analytics by Repobeats") + +
    +Kudos to all our amazing contributors ❤️ +
    + + +
    + Made with contrib.rocks +
    ### Code @@ -79,21 +95,17 @@ We'll happily advice on how best to implement a feature, or we can show you wher You can also have a look at our `Todo List` in our [Code Documentation](https://cockatrice.github.io/docs) or search the repo for [`\todo` comments](https://github.com/search?q=repo%3ACockatrice%2FCockatrice%20%5Ctodo&type=code). +### Documentation [![CI Docs](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml/badge.svg?event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml?query=event%3Apush) + +There are various places where useful information for different needs are maintained: +- [Official Code Documentation](https://cockatrice.github.io/docs/) +- [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki) `Community supported` +- [Official Webpage](https://cockatrice.github.io/) +- [Official README](https://github.com/Cockatrice/Cockatrice/blob/master/README.md) `This file` + Cockatrice tries to use the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide. -#### Repository Activity -![Cockatrice Repo Analytics](https://repobeats.axiom.co/api/embed/c7cec938789a5bbaeb4182a028b4dbb96db8f181.svg "Cockatrice Repo Analytics by Repobeats") - -
    -Kudos to all our amazing contributors ❤️ -
    - - -
    - Made with contrib.rocks -
    - -### Translations [![Transifex Project](https://img.shields.io/badge/translate-on%20transifex-brightgreen)](https://explore.transifex.com/cockatrice/cockatrice/) +### Translation [![Transifex Project](https://img.shields.io/badge/translate-on%20transifex-brightgreen)](https://explore.transifex.com/cockatrice/cockatrice/) Cockatrice uses Transifex to manage translations. You can help us bring Cockatrice, Oracle and Webatrice to your language and just adjust single wordings right from within your browser by visiting our [Transifex project page](https://explore.transifex.com/cockatrice/cockatrice/).