Compare commits

..

1 Commits

Author SHA1 Message Date
Brübach, Lukas
a2af648e00 [VDE] Be saner about proxy indices 2025-12-05 04:14:53 +01:00
140 changed files with 1031 additions and 2575 deletions

View File

@@ -166,7 +166,7 @@ jobs:
- name: Restore compiler cache (ccache)
id: ccache_restore
uses: actions/cache/restore@v5
uses: actions/cache/restore@v4
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@v5
uses: actions/cache/save@v4
with:
path: ${{env.CACHE}}
key: ${{ steps.ccache_restore.outputs.cache-primary-key }}

View File

@@ -33,7 +33,7 @@ jobs:
- name: Create pull request
if: github.event_name != 'pull_request'
id: create_pr
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
add-paths: |
cockatrice/translations/*.ts

View File

@@ -57,7 +57,7 @@ jobs:
- name: Create pull request
if: github.event_name != 'pull_request'
id: create_pr
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
add-paths: |
cockatrice/cockatrice_en@source.ts

4
.gitmodules vendored
View File

@@ -1,7 +1,3 @@
[submodule "vcpkg"]
path = vcpkg
url = https://github.com/microsoft/vcpkg.git
[submodule "doxygen-awesome-css"]
path = doc/doxygen/theme
url = https://github.com/jothepro/doxygen-awesome-css.git

View File

@@ -1068,8 +1068,7 @@ RECURSIVE = YES
EXCLUDE = build/ \
cmake/ \
doc/doxygen/theme/docs/ \
doc/doxygen/theme/include/ \
dbconverter/ \
vcpkg/ \
webclient/
@@ -1432,8 +1431,7 @@ HTML_STYLESHEET =
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = doc/doxygen/css/doxygen_style.css \
doc/doxygen/theme/doxygen-awesome.css
HTML_EXTRA_STYLESHEET = doc/doxygen/css/doxygen_style.css
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
@@ -1456,7 +1454,7 @@ HTML_EXTRA_FILES = doc/doxygen/js/graph_toggle.js
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE = LIGHT # required with doxygen-awesome-css theme, Auto Dark Mode will still work
HTML_COLORSTYLE = AUTO_DARK
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
@@ -1767,7 +1765,7 @@ ECLIPSE_DOC_ID = org.doxygen.Project
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO # YES is bugged in the theme, see jothepro/doxygen-awesome-css/issues/201
DISABLE_INDEX = YES
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information. If the tag

View File

@@ -46,8 +46,7 @@ Latest <kbd>beta</kbd> version:
- [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
- [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
- [cockatrice.github.io](https://github.com/Cockatrice/cockatrice.github.io): Code of the official webpage of the Cockatrice project
# Community Resources [![Discord](https://img.shields.io/discord/314987288398659595?label=Discord&logo=discord&logoColor=white)](https://discord.gg/3Z9yzmA)
@@ -55,7 +54,6 @@ Latest <kbd>beta</kbd> 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)
@@ -77,15 +75,10 @@ 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.<br>
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.
#### Repository Activity
![Cockatrice Repo Analytics](https://repobeats.axiom.co/api/embed/c7cec938789a5bbaeb4182a028b4dbb96db8f181.svg "Cockatrice Repo Analytics by Repobeats")
<details>
<summary><b>Kudos to all our amazing contributors ❤️</b></summary>
<summary><b>Kudos to our amazing contributors ❤️</b></summary>
<br>
<a href="https://github.com/Cockatrice/Cockatrice/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Cockatrice/Cockatrice" />

View File

@@ -19,10 +19,7 @@ set(cockatrice_SOURCES
src/client/settings/card_counter_settings.cpp
src/client/settings/shortcut_treeview.cpp
src/client/settings/shortcuts_settings.cpp
src/interface/deck_loader/card_node_function.cpp
src/interface/deck_loader/deck_file_format.cpp
src/interface/deck_loader/deck_loader.cpp
src/interface/deck_loader/loaded_deck.cpp
src/interface/widgets/dialogs/dlg_connect.cpp
src/interface/widgets/dialogs/dlg_convert_deck_to_cod_format.cpp
src/interface/widgets/dialogs/dlg_create_game.cpp
@@ -202,7 +199,6 @@ set(cockatrice_SOURCES
src/interface/widgets/utility/sequence_edit.cpp
src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_format_legality_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
@@ -230,7 +226,6 @@ 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

View File

@@ -1,10 +1,6 @@
[Rules]
# The default log level is info
*.debug = false
#*.info = true
#*.warning = true
#*.critical = true
#*.fatal = true
# Uncomment a rule to see debug level logs for that category,
# or set <category> = false to disable logging

View File

@@ -15,18 +15,15 @@ searches are case insensitive.
<dd>[n:red n:deck n:wins](#n:red n:deck n:wins) <small>(Any deck with a name containing the words red, deck, and wins)</small></dd>
<dd>[n:"red deck wins"](#n:%22red deck wins%22) <small>(Any deck with a name containing the exact phrase "red deck wins")</small></dd>
<dt><u>F</u>ile <u>N</u>ame:</dt>
<dd>[fn:aggro](#fn:aggro) <small>(Any deck with a filename containing the word aggro)</small></dd>
<dd>[fn:red fn:deck fn:wins](#fn:red fn:deck fn:wins) <small>(Any deck with a filename containing the words red, deck, and wins)</small></dd>
<dd>[fn:"red deck wins"](#fn:%22red deck wins%22) <small>(Any deck with a filename containing the exact phrase "red deck wins")</small></dd>
<dt><u>F</u>ile Name:</dt>
<dd>[f:aggro](#f:aggro) <small>(Any deck with a filename containing the word aggro)</small></dd>
<dd>[f:red f:deck f:wins](#f:red f:deck f:wins) <small>(Any deck with a filename containing the words red, deck, and wins)</small></dd>
<dd>[f:"red deck wins"](#f:%22red deck wins%22) <small>(Any deck with a filename containing the exact phrase "red deck wins")</small></dd>
<dt>Relative <u>P</u>ath (starting from the deck folder):</dt>
<dd>[p:aggro](#p:aggro) <small>(Any deck that has "aggro" somewhere in its relative path)</small></dd>
<dd>[p:edh/](#p:edh/) <small>(Any deck with "edh/" in its relative path, A.K.A. decks in the "edh" folder)</small></dd>
<dt><u>F</u>ormat:</dt>
<dd>[f:standard](#f:standard) <small>(Any deck with format set to standard)</small></dd>
<dt>Deck Contents (Uses [card search expressions](#cardSearchSyntaxHelp)):</dt>
<dd><a href="#[[plains]]">[[plains]]</a> <small>(Any deck that contains at least one card with "plains" in its name)</small></dd>
<dd><a href="#[[t:legendary]]">[[t:legendary]]</a> <small>(Any deck that contains at least one legendary)</small></dd>

View File

@@ -350,11 +350,11 @@
style="fill-opacity:1;stroke:black;stroke-width:2.78220296;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 49.84375 1.71875 C 36.719738 1.71875 26.0625 12.375988 26.0625 25.5 C 26.0625 32.977454 29.538325 39.612734 34.9375 43.96875 C 24.439951 49.943698 17.919149 62.196126 14.3125 75.65625 C 9.0380874 95.34065 30.224013 98.21875 49.84375 98.21875 C 69.463486 98.21875 90.549327 94.96715 85.375 75.65625 C 81.693381 61.916246 75.224585 49.827177 64.8125 43.9375 C 70.181573 39.580662 73.59375 32.953205 73.59375 25.5 C 73.59375 12.375988 62.967762 1.71875 49.84375 1.71875 z "
transform="translate(0,952.36218)"
id="left" />
id="right" />
<path
style="opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.73577702;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 51.28696,1001.834 0,-46.98372 1.434151,0.16768 c 5.155008,0.60274 9.462857,2.72154 12.938257,6.36366 4.74393,4.9715 6.87913,11.35611 6.16464,18.43328 -0.53702,5.31935 -3.09008,10.59498 -6.83833,14.13074 l -1.94072,1.83069 3.04083,2.20427 c 3.58084,2.5957 7.18975,6.4912 9.55296,10.3116 4.89572,7.9144 9.23593,21.4918 8.50487,26.6055 -0.81312,5.6877 -5.43872,9.6977 -13.62216,11.8093 -3.80822,0.9826 -7.68056,1.4713 -14.763321,1.8633 l -4.471177,0.2474 0,-46.9837 z"
id="right"
id="left"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:url(#linearGradient3);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.77952756;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -321,11 +321,11 @@
style="fill-opacity:1;stroke:black;stroke-width:2.78220296;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 49.84375 1.71875 C 36.719738 1.71875 26.0625 12.375988 26.0625 25.5 C 26.0625 32.977454 29.538325 39.612734 34.9375 43.96875 C 24.439951 49.943698 17.919149 62.196126 14.3125 75.65625 C 9.0380874 95.34065 30.224013 98.21875 49.84375 98.21875 C 69.463486 98.21875 90.549327 94.96715 85.375 75.65625 C 81.693381 61.916246 75.224585 49.827177 64.8125 43.9375 C 70.181573 39.580662 73.59375 32.953205 73.59375 25.5 C 73.59375 12.375988 62.967762 1.71875 49.84375 1.71875 z "
transform="translate(0,952.36218)"
id="left" />
id="right" />
<path
style="opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.73577702;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 51.28696,1001.834 0,-46.98372 1.434151,0.16768 c 5.155008,0.60274 9.462857,2.72154 12.938257,6.36366 4.74393,4.9715 6.87913,11.35611 6.16464,18.43328 -0.53702,5.31935 -3.09008,10.59498 -6.83833,14.13074 l -1.94072,1.83069 3.04083,2.20427 c 3.58084,2.5957 7.18975,6.4912 9.55296,10.3116 4.89572,7.9144 9.23593,21.4918 8.50487,26.6055 -0.81312,5.6877 -5.43872,9.6977 -13.62216,11.8093 -3.80822,0.9826 -7.68056,1.4713 -14.763321,1.8633 l -4.471177,0.2474 0,-46.9837 z"
id="right"
id="left"
inkscape:connector-curvature="0" />
<path
d="m 46.656521,12.167234 18.055171,18.054184 a 6.6081919,6.6078288 0 0 1 -0.126303,9.352065 6.6804126,6.6800456 0 0 1 -8.233169,1.011048 l -7.944268,7.943843 6.463762,6.445343 a 6.9331851,6.9328042 0 0 1 5.741536,2.022073 l 28.057729,28.092294 a 6.9962797,6.9958953 0 0 1 -9.894222,9.893685 L 50.719018,66.907526 A 7.0595711,7.0591833 0 0 1 49.18433,59.270613 l -5.741527,-5.741238 -7.944298,7.943843 A 6.716523,6.7161541 0 0 1 25.134866,69.832263 L 7.079684,51.778091 a 6.716523,6.7161541 0 0 1 8.39566,-10.345064 L 36.31101,20.59853 a 6.716523,6.7161541 0 0 1 10.345612,-8.431329 z"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -340,11 +340,11 @@
style="fill-opacity:1;stroke:black;stroke-width:2.78220296;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 49.84375 1.71875 C 36.719738 1.71875 26.0625 12.375988 26.0625 25.5 C 26.0625 32.977454 29.538325 39.612734 34.9375 43.96875 C 24.439951 49.943698 17.919149 62.196126 14.3125 75.65625 C 9.0380874 95.34065 30.224013 98.21875 49.84375 98.21875 C 69.463486 98.21875 90.549327 94.96715 85.375 75.65625 C 81.693381 61.916246 75.224585 49.827177 64.8125 43.9375 C 70.181573 39.580662 73.59375 32.953205 73.59375 25.5 C 73.59375 12.375988 62.967762 1.71875 49.84375 1.71875 z "
transform="translate(0,952.36218)"
id="left" />
id="right" />
<path
style="opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.73577702;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 51.28696,1001.834 0,-46.98372 1.434151,0.16768 c 5.155008,0.60274 9.462857,2.72154 12.938257,6.36366 4.74393,4.9715 6.87913,11.35611 6.16464,18.43328 -0.53702,5.31935 -3.09008,10.59498 -6.83833,14.13074 l -1.94072,1.83069 3.04083,2.20427 c 3.58084,2.5957 7.18975,6.4912 9.55296,10.3116 4.89572,7.9144 9.23593,21.4918 8.50487,26.6055 -0.81312,5.6877 -5.43872,9.6977 -13.62216,11.8093 -3.80822,0.9826 -7.68056,1.4713 -14.763321,1.8633 l -4.471177,0.2474 0,-46.9837 z"
id="right"
id="left"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -8,7 +8,6 @@
#include <QUrlQuery>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <version_string.h>
DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent)
: QObject(parent), cardDatabase(_cardDatabase)
@@ -63,7 +62,6 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck)
QNetworkRequest request(QUrl("https://deckstats.net/index.php"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
manager->post(request, data);
}

View File

@@ -8,7 +8,6 @@
#include <QUrlQuery>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <version_string.h>
TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent)
: QObject(parent), cardDatabase(_cardDatabase)
@@ -88,7 +87,6 @@ void TappedOutInterface::analyzeDeck(DeckList *deck)
QNetworkRequest request(QUrl("https://tappedout.net/mtg-decks/paste/"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
manager->post(request, data);
}

View File

@@ -6,8 +6,6 @@
#ifndef INTERFACE_JSON_DECK_PARSER_H
#define INTERFACE_JSON_DECK_PARSER_H
#include "../../../interface/deck_loader/card_node_function.h"
#include "../../../interface/deck_loader/deck_loader.h"
#include <QJsonArray>
@@ -18,21 +16,21 @@ class IJsonDeckParser
public:
virtual ~IJsonDeckParser() = default;
virtual DeckList parse(const QJsonObject &obj) = 0;
virtual DeckLoader *parse(const QJsonObject &obj) = 0;
};
class ArchidektJsonParser : public IJsonDeckParser
{
public:
DeckList parse(const QJsonObject &obj) override
DeckLoader *parse(const QJsonObject &obj) override
{
DeckList deckList;
DeckLoader *loader = new DeckLoader(nullptr);
QString deckName = obj.value("name").toString();
QString deckDescription = obj.value("description").toString();
deckList.setName(deckName);
deckList.setComments(deckDescription);
loader->getDeckList()->setName(deckName);
loader->getDeckList()->setComments(deckDescription);
QString outputText;
QTextStream outStream(&outputText);
@@ -49,25 +47,25 @@ public:
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
}
deckList.loadFromStream_Plain(outStream, false);
deckList.forEachCard(CardNodeFunction::ResolveProviderId());
loader->getDeckList()->loadFromStream_Plain(outStream, false);
DeckLoader::resolveSetNameAndNumberToProviderID(loader->getDeckList());
return deckList;
return loader;
}
};
class MoxfieldJsonParser : public IJsonDeckParser
{
public:
DeckList parse(const QJsonObject &obj) override
DeckLoader *parse(const QJsonObject &obj) override
{
DeckList deckList;
DeckLoader *loader = new DeckLoader(nullptr);
QString deckName = obj.value("name").toString();
QString deckDescription = obj.value("description").toString();
deckList.setName(deckName);
deckList.setComments(deckDescription);
loader->getDeckList()->setName(deckName);
loader->getDeckList()->setComments(deckDescription);
QString outputText;
QTextStream outStream(&outputText);
@@ -96,8 +94,8 @@ public:
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
}
deckList.loadFromStream_Plain(outStream, false);
deckList.forEachCard(CardNodeFunction::ResolveProviderId());
loader->getDeckList()->loadFromStream_Plain(outStream, false);
DeckLoader::resolveSetNameAndNumberToProviderID(loader->getDeckList());
QJsonObject commandersObj = obj.value("commanders").toObject();
if (!commandersObj.isEmpty()) {
@@ -108,12 +106,12 @@ public:
QString collectorNumber = cardData.value("cn").toString();
QString providerId = cardData.value("scryfall_id").toString();
deckList.setBannerCard({commanderName, providerId});
deckList.addCard(commanderName, DECK_ZONE_MAIN, -1, setName, collectorNumber, providerId);
loader->getDeckList()->setBannerCard({commanderName, providerId});
loader->getDeckList()->addCard(commanderName, DECK_ZONE_MAIN, -1, setName, collectorNumber, providerId);
}
}
return deckList;
return loader;
}
};

View File

@@ -14,7 +14,6 @@
#include <QtConcurrent>
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/card/database/card_database_manager.h>
#include <version_string.h>
#define SPOILERS_STATUS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/SpoilerSeasonEnabled"
#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml"
@@ -40,9 +39,7 @@ void SpoilerBackgroundUpdater::startSpoilerDownloadProcess(QString url, bool sav
void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults)
{
auto *nam = new QNetworkAccessManager(this);
auto request = QNetworkRequest(url);
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
QNetworkReply *reply = nam->get(request);
QNetworkReply *reply = nam->get(QNetworkRequest(url));
if (saveResults) {
// This will write out to the file (used for spoiler.xml)

View File

@@ -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 / FormatQuery / GenericQuery
QueryPart <- NotQuery / DeckContentQuery / DeckNameQuery / FileNameQuery / PathQuery / GenericQuery
NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart
@@ -22,9 +22,8 @@ CardSearch <- '[[' CardFilterString ']]'
CardFilterString <- (!']]'.)*
DeckNameQuery <- ([Dd] 'eck')? [Nn] 'ame'? [:] String
FileNameQuery <- [Ff] ([Nn] / 'ile' ([Nn] 'ame')?) [:] String
FileNameQuery <- [Ff] ('ile' 'name'?)? [:] String
PathQuery <- [Pp] 'ath'? [:] String
FormatQuery <- [Ff] 'ormat'? [:] String
GenericQuery <- String
@@ -119,13 +118,12 @@ static void setupParserRules()
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) -> bool {
int count = 0;
auto cardNodes = deck->deckLoader->getDeck().deckList.getCardNodes();
for (auto node : cardNodes) {
deck->deckLoader->getDeckList()->forEachCard([&](InnerDecklistNode *, const DecklistCardNode *node) {
auto cardInfoPtr = CardDatabaseManager::query()->getCardInfo(node->getName());
if (!cardInfoPtr.isNull() && cardFilter.check(cardInfoPtr)) {
count += node->getNumber();
}
}
});
return numberMatcher(count);
};
};
@@ -139,7 +137,7 @@ static void setupParserRules()
search["DeckNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
return deck->deckLoader->getDeck().deckList.getName().contains(name, Qt::CaseInsensitive);
return deck->deckLoader->getDeckList()->getName().contains(name, Qt::CaseInsensitive);
};
};
@@ -158,14 +156,6 @@ static void setupParserRules()
};
};
search["FormatQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto format = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
auto gameFormat = deck->deckLoader->getDeck().deckList.getGameFormat();
return QString::compare(format, gameFormat, Qt::CaseInsensitive) == 0;
};
};
search["GenericQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {

View File

@@ -343,7 +343,10 @@ void DeckViewScene::rebuildTree()
if (!deck)
return;
for (auto *currentZone : deck->getZoneNodes()) {
InnerDecklistNode *listRoot = deck->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
auto *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
DeckViewCardContainer *container = cardContainers.value(currentZone->getName(), 0);
if (!container) {
container = new DeckViewCardContainer(currentZone->getName());

View File

@@ -259,7 +259,7 @@ void DeckViewContainer::loadLocalDeck()
void DeckViewContainer::loadDeckFromFile(const QString &filePath)
{
DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(filePath);
DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(filePath);
DeckLoader deck(this);
bool success = deck.loadFromFile(filePath, fmt, true);
@@ -269,12 +269,12 @@ void DeckViewContainer::loadDeckFromFile(const QString &filePath)
return;
}
loadDeckFromDeckList(deck.getDeck().deckList);
loadDeckFromDeckLoader(&deck);
}
void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck)
void DeckViewContainer::loadDeckFromDeckLoader(DeckLoader *deck)
{
QString deckString = deck.writeToString_Native();
QString deckString = deck->getDeckList()->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;
}
DeckList deck = dlg.getDeckList();
loadDeckFromDeckList(deck);
DeckLoader *deck = dlg.getDeckList();
loadDeckFromDeckLoader(deck);
}
void DeckViewContainer::loadFromWebsite()
@@ -320,15 +320,16 @@ void DeckViewContainer::loadFromWebsite()
return;
}
DeckList deck = dlg.getDeck();
loadDeckFromDeckList(deck);
DeckLoader *deck = dlg.getDeck();
loadDeckFromDeckLoader(deck);
}
void DeckViewContainer::deckSelectFinished(const Response &r)
{
const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext);
DeckList newDeck = DeckList(QString::fromStdString(resp.deck()));
CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(newDeck.getCardRefList()));
DeckLoader newDeck(this, new DeckList(QString::fromStdString(resp.deck())));
CardPictureLoader::cacheCardPixmaps(
CardDatabaseManager::query()->getCards(newDeck.getDeckList()->getCardRefList()));
setDeck(newDeck);
switchToDeckLoadedView();
}
@@ -409,8 +410,8 @@ void DeckViewContainer::setSideboardLocked(bool locked)
deckView->resetSideboardPlan();
}
void DeckViewContainer::setDeck(const DeckList &deck)
void DeckViewContainer::setDeck(DeckLoader &deck)
{
deckView->setDeck(deck);
deckView->setDeck(*deck.getDeckList());
switchToDeckLoadedView();
}

View File

@@ -85,12 +85,12 @@ public:
void setReadyStart(bool ready);
void readyAndUpdate();
void setSideboardLocked(bool locked);
void setDeck(const DeckList &deck);
void setDeck(DeckLoader &deck);
void setVisualDeckStorageExists(bool exists);
public slots:
void loadDeckFromFile(const QString &filePath);
void loadDeckFromDeckList(const DeckList &deck);
void loadDeckFromDeckLoader(DeckLoader *deck);
};
#endif // DECK_VIEW_CONTAINER_H

View File

@@ -5,7 +5,6 @@
#include "../player_actions.h"
#include "player_menu.h"
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
UtilityMenu::UtilityMenu(Player *_player, QMenu *playerMenu) : QMenu(playerMenu), player(_player)
@@ -60,19 +59,21 @@ void UtilityMenu::populatePredefinedTokensMenu()
clear();
setEnabled(false);
predefinedTokens.clear();
const DeckList &deckList = player->getDeck();
DeckLoader *_deck = player->getDeck();
if (deckList.isEmpty()) {
if (!_deck) {
return;
}
auto tokenCardNodes = deckList.getCardNodes({DECK_ZONE_TOKENS});
InnerDecklistNode *tokenZone =
dynamic_cast<InnerDecklistNode *>(_deck->getDeckList()->getRoot()->findChild(DECK_ZONE_TOKENS));
if (!tokenCardNodes.isEmpty()) {
setEnabled(true);
if (tokenZone) {
if (!tokenZone->empty())
setEnabled(true);
for (int i = 0; i < tokenCardNodes.size(); ++i) {
const QString tokenName = tokenCardNodes[i]->getName();
for (int i = 0; i < tokenZone->size(); ++i) {
const QString tokenName = tokenZone->at(i)->getName();
predefinedTokens.append(tokenName);
QAction *a = addAction(tokenName);
if (i < 10) {

View File

@@ -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), zoneId(0), dialogSemaphore(false)
conceded(false), deck(nullptr), zoneId(0), dialogSemaphore(false)
{
initializeZones();
@@ -263,9 +263,10 @@ void Player::deleteCard(CardItem *card)
}
}
void Player::setDeck(const DeckList &_deck)
// TODO: Does a player need a DeckLoader?
void Player::setDeck(DeckLoader &_deck)
{
deck = _deck;
deck = new DeckLoader(this, _deck.getDeckList());
emit deckChanged();
}

View File

@@ -9,7 +9,6 @@
#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"
@@ -45,6 +44,7 @@ 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(const LoadedDeck &deck);
void openDeckEditor(DeckLoader *deck);
void deckChanged();
void newCardAdded(AbstractCardItem *card);
void rearrangeCounters();
@@ -130,9 +130,9 @@ public:
return playerMenu;
}
void setDeck(const DeckList &_deck);
void setDeck(DeckLoader &_deck);
[[nodiscard]] const DeckList &getDeck() const
[[nodiscard]] DeckLoader *getDeck() const
{
return deck;
}
@@ -241,7 +241,7 @@ private:
bool active;
bool conceded;
DeckList deck;
DeckLoader *deck;
int zoneId;
QMap<QString, CardZoneLogic *> zones;

View File

@@ -218,7 +218,7 @@ void PlayerActions::actAlwaysLookAtTopCard()
void PlayerActions::actOpenDeckInDeckEditor()
{
emit player->openDeckEditor({.deckList = player->getDeck()});
emit player->openDeckEditor(player->getDeck());
}
void PlayerActions::actViewGraveyard()

View File

@@ -10,7 +10,6 @@
#include <QNetworkReply>
#include <QThread>
#include <utility>
#include <version_string.h>
static constexpr int MAX_REQUESTS_PER_SEC = 10;
@@ -87,7 +86,6 @@ QNetworkReply *CardPictureLoaderWorker::makeRequest(const QUrl &url, CardPicture
}
QNetworkRequest req(url);
req.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
if (!picDownload) {
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
}

View File

@@ -1,40 +0,0 @@
#include "card_node_function.h"
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
void CardNodeFunction::SetProviderIdToPreferred::operator()(const InnerDecklistNode *node, DecklistCardNode *card) const
{
Q_UNUSED(node);
PrintingInfo preferredPrinting = CardDatabaseManager::query()->getPreferredPrinting(card->getName());
QString providerId = preferredPrinting.getUuid();
QString setShortName = preferredPrinting.getSet()->getShortName();
QString collectorNumber = preferredPrinting.getProperty("num");
card->setCardProviderId(providerId);
card->setCardCollectorNumber(collectorNumber);
card->setCardSetShortName(setShortName);
}
void CardNodeFunction::ClearPrintingData::operator()(const InnerDecklistNode *node, DecklistCardNode *card) const
{
Q_UNUSED(node);
card->setCardSetShortName(nullptr);
card->setCardCollectorNumber(nullptr);
card->setCardProviderId(nullptr);
}
void CardNodeFunction::ResolveProviderId::operator()(const InnerDecklistNode *node, DecklistCardNode *card) const
{
Q_UNUSED(node);
// Retrieve the providerId based on setName and collectorNumber
QString providerId =
CardDatabaseManager::getInstance()
->query()
->getSpecificPrinting(card->getName(), card->getCardSetShortName(), card->getCardCollectorNumber())
.getUuid();
// Set the providerId on the card
card->setCardProviderId(providerId);
}

View File

@@ -1,39 +0,0 @@
#ifndef COCKATRICE_DECK_FUNCTION_H
#define COCKATRICE_DECK_FUNCTION_H
class DecklistCardNode;
class InnerDecklistNode;
/**
* Functions to be used with DeckList::forEachCard
*/
namespace CardNodeFunction
{
/**
* @brief Sets the providerId of the card to the preferred printing.
*/
struct SetProviderIdToPreferred
{
void operator()(const InnerDecklistNode *node, DecklistCardNode *card) const;
};
/**
* @brief Clears all fields on the card related to the printing
*/
struct ClearPrintingData
{
void operator()(const InnerDecklistNode *node, DecklistCardNode *card) const;
};
/**
* @brief Sets the providerId of the card based on its set name and collector number.
*/
struct ResolveProviderId
{
void operator()(const InnerDecklistNode *node, DecklistCardNode *card) const;
};
} // namespace CardNodeFunction
#endif // COCKATRICE_DECK_FUNCTION_H

View File

@@ -1,9 +0,0 @@
#include "deck_file_format.h"
DeckFileFormat::Format DeckFileFormat::getFormatFromName(const QString &fileName)
{
if (fileName.endsWith(".cod", Qt::CaseInsensitive)) {
return Cockatrice;
}
return PlainText;
}

View File

@@ -1,36 +0,0 @@
#ifndef COCKATRICE_DECK_FILE_FORMAT_H
#define COCKATRICE_DECK_FILE_FORMAT_H
#include <QString>
namespace DeckFileFormat
{
/**
* The deck file formats that Cockatrice supports.
*/
enum Format
{
/**
* Plaintext deck files, a format that is intended to be widely supported among different programs.
* This format does not support Cockatrice specific features such as banner cards or tags.
*/
PlainText,
/**
* This is cockatrice's native deck file format, and supports deck metadata such as banner cards and tags.
* Stored as .cod files.
*/
Cockatrice
};
/**
* Determines what deck file format the given filename corresponds to.
*
* @param fileName The filename
* @return The deck format
*/
Format getFormatFromName(const QString &fileName);
} // namespace DeckFileFormat
#endif // COCKATRICE_DECK_FILE_FORMAT_H

View File

@@ -25,11 +25,15 @@ 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)
DeckLoader::DeckLoader(QObject *parent) : QObject(parent), deckList(new DeckList())
{
}
bool DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bool userRequest)
DeckLoader::DeckLoader(QObject *parent, DeckList *_deckList) : QObject(parent), deckList(_deckList)
{
}
bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt, bool userRequest)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -37,19 +41,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);
case PlainTextFormat:
result = deckList->loadFromFile_Plain(&file);
break;
case DeckFileFormat::Cockatrice: {
result = deckList.loadFromFile_Native(&file);
case CockatriceFormat: {
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);
fmt = DeckFileFormat::PlainText;
result = deckList->loadFromFile_Plain(&file);
fmt = PlainTextFormat;
}
break;
}
@@ -59,8 +62,7 @@ bool DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fm
}
if (result) {
loadedDeck.deckList = deckList;
loadedDeck.lastLoadInfo = {
lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
@@ -75,7 +77,7 @@ bool DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fm
return result;
}
bool DeckLoader::loadFromFileAsync(const QString &fileName, DeckFileFormat::Format fmt, bool userRequest)
bool DeckLoader::loadFromFileAsync(const QString &fileName, FileFormat fmt, bool userRequest)
{
auto *watcher = new QFutureWatcher<bool>(this);
@@ -84,7 +86,7 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, DeckFileFormat::Form
watcher->deleteLater();
if (result) {
loadedDeck.lastLoadInfo = {
lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
@@ -104,14 +106,14 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, DeckFileFormat::Form
}
switch (fmt) {
case DeckFileFormat::PlainText:
return loadedDeck.deckList.loadFromFile_Plain(&file);
case DeckFileFormat::Cockatrice: {
case PlainTextFormat:
return deckList->loadFromFile_Plain(&file);
case CockatriceFormat: {
bool result = false;
result = loadedDeck.deckList.loadFromFile_Native(&file);
result = deckList->loadFromFile_Native(&file);
if (!result) {
file.seek(0);
return loadedDeck.deckList.loadFromFile_Plain(&file);
return deckList->loadFromFile_Plain(&file);
}
return result;
}
@@ -127,9 +129,9 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, DeckFileFormat::Form
bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId)
{
bool result = loadedDeck.deckList.loadFromString_Native(nativeString);
bool result = deckList->loadFromString_Native(nativeString);
if (result) {
loadedDeck.lastLoadInfo = {
lastLoadInfo = {
.remoteDeckId = remoteDeckId,
};
@@ -138,7 +140,7 @@ bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId)
return result;
}
bool DeckLoader::saveToFile(const QString &fileName, DeckFileFormat::Format fmt)
bool DeckLoader::saveToFile(const QString &fileName, FileFormat fmt)
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
@@ -147,17 +149,17 @@ bool DeckLoader::saveToFile(const QString &fileName, DeckFileFormat::Format fmt)
bool result = false;
switch (fmt) {
case DeckFileFormat::PlainText:
result = loadedDeck.deckList.saveToFile_Plain(&file);
case PlainTextFormat:
result = deckList->saveToFile_Plain(&file);
break;
case DeckFileFormat::Cockatrice:
result = loadedDeck.deckList.saveToFile_Native(&file);
case CockatriceFormat:
result = deckList->saveToFile_Native(&file);
qCInfo(DeckLoaderLog) << "Saving to " << fileName << "-" << result;
break;
}
if (result) {
loadedDeck.lastLoadInfo = {
lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
@@ -170,7 +172,7 @@ bool DeckLoader::saveToFile(const QString &fileName, DeckFileFormat::Format fmt)
return result;
}
bool DeckLoader::updateLastLoadedTimestamp(const QString &fileName, DeckFileFormat::Format fmt)
bool DeckLoader::updateLastLoadedTimestamp(const QString &fileName, FileFormat fmt)
{
QFileInfo fileInfo(fileName);
if (!fileInfo.exists()) {
@@ -191,19 +193,19 @@ bool DeckLoader::updateLastLoadedTimestamp(const QString &fileName, DeckFileForm
// Perform file modifications
switch (fmt) {
case DeckFileFormat::PlainText:
result = loadedDeck.deckList.saveToFile_Plain(&file);
case PlainTextFormat:
result = deckList->saveToFile_Plain(&file);
break;
case DeckFileFormat::Cockatrice:
loadedDeck.deckList.setLastLoadedTimestamp(QDateTime::currentDateTime().toString());
result = loadedDeck.deckList.saveToFile_Native(&file);
case CockatriceFormat:
deckList->setLastLoadedTimestamp(QDateTime::currentDateTime().toString());
result = deckList->saveToFile_Native(&file);
break;
}
file.close(); // Close the file to ensure changes are flushed
if (result) {
loadedDeck.lastLoadInfo = {
lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
@@ -267,20 +269,6 @@ static QString toDecklistExportString(const DecklistCardNode *card)
return cardString;
}
/**
* Converts all cards in the list to their decklist export string and joins them into one string
*/
static QString toDecklistExportString(const QList<const DecklistCardNode *> &cardNodes)
{
QString result;
for (auto cardNode : cardNodes) {
result += toDecklistExportString(cardNode);
}
return result;
}
/**
* Export deck to decklist function, called to format the deck in a way to be sent to a server
*
@@ -291,11 +279,29 @@ QString DeckLoader::exportDeckToDecklist(const DeckList *deckList, DecklistWebsi
{
// Add the base url
QString deckString = "https://" + getDomainForWebsite(website) + "/?";
// Create two strings to pass to function
QString mainBoardCards, sideBoardCards;
// export all cards in zone
QString mainBoardCards = toDecklistExportString(deckList->getCardNodes({DECK_ZONE_MAIN}));
QString sideBoardCards = toDecklistExportString(deckList->getCardNodes({DECK_ZONE_SIDE}));
// Set up the function to call
auto formatDeckListForExport = [&mainBoardCards, &sideBoardCards](const auto *node, const auto *card) {
// Get the card name
CardInfoPtr dbCard = CardDatabaseManager::query()->getCardInfo(card->getName());
if (!dbCard || dbCard->getIsToken()) {
// If it's a token, we don't care about the card.
return;
}
// Check if it's a sideboard card.
if (node->getName() == DECK_ZONE_SIDE) {
sideBoardCards += toDecklistExportString(card);
} else {
// If it's a mainboard card, do the same thing, but for the mainboard card string
mainBoardCards += toDecklistExportString(card);
}
};
// call our struct function for each card in the deck
deckList->forEachCard(formatDeckListForExport);
// Remove the extra return at the end of the last cards
mainBoardCards.chop(3);
sideBoardCards.chop(3);
@@ -310,6 +316,112 @@ QString DeckLoader::exportDeckToDecklist(const DeckList *deckList, DecklistWebsi
return deckString;
}
// This struct is here to support the forEachCard function call, defined in decklist.
// It requires a function to be called for each card, and it will set the providerId to the preferred printing.
struct SetProviderIdToPreferred
{
// Main operator for struct, allowing the foreachcard to work.
SetProviderIdToPreferred()
{
}
void operator()(const InnerDecklistNode *node, DecklistCardNode *card) const
{
Q_UNUSED(node);
PrintingInfo preferredPrinting = CardDatabaseManager::query()->getPreferredPrinting(card->getName());
QString providerId = preferredPrinting.getUuid();
QString setShortName = preferredPrinting.getSet()->getShortName();
QString collectorNumber = preferredPrinting.getProperty("num");
card->setCardProviderId(providerId);
card->setCardCollectorNumber(collectorNumber);
card->setCardSetShortName(setShortName);
}
};
/**
* This function iterates through each card in the decklist and sets the providerId
* on each card based on its set name and collector number.
*
* @param deckList The decklist to modify
*/
void DeckLoader::setProviderIdToPreferredPrinting(const DeckList *deckList)
{
// Set up the struct to call.
SetProviderIdToPreferred setProviderIdToPreferred;
// Call the forEachCard method for each card in the deck
deckList->forEachCard(setProviderIdToPreferred);
}
/**
* Sets the providerId on each card in the decklist based on its set name and collector number.
*
* @param deckList The decklist to modify
*/
void DeckLoader::resolveSetNameAndNumberToProviderID(const DeckList *deckList)
{
auto setProviderId = [](const auto node, const auto card) {
Q_UNUSED(node);
// Retrieve the providerId based on setName and collectorNumber
QString providerId =
CardDatabaseManager::getInstance()
->query()
->getSpecificPrinting(card->getName(), card->getCardSetShortName(), card->getCardCollectorNumber())
.getUuid();
// Set the providerId on the card
card->setCardProviderId(providerId);
};
deckList->forEachCard(setProviderId);
}
// This struct is here to support the forEachCard function call, defined in decklist.
// It requires a function to be called for each card, and it will set the providerId.
struct ClearSetNameNumberAndProviderId
{
// Main operator for struct, allowing the foreachcard to work.
ClearSetNameNumberAndProviderId()
{
}
void operator()(const InnerDecklistNode *node, DecklistCardNode *card) const
{
Q_UNUSED(node);
// Set the providerId on the card
card->setCardSetShortName(nullptr);
card->setCardCollectorNumber(nullptr);
card->setCardProviderId(nullptr);
}
};
/**
* Clears the set name and numbers on each card in the decklist.
*
* @param deckList The decklist to modify
*/
void DeckLoader::clearSetNamesAndNumbers(const DeckList *deckList)
{
auto clearSetNameAndNumber = [](const auto node, auto card) {
Q_UNUSED(node)
// Set the providerId on the card
card->setCardSetShortName(nullptr);
card->setCardCollectorNumber(nullptr);
card->setCardProviderId(nullptr);
};
deckList->forEachCard(clearSetNameAndNumber);
}
DeckLoader::FileFormat DeckLoader::getFormatFromName(const QString &fileName)
{
if (fileName.endsWith(".cod", Qt::CaseInsensitive)) {
return CockatriceFormat;
}
return PlainTextFormat;
}
void DeckLoader::saveToClipboard(const DeckList *deckList, bool addComments, bool addSetNameAndNumber)
{
QString buffer;
@@ -329,7 +441,9 @@ bool DeckLoader::saveToStream_Plain(QTextStream &out,
}
// loop zones
for (auto zoneNode : deckList->getZoneNodes()) {
for (int i = 0; i < deckList->getRoot()->size(); i++) {
const auto *zoneNode = dynamic_cast<InnerDecklistNode *>(deckList->getRoot()->at(i));
saveToStream_DeckZone(out, zoneNode, addComments, addSetNameAndNumber);
// end of zone
@@ -450,12 +564,12 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName)
bool result = false;
// Perform file modifications based on the detected format
switch (DeckFileFormat::getFormatFromName(fileName)) {
case DeckFileFormat::PlainText:
switch (getFormatFromName(fileName)) {
case PlainTextFormat:
// Save in Cockatrice's native format
result = loadedDeck.deckList.saveToFile_Native(&file);
result = deckList->saveToFile_Native(&file);
break;
case DeckFileFormat::Cockatrice:
case CockatriceFormat:
qCInfo(DeckLoaderLog) << "File is already in Cockatrice format. No conversion needed.";
result = true;
break;
@@ -474,16 +588,39 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName)
} else {
qCInfo(DeckLoaderLog) << "Original file deleted successfully:" << fileName;
}
loadedDeck.lastLoadInfo = {
lastLoadInfo = {
.fileName = newFileName,
.fileFormat = DeckFileFormat::Cockatrice,
.fileFormat = CockatriceFormat,
};
}
return result;
}
void DeckLoader::printDeckListNode(QTextCursor *cursor, const InnerDecklistNode *node)
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, InnerDecklistNode *node)
{
const int totalColumns = 2;
@@ -566,11 +703,12 @@ void DeckLoader::printDeckList(QPrinter *printer, const DeckList *deckList)
cursor.insertText(deckList->getComments());
cursor.insertBlock(headerBlockFormat, headerCharFormat);
for (auto zoneNode : deckList->getZoneNodes()) {
for (int i = 0; i < deckList->getRoot()->size(); i++) {
cursor.insertHtml("<br><img src=theme:hr.jpg>");
// cursor.insertHtml("<hr>");
cursor.insertBlock(headerBlockFormat, headerCharFormat);
printDeckListNode(&cursor, zoneNode);
printDeckListNode(&cursor, dynamic_cast<InnerDecklistNode *>(deckList->getRoot()->at(i)));
}
doc.print(printer);

View File

@@ -7,16 +7,14 @@
#ifndef DECK_LOADER_H
#define DECK_LOADER_H
#include "loaded_deck.h"
#include <QLoggingCategory>
#include <QPrinter>
#include <QTextCursor>
#include <libcockatrice/deck_list/deck_list.h>
inline Q_LOGGING_CATEGORY(DeckLoaderLog, "deck_loader");
inline Q_LOGGING_CATEGORY(DeckLoaderLog, "deck_loader")
class DeckLoader : public QObject
class DeckLoader : public QObject
{
Q_OBJECT
signals:
@@ -24,6 +22,27 @@ signals:
void loadFinished(bool success);
public:
enum FileFormat
{
PlainTextFormat,
CockatriceFormat
};
/**
* @brief Information about where the deck was loaded from.
*
* For local decks, the remoteDeckId field will always be -1.
* For remote decks, fileName will be empty and fileFormat will always be CockatriceFormat
*/
struct LoadInfo
{
static constexpr int NON_REMOTE_ID = -1;
QString fileName = "";
FileFormat fileFormat = CockatriceFormat;
int remoteDeckId = NON_REMOTE_ID;
};
/**
* Supported file extensions for decklist files
*/
@@ -41,26 +60,44 @@ public:
};
private:
LoadedDeck loadedDeck;
DeckList *deckList;
LoadInfo lastLoadInfo;
public:
DeckLoader(QObject *parent);
DeckLoader(QObject *parent, DeckList *_deckList);
DeckLoader(const DeckLoader &) = delete;
DeckLoader &operator=(const DeckLoader &) = delete;
[[nodiscard]] bool hasNotBeenLoaded() const
const LoadInfo &getLastLoadInfo() const
{
return loadedDeck.lastLoadInfo.isEmpty();
return lastLoadInfo;
}
bool loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bool userRequest = false);
bool loadFromFileAsync(const QString &fileName, DeckFileFormat::Format fmt, bool userRequest);
void setLastLoadInfo(const LoadInfo &info)
{
lastLoadInfo = info;
}
[[nodiscard]] bool hasNotBeenLoaded() const
{
return lastLoadInfo.fileName.isEmpty() && lastLoadInfo.remoteDeckId == LoadInfo::NON_REMOTE_ID;
}
static void clearSetNamesAndNumbers(const DeckList *deckList);
static FileFormat getFormatFromName(const QString &fileName);
bool loadFromFile(const QString &fileName, FileFormat fmt, bool userRequest = false);
bool loadFromFileAsync(const QString &fileName, FileFormat fmt, bool userRequest);
bool loadFromRemote(const QString &nativeString, int remoteDeckId);
bool saveToFile(const QString &fileName, DeckFileFormat::Format fmt);
bool updateLastLoadedTimestamp(const QString &fileName, DeckFileFormat::Format fmt);
bool saveToFile(const QString &fileName, FileFormat fmt);
bool updateLastLoadedTimestamp(const QString &fileName, FileFormat fmt);
static QString exportDeckToDecklist(const DeckList *deckList, DecklistWebsite website);
static void setProviderIdToPreferredPrinting(const DeckList *deckList);
static void resolveSetNameAndNumberToProviderID(const DeckList *deckList);
static void saveToClipboard(const DeckList *deckList, bool addComments = true, bool addSetNameAndNumber = true);
static bool saveToStream_Plain(QTextStream &out,
const DeckList *deckList,
@@ -76,21 +113,13 @@ public:
bool convertToCockatriceFormat(QString fileName);
LoadedDeck &getDeck()
DeckList *getDeckList()
{
return loadedDeck;
}
const LoadedDeck &getDeck() const
{
return loadedDeck;
}
void setDeck(const LoadedDeck &deck)
{
loadedDeck = deck;
return deckList;
}
private:
static void printDeckListNode(QTextCursor *cursor, const InnerDecklistNode *node);
static void printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node);
static void saveToStream_DeckHeader(QTextStream &out, const DeckList *deckList);
static void saveToStream_DeckZone(QTextStream &out,
@@ -102,6 +131,9 @@ private:
QList<DecklistCardNode *> cards,
bool addComments = true,
bool addSetNameAndNumber = true);
[[nodiscard]] static QString getCardZoneFromName(const QString &cardName, QString currentZoneName);
[[nodiscard]] static QString getCompleteCardName(const QString &cardName);
};
#endif

View File

@@ -1,11 +0,0 @@
#include "loaded_deck.h"
bool LoadedDeck::LoadInfo::isEmpty() const
{
return fileName.isEmpty() && remoteDeckId == NON_REMOTE_ID;
}
bool LoadedDeck::isEmpty() const
{
return deckList.isEmpty() && lastLoadInfo.isEmpty();
}

View File

@@ -1,39 +0,0 @@
#ifndef COCKATRICE_LOADED_DECK_H
#define COCKATRICE_LOADED_DECK_H
#include "deck_file_format.h"
#include "libcockatrice/deck_list/deck_list.h"
#include <QString>
/**
* @brief Represents a deck that was loaded from somewhere.
* Contains the DeckList itself, as well as info about where it was loaded from.
*/
struct LoadedDeck
{
/**
* @brief Information about where the deck was loaded from.
*
* For local decks, the remoteDeckId field will always be -1.
* For remote decks, fileName will be empty and fileFormat will always be CockatriceFormat
*/
struct LoadInfo
{
static constexpr int NON_REMOTE_ID = -1;
QString fileName = "";
DeckFileFormat::Format fileFormat = DeckFileFormat::Cockatrice;
int remoteDeckId = NON_REMOTE_ID;
bool isEmpty() const;
};
DeckList deckList; ///< The decklist itself
LoadInfo lastLoadInfo = {}; ///< info about where the deck was loaded from
bool isEmpty() const;
};
#endif // COCKATRICE_LOADED_DECK_H

View File

@@ -39,6 +39,10 @@ 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);
@@ -273,7 +277,7 @@ void CardInfoPictureWidget::leaveEvent(QEvent *event)
if (hoverToZoomEnabled) {
hoverTimer->stop();
destroyEnlargedPixmapWidget();
enlargedPixmapWidget->hide();
}
if (raiseOnEnter) {
@@ -290,7 +294,7 @@ void CardInfoPictureWidget::moveEvent(QMoveEvent *event)
QWidget::moveEvent(event);
hoverTimer->stop();
destroyEnlargedPixmapWidget();
enlargedPixmapWidget->hide();
if (animation->state() == QAbstractAnimation::Running) {
return;
@@ -306,7 +310,7 @@ void CardInfoPictureWidget::mouseMoveEvent(QMouseEvent *event)
{
QWidget::mouseMoveEvent(event);
if (hoverToZoomEnabled && enlargedPixmapWidget && enlargedPixmapWidget->isVisible()) {
if (hoverToZoomEnabled && enlargedPixmapWidget->isVisible()) {
const QPoint cursorPos = QCursor::pos();
const QRect screenGeometry = QGuiApplication::screenAt(cursorPos)->geometry();
const QSize widgetSize = enlargedPixmapWidget->size();
@@ -340,7 +344,7 @@ void CardInfoPictureWidget::mousePressEvent(QMouseEvent *event)
void CardInfoPictureWidget::hideEvent(QHideEvent *event)
{
destroyEnlargedPixmapWidget();
enlargedPixmapWidget->hide();
QWidget::hideEvent(event);
}
@@ -440,19 +444,12 @@ QMenu *CardInfoPictureWidget::createAddToOpenDeckMenu()
* If card information is available, the enlarged pixmap is loaded, positioned near the cursor,
* and displayed.
*/
void CardInfoPictureWidget::showEnlargedPixmap()
void CardInfoPictureWidget::showEnlargedPixmap() const
{
if (!exactCard) {
return;
}
// Lazy creation of the enlarged widget
if (!enlargedPixmapWidget) {
enlargedPixmapWidget = new CardInfoPictureEnlargedWidget(const_cast<CardInfoPictureWidget *>(this)->window());
enlargedPixmapWidget->hide();
connect(this, &QObject::destroyed, enlargedPixmapWidget, &CardInfoPictureEnlargedWidget::deleteLater);
}
const QSize enlargedSize(static_cast<int>(size().width() * 2), static_cast<int>(size().width() * aspectRatio * 2));
enlargedPixmapWidget->setCardPixmap(exactCard, enlargedSize);
@@ -463,6 +460,7 @@ void CardInfoPictureWidget::showEnlargedPixmap()
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;
}
@@ -474,11 +472,3 @@ void CardInfoPictureWidget::showEnlargedPixmap()
enlargedPixmapWidget->show();
}
void CardInfoPictureWidget::destroyEnlargedPixmapWidget()
{
if (enlargedPixmapWidget) {
enlargedPixmapWidget->deleteLater();
enlargedPixmapWidget = nullptr;
}
}

View File

@@ -63,8 +63,7 @@ protected:
{
return resizedPixmap;
}
void showEnlargedPixmap();
void destroyEnlargedPixmapWidget();
void showEnlargedPixmap() const;
private:
ExactCard exactCard;

View File

@@ -17,6 +17,7 @@
* @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.
*/

View File

@@ -21,26 +21,32 @@ void DeckListStatisticsAnalyzer::update()
manaCurveMap.clear();
manaDevotionMap.clear();
QList<ExactCard> cards = model->getCards();
auto nodes = model->getDeckList()->getCardNodes();
for (const ExactCard &card : cards) {
// ---- Mana curve ----
if (config.computeManaCurve) {
manaCurveMap[card.getInfo().getCmc().toInt()]++;
}
for (auto *node : nodes) {
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(node->getName());
if (!info)
continue;
// ---- 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();
}
for (int i = 0; i < node->getNumber(); ++i) {
// ---- Mana curve ----
if (config.computeManaCurve) {
manaCurveMap[info->getCmc().toInt()]++;
}
// ---- Devotion ----
if (config.computeDevotion) {
auto devo = countManaSymbols(card.getInfo().getManaCost());
for (auto &d : devo)
manaDevotionMap[d.first] += d.second;
// ---- 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;
}
}
}

View File

@@ -56,7 +56,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
deckModel->setObjectName("deckModel");
connect(deckModel, &DeckListModel::deckHashChanged, this, &DeckEditorDeckDockWidget::updateHash);
deckLoader = new DeckLoader(this);
deckLoader = new DeckLoader(this, deckModel->getDeckList());
proxy = new DeckListStyleProxy(this);
proxy->setSourceModel(deckModel);
@@ -124,12 +124,6 @@ void DeckEditorDeckDockWidget::createDeckDock()
quickSettingsWidget->addSettingsWidget(showBannerCardCheckBox);
quickSettingsWidget->addSettingsWidget(showTagsWidgetCheckBox);
formatLabel = new QLabel(this);
formatComboBox = new QComboBox(this);
formatComboBox->addItem(tr("Loading Database..."));
formatComboBox->setEnabled(false); // Disable until loaded
commentsLabel = new QLabel();
commentsLabel->setObjectName("commentsLabel");
commentsEdit = new QTextEdit;
@@ -214,16 +208,13 @@ void DeckEditorDeckDockWidget::createDeckDock()
upperLayout->addWidget(commentsLabel, 1, 0);
upperLayout->addWidget(commentsEdit, 1, 1);
upperLayout->addWidget(formatLabel, 2, 0);
upperLayout->addWidget(formatComboBox, 2, 1);
upperLayout->addWidget(bannerCardLabel, 2, 0);
upperLayout->addWidget(bannerCardComboBox, 2, 1);
upperLayout->addWidget(bannerCardLabel, 3, 0);
upperLayout->addWidget(bannerCardComboBox, 3, 1);
upperLayout->addWidget(deckTagsDisplayWidget, 3, 1);
upperLayout->addWidget(deckTagsDisplayWidget, 4, 1);
upperLayout->addWidget(activeGroupCriteriaLabel, 5, 0);
upperLayout->addWidget(activeGroupCriteriaComboBox, 5, 1);
upperLayout->addWidget(activeGroupCriteriaLabel, 4, 0);
upperLayout->addWidget(activeGroupCriteriaComboBox, 4, 1);
hashLabel1 = new QLabel();
hashLabel1->setObjectName("hashLabel1");
@@ -272,46 +263,6 @@ void DeckEditorDeckDockWidget::createDeckDock()
refreshShortcuts();
retranslateUi();
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
&DeckEditorDeckDockWidget::initializeFormats);
if (CardDatabaseManager::getInstance()->getLoadStatus() == LoadStatus::Ok) {
initializeFormats();
}
}
void DeckEditorDeckDockWidget::initializeFormats()
{
QMap<QString, int> allFormats = CardDatabaseManager::query()->getAllFormatsWithCount();
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
}
if (!deckModel->getDeckList()->getGameFormat().isEmpty()) {
deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat());
formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat()));
} else {
// Ensure no selection is visible initially
formatComboBox->setCurrentIndex(-1);
}
connect(formatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
if (index >= 0) {
QString formatKey = formatComboBox->itemData(index).toString();
deckModel->setActiveFormat(formatKey);
} else {
deckModel->setActiveFormat(QString()); // clear format if deselected
}
emit deckModified();
});
}
ExactCard DeckEditorDeckDockWidget::getCurrentCard()
@@ -347,7 +298,7 @@ void DeckEditorDeckDockWidget::updateCard(const QModelIndex /*&current*/, const
void DeckEditorDeckDockWidget::updateName(const QString &name)
{
emit requestDeckHistorySave(
QString(tr("Rename deck to \"%1\" from \"%2\"")).arg(name).arg(deckLoader->getDeck().deckList.getName()));
QString(tr("Rename deck to \"%1\" from \"%2\"")).arg(name).arg(deckLoader->getDeckList()->getName()));
deckModel->getDeckList()->setName(name);
deckEditor->setModified(name.isEmpty());
emit nameChanged();
@@ -357,7 +308,7 @@ void DeckEditorDeckDockWidget::updateName(const QString &name)
void DeckEditorDeckDockWidget::updateComments()
{
emit requestDeckHistorySave(tr("Updated comments (was %1 chars, now %2 chars)")
.arg(deckLoader->getDeck().deckList.getComments().size())
.arg(deckLoader->getDeckList()->getComments().size())
.arg(commentsEdit->toPlainText().size()));
deckModel->getDeckList()->setComments(commentsEdit->toPlainText());
@@ -386,7 +337,7 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
// Collect unique (name, providerId) pairs
QSet<QPair<QString, QString>> bannerCardSet;
QList<const DecklistCardNode *> cardsInDeck = deckModel->getDeckList()->getCardNodes();
QList<DecklistCardNode *> cardsInDeck = deckModel->getDeckList()->getCardNodes();
for (auto currentCard : cardsInDeck) {
if (!CardDatabaseManager::query()->getCard(currentCard->toCardRef())) {
@@ -474,12 +425,13 @@ void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck()
/**
* Sets the currently active deck for this tab
* @param _deck The deck.
* @param _deck The deck. Takes ownership of the object
*/
void DeckEditorDeckDockWidget::setDeck(const LoadedDeck &_deck)
void DeckEditorDeckDockWidget::setDeck(DeckLoader *_deck)
{
deckLoader->setDeck(_deck);
deckModel->setDeckList(&deckLoader->getDeck().deckList);
deckLoader = _deck;
deckLoader->setParent(this);
deckModel->setDeckList(deckLoader->getDeckList());
connect(deckLoader, &DeckLoader::deckLoaded, deckModel, &DeckListModel::rebuildTree);
emit requestDeckHistoryClear();
@@ -514,12 +466,6 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
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();
}
DeckLoader *DeckEditorDeckDockWidget::getDeckLoader()
@@ -642,7 +588,7 @@ bool DeckEditorDeckDockWidget::swapCard(const QModelIndex &currentIndex)
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;
}
@@ -663,8 +609,18 @@ void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString z
}
deckView->clearSelection();
deckView->setCurrentIndex(proxy->mapToSource(idx));
offsetCountAtIndex(idx, -1);
setCurrentProxyIndex(idx);
offsetCountAtIndex(proxy->mapFromSource(idx), -1);
}
void DeckEditorDeckDockWidget::setCurrentProxyIndex(const QModelIndex &index)
{
deckView->setCurrentIndex(proxy->mapFromSource(index));
}
void DeckEditorDeckDockWidget::scrollToProxyIndex(const QModelIndex &index)
{
deckView->setCurrentIndex(proxy->mapFromSource(index));
}
void DeckEditorDeckDockWidget::actDecrementSelection()
@@ -778,8 +734,6 @@ void DeckEditorDeckDockWidget::retranslateUi()
showTagsWidgetCheckBox->setText(tr("Show tags selection menu"));
commentsLabel->setText(tr("&Comments:"));
activeGroupCriteriaLabel->setText(tr("Group by:"));
formatLabel->setText(tr("Format:"));
hashLabel1->setText(tr("Hash:"));
aIncrement->setText(tr("&Increment number"));

View File

@@ -57,7 +57,7 @@ public:
public slots:
void cleanDeck();
void updateBannerCardComboBox();
void setDeck(const LoadedDeck &_deck);
void setDeck(DeckLoader *_deck);
void syncDisplayWidgetsToModel();
void sortDeckModelToDeckView();
DeckLoader *getDeckLoader();
@@ -69,8 +69,9 @@ public slots:
void actSwapCard();
void actRemoveCard();
void offsetCountAtIndex(const QModelIndex &idx, int offset);
void initializeFormats();
void expandAll();
void setCurrentProxyIndex(const QModelIndex &index);
void scrollToProxyIndex(const QModelIndex &index);
signals:
void nameChanged();
@@ -101,8 +102,6 @@ private:
LineEditUnfocusable *hashLabel;
QLabel *activeGroupCriteriaLabel;
QComboBox *activeGroupCriteriaComboBox;
QLabel *formatLabel;
QComboBox *formatComboBox;
QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard;

View File

@@ -23,7 +23,8 @@ QVariant DeckListStyleProxy::data(const QModelIndex &index, int role) const
if (role == Qt::BackgroundRole) {
if (isCard) {
const bool legal = QIdentityProxyModel::data(index, DeckRoles::IsLegalRole).toBool();
const bool legal =
true; // TODO: Not implemented yet. QIdentityProxyModel::data(index, DeckRoles::IsLegalRole).toBool();
int base = 255 - (index.row() % 2) * 30;
return legal ? QBrush(QColor(base, base, base)) : QBrush(QColor(255, base / 3, base / 3));
} else {

View File

@@ -109,7 +109,7 @@ void DlgCreateGame::sharedCtor()
gameSetupOptionsLayout->addWidget(startingLifeTotalLabel, 0, 0);
gameSetupOptionsLayout->addWidget(startingLifeTotalEdit, 0, 1);
gameSetupOptionsLayout->addWidget(shareDecklistsOnLoadCheckBox, 1, 0);
if (room && room->getUserInfo()->user_level() & ServerInfo_User::IsJudge) {
if (room->getUserInfo()->user_level() & ServerInfo_User::IsJudge) {
gameSetupOptionsLayout->addWidget(createGameAsJudgeCheckBox, 2, 0);
} else {
createGameAsJudgeCheckBox->setChecked(false);

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,7 +1,6 @@
#include "dlg_load_deck_from_clipboard.h"
#include "../../../client/settings/cache_settings.h"
#include "../../deck_loader/card_node_function.h"
#include "../../deck_loader/deck_loader.h"
#include "dlg_settings.h"
@@ -66,26 +65,26 @@ void AbstractDlgDeckTextEdit::setText(const QString &text)
}
/**
* Tries to load the current contents of the contentsEdit into the deckList
* Tries to load the current contents of the contentsEdit into the DeckLoader
*
* @param deckList The deckList to load the deck into
* @param deckLoader The DeckLoader to load the deck into
* @return Whether the loading was successful
*/
bool AbstractDlgDeckTextEdit::loadIntoDeck(DeckList &deckList) const
bool AbstractDlgDeckTextEdit::loadIntoDeck(DeckLoader *deckLoader) const
{
QString buffer = contentsEdit->toPlainText();
if (buffer.contains("<cockatrice_deck version=\"1\">")) {
return deckList.loadFromString_Native(buffer);
return deckLoader->getDeckList()->loadFromString_Native(buffer);
}
QTextStream stream(&buffer);
if (deckList.loadFromStream_Plain(stream, true)) {
if (deckLoader->getDeckList()->loadFromStream_Plain(stream, true)) {
if (loadSetNameAndNumberCheckBox->isChecked()) {
deckList.forEachCard(CardNodeFunction::ResolveProviderId());
DeckLoader::resolveSetNameAndNumberToProviderID(deckLoader->getDeckList());
} else {
deckList.forEachCard(CardNodeFunction::ClearPrintingData());
DeckLoader::clearSetNamesAndNumbers(deckLoader->getDeckList());
}
return true;
}
@@ -108,7 +107,7 @@ void AbstractDlgDeckTextEdit::keyPressEvent(QKeyEvent *event)
*
* @param parent The parent widget
*/
DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : AbstractDlgDeckTextEdit(parent)
DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : AbstractDlgDeckTextEdit(parent), deckList(nullptr)
{
setWindowTitle(tr("Load deck from clipboard"));
@@ -122,6 +121,8 @@ void DlgLoadDeckFromClipboard::actRefresh()
void DlgLoadDeckFromClipboard::actOK()
{
deckList = new DeckLoader(this);
if (loadIntoDeck(deckList)) {
accept();
} else {
@@ -132,15 +133,18 @@ void DlgLoadDeckFromClipboard::actOK()
/**
* Creates the dialog window for the "Edit deck in clipboard" action
*
* @param _deckList The existing deck in the deck editor.
* @param _deckLoader The existing deck in the deck editor. Copies the instance
* @param _annotated Whether to add annotations to the text that is loaded from the deck
* @param parent The parent widget
*/
DlgEditDeckInClipboard::DlgEditDeckInClipboard(const DeckList &_deckList, bool _annotated, QWidget *parent)
: AbstractDlgDeckTextEdit(parent), deckList(_deckList), annotated(_annotated)
DlgEditDeckInClipboard::DlgEditDeckInClipboard(DeckLoader *_deckLoader, bool _annotated, QWidget *parent)
: AbstractDlgDeckTextEdit(parent), annotated(_annotated)
{
setWindowTitle(tr("Edit deck in clipboard"));
deckLoader = new DeckLoader(this, _deckLoader->getDeckList());
deckLoader->setParent(this);
DlgEditDeckInClipboard::actRefresh();
}
@@ -160,12 +164,12 @@ static QString deckListToString(const DeckList *deckList, bool addComments)
void DlgEditDeckInClipboard::actRefresh()
{
setText(deckListToString(&deckList, annotated));
setText(deckListToString(deckLoader->getDeckList(), annotated));
}
void DlgEditDeckInClipboard::actOK()
{
if (loadIntoDeck(deckList)) {
if (loadIntoDeck(deckLoader)) {
accept();
} else {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck list."));

View File

@@ -8,11 +8,10 @@
#ifndef DLG_LOAD_DECK_FROM_CLIPBOARD_H
#define DLG_LOAD_DECK_FROM_CLIPBOARD_H
#include "../../deck_loader/loaded_deck.h"
#include <QCheckBox>
#include <QDialog>
class DeckLoader;
class QPlainTextEdit;
class QPushButton;
@@ -36,13 +35,15 @@ public:
/**
* Gets the loaded deck. Only call this method after this dialog window has been successfully exec'd.
*
* @return The loaded decklist
* 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
*/
[[nodiscard]] virtual const DeckList &getDeckList() = 0;
[[nodiscard]] virtual DeckLoader *getDeckList() const = 0;
protected:
void setText(const QString &text);
bool loadIntoDeck(DeckList &deckList) const;
bool loadIntoDeck(DeckLoader *deckLoader) const;
void keyPressEvent(QKeyEvent *event) override;
protected slots:
@@ -61,12 +62,12 @@ protected slots:
void actRefresh() override;
private:
DeckList deckList;
DeckLoader *deckList;
public:
explicit DlgLoadDeckFromClipboard(QWidget *parent = nullptr);
[[nodiscard]] const DeckList &getDeckList() override
[[nodiscard]] DeckLoader *getDeckList() const override
{
return deckList;
}
@@ -83,15 +84,15 @@ protected slots:
void actRefresh() override;
private:
DeckList deckList;
DeckLoader *deckLoader;
bool annotated;
public:
explicit DlgEditDeckInClipboard(const DeckList &_deckList, bool _annotated, QWidget *parent = nullptr);
explicit DlgEditDeckInClipboard(DeckLoader *_deckLoader, bool _annotated, QWidget *parent = nullptr);
[[nodiscard]] const DeckList &getDeckList() override
[[nodiscard]] DeckLoader *getDeckList() const override
{
return deckList;
return deckLoader;
}
};

View File

@@ -8,7 +8,6 @@
#include <QJsonObject>
#include <QMessageBox>
#include <QNetworkReply>
#include <version_string.h>
DlgLoadDeckFromWebsite::DlgLoadDeckFromWebsite(QWidget *parent) : QDialog(parent)
{
@@ -68,7 +67,6 @@ void DlgLoadDeckFromWebsite::accept()
}
QNetworkRequest request(QUrl(info.fullUrl));
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
QNetworkReply *reply = nam->get(request);
QEventLoop loop;
@@ -97,11 +95,11 @@ void DlgLoadDeckFromWebsite::accept()
}
// Parse the plain text deck here
DeckList deckList;
DeckLoader *loader = new DeckLoader(this);
QTextStream stream(&deckText);
deckList.loadFromStream_Plain(stream, false);
deckList.forEachCard(CardNodeFunction::ResolveProviderId());
deck = deckList;
loader->getDeckList()->loadFromStream_Plain(stream, false);
DeckLoader::resolveSetNameAndNumberToProviderID(loader->getDeckList());
deck = loader;
QDialog::accept();
return;

View File

@@ -26,9 +26,9 @@ public:
explicit DlgLoadDeckFromWebsite(QWidget *parent);
void retranslateUi();
bool testValidUrl();
DeckList deck;
DeckLoader *deck;
const DeckList &getDeck() const
DeckLoader *getDeck()
{
return deck;
}

View File

@@ -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;

View File

@@ -1,6 +1,5 @@
#include "dlg_select_set_for_cards.h"
#include "../../deck_loader/card_node_function.h"
#include "../../deck_loader/deck_loader.h"
#include "../interface/widgets/cards/card_info_picture_widget.h"
#include "../interface/widgets/general/layout_containers/flow_widget.h"
@@ -178,7 +177,7 @@ void DlgSelectSetForCards::actOK()
void DlgSelectSetForCards::actClear()
{
emit deckAboutToBeModified(tr("Cleared all printing information."));
model->getDeckList()->forEachCard(CardNodeFunction::ClearPrintingData());
DeckLoader::clearSetNamesAndNumbers(model->getDeckList());
emit deckModified();
accept();
}
@@ -186,8 +185,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());
DeckLoader::clearSetNamesAndNumbers(model->getDeckList());
DeckLoader::setProviderIdToPreferredPrinting(model->getDeckList());
emit deckModified();
accept();
}
@@ -224,10 +223,14 @@ QMap<QString, int> DlgSelectSetForCards::getSetsForCards()
if (!model)
return setCounts;
QList<QString> cardNames = model->getCardNames();
DeckList *decklist = model->getDeckList();
if (!decklist)
return setCounts;
for (auto cardName : cardNames) {
CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName);
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes();
for (auto currentCard : cardsInDeck) {
CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
if (!infoPtr)
continue;
@@ -263,15 +266,19 @@ void DlgSelectSetForCards::updateCardLists()
}
}
QList<QString> cardNames = model->getCardNames();
DeckList *decklist = model->getDeckList();
if (!decklist)
return;
for (auto cardName : cardNames) {
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes();
for (auto currentCard : cardsInDeck) {
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(cardName)) {
if (it.value().contains(currentCard->getName())) {
found = true;
foundSetName = it.key(); // Store the set name where it was found
break; // Stop at the first match
@@ -280,16 +287,16 @@ void DlgSelectSetForCards::updateCardLists()
if (!found) {
// The card was not in any selected set
ExactCard card = CardDatabaseManager::query()->getCard({cardName});
ExactCard card = CardDatabaseManager::query()->getCard({currentCard->getName()});
CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(uneditedCardsFlowWidget);
picture_widget->setCard(card);
uneditedCardsFlowWidget->addWidget(picture_widget);
} else {
ExactCard card =
CardDatabaseManager::query()->getCard({cardName, CardDatabaseManager::getInstance()
->query()
->getSpecificPrinting(cardName, foundSetName, "")
.getUuid()});
ExactCard card = CardDatabaseManager::query()->getCard(
{currentCard->getName(), CardDatabaseManager::getInstance()
->query()
->getSpecificPrinting(currentCard->getName(), foundSetName, "")
.getUuid()});
CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(modifiedCardsFlowWidget);
picture_widget->setCard(card);
modifiedCardsFlowWidget->addWidget(picture_widget);
@@ -348,16 +355,20 @@ QMap<QString, QStringList> DlgSelectSetForCards::getCardsForSets()
if (!model)
return setCards;
QList<QString> cardNames = model->getCardNames();
DeckList *decklist = model->getDeckList();
if (!decklist)
return setCards;
for (auto cardName : cardNames) {
CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName);
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes();
for (auto currentCard : cardsInDeck) {
CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
if (!infoPtr)
continue;
SetToPrintingsMap setMap = infoPtr->getSets();
for (auto it = setMap.begin(); it != setMap.end(); ++it) {
setCards[it.key()].append(cardName);
setCards[it.key()].append(currentCard->getName());
}
}

View File

@@ -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?"),

View File

@@ -20,6 +20,10 @@ 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",
DeckLoader::CockatriceFormat, false);
gradientColors = extractDominantColors(background);
@@ -68,20 +72,13 @@ void HomeWidget::initializeBackgroundFromSource()
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
break;
case BackgroundSources::DeckFileArt:
loadBackgroundSourceDeck();
backgroundSourceDeck->loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod",
DeckLoader::CockatriceFormat, false);
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());
@@ -98,7 +95,7 @@ void HomeWidget::updateRandomCard()
newCard.getCardPtr()->getProperty("layout") != "normal");
break;
case BackgroundSources::DeckFileArt:
QList<CardRef> cardRefs = backgroundSourceDeck.getCardRefList();
QList<CardRef> cardRefs = backgroundSourceDeck->getDeckList()->getCardRefList();
ExactCard oldCard = backgroundSourceCard->getCard();
if (!cardRefs.empty()) {
@@ -186,7 +183,7 @@ QGroupBox *HomeWidget::createButtons()
auto visualDeckEditorButton = new HomeStyledButton(tr("Create New Deck"), gradientColors);
connect(visualDeckEditorButton, &QPushButton::clicked, tabSupervisor,
[this] { tabSupervisor->openDeckInNewTab(LoadedDeck()); });
[this] { tabSupervisor->openDeckInNewTab(nullptr); });
boxLayout->addWidget(visualDeckEditorButton);
auto visualDeckStorageButton = new HomeStyledButton(tr("Browse Decks"), gradientColors);
connect(visualDeckStorageButton, &QPushButton::clicked, tabSupervisor,

View File

@@ -40,12 +40,10 @@ private:
TabSupervisor *tabSupervisor;
QPixmap background;
CardInfoPictureArtCropWidget *backgroundSourceCard = nullptr;
DeckList backgroundSourceDeck;
DeckLoader *backgroundSourceDeck;
QPixmap overlay;
QPair<QColor, QColor> gradientColors;
HomeStyledButton *connectButton;
void loadBackgroundSourceDeck();
};
#endif // HOME_WIDGET_H

View File

@@ -190,7 +190,7 @@ void CardAmountWidget::addPrinting(const QString &zone)
newCardIndex = deckModel->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(),
rootCard.getPrinting().getProperty("num"));
deckView->setCurrentIndex(newCardIndex);
deckEditor->deckDockWidget->setCurrentProxyIndex(newCardIndex);
deckView->setFocus(Qt::FocusReason::MouseFocusReason);
deckEditor->setModified(true);
}
@@ -256,7 +256,7 @@ void CardAmountWidget::offsetCountAtIndex(const QModelIndex &idx, int offset)
const int count = deckModel->data(numberIndex, Qt::EditRole).toInt();
const int new_count = count + offset;
deckView->setCurrentIndex(numberIndex);
deckEditor->deckDockWidget->setCurrentProxyIndex(numberIndex);
if (new_count <= 0) {
deckModel->removeRow(idx.row(), idx.parent());
@@ -306,12 +306,19 @@ int CardAmountWidget::countCardsInZone(const QString &deckZone)
return -1;
}
QList<ExactCard> cards = deckModel->getCardsForZone(deckZone);
DeckList *decklist = deckModel->getDeckList();
if (!decklist) {
return -1;
}
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes({deckZone});
int count = 0;
for (auto currentCard : cards) {
if (currentCard.getPrinting().getUuid() == rootCard.getPrinting().getProperty("uuid")) {
count++;
for (auto currentCard : cardsInDeck) {
for (int k = 0; k < currentCard->getNumber(); ++k) {
if (currentCard->getCardProviderId() == rootCard.getPrinting().getProperty("uuid")) {
count++;
}
}
}

View File

@@ -185,7 +185,7 @@ void PrintingSelector::selectCard(const int changeBy)
}
if (nextIndex.isValid()) {
deckView->setCurrentIndex(nextIndex);
deckEditor->deckDockWidget->setCurrentProxyIndex(nextIndex);
deckView->setFocus(Qt::FocusReason::MouseFocusReason);
}
}

View File

@@ -206,7 +206,7 @@ void ChatView::appendMessage(QString message,
defaultFormat = QTextCharFormat();
if (!isUserMessage) {
if (messageType == Event_RoomSay::ChatHistory) {
defaultFormat.setForeground(Qt::gray); //! \todo hardcoded color
defaultFormat.setForeground(Qt::gray); // FIXME : 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); //! \todo hardcoded color
defaultFormat.setForeground(Qt::darkGreen); // FIXME : hardcoded color
defaultFormat.setFontWeight(QFont::Bold);
}
}

View File

@@ -155,7 +155,7 @@ void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, QString zoneNam
QModelIndex newCardIndex = deckDockWidget->deckModel->addCard(card, zoneName);
deckDockWidget->expandAll();
deckDockWidget->deckView->clearSelection();
deckDockWidget->deckView->setCurrentIndex(newCardIndex);
deckDockWidget->setCurrentProxyIndex(newCardIndex);
setModified(true);
databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length());
@@ -213,22 +213,22 @@ void AbstractTabDeckEditor::actSwapCard(const ExactCard &card, const QString &zo
/**
* @brief Opens a deck in this tab.
* @param deck The deck
* @param deck DeckLoader object (takes ownership).
*/
void AbstractTabDeckEditor::openDeck(const LoadedDeck &deck)
void AbstractTabDeckEditor::openDeck(DeckLoader *deck)
{
setDeck(deck);
if (!deck.lastLoadInfo.fileName.isEmpty()) {
SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(deck.lastLoadInfo.fileName);
if (!deck->getLastLoadInfo().fileName.isEmpty()) {
SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(deck->getLastLoadInfo().fileName);
}
}
/**
* @brief Sets the currently active deck.
* @param _deck The deck
* @param _deck DeckLoader object.
*/
void AbstractTabDeckEditor::setDeck(const LoadedDeck &_deck)
void AbstractTabDeckEditor::setDeck(DeckLoader *_deck)
{
deckDockWidget->setDeck(_deck);
CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(getDeckList()->getCardRefList()));
@@ -265,8 +265,8 @@ void AbstractTabDeckEditor::setModified(bool _modified)
*/
bool AbstractTabDeckEditor::isBlankNewDeck() const
{
const LoadedDeck &loadedDeck = deckDockWidget->getDeckLoader()->getDeck();
return !modified && loadedDeck.isEmpty();
DeckLoader *deck = deckDockWidget->getDeckLoader();
return !modified && deck->getDeckList()->isBlankDeck() && deck->hasNotBeenLoaded();
}
/** @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(LoadedDeck());
emit openDeckEditor(nullptr);
return;
}
@@ -380,17 +380,19 @@ void AbstractTabDeckEditor::actOpenRecent(const QString &fileName)
*/
void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation)
{
DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(fileName);
DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(fileName);
auto l = DeckLoader(this);
if (l.loadFromFile(fileName, fmt, true)) {
auto *l = new DeckLoader(this);
if (l->loadFromFile(fileName, fmt, true)) {
if (deckOpenLocation == NEW_TAB) {
emit openDeckEditor(l.getDeck());
emit openDeckEditor(l);
l->deleteLater();
} else {
deckMenu->setSaveStatus(false);
openDeck(l.getDeck());
openDeck(l);
}
} else {
l->deleteLater();
QMessageBox::critical(this, tr("Error"), tr("Could not open deck at %1").arg(fileName));
}
deckMenu->setSaveStatus(true);
@@ -403,16 +405,16 @@ void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLo
*/
bool AbstractTabDeckEditor::actSaveDeck()
{
const LoadedDeck &loadedDeck = getDeckLoader()->getDeck();
if (loadedDeck.lastLoadInfo.remoteDeckId != LoadedDeck::LoadInfo::NON_REMOTE_ID) {
QString deckString = loadedDeck.deckList.writeToString_Native();
DeckLoader *const deck = getDeckLoader();
if (deck->getLastLoadInfo().remoteDeckId != DeckLoader::LoadInfo::NON_REMOTE_ID) {
QString deckString = deck->getDeckList()->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<google::protobuf::uint32>(loadedDeck.lastLoadInfo.remoteDeckId));
cmd.set_deck_id(static_cast<google::protobuf::uint32>(deck->getLastLoadInfo().remoteDeckId));
cmd.set_deck_list(deckString.toStdString());
PendingCommand *pend = AbstractClient::prepareSessionCommand(cmd);
@@ -420,11 +422,9 @@ bool AbstractTabDeckEditor::actSaveDeck()
tabSupervisor->getClient()->sendCommand(pend);
return true;
}
if (loadedDeck.lastLoadInfo.fileName.isEmpty())
} else if (deck->getLastLoadInfo().fileName.isEmpty())
return actSaveDeckAs();
if (getDeckLoader()->saveToFile(loadedDeck.lastLoadInfo.fileName, loadedDeck.lastLoadInfo.fileFormat)) {
else if (deck->saveToFile(deck->getLastLoadInfo().fileName, deck->getLastLoadInfo().fileFormat)) {
setModified(false);
return true;
}
@@ -452,7 +452,7 @@ bool AbstractTabDeckEditor::actSaveDeckAs()
return false;
QString fileName = dialog.selectedFiles().at(0);
DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(fileName);
DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(fileName);
if (!getDeckLoader()->saveToFile(fileName, fmt)) {
QMessageBox::critical(
@@ -493,9 +493,9 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard()
return;
if (deckOpenLocation == NEW_TAB) {
emit openDeckEditor({.deckList = dlg.getDeckList()});
emit openDeckEditor(dlg.getDeckList());
} else {
setDeck({.deckList = dlg.getDeckList()});
setDeck(dlg.getDeckList());
setModified(true);
}
@@ -508,11 +508,11 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard()
*/
void AbstractTabDeckEditor::editDeckInClipboard(bool annotated)
{
DlgEditDeckInClipboard dlg(getDeckLoader()->getDeck().deckList, annotated, this);
DlgEditDeckInClipboard dlg(getDeckLoader(), annotated, this);
if (!dlg.exec())
return;
setDeck({dlg.getDeckList(), getDeckLoader()->getDeck().lastLoadInfo});
setDeck(dlg.getDeckList());
setModified(true);
deckMenu->setSaveStatus(true);
}
@@ -576,9 +576,9 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite()
return;
if (deckOpenLocation == NEW_TAB) {
emit openDeckEditor({.deckList = dlg.getDeck()});
emit openDeckEditor(dlg.getDeck());
} else {
setDeck({.deckList = dlg.getDeck()});
setDeck(dlg.getDeck());
setModified(true);
}
@@ -591,8 +591,8 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite()
*/
void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite website)
{
if (DeckList *deckList = getDeckList()) {
QString decklistUrlString = DeckLoader::exportDeckToDecklist(deckList, website);
if (DeckLoader *const deck = getDeckLoader()) {
QString decklistUrlString = deck->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.

View File

@@ -114,9 +114,9 @@ public:
virtual void retranslateUi() override = 0;
/** @brief Opens a deck in this tab.
* @param deck The deck to open
* @param deck Pointer to a DeckLoader object.
*/
void openDeck(const LoadedDeck &deck);
void openDeck(DeckLoader *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(const LoadedDeck &deck);
void openDeckEditor(DeckLoader *deckLoader);
/** @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(const LoadedDeck &_deck);
virtual void setDeck(DeckLoader *_deck);
/** @brief Helper for editing decks from the clipboard. */
void editDeckInClipboard(bool annotated);

View File

@@ -1,227 +0,0 @@
#ifndef COCKATRICE_ARCHIDEKT_FORMATS_H
#define COCKATRICE_ARCHIDEKT_FORMATS_H
#include <QHash>
#include <QString>
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<QString, DeckFormat> 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

View File

@@ -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
//! \todo games = {""};
//! \todo options = {""};
// TODO but not really important
// games = {""};
// 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
//! \todo globalCategories = {""};
// TODO but not really important
// globalCategories = {""};
}
void ArchidektApiResponseCard::debugPrint() const

View File

@@ -39,11 +39,6 @@ public:
return name;
};
int getDeckFormat() const
{
return deckFormat;
}
private:
int id;
QString name;

View File

@@ -1,12 +1,10 @@
#include "archidekt_api_response_deck_display_widget.h"
#include "../../../../../deck_loader/card_node_function.h"
#include "../../../../../deck_loader/deck_loader.h"
#include "../../../../cards/card_info_picture_with_text_overlay_widget.h"
#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 <QSortFilterProxyModel>
@@ -70,7 +68,7 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi
connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset);
model->getDeckList()->loadFromStream_Plain(deckStream, false);
model->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId());
DeckLoader::resolveSetNameAndNumberToProviderID(model->getDeckList());
model->rebuildTree();
@@ -90,14 +88,12 @@ void ArchidektApiResponseDeckDisplayWidget::onGroupCriteriaChange(const QString
void ArchidektApiResponseDeckDisplayWidget::actOpenInDeckEditor()
{
DeckList deckList(*model->getDeckList());
deckList.setName(response.getDeckName());
deckList.setGameFormat(
ArchidektFormats::formatToCockatriceName(ArchidektFormats::DeckFormat(response.getDeckFormat() - 1)));
auto loader = new DeckLoader(this);
loader->getDeckList()->loadFromString_Native(model->getDeckList()->writeToString_Native());
LoadedDeck loadedDeck = {deckList, {}};
loader->getDeckList()->setName(response.getDeckName());
emit openInDeckEditor(loadedDeck);
emit openInDeckEditor(loader);
}
void ArchidektApiResponseDeckDisplayWidget::clearAllDisplayWidgets()

View File

@@ -31,7 +31,7 @@
*
* ### Signals
* - `requestNavigation(QString url)` — triggered when navigation to a deck URL is requested.
* - `openInDeckEditor(const LoadedDeck &deck)` — emitted when the user chooses to open the deck
* - `openInDeckEditor(DeckLoader *loader)` — 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 deck LoadedDeck containing the deck data.
* @param loader Initialized DeckLoader containing the deck data.
*/
void openInDeckEditor(const LoadedDeck &deck);
void openInDeckEditor(DeckLoader *loader);
public:
/**
@@ -75,7 +75,7 @@ public:
void retranslateUi();
/**
* @brief Opens the deck in the deck editor.
* @brief Opens the deck in the deck editor via DeckLoader.
*/
void actOpenInDeckEditor();

View File

@@ -12,11 +12,10 @@
#include <QNetworkReply>
#include <QPixmap>
#include <QWidget>
#include <version_string.h>
#define ARCHIDEKT_DEFAULT_IMAGE "https://storage.googleapis.com/topdekt-user/images/archidekt_deck_card_shadow.jpg"
static QString timeAgo(const QString &timestamp)
QString timeAgo(const QString &timestamp)
{
QDateTime dt = QDateTime::fromString(timestamp, Qt::ISODate);
@@ -83,7 +82,6 @@ ArchidektApiResponseDeckEntryDisplayWidget::ArchidektApiResponseDeckEntryDisplay
imageUrl = response.getFeatured().isEmpty() ? QUrl(ARCHIDEKT_DEFAULT_IMAGE) : QUrl(response.getFeatured());
QNetworkRequest req(imageUrl);
req.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
QNetworkReply *reply = imageNetworkManager->get(req);
// tag the reply with "this" so we know it belongs to us later

View File

@@ -22,7 +22,6 @@
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/models/database/card/card_completer_proxy_model.h>
#include <libcockatrice/models/database/card/card_search_model.h>
#include <version_string.h>
TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
{
@@ -214,7 +213,7 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
minDeckSizeLogicCombo->addItems({"Exact", "", ""}); // Exact = unset, ≥ = GTE, ≤ = LTE
minDeckSizeLogicCombo->setCurrentIndex(1); // default GTE
connect(minDeckSizeSpin, qOverload<int>(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
connect(minDeckSizeSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
connect(minDeckSizeLogicCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
// Page number
@@ -224,7 +223,7 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
pageSpin->setRange(1, 9999);
pageSpin->setValue(1);
connect(pageSpin, qOverload<int>(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
connect(pageSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
// Page display
currentPageDisplay = new QWidget(container);
@@ -427,21 +426,18 @@ void TabArchidekt::doSearchImmediate()
{
QString url = buildSearchUrl();
QNetworkRequest req{QUrl(url)};
req.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(req);
}
void TabArchidekt::actNavigatePage(QString url)
{
QNetworkRequest request{QUrl(url)};
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(request);
}
void TabArchidekt::getTopDecks()
{
QNetworkRequest request{QUrl(buildSearchUrl())};
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(request);
}

View File

@@ -14,8 +14,10 @@ void EdhrecDeckApiResponse::fromJson(const QJsonArray &json)
deckList += cardlistValue.toString() + "\n";
}
deckLoader = new DeckLoader(nullptr);
QTextStream stream(&deckList);
deck.loadFromStream_Plain(stream, true);
deckLoader->getDeckList()->loadFromStream_Plain(stream, true);
}
void EdhrecDeckApiResponse::debugPrint() const

View File

@@ -21,7 +21,7 @@ public:
// Debug method for logging
void debugPrint() const;
DeckList deck;
DeckLoader *deckLoader;
};
#endif // EDHREC_DECK_API_RESPONSE_H

View File

@@ -25,7 +25,6 @@
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/models/database/card/card_completer_proxy_model.h>
#include <libcockatrice/models/database/card/card_search_model.h>
#include <version_string.h>
static bool canBeCommander(const CardInfoPtr &cardInfo)
{
@@ -167,7 +166,6 @@ void TabEdhRecMain::setCard(CardInfoPtr _cardToQuery, bool isCommander)
}
QNetworkRequest request{QUrl(url)};
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(request);
}
@@ -175,7 +173,6 @@ void TabEdhRecMain::setCard(CardInfoPtr _cardToQuery, bool isCommander)
void TabEdhRecMain::actNavigatePage(QString url)
{
QNetworkRequest request{QUrl("https://json.edhrec.com/pages" + url + ".json")};
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(request);
}
@@ -183,7 +180,6 @@ void TabEdhRecMain::actNavigatePage(QString url)
void TabEdhRecMain::getTopCards()
{
QNetworkRequest request{QUrl("https://json.edhrec.com/pages/top/year.json")};
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(request);
}
@@ -191,7 +187,6 @@ void TabEdhRecMain::getTopCards()
void TabEdhRecMain::getTopCommanders()
{
QNetworkRequest request{QUrl("https://json.edhrec.com/pages/commanders/year.json")};
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(request);
}
@@ -199,7 +194,7 @@ void TabEdhRecMain::getTopCommanders()
void TabEdhRecMain::getTopTags()
{
QNetworkRequest request{QUrl("https://json.edhrec.com/pages/tags.json")};
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
networkManager->get(request);
}
@@ -363,7 +358,7 @@ void TabEdhRecMain::processAverageDeckResponse(QJsonObject reply)
{
EdhrecAverageDeckApiResponse deckData;
deckData.fromJson(reply);
tabSupervisor->openDeckInNewTab({deckData.deck.deck, {}});
tabSupervisor->openDeckInNewTab(deckData.deck.deckLoader);
}
void TabEdhRecMain::prettyPrintJson(const QJsonValue &value, int indentLevel)

View File

@@ -12,6 +12,7 @@ class CardDatabaseDisplayModel;
class DeckListModel;
class QLabel;
class DeckLoader;
/**
* @class TabDeckEditor

View File

@@ -242,10 +242,10 @@ void TabDeckStorage::actOpenLocalDeck()
QString filePath = localDirModel->filePath(curLeft);
auto deckLoader = new DeckLoader(this);
if (!deckLoader->loadFromFile(filePath, DeckFileFormat::Cockatrice, true))
if (!deckLoader->loadFromFile(filePath, DeckLoader::CockatriceFormat, true))
continue;
emit openDeckEditor(deckLoader->getDeck());
emit openDeckEditor(deckLoader);
}
}
@@ -307,15 +307,13 @@ void TabDeckStorage::uploadDeck(const QString &filePath, const QString &targetPa
QFile deckFile(filePath);
QFileInfo deckFileInfo(deckFile);
DeckLoader deckLoader(this);
if (!deckLoader.loadFromFile(filePath, DeckFileFormat::Cockatrice)) {
DeckLoader deck(this);
if (!deck.loadFromFile(filePath, DeckLoader::CockatriceFormat)) {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck file"));
return;
}
DeckList deck = deckLoader.getDeck().deckList;
if (deck.getName().isEmpty()) {
if (deck.getDeckList()->getName().isEmpty()) {
bool ok;
QString deckName =
getTextWithMax(this, tr("Enter deck name"), tr("This decklist does not have a name.\nPlease enter a name:"),
@@ -324,12 +322,12 @@ void TabDeckStorage::uploadDeck(const QString &filePath, const QString &targetPa
return;
if (deckName.isEmpty())
deckName = tr("Unnamed deck");
deck.setName(deckName);
deck.getDeckList()->setName(deckName);
} else {
deck.setName(deck.getName().left(MAX_NAME_LENGTH));
deck.getDeckList()->setName(deck.getDeckList()->getName().left(MAX_NAME_LENGTH));
}
QString deckString = deck.writeToString_Native();
QString deckString = deck.getDeckList()->writeToString_Native();
if (deckString.length() > MAX_FILE_LENGTH) {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck file"));
return;
@@ -438,7 +436,7 @@ void TabDeckStorage::openRemoteDeckFinished(const Response &r, const CommandCont
if (!loader.loadFromRemote(QString::fromStdString(resp.deck()), cmd.deck_id()))
return;
emit openDeckEditor(loader.getDeck());
emit openDeckEditor(&loader);
}
void TabDeckStorage::actDownload()
@@ -494,12 +492,8 @@ void TabDeckStorage::downloadFinished(const Response &r,
const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext);
QString filePath = extraData.toString();
DeckList deckList = DeckList(QString::fromStdString(resp.deck()));
DeckLoader deckLoader(this);
deckLoader.setDeck({deckList, {}});
deckLoader.saveToFile(filePath, DeckFileFormat::Cockatrice);
DeckLoader deck(this, new DeckList(QString::fromStdString(resp.deck())));
deck.saveToFile(filePath, DeckLoader::CockatriceFormat);
}
void TabDeckStorage::actNewFolder()

View File

@@ -13,7 +13,6 @@
#include <libcockatrice/network/client/abstract/abstract_client.h>
struct LoadedDeck;
class ServerInfo_User;
class AbstractClient;
class QTreeView;
@@ -24,6 +23,7 @@ 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(const LoadedDeck &deck);
void openDeckEditor(DeckLoader *deckLoader);
};
#endif

View File

@@ -749,10 +749,11 @@ void TabGame::loadDeckForLocalPlayer(Player *localPlayer, int playerId, ServerIn
{
TabbedDeckViewContainer *deckViewContainer = deckViewContainers.value(playerId);
if (playerInfo.has_deck_list()) {
DeckList deckList = DeckList(QString::fromStdString(playerInfo.deck_list()));
CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(deckList.getCardRefList()));
deckViewContainer->playerDeckView->setDeck(deckList);
localPlayer->setDeck(deckList);
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);
}
}

View File

@@ -45,6 +45,7 @@ class ReplayTimelineWidget;
class CardZone;
class AbstractCardItem;
class CardItem;
class DeckLoader;
class QVBoxLayout;
class QHBoxLayout;
class GameReplay;
@@ -118,7 +119,7 @@ signals:
void containerProcessingStarted(const GameEventContext &context);
void containerProcessingDone();
void openMessageDialog(const QString &userName, bool focus);
void openDeckEditor(const LoadedDeck &deck);
void openDeckEditor(DeckLoader *deck);
void notIdle();
void phaseChanged(int phase);

View File

@@ -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(LoadedDeck()); });
connect(aTabDeckEditor, &QAction::triggered, this, [this] { addDeckEditorTab(nullptr); });
aTabVisualDeckEditor = new QAction(this);
connect(aTabVisualDeckEditor, &QAction::triggered, this, [this] { addVisualDeckEditorTab(LoadedDeck()); });
connect(aTabVisualDeckEditor, &QAction::triggered, this, [this] { addVisualDeckEditorTab(nullptr); });
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.
* @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance.
*/
void TabSupervisor::openDeckInNewTab(const LoadedDeck &deckToOpen)
void TabSupervisor::openDeckInNewTab(DeckLoader *deckToOpen)
{
int type = SettingsCache::instance().getDefaultDeckEditorType();
switch (type) {
@@ -868,12 +868,13 @@ void TabSupervisor::openDeckInNewTab(const LoadedDeck &deckToOpen)
/**
* Creates a new deck editor tab
* @param deckToOpen The deck to open in the tab.
* @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance.
*/
TabDeckEditor *TabSupervisor::addDeckEditorTab(const LoadedDeck &deckToOpen)
TabDeckEditor *TabSupervisor::addDeckEditorTab(DeckLoader *deckToOpen)
{
auto *tab = new TabDeckEditor(this);
tab->openDeck(deckToOpen);
if (deckToOpen)
tab->openDeck(deckToOpen);
connect(tab, &AbstractTabDeckEditor::deckEditorClosing, this, &TabSupervisor::deckEditorClosed);
connect(tab, &AbstractTabDeckEditor::openDeckEditor, this, &TabSupervisor::addDeckEditorTab);
myAddTab(tab);
@@ -882,10 +883,11 @@ TabDeckEditor *TabSupervisor::addDeckEditorTab(const LoadedDeck &deckToOpen)
return tab;
}
TabDeckEditorVisual *TabSupervisor::addVisualDeckEditorTab(const LoadedDeck &deckToOpen)
TabDeckEditorVisual *TabSupervisor::addVisualDeckEditorTab(DeckLoader *deckToOpen)
{
auto *tab = new TabDeckEditorVisual(this);
tab->openDeck(deckToOpen);
if (deckToOpen)
tab->openDeck(deckToOpen);
connect(tab, &AbstractTabDeckEditor::deckEditorClosing, this, &TabSupervisor::deckEditorClosed);
connect(tab, &AbstractTabDeckEditor::openDeckEditor, this, &TabSupervisor::addVisualDeckEditorTab);
myAddTab(tab);

View File

@@ -168,9 +168,9 @@ signals:
void showWindowIfHidden();
public slots:
void openDeckInNewTab(const LoadedDeck &deckToOpen);
TabDeckEditor *addDeckEditorTab(const LoadedDeck &deckToOpen);
TabDeckEditorVisual *addVisualDeckEditorTab(const LoadedDeck &deckToOpen);
void openDeckInNewTab(DeckLoader *deckToOpen);
TabDeckEditor *addDeckEditorTab(DeckLoader *deckToOpen);
TabDeckEditorVisual *addVisualDeckEditorTab(DeckLoader *deckToOpen);
TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab();
TabEdhRecMain *addEdhrecMainTab();
TabArchidekt *addArchidektTab();

View File

@@ -232,8 +232,8 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event,
return;
} else {
// Normal click = clear selection, select this, set current
deckDockWidget->deckView->setCurrentIndex(idx);
deckDockWidget->deckView->scrollTo(idx);
deckDockWidget->setCurrentProxyIndex(idx);
deckDockWidget->scrollToProxyIndex(idx);
return;
}

View File

@@ -24,11 +24,11 @@ TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor)
void TabDeckStorageVisual::actOpenLocalDeck(const QString &filePath)
{
auto deckLoader = DeckLoader(this);
if (!deckLoader.loadFromFile(filePath, DeckFileFormat::getFormatFromName(filePath), true)) {
auto deckLoader = new DeckLoader(this);
if (!deckLoader->loadFromFile(filePath, DeckLoader::getFormatFromName(filePath), true)) {
QMessageBox::critical(this, tr("Error"), tr("Could not open deck at %1").arg(filePath));
return;
}
emit openDeckEditor(deckLoader.getDeck());
emit openDeckEditor(deckLoader);
}

View File

@@ -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(const LoadedDeck &deck);
void openDeckEditor(DeckLoader *deckLoader);
private:
VisualDeckStorageWidget *visualDeckStorageWidget;

View File

@@ -1,205 +0,0 @@
#include "visual_database_display_format_legality_filter_widget.h"
#include "../../../filters/filter_tree_model.h"
#include <QPushButton>
#include <QSpinBox>
#include <QTimer>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/filters/filter_tree.h>
VisualDatabaseDisplayFormatLegalityFilterWidget::VisualDatabaseDisplayFormatLegalityFilterWidget(
QWidget *parent,
FilterTreeModel *_filterModel)
: QWidget(parent), filterModel(_filterModel)
{
allFormatsWithCount = CardDatabaseManager::query()->getAllFormatsWithCount();
setMaximumHeight(75);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
layout = new QHBoxLayout(this);
setLayout(layout);
layout->setContentsMargins(0, 1, 0, 1);
layout->setSpacing(1);
layout->setAlignment(Qt::AlignTop);
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
layout->addWidget(flowWidget);
// Create the spinbox
spinBox = new QSpinBox(this);
spinBox->setMinimum(1);
spinBox->setMaximum(getMaxMainTypeCount()); // Set the max value dynamically
spinBox->setValue(150);
layout->addWidget(spinBox);
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), this,
&VisualDatabaseDisplayFormatLegalityFilterWidget::updateFormatButtonsVisibility);
// Create the toggle button for Exact Match/Includes mode
toggleButton = new QPushButton(this);
toggleButton->setCheckable(true);
layout->addWidget(toggleButton);
connect(toggleButton, &QPushButton::toggled, this,
&VisualDatabaseDisplayFormatLegalityFilterWidget::updateFilterMode);
connect(filterModel, &FilterTreeModel::layoutChanged, this, [this]() {
QTimer::singleShot(100, this, &VisualDatabaseDisplayFormatLegalityFilterWidget::syncWithFilterModel);
});
createFormatButtons(); // Populate buttons initially
updateFilterMode(false); // Initialize toggle button text
retranslateUi();
}
void VisualDatabaseDisplayFormatLegalityFilterWidget::retranslateUi()
{
spinBox->setToolTip(tr("Do not display formats with less than this amount of cards in the database"));
toggleButton->setToolTip(tr("Filter mode (AND/OR/NOT conjunctions of filters)"));
}
void VisualDatabaseDisplayFormatLegalityFilterWidget::createFormatButtons()
{
// Iterate through main types and create buttons
for (auto it = allFormatsWithCount.begin(); it != allFormatsWithCount.end(); ++it) {
auto *button = new QPushButton(it.key(), flowWidget);
button->setCheckable(true);
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
"QPushButton:checked { background-color: green; color: white; }");
flowWidget->addWidget(button);
formatButtons[it.key()] = button;
// Connect toggle signal
connect(button, &QPushButton::toggled, this,
[this, mainType = it.key()](bool checked) { handleFormatToggled(mainType, checked); });
}
updateFormatButtonsVisibility(); // Ensure visibility is updated initially
}
void VisualDatabaseDisplayFormatLegalityFilterWidget::updateFormatButtonsVisibility()
{
int threshold = spinBox->value(); // Get the current spinbox value
// Iterate through buttons and hide/disable those below the threshold
for (auto it = formatButtons.begin(); it != formatButtons.end(); ++it) {
bool visible = allFormatsWithCount[it.key()] >= threshold;
it.value()->setVisible(visible);
it.value()->setEnabled(visible);
}
}
int VisualDatabaseDisplayFormatLegalityFilterWidget::getMaxMainTypeCount() const
{
int maxCount = 1;
for (auto it = allFormatsWithCount.begin(); it != allFormatsWithCount.end(); ++it) {
maxCount = qMax(maxCount, it.value());
}
return maxCount;
}
void VisualDatabaseDisplayFormatLegalityFilterWidget::handleFormatToggled(const QString &format, bool active)
{
activeFormats[format] = active;
if (formatButtons.contains(format)) {
formatButtons[format]->setChecked(active);
}
updateFormatFilter();
}
void VisualDatabaseDisplayFormatLegalityFilterWidget::updateFormatFilter()
{
// Clear existing filters related to main type
filterModel->blockSignals(true);
filterModel->filterTree()->blockSignals(true);
filterModel->clearFiltersOfType(CardFilter::Attr::AttrFormat);
if (exactMatchMode) {
// Exact Match: Only selected main types are allowed
QSet<QString> selectedTypes;
for (const auto &type : activeFormats.keys()) {
if (activeFormats[type]) {
selectedTypes.insert(type);
}
}
if (!selectedTypes.isEmpty()) {
// Require all selected types (TypeAnd)
for (const auto &type : selectedTypes) {
QString typeString = type;
filterModel->addFilter(
new CardFilter(typeString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrFormat));
}
// Exclude any other types (TypeAndNot)
for (const auto &type : formatButtons.keys()) {
if (!selectedTypes.contains(type)) {
QString typeString = type;
filterModel->addFilter(
new CardFilter(typeString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrFormat));
}
}
}
} else {
// Default Includes Mode (TypeOr) - match any selected main types
for (const auto &type : activeFormats.keys()) {
if (activeFormats[type]) {
QString typeString = type;
filterModel->addFilter(
new CardFilter(typeString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrFormat));
}
}
}
filterModel->blockSignals(false);
filterModel->filterTree()->blockSignals(false);
emit filterModel->filterTree()->changed();
emit filterModel->layoutChanged();
}
void VisualDatabaseDisplayFormatLegalityFilterWidget::updateFilterMode(bool checked)
{
exactMatchMode = checked;
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
updateFormatFilter();
}
void VisualDatabaseDisplayFormatLegalityFilterWidget::syncWithFilterModel()
{
// Temporarily block signals for each button to prevent toggling while updating button states
for (auto it = formatButtons.begin(); it != formatButtons.end(); ++it) {
it.value()->blockSignals(true);
}
// Uncheck all buttons
for (auto it = formatButtons.begin(); it != formatButtons.end(); ++it) {
it.value()->setChecked(false);
}
// Get active filters for main types
QSet<QString> activeTypes;
for (const auto &filter : filterModel->getFiltersOfType(CardFilter::AttrFormat)) {
if (filter->type() == CardFilter::Type::TypeAnd) {
activeTypes.insert(filter->term());
}
}
// Check the buttons for active types
for (const auto &type : activeTypes) {
activeFormats[type] = true;
if (formatButtons.contains(type)) {
formatButtons[type]->setChecked(true);
}
}
// Re-enable signal emissions for each button
for (auto it = formatButtons.begin(); it != formatButtons.end(); ++it) {
it.value()->blockSignals(false);
}
// Update the visibility of buttons
updateFormatButtonsVisibility();
}

View File

@@ -1,43 +0,0 @@
#ifndef COCKATRICE_VISUAL_DATABASE_DISPLAY_FORMAT_LEGALITY_FILTER_WIDGET_H
#define COCKATRICE_VISUAL_DATABASE_DISPLAY_FORMAT_LEGALITY_FILTER_WIDGET_H
#include "../../../filters/filter_tree_model.h"
#include "../general/layout_containers/flow_widget.h"
#include <QMap>
#include <QPushButton>
#include <QSpinBox>
#include <QToolButton>
#include <QVBoxLayout>
#include <QWidget>
class VisualDatabaseDisplayFormatLegalityFilterWidget : public QWidget
{
Q_OBJECT
public:
explicit VisualDatabaseDisplayFormatLegalityFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
void retranslateUi();
void createFormatButtons();
void updateFormatButtonsVisibility();
int getMaxMainTypeCount() const;
void handleFormatToggled(const QString &format, bool active);
void updateFormatFilter();
void updateFilterMode(bool checked);
void syncWithFilterModel();
private:
FilterTreeModel *filterModel;
QMap<QString, int> allFormatsWithCount;
QSpinBox *spinBox;
QHBoxLayout *layout;
FlowWidget *flowWidget;
QPushButton *toggleButton; // Mode switch button
QMap<QString, bool> activeFormats; // Track active filters
QMap<QString, QPushButton *> formatButtons; // Store toggle buttons
bool exactMatchMode = false; // Toggle between "Exact Match" and "Includes"
};
#endif // COCKATRICE_VISUAL_DATABASE_DISPLAY_FORMAT_LEGALITY_FILTER_WIDGET_H

View File

@@ -33,7 +33,7 @@ VisualDatabaseDisplayMainTypeFilterWidget::VisualDatabaseDisplayMainTypeFilterWi
spinBox->setMaximum(getMaxMainTypeCount()); // Set the max value dynamically
spinBox->setValue(150);
layout->addWidget(spinBox);
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), this,
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
&VisualDatabaseDisplayMainTypeFilterWidget::updateMainTypeButtonsVisibility);
// Create the toggle button for Exact Match/Includes mode

View File

@@ -51,7 +51,7 @@ VisualDatabaseDisplayNameFilterWidget::VisualDatabaseDisplayNameFilterWidget(QWi
void VisualDatabaseDisplayNameFilterWidget::retranslateUi()
{
searchBox->setPlaceholderText(tr("Filter by name... (Exact match)"));
searchBox->setPlaceholderText(tr("Filter by name..."));
loadFromDeckButton->setText(tr("Load from Deck"));
loadFromDeckButton->setToolTip(tr("Apply all card names in currently loaded deck as exact match name filters"));
loadFromClipboardButton->setText(tr("Load from Clipboard"));
@@ -64,10 +64,14 @@ void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck()
if (!deckListModel)
return;
DeckList *decklist = deckListModel->getDeckList();
if (!decklist)
return;
QList<QString> cardNames = deckListModel->getCardNames();
for (auto cardName : cardNames) {
createNameFilter(cardName);
QList<DecklistCardNode *> cardsInDeck = decklist->getCardNodes();
for (auto currentCard : cardsInDeck) {
createNameFilter(currentCard->getName());
}
updateFilterModel();
@@ -79,7 +83,7 @@ void VisualDatabaseDisplayNameFilterWidget::actLoadFromClipboard()
if (!dlg.exec())
return;
QStringList cardsInClipboard = dlg.getDeckList().getCardList();
QStringList cardsInClipboard = dlg.getDeckList()->getDeckList()->getCardList();
for (QString cardName : cardsInClipboard) {
createNameFilter(cardName);
}
@@ -119,14 +123,14 @@ void VisualDatabaseDisplayNameFilterWidget::updateFilterModel()
{
// Clear existing name filters
emit filterModel->layoutAboutToBeChanged();
filterModel->clearFiltersOfType(CardFilter::Attr::AttrNameExact);
filterModel->clearFiltersOfType(CardFilter::Attr::AttrName);
filterModel->blockSignals(true);
filterModel->filterTree()->blockSignals(true);
for (const auto &name : activeFilters.keys()) {
QString nameString = name;
filterModel->addFilter(new CardFilter(nameString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrNameExact));
filterModel->addFilter(new CardFilter(nameString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrName));
}
filterModel->blockSignals(false);
@@ -142,7 +146,7 @@ void VisualDatabaseDisplayNameFilterWidget::updateFilterModel()
void VisualDatabaseDisplayNameFilterWidget::syncWithFilterModel()
{
QStringList currentFilters;
for (const auto &filter : filterModel->getFiltersOfType(CardFilter::Attr::AttrNameExact)) {
for (const auto &filter : filterModel->getFiltersOfType(CardFilter::Attr::AttrName)) {
if (filter->type() == CardFilter::Type::TypeOr) {
currentFilters.append(filter->term());
}

View File

@@ -27,7 +27,7 @@ VisualDatabaseDisplayRecentSetFilterSettingsWidget::VisualDatabaseDisplayRecentS
filterToMostRecentSetsAmount->setMaximum(100);
filterToMostRecentSetsAmount->setValue(
SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsAmount());
connect(filterToMostRecentSetsAmount, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
connect(filterToMostRecentSetsAmount, QOverload<int>::of(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsAmount);
layout->addWidget(filterToMostRecentSetsCheckBox);

View File

@@ -26,7 +26,7 @@ VisualDatabaseDisplaySubTypeFilterWidget::VisualDatabaseDisplaySubTypeFilterWidg
spinBox->setMaximum(getMaxSubTypeCount());
spinBox->setValue(150);
layout->addWidget(spinBox);
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), this,
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
&VisualDatabaseDisplaySubTypeFilterWidget::updateSubTypeButtonsVisibility);
// Create search box

View File

@@ -206,7 +206,6 @@ void VisualDatabaseDisplayWidget::initialize()
saveLoadWidget = new VisualDatabaseDisplayFilterSaveLoadWidget(this, filterModel);
nameFilterWidget = new VisualDatabaseDisplayNameFilterWidget(this, deckEditor, filterModel);
mainTypeFilterWidget = new VisualDatabaseDisplayMainTypeFilterWidget(this, filterModel);
formatLegalityWidget = new VisualDatabaseDisplayFormatLegalityFilterWidget(this, filterModel);
subTypeFilterWidget = new VisualDatabaseDisplaySubTypeFilterWidget(this, filterModel);
setFilterWidget = new VisualDatabaseDisplaySetFilterWidget(this, filterModel);
@@ -224,7 +223,6 @@ void VisualDatabaseDisplayWidget::initialize()
filterContainerLayout->addWidget(quickFilterSubTypeWidget);
filterContainerLayout->addWidget(quickFilterSetWidget);
filterContainerLayout->addWidget(mainTypeFilterWidget);
filterContainerLayout->addWidget(formatLegalityWidget);
searchLayout->addWidget(colorFilterWidget);
searchLayout->addWidget(clearFilterWidget);

View File

@@ -17,7 +17,6 @@
#include "../utility/custom_line_edit.h"
#include "visual_database_display_color_filter_widget.h"
#include "visual_database_display_filter_save_load_widget.h"
#include "visual_database_display_format_legality_filter_widget.h"
#include "visual_database_display_main_type_filter_widget.h"
#include "visual_database_display_name_filter_widget.h"
#include "visual_database_display_set_filter_widget.h"
@@ -92,7 +91,6 @@ private:
SettingsButtonWidget *quickFilterNameWidget;
VisualDatabaseDisplayNameFilterWidget *nameFilterWidget;
VisualDatabaseDisplayMainTypeFilterWidget *mainTypeFilterWidget;
VisualDatabaseDisplayFormatLegalityFilterWidget *formatLegalityWidget;
SettingsButtonWidget *quickFilterSubTypeWidget;
VisualDatabaseDisplaySubTypeFilterWidget *subTypeFilterWidget;
SettingsButtonWidget *quickFilterSetWidget;

View File

@@ -2,9 +2,7 @@
#include "../tabs/visual_deck_editor/tab_deck_editor_visual.h"
#include <libcockatrice/utility/qt_utils.h>
VisualDeckDisplayOptionsWidget::VisualDeckDisplayOptionsWidget(QWidget *parent) : QWidget(parent)
VisualDeckDisplayOptionsWidget::VisualDeckDisplayOptionsWidget(QWidget *parent)
{
groupAndSortLayout = new QHBoxLayout(this);
groupAndSortLayout->setAlignment(Qt::AlignLeft);
@@ -13,19 +11,23 @@ VisualDeckDisplayOptionsWidget::VisualDeckDisplayOptionsWidget(QWidget *parent)
groupByLabel = new QLabel(this);
groupByComboBox = new QComboBox(this);
if (auto visualDeckEditorWidget = qobject_cast<VisualDeckEditorWidget *>(parent)) {
if (auto tabWidget = qobject_cast<TabDeckEditorVisualTabWidget *>(visualDeckEditorWidget)) {
// Inside a central widget QWidget container inside TabDeckEditorVisual
if (auto tab = qobject_cast<TabDeckEditorVisual *>(tabWidget->parent()->parent())) {
auto originalBox = tab->getDeckDockWidget()->getGroupByComboBox();
groupByComboBox->setModel(originalBox->model());
groupByComboBox->setModelColumn(originalBox->modelColumn());
if (auto tab = QtUtils::findParentOfType<TabDeckEditorVisual>(this)) {
auto originalBox = tab->getDeckDockWidget()->getGroupByComboBox();
groupByComboBox->setModel(originalBox->model());
groupByComboBox->setModelColumn(originalBox->modelColumn());
// Original -> clone
connect(originalBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
[this](int index) { groupByComboBox->setCurrentIndex(index); });
// Original -> clone
connect(originalBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
[this](int index) { groupByComboBox->setCurrentIndex(index); });
// Clone -> original
connect(groupByComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
[originalBox](int index) { originalBox->setCurrentIndex(index); });
// Clone -> original
connect(groupByComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
[originalBox](int index) { originalBox->setCurrentIndex(index); });
}
}
} else {
groupByComboBox->addItem(
tr(qPrintable(DeckListModelGroupCriteria::toString(DeckListModelGroupCriteria::MAIN_TYPE))),

View File

@@ -24,9 +24,9 @@ VisualDeckEditorSampleHandWidget::VisualDeckEditorSampleHandWidget(QWidget *pare
handSizeSpinBox = new QSpinBox(this);
handSizeSpinBox->setValue(SettingsCache::instance().getVisualDeckEditorSampleHandSize());
handSizeSpinBox->setMinimum(1);
connect(handSizeSpinBox, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
connect(handSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setVisualDeckEditorSampleHandSize);
connect(handSizeSpinBox, qOverload<int>(&QSpinBox::valueChanged), this,
connect(handSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
&VisualDeckEditorSampleHandWidget::updateDisplay);
resetAndHandSizeLayout->addWidget(handSizeSpinBox);
@@ -76,11 +76,25 @@ void VisualDeckEditorSampleHandWidget::updateDisplay()
QList<ExactCard> VisualDeckEditorSampleHandWidget::getRandomCards(int amountToGet)
{
QList<ExactCard> mainDeckCards;
QList<ExactCard> randomCards;
if (!deckListModel)
return randomCards;
DeckList *decklist = deckListModel->getDeckList();
if (!decklist)
return randomCards;
QList<ExactCard> mainDeckCards = deckListModel->getCardsForZone(DECK_ZONE_MAIN);
QList<DecklistCardNode *> 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);
}
}
}
if (mainDeckCards.isEmpty())
return randomCards;

View File

@@ -79,8 +79,8 @@ static QStringList findAllKnownTags()
QStringList knownTags;
auto loader = DeckLoader(nullptr);
for (const QString &file : allFiles) {
loader.loadFromFile(file, DeckFileFormat::getFormatFromName(file), false);
QStringList tags = loader.getDeck().deckList.getTags();
loader.loadFromFile(file, DeckLoader::getFormatFromName(file), false);
QStringList tags = loader.getDeckList()->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->getDeck().lastLoadInfo.fileName;
deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastLoadInfo().fileName;
deckPreviewWidget->refreshBannerCardText();
}
@@ -136,7 +136,7 @@ static void convertFileToCockatriceFormat(DeckPreviewWidget *deckPreviewWidget)
*/
bool DeckPreviewDeckTagsDisplayWidget::promptFileConversionIfRequired(DeckPreviewWidget *deckPreviewWidget)
{
if (DeckFileFormat::getFormatFromName(deckPreviewWidget->filePath) == DeckFileFormat::Cockatrice) {
if (DeckLoader::getFormatFromName(deckPreviewWidget->filePath) == DeckLoader::CockatriceFormat) {
return true;
}

View File

@@ -32,7 +32,7 @@ DeckPreviewWidget::DeckPreviewWidget(QWidget *_parent,
many deck loads have finished already and if we've loaded all decks and THEN load all the tags at once. */
connect(deckLoader, &DeckLoader::loadFinished, visualDeckStorageWidget->tagFilterWidget,
&VisualDeckStorageTagFilterWidget::refreshTags);
deckLoader->loadFromFileAsync(filePath, DeckFileFormat::getFormatFromName(filePath), false);
deckLoader->loadFromFileAsync(filePath, DeckLoader::getFormatFromName(filePath), false);
bannerCardDisplayWidget =
new DeckPreviewCardPictureWidget(this, false, visualDeckStorageWidget->deckPreviewSelectionAnimationEnabled);
@@ -74,16 +74,16 @@ void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess)
if (!deckLoadSuccess) {
return;
}
auto bannerCard = deckLoader->getDeck().deckList.getBannerCard().name.isEmpty()
auto bannerCard = deckLoader->getDeckList()->getBannerCard().name.isEmpty()
? ExactCard()
: CardDatabaseManager::query()->getCard(deckLoader->getDeck().deckList.getBannerCard());
: CardDatabaseManager::query()->getCard(deckLoader->getDeckList()->getBannerCard());
bannerCardDisplayWidget->setCard(bannerCard);
bannerCardDisplayWidget->setFontSize(24);
setFilePath(deckLoader->getDeck().lastLoadInfo.fileName);
setFilePath(deckLoader->getLastLoadInfo().fileName);
colorIdentityWidget = new ColorIdentityWidget(this, getColorIdentity());
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckLoader->getDeck().deckList.getTags());
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckLoader->getDeckList()->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->getDeck().deckList.getBannerCard().name);
bannerCardComboBox->setCurrentText(deckLoader->getDeckList()->getBannerCard().name);
bannerCardComboBox->installEventFilter(new NoScrollFilter());
connect(bannerCardComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&DeckPreviewWidget::setBannerCard);
@@ -153,7 +153,7 @@ void DeckPreviewWidget::updateTagsVisibility(bool visible)
QString DeckPreviewWidget::getColorIdentity()
{
QStringList cardList = deckLoader->getDeck().deckList.getCardList();
QStringList cardList = deckLoader->getDeckList()->getCardList();
if (cardList.isEmpty()) {
return {};
}
@@ -187,8 +187,8 @@ QString DeckPreviewWidget::getColorIdentity()
*/
QString DeckPreviewWidget::getDisplayName() const
{
QString deckName = deckLoader->getDeck().deckList.getName();
return !deckName.isEmpty() ? deckName : QFileInfo(deckLoader->getDeck().lastLoadInfo.fileName).fileName();
return deckLoader->getDeckList()->getName().isEmpty() ? QFileInfo(deckLoader->getLastLoadInfo().fileName).fileName()
: deckLoader->getDeckList()->getName();
}
void DeckPreviewWidget::setFilePath(const QString &_filePath)
@@ -235,7 +235,7 @@ void DeckPreviewWidget::updateBannerCardComboBox()
// Prepare the new items with deduplication
QSet<QPair<QString, QString>> bannerCardSet;
QList<const DecklistCardNode *> cardsInDeck = deckLoader->getDeck().deckList.getCardNodes();
QList<DecklistCardNode *> cardsInDeck = deckLoader->getDeckList()->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->getDeck().deckList.getBannerCard().name);
int bannerIndex = bannerCardComboBox->findText(deckLoader->getDeckList()->getBannerCard().name);
if (bannerIndex != -1) {
bannerCardComboBox->setCurrentIndex(bannerIndex);
} else {
@@ -287,8 +287,8 @@ void DeckPreviewWidget::setBannerCard(int /* changedIndex */)
{
auto [name, id] = bannerCardComboBox->currentData().value<QPair<QString, QString>>();
CardRef cardRef = {name, id};
deckLoader->getDeck().deckList.setBannerCard(cardRef);
deckLoader->saveToFile(filePath, DeckFileFormat::getFormatFromName(filePath));
deckLoader->getDeckList()->setBannerCard(cardRef);
deckLoader->saveToFile(filePath, DeckLoader::getFormatFromName(filePath));
bannerCardDisplayWidget->setCard(CardDatabaseManager::query()->getCard(cardRef));
}
@@ -310,8 +310,8 @@ void DeckPreviewWidget::imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewC
void DeckPreviewWidget::setTags(const QStringList &tags)
{
deckLoader->getDeck().deckList.setTags(tags);
deckLoader->saveToFile(filePath, DeckFileFormat::Cockatrice);
deckLoader->getDeckList()->setTags(tags);
deckLoader->saveToFile(filePath, DeckLoader::CockatriceFormat);
}
QMenu *DeckPreviewWidget::createRightClickMenu()
@@ -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->getDeck()); });
[this] { emit openDeckEditor(deckLoader); });
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->getDeck().deckList, true, true); });
[this] { DeckLoader::saveToClipboard(deckLoader->getDeckList(), 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->getDeckList(), true, false); });
connect(saveToClipboardMenu->addAction(tr("Not Annotated")), &QAction::triggered, this,
[this] { DeckLoader::saveToClipboard(&deckLoader->getDeck().deckList, false, true); });
[this] { DeckLoader::saveToClipboard(deckLoader->getDeckList(), 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->getDeckList(), false, false); });
menu->addSeparator();
@@ -376,7 +376,7 @@ void DeckPreviewWidget::addSetBannerCardMenu(QMenu *menu)
void DeckPreviewWidget::actRenameDeck()
{
// read input
const QString oldName = deckLoader->getDeck().deckList.getName();
const QString oldName = deckLoader->getDeckList()->getName();
bool ok;
QString newName = QInputDialog::getText(this, "Rename deck", tr("New name:"), QLineEdit::Normal, oldName, &ok);
@@ -385,8 +385,8 @@ void DeckPreviewWidget::actRenameDeck()
}
// write change
deckLoader->getDeck().deckList.setName(newName);
deckLoader->saveToFile(filePath, DeckFileFormat::getFormatFromName(filePath));
deckLoader->getDeckList()->setName(newName);
deckLoader->saveToFile(filePath, DeckLoader::getFormatFromName(filePath));
// update VDS
refreshBannerCardText();
@@ -416,7 +416,9 @@ void DeckPreviewWidget::actRenameFile()
return;
}
deckLoader->getDeck().lastLoadInfo.fileName = newFilePath;
DeckLoader::LoadInfo lastLoadInfo = deckLoader->getLastLoadInfo();
lastLoadInfo.fileName = newFilePath;
deckLoader->setLastLoadInfo(lastLoadInfo);
// update VDS
setFilePath(newFilePath);

View File

@@ -51,7 +51,7 @@ public:
signals:
void deckLoadRequested(const QString &filePath);
void openDeckEditor(const LoadedDeck &deck);
void openDeckEditor(DeckLoader *deck);
public slots:
void setFilePath(const QString &filePath);

View File

@@ -211,7 +211,7 @@ QStringList VisualDeckStorageFolderDisplayWidget::gatherAllTagsFromFlowWidget()
// Iterate through all DeckPreviewWidgets
for (DeckPreviewWidget *display : flowWidget->findChildren<DeckPreviewWidget *>()) {
// Get tags from each DeckPreviewWidget
QStringList tags = display->deckLoader->getDeck().deckList.getTags();
QStringList tags = display->deckLoader->getDeckList()->getTags();
// Add tags to the list while avoiding duplicates
allTags.append(tags);

View File

@@ -61,10 +61,10 @@ VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidg
unusedColorIdentitiesOpacitySpinBox->setMaximum(100);
unusedColorIdentitiesOpacitySpinBox->setValue(
SettingsCache::instance().getVisualDeckStorageUnusedColorIdentitiesOpacity());
connect(unusedColorIdentitiesOpacitySpinBox, qOverload<int>(&QSpinBox::valueChanged), this,
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
&VisualDeckStorageQuickSettingsWidget::unusedColorIdentitiesOpacityChanged);
connect(unusedColorIdentitiesOpacitySpinBox, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity);
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
&SettingsCache::instance(), &SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity);
unusedColorIdentitiesOpacityLabel->setBuddy(unusedColorIdentitiesOpacitySpinBox);

View File

@@ -95,17 +95,14 @@ QList<DeckPreviewWidget *> VisualDeckStorageSortWidget::filterFiles(QList<DeckPr
switch (sortOrder) {
case ByName:
return widget1->deckLoader->getDeck().deckList.getName() <
widget2->deckLoader->getDeck().deckList.getName();
return widget1->deckLoader->getDeckList()->getName() < widget2->deckLoader->getDeckList()->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->getDeck().deckList.getLastLoadedTimestamp());
QDateTime time2 =
QDateTime::fromString(widget2->deckLoader->getDeck().deckList.getLastLoadedTimestamp());
QDateTime time1 = QDateTime::fromString(widget1->deckLoader->getDeckList()->getLastLoadedTimestamp());
QDateTime time2 = QDateTime::fromString(widget2->deckLoader->getDeckList()->getLastLoadedTimestamp());
return time1 > time2;
}
}

View File

@@ -57,7 +57,7 @@ void VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QList<Dec
}
for (DeckPreviewWidget *deckPreview : deckPreviews) {
QStringList deckTags = deckPreview->deckLoader->getDeck().deckList.getTags();
QStringList deckTags = deckPreview->deckLoader->getDeckList()->getTags();
bool hasAllSelected = std::all_of(selectedTags.begin(), selectedTags.end(),
[&deckTags](const QString &tag) { return deckTags.contains(tag); });
@@ -153,7 +153,7 @@ QSet<QString> VisualDeckStorageTagFilterWidget::gatherAllTags() const
for (DeckPreviewWidget *widget : deckWidgets) {
if (widget->checkVisibility()) {
for (const QString &tag : widget->deckLoader->getDeck().deckList.getTags()) {
for (const QString &tag : widget->deckLoader->getDeckList()->getTags()) {
allTags.insert(tag);
}
}

View File

@@ -53,7 +53,7 @@ public slots:
signals:
void bannerCardsRefreshed();
void deckLoadRequested(const QString &filePath);
void openDeckEditor(const LoadedDeck &deck);
void openDeckEditor(DeckLoader *deck);
private:
QVBoxLayout *layout;

View File

@@ -1,16 +1,10 @@
#ifndef MAIN_H
#define MAIN_H
#include "libcockatrice/interfaces/noop_card_set_priority_controller.h"
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/card/database/parser/cockatrice_xml_4.h>
#include <libcockatrice/interfaces/noop_card_preference_provider.h>
static const QList<AllowedCount> kConstructedCounts = {{4, "legal"}, {0, "banned"}};
static const QList<AllowedCount> kSingletonCounts = {{1, "legal"}, {0, "banned"}};
class CardDatabaseConverter : public CardDatabase
{
public:
@@ -28,74 +22,8 @@ public:
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<AllowedCount> &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;
CockatriceXml4Parser parser(new NoopCardPreferenceProvider());
return parser.saveToFile(sets, cards, fileName);
}
};

View File

@@ -1,7 +1,5 @@
@page developer_reference Developer Reference
- @subpage logging
- @subpage primer_cards
- @subpage card_database_schema_and_parsing

View File

@@ -1,184 +0,0 @@
@page logging Logging
Cockatrice uses QtLogging from the QtCore module for its logging. See
the [official documentation](https://doc.qt.io/qt-6/qtlogging.html) for further details.
# Log Message Pattern
Any message logged through the QtLogging system automatically conforms to this message pattern:
Generic:
```
[<timestamp> <log_level>] [<class:function>] - <message> [<filename>:<line_no>]
```
Example:
```
[2025-12-05 14:48:25.908 I] [MainWindow::startupConfigCheck] - Startup: found config with current version [window_main.cpp:951]
```
For more information, see [Logging Setup](#logging-setup).
# Log Level and Categories
\note The default log level for the application is info.
This means that you should only use qInfo() in production-level code if you are truly sure that this message is
beneficial to end-users and other developers. As a general rule, if your functionality logs to info more than twice in
response to a user interaction, you are advised to consider moving some of these logs down to the debug level.
\warning You are strongly advised to avoid the use of the generic logging macros (e.g. qDebug(), qInfo(), qWarn()).
\note You should instead use the corresponding category logging macros (qCDebug(), qCInfo(), qCWarn()) and define
logging
categories for your log statements.
Example:
```c++
in .h
inline Q_LOGGING_CATEGORY(ExampleCategory, "cockatrice_example_category");
inline Q_LOGGING_CATEGORY(ExampleSubCategory, "cockatrice_example_category.sub_category");
in .cpp
qCInfo(ExampleCategory) << "Info level logs are usually sent through the main category"
qCDebug(ExampleSubCategory) << "Debug level logs are permitted their own category to allow selective silencing"
```
For more information on how to enable or disable logging categories,
see [Logging Configuration](#logging-configuration).
# Logging Configuration
For configuring our logging, we use the qtlogging.ini, located under cockatrice/resources/config/qtlogging.ini, which is
baked into the application in release version and set as the QT_LOGGING_CONF environment variable in main.cpp.
```c++
#ifdef Q_OS_APPLE
// <build>/cockatrice/cockatrice.app/Contents/MacOS/cockatrice
const QByteArray configPath = "../../../qtlogging.ini";
#elif defined(Q_OS_UNIX)
// <build>/cockatrice/cockatrice
const QByteArray configPath = "./qtlogging.ini";
#elif defined(Q_OS_WIN)
// <build>/cockatrice/Debug/cockatrice.exe
const QByteArray configPath = "../qtlogging.ini";
#else
const QByteArray configPath = "";
#endif
if (!qEnvironmentVariableIsSet(("QT_LOGGING_CONF"))) {
// Set the QT_LOGGING_CONF environment variable
qputenv("QT_LOGGING_CONF", configPath);
}
```
For more information on how to use this file and on how Qt evaluates which logging rules/file to use, please
see the [official Qt documentation](https://doc.qt.io/qt-6/qloggingcategory.html#configuring-categories).
Some examples:
```
# Turn off all logging except everything from card_picture_loader and all sub categories
[Rules]
# The default log level is info
*.debug = false
*.info = false
*.warning = false
*.critical = false
*.fatal = false
card_picture_loader.* = true
```
```
# Turn off all logging except info level logs from card_picture_loader and all sub categories
[Rules]
# The default log level is info
*.debug = false
*.info = false
*.warning = false
*.critical = false
*.fatal = false
card_picture_loader.*.info = true
```
```
[Rules]
# Turn on debug level logs for card_picture_loader but keep logging for sub categories suppressed
*.debug = false
card_picture_loader.debug = true
```
```
[Rules]
# Turn on all logs for worker subcategory of card_picture_loader
*.debug = false
card_picture_loader.worker = true
```
```
[Rules]
# Turn off some noisy and irrelevant startup logging for local development
*.debug = false
qt_translator = false
window_main.* = false
release_channel = false
spoiler_background_updater = false
theme_manager = false
sound_engine = false
tapped_out_interface = false
card_database = false
card_database.loading = false
card_database.loading.success_or_failure = true
cockatrice_xml.* = false
```
# Logging Setup
This is achieved through our logging setup in @ref main.cpp, where we set the message pattern and install a custom
logger which replaces the full file path at the end with just the file name (Qt shows the full and quite lengthy path by
default).
```c++
qSetMessagePattern(
"\033[0m[%{time yyyy-MM-dd h:mm:ss.zzz} "
"%{if-debug}\033[36mD%{endif}%{if-info}\033[32mI%{endif}%{if-warning}\033[33mW%{endif}%{if-critical}\033[31mC%{"
"endif}%{if-fatal}\033[1;31mF%{endif}\033[0m] [%{function}] - %{message} [%{file}:%{line}]");
QApplication app(argc, argv);
QObject::connect(&app, &QApplication::lastWindowClosed, &app, &QApplication::quit);
qInstallMessageHandler(CockatriceLogger);
```
```c++
static void CockatriceLogger(QtMsgType type, const QMessageLogContext &ctx, const QString &message)
{
QString logMessage = qFormatLogMessage(type, ctx, message);
// Regular expression to match the full path in the square brackets and extract only the filename and line number
QRegularExpression regex(R"(\[(?:.:)?[\/\\].*[\/\\]([^\/\\]+\:\d+)\])");
QRegularExpressionMatch match = regex.match(logMessage);
if (match.hasMatch()) {
// Extract the filename and line number (e.g., "main.cpp:211")
QString filenameLine = match.captured(1);
// Replace the full path in square brackets with just the filename and line number
logMessage.replace(match.captured(0), QString("[%1]").arg(filenameLine));
}
Logger::getInstance().log(type, ctx, logMessage);
}
```

View File

@@ -41,8 +41,6 @@ add_library(
libcockatrice/card/relation/card_relation.cpp
libcockatrice/card/set/card_set.cpp
libcockatrice/card/set/card_set_list.cpp
libcockatrice/card/format/format_legality_rules.cpp
libcockatrice/card/format/format_legality_rules.h
)
target_include_directories(

Some files were not shown because too many files have changed in this diff Show More