diff --git a/.ci/compile.sh b/.ci/compile.sh
index 5d2e18390..424527f96 100755
--- a/.ci/compile.sh
+++ b/.ci/compile.sh
@@ -122,7 +122,7 @@ if [[ $MAKE_SERVER ]]; then
flags+=("-DWITH_SERVER=1")
fi
if [[ $MAKE_NO_CLIENT ]]; then
- flags+=("-DWITH_CLIENT=0" "-DWITH_ORACLE=0" "-DWITH_DBCONVERTER=0")
+ flags+=("-DWITH_CLIENT=0" "-DWITH_ORACLE=0")
fi
if [[ $MAKE_TEST ]]; then
flags+=("-DTEST=1")
@@ -258,7 +258,7 @@ fi
if [[ $RUNNER_OS == macOS ]]; then
echo "::group::Inspect Mach-O binaries"
- for app in cockatrice oracle servatrice dbconverter; do
+ for app in cockatrice oracle servatrice; do
binary="$GITHUB_WORKSPACE/build/$app/$app.app/Contents/MacOS/$app"
echo "Inspecting $app..."
vtool -show-build "$binary"
diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml
index 44bee4959..d87f20732 100644
--- a/.github/workflows/desktop-build.yml
+++ b/.github/workflows/desktop-build.yml
@@ -142,7 +142,6 @@ jobs:
- distro: Ubuntu
version: 22.04
package: DEB
- test: skip # Running tests on all distros is superfluous
- distro: Ubuntu
version: 24.04
@@ -166,7 +165,7 @@ jobs:
- name: Restore compiler cache (ccache)
id: ccache_restore
- uses: actions/cache/restore@v4
+ uses: actions/cache/restore@v5
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
with:
@@ -205,7 +204,7 @@ jobs:
- name: Save compiler cache (ccache)
if: github.ref == 'refs/heads/master'
- uses: actions/cache/save@v4
+ uses: actions/cache/save@v5
with:
path: ${{env.CACHE}}
key: ${{ steps.ccache_restore.outputs.cache-primary-key }}
@@ -213,7 +212,7 @@ jobs:
- name: Upload artifact
id: upload_artifact
if: matrix.package != 'skip'
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: ${{matrix.distro}}${{matrix.version}}-package
path: ${{steps.build.outputs.path}}
@@ -336,6 +335,11 @@ jobs:
name: ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
needs: configure
runs-on: ${{matrix.runner}}
+ env:
+ CCACHE_DIR: ${{github.workspace}}/.cache/
+ # Cache size over the entire repo is 10Gi:
+ # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
+ CCACHE_SIZE: 500M
steps:
- name: Checkout
@@ -350,16 +354,20 @@ jobs:
with:
msbuild-architecture: x64
- # Using jianmingyong/ccache-action to setup ccache without using brew
- # It tries to download a binary of ccache from GitHub Release and falls back to building from source if it fails
- name: Setup ccache
+ if: matrix.use_ccache == 1 && matrix.os == 'macOS'
+ run: brew install ccache
+
+ - name: Restore compiler cache (ccache)
if: matrix.use_ccache == 1
- uses: jianmingyong/ccache-action@v1
+ id: ccache_restore
+ uses: actions/cache/restore@v5
+ env:
+ BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
with:
- install-type: "binary"
- ccache-key-prefix: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}
- max-size: 500M
- gh-token: ${{ secrets.GITHUB_TOKEN }}
+ path: ${{env.CCACHE_DIR}}
+ key: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-${{env.BRANCH_NAME}}
+ restore-keys: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-
- name: Restore thin Qt ${{matrix.qt_version}} libraries
if: matrix.os == 'macOS'
@@ -429,6 +437,15 @@ jobs:
TARGET_MACOS_VERSION: ${{ matrix.override_target }}
run: .ci/compile.sh --server --test --vcpkg
+ - name: Save compiler cache (ccache)
+ if: github.ref == 'refs/heads/master' && matrix.use_ccache == 1
+ uses: actions/cache/save@v5
+ env:
+ BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
+ with:
+ path: ${{env.CCACHE_DIR}}
+ key: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-${{env.BRANCH_NAME}}
+
- name: Sign app bundle
if: matrix.os == 'macOS' && matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null)
env:
@@ -476,7 +493,7 @@ jobs:
- name: Upload artifact
id: upload_artifact
if: matrix.make_package
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: ${{matrix.artifact_name}}
path: ${{steps.build.outputs.path}}
@@ -484,7 +501,7 @@ jobs:
- name: Upload pdb database
if: matrix.os == 'Windows'
- uses: actions/upload-artifact@v5
+ uses: actions/upload-artifact@v6
with:
name: Windows${{matrix.target}}-debug-pdbs
path: |
diff --git a/.github/workflows/translations-pull.yml b/.github/workflows/translations-pull.yml
index 81d6f4e75..ed61e3b19 100644
--- a/.github/workflows/translations-pull.yml
+++ b/.github/workflows/translations-pull.yml
@@ -33,7 +33,7 @@ jobs:
- name: Create pull request
if: github.event_name != 'pull_request'
id: create_pr
- uses: peter-evans/create-pull-request@v7
+ uses: peter-evans/create-pull-request@v8
with:
add-paths: |
cockatrice/translations/*.ts
diff --git a/.github/workflows/translations-push.yml b/.github/workflows/translations-push.yml
index d201e7ad8..777e9e6ac 100644
--- a/.github/workflows/translations-push.yml
+++ b/.github/workflows/translations-push.yml
@@ -57,7 +57,7 @@ jobs:
- name: Create pull request
if: github.event_name != 'pull_request'
id: create_pr
- uses: peter-evans/create-pull-request@v7
+ uses: peter-evans/create-pull-request@v8
with:
add-paths: |
cockatrice/cockatrice_en@source.ts
diff --git a/CMakeLists.txt b/CMakeLists.txt
index affc6472d..574ccecb3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,8 +20,6 @@ option(WITH_SERVER "build servatrice" OFF)
option(WITH_CLIENT "build cockatrice" ON)
# Compile oracle
option(WITH_ORACLE "build oracle" ON)
-# Compile dbconverter
-option(WITH_DBCONVERTER "build dbconverter" ON)
# Compile tests
option(TEST "build tests" OFF)
# Use vcpkg regardless of OS
@@ -356,11 +354,6 @@ if(WITH_ORACLE)
set(CPACK_INSTALL_CMAKE_PROJECTS "Oracle;Oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
endif()
-if(WITH_DBCONVERTER)
- add_subdirectory(dbconverter)
- set(CPACK_INSTALL_CMAKE_PROJECTS "Dbconverter;Dbconverter;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
-endif()
-
if(TEST)
include(CTest)
add_subdirectory(tests)
diff --git a/Dockerfile b/Dockerfile
index 80f9f69f2..d185b746a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
WORKDIR /src
COPY . .
RUN mkdir build && cd build && \
- cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 && \
+ cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 && \
make -j$(nproc) && \
make install
diff --git a/Doxyfile b/Doxyfile
index d5a3a4b7a..ecacc6b8f 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -1068,7 +1068,6 @@ RECURSIVE = YES
EXCLUDE = build/ \
cmake/ \
- dbconverter/ \
vcpkg/ \
webclient/
diff --git a/README.md b/README.md
index b1233f749..c08aee0e7 100644
--- a/README.md
+++ b/README.md
@@ -44,10 +44,10 @@ Latest beta version:
# Related Repositories
-- [Magic-Token](https://github.com/Cockatrice/Magic-Token): MtG token data to use in Cockatrice
-- [Magic-Spoiler](https://github.com/Cockatrice/Magic-Spoiler): Script to generate MtG spoiler data from [MTGJSON](https://github.com/mtgjson/mtgjson) to use in Cockatrice
+- [Magic-Token](https://github.com/Cockatrice/Magic-Token): File with MtG token data for use in Cockatrice
+- [Magic-Spoiler](https://github.com/Cockatrice/Magic-Spoiler): Code to generate MtG spoiler data from [MTGJSON](https://github.com/mtgjson/mtgjson) for use in Cockatrice
- [cockatrice.github.io](https://github.com/Cockatrice/cockatrice.github.io): Code of the official Cockatrice webpage
-- [Cockatrice @Flathub](https://github.com/flathub/io.github.Cockatrice.cockatrice): Configuration for our Linux `flatpak` package
+- [io.github.Cockatrice.cockatrice](https://github.com/flathub/io.github.Cockatrice.cockatrice): Configuration of our Linux `flatpak` package hosted at [Flathub](https://flathub.org/en/apps/io.github.Cockatrice.cockatrice)
# Community Resources [](https://discord.gg/3Z9yzmA)
@@ -55,15 +55,31 @@ Latest beta version:
Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other projet contributors (`#dev` channel) or fellow users of the app. Come here to talk about the application, features, or just to hang out.
- [Official Website](https://cockatrice.github.io)
- [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
-- [Official Code Documentation](https://cockatrice.github.io/docs)
- [Official Discord](https://discord.gg/3Z9yzmA)
- [reddit r/Cockatrice](https://reddit.com/r/cockatrice)
->[!IMPORTANT]
->For support regarding specific servers, please contact that server's admin/mods and use their dedicated communication channels rather than contacting the team building the software.
+> [!IMPORTANT]
+> For support regarding specific servers, please contact that server's admin/mods and use their dedicated communication channels rather than contacting the team building the software.
# Contribute
+
+ Code |
+ Documentation |
+ Translation
+
+
+#### Repository Activity
+
+
+
+Kudos to all our amazing contributors ❤️
+
+
+
+
+ Made with contrib.rocks
+
### Code
@@ -77,18 +93,19 @@ This tag is used for issues that we are looking for somebody to pick up. Often t
For both tags, we're willing to provide help to contributors in showing them where and how they can make changes, as well as code reviews for submitted changes.
We'll happily advice on how best to implement a feature, or we can show you where the codebase is doing something similar before you get too far along - put a note on an issue you want to discuss more on!
+You can also have a look at our `Todo List` in our [Code Documentation](https://cockatrice.github.io/docs) or search the repo for [`\todo` comments](https://github.com/search?q=repo%3ACockatrice%2FCockatrice%20%5Ctodo&type=code).
+
+### Documentation [](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml?query=event%3Apush)
+
+There are various places where useful information for different needs are maintained:
+- [Official Code Documentation](https://cockatrice.github.io/docs/)
+- [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki) `Community supported`
+- [Official Webpage](https://cockatrice.github.io/)
+- [Official README](https://github.com/Cockatrice/Cockatrice/blob/master/README.md) `This file`
+
Cockatrice tries to use the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide.
-
-Kudos to our amazing contributors ❤️
-
-
-
-
- Made with contrib.rocks
-
-
-### Translations [](https://explore.transifex.com/cockatrice/cockatrice/)
+### Translation [](https://explore.transifex.com/cockatrice/cockatrice/)
Cockatrice uses Transifex to manage translations. You can help us bring Cockatrice, Oracle and Webatrice to your language and just adjust single wordings right from within your browser by visiting our [Transifex project page](https://explore.transifex.com/cockatrice/cockatrice/).
@@ -126,8 +143,8 @@ You can then
make package
```
->[!NOTE]
->Detailed compiling instructions can be found in the Cockatrice wiki at [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)
+> [!NOTE]
+> Detailed compiling instructions can be found in the Cockatrice wiki at [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)
diff --git a/cmake/CMakeDMGSetup.script b/cmake/CMakeDMGSetup.script
index fe02eea13..e9a826eb8 100644
--- a/cmake/CMakeDMGSetup.script
+++ b/cmake/CMakeDMGSetup.script
@@ -42,7 +42,6 @@ tell disk image_name
set position of item "Cockatrice.app" to { 139, 214 }
set position of item "Oracle.app" to { 139, 414 }
set position of item "Servatrice.app" to { 139, 614 }
- set position of item "dbconverter.app" to { 1400, 1400 }
set position of item "Applications" to { 861, 414 }
end tell
update without registering applications
diff --git a/cmake/FindQtRuntime.cmake b/cmake/FindQtRuntime.cmake
index 769f9b463..c205ebdcf 100644
--- a/cmake/FindQtRuntime.cmake
+++ b/cmake/FindQtRuntime.cmake
@@ -1,12 +1,11 @@
# Find a compatible Qt version
-# Inputs: WITH_SERVER, WITH_CLIENT, WITH_ORACLE, WITH_DBCONVERTER, FORCE_USE_QT5
+# Inputs: WITH_SERVER, WITH_CLIENT, WITH_ORACLE, FORCE_USE_QT5
# Optional Input: QT6_DIR -- Hint as to where Qt6 lives on the system
# Optional Input: QT5_DIR -- Hint as to where Qt5 lives on the system
# Output: COCKATRICE_QT_VERSION_NAME -- Example values: Qt5, Qt6
# Output: SERVATRICE_QT_MODULES
# Output: COCKATRICE_QT_MODULES
# Output: ORACLE_QT_MODULES
-# Output: DBCONVERTER_QT_MODULES
# Output: TEST_QT_MODULES
set(REQUIRED_QT_COMPONENTS Core)
@@ -29,15 +28,12 @@ endif()
if(WITH_ORACLE)
set(_ORACLE_NEEDED Concurrent Network Svg Widgets)
endif()
-if(WITH_DBCONVERTER)
- set(_DBCONVERTER_NEEDED Network Widgets)
-endif()
if(TEST)
set(_TEST_NEEDED Widgets)
endif()
set(REQUIRED_QT_COMPONENTS ${REQUIRED_QT_COMPONENTS} ${_SERVATRICE_NEEDED} ${_COCKATRICE_NEEDED} ${_ORACLE_NEEDED}
- ${_DBCONVERTER_NEEDED} ${_TEST_NEEDED}
+ ${_TEST_NEEDED}
)
list(REMOVE_DUPLICATES REQUIRED_QT_COMPONENTS)
@@ -63,7 +59,7 @@ if(Qt6_FOUND)
endif()
else()
find_package(
- Qt5 5.8.0
+ Qt5 5.15.2
COMPONENTS ${REQUIRED_QT_COMPONENTS}
QUIET HINTS ${Qt5_DIR}
)
@@ -112,7 +108,6 @@ message(DEBUG "QT_LIBRARY_DIR = ${QT_LIBRARY_DIR}")
string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" SERVATRICE_QT_MODULES "${_SERVATRICE_NEEDED}")
string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" COCKATRICE_QT_MODULES "${_COCKATRICE_NEEDED}")
string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" ORACLE_QT_MODULES "${_ORACLE_NEEDED}")
-string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" DB_CONVERTER_QT_MODULES "${_DBCONVERTER_NEEDED}")
string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" TEST_QT_MODULES "${_TEST_NEEDED}")
# Core-only export (useful for headless libs)
diff --git a/cmake/NSIS.template.in b/cmake/NSIS.template.in
index fbdde0b86..a0d51fa2b 100644
--- a/cmake/NSIS.template.in
+++ b/cmake/NSIS.template.in
@@ -213,7 +213,6 @@ ${AndIf} ${FileExists} "$INSTDIR\portable.dat"
Delete "$INSTDIR\uninstall.exe"
Delete "$INSTDIR\cockatrice.exe"
Delete "$INSTDIR\oracle.exe"
- Delete "$INSTDIR\dbconverter.exe"
Delete "$INSTDIR\servatrice.exe"
Delete "$INSTDIR\Qt*.dll"
Delete "$INSTDIR\libmysql.dll"
diff --git a/cmake/SignMacApplications.cmake b/cmake/SignMacApplications.cmake
index b91e53725..c08c30c1e 100644
--- a/cmake/SignMacApplications.cmake
+++ b/cmake/SignMacApplications.cmake
@@ -3,7 +3,7 @@
string(LENGTH "$ENV{MACOS_CERTIFICATE_NAME}" MACOS_CERTIFICATE_NAME_LEN)
if(APPLE AND MACOS_CERTIFICATE_NAME_LEN GREATER 0)
- set(APPLICATIONS "cockatrice" "servatrice" "oracle" "dbconverter")
+ set(APPLICATIONS "cockatrice" "servatrice" "oracle")
foreach(app_name IN LISTS APPLICATIONS)
set(FULL_APP_PATH "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/${app_name}.app")
diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt
index c9fa80e6b..c6a29969f 100644
--- a/cockatrice/CMakeLists.txt
+++ b/cockatrice/CMakeLists.txt
@@ -156,6 +156,7 @@ set(cockatrice_SOURCES
src/interface/widgets/deck_editor/deck_editor_filter_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
src/interface/widgets/deck_editor/deck_list_style_proxy.cpp
+ src/interface/widgets/deck_editor/deck_state_manager.cpp
src/interface/widgets/general/background_sources.cpp
src/interface/widgets/general/display/background_plate_widget.cpp
src/interface/widgets/general/display/banner_widget.cpp
@@ -230,6 +231,7 @@ set(cockatrice_SOURCES
src/interface/widgets/tabs/abstract_tab_deck_editor.cpp
src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp
src/interface/widgets/tabs/api/archidekt/api_response/archidekt_deck_listing_api_response.cpp
+ src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card_entry.cpp
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_edition.cpp
@@ -286,6 +288,10 @@ set(cockatrice_SOURCES
src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
src/interface/key_signals.cpp
src/interface/logger.cpp
+ src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp
+ src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h
+ src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp
+ src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h
)
add_subdirectory(sounds)
diff --git a/cockatrice/resources/help/deck_search.md b/cockatrice/resources/help/deck_search.md
index 6d58ee590..c42e41c42 100644
--- a/cockatrice/resources/help/deck_search.md
+++ b/cockatrice/resources/help/deck_search.md
@@ -15,15 +15,18 @@ searches are case insensitive.
[n:red n:deck n:wins](#n:red n:deck n:wins) (Any deck with a name containing the words red, deck, and wins)
[n:"red deck wins"](#n:%22red deck wins%22) (Any deck with a name containing the exact phrase "red deck wins")
-File Name:
-[f:aggro](#f:aggro) (Any deck with a filename containing the word aggro)
-[f:red f:deck f:wins](#f:red f:deck f:wins) (Any deck with a filename containing the words red, deck, and wins)
-[f:"red deck wins"](#f:%22red deck wins%22) (Any deck with a filename containing the exact phrase "red deck wins")
+File Name:
+[fn:aggro](#fn:aggro) (Any deck with a filename containing the word aggro)
+[fn:red fn:deck fn:wins](#fn:red fn:deck fn:wins) (Any deck with a filename containing the words red, deck, and wins)
+[fn:"red deck wins"](#fn:%22red deck wins%22) (Any deck with a filename containing the exact phrase "red deck wins")
Relative Path (starting from the deck folder):
[p:aggro](#p:aggro) (Any deck that has "aggro" somewhere in its relative path)
[p:edh/](#p:edh/) (Any deck with "edh/" in its relative path, A.K.A. decks in the "edh" folder)
+Format:
+[f:standard](#f:standard) (Any deck with format set to standard)
+
Deck Contents (Uses [card search expressions](#cardSearchSyntaxHelp)):
[[plains]] (Any deck that contains at least one card with "plains" in its name)
[[t:legendary]] (Any deck that contains at least one legendary)
diff --git a/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp b/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp
index 5e9b874d0..0298daa6b 100644
--- a/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp
+++ b/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp
@@ -43,23 +43,23 @@ void DeckStatsInterface::queryFinished(QNetworkReply *reply)
deleteLater();
}
-void DeckStatsInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data)
+void DeckStatsInterface::getAnalyzeRequestData(const DeckList &deck, QByteArray &data)
{
DeckList deckWithoutTokens;
- copyDeckWithoutTokens(*deck, deckWithoutTokens);
+ copyDeckWithoutTokens(deck, deckWithoutTokens);
QUrl params;
QUrlQuery urlQuery;
urlQuery.addQueryItem("deck", deckWithoutTokens.writeToString_Plain());
- urlQuery.addQueryItem("decktitle", deck->getName());
+ urlQuery.addQueryItem("decktitle", deck.getName());
params.setQuery(urlQuery);
- data->append(params.query(QUrl::EncodeReserved).toUtf8());
+ data.append(params.query(QUrl::EncodeReserved).toUtf8());
}
-void DeckStatsInterface::analyzeDeck(DeckList *deck)
+void DeckStatsInterface::analyzeDeck(const DeckList &deck)
{
QByteArray data;
- getAnalyzeRequestData(deck, &data);
+ getAnalyzeRequestData(deck, data);
QNetworkRequest request(QUrl("https://deckstats.net/index.php"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
@@ -68,7 +68,7 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck)
manager->post(request, data);
}
-void DeckStatsInterface::copyDeckWithoutTokens(DeckList &source, DeckList &destination)
+void DeckStatsInterface::copyDeckWithoutTokens(const DeckList &source, DeckList &destination)
{
auto copyIfNotAToken = [this, &destination](const auto node, const auto card) {
CardInfoPtr dbCard = cardDatabase.query()->getCardInfo(card->getName());
diff --git a/cockatrice/src/client/network/interfaces/deck_stats_interface.h b/cockatrice/src/client/network/interfaces/deck_stats_interface.h
index fd1691a8b..7dc841027 100644
--- a/cockatrice/src/client/network/interfaces/deck_stats_interface.h
+++ b/cockatrice/src/client/network/interfaces/deck_stats_interface.h
@@ -28,15 +28,15 @@ private:
* closest non-token card instead. So we construct a new deck which has no
* tokens.
*/
- void copyDeckWithoutTokens(DeckList &source, DeckList &destination);
+ void copyDeckWithoutTokens(const DeckList &source, DeckList &destination);
private slots:
void queryFinished(QNetworkReply *reply);
- void getAnalyzeRequestData(DeckList *deck, QByteArray *data);
+ void getAnalyzeRequestData(const DeckList &deck, QByteArray &data);
public:
explicit DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = nullptr);
- void analyzeDeck(DeckList *deck);
+ void analyzeDeck(const DeckList &deck);
};
#endif
diff --git a/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp b/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp
index 137c7728c..a30a7f531 100644
--- a/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp
+++ b/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp
@@ -67,24 +67,24 @@ void TappedOutInterface::queryFinished(QNetworkReply *reply)
deleteLater();
}
-void TappedOutInterface::getAnalyzeRequestData(DeckList *deck, QByteArray *data)
+void TappedOutInterface::getAnalyzeRequestData(const DeckList &deck, QByteArray &data)
{
DeckList mainboard, sideboard;
- copyDeckSplitMainAndSide(*deck, mainboard, sideboard);
+ copyDeckSplitMainAndSide(deck, mainboard, sideboard);
QUrl params;
QUrlQuery urlQuery;
- urlQuery.addQueryItem("name", deck->getName());
+ urlQuery.addQueryItem("name", deck.getName());
urlQuery.addQueryItem("mainboard", mainboard.writeToString_Plain(false, true));
urlQuery.addQueryItem("sideboard", sideboard.writeToString_Plain(false, true));
params.setQuery(urlQuery);
- data->append(params.query(QUrl::EncodeReserved).toUtf8());
+ data.append(params.query(QUrl::EncodeReserved).toUtf8());
}
-void TappedOutInterface::analyzeDeck(DeckList *deck)
+void TappedOutInterface::analyzeDeck(const DeckList &deck)
{
QByteArray data;
- getAnalyzeRequestData(deck, &data);
+ getAnalyzeRequestData(deck, data);
QNetworkRequest request(QUrl("https://tappedout.net/mtg-decks/paste/"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
@@ -93,7 +93,7 @@ void TappedOutInterface::analyzeDeck(DeckList *deck)
manager->post(request, data);
}
-void TappedOutInterface::copyDeckSplitMainAndSide(DeckList &source, DeckList &mainboard, DeckList &sideboard)
+void TappedOutInterface::copyDeckSplitMainAndSide(const DeckList &source, DeckList &mainboard, DeckList &sideboard)
{
auto copyMainOrSide = [this, &mainboard, &sideboard](const auto node, const auto card) {
CardInfoPtr dbCard = cardDatabase.query()->getCardInfo(card->getName());
diff --git a/cockatrice/src/client/network/interfaces/tapped_out_interface.h b/cockatrice/src/client/network/interfaces/tapped_out_interface.h
index 056d7fd9a..0ea9c8358 100644
--- a/cockatrice/src/client/network/interfaces/tapped_out_interface.h
+++ b/cockatrice/src/client/network/interfaces/tapped_out_interface.h
@@ -30,14 +30,14 @@ private:
QNetworkAccessManager *manager;
CardDatabase &cardDatabase;
- void copyDeckSplitMainAndSide(DeckList &source, DeckList &mainboard, DeckList &sideboard);
+ void copyDeckSplitMainAndSide(const DeckList &source, DeckList &mainboard, DeckList &sideboard);
private slots:
void queryFinished(QNetworkReply *reply);
- void getAnalyzeRequestData(DeckList *deck, QByteArray *data);
+ void getAnalyzeRequestData(const DeckList &deck, QByteArray &data);
public:
explicit TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent = nullptr);
- void analyzeDeck(DeckList *deck);
+ void analyzeDeck(const DeckList &deck);
};
#endif
diff --git a/cockatrice/src/client/network/parsers/interface_json_deck_parser.h b/cockatrice/src/client/network/parsers/interface_json_deck_parser.h
index e0fe4967a..e19e4f4e4 100644
--- a/cockatrice/src/client/network/parsers/interface_json_deck_parser.h
+++ b/cockatrice/src/client/network/parsers/interface_json_deck_parser.h
@@ -18,21 +18,21 @@ class IJsonDeckParser
public:
virtual ~IJsonDeckParser() = default;
- virtual DeckLoader *parse(const QJsonObject &obj) = 0;
+ virtual DeckList parse(const QJsonObject &obj) = 0;
};
class ArchidektJsonParser : public IJsonDeckParser
{
public:
- DeckLoader *parse(const QJsonObject &obj) override
+ DeckList parse(const QJsonObject &obj) override
{
- DeckLoader *loader = new DeckLoader(nullptr);
+ DeckList deckList;
QString deckName = obj.value("name").toString();
QString deckDescription = obj.value("description").toString();
- loader->getDeckList()->setName(deckName);
- loader->getDeckList()->setComments(deckDescription);
+ deckList.setName(deckName);
+ deckList.setComments(deckDescription);
QString outputText;
QTextStream outStream(&outputText);
@@ -49,25 +49,25 @@ public:
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
}
- loader->getDeckList()->loadFromStream_Plain(outStream, false);
- loader->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId());
+ deckList.loadFromStream_Plain(outStream, false);
+ deckList.forEachCard(CardNodeFunction::ResolveProviderId());
- return loader;
+ return deckList;
}
};
class MoxfieldJsonParser : public IJsonDeckParser
{
public:
- DeckLoader *parse(const QJsonObject &obj) override
+ DeckList parse(const QJsonObject &obj) override
{
- DeckLoader *loader = new DeckLoader(nullptr);
+ DeckList deckList;
QString deckName = obj.value("name").toString();
QString deckDescription = obj.value("description").toString();
- loader->getDeckList()->setName(deckName);
- loader->getDeckList()->setComments(deckDescription);
+ deckList.setName(deckName);
+ deckList.setComments(deckDescription);
QString outputText;
QTextStream outStream(&outputText);
@@ -96,8 +96,8 @@ public:
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
}
- loader->getDeckList()->loadFromStream_Plain(outStream, false);
- loader->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId());
+ deckList.loadFromStream_Plain(outStream, false);
+ deckList.forEachCard(CardNodeFunction::ResolveProviderId());
QJsonObject commandersObj = obj.value("commanders").toObject();
if (!commandersObj.isEmpty()) {
@@ -108,12 +108,12 @@ public:
QString collectorNumber = cardData.value("cn").toString();
QString providerId = cardData.value("scryfall_id").toString();
- loader->getDeckList()->setBannerCard({commanderName, providerId});
- loader->getDeckList()->addCard(commanderName, DECK_ZONE_MAIN, -1, setName, collectorNumber, providerId);
+ deckList.setBannerCard({commanderName, providerId});
+ deckList.addCard(commanderName, DECK_ZONE_MAIN, -1, setName, collectorNumber, providerId);
}
}
- return loader;
+ return deckList;
}
};
diff --git a/cockatrice/src/client/settings/cache_settings.cpp b/cockatrice/src/client/settings/cache_settings.cpp
index 7a518df67..fde8e9b34 100644
--- a/cockatrice/src/client/settings/cache_settings.cpp
+++ b/cockatrice/src/client/settings/cache_settings.cpp
@@ -2,6 +2,7 @@
#include "../network/update/client/release_channel.h"
#include "card_counter_settings.h"
+#include "version_string.h"
#include
#include
@@ -198,7 +199,13 @@ SettingsCache::SettingsCache()
mbDownloadSpoilers = settings->value("personal/downloadspoilers", false).toBool();
- checkUpdatesOnStartup = settings->value("personal/startupUpdateCheck", true).toBool();
+ if (settings->contains("personal/startupUpdateCheck")) {
+ checkUpdatesOnStartup = settings->value("personal/startupUpdateCheck", true).toBool();
+ } else if (QString(VERSION_STRING).contains("custom", Qt::CaseInsensitive)) {
+ checkUpdatesOnStartup = false; // do not run auto updater on custom version
+ } else {
+ checkUpdatesOnStartup = true; // default to run auto updater
+ }
startupCardUpdateCheckPromptForUpdate =
settings->value("personal/startupCardUpdateCheckPromptForUpdate", true).toBool();
startupCardUpdateCheckAlwaysUpdate = settings->value("personal/startupCardUpdateCheckAlwaysUpdate", false).toBool();
@@ -206,7 +213,15 @@ SettingsCache::SettingsCache()
lastCardUpdateCheck = settings->value("personal/lastCardUpdateCheck", QDateTime::currentDateTime().date()).toDate();
notifyAboutUpdates = settings->value("personal/updatenotification", true).toBool();
notifyAboutNewVersion = settings->value("personal/newversionnotification", true).toBool();
- updateReleaseChannel = settings->value("personal/updatereleasechannel", 0).toInt();
+
+ if (settings->contains("personal/updatereleasechannel")) {
+ updateReleaseChannel = settings->value("personal/updatereleasechannel").toInt();
+ } else if (QString(VERSION_STRING).contains("beta", Qt::CaseInsensitive)) {
+ // default to beta if this is a beta release
+ updateReleaseChannel = 1;
+ } else {
+ updateReleaseChannel = 0; // stable
+ }
lang = settings->value("personal/lang").toString();
keepalive = settings->value("personal/keepalive", 3).toInt();
diff --git a/cockatrice/src/client/settings/shortcut_treeview.cpp b/cockatrice/src/client/settings/shortcut_treeview.cpp
index 6b329b23d..b909d47ac 100644
--- a/cockatrice/src/client/settings/shortcut_treeview.cpp
+++ b/cockatrice/src/client/settings/shortcut_treeview.cpp
@@ -150,12 +150,7 @@ void ShortcutTreeView::currentChanged(const QModelIndex ¤t, const QModelIn
*/
void ShortcutTreeView::updateSearchString(const QString &searchString)
{
-#if QT_VERSION > QT_VERSION_CHECK(5, 14, 0)
- const auto skipEmptyParts = Qt::SkipEmptyParts;
-#else
- const auto skipEmptyParts = QString::SkipEmptyParts;
-#endif
- QStringList searchWords = searchString.split(" ", skipEmptyParts);
+ QStringList searchWords = searchString.split(" ", Qt::SkipEmptyParts);
auto escapeRegex = [](const QString &s) { return QRegularExpression::escape(s); };
std::transform(searchWords.begin(), searchWords.end(), searchWords.begin(), escapeRegex);
diff --git a/cockatrice/src/filters/deck_filter_string.cpp b/cockatrice/src/filters/deck_filter_string.cpp
index dfc54afe0..5cce5d323 100644
--- a/cockatrice/src/filters/deck_filter_string.cpp
+++ b/cockatrice/src/filters/deck_filter_string.cpp
@@ -13,7 +13,7 @@ QueryPartList <- ComplexQueryPart ( ws ("AND" ws)? ComplexQueryPart)* ws*
ComplexQueryPart <- SomewhatComplexQueryPart ws "OR" ws ComplexQueryPart / SomewhatComplexQueryPart
SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart
-QueryPart <- NotQuery / DeckContentQuery / DeckNameQuery / FileNameQuery / PathQuery / GenericQuery
+QueryPart <- NotQuery / DeckContentQuery / DeckNameQuery / FileNameQuery / PathQuery / FormatQuery / GenericQuery
NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart
@@ -22,8 +22,9 @@ CardSearch <- '[[' CardFilterString ']]'
CardFilterString <- (!']]'.)*
DeckNameQuery <- ([Dd] 'eck')? [Nn] 'ame'? [:] String
-FileNameQuery <- [Ff] ('ile' 'name'?)? [:] String
+FileNameQuery <- [Ff] ([Nn] / 'ile' ([Nn] 'ame')?) [:] String
PathQuery <- [Pp] 'ath'? [:] String
+FormatQuery <- [Ff] 'ormat'? [:] String
GenericQuery <- String
@@ -118,7 +119,7 @@ static void setupParserRules()
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) -> bool {
int count = 0;
- auto cardNodes = deck->deckLoader->getDeckList()->getCardNodes();
+ auto cardNodes = deck->deckLoader->getDeck().deckList.getCardNodes();
for (auto node : cardNodes) {
auto cardInfoPtr = CardDatabaseManager::query()->getCardInfo(node->getName());
if (!cardInfoPtr.isNull() && cardFilter.check(cardInfoPtr)) {
@@ -138,7 +139,7 @@ static void setupParserRules()
search["DeckNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
- return deck->deckLoader->getDeckList()->getName().contains(name, Qt::CaseInsensitive);
+ return deck->deckLoader->getDeck().deckList.getName().contains(name, Qt::CaseInsensitive);
};
};
@@ -157,6 +158,14 @@ static void setupParserRules()
};
};
+ search["FormatQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
+ auto format = std::any_cast(sv[0]);
+ return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
+ auto gameFormat = deck->deckLoader->getDeck().deckList.getGameFormat();
+ return QString::compare(format, gameFormat, Qt::CaseInsensitive) == 0;
+ };
+ };
+
search["GenericQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
diff --git a/cockatrice/src/game/deckview/deck_view_container.cpp b/cockatrice/src/game/deckview/deck_view_container.cpp
index 5d4a07bdf..c7876a12f 100644
--- a/cockatrice/src/game/deckview/deck_view_container.cpp
+++ b/cockatrice/src/game/deckview/deck_view_container.cpp
@@ -269,12 +269,12 @@ void DeckViewContainer::loadDeckFromFile(const QString &filePath)
return;
}
- loadDeckFromDeckLoader(&deck);
+ loadDeckFromDeckList(deck.getDeck().deckList);
}
-void DeckViewContainer::loadDeckFromDeckLoader(DeckLoader *deck)
+void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck)
{
- QString deckString = deck->getDeckList()->writeToString_Native();
+ QString deckString = deck.writeToString_Native();
if (deckString.length() > MAX_FILE_LENGTH) {
QMessageBox::critical(this, tr("Error"), tr("Deck is greater than maximum file size."));
@@ -308,8 +308,8 @@ void DeckViewContainer::loadFromClipboard()
return;
}
- DeckLoader *deck = dlg.getDeckList();
- loadDeckFromDeckLoader(deck);
+ DeckList deck = dlg.getDeckList();
+ loadDeckFromDeckList(deck);
}
void DeckViewContainer::loadFromWebsite()
@@ -320,16 +320,15 @@ void DeckViewContainer::loadFromWebsite()
return;
}
- DeckLoader *deck = dlg.getDeck();
- loadDeckFromDeckLoader(deck);
+ DeckList deck = dlg.getDeck();
+ loadDeckFromDeckList(deck);
}
void DeckViewContainer::deckSelectFinished(const Response &r)
{
const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext);
- DeckLoader newDeck(this, new DeckList(QString::fromStdString(resp.deck())));
- CardPictureLoader::cacheCardPixmaps(
- CardDatabaseManager::query()->getCards(newDeck.getDeckList()->getCardRefList()));
+ DeckList newDeck = DeckList(QString::fromStdString(resp.deck()));
+ CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(newDeck.getCardRefList()));
setDeck(newDeck);
switchToDeckLoadedView();
}
@@ -410,8 +409,8 @@ void DeckViewContainer::setSideboardLocked(bool locked)
deckView->resetSideboardPlan();
}
-void DeckViewContainer::setDeck(DeckLoader &deck)
+void DeckViewContainer::setDeck(const DeckList &deck)
{
- deckView->setDeck(*deck.getDeckList());
+ deckView->setDeck(deck);
switchToDeckLoadedView();
}
\ No newline at end of file
diff --git a/cockatrice/src/game/deckview/deck_view_container.h b/cockatrice/src/game/deckview/deck_view_container.h
index 620929789..6d685cd79 100644
--- a/cockatrice/src/game/deckview/deck_view_container.h
+++ b/cockatrice/src/game/deckview/deck_view_container.h
@@ -85,12 +85,12 @@ public:
void setReadyStart(bool ready);
void readyAndUpdate();
void setSideboardLocked(bool locked);
- void setDeck(DeckLoader &deck);
+ void setDeck(const DeckList &deck);
void setVisualDeckStorageExists(bool exists);
public slots:
void loadDeckFromFile(const QString &filePath);
- void loadDeckFromDeckLoader(DeckLoader *deck);
+ void loadDeckFromDeckList(const DeckList &deck);
};
#endif // DECK_VIEW_CONTAINER_H
diff --git a/cockatrice/src/game/dialogs/dlg_create_token.cpp b/cockatrice/src/game/dialogs/dlg_create_token.cpp
index 1f6f9b08c..836c0c16a 100644
--- a/cockatrice/src/game/dialogs/dlg_create_token.cpp
+++ b/cockatrice/src/game/dialogs/dlg_create_token.cpp
@@ -117,11 +117,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
chooseTokenFromDeckRadioButton->setDisabled(true); // No tokens in deck = no need for option
} else {
chooseTokenFromDeckRadioButton->setChecked(true);
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
cardDatabaseDisplayModel->setCardNameSet(QSet(predefinedTokens.begin(), predefinedTokens.end()));
-#else
- cardDatabaseDisplayModel->setCardNameSet(QSet::fromList(predefinedTokens));
-#endif
}
auto *tokenChooseLayout = new QVBoxLayout;
@@ -223,11 +219,7 @@ void DlgCreateToken::actChooseTokenFromAll(bool checked)
void DlgCreateToken::actChooseTokenFromDeck(bool checked)
{
if (checked) {
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
cardDatabaseDisplayModel->setCardNameSet(QSet(predefinedTokens.begin(), predefinedTokens.end()));
-#else
- cardDatabaseDisplayModel->setCardNameSet(QSet::fromList(predefinedTokens));
-#endif
}
}
diff --git a/cockatrice/src/game/player/menu/utility_menu.cpp b/cockatrice/src/game/player/menu/utility_menu.cpp
index ab14d629a..37bdcbbaf 100644
--- a/cockatrice/src/game/player/menu/utility_menu.cpp
+++ b/cockatrice/src/game/player/menu/utility_menu.cpp
@@ -60,13 +60,13 @@ void UtilityMenu::populatePredefinedTokensMenu()
clear();
setEnabled(false);
predefinedTokens.clear();
- DeckLoader *_deck = player->getDeck();
+ const DeckList &deckList = player->getDeck();
- if (!_deck) {
+ if (deckList.isEmpty()) {
return;
}
- auto tokenCardNodes = _deck->getDeckList()->getCardNodes({DECK_ZONE_TOKENS});
+ auto tokenCardNodes = deckList.getCardNodes({DECK_ZONE_TOKENS});
if (!tokenCardNodes.isEmpty()) {
setEnabled(true);
diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp
index efce40cd7..0723ae6bc 100644
--- a/cockatrice/src/game/player/player.cpp
+++ b/cockatrice/src/game/player/player.cpp
@@ -32,7 +32,7 @@
Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent)
: QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)),
playerEventHandler(new PlayerEventHandler(this)), playerActions(new PlayerActions(this)), active(false),
- conceded(false), deck(nullptr), zoneId(0), dialogSemaphore(false)
+ conceded(false), zoneId(0), dialogSemaphore(false)
{
initializeZones();
@@ -263,10 +263,9 @@ void Player::deleteCard(CardItem *card)
}
}
-// TODO: Does a player need a DeckLoader?
-void Player::setDeck(DeckLoader &_deck)
+void Player::setDeck(const DeckList &_deck)
{
- deck = new DeckLoader(this, _deck.getDeckList());
+ deck = _deck;
emit deckChanged();
}
diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h
index 0a4fc9e69..0a03b3abe 100644
--- a/cockatrice/src/game/player/player.h
+++ b/cockatrice/src/game/player/player.h
@@ -9,6 +9,7 @@
#include "../../game_graphics/board/abstract_graphics_item.h"
#include "../../interface/widgets/menus/tearoff_menu.h"
+#include "../interface/deck_loader/loaded_deck.h"
#include "../zones/logic/hand_zone_logic.h"
#include "../zones/logic/pile_zone_logic.h"
#include "../zones/logic/stack_zone_logic.h"
@@ -44,7 +45,6 @@ class ArrowTarget;
class CardDatabase;
class CardZone;
class CommandContainer;
-class DeckLoader;
class GameCommand;
class GameEvent;
class PlayerInfo;
@@ -66,7 +66,7 @@ class Player : public QObject
Q_OBJECT
signals:
- void openDeckEditor(DeckLoader *deck);
+ void openDeckEditor(const LoadedDeck &deck);
void deckChanged();
void newCardAdded(AbstractCardItem *card);
void rearrangeCounters();
@@ -130,9 +130,9 @@ public:
return playerMenu;
}
- void setDeck(DeckLoader &_deck);
+ void setDeck(const DeckList &_deck);
- [[nodiscard]] DeckLoader *getDeck() const
+ [[nodiscard]] const DeckList &getDeck() const
{
return deck;
}
@@ -241,7 +241,7 @@ private:
bool active;
bool conceded;
- DeckLoader *deck;
+ DeckList deck;
int zoneId;
QMap zones;
diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp
index 058fdb510..444eecb0d 100644
--- a/cockatrice/src/game/player/player_actions.cpp
+++ b/cockatrice/src/game/player/player_actions.cpp
@@ -218,7 +218,7 @@ void PlayerActions::actAlwaysLookAtTopCard()
void PlayerActions::actOpenDeckInDeckEditor()
{
- emit player->openDeckEditor(player->getDeck());
+ emit player->openDeckEditor({.deckList = player->getDeck()});
}
void PlayerActions::actViewGraveyard()
diff --git a/cockatrice/src/game/player/player_event_handler.cpp b/cockatrice/src/game/player/player_event_handler.cpp
index 4cf814d86..331605918 100644
--- a/cockatrice/src/game/player/player_event_handler.cpp
+++ b/cockatrice/src/game/player/player_event_handler.cpp
@@ -78,14 +78,7 @@ void PlayerEventHandler::eventShuffle(const Event_Shuffle &event)
void PlayerEventHandler::eventRollDie(const Event_RollDie &event)
{
if (!event.values().empty()) {
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QList rolls(event.values().begin(), event.values().end());
-#else
- QList rolls;
- for (const auto &value : event.values()) {
- rolls.append(value);
- }
-#endif
std::sort(rolls.begin(), rolls.end());
emit logRollDie(player, static_cast(event.sides()), rolls);
} else if (event.value()) {
diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp
index 91abb4663..e15a466c3 100644
--- a/cockatrice/src/game/zones/view_zone_widget.cpp
+++ b/cockatrice/src/game/zones/view_zone_widget.cpp
@@ -13,12 +13,20 @@
#include
#include
#include
+#include
#include
#include
#include
+#include
#include
#include
+namespace
+{
+constexpr qreal kTitleBarHeight = 24.0;
+constexpr qreal kMinVisibleWidth = 100.0;
+} // namespace
+
/**
* @param _player the player the cards were revealed to.
* @param _origZone the zone the cards were revealed from.
@@ -241,33 +249,182 @@ void ZoneViewWidget::retranslateUi()
pileViewCheckBox.setText(tr("pile view"));
}
-void ZoneViewWidget::moveEvent(QGraphicsSceneMoveEvent * /* event */)
+void ZoneViewWidget::stopWindowDrag()
{
- if (!scene())
+ if (!draggingWindow)
return;
- int titleBarHeight = 24;
+ draggingWindow = false;
+ ungrabMouse();
+}
- QPointF scenePos = pos();
+void ZoneViewWidget::startWindowDrag(QGraphicsSceneMouseEvent *event)
+{
+ draggingWindow = true;
+ dragStartItemPos = pos();
+ dragStartScreenPos = event->screenPos();
+ dragView = findDragView(event->widget());
- if (scenePos.x() < 0) {
- scenePos.setX(0);
- } else {
- qreal maxw = scene()->sceneRect().width() - 100;
- if (scenePos.x() > maxw)
- scenePos.setX(maxw);
+ // need to grab mouse to receive events and not miss initial movement
+ grabMouse();
+}
+
+QRectF ZoneViewWidget::closeButtonRect(QWidget *styleWidget) const
+{
+ const QRectF frameRectF = windowFrameRect();
+ const QRect titleBarRect(frameRectF.toRect().x(), frameRectF.toRect().y(), frameRectF.toRect().width(),
+ static_cast(kTitleBarHeight));
+
+ // query the style for the close button position (handles macOS top-left placement)
+ if (styleWidget) {
+ QStyleOptionTitleBar opt;
+ opt.initFrom(styleWidget);
+ opt.rect = titleBarRect;
+ opt.text = windowTitle();
+ opt.icon = styleWidget->windowIcon();
+ opt.titleBarFlags = Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
+ opt.subControls = QStyle::SC_TitleBarCloseButton;
+ opt.activeSubControls = QStyle::SC_TitleBarCloseButton;
+ opt.titleBarState = styleWidget->isActiveWindow() ? Qt::WindowActive : Qt::WindowNoState;
+ if (styleWidget->isActiveWindow())
+ opt.state |= QStyle::State_Active;
+ const QRect r = styleWidget->style()->subControlRect(QStyle::CC_TitleBar, &opt, QStyle::SC_TitleBarCloseButton,
+ styleWidget);
+ if (r.isValid() && !r.isEmpty()) {
+ return QRectF(r);
+ }
}
- if (scenePos.y() < titleBarHeight) {
- scenePos.setY(titleBarHeight);
- } else {
- qreal maxh = scene()->sceneRect().height() - titleBarHeight;
- if (scenePos.y() > maxh)
- scenePos.setY(maxh);
+ // fallback: square at right end of titlebar (Windows/Linux style)
+ return QRectF(frameRectF.right() - kTitleBarHeight, frameRectF.top(), kTitleBarHeight, kTitleBarHeight);
+}
+
+QGraphicsView *ZoneViewWidget::findDragView(QWidget *eventWidget) const
+{
+ QWidget *current = eventWidget;
+ while (current) {
+ if (auto *view = qobject_cast(current))
+ return view;
+ current = current->parentWidget();
}
- if (scenePos != pos())
- setPos(scenePos);
+ if (scene() && !scene()->views().isEmpty())
+ return scene()->views().constFirst();
+
+ return nullptr;
+}
+
+QPointF ZoneViewWidget::calcDraggedWindowPos(const QPoint &screenPos,
+ const QPointF &scenePos,
+ const QPointF &buttonDownScenePos) const
+{
+ if (dragView && dragView->viewport()) {
+ const QPoint vpStart = dragView->viewport()->mapFromGlobal(dragStartScreenPos);
+ const QPoint vpNow = dragView->viewport()->mapFromGlobal(screenPos);
+ const QPointF sceneStart = dragView->mapToScene(vpStart);
+ const QPointF sceneNow = dragView->mapToScene(vpNow);
+ return dragStartItemPos + (sceneNow - sceneStart);
+ }
+
+ return dragStartItemPos + (scenePos - buttonDownScenePos);
+}
+
+bool ZoneViewWidget::windowFrameEvent(QEvent *event)
+{
+ if (event->type() == QEvent::UngrabMouse) {
+ stopWindowDrag();
+ return QGraphicsWidget::windowFrameEvent(event);
+ }
+
+ auto *me = dynamic_cast(event);
+ if (!me)
+ return QGraphicsWidget::windowFrameEvent(event);
+
+ switch (event->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ if (me->button() == Qt::LeftButton && windowFrameSectionAt(me->pos()) == Qt::TitleBarArea) {
+ // avoid drag on close button
+ if (closeButtonRect(me->widget()).contains(me->pos())) {
+ me->accept();
+ close();
+ return true;
+ }
+ startWindowDrag(me);
+ me->accept();
+ return true;
+ }
+ break;
+
+ case QEvent::GraphicsSceneMouseMove:
+ if (draggingWindow) {
+ if (!(me->buttons() & Qt::LeftButton)) {
+ stopWindowDrag();
+ } else {
+ setPos(
+ calcDraggedWindowPos(me->screenPos(), me->scenePos(), me->buttonDownScenePos(Qt::LeftButton)));
+ }
+ me->accept();
+ return true;
+ }
+ break;
+
+ case QEvent::GraphicsSceneMouseRelease:
+ if (draggingWindow && me->button() == Qt::LeftButton) {
+ stopWindowDrag();
+ me->accept();
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return QGraphicsWidget::windowFrameEvent(event);
+}
+
+void ZoneViewWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ // move if the scene routes moves while dragging
+ if (draggingWindow && (event->buttons() & Qt::LeftButton)) {
+ setPos(calcDraggedWindowPos(event->screenPos(), event->scenePos(), event->buttonDownScenePos(Qt::LeftButton)));
+ event->accept();
+ return;
+ }
+
+ QGraphicsWidget::mouseMoveEvent(event);
+}
+
+void ZoneViewWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (draggingWindow && event->button() == Qt::LeftButton) {
+ stopWindowDrag();
+ event->accept();
+ return;
+ }
+
+ QGraphicsWidget::mouseReleaseEvent(event);
+}
+
+QVariant ZoneViewWidget::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ if (change == QGraphicsItem::ItemPositionChange && scene()) {
+ // Keep grab area in main view
+ const QRectF sceneRect = scene()->sceneRect();
+ const QPointF requestedPos = value.toPointF();
+ QPointF desiredPos = requestedPos;
+
+ const qreal minX = sceneRect.left();
+ const qreal maxX = qMax(minX, sceneRect.right() - kMinVisibleWidth);
+ const qreal minY = sceneRect.top() + kTitleBarHeight;
+ const qreal maxY = qMax(minY, sceneRect.bottom() - kTitleBarHeight);
+
+ desiredPos.setX(qBound(minX, desiredPos.x(), maxX));
+ desiredPos.setY(qBound(minY, desiredPos.y(), maxY));
+ return desiredPos;
+ }
+
+ return QGraphicsWidget::itemChange(change, value);
}
void ZoneViewWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
@@ -350,6 +507,7 @@ void ZoneViewWidget::handleScrollBarChange(int value)
void ZoneViewWidget::closeEvent(QCloseEvent *event)
{
+ stopWindowDrag();
disconnect(zone, &ZoneViewZone::closed, this, 0);
// manually call zone->close in order to remove it from the origZones views
zone->close();
diff --git a/cockatrice/src/game/zones/view_zone_widget.h b/cockatrice/src/game/zones/view_zone_widget.h
index 272ff5560..4ed8f74d8 100644
--- a/cockatrice/src/game/zones/view_zone_widget.h
+++ b/cockatrice/src/game/zones/view_zone_widget.h
@@ -3,7 +3,6 @@
* @ingroup GameGraphicsZones
* @brief TODO: Document this.
*/
-
#ifndef ZONEVIEWWIDGET_H
#define ZONEVIEWWIDGET_H
@@ -14,6 +13,7 @@
#include
#include
#include
+#include
#include
class QLabel;
@@ -28,6 +28,8 @@ class ServerInfo_Card;
class QGraphicsSceneMouseEvent;
class QGraphicsSceneWheelEvent;
class QStyleOption;
+class QGraphicsView;
+class QWidget;
class ScrollableGraphicsProxyWidget : public QGraphicsProxyWidget
{
@@ -66,6 +68,33 @@ private:
int extraHeight;
Player *player;
+ bool draggingWindow = false;
+ QPoint dragStartScreenPos;
+ QPointF dragStartItemPos;
+ QPointer dragView;
+
+ void stopWindowDrag();
+ void startWindowDrag(QGraphicsSceneMouseEvent *event);
+ QRectF closeButtonRect(QWidget *styleWidget) const;
+ /**
+ * @brief Resolves the QGraphicsView to use for drag coordinate mapping
+ *
+ * @param eventWidget QWidget that originated the mouse event
+ * @return The resolved QGraphicsView
+ */
+ QGraphicsView *findDragView(QWidget *eventWidget) const;
+ /**
+ * @brief Calculates the desired widget position while dragging
+ *
+ * @param screenPos Global screen coordinates of the current mouse position
+ * @param scenePos Scene coordinates of the current mouse position
+ * @param buttonDownScenePos Scene coordinates of the initial mouse press position
+ *
+ * @return The new widget position in scene coordinates
+ */
+ QPointF
+ calcDraggedWindowPos(const QPoint &screenPos, const QPointF &scenePos, const QPointF &buttonDownScenePos) const;
+
void resizeScrollbar(qreal newZoneHeight);
signals:
void closePressed(ZoneViewWidget *zv);
@@ -76,7 +105,6 @@ private slots:
void resizeToZoneContents(bool forceInitialHeight = false);
void handleScrollBarChange(int value);
void zoneDeleted();
- void moveEvent(QGraphicsSceneMoveEvent * /* event */) override;
void resizeEvent(QGraphicsSceneResizeEvent * /* event */) override;
void expandWindow();
@@ -101,6 +129,10 @@ public:
protected:
void closeEvent(QCloseEvent *event) override;
void initStyleOption(QStyleOption *option) const override;
+ bool windowFrameEvent(QEvent *event) override;
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
};
diff --git a/cockatrice/src/game_graphics/board/abstract_graphics_item.cpp b/cockatrice/src/game_graphics/board/abstract_graphics_item.cpp
index 8ced8d5bc..05f4a41ab 100644
--- a/cockatrice/src/game_graphics/board/abstract_graphics_item.cpp
+++ b/cockatrice/src/game_graphics/board/abstract_graphics_item.cpp
@@ -17,12 +17,7 @@ void AbstractGraphicsItem::paintNumberEllipse(int number,
font.setWeight(QFont::Bold);
QFontMetrics fm(font);
- double w = 1.3 *
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
- fm.horizontalAdvance(numStr);
-#else
- fm.width(numStr);
-#endif
+ double w = 1.3 * fm.horizontalAdvance(numStr);
double h = fm.height() * 1.3;
if (w < h)
w = h;
diff --git a/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp b/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp
index 77fddc9de..128b03c95 100644
--- a/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp
+++ b/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp
@@ -21,9 +21,7 @@ CardPictureLoaderWorker::CardPictureLoaderWorker()
// We need a timeout to ensure requests don't hang indefinitely in case of
// cache corruption, see related Qt bug: https://bugreports.qt.io/browse/QTBUG-111397
// Use Qt's default timeout (30s, as of 2023-02-22)
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
networkManager->setTransferTimeout();
-#endif
cache = new QNetworkDiskCache(this);
cache->setCacheDirectory(SettingsCache::instance().getNetworkCachePath());
cache->setMaximumCacheSize(1024L * 1024L *
diff --git a/cockatrice/src/interface/deck_loader/deck_loader.cpp b/cockatrice/src/interface/deck_loader/deck_loader.cpp
index a3f0ff619..45c3ec1ba 100644
--- a/cockatrice/src/interface/deck_loader/deck_loader.cpp
+++ b/cockatrice/src/interface/deck_loader/deck_loader.cpp
@@ -25,11 +25,7 @@ const QStringList DeckLoader::ACCEPTED_FILE_EXTENSIONS = {"*.cod", "*.dec", "*.d
const QStringList DeckLoader::FILE_NAME_FILTERS = {
tr("Common deck formats (%1)").arg(ACCEPTED_FILE_EXTENSIONS.join(" ")), tr("All files (*.*)")};
-DeckLoader::DeckLoader(QObject *parent) : QObject(parent), deckList(new DeckList())
-{
-}
-
-DeckLoader::DeckLoader(QObject *parent, DeckList *_deckList) : QObject(parent), deckList(_deckList)
+DeckLoader::DeckLoader(QObject *parent) : QObject(parent)
{
}
@@ -41,17 +37,18 @@ bool DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fm
}
bool result = false;
+ DeckList deckList = DeckList();
switch (fmt) {
case DeckFileFormat::PlainText:
- result = deckList->loadFromFile_Plain(&file);
+ result = deckList.loadFromFile_Plain(&file);
break;
case DeckFileFormat::Cockatrice: {
- result = deckList->loadFromFile_Native(&file);
+ result = deckList.loadFromFile_Native(&file);
qCInfo(DeckLoaderLog) << "Loaded from" << fileName << "-" << result;
if (!result) {
qCInfo(DeckLoaderLog) << "Retrying as plain format";
file.seek(0);
- result = deckList->loadFromFile_Plain(&file);
+ result = deckList.loadFromFile_Plain(&file);
fmt = DeckFileFormat::PlainText;
}
break;
@@ -62,7 +59,8 @@ bool DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fm
}
if (result) {
- lastLoadInfo = {
+ loadedDeck.deckList = deckList;
+ loadedDeck.lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
@@ -86,7 +84,7 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, DeckFileFormat::Form
watcher->deleteLater();
if (result) {
- lastLoadInfo = {
+ loadedDeck.lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
@@ -107,13 +105,13 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, DeckFileFormat::Form
switch (fmt) {
case DeckFileFormat::PlainText:
- return deckList->loadFromFile_Plain(&file);
+ return loadedDeck.deckList.loadFromFile_Plain(&file);
case DeckFileFormat::Cockatrice: {
bool result = false;
- result = deckList->loadFromFile_Native(&file);
+ result = loadedDeck.deckList.loadFromFile_Native(&file);
if (!result) {
file.seek(0);
- return deckList->loadFromFile_Plain(&file);
+ return loadedDeck.deckList.loadFromFile_Plain(&file);
}
return result;
}
@@ -129,9 +127,9 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, DeckFileFormat::Form
bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId)
{
- bool result = deckList->loadFromString_Native(nativeString);
+ bool result = loadedDeck.deckList.loadFromString_Native(nativeString);
if (result) {
- lastLoadInfo = {
+ loadedDeck.lastLoadInfo = {
.remoteDeckId = remoteDeckId,
};
@@ -150,16 +148,16 @@ bool DeckLoader::saveToFile(const QString &fileName, DeckFileFormat::Format fmt)
bool result = false;
switch (fmt) {
case DeckFileFormat::PlainText:
- result = deckList->saveToFile_Plain(&file);
+ result = loadedDeck.deckList.saveToFile_Plain(&file);
break;
case DeckFileFormat::Cockatrice:
- result = deckList->saveToFile_Native(&file);
+ result = loadedDeck.deckList.saveToFile_Native(&file);
qCInfo(DeckLoaderLog) << "Saving to " << fileName << "-" << result;
break;
}
if (result) {
- lastLoadInfo = {
+ loadedDeck.lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
@@ -194,18 +192,18 @@ bool DeckLoader::updateLastLoadedTimestamp(const QString &fileName, DeckFileForm
// Perform file modifications
switch (fmt) {
case DeckFileFormat::PlainText:
- result = deckList->saveToFile_Plain(&file);
+ result = loadedDeck.deckList.saveToFile_Plain(&file);
break;
case DeckFileFormat::Cockatrice:
- deckList->setLastLoadedTimestamp(QDateTime::currentDateTime().toString());
- result = deckList->saveToFile_Native(&file);
+ loadedDeck.deckList.setLastLoadedTimestamp(QDateTime::currentDateTime().toString());
+ result = loadedDeck.deckList.saveToFile_Native(&file);
break;
}
file.close(); // Close the file to ensure changes are flushed
if (result) {
- lastLoadInfo = {
+ loadedDeck.lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
@@ -289,14 +287,14 @@ static QString toDecklistExportString(const QList &car
* @param deckList The decklist to export
* @param website The website we're sending the deck to
*/
-QString DeckLoader::exportDeckToDecklist(const DeckList *deckList, DecklistWebsite website)
+QString DeckLoader::exportDeckToDecklist(const DeckList &deckList, DecklistWebsite website)
{
// Add the base url
QString deckString = "https://" + getDomainForWebsite(website) + "/?";
// export all cards in zone
- QString mainBoardCards = toDecklistExportString(deckList->getCardNodes({DECK_ZONE_MAIN}));
- QString sideBoardCards = toDecklistExportString(deckList->getCardNodes({DECK_ZONE_SIDE}));
+ QString mainBoardCards = toDecklistExportString(deckList.getCardNodes({DECK_ZONE_MAIN}));
+ QString sideBoardCards = toDecklistExportString(deckList.getCardNodes({DECK_ZONE_SIDE}));
// Remove the extra return at the end of the last cards
mainBoardCards.chop(3);
@@ -312,7 +310,7 @@ QString DeckLoader::exportDeckToDecklist(const DeckList *deckList, DecklistWebsi
return deckString;
}
-void DeckLoader::saveToClipboard(const DeckList *deckList, bool addComments, bool addSetNameAndNumber)
+void DeckLoader::saveToClipboard(const DeckList &deckList, bool addComments, bool addSetNameAndNumber)
{
QString buffer;
QTextStream stream(&buffer);
@@ -322,7 +320,7 @@ void DeckLoader::saveToClipboard(const DeckList *deckList, bool addComments, boo
}
bool DeckLoader::saveToStream_Plain(QTextStream &out,
- const DeckList *deckList,
+ const DeckList &deckList,
bool addComments,
bool addSetNameAndNumber)
{
@@ -331,7 +329,7 @@ bool DeckLoader::saveToStream_Plain(QTextStream &out,
}
// loop zones
- for (auto zoneNode : deckList->getZoneNodes()) {
+ for (auto zoneNode : deckList.getZoneNodes()) {
saveToStream_DeckZone(out, zoneNode, addComments, addSetNameAndNumber);
// end of zone
@@ -341,14 +339,14 @@ bool DeckLoader::saveToStream_Plain(QTextStream &out,
return true;
}
-void DeckLoader::saveToStream_DeckHeader(QTextStream &out, const DeckList *deckList)
+void DeckLoader::saveToStream_DeckHeader(QTextStream &out, const DeckList &deckList)
{
- if (!deckList->getName().isEmpty()) {
- out << "// " << deckList->getName() << "\n\n";
+ if (!deckList.getName().isEmpty()) {
+ out << "// " << deckList.getName() << "\n\n";
}
- if (!deckList->getComments().isEmpty()) {
- QStringList commentRows = deckList->getComments().split(QRegularExpression("\n|\r\n|\r"));
+ if (!deckList.getComments().isEmpty()) {
+ QStringList commentRows = deckList.getComments().split(QRegularExpression("\n|\r\n|\r"));
for (const QString &row : commentRows) {
out << "// " << row << "\n";
}
@@ -436,7 +434,7 @@ void DeckLoader::saveToStream_DeckZoneCards(QTextStream &out,
}
}
-bool DeckLoader::convertToCockatriceFormat(QString fileName)
+bool DeckLoader::convertToCockatriceFormat(const QString &fileName)
{
// Change the file extension to .cod
QFileInfo fileInfo(fileName);
@@ -455,7 +453,7 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName)
switch (DeckFileFormat::getFormatFromName(fileName)) {
case DeckFileFormat::PlainText:
// Save in Cockatrice's native format
- result = deckList->saveToFile_Native(&file);
+ result = loadedDeck.deckList.saveToFile_Native(&file);
break;
case DeckFileFormat::Cockatrice:
qCInfo(DeckLoaderLog) << "File is already in Cockatrice format. No conversion needed.";
@@ -476,7 +474,7 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName)
} else {
qCInfo(DeckLoaderLog) << "Original file deleted successfully:" << fileName;
}
- lastLoadInfo = {
+ loadedDeck.lastLoadInfo = {
.fileName = newFileName,
.fileFormat = DeckFileFormat::Cockatrice,
};
@@ -485,29 +483,6 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName)
return result;
}
-QString DeckLoader::getCardZoneFromName(const QString &cardName, QString currentZoneName)
-{
- CardInfoPtr card = CardDatabaseManager::query()->getCardInfo(cardName);
-
- if (card && card->getIsToken()) {
- return DECK_ZONE_TOKENS;
- }
-
- return currentZoneName;
-}
-
-QString DeckLoader::getCompleteCardName(const QString &cardName)
-{
- if (CardDatabaseManager::getInstance()) {
- ExactCard temp = CardDatabaseManager::query()->guessCard({cardName});
- if (temp) {
- return temp.getName();
- }
- }
-
- return cardName;
-}
-
void DeckLoader::printDeckListNode(QTextCursor *cursor, const InnerDecklistNode *node)
{
const int totalColumns = 2;
@@ -568,7 +543,7 @@ void DeckLoader::printDeckListNode(QTextCursor *cursor, const InnerDecklistNode
cursor->movePosition(QTextCursor::End);
}
-void DeckLoader::printDeckList(QPrinter *printer, const DeckList *deckList)
+void DeckLoader::printDeckList(QPrinter *printer, const DeckList &deckList)
{
QTextDocument doc;
@@ -584,14 +559,14 @@ void DeckLoader::printDeckList(QPrinter *printer, const DeckList *deckList)
headerCharFormat.setFontWeight(QFont::Bold);
cursor.insertBlock(headerBlockFormat, headerCharFormat);
- cursor.insertText(deckList->getName());
+ cursor.insertText(deckList.getName());
headerCharFormat.setFontPointSize(12);
cursor.insertBlock(headerBlockFormat, headerCharFormat);
- cursor.insertText(deckList->getComments());
+ cursor.insertText(deckList.getComments());
cursor.insertBlock(headerBlockFormat, headerCharFormat);
- for (auto zoneNode : deckList->getZoneNodes()) {
+ for (auto zoneNode : deckList.getZoneNodes()) {
cursor.insertHtml("
");
cursor.insertBlock(headerBlockFormat, headerCharFormat);
diff --git a/cockatrice/src/interface/deck_loader/deck_loader.h b/cockatrice/src/interface/deck_loader/deck_loader.h
index ae3088ff8..ec5636995 100644
--- a/cockatrice/src/interface/deck_loader/deck_loader.h
+++ b/cockatrice/src/interface/deck_loader/deck_loader.h
@@ -41,28 +41,16 @@ public:
};
private:
- DeckList *deckList;
- LoadedDeck::LoadInfo lastLoadInfo;
+ LoadedDeck loadedDeck;
public:
DeckLoader(QObject *parent);
- DeckLoader(QObject *parent, DeckList *_deckList);
DeckLoader(const DeckLoader &) = delete;
DeckLoader &operator=(const DeckLoader &) = delete;
- const LoadedDeck::LoadInfo &getLastLoadInfo() const
- {
- return lastLoadInfo;
- }
-
- void setLastLoadInfo(const LoadedDeck::LoadInfo &info)
- {
- lastLoadInfo = info;
- }
-
[[nodiscard]] bool hasNotBeenLoaded() const
{
- return lastLoadInfo.isEmpty();
+ return loadedDeck.lastLoadInfo.isEmpty();
}
bool loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bool userRequest = false);
@@ -71,11 +59,11 @@ public:
bool saveToFile(const QString &fileName, DeckFileFormat::Format fmt);
bool updateLastLoadedTimestamp(const QString &fileName, DeckFileFormat::Format fmt);
- static QString exportDeckToDecklist(const DeckList *deckList, DecklistWebsite website);
+ static QString exportDeckToDecklist(const DeckList &deckList, DecklistWebsite website);
- static void saveToClipboard(const DeckList *deckList, bool addComments = true, bool addSetNameAndNumber = true);
+ static void saveToClipboard(const DeckList &deckList, bool addComments = true, bool addSetNameAndNumber = true);
static bool saveToStream_Plain(QTextStream &out,
- const DeckList *deckList,
+ const DeckList &deckList,
bool addComments = true,
bool addSetNameAndNumber = true);
@@ -84,18 +72,26 @@ public:
* @param printer The printer to render the decklist to.
* @param deckList
*/
- static void printDeckList(QPrinter *printer, const DeckList *deckList);
+ static void printDeckList(QPrinter *printer, const DeckList &deckList);
- bool convertToCockatriceFormat(QString fileName);
+ bool convertToCockatriceFormat(const QString &fileName);
- DeckList *getDeckList()
+ LoadedDeck &getDeck()
{
- return deckList;
+ return loadedDeck;
+ }
+ const LoadedDeck &getDeck() const
+ {
+ return loadedDeck;
+ }
+ void setDeck(const LoadedDeck &deck)
+ {
+ loadedDeck = deck;
}
private:
static void printDeckListNode(QTextCursor *cursor, const InnerDecklistNode *node);
- static void saveToStream_DeckHeader(QTextStream &out, const DeckList *deckList);
+ static void saveToStream_DeckHeader(QTextStream &out, const DeckList &deckList);
static void saveToStream_DeckZone(QTextStream &out,
const InnerDecklistNode *zoneNode,
@@ -106,9 +102,6 @@ private:
QList cards,
bool addComments = true,
bool addSetNameAndNumber = true);
-
- [[nodiscard]] static QString getCardZoneFromName(const QString &cardName, QString currentZoneName);
- [[nodiscard]] static QString getCompleteCardName(const QString &cardName);
};
#endif
diff --git a/cockatrice/src/interface/deck_loader/loaded_deck.h b/cockatrice/src/interface/deck_loader/loaded_deck.h
index 90f3853cf..e07cd0db6 100644
--- a/cockatrice/src/interface/deck_loader/loaded_deck.h
+++ b/cockatrice/src/interface/deck_loader/loaded_deck.h
@@ -30,8 +30,8 @@ struct LoadedDeck
bool isEmpty() const;
};
- DeckList deckList; ///< The decklist itself
- LoadInfo lastLoadInfo; ///< info about where the deck was loaded from
+ DeckList deckList; ///< The decklist itself
+ LoadInfo lastLoadInfo = {}; ///< info about where the deck was loaded from
bool isEmpty() const;
};
diff --git a/cockatrice/src/interface/logger.cpp b/cockatrice/src/interface/logger.cpp
index 37e07acec..d6df065e8 100644
--- a/cockatrice/src/interface/logger.cpp
+++ b/cockatrice/src/interface/logger.cpp
@@ -57,17 +57,10 @@ void Logger::openLogfileSession()
return;
}
fileStream.setDevice(&fileHandle);
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
fileStream << "Log session started at " << QDateTime::currentDateTime().toString() << Qt::endl;
fileStream << getClientVersion() << Qt::endl;
fileStream << getSystemArchitecture() << Qt::endl;
fileStream << getClientInstallInfo() << Qt::endl;
-#else
- fileStream << "Log session started at " << QDateTime::currentDateTime().toString() << endl;
- fileStream << getClientVersion() << endl;
- fileStream << getSystemArchitecture() << endl;
- fileStream << getClientInstallInfo() << endl;
-#endif
logToFileEnabled = true;
}
@@ -77,11 +70,7 @@ void Logger::closeLogfileSession()
return;
logToFileEnabled = false;
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
fileStream << "Log session closed at " << QDateTime::currentDateTime().toString() << Qt::endl;
-#else
- fileStream << "Log session closed at " << QDateTime::currentDateTime().toString() << endl;
-#endif
fileHandle.close();
}
@@ -103,11 +92,7 @@ void Logger::internalLog(const QString &message)
std::cerr << message.toStdString() << std::endl; // Print to stdout
if (logToFileEnabled) {
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
fileStream << message << Qt::endl; // Print to fileStream
-#else
- fileStream << message << endl; // Print to fileStream
-#endif
}
}
diff --git a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
index cb83768f4..fdac9d0f0 100644
--- a/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
@@ -91,8 +91,9 @@ QWidget *CardGroupDisplayWidget::constructWidgetForIndex(QPersistentModelIndex i
if (indexToWidgetMap.contains(index)) {
return indexToWidgetMap[index];
}
- auto cardName = deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString();
- auto cardProviderId = deckListModel->data(index.sibling(index.row(), 4), Qt::EditRole).toString();
+ auto cardName = index.sibling(index.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
+ auto cardProviderId =
+ index.sibling(index.row(), DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::EditRole).toString();
auto widget = new CardInfoPictureWithTextOverlayWidget(getLayoutParent(), true);
widget->setScaleFactor(cardSizeWidget->getSlider()->value());
@@ -114,7 +115,7 @@ void CardGroupDisplayWidget::updateCardDisplays()
// This doesn't really matter since overwrite the whole lessThan function to just compare dynamically anyway.
proxy.setSortRole(Qt::EditRole);
- proxy.sort(1, Qt::AscendingOrder);
+ proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
// 1. trackedIndex is a source index → map it to proxy space
QModelIndex proxyParent = proxy.mapFromSource(trackedIndex);
diff --git a/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp b/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp
index 819bc57d0..f8b581e33 100644
--- a/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp
@@ -27,7 +27,7 @@ CardInfoDisplayWidget::CardInfoDisplayWidget(const CardRef &cardRef, QWidget *pa
layout->addWidget(text, 0, Qt::AlignCenter);
setLayout(layout);
- setFrameStyle(QFrame::Panel | QFrame::Raised);
+ setFrameStyle(static_cast(QFrame::Panel) | QFrame::Raised);
int pixmapHeight = QGuiApplication::primaryScreen()->geometry().height() / 3;
int pixmapWidth = static_cast(pixmapHeight / aspectRatio);
diff --git a/cockatrice/src/interface/widgets/cards/card_info_picture_widget.cpp b/cockatrice/src/interface/widgets/cards/card_info_picture_widget.cpp
index fc1b9ebe2..85d210e1a 100644
--- a/cockatrice/src/interface/widgets/cards/card_info_picture_widget.cpp
+++ b/cockatrice/src/interface/widgets/cards/card_info_picture_widget.cpp
@@ -39,10 +39,6 @@ CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool _hoverT
setMouseTracking(true);
}
- enlargedPixmapWidget = new CardInfoPictureEnlargedWidget(this->window());
- enlargedPixmapWidget->hide();
- connect(this, &QObject::destroyed, enlargedPixmapWidget, &CardInfoPictureEnlargedWidget::deleteLater);
-
hoverTimer = new QTimer(this);
hoverTimer->setSingleShot(true);
connect(hoverTimer, &QTimer::timeout, this, &CardInfoPictureWidget::showEnlargedPixmap);
@@ -277,7 +273,7 @@ void CardInfoPictureWidget::leaveEvent(QEvent *event)
if (hoverToZoomEnabled) {
hoverTimer->stop();
- enlargedPixmapWidget->hide();
+ destroyEnlargedPixmapWidget();
}
if (raiseOnEnter) {
@@ -294,7 +290,7 @@ void CardInfoPictureWidget::moveEvent(QMoveEvent *event)
QWidget::moveEvent(event);
hoverTimer->stop();
- enlargedPixmapWidget->hide();
+ destroyEnlargedPixmapWidget();
if (animation->state() == QAbstractAnimation::Running) {
return;
@@ -310,7 +306,7 @@ void CardInfoPictureWidget::mouseMoveEvent(QMouseEvent *event)
{
QWidget::mouseMoveEvent(event);
- if (hoverToZoomEnabled && enlargedPixmapWidget->isVisible()) {
+ if (hoverToZoomEnabled && enlargedPixmapWidget && enlargedPixmapWidget->isVisible()) {
const QPoint cursorPos = QCursor::pos();
const QRect screenGeometry = QGuiApplication::screenAt(cursorPos)->geometry();
const QSize widgetSize = enlargedPixmapWidget->size();
@@ -344,7 +340,7 @@ void CardInfoPictureWidget::mousePressEvent(QMouseEvent *event)
void CardInfoPictureWidget::hideEvent(QHideEvent *event)
{
- enlargedPixmapWidget->hide();
+ destroyEnlargedPixmapWidget();
QWidget::hideEvent(event);
}
@@ -444,12 +440,19 @@ QMenu *CardInfoPictureWidget::createAddToOpenDeckMenu()
* If card information is available, the enlarged pixmap is loaded, positioned near the cursor,
* and displayed.
*/
-void CardInfoPictureWidget::showEnlargedPixmap() const
+void CardInfoPictureWidget::showEnlargedPixmap()
{
if (!exactCard) {
return;
}
+ // Lazy creation of the enlarged widget
+ if (!enlargedPixmapWidget) {
+ enlargedPixmapWidget = new CardInfoPictureEnlargedWidget(const_cast(this)->window());
+ enlargedPixmapWidget->hide();
+ connect(this, &QObject::destroyed, enlargedPixmapWidget, &CardInfoPictureEnlargedWidget::deleteLater);
+ }
+
const QSize enlargedSize(static_cast(size().width() * 2), static_cast(size().width() * aspectRatio * 2));
enlargedPixmapWidget->setCardPixmap(exactCard, enlargedSize);
@@ -460,7 +463,6 @@ void CardInfoPictureWidget::showEnlargedPixmap() const
int newX = cursorPos.x() + enlargedPixmapOffset;
int newY = cursorPos.y() + enlargedPixmapOffset;
- // Adjust if out of bounds
if (newX + widgetSize.width() > screenGeometry.right()) {
newX = cursorPos.x() - widgetSize.width() - enlargedPixmapOffset;
}
@@ -472,3 +474,11 @@ void CardInfoPictureWidget::showEnlargedPixmap() const
enlargedPixmapWidget->show();
}
+
+void CardInfoPictureWidget::destroyEnlargedPixmapWidget()
+{
+ if (enlargedPixmapWidget) {
+ enlargedPixmapWidget->deleteLater();
+ enlargedPixmapWidget = nullptr;
+ }
+}
diff --git a/cockatrice/src/interface/widgets/cards/card_info_picture_widget.h b/cockatrice/src/interface/widgets/cards/card_info_picture_widget.h
index 980d700f6..c9dbc47ab 100644
--- a/cockatrice/src/interface/widgets/cards/card_info_picture_widget.h
+++ b/cockatrice/src/interface/widgets/cards/card_info_picture_widget.h
@@ -63,7 +63,8 @@ protected:
{
return resizedPixmap;
}
- void showEnlargedPixmap() const;
+ void showEnlargedPixmap();
+ void destroyEnlargedPixmapWidget();
private:
ExactCard exactCard;
diff --git a/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp b/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp
index 132964f13..4742467b1 100644
--- a/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/cards/deck_card_zone_display_widget.cpp
@@ -82,10 +82,11 @@ void DeckCardZoneDisplayWidget::cleanupInvalidCardGroup(CardGroupDisplayWidget *
void DeckCardZoneDisplayWidget::constructAppropriateWidget(QPersistentModelIndex index)
{
- auto categoryName = deckListModel->data(index.sibling(index.row(), 1), Qt::EditRole).toString();
if (indexToWidgetMap.contains(index)) {
return;
}
+
+ auto categoryName = index.sibling(index.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
if (displayType == DisplayType::Overlap) {
auto *displayWidget = new OverlappedCardGroupDisplayWidget(
cardGroupContainer, deckListModel, selectionModel, index, zoneName, categoryName, activeGroupCriteria,
@@ -120,7 +121,7 @@ void DeckCardZoneDisplayWidget::displayCards()
QSortFilterProxyModel proxy;
proxy.setSourceModel(deckListModel);
proxy.setSortRole(Qt::EditRole);
- proxy.sort(1, Qt::AscendingOrder);
+ proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
// 1. trackedIndex is a source index → map it to proxy space
QModelIndex proxyParent = proxy.mapFromSource(trackedIndex);
diff --git a/cockatrice/src/interface/widgets/cards/deck_preview_card_picture_widget.cpp b/cockatrice/src/interface/widgets/cards/deck_preview_card_picture_widget.cpp
index 486b3302d..b27fe274f 100644
--- a/cockatrice/src/interface/widgets/cards/deck_preview_card_picture_widget.cpp
+++ b/cockatrice/src/interface/widgets/cards/deck_preview_card_picture_widget.cpp
@@ -17,7 +17,6 @@
* @param outlineColor The color of the outline around the text.
* @param fontSize The font size of the overlay text.
* @param alignment The alignment of the text within the overlay.
- * @param _deckLoader The Deck Loader holding the Deck associated with this preview.
*
* Sets the widget's size policy and default border style.
*/
diff --git a/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp b/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp
index a5ddf37a1..3ca18d21c 100644
--- a/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp
+++ b/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp
@@ -21,32 +21,26 @@ void DeckListStatisticsAnalyzer::update()
manaCurveMap.clear();
manaDevotionMap.clear();
- auto nodes = model->getDeckList()->getCardNodes();
+ QList cards = model->getCards();
- for (auto *node : nodes) {
- CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(node->getName());
- if (!info)
- continue;
+ for (const ExactCard &card : cards) {
+ // ---- Mana curve ----
+ if (config.computeManaCurve) {
+ manaCurveMap[card.getInfo().getCmc().toInt()]++;
+ }
- for (int i = 0; i < node->getNumber(); ++i) {
- // ---- Mana curve ----
- if (config.computeManaCurve) {
- manaCurveMap[info->getCmc().toInt()]++;
- }
+ // ---- Mana base ----
+ if (config.computeManaBase) {
+ auto mana = determineManaProduction(card.getInfo().getText());
+ for (auto it = mana.begin(); it != mana.end(); ++it)
+ manaBaseMap[it.key()] += it.value();
+ }
- // ---- Mana base ----
- if (config.computeManaBase) {
- auto mana = determineManaProduction(info->getText());
- for (auto it = mana.begin(); it != mana.end(); ++it)
- manaBaseMap[it.key()] += it.value();
- }
-
- // ---- Devotion ----
- if (config.computeDevotion) {
- auto devo = countManaSymbols(info->getManaCost());
- for (auto &d : devo)
- manaDevotionMap[d.first] += d.second;
- }
+ // ---- Devotion ----
+ if (config.computeDevotion) {
+ auto devo = countManaSymbols(card.getInfo().getManaCost());
+ for (auto &d : devo)
+ manaDevotionMap[d.first] += d.second;
}
}
diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp
index b4b27e48a..f5549a98c 100644
--- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp
@@ -134,13 +134,13 @@ void DeckEditorDatabaseDisplayWidget::clearAllDatabaseFilters()
void DeckEditorDatabaseDisplayWidget::updateCard(const QModelIndex ¤t, const QModelIndex & /*previous*/)
{
- const QString cardName = current.sibling(current.row(), 0).data().toString();
-
if (!current.isValid()) {
return;
}
- if (!current.model()->hasChildren(current.sibling(current.row(), 0))) {
+ const QString cardName = current.siblingAtColumn(CardDatabaseModel::NameColumn).data().toString();
+
+ if (!current.model()->hasChildren(current.siblingAtColumn(CardDatabaseModel::NameColumn))) {
emit cardChanged(CardDatabaseManager::query()->getPreferredCard(cardName));
}
}
@@ -172,7 +172,7 @@ ExactCard DeckEditorDatabaseDisplayWidget::currentCard() const
return {};
}
- const QString cardName = currentIndex.sibling(currentIndex.row(), 0).data().toString();
+ const QString cardName = currentIndex.siblingAtColumn(CardDatabaseModel::NameColumn).data().toString();
return CardDatabaseManager::query()->getPreferredCard(cardName);
}
diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
index 268a13d5f..62195974b 100644
--- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
+++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
@@ -1,8 +1,8 @@
#include "deck_editor_deck_dock_widget.h"
#include "../../../client/settings/cache_settings.h"
-#include "../../deck_loader/deck_loader.h"
#include "deck_list_style_proxy.h"
+#include "deck_state_manager.h"
#include
#include
@@ -38,7 +38,7 @@ static int findRestoreIndex(const CardRef &wanted, const QComboBox *combo)
}
DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent)
- : QDockWidget(parent), deckEditor(parent)
+ : QDockWidget(parent), deckEditor(parent), deckStateManager(parent->deckStateManager)
{
setObjectName("deckDock");
@@ -52,19 +52,19 @@ DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent
void DeckEditorDeckDockWidget::createDeckDock()
{
- deckModel = new DeckListModel(this);
- deckModel->setObjectName("deckModel");
- connect(deckModel, &DeckListModel::deckHashChanged, this, &DeckEditorDeckDockWidget::updateHash);
-
- deckLoader = new DeckLoader(this, deckModel->getDeckList());
+ connect(getModel(), &DeckListModel::deckHashChanged, this, &DeckEditorDeckDockWidget::updateHash);
proxy = new DeckListStyleProxy(this);
- proxy->setSourceModel(deckModel);
+ proxy->setSourceModel(getModel());
- historyManagerWidget = new DeckListHistoryManagerWidget(deckModel, proxy, deckEditor->getHistoryManager(), this);
+ historyManagerWidget = new DeckListHistoryManagerWidget(deckStateManager, proxy, this);
connect(historyManagerWidget, &DeckListHistoryManagerWidget::requestDisplayWidgetSync, this,
&DeckEditorDeckDockWidget::syncDisplayWidgetsToModel);
+ connect(deckStateManager, &DeckStateManager::focusIndexChanged, this, &DeckEditorDeckDockWidget::setSelectedIndex);
+ connect(deckStateManager, &DeckStateManager::deckReplaced, this,
+ &DeckEditorDeckDockWidget::syncDisplayWidgetsToModel);
+
deckView = new QTreeView();
deckView->setObjectName("deckView");
deckView->setModel(proxy);
@@ -76,14 +76,14 @@ void DeckEditorDeckDockWidget::createDeckDock()
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
connect(deckView->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
&DeckEditorDeckDockWidget::updateCard);
- connect(deckView, &QTreeView::doubleClicked, this, &DeckEditorDeckDockWidget::actSwapCard);
+ connect(deckView, &QTreeView::doubleClicked, this, &DeckEditorDeckDockWidget::actSwapSelection);
deckView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(deckView, &QTreeView::customContextMenuRequested, this, &DeckEditorDeckDockWidget::decklistCustomMenu);
- connect(&deckViewKeySignals, &KeySignals::onShiftS, this, &DeckEditorDeckDockWidget::actSwapCard);
- connect(&deckViewKeySignals, &KeySignals::onEnter, this, &DeckEditorDeckDockWidget::actIncrement);
- connect(&deckViewKeySignals, &KeySignals::onCtrlAltEqual, this, &DeckEditorDeckDockWidget::actIncrement);
+ connect(&deckViewKeySignals, &KeySignals::onShiftS, this, &DeckEditorDeckDockWidget::actSwapSelection);
+ connect(&deckViewKeySignals, &KeySignals::onEnter, this, &DeckEditorDeckDockWidget::actIncrementSelection);
+ connect(&deckViewKeySignals, &KeySignals::onCtrlAltEqual, this, &DeckEditorDeckDockWidget::actIncrementSelection);
connect(&deckViewKeySignals, &KeySignals::onCtrlAltMinus, this, &DeckEditorDeckDockWidget::actDecrementSelection);
- connect(&deckViewKeySignals, &KeySignals::onShiftRight, this, &DeckEditorDeckDockWidget::actIncrement);
+ connect(&deckViewKeySignals, &KeySignals::onShiftRight, this, &DeckEditorDeckDockWidget::actIncrementSelection);
connect(&deckViewKeySignals, &KeySignals::onShiftLeft, this, &DeckEditorDeckDockWidget::actDecrementSelection);
connect(&deckViewKeySignals, &KeySignals::onDelete, this, &DeckEditorDeckDockWidget::actRemoveCard);
@@ -97,7 +97,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
nameDebounceTimer = new QTimer(this);
nameDebounceTimer->setSingleShot(true);
nameDebounceTimer->setInterval(300); // debounce duration in ms
- connect(nameDebounceTimer, &QTimer::timeout, this, [this]() { updateName(nameEdit->text()); });
+ connect(nameDebounceTimer, &QTimer::timeout, this, &DeckEditorDeckDockWidget::writeName);
connect(nameEdit, &LineEditUnfocusable::textChanged, this, [this]() {
nameDebounceTimer->start(); // restart debounce timer
@@ -141,7 +141,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
commentsDebounceTimer = new QTimer(this);
commentsDebounceTimer->setSingleShot(true);
commentsDebounceTimer->setInterval(400); // longer debounce for multi-line
- connect(commentsDebounceTimer, &QTimer::timeout, this, [this]() { updateComments(); });
+ connect(commentsDebounceTimer, &QTimer::timeout, this, &DeckEditorDeckDockWidget::writeComments);
connect(commentsEdit, &QTextEdit::textChanged, this, [this]() {
commentsDebounceTimer->start(); // restart debounce timer
@@ -152,18 +152,21 @@ void DeckEditorDeckDockWidget::createDeckDock()
bannerCardLabel->setText(tr("Banner Card"));
bannerCardLabel->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
bannerCardComboBox = new QComboBox(this);
- connect(deckModel, &DeckListModel::dataChanged, this, [this]() {
+ connect(getModel(), &DeckListModel::dataChanged, this, [this]() {
// Delay the update to avoid race conditions
QTimer::singleShot(100, this, &DeckEditorDeckDockWidget::updateBannerCardComboBox);
});
+ connect(getModel(), &DeckListModel::cardAddedAt, this, &DeckEditorDeckDockWidget::recursiveExpand);
+ connect(getModel(), &DeckListModel::modelReset, this, &DeckEditorDeckDockWidget::expandAll);
+
connect(bannerCardComboBox, QOverload::of(&QComboBox::currentIndexChanged), this,
- &DeckEditorDeckDockWidget::setBannerCard);
+ &DeckEditorDeckDockWidget::writeBannerCard);
bannerCardComboBox->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
- deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList()->getTags());
+ deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, {});
deckTagsDisplayWidget->setHidden(!SettingsCache::instance().getDeckEditorTagsWidgetVisible());
- connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, this,
- &DeckEditorDeckDockWidget::setTags);
+ connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, deckStateManager,
+ &DeckStateManager::setTags);
activeGroupCriteriaLabel = new QLabel(this);
@@ -172,16 +175,14 @@ void DeckEditorDeckDockWidget::createDeckDock()
activeGroupCriteriaComboBox->addItem(tr("Mana Cost"), DeckListModelGroupCriteria::MANA_COST);
activeGroupCriteriaComboBox->addItem(tr("Colors"), DeckListModelGroupCriteria::COLOR);
connect(activeGroupCriteriaComboBox, QOverload::of(&QComboBox::currentIndexChanged), [this]() {
- deckModel->setActiveGroupCriteria(static_cast(
+ getModel()->setActiveGroupCriteria(static_cast(
activeGroupCriteriaComboBox->currentData(Qt::UserRole).toInt()));
- deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
- deckView->expandAll();
- deckView->expandAll();
+ getModel()->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
});
aIncrement = new QAction(QString(), this);
aIncrement->setIcon(QPixmap("theme:icons/increment"));
- connect(aIncrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actIncrement);
+ connect(aIncrement, &QAction::triggered, this, &DeckEditorDeckDockWidget::actIncrementSelection);
auto *tbIncrement = new QToolButton(this);
tbIncrement->setDefaultAction(aIncrement);
@@ -199,7 +200,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
aSwapCard = new QAction(QString(), this);
aSwapCard->setIcon(QPixmap("theme:icons/swap"));
- connect(aSwapCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actSwapCard);
+ connect(aSwapCard, &QAction::triggered, this, &DeckEditorDeckDockWidget::actSwapSelection);
auto *tbSwapCard = new QToolButton(this);
tbSwapCard->setDefaultAction(aSwapCard);
@@ -283,21 +284,21 @@ void DeckEditorDeckDockWidget::createDeckDock()
void DeckEditorDeckDockWidget::initializeFormats()
{
- QMap allFormats = CardDatabaseManager::query()->getAllFormatsWithCount();
+ QStringList allFormats = CardDatabaseManager::query()->getAllFormatsWithCount().keys();
formatComboBox->clear(); // Remove "Loading Database..."
formatComboBox->setEnabled(true);
// Populate with formats
formatComboBox->addItem("", "");
- for (auto it = allFormats.constBegin(); it != allFormats.constEnd(); ++it) {
- QString displayText = QString("%1").arg(it.key());
- formatComboBox->addItem(displayText, it.key()); // store the raw key in itemData
+ for (auto formatName : allFormats) {
+ formatComboBox->addItem(formatName, formatName); // store the raw key in itemData
}
- if (!deckModel->getDeckList()->getGameFormat().isEmpty()) {
- deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat());
- formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat()));
+ QString format = deckStateManager->getMetadata().gameFormat;
+ if (!format.isEmpty()) {
+ getModel()->setActiveFormat(format);
+ formatComboBox->setCurrentIndex(formatComboBox->findData(format));
} else {
// Ensure no selection is visible initially
formatComboBox->setCurrentIndex(-1);
@@ -306,11 +307,10 @@ void DeckEditorDeckDockWidget::initializeFormats()
connect(formatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) {
if (index >= 0) {
QString formatKey = formatComboBox->itemData(index).toString();
- deckModel->setActiveFormat(formatKey);
+ deckStateManager->setFormat(formatKey);
} else {
- deckModel->setActiveFormat(QString()); // clear format if deselected
+ deckStateManager->setFormat(""); // clear format if deselected
}
- emit deckModified();
});
}
@@ -319,17 +319,17 @@ ExactCard DeckEditorDeckDockWidget::getCurrentCard()
QModelIndex current = deckView->selectionModel()->currentIndex();
if (!current.isValid())
return {};
- const QString cardName = current.sibling(current.row(), 1).data().toString();
- const QString cardProviderID = current.sibling(current.row(), 4).data().toString();
+ const QString cardName = current.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString();
+ const QString cardProviderID = current.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data().toString();
const QModelIndex gparent = current.parent().parent();
if (!gparent.isValid()) {
return {};
}
- const QString zoneName = gparent.sibling(gparent.row(), 1).data(Qt::EditRole).toString();
+ const QString zoneName = gparent.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
- if (!current.model()->hasChildren(current.sibling(current.row(), 0))) {
+ if (!current.model()->hasChildren(current.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT))) {
if (ExactCard selectedCard = CardDatabaseManager::query()->getCard({cardName, cardProviderID})) {
return selectedCard;
}
@@ -340,43 +340,37 @@ ExactCard DeckEditorDeckDockWidget::getCurrentCard()
void DeckEditorDeckDockWidget::updateCard(const QModelIndex /*¤t*/, const QModelIndex & /*previous*/)
{
if (ExactCard card = getCurrentCard()) {
- emit cardChanged(card);
+ emit selectedCardChanged(card);
}
}
-void DeckEditorDeckDockWidget::updateName(const QString &name)
+/**
+ * @brief Writes the contents of the name textBox to the DeckStateManager
+ */
+void DeckEditorDeckDockWidget::writeName()
{
- emit requestDeckHistorySave(
- QString(tr("Rename deck to \"%1\" from \"%2\"")).arg(name).arg(deckLoader->getDeckList()->getName()));
- deckModel->getDeckList()->setName(name);
- deckEditor->setModified(name.isEmpty());
- emit nameChanged();
- emit deckModified();
+ QString name = nameEdit->text();
+ deckStateManager->setName(name);
}
-void DeckEditorDeckDockWidget::updateComments()
+/**
+ * @brief Writes the contents of the comments textBox to the DeckStateManager
+ */
+void DeckEditorDeckDockWidget::writeComments()
{
- emit requestDeckHistorySave(tr("Updated comments (was %1 chars, now %2 chars)")
- .arg(deckLoader->getDeckList()->getComments().size())
- .arg(commentsEdit->toPlainText().size()));
-
- deckModel->getDeckList()->setComments(commentsEdit->toPlainText());
- deckEditor->setModified(commentsEdit->toPlainText().isEmpty());
- emit commentsChanged();
- emit deckModified();
+ QString comments = commentsEdit->toPlainText();
+ deckStateManager->setComments(comments);
}
void DeckEditorDeckDockWidget::updateHash()
{
- hashLabel->setText(deckModel->getDeckList()->getDeckHash());
- emit hashChanged();
- emit deckModified();
+ hashLabel->setText(deckStateManager->getDeckHash());
}
void DeckEditorDeckDockWidget::updateBannerCardComboBox()
{
// Store current banner card identity
- CardRef wanted = deckModel->getDeckList()->getBannerCard();
+ CardRef wanted = deckStateManager->getMetadata().bannerCard;
// Block signals temporarily
bool wasBlocked = bannerCardComboBox->blockSignals(true);
@@ -386,15 +380,15 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
// Collect unique (name, providerId) pairs
QSet> bannerCardSet;
- QList cardsInDeck = deckModel->getDeckList()->getCardNodes();
+ QList cardsInDeck = getModel()->getCardRefs();
- for (auto currentCard : cardsInDeck) {
- if (!CardDatabaseManager::query()->getCard(currentCard->toCardRef())) {
+ for (auto cardRef : cardsInDeck) {
+ if (!CardDatabaseManager::query()->getCard(cardRef)) {
continue;
}
// Insert one entry per distinct card, ignore copies
- bannerCardSet.insert({currentCard->getName(), currentCard->getCardProviderId()});
+ bannerCardSet.insert({cardRef.name, cardRef.providerId});
}
// Convert to sorted list
@@ -415,7 +409,6 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
// Handle results
if (restoreIndex != -1) {
bannerCardComboBox->setCurrentIndex(restoreIndex);
- syncDeckListBannerCardWithComboBox();
} else {
// Add a placeholder "-" and set it as the current selection
bannerCardComboBox->insertItem(0, "-");
@@ -426,25 +419,14 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
bannerCardComboBox->blockSignals(wasBlocked);
}
-void DeckEditorDeckDockWidget::setBannerCard(int /* changedIndex */)
+/**
+ * @brief Writes the selected bannerCard to the DeckStateManager
+ */
+void DeckEditorDeckDockWidget::writeBannerCard(int index)
{
- emit requestDeckHistorySave(tr("Banner card changed"));
- syncDeckListBannerCardWithComboBox();
- deckEditor->setModified(true);
- emit deckModified();
-}
-
-void DeckEditorDeckDockWidget::setTags(const QStringList &tags)
-{
- deckModel->getDeckList()->setTags(tags);
- deckEditor->setModified(true);
- emit deckModified();
-}
-
-void DeckEditorDeckDockWidget::syncDeckListBannerCardWithComboBox()
-{
- auto [name, id] = bannerCardComboBox->currentData().value>();
- deckModel->getDeckList()->setBannerCard({name, id});
+ auto [name, id] = bannerCardComboBox->itemData(index).value>();
+ CardRef bannerCard = {name, id};
+ deckStateManager->setBannerCard(bannerCard);
}
void DeckEditorDeckDockWidget::updateShowBannerCardComboBox(const bool visible)
@@ -460,7 +442,7 @@ void DeckEditorDeckDockWidget::updateShowTagsWidget(const bool visible)
void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck()
{
- if (deckModel->getDeckList()->getBannerCard().name == "") {
+ if (deckStateManager->getMetadata().bannerCard.name == "") {
if (bannerCardComboBox->findText("-") != -1) {
bannerCardComboBox->setCurrentIndex(bannerCardComboBox->findText("-"));
} else {
@@ -468,37 +450,26 @@ void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck()
bannerCardComboBox->setCurrentIndex(0);
}
} else {
- bannerCardComboBox->setCurrentText(deckModel->getDeckList()->getBannerCard().name);
+ bannerCardComboBox->setCurrentText(deckStateManager->getMetadata().bannerCard.name);
}
}
-/**
- * Sets the currently active deck for this tab
- * @param _deck The deck. Takes ownership of the object
- */
-void DeckEditorDeckDockWidget::setDeck(DeckLoader *_deck)
+void DeckEditorDeckDockWidget::setSelectedIndex(const QModelIndex &newCardIndex)
{
- deckLoader = _deck;
- deckLoader->setParent(this);
- deckModel->setDeckList(deckLoader->getDeckList());
- connect(deckLoader, &DeckLoader::deckLoaded, deckModel, &DeckListModel::rebuildTree);
-
- emit requestDeckHistoryClear();
- historyManagerWidget->setDeckListModel(deckModel);
-
- syncDisplayWidgetsToModel();
-
- emit deckChanged();
+ deckView->clearSelection();
+ deckView->setCurrentIndex(newCardIndex);
+ recursiveExpand(newCardIndex);
+ deckView->setFocus(Qt::FocusReason::MouseFocusReason);
}
void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
{
nameEdit->blockSignals(true);
- nameEdit->setText(deckModel->getDeckList()->getName());
+ nameEdit->setText(deckStateManager->getMetadata().name);
nameEdit->blockSignals(false);
commentsEdit->blockSignals(true);
- commentsEdit->setText(deckModel->getDeckList()->getComments());
+ commentsEdit->setText(deckStateManager->getMetadata().comments);
commentsEdit->blockSignals(false);
bannerCardComboBox->blockSignals(true);
@@ -507,61 +478,100 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
bannerCardComboBox->blockSignals(false);
updateHash();
sortDeckModelToDeckView();
- expandAll();
- deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags());
+ deckTagsDisplayWidget->setTags(deckStateManager->getMetadata().tags);
}
void DeckEditorDeckDockWidget::sortDeckModelToDeckView()
{
- deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
- deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat());
- formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat()));
- deckView->expandAll();
- deckView->expandAll();
-
- emit deckChanged();
-}
-
-DeckLoader *DeckEditorDeckDockWidget::getDeckLoader()
-{
- return deckLoader;
-}
-
-DeckList *DeckEditorDeckDockWidget::getDeckList()
-{
- return deckModel->getDeckList();
+ getModel()->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
+ getModel()->setActiveFormat(deckStateManager->getMetadata().gameFormat);
+ formatComboBox->setCurrentIndex(formatComboBox->findData(deckStateManager->getMetadata().gameFormat));
}
/**
- * Resets the tab to the state for a blank new tab.
+ * @brief Convenience method to get the underlying model instance from the DeckStateManager
*/
-void DeckEditorDeckDockWidget::cleanDeck()
+DeckListModel *DeckEditorDeckDockWidget::getModel() const
{
- deckModel->cleanList();
- nameEdit->setText(QString());
- emit nameChanged();
- commentsEdit->setText(QString());
- emit commentsChanged();
- hashLabel->setText(QString());
- emit hashChanged();
- emit deckModified();
- emit deckChanged();
- updateBannerCardComboBox();
- deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags());
+ return deckStateManager->getModel();
}
-void DeckEditorDeckDockWidget::recursiveExpand(const QModelIndex &index)
+void DeckEditorDeckDockWidget::selectPrevCard()
{
- if (index.parent().isValid())
- recursiveExpand(index.parent());
- deckView->expand(index);
+ changeSelectedCard(-1);
}
+void DeckEditorDeckDockWidget::selectNextCard()
+{
+ changeSelectedCard(1);
+}
+
+/**
+ * @brief Selects a card based on the change direction.
+ *
+ * @param changeBy The direction to change, -1 for previous, 1 for next.
+ */
+void DeckEditorDeckDockWidget::changeSelectedCard(int changeBy)
+{
+ if (changeBy == 0) {
+ return;
+ }
+
+ // Get the current index of the selected item
+ auto deckViewCurrentIndex = deckView->currentIndex();
+
+ // For some reason, if the deckModel is modified but the view is not manually reselected,
+ // currentIndex will return an index for the underlying deckModel instead of the proxy.
+ // That index will return an invalid index when indexBelow/indexAbove crosses a header node,
+ // causing the selection to fail to move down.
+ /// \todo Figure out why it's happening so we can do a proper fix instead of a hacky workaround
+ if (deckViewCurrentIndex.model() == proxy->sourceModel()) {
+ deckViewCurrentIndex = proxy->mapFromSource(deckViewCurrentIndex);
+ }
+
+ auto nextIndex = deckViewCurrentIndex.siblingAtRow(deckViewCurrentIndex.row() + changeBy);
+ if (!nextIndex.isValid()) {
+ nextIndex = deckViewCurrentIndex;
+
+ // Increment to the next valid index, skipping header rows
+ AbstractDecklistNode *node;
+ do {
+ if (changeBy > 0) {
+ nextIndex = deckView->indexBelow(nextIndex);
+ } else {
+ nextIndex = deckView->indexAbove(nextIndex);
+ }
+ node = static_cast(nextIndex.internalPointer());
+ } while (node && node->isDeckHeader());
+ }
+
+ if (nextIndex.isValid()) {
+ deckView->setCurrentIndex(nextIndex);
+ deckView->setFocus(Qt::FocusReason::MouseFocusReason);
+ }
+}
+
+/**
+ * @brief Expands all parents of the given index.
+ * @param sourceIndex The index to expand (model source index)
+ */
+void DeckEditorDeckDockWidget::recursiveExpand(const QModelIndex &sourceIndex)
+{
+ auto index = proxy->mapFromSource(sourceIndex);
+
+ while (index.parent().isValid()) {
+ index = index.parent();
+ deckView->expand(index);
+ }
+}
+
+/**
+ * @brief Fully expands all levels of the deck view
+ */
void DeckEditorDeckDockWidget::expandAll()
{
- deckView->expandAll();
- deckView->expandAll();
+ deckView->expandRecursively(deckView->rootIndex());
}
/**
@@ -575,7 +585,7 @@ QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodes() const
auto selectedRows = deckView->selectionModel()->selectedRows();
const auto notLeafNode = [this](const QModelIndex &index) {
- return deckModel->hasChildren(proxy->mapToSource(index));
+ return getModel()->hasChildren(proxy->mapToSource(index));
};
selectedRows.erase(std::remove_if(selectedRows.begin(), selectedRows.end(), notLeafNode), selectedRows.end());
@@ -583,16 +593,39 @@ QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodes() const
return selectedRows;
}
-void DeckEditorDeckDockWidget::actIncrement()
+void DeckEditorDeckDockWidget::actAddCard(const ExactCard &card, const QString &_zoneName)
+{
+ if (!card) {
+ return;
+ }
+
+ QString zoneName = card.getInfo().getIsToken() ? DECK_ZONE_TOKENS : _zoneName;
+ deckStateManager->addCard(card, zoneName);
+}
+
+void DeckEditorDeckDockWidget::actIncrementSelection()
{
auto selectedRows = getSelectedCardNodes();
for (const auto &index : selectedRows) {
- offsetCountAtIndex(index, 1);
+ offsetCountAtIndex(index, true);
}
}
-void DeckEditorDeckDockWidget::actSwapCard()
+void DeckEditorDeckDockWidget::actSwapCard(const ExactCard &card, const QString &zoneName)
+{
+ QString providerId = card.getPrinting().getUuid();
+ QString collectorNumber = card.getPrinting().getProperty("num");
+
+ QModelIndex foundCard = getModel()->findCard(card.getName(), zoneName, providerId, collectorNumber);
+ if (!foundCard.isValid()) {
+ foundCard = getModel()->findCard(card.getName(), zoneName);
+ }
+
+ deckStateManager->swapCardAtIndex(foundCard);
+}
+
+void DeckEditorDeckDockWidget::actSwapSelection()
{
auto selectedRows = getSelectedCardNodes();
@@ -602,52 +635,15 @@ void DeckEditorDeckDockWidget::actSwapCard()
deckView->setSelectionMode(QAbstractItemView::SingleSelection);
}
- bool isModified = false;
for (const auto ¤tIndex : selectedRows) {
- if (swapCard(currentIndex)) {
- isModified = true;
- }
+ deckStateManager->swapCardAtIndex(currentIndex);
}
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
- if (isModified) {
- emit deckModified();
- }
-
update();
}
-/**
- * Swaps the card at the index between the maindeck and sideboard
- *
- * @param currentIndex The index to swap.
- * @return True if the swap was successful
- */
-bool DeckEditorDeckDockWidget::swapCard(const QModelIndex ¤tIndex)
-{
- if (!currentIndex.isValid())
- return false;
- const QString cardName = currentIndex.sibling(currentIndex.row(), 1).data().toString();
- const QString cardProviderID = currentIndex.sibling(currentIndex.row(), 4).data().toString();
- const QModelIndex gparent = currentIndex.parent().parent();
-
- if (!gparent.isValid())
- return false;
-
- const QString zoneName = gparent.sibling(gparent.row(), 1).data(Qt::EditRole).toString();
- offsetCountAtIndex(currentIndex, -1);
- const QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN;
-
- ExactCard card = CardDatabaseManager::query()->getCard({cardName, cardProviderID});
- QModelIndex newCardIndex = card ? deckModel->addCard(card, otherZoneName)
- // Third argument (true) says create the card no matter what, even if not in DB
- : deckModel->addPreferredPrintingCard(cardName, otherZoneName, true);
- recursiveExpand(proxy->mapToSource(newCardIndex));
-
- return true;
-}
-
void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString zoneName)
{
if (!card)
@@ -655,17 +651,7 @@ void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString z
if (card.getInfo().getIsToken())
zoneName = DECK_ZONE_TOKENS;
- QString providerId = card.getPrinting().getUuid();
- QString collectorNumber = card.getPrinting().getProperty("num");
-
- QModelIndex idx = deckModel->findCard(card.getName(), zoneName, providerId, collectorNumber);
- if (!idx.isValid()) {
- return;
- }
-
- deckView->clearSelection();
- deckView->setCurrentIndex(proxy->mapToSource(idx));
- offsetCountAtIndex(idx, -1);
+ deckStateManager->decrementCard(card, zoneName);
}
void DeckEditorDeckDockWidget::actDecrementSelection()
@@ -679,7 +665,7 @@ void DeckEditorDeckDockWidget::actDecrementSelection()
}
for (const auto &index : selectedRows) {
- offsetCountAtIndex(index, -1);
+ offsetCountAtIndex(index, false);
}
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
@@ -695,58 +681,31 @@ void DeckEditorDeckDockWidget::actRemoveCard()
deckView->setSelectionMode(QAbstractItemView::SingleSelection);
}
- bool isModified = false;
- for (const auto &index : selectedRows) {
- if (!index.isValid() || deckModel->hasChildren(index)) {
- continue;
- }
- QModelIndex sourceIndex = proxy->mapToSource(index);
- QString cardName = sourceIndex.sibling(sourceIndex.row(), 1).data().toString();
-
- emit requestDeckHistorySave(QString(tr("Removed \"%1\" (all copies)")).arg(cardName));
-
- deckModel->removeRow(sourceIndex.row(), sourceIndex.parent());
- isModified = true;
+ for (const auto &row : selectedRows) {
+ deckStateManager->removeCardAtIndex(row);
}
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
-
- if (isModified) {
- emit deckModified();
- }
}
-void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, int offset)
+/**
+ * @brief Increments or decrements the amount of the card node at the index by 1.
+ * @param idx The proxy index
+ * @param isIncrement If true, increments the count. If false, decrements the count
+ */
+void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, bool isIncrement)
{
- if (!idx.isValid() || deckModel->hasChildren(idx)) {
+ if (!idx.isValid() || getModel()->hasChildren(idx)) {
return;
}
QModelIndex sourceIndex = proxy->mapToSource(idx);
- const QModelIndex numberIndex = sourceIndex.sibling(sourceIndex.row(), 0);
- const QModelIndex nameIndex = sourceIndex.sibling(sourceIndex.row(), 1);
-
- const QString cardName = deckModel->data(nameIndex, Qt::EditRole).toString();
- const int count = deckModel->data(numberIndex, Qt::EditRole).toInt();
- const int new_count = count + offset;
-
- const auto reason =
- QString(tr("%1 %2 × \"%3\" (%4)"))
- .arg(offset > 0 ? tr("Added") : tr("Removed"))
- .arg(qAbs(offset))
- .arg(cardName)
- .arg(deckModel->data(sourceIndex.sibling(sourceIndex.row(), 4), Qt::DisplayRole).toString());
-
- emit requestDeckHistorySave(reason);
-
- if (new_count <= 0) {
- deckModel->removeRow(sourceIndex.row(), sourceIndex.parent());
+ if (isIncrement) {
+ deckStateManager->incrementCountAtIndex(sourceIndex);
} else {
- deckModel->setData(numberIndex, new_count, Qt::EditRole);
+ deckStateManager->decrementCountAtIndex(sourceIndex);
}
-
- emit deckModified();
}
void DeckEditorDeckDockWidget::decklistCustomMenu(QPoint point)
diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h
index 08aa38e5e..1a82b00d1 100644
--- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h
+++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.h
@@ -28,22 +28,14 @@ class DeckEditorDeckDockWidget : public QDockWidget
Q_OBJECT
public:
explicit DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent);
- DeckLoader *deckLoader;
+
DeckListStyleProxy *proxy;
- DeckListModel *deckModel;
QTreeView *deckView;
QComboBox *bannerCardComboBox;
void createDeckDock();
ExactCard getCurrentCard();
void retranslateUi();
- QString getDeckName()
- {
- return nameEdit->text();
- }
- QString getSimpleDeckName()
- {
- return nameEdit->text().simplified();
- }
+
QComboBox *getGroupByComboBox()
{
return activeGroupCriteriaComboBox;
@@ -55,35 +47,27 @@ public:
}
public slots:
- void cleanDeck();
+ void selectPrevCard();
+ void selectNextCard();
void updateBannerCardComboBox();
- void setDeck(DeckLoader *_deck);
void syncDisplayWidgetsToModel();
void sortDeckModelToDeckView();
- DeckLoader *getDeckLoader();
- DeckList *getDeckList();
- void actIncrement();
- bool swapCard(const QModelIndex &idx);
+ void actAddCard(const ExactCard &card, const QString &zoneName);
+ void actIncrementSelection();
void actDecrementCard(const ExactCard &card, QString zoneName);
void actDecrementSelection();
- void actSwapCard();
+ void actSwapCard(const ExactCard &card, const QString &zoneName);
+ void actSwapSelection();
void actRemoveCard();
- void offsetCountAtIndex(const QModelIndex &idx, int offset);
void initializeFormats();
- void expandAll();
signals:
- void nameChanged();
- void commentsChanged();
- void hashChanged();
- void deckChanged();
- void deckModified();
- void requestDeckHistorySave(const QString &modificationReason);
- void requestDeckHistoryClear();
- void cardChanged(const ExactCard &_card);
+ void selectedCardChanged(const ExactCard &card);
private:
AbstractTabDeckEditor *deckEditor;
+ DeckStateManager *deckStateManager;
+
DeckListHistoryManagerWidget *historyManagerWidget;
KeySignals deckViewKeySignals;
QLabel *nameLabel;
@@ -106,22 +90,25 @@ private:
QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard;
- void recursiveExpand(const QModelIndex &index);
+ DeckListModel *getModel() const;
[[nodiscard]] QModelIndexList getSelectedCardNodes() const;
+ void offsetCountAtIndex(const QModelIndex &idx, bool isIncrement);
private slots:
void decklistCustomMenu(QPoint point);
void updateCard(QModelIndex, const QModelIndex ¤t);
- void updateName(const QString &name);
- void updateComments();
- void setBannerCard(int);
- void setTags(const QStringList &tags);
- void syncDeckListBannerCardWithComboBox();
+ void writeName();
+ void writeComments();
+ void writeBannerCard(int);
+ void setSelectedIndex(const QModelIndex &newCardIndex);
void updateHash();
void refreshShortcuts();
void updateShowBannerCardComboBox(bool visible);
void updateShowTagsWidget(bool visible);
void syncBannerCardComboBoxSelectionWithDeck();
+ void changeSelectedCard(int changeBy);
+ void recursiveExpand(const QModelIndex &parent);
+ void expandAll();
};
#endif // DECK_EDITOR_DECK_DOCK_WIDGET_H
diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
index 0c068e043..03760c22d 100644
--- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
+++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
@@ -33,6 +33,10 @@ void DeckEditorPrintingSelectorDockWidget::createPrintingSelectorDock()
installEventFilter(deckEditor);
connect(this, &QDockWidget::topLevelChanged, deckEditor, &AbstractTabDeckEditor::dockTopLevelChanged);
+ connect(printingSelector, &PrintingSelector::prevCardRequested, deckEditor->getDeckDockWidget(),
+ &DeckEditorDeckDockWidget::selectPrevCard);
+ connect(printingSelector, &PrintingSelector::nextCardRequested, deckEditor->getDeckDockWidget(),
+ &DeckEditorDeckDockWidget::selectNextCard);
}
void DeckEditorPrintingSelectorDockWidget::retranslateUi()
diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp
index c68934ea6..d2d748753 100644
--- a/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp
+++ b/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp
@@ -1,10 +1,11 @@
#include "deck_list_history_manager_widget.h"
-DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckListModel *_deckListModel,
+#include "deck_state_manager.h"
+
+DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_deckStateManager,
DeckListStyleProxy *_styleProxy,
- DeckListHistoryManager *manager,
QWidget *parent)
- : QWidget(parent), deckListModel(_deckListModel), styleProxy(_styleProxy), historyManager(manager)
+ : QWidget(parent), deckStateManager(_deckStateManager), styleProxy(_styleProxy)
{
layout = new QHBoxLayout(this);
@@ -43,8 +44,7 @@ DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckListModel *_deckL
connect(historyList, &QListWidget::itemClicked, this, &DeckListHistoryManagerWidget::onListClicked);
- connect(historyManager, &DeckListHistoryManager::undoRedoStateChanged, this,
- &DeckListHistoryManagerWidget::refreshList);
+ connect(deckStateManager, &DeckStateManager::historyChanged, this, &DeckListHistoryManagerWidget::refreshList);
refreshList();
retranslateUi();
@@ -58,15 +58,12 @@ void DeckListHistoryManagerWidget::retranslateUi()
historyLabel->setText(tr("Click on an entry to revert to that point in the history."));
}
-void DeckListHistoryManagerWidget::setDeckListModel(DeckListModel *_deckListModel)
-{
- deckListModel = _deckListModel;
-}
-
void DeckListHistoryManagerWidget::refreshList()
{
historyList->clear();
+ DeckListHistoryManager *historyManager = deckStateManager->getHistoryManager();
+
// Fill redo section first (oldest redo at top, newest redo closest to divider)
const auto redoStack = historyManager->getRedoStack();
for (int i = 0; i < redoStack.size(); ++i) { // iterate forward
@@ -98,36 +95,7 @@ void DeckListHistoryManagerWidget::refreshList()
redoButton->setEnabled(historyManager->canRedo());
}
-void DeckListHistoryManagerWidget::doUndo()
-{
- if (!historyManager->canUndo()) {
- return;
- }
-
- historyManager->undo(deckListModel->getDeckList());
- deckListModel->rebuildTree();
- emit deckListModel->layoutChanged();
- emit requestDisplayWidgetSync();
-
- refreshList();
-}
-
-void DeckListHistoryManagerWidget::doRedo()
-{
- if (!historyManager->canRedo()) {
- return;
- }
-
- historyManager->redo(deckListModel->getDeckList());
- deckListModel->rebuildTree();
-
- emit deckListModel->layoutChanged();
- emit requestDisplayWidgetSync();
-
- refreshList();
-}
-
-void DeckListHistoryManagerWidget::onListClicked(QListWidgetItem *item)
+void DeckListHistoryManagerWidget::onListClicked(const QListWidgetItem *item)
{
// Ignore non-selectable items (like divider)
if (!(item->flags() & Qt::ItemIsSelectable)) {
@@ -138,23 +106,24 @@ void DeckListHistoryManagerWidget::onListClicked(QListWidgetItem *item)
int index = item->data(Qt::UserRole + 1).toInt();
if (mode == "redo") {
- const auto redoStack = historyManager->getRedoStack();
+ const auto redoStack = deckStateManager->getHistoryManager()->getRedoStack();
int steps = redoStack.size() - index;
- for (int i = 0; i < steps; ++i) {
- historyManager->redo(deckListModel->getDeckList());
- }
+ deckStateManager->redo(steps);
} else if (mode == "undo") {
- const auto undoStack = historyManager->getUndoStack();
- int steps = undoStack.size() - 1 - index;
- for (int i = 0; i < steps + 1; ++i) {
- historyManager->undo(deckListModel->getDeckList());
- }
+ const auto undoStack = deckStateManager->getHistoryManager()->getUndoStack();
+ int steps = undoStack.size() - index;
+ deckStateManager->undo(steps);
}
- deckListModel->rebuildTree();
-
- emit deckListModel->layoutChanged();
- emit requestDisplayWidgetSync();
-
refreshList();
+}
+
+void DeckListHistoryManagerWidget::doUndo()
+{
+ deckStateManager->undo();
+}
+
+void DeckListHistoryManagerWidget::doRedo()
+{
+ deckStateManager->redo();
}
\ No newline at end of file
diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.h b/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.h
index 0e208ad2b..ab53912e2 100644
--- a/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.h
+++ b/cockatrice/src/interface/widgets/deck_editor/deck_list_history_manager_widget.h
@@ -14,6 +14,8 @@
#include
#include
+class DeckStateManager;
+
class DeckListHistoryManagerWidget : public QWidget
{
Q_OBJECT
@@ -25,22 +27,19 @@ public slots:
void retranslateUi();
public:
- explicit DeckListHistoryManagerWidget(DeckListModel *deckListModel,
+ explicit DeckListHistoryManagerWidget(DeckStateManager *deckStateManager,
DeckListStyleProxy *styleProxy,
- DeckListHistoryManager *manager,
QWidget *parent = nullptr);
- void setDeckListModel(DeckListModel *_deckListModel);
private slots:
void refreshList();
- void onListClicked(QListWidgetItem *item);
+ void onListClicked(const QListWidgetItem *item);
void doUndo();
void doRedo();
private:
- DeckListModel *deckListModel;
+ DeckStateManager *deckStateManager;
DeckListStyleProxy *styleProxy;
- DeckListHistoryManager *historyManager;
QHBoxLayout *layout;
QAction *aUndo;
diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp
new file mode 100644
index 000000000..628d33d51
--- /dev/null
+++ b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp
@@ -0,0 +1,361 @@
+#include "deck_state_manager.h"
+
+#include
+#include
+
+DeckStateManager::DeckStateManager(QObject *parent)
+ : QObject(parent), deckList(QSharedPointer(new DeckList)),
+ deckListModel(new DeckListModel(this, deckList)), historyManager(new DeckListHistoryManager(this))
+{
+ connect(historyManager, &DeckListHistoryManager::undoRedoStateChanged, this, [this] {
+ setModified(true);
+ emit historyChanged();
+ });
+ connect(deckListModel, &DeckListModel::rowsInserted, this, &DeckStateManager::uniqueCardsChanged);
+ connect(deckListModel, &DeckListModel::rowsRemoved, this, &DeckStateManager::uniqueCardsChanged);
+}
+
+const DeckList &DeckStateManager::getDeckList() const
+{
+ return *deckList.get();
+}
+
+LoadedDeck DeckStateManager::toLoadedDeck() const
+{
+ return {getDeckList(), lastLoadInfo};
+}
+
+DeckList::Metadata const &DeckStateManager::getMetadata() const
+{
+ return deckList->getMetadata();
+}
+
+QString DeckStateManager::getSimpleDeckName() const
+{
+ return deckList->getMetadata().name.simplified();
+}
+
+QString DeckStateManager::getDeckHash() const
+{
+ return deckList->getDeckHash();
+}
+
+bool DeckStateManager::isModified() const
+{
+ return modified;
+}
+
+void DeckStateManager::setModified(bool state)
+{
+ if (state == modified) {
+ return;
+ }
+
+ modified = state;
+ emit isModifiedChanged(modified);
+}
+
+bool DeckStateManager::isBlankNewDeck() const
+{
+ return !isModified() && deckList->isBlankDeck();
+}
+
+void DeckStateManager::replaceDeck(const LoadedDeck &deck)
+{
+ lastLoadInfo = deck.lastLoadInfo;
+ deckList = QSharedPointer(new DeckList(deck.deckList));
+ deckListModel->setDeckList(deckList);
+
+ historyManager->clear();
+
+ setModified(false);
+ emit deckReplaced();
+}
+
+void DeckStateManager::clearDeck()
+{
+ replaceDeck(LoadedDeck());
+}
+
+bool DeckStateManager::modifyDeck(const QString &reason, const std::function &operation)
+{
+ DeckListMemento memento = deckList->createMemento(reason);
+ bool success = operation(deckListModel);
+
+ if (success) {
+ historyManager->save(memento);
+ doCardModified();
+ }
+
+ return success;
+}
+
+QModelIndex DeckStateManager::modifyDeck(const QString &reason,
+ const std::function &operation)
+{
+ DeckListMemento memento = deckList->createMemento(reason);
+ QModelIndex idx = operation(deckListModel);
+
+ if (idx.isValid()) {
+ historyManager->save(memento);
+ doCardModified();
+ }
+
+ return idx;
+}
+
+void DeckStateManager::setName(const QString &name)
+{
+ QString previous = deckList->getName();
+ if (previous == name) {
+ return;
+ }
+
+ requestHistorySave(tr("Rename deck to \"%1\" from \"%2\"").arg(name).arg(previous));
+ deckList->setName(name);
+
+ doMetadataModified();
+}
+
+void DeckStateManager::setComments(const QString &comments)
+{
+ QString previous = deckList->getComments();
+ if (previous == comments) {
+ return;
+ }
+
+ requestHistorySave(tr("Updated comments (was %1 chars, now %2 chars)").arg(previous.size()).arg(comments.size()));
+ deckList->setComments(comments);
+
+ doMetadataModified();
+}
+
+void DeckStateManager::setBannerCard(const CardRef &bannerCard)
+{
+ CardRef previous = deckList->getBannerCard();
+ if (previous == bannerCard) {
+ return;
+ }
+
+ requestHistorySave(tr("Set banner card to %1 (%2)").arg(bannerCard.name).arg(bannerCard.providerId));
+ deckList->setBannerCard(bannerCard);
+
+ doMetadataModified();
+}
+
+void DeckStateManager::setTags(const QStringList &tags)
+{
+ QStringList previous = deckList->getTags();
+ if (previous == tags) {
+ return;
+ }
+
+ requestHistorySave(tr("Tags changed"));
+ deckList->setTags(tags);
+
+ doMetadataModified();
+}
+
+void DeckStateManager::setFormat(const QString &format)
+{
+ if (deckList->getMetadata().gameFormat == format) {
+ return;
+ }
+
+ requestHistorySave(tr("Set format to %1").arg(format));
+ deckListModel->setActiveFormat(format);
+
+ doMetadataModified();
+}
+
+QModelIndex DeckStateManager::addCard(const ExactCard &card, const QString &zoneName)
+{
+ if (!card) {
+ return {};
+ }
+
+ QString reason = tr("Added (%1): %2 (%3) %4")
+ .arg(zoneName, card.getName(), card.getPrinting().getSet()->getCorrectedShortName(),
+ card.getPrinting().getProperty("num"));
+
+ QModelIndex idx = modifyDeck(reason, [&card, &zoneName](auto model) { return model->addCard(card, zoneName); });
+
+ if (idx.isValid()) {
+ emit focusIndexChanged(idx);
+ }
+
+ return idx;
+}
+
+QModelIndex DeckStateManager::decrementCard(const ExactCard &card, const QString &zoneName)
+{
+ if (!card)
+ return {};
+
+ QString providerId = card.getPrinting().getUuid();
+ QString collectorNumber = card.getPrinting().getProperty("num");
+
+ QModelIndex idx = deckListModel->findCard(card.getName(), zoneName, providerId, collectorNumber);
+ if (!idx.isValid()) {
+ return {};
+ }
+
+ bool success = offsetCountAtIndex(idx, false);
+
+ if (!success) {
+ return {};
+ }
+
+ if (idx.isValid()) {
+ emit focusIndexChanged(idx);
+ }
+
+ return idx;
+}
+
+static bool doSwapCard(DeckListModel *model,
+ const QModelIndex &idx,
+ const QString &cardName,
+ const QString &providerId,
+ const QString &otherZone)
+{
+ bool success = model->offsetCountAtIndex(idx, -1);
+ if (!success) {
+ return false;
+ }
+
+ if (ExactCard card = CardDatabaseManager::query()->getCard({cardName, providerId})) {
+ model->addCard(card, otherZone);
+ } else {
+ // Third argument (true) says create the card no matter what, even if not in DB
+ model->addPreferredPrintingCard(cardName, otherZone, true);
+ }
+
+ return true;
+}
+
+bool DeckStateManager::swapCardAtIndex(const QModelIndex &idx)
+{
+ if (!idx.isValid())
+ return false;
+
+ QString cardName = idx.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString();
+ QString providerId = idx.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data().toString();
+ QModelIndex gparent = idx.parent().parent();
+
+ if (!gparent.isValid())
+ return false;
+
+ QString zoneName = gparent.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
+ QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN;
+
+ QString reason = tr("Moved to %1 1 × \"%2\" (%3)") //
+ .arg(otherZoneName)
+ .arg(cardName)
+ .arg(providerId);
+
+ return modifyDeck(reason, [&idx, &cardName, &providerId, &otherZoneName](auto model) {
+ return doSwapCard(model, idx, cardName, providerId, otherZoneName);
+ });
+}
+
+bool DeckStateManager::removeCardAtIndex(const QModelIndex &idx)
+{
+ if (!idx.isValid() || deckListModel->hasChildren(idx)) {
+ return false;
+ }
+
+ QString cardName = idx.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString();
+
+ QString reason = tr("Removed \"%1\" (all copies)").arg(cardName);
+
+ return modifyDeck(reason, [&idx](auto model) { return model->removeRow(idx.row(), idx.parent()); });
+}
+
+bool DeckStateManager::incrementCountAtIndex(const QModelIndex &idx)
+{
+ return offsetCountAtIndex(idx, 1);
+}
+
+bool DeckStateManager::decrementCountAtIndex(const QModelIndex &idx)
+{
+ return offsetCountAtIndex(idx, -1);
+}
+
+bool DeckStateManager::offsetCountAtIndex(const QModelIndex &idx, int offset)
+{
+ if (!idx.isValid()) {
+ return false;
+ }
+
+ QString cardName = idx.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
+ QString providerId = idx.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString();
+
+ QString reason = tr("%1 1 × \"%2\" (%3)") //
+ .arg(offset > 0 ? tr("Added") : tr("Removed"))
+ .arg(cardName)
+ .arg(providerId);
+
+ return modifyDeck(reason, [&idx, &offset](auto model) { return model->offsetCountAtIndex(idx, offset); });
+}
+
+void DeckStateManager::undo(int steps)
+{
+ if (!historyManager->canUndo()) {
+ return;
+ }
+
+ for (int i = 0; i < steps; i++) {
+ if (!historyManager->canUndo()) {
+ continue;
+ }
+ historyManager->undo(deckList.get());
+ }
+
+ deckListModel->rebuildTree();
+
+ emit deckListModel->layoutChanged();
+}
+
+void DeckStateManager::redo(int steps)
+{
+ if (!historyManager->canRedo()) {
+ return;
+ }
+
+ for (int i = 0; i < steps; i++) {
+ if (!historyManager->canRedo()) {
+ continue;
+ }
+ historyManager->redo(deckList.get());
+ }
+
+ deckListModel->rebuildTree();
+
+ emit deckListModel->layoutChanged();
+}
+
+void DeckStateManager::requestHistorySave(const QString &reason)
+{
+ historyManager->save(deckList->createMemento(reason));
+}
+
+/**
+ * @brief Handles updating state and emitting signals whenever the cards are modified
+ */
+void DeckStateManager::doCardModified()
+{
+ setModified(true);
+ emit cardModified();
+ emit deckModified();
+}
+
+/**
+ * @brief Handles updating state and emitting signals whenever the metadata is modified
+ */
+void DeckStateManager::doMetadataModified()
+{
+ setModified(true);
+ emit metadataModified();
+ emit deckModified();
+}
diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h
new file mode 100644
index 000000000..0f3ba3255
--- /dev/null
+++ b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h
@@ -0,0 +1,297 @@
+#ifndef COCKATRICE_DECK_STATE_MANAGER_H
+#define COCKATRICE_DECK_STATE_MANAGER_H
+
+#include "../../deck_loader/loaded_deck.h"
+#include "deck_list_model.h"
+
+#include
+#include
+
+class DeckListHistoryManager;
+
+/**
+ * @brief This class centralizes the management of the state of the deck in the deck editor tab.
+ * It is responsible for owning and managing the DeckListModel, underlying DeckList, load info, and edit history.
+ *
+ * Although this class provides getters for the underlying DeckListModel, you should generally refrain from directly
+ * modifying the returned model. Outside modifications to the deck state should be done through @link
+ * DeckStateManager::modifyDeck and the metadata setters.
+ * Those methods ensure that the history is recorded and correct signals are emitted.
+ */
+class DeckStateManager : public QObject
+{
+ Q_OBJECT
+
+ LoadedDeck::LoadInfo lastLoadInfo;
+ QSharedPointer deckList;
+ DeckListModel *deckListModel;
+ DeckListHistoryManager *historyManager;
+
+ bool modified = false;
+
+public:
+ explicit DeckStateManager(QObject *parent = nullptr);
+
+ /**
+ * Gets the underlying HistoryManager.
+ * @return The DeckListHistoryManager instance
+ */
+ DeckListHistoryManager *getHistoryManager() const
+ {
+ return historyManager;
+ }
+
+ /**
+ * @brief Gets the underlying DeckListModel.
+ * You should generally refrain modifying the returned model directly.
+ * However, it's fine (and intended) to perform queries on the returned model.
+ * @return The DeckListModel instance
+ */
+ DeckListModel *getModel() const
+ {
+ return deckListModel;
+ }
+
+ /**
+ * @brief Gets a view of the current deck.
+ */
+ const DeckList &getDeckList() const;
+
+ /**
+ * @brief Creates a LoadedDeck containing the contents of the current deck and the current LoadInfo.
+ *
+ * @return A new LoadedDeck instance.
+ */
+ LoadedDeck toLoadedDeck() const;
+
+ /**
+ * @brief Gets a view of the metadata in the DeckList
+ */
+ DeckList::Metadata const &getMetadata() const;
+
+ /**
+ * @brief Gets the deck's simplified name.
+ */
+ QString getSimpleDeckName() const;
+
+ /**
+ * @brief Gets the deck hash.
+ */
+ QString getDeckHash() const;
+
+ /**
+ * @brief Checks if the deck has been modified since it was last saved
+ */
+ bool isModified() const;
+
+ /**
+ * @brief Sets the new isModified state, emitting a signal if the state changed.
+ * This class will automatically update its isModified state, but you may need to set it manually to handle, for
+ * example, saving.
+ * @param state The state
+ */
+ void setModified(bool state);
+
+ /**
+ * @brief Checks if the deck state is as if it was a new deck
+ */
+ bool isBlankNewDeck() const;
+
+ /**
+ * @brief Overwrites the current deck with a new deck, resetting all history
+ * @param deck The new deck.
+ */
+ void replaceDeck(const LoadedDeck &deck);
+
+ /**
+ * @brief Resets the deck to a blank new deck, resetting all history.
+ */
+ void clearDeck();
+
+ /**
+ * @brief Sets the lastLoadInfo.
+ * @param loadInfo The lastLoadInfo
+ */
+ void setLastLoadInfo(const LoadedDeck::LoadInfo &loadInfo)
+ {
+ lastLoadInfo = loadInfo;
+ }
+
+ /**
+ * @brief Modifies the cards in the deck, in a wrapped operation that is saved to the history.
+ *
+ * The operation is a function that accepts a DeckListModel that it operates upon, and returns a bool.
+ *
+ * This method will pass the underlying DeckListModel into the operation function. The function can call methods on
+ * the model to modify the deck.
+ * The function should return a bool to indicate success/failure.
+ *
+ * If the operation returns true, the state of the deck before the operation is ran is saved to the history, and the
+ * isModified state is updated.
+ * If the operation returns false, the history and isModified state is not updated.
+ *
+ * Note that even if the operation fails, any modifications to the model will already have been made.
+ * It's recommended for the operation to always return true if any modification has already been made to the model,
+ * as not doing that may cause the state to become desynced.
+ *
+ * @param reason The reason to display in the history
+ * @param operation The modification operation.
+ * @return The bool returned from the operation
+ */
+ bool modifyDeck(const QString &reason, const std::function &operation);
+
+ /**
+ * @brief Modifies the cards in the deck, in a wrapped operation that is saved to the history.
+ *
+ * The operation is a function that accepts a DeckListModel that it operates upon, and returns a QModelIndex.
+ * If the index is invalid, then the operation is considered to be a failure.
+ *
+ * See the other @link DeckStateManager::modifyDeck for more info about the behavior of this method.
+ *
+ * @param reason The reason to display in the history
+ * @param operation The modification operation.
+ * @return The QModelIndex returned from the operation
+ */
+ QModelIndex modifyDeck(const QString &reason, const std::function &operation);
+
+ /// @name Metadata setters
+ /// @brief These methods set the metadata. Will no-op if the new value is the same as the current value.
+ /// Saves the operation to history if successful.
+ ///@{
+ void setName(const QString &name);
+ void setComments(const QString &comments);
+ void setBannerCard(const CardRef &bannerCard);
+ void setTags(const QStringList &tags);
+ void setFormat(const QString &format);
+ ///@}
+
+ /**
+ * @brief Adds the given card to the given zone.
+ * Saves the operation to history if successful.
+ *
+ * @param card The card to add
+ * @param zoneName The zone to add the card to
+ * @return The index of the added card
+ */
+ QModelIndex addCard(const ExactCard &card, const QString &zoneName);
+
+ /**
+ * @brief Removes 1 copy of the given card from the given zone.
+ * Saves the operation to history if successful.
+ *
+ * @param card The card to remove
+ * @param zoneName The zone to remove the card from
+ * @return The index of the removed card. Will be invalid if the last copy was removed.
+ */
+ QModelIndex decrementCard(const ExactCard &card, const QString &zoneName);
+
+ /**
+ * @brief Swaps one copy of the card at the given index between the maindeck and sideboard.
+ * No-ops if index is invalid or not a card node.
+ * Saves the operation to history if successful.
+ *
+ * @param idx The model index
+ * @return Whether the operation was successfully performed
+ */
+ bool swapCardAtIndex(const QModelIndex &idx);
+
+ /**
+ * @brief Removes all copies of the card at the given index.
+ * No-ops if index is invalid or not a card node.
+ * Saves the operation to history if successful.
+ *
+ * @param idx The model index
+ * @return Whether the operation was successfully performed
+ */
+ bool removeCardAtIndex(const QModelIndex &idx);
+
+ /**
+ * @brief Increments the number of copies of the card at the given index by 1.
+ * No-ops if index is invalid or not a card node.
+ * Saves the operation to history if successful.
+ *
+ * @param idx The model index
+ * @return Whether the operation was successfully performed
+ */
+ bool incrementCountAtIndex(const QModelIndex &idx);
+
+ /**
+ * @brief Decrements the number of copies of the card at the given index by 1.
+ * No-ops if index is invalid or not a card node.
+ * Saves the operation to history if successful.
+ *
+ * @param idx The model index
+ * @return Whether the operation was successfully performed
+ */
+ bool decrementCountAtIndex(const QModelIndex &idx);
+
+ /**
+ * Undoes n steps of the history, setting the decklist state and updating the current step in the historyManager.
+ * @param steps Number of steps to undo.
+ */
+ void undo(int steps = 1);
+
+ /**
+ * Redoes n steps of the history, setting the decklist state and updating the current step in the historyManager.
+ * @param steps Number of steps to redo.
+ */
+ void redo(int steps = 1);
+
+public slots:
+ /**
+ * Saves the current decklist state to history.
+ * @param reason The reason that is shown in the history.
+ */
+ void requestHistorySave(const QString &reason);
+
+private:
+ bool offsetCountAtIndex(const QModelIndex &idx, int offset);
+ void doCardModified();
+ void doMetadataModified();
+
+signals:
+ /**
+ * A modification has been made to the cards in the deck
+ */
+ void cardModified();
+
+ /**
+ * A card that wasn't previously in the deck was added to the deck, or the last copy of a card was removed from the
+ * deck.
+ */
+ void uniqueCardsChanged();
+
+ /**
+ * A modification has been made to the metadata in the deck
+ */
+ void metadataModified();
+
+ /**
+ * A modification has been made to the cards or metadata in the deck
+ */
+ void deckModified();
+
+ /**
+ * The history has been greatly changed and needs to be reloaded.
+ */
+ void historyChanged();
+
+ /**
+ * The deck has been completely changed.
+ */
+ void deckReplaced();
+
+ /**
+ * The isModified state of the deck has changed
+ * @param isModified the new state
+ */
+ void isModifiedChanged(bool isModified);
+
+ /**
+ * The selected card on any views connected to this deck should be changed to this index.
+ * @param index The model index
+ */
+ void focusIndexChanged(QModelIndex index);
+};
+
+#endif // COCKATRICE_DECK_STATE_MANAGER_H
\ No newline at end of file
diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_edit_password.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_edit_password.cpp
index 998dafb66..e3bbc7435 100644
--- a/cockatrice/src/interface/widgets/dialogs/dlg_edit_password.cpp
+++ b/cockatrice/src/interface/widgets/dialogs/dlg_edit_password.cpp
@@ -59,7 +59,7 @@ DlgEditPassword::DlgEditPassword(QWidget *parent) : QDialog(parent)
void DlgEditPassword::actOk()
{
- // TODO this stuff should be using qvalidators
+ //! \todo this stuff should be using qvalidators
if (newPasswordEdit->text().length() < 8) {
QMessageBox::critical(this, tr("Error"), tr("Your password is too short."));
return;
diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp
index e0d855d7e..c9c41722e 100644
--- a/cockatrice/src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp
+++ b/cockatrice/src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp
@@ -121,7 +121,7 @@ void DlgForgotPasswordReset::actOk()
return;
}
- // TODO this stuff should be using qvalidators
+ //! \todo this stuff should be using qvalidators
if (newpasswordEdit->text().length() < 8) {
QMessageBox::critical(this, tr("Error"), tr("Your password is too short."));
return;
diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp
index 952364e37..b72130c81 100644
--- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp
+++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp
@@ -66,26 +66,26 @@ void AbstractDlgDeckTextEdit::setText(const QString &text)
}
/**
- * Tries to load the current contents of the contentsEdit into the DeckLoader
+ * Tries to load the current contents of the contentsEdit into the deckList
*
- * @param deckLoader The DeckLoader to load the deck into
+ * @param deckList The deckList to load the deck into
* @return Whether the loading was successful
*/
-bool AbstractDlgDeckTextEdit::loadIntoDeck(DeckLoader *deckLoader) const
+bool AbstractDlgDeckTextEdit::loadIntoDeck(DeckList &deckList) const
{
QString buffer = contentsEdit->toPlainText();
if (buffer.contains("")) {
- return deckLoader->getDeckList()->loadFromString_Native(buffer);
+ return deckList.loadFromString_Native(buffer);
}
QTextStream stream(&buffer);
- if (deckLoader->getDeckList()->loadFromStream_Plain(stream, true)) {
+ if (deckList.loadFromStream_Plain(stream, true)) {
if (loadSetNameAndNumberCheckBox->isChecked()) {
- deckLoader->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId());
+ deckList.forEachCard(CardNodeFunction::ResolveProviderId());
} else {
- deckLoader->getDeckList()->forEachCard(CardNodeFunction::ClearPrintingData());
+ deckList.forEachCard(CardNodeFunction::ClearPrintingData());
}
return true;
}
@@ -108,7 +108,7 @@ void AbstractDlgDeckTextEdit::keyPressEvent(QKeyEvent *event)
*
* @param parent The parent widget
*/
-DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : AbstractDlgDeckTextEdit(parent), deckList(nullptr)
+DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : AbstractDlgDeckTextEdit(parent)
{
setWindowTitle(tr("Load deck from clipboard"));
@@ -122,8 +122,6 @@ void DlgLoadDeckFromClipboard::actRefresh()
void DlgLoadDeckFromClipboard::actOK()
{
- deckList = new DeckLoader(this);
-
if (loadIntoDeck(deckList)) {
accept();
} else {
@@ -134,18 +132,15 @@ void DlgLoadDeckFromClipboard::actOK()
/**
* Creates the dialog window for the "Edit deck in clipboard" action
*
- * @param _deckLoader The existing deck in the deck editor. Copies the instance
+ * @param _deckList The existing deck in the deck editor.
* @param _annotated Whether to add annotations to the text that is loaded from the deck
* @param parent The parent widget
*/
-DlgEditDeckInClipboard::DlgEditDeckInClipboard(DeckLoader *_deckLoader, bool _annotated, QWidget *parent)
- : AbstractDlgDeckTextEdit(parent), annotated(_annotated)
+DlgEditDeckInClipboard::DlgEditDeckInClipboard(const DeckList &_deckList, bool _annotated, QWidget *parent)
+ : AbstractDlgDeckTextEdit(parent), deckList(_deckList), annotated(_annotated)
{
setWindowTitle(tr("Edit deck in clipboard"));
- deckLoader = new DeckLoader(this, _deckLoader->getDeckList());
- deckLoader->setParent(this);
-
DlgEditDeckInClipboard::actRefresh();
}
@@ -155,7 +150,7 @@ DlgEditDeckInClipboard::DlgEditDeckInClipboard(DeckLoader *_deckLoader, bool _an
* @param addComments Whether to add annotations
* @return A QString
*/
-static QString deckListToString(const DeckList *deckList, bool addComments)
+static QString deckListToString(const DeckList &deckList, bool addComments)
{
QString buffer;
QTextStream stream(&buffer);
@@ -165,12 +160,12 @@ static QString deckListToString(const DeckList *deckList, bool addComments)
void DlgEditDeckInClipboard::actRefresh()
{
- setText(deckListToString(deckLoader->getDeckList(), annotated));
+ setText(deckListToString(deckList, annotated));
}
void DlgEditDeckInClipboard::actOK()
{
- if (loadIntoDeck(deckLoader)) {
+ if (loadIntoDeck(deckList)) {
accept();
} else {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck list."));
diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.h b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.h
index 982840228..0e59653ea 100644
--- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.h
+++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.h
@@ -8,10 +8,11 @@
#ifndef DLG_LOAD_DECK_FROM_CLIPBOARD_H
#define DLG_LOAD_DECK_FROM_CLIPBOARD_H
+#include "../../deck_loader/loaded_deck.h"
+
#include
#include
-class DeckLoader;
class QPlainTextEdit;
class QPushButton;
@@ -35,15 +36,13 @@ public:
/**
* Gets the loaded deck. Only call this method after this dialog window has been successfully exec'd.
*
- * The returned DeckLoader is parented to this object; make sure to take ownership of the DeckLoader if you intend
- * to use it, since otherwise it will get destroyed once this dlg is destroyed
- * @return The DeckLoader
+ * @return The loaded decklist
*/
- [[nodiscard]] virtual DeckLoader *getDeckList() const = 0;
+ [[nodiscard]] virtual const DeckList &getDeckList() = 0;
protected:
void setText(const QString &text);
- bool loadIntoDeck(DeckLoader *deckLoader) const;
+ bool loadIntoDeck(DeckList &deckList) const;
void keyPressEvent(QKeyEvent *event) override;
protected slots:
@@ -62,12 +61,12 @@ protected slots:
void actRefresh() override;
private:
- DeckLoader *deckList;
+ DeckList deckList;
public:
explicit DlgLoadDeckFromClipboard(QWidget *parent = nullptr);
- [[nodiscard]] DeckLoader *getDeckList() const override
+ [[nodiscard]] const DeckList &getDeckList() override
{
return deckList;
}
@@ -84,15 +83,15 @@ protected slots:
void actRefresh() override;
private:
- DeckLoader *deckLoader;
+ DeckList deckList;
bool annotated;
public:
- explicit DlgEditDeckInClipboard(DeckLoader *_deckLoader, bool _annotated, QWidget *parent = nullptr);
+ explicit DlgEditDeckInClipboard(const DeckList &_deckList, bool _annotated, QWidget *parent = nullptr);
- [[nodiscard]] DeckLoader *getDeckList() const override
+ [[nodiscard]] const DeckList &getDeckList() override
{
- return deckLoader;
+ return deckList;
}
};
diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp
index 2b63af1a4..da4135246 100644
--- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp
+++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp
@@ -97,11 +97,11 @@ void DlgLoadDeckFromWebsite::accept()
}
// Parse the plain text deck here
- DeckLoader *loader = new DeckLoader(this);
+ DeckList deckList;
QTextStream stream(&deckText);
- loader->getDeckList()->loadFromStream_Plain(stream, false);
- loader->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId());
- deck = loader;
+ deckList.loadFromStream_Plain(stream, false);
+ deckList.forEachCard(CardNodeFunction::ResolveProviderId());
+ deck = deckList;
QDialog::accept();
return;
diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h
index 84adb760f..fe6f0a7e9 100644
--- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h
+++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h
@@ -26,9 +26,9 @@ public:
explicit DlgLoadDeckFromWebsite(QWidget *parent);
void retranslateUi();
bool testValidUrl();
- DeckLoader *deck;
+ DeckList deck;
- DeckLoader *getDeck()
+ const DeckList &getDeck() const
{
return deck;
}
diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp
index 4be4cdf78..a3f232d9b 100644
--- a/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp
+++ b/cockatrice/src/interface/widgets/dialogs/dlg_register.cpp
@@ -356,7 +356,7 @@ DlgRegister::DlgRegister(QWidget *parent) : QDialog(parent)
void DlgRegister::actOk()
{
- // TODO this stuff should be using qvalidators
+ //! \todo this stuff should be using qvalidators
if (passwordEdit->text().length() < 8) {
QMessageBox::critical(this, tr("Registration Warning"), tr("Your password is too short."));
return;
diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp
index 2bfe650c3..6ed6b67a4 100644
--- a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp
+++ b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp
@@ -2,6 +2,7 @@
#include "../../deck_loader/card_node_function.h"
#include "../../deck_loader/deck_loader.h"
+#include "../deck_editor/deck_state_manager.h"
#include "../interface/widgets/cards/card_info_picture_widget.h"
#include "../interface/widgets/general/layout_containers/flow_widget.h"
@@ -21,7 +22,8 @@
#include
#include
-DlgSelectSetForCards::DlgSelectSetForCards(QWidget *parent, DeckListModel *_model) : QDialog(parent), model(_model)
+DlgSelectSetForCards::DlgSelectSetForCards(QWidget *parent, DeckStateManager *deckStateManger)
+ : QDialog(parent), deckStateManager(deckStateManger)
{
setMinimumSize(500, 500);
setAcceptDrops(true);
@@ -143,52 +145,61 @@ void DlgSelectSetForCards::retranslateUi()
setAllToPreferredButton->setText(tr("Set all to preferred"));
}
+static bool swapPrinting(DeckListModel *model, const QString &modifiedSet, const QString &cardName)
+{
+ QModelIndex idx = model->findCard(cardName, DECK_ZONE_MAIN);
+ if (!idx.isValid()) {
+ return false;
+ }
+ int amount = model->data(idx.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT), Qt::DisplayRole).toInt();
+ model->removeRow(idx.row(), idx.parent());
+ CardInfoPtr cardInfo = CardDatabaseManager::query()->getCardInfo(cardName);
+ PrintingInfo printing = CardDatabaseManager::query()->getSpecificPrinting(cardName, modifiedSet, "");
+ for (int i = 0; i < amount; i++) {
+ model->addCard(ExactCard(cardInfo, printing), DECK_ZONE_MAIN);
+ }
+ return true;
+}
+
void DlgSelectSetForCards::actOK()
{
QMap modifiedSetsAndCardsMap = getModifiedCards();
if (modifiedSetsAndCardsMap.isEmpty()) {
accept(); // Nothing to do
- } else {
- emit deckAboutToBeModified(tr("Bulk modified printings."));
+ return;
}
- for (QString modifiedSet : modifiedSetsAndCardsMap.keys()) {
- for (QString card : modifiedSetsAndCardsMap.value(modifiedSet)) {
- QModelIndex find_card = model->findCard(card, DECK_ZONE_MAIN);
- if (!find_card.isValid()) {
- continue;
- }
- int amount =
- model->data(find_card.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT), Qt::DisplayRole).toInt();
- model->removeRow(find_card.row(), find_card.parent());
- CardInfoPtr cardInfo = CardDatabaseManager::query()->getCardInfo(card);
- PrintingInfo printing = CardDatabaseManager::query()->getSpecificPrinting(card, modifiedSet, "");
- for (int i = 0; i < amount; i++) {
- model->addCard(ExactCard(cardInfo, printing), DECK_ZONE_MAIN);
+ auto bulkModify = [&modifiedSetsAndCardsMap](DeckListModel *model) {
+ for (QString modifiedSet : modifiedSetsAndCardsMap.keys()) {
+ for (QString card : modifiedSetsAndCardsMap.value(modifiedSet)) {
+ swapPrinting(model, modifiedSet, card);
}
}
- }
- if (!modifiedSetsAndCardsMap.isEmpty()) {
- emit deckModified();
- }
+ return true;
+ };
+
+ deckStateManager->modifyDeck(tr("Bulk modified printings."), bulkModify);
+
accept();
}
void DlgSelectSetForCards::actClear()
{
- emit deckAboutToBeModified(tr("Cleared all printing information."));
- model->getDeckList()->forEachCard(CardNodeFunction::ClearPrintingData());
- emit deckModified();
+ deckStateManager->modifyDeck(tr("Cleared all printing information."), [](auto model) {
+ model->forEachCard(CardNodeFunction::ClearPrintingData());
+ return true;
+ });
accept();
}
void DlgSelectSetForCards::actSetAllToPreferred()
{
- emit deckAboutToBeModified(tr("Set all printings to preferred."));
- model->getDeckList()->forEachCard(CardNodeFunction::ClearPrintingData());
- model->getDeckList()->forEachCard(CardNodeFunction::SetProviderIdToPreferred());
- emit deckModified();
+ deckStateManager->modifyDeck(tr("Set all printings to preferred."), [](auto model) {
+ model->forEachCard(CardNodeFunction::ClearPrintingData());
+ model->forEachCard(CardNodeFunction::SetProviderIdToPreferred());
+ return true;
+ });
accept();
}
@@ -221,17 +232,11 @@ void DlgSelectSetForCards::sortSetsByCount()
QMap DlgSelectSetForCards::getSetsForCards()
{
QMap setCounts;
- if (!model)
- return setCounts;
- DeckList *decklist = model->getDeckList();
- if (!decklist)
- return setCounts;
+ QList cardNames = deckStateManager->getModel()->getCardNames();
- QList cardsInDeck = decklist->getCardNodes();
-
- for (auto currentCard : cardsInDeck) {
- CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
+ for (auto cardName : cardNames) {
+ CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName);
if (!infoPtr)
continue;
@@ -267,19 +272,15 @@ void DlgSelectSetForCards::updateCardLists()
}
}
- DeckList *decklist = model->getDeckList();
- if (!decklist)
- return;
+ QList cardNames = deckStateManager->getModel()->getCardNames();
- QList cardsInDeck = decklist->getCardNodes();
-
- for (auto currentCard : cardsInDeck) {
+ for (auto cardName : cardNames) {
bool found = false;
QString foundSetName;
// Check across all sets if the card is present
for (auto it = selectedCardsBySet.begin(); it != selectedCardsBySet.end(); ++it) {
- if (it.value().contains(currentCard->getName())) {
+ if (it.value().contains(cardName)) {
found = true;
foundSetName = it.key(); // Store the set name where it was found
break; // Stop at the first match
@@ -288,16 +289,16 @@ void DlgSelectSetForCards::updateCardLists()
if (!found) {
// The card was not in any selected set
- ExactCard card = CardDatabaseManager::query()->getCard({currentCard->getName()});
+ ExactCard card = CardDatabaseManager::query()->getCard({cardName});
CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(uneditedCardsFlowWidget);
picture_widget->setCard(card);
uneditedCardsFlowWidget->addWidget(picture_widget);
} else {
- ExactCard card = CardDatabaseManager::query()->getCard(
- {currentCard->getName(), CardDatabaseManager::getInstance()
- ->query()
- ->getSpecificPrinting(currentCard->getName(), foundSetName, "")
- .getUuid()});
+ ExactCard card =
+ CardDatabaseManager::query()->getCard({cardName, CardDatabaseManager::getInstance()
+ ->query()
+ ->getSpecificPrinting(cardName, foundSetName, "")
+ .getUuid()});
CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(modifiedCardsFlowWidget);
picture_widget->setCard(card);
modifiedCardsFlowWidget->addWidget(picture_widget);
@@ -353,23 +354,17 @@ void DlgSelectSetForCards::dropEvent(QDropEvent *event)
QMap DlgSelectSetForCards::getCardsForSets()
{
QMap setCards;
- if (!model)
- return setCards;
- DeckList *decklist = model->getDeckList();
- if (!decklist)
- return setCards;
+ QList cardNames = deckStateManager->getModel()->getCardNames();
- QList cardsInDeck = decklist->getCardNodes();
-
- for (auto currentCard : cardsInDeck) {
- CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
+ for (auto cardName : cardNames) {
+ CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName);
if (!infoPtr)
continue;
SetToPrintingsMap setMap = infoPtr->getSets();
for (auto it = setMap.begin(); it != setMap.end(); ++it) {
- setCards[it.key()].append(currentCard->getName());
+ setCards[it.key()].append(cardName);
}
}
diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.h b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.h
index 5cdef5a30..795366b57 100644
--- a/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.h
+++ b/cockatrice/src/interface/widgets/dialogs/dlg_select_set_for_cards.h
@@ -18,6 +18,7 @@
#include
#include
+class DeckStateManager;
class SetEntryWidget; // Forward declaration
class DlgSelectSetForCards : public QDialog
@@ -25,7 +26,7 @@ class DlgSelectSetForCards : public QDialog
Q_OBJECT
public:
- explicit DlgSelectSetForCards(QWidget *parent, DeckListModel *_model);
+ explicit DlgSelectSetForCards(QWidget *parent, DeckStateManager *deckStateManager);
void retranslateUi();
void sortSetsByCount();
QMap getCardsForSets();
@@ -37,7 +38,6 @@ public:
signals:
void widgetOrderChanged();
void orderChanged();
- void deckAboutToBeModified(const QString &reason);
void deckModified();
public slots:
@@ -61,7 +61,7 @@ private:
QLabel *modifiedCardsLabel;
QWidget *listContainer;
QListWidget *listWidget;
- DeckListModel *model;
+ DeckStateManager *deckStateManager;
QMap setEntries;
QPushButton *clearButton;
QPushButton *setAllToPreferredButton;
diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp
index c0b80c6c1..191cc5d0b 100644
--- a/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp
+++ b/cockatrice/src/interface/widgets/dialogs/dlg_settings.cpp
@@ -1913,7 +1913,7 @@ void DlgSettings::closeEvent(QCloseEvent *event)
}
if (!QDir(SettingsCache::instance().getDeckPath()).exists() || SettingsCache::instance().getDeckPath().isEmpty()) {
- // TODO: Prompt to create it
+ //! \todo Prompt to create it
if (QMessageBox::critical(
this, tr("Error"),
tr("The path to your deck directory is invalid. Would you like to go back and set the correct path?"),
@@ -1924,7 +1924,7 @@ void DlgSettings::closeEvent(QCloseEvent *event)
}
if (!QDir(SettingsCache::instance().getPicsPath()).exists() || SettingsCache::instance().getPicsPath().isEmpty()) {
- // TODO: Prompt to create it
+ //! \todo Prompt to create it
if (QMessageBox::critical(this, tr("Error"),
tr("The path to your card pictures directory is invalid. Would you like to go back "
"and set the correct path?"),
diff --git a/cockatrice/src/interface/widgets/general/home_widget.cpp b/cockatrice/src/interface/widgets/general/home_widget.cpp
index 7aa6b40c7..f35f90256 100644
--- a/cockatrice/src/interface/widgets/general/home_widget.cpp
+++ b/cockatrice/src/interface/widgets/general/home_widget.cpp
@@ -20,10 +20,6 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
layout = new QGridLayout(this);
backgroundSourceCard = new CardInfoPictureArtCropWidget(this);
- backgroundSourceDeck = new DeckLoader(this);
-
- backgroundSourceDeck->loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod",
- DeckFileFormat::Cockatrice, false);
gradientColors = extractDominantColors(background);
@@ -72,13 +68,20 @@ void HomeWidget::initializeBackgroundFromSource()
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
break;
case BackgroundSources::DeckFileArt:
- backgroundSourceDeck->loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod",
- DeckFileFormat::Cockatrice, false);
+ loadBackgroundSourceDeck();
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
break;
}
}
+void HomeWidget::loadBackgroundSourceDeck()
+{
+ DeckLoader deckLoader = DeckLoader(this);
+ deckLoader.loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod", DeckFileFormat::Cockatrice,
+ false);
+ backgroundSourceDeck = deckLoader.getDeck().deckList;
+}
+
void HomeWidget::updateRandomCard()
{
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
@@ -95,7 +98,7 @@ void HomeWidget::updateRandomCard()
newCard.getCardPtr()->getProperty("layout") != "normal");
break;
case BackgroundSources::DeckFileArt:
- QList cardRefs = backgroundSourceDeck->getDeckList()->getCardRefList();
+ QList cardRefs = backgroundSourceDeck.getCardRefList();
ExactCard oldCard = backgroundSourceCard->getCard();
if (!cardRefs.empty()) {
@@ -183,7 +186,7 @@ QGroupBox *HomeWidget::createButtons()
auto visualDeckEditorButton = new HomeStyledButton(tr("Create New Deck"), gradientColors);
connect(visualDeckEditorButton, &QPushButton::clicked, tabSupervisor,
- [this] { tabSupervisor->openDeckInNewTab(nullptr); });
+ [this] { tabSupervisor->openDeckInNewTab(LoadedDeck()); });
boxLayout->addWidget(visualDeckEditorButton);
auto visualDeckStorageButton = new HomeStyledButton(tr("Browse Decks"), gradientColors);
connect(visualDeckStorageButton, &QPushButton::clicked, tabSupervisor,
diff --git a/cockatrice/src/interface/widgets/general/home_widget.h b/cockatrice/src/interface/widgets/general/home_widget.h
index 1db33446a..233fda543 100644
--- a/cockatrice/src/interface/widgets/general/home_widget.h
+++ b/cockatrice/src/interface/widgets/general/home_widget.h
@@ -40,10 +40,12 @@ private:
TabSupervisor *tabSupervisor;
QPixmap background;
CardInfoPictureArtCropWidget *backgroundSourceCard = nullptr;
- DeckLoader *backgroundSourceDeck;
+ DeckList backgroundSourceDeck;
QPixmap overlay;
QPair gradientColors;
HomeStyledButton *connectButton;
+
+ void loadBackgroundSourceDeck();
};
#endif // HOME_WIDGET_H
diff --git a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp
index 5b67ae727..b1346e4fd 100644
--- a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp
+++ b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp
@@ -11,20 +11,15 @@
* UI elements for managing card counts in both the mainboard and sideboard zones.
*
* @param parent The parent widget.
- * @param deckEditor Pointer to the TabDeckEditor.
- * @param deckModel Pointer to the DeckListModel.
- * @param deckView Pointer to the QTreeView for the deck display.
+ * @param deckStateManager Pointer to the DeckStateManager
* @param cardSizeSlider Pointer to the QSlider used for dynamic font resizing.
* @param rootCard The root card for the widget.
*/
AllZonesCardAmountWidget::AllZonesCardAmountWidget(QWidget *parent,
- AbstractTabDeckEditor *deckEditor,
- DeckListModel *deckModel,
- QTreeView *deckView,
+ DeckStateManager *deckStateManager,
QSlider *cardSizeSlider,
const ExactCard &rootCard)
- : QWidget(parent), deckEditor(deckEditor), deckModel(deckModel), deckView(deckView), cardSizeSlider(cardSizeSlider),
- rootCard(rootCard)
+ : QWidget(parent), cardSizeSlider(cardSizeSlider)
{
layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignHCenter);
@@ -33,11 +28,9 @@ AllZonesCardAmountWidget::AllZonesCardAmountWidget(QWidget *parent,
setContentsMargins(5, 5, 5, 5); // Padding around the text
zoneLabelMainboard = new ShadowBackgroundLabel(this, tr("Mainboard"));
- buttonBoxMainboard =
- new CardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard, DECK_ZONE_MAIN);
+ buttonBoxMainboard = new CardAmountWidget(this, deckStateManager, cardSizeSlider, rootCard, DECK_ZONE_MAIN);
zoneLabelSideboard = new ShadowBackgroundLabel(this, tr("Sideboard"));
- buttonBoxSideboard =
- new CardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard, DECK_ZONE_SIDE);
+ buttonBoxSideboard = new CardAmountWidget(this, deckStateManager, cardSizeSlider, rootCard, DECK_ZONE_SIDE);
layout->addWidget(zoneLabelMainboard, 0, Qt::AlignHCenter | Qt::AlignBottom);
layout->addWidget(buttonBoxMainboard, 0, Qt::AlignHCenter | Qt::AlignTop);
diff --git a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h
index 1a8ec8bfd..5a03c5f4a 100644
--- a/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h
+++ b/cockatrice/src/interface/widgets/printing_selector/all_zones_card_amount_widget.h
@@ -18,9 +18,7 @@ class AllZonesCardAmountWidget : public QWidget
Q_OBJECT
public:
explicit AllZonesCardAmountWidget(QWidget *parent,
- AbstractTabDeckEditor *deckEditor,
- DeckListModel *deckModel,
- QTreeView *deckView,
+ DeckStateManager *deckStateManager,
QSlider *cardSizeSlider,
const ExactCard &rootCard);
int getMainboardAmount();
@@ -36,11 +34,7 @@ public slots:
private:
QVBoxLayout *layout;
- AbstractTabDeckEditor *deckEditor;
- DeckListModel *deckModel;
- QTreeView *deckView;
QSlider *cardSizeSlider;
- ExactCard rootCard;
QLabel *zoneLabelMainboard;
CardAmountWidget *buttonBoxMainboard;
QLabel *zoneLabelSideboard;
diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp
index 0d0195d34..62c8e2d60 100644
--- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp
+++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.cpp
@@ -1,5 +1,7 @@
#include "card_amount_widget.h"
+#include "../deck_editor/deck_state_manager.h"
+
#include
#include
@@ -7,22 +9,17 @@
* @brief Constructs a widget for displaying and controlling the card count in a specific zone.
*
* @param parent The parent widget.
- * @param deckEditor Pointer to the TabDeckEditor instance.
- * @param deckModel Pointer to the DeckListModel instance.
- * @param deckView Pointer to the QTreeView displaying the deck.
* @param cardSizeSlider Pointer to the QSlider for adjusting font size.
* @param rootCard The root card to manage within the widget.
* @param zoneName The zone name (e.g., DECK_ZONE_MAIN or DECK_ZONE_SIDE).
*/
CardAmountWidget::CardAmountWidget(QWidget *parent,
- AbstractTabDeckEditor *deckEditor,
- DeckListModel *deckModel,
- QTreeView *deckView,
+ DeckStateManager *deckStateManager,
QSlider *cardSizeSlider,
const ExactCard &rootCard,
const QString &zoneName)
- : QWidget(parent), deckEditor(deckEditor), deckModel(deckModel), deckView(deckView), cardSizeSlider(cardSizeSlider),
- rootCard(rootCard), zoneName(zoneName), hovered(false)
+ : QWidget(parent), deckStateManager(deckStateManager), cardSizeSlider(cardSizeSlider), rootCard(rootCard),
+ zoneName(zoneName), hovered(false)
{
layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
@@ -56,15 +53,10 @@ CardAmountWidget::CardAmountWidget(QWidget *parent,
layout->addWidget(incrementButton);
// React to model changes
- connect(deckModel, &DeckListModel::dataChanged, this, &CardAmountWidget::updateCardCount);
- connect(deckModel, &QAbstractItemModel::rowsRemoved, this, &CardAmountWidget::updateCardCount);
+ connect(deckStateManager, &DeckStateManager::cardModified, this, &CardAmountWidget::updateCardCount);
// Connect slider for dynamic font size adjustment
connect(cardSizeSlider, &QSlider::valueChanged, this, &CardAmountWidget::adjustFontSize);
-
- if (deckEditor) {
- connect(this, &CardAmountWidget::deckModified, deckEditor, &AbstractTabDeckEditor::onDeckHistorySaveRequested);
- }
}
/**
@@ -137,6 +129,29 @@ void CardAmountWidget::updateCardCount()
layout->activate();
}
+static QModelIndex addAndReplacePrintings(DeckListModel *model,
+ const QModelIndex &existing,
+ const ExactCard &rootCard,
+ const QString &zone,
+ int extraCopies,
+ bool replaceProviderless)
+{
+ auto newCardIndex = model->addCard(rootCard, zone);
+ if (!newCardIndex.isValid()) {
+ return {};
+ }
+
+ // Check if a card without a providerId already exists in the deckModel and replace it, if so.
+ if (existing.isValid() && existing != newCardIndex && replaceProviderless) {
+ model->offsetCountAtIndex(newCardIndex, extraCopies);
+ model->removeRow(existing.row(), existing.parent());
+ }
+
+ // Set Index and Focus as if the user had just clicked the new card and modify the deckEditor saveState
+ return model->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(),
+ rootCard.getPrinting().getProperty("num"));
+}
+
/**
* @brief Adds a printing of the card to the specified zone (Mainboard or Sideboard).
*
@@ -144,25 +159,24 @@ void CardAmountWidget::updateCardCount()
*/
void CardAmountWidget::addPrinting(const QString &zone)
{
- int addedCount = 1;
// Check if we will need to add extra copies due to replacing copies without providerIds
- QModelIndex existing = deckModel->findCard(rootCard.getName(), zone);
+ QModelIndex existing = deckStateManager->getModel()->findCard(rootCard.getName(), zone);
+
int extraCopies = 0;
bool replacingProviderless = false;
if (existing.isValid()) {
- QString providerId = deckModel->data(existing.sibling(existing.row(), 4), Qt::DisplayRole).toString();
- if (providerId.isEmpty()) {
- int amount = deckModel->data(existing, Qt::DisplayRole).toInt();
+ QString foundProviderId =
+ existing.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString();
+ if (foundProviderId.isEmpty()) {
+ int amount = existing.data(Qt::DisplayRole).toInt();
extraCopies = amount - 1; // One less because we *always* add one
replacingProviderless = true;
}
}
- addedCount += extraCopies;
-
QString reason = QString("Added %1 copies of '%2 (%3) %4' to %5 [ProviderID: %6]%7")
- .arg(addedCount)
+ .arg(1 + extraCopies)
.arg(rootCard.getName())
.arg(rootCard.getPrinting().getSet()->getShortName())
.arg(rootCard.getPrinting().getProperty("num"))
@@ -170,29 +184,14 @@ void CardAmountWidget::addPrinting(const QString &zone)
.arg(rootCard.getPrinting().getUuid())
.arg(replacingProviderless ? " (replaced providerless printings)" : "");
- emit deckModified(reason);
-
// Add the card and expand the list UI
- auto newCardIndex = deckModel->addCard(rootCard, zone);
- recursiveExpand(newCardIndex);
+ QModelIndex newCardIndex = deckStateManager->modifyDeck(reason, [&](auto model) {
+ return addAndReplacePrintings(model, existing, rootCard, zone, extraCopies, replacingProviderless);
+ });
- // Check if a card without a providerId already exists in the deckModel and replace it, if so.
- QString foundProviderId = deckModel->data(existing.sibling(existing.row(), 4), Qt::DisplayRole).toString();
- if (existing.isValid() && existing != newCardIndex && foundProviderId == "") {
- auto amount = deckModel->data(existing, Qt::DisplayRole);
- for (int i = 0; i < amount.toInt() - 1; i++) {
- deckModel->addCard(rootCard, zone);
- }
- deckModel->removeRow(existing.row(), existing.parent());
+ if (newCardIndex.isValid()) {
+ emit deckStateManager->focusIndexChanged(newCardIndex);
}
-
- // Set Index and Focus as if the user had just clicked the new card and modify the deckEditor saveState
- newCardIndex = deckModel->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(),
- rootCard.getPrinting().getProperty("num"));
-
- deckView->setCurrentIndex(newCardIndex);
- deckView->setFocus(Qt::FocusReason::MouseFocusReason);
- deckEditor->setModified(true);
}
/**
@@ -227,46 +226,6 @@ void CardAmountWidget::removePrintingSideboard()
decrementCardHelper(DECK_ZONE_SIDE);
}
-/**
- * @brief Recursively expands the card in the deck view starting from the given index.
- *
- * @param index The model index of the card to expand.
- */
-void CardAmountWidget::recursiveExpand(const QModelIndex &index)
-{
- if (index.parent().isValid()) {
- recursiveExpand(index.parent());
- }
- deckView->expand(index);
-}
-
-/**
- * @brief Offsets the card count at the specified index by the given amount.
- *
- * @param idx The model index of the card.
- * @param offset The amount to add or subtract from the card count.
- */
-void CardAmountWidget::offsetCountAtIndex(const QModelIndex &idx, int offset)
-{
- if (!idx.isValid() || offset == 0) {
- return;
- }
-
- const QModelIndex numberIndex = idx.sibling(idx.row(), 0);
- const int count = deckModel->data(numberIndex, Qt::EditRole).toInt();
- const int new_count = count + offset;
-
- deckView->setCurrentIndex(numberIndex);
-
- if (new_count <= 0) {
- deckModel->removeRow(idx.row(), idx.parent());
- } else {
- deckModel->setData(numberIndex, new_count, Qt::EditRole);
- }
-
- deckEditor->setModified(true);
-}
-
/**
* @brief Helper function to decrement the card count for a given zone.
*
@@ -281,13 +240,11 @@ void CardAmountWidget::decrementCardHelper(const QString &zone)
.arg(zone == DECK_ZONE_MAIN ? "mainboard" : "sideboard")
.arg(rootCard.getPrinting().getUuid());
- emit deckModified(reason);
-
- QModelIndex idx = deckModel->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(),
+ deckStateManager->modifyDeck(reason, [this, &zone](auto model) {
+ QModelIndex idx = model->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(),
rootCard.getPrinting().getProperty("num"));
-
- offsetCountAtIndex(idx, -1);
- deckEditor->setModified(true);
+ return model->offsetCountAtIndex(idx, -1);
+ });
}
/**
@@ -298,29 +255,14 @@ void CardAmountWidget::decrementCardHelper(const QString &zone)
*/
int CardAmountWidget::countCardsInZone(const QString &deckZone)
{
- if (rootCard.getPrinting().getUuid().isEmpty()) {
+ QString uuid = rootCard.getPrinting().getUuid();
+
+ if (uuid.isEmpty()) {
return 0; // Cards without uuids/providerIds CANNOT match another card, they are undefined for us.
}
- if (!deckModel) {
- return -1;
- }
+ QList cards = deckStateManager->getModel()->getCardsForZone(deckZone);
- DeckList *decklist = deckModel->getDeckList();
- if (!decklist) {
- return -1;
- }
-
- QList cardsInDeck = decklist->getCardNodes({deckZone});
-
- int count = 0;
- for (auto currentCard : cardsInDeck) {
- for (int k = 0; k < currentCard->getNumber(); ++k) {
- if (currentCard->getCardProviderId() == rootCard.getPrinting().getProperty("uuid")) {
- count++;
- }
- }
- }
-
- return count;
+ return std::count_if(cards.cbegin(), cards.cend(),
+ [&uuid](const ExactCard &card) { return card.getPrinting().getUuid() == uuid; });
}
\ No newline at end of file
diff --git a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h
index 6d059bc04..983416782 100644
--- a/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h
+++ b/cockatrice/src/interface/widgets/printing_selector/card_amount_widget.h
@@ -27,9 +27,7 @@ signals:
public:
explicit CardAmountWidget(QWidget *parent,
- AbstractTabDeckEditor *deckEditor,
- DeckListModel *deckModel,
- QTreeView *deckView,
+ DeckStateManager *deckStateManager,
QSlider *cardSizeSlider,
const ExactCard &rootCard,
const QString &zoneName);
@@ -44,9 +42,7 @@ protected:
void showEvent(QShowEvent *event) override;
private:
- AbstractTabDeckEditor *deckEditor;
- DeckListModel *deckModel;
- QTreeView *deckView;
+ DeckStateManager *deckStateManager;
QSlider *cardSizeSlider;
ExactCard rootCard;
QString zoneName;
@@ -57,9 +53,7 @@ private:
bool hovered;
- void offsetCountAtIndex(const QModelIndex &idx, int offset);
void decrementCardHelper(const QString &zoneName);
- void recursiveExpand(const QModelIndex &index);
private slots:
void addPrintingMainboard();
diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp
index f27101b93..95d6b2cdf 100644
--- a/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp
+++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector.cpp
@@ -3,6 +3,7 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../interface/card_picture_loader/card_picture_loader.h"
#include "../../../interface/widgets/dialogs/dlg_select_set_for_cards.h"
+#include "../deck_editor/deck_state_manager.h"
#include "printing_selector_card_display_widget.h"
#include "printing_selector_card_search_widget.h"
#include "printing_selector_card_selection_widget.h"
@@ -21,12 +22,9 @@
*
* @param parent The parent widget for the PrintingSelector.
* @param deckEditor The TabDeckEditor instance used for managing the deck.
- * @param deckModel The DeckListModel instance that provides data for the deck's contents.
- * @param deckView The QTreeView instance used to display the deck and its contents.
*/
PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deckEditor)
- : QWidget(parent), deckEditor(_deckEditor), deckModel(deckEditor->deckDockWidget->deckModel),
- deckView(deckEditor->deckDockWidget->deckView)
+ : QWidget(parent), deckEditor(_deckEditor), deckStateManager(_deckEditor->deckStateManager)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
layout = new QVBoxLayout(this);
@@ -74,13 +72,12 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck
layout->addWidget(flowWidget);
- cardSelectionBar = new PrintingSelectorCardSelectionWidget(this);
+ cardSelectionBar = new PrintingSelectorCardSelectionWidget(this, deckStateManager);
cardSelectionBar->setVisible(SettingsCache::instance().getPrintingSelectorNavigationButtonsVisible());
layout->addWidget(cardSelectionBar);
// Connect deck model data change signal to update display
- connect(deckModel, &DeckListModel::rowsInserted, this, &PrintingSelector::printingsInDeckChanged);
- connect(deckModel, &DeckListModel::rowsRemoved, this, &PrintingSelector::printingsInDeckChanged);
+ connect(deckStateManager, &DeckStateManager::uniqueCardsChanged, this, &PrintingSelector::printingsInDeckChanged);
retranslateUi();
}
@@ -115,9 +112,8 @@ void PrintingSelector::updateDisplay()
* @brief Sets the current card for the selector and updates the display.
*
* @param newCard The new card to set.
- * @param _currentZone The current zone the card is in.
*/
-void PrintingSelector::setCard(const CardInfoPtr &newCard, const QString &_currentZone)
+void PrintingSelector::setCard(const CardInfoPtr &newCard)
{
if (newCard.isNull()) {
return;
@@ -129,7 +125,6 @@ void PrintingSelector::setCard(const CardInfoPtr &newCard, const QString &_curre
}
selectedCard = newCard;
- currentZone = _currentZone;
if (isVisible()) {
updateDisplay();
}
@@ -138,58 +133,6 @@ void PrintingSelector::setCard(const CardInfoPtr &newCard, const QString &_curre
flowWidget->repaint();
}
-/**
- * @brief Selects the previous card in the list.
- */
-void PrintingSelector::selectPreviousCard()
-{
- selectCard(-1);
-}
-
-/**
- * @brief Selects the next card in the list.
- */
-void PrintingSelector::selectNextCard()
-{
- selectCard(1);
-}
-
-/**
- * @brief Selects a card based on the change direction.
- *
- * @param changeBy The direction to change, -1 for previous, 1 for next.
- */
-void PrintingSelector::selectCard(const int changeBy)
-{
- if (changeBy == 0) {
- return;
- }
-
- // Get the current index of the selected item
- auto deckViewCurrentIndex = deckView->currentIndex();
-
- auto nextIndex = deckViewCurrentIndex.siblingAtRow(deckViewCurrentIndex.row() + changeBy);
- if (!nextIndex.isValid()) {
- nextIndex = deckViewCurrentIndex;
-
- // Increment to the next valid index, skipping header rows
- AbstractDecklistNode *node;
- do {
- if (changeBy > 0) {
- nextIndex = deckView->indexBelow(nextIndex);
- } else {
- nextIndex = deckView->indexAbove(nextIndex);
- }
- node = static_cast(nextIndex.internalPointer());
- } while (node && node->isDeckHeader());
- }
-
- if (nextIndex.isValid()) {
- deckView->setCurrentIndex(nextIndex);
- deckView->setFocus(Qt::FocusReason::MouseFocusReason);
- }
-}
-
/**
* @brief Loads and displays all sets for the current selected card.
*/
@@ -206,7 +149,8 @@ void PrintingSelector::getAllSetsForCurrentCard()
QList printingsToUse;
if (SettingsCache::instance().getBumpSetsWithCardsInDeckToTop()) {
- printingsToUse = sortToolBar->prependPrintingsInDeck(filteredPrintings, selectedCard, deckModel);
+ printingsToUse =
+ sortToolBar->prependPrintingsInDeck(filteredPrintings, selectedCard, deckStateManager->getModel());
} else {
printingsToUse = filteredPrintings;
}
@@ -218,8 +162,8 @@ void PrintingSelector::getAllSetsForCurrentCard()
connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=, this]() mutable {
for (int i = 0; i < BATCH_SIZE && currentIndex < printingsToUse.size(); ++i, ++currentIndex) {
auto card = ExactCard(selectedCard, printingsToUse[currentIndex]);
- auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(
- this, deckEditor, deckModel, deckView, cardSizeWidget->getSlider(), card, currentZone);
+ auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(this, deckEditor, deckStateManager,
+ cardSizeWidget->getSlider(), card);
flowWidget->addWidget(cardDisplayWidget);
cardDisplayWidget->clampSetNameToPicture();
connect(cardDisplayWidget, &PrintingSelectorCardDisplayWidget::cardPreferenceChanged, this,
diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector.h
index fbe3b5b06..e1c07addf 100644
--- a/cockatrice/src/interface/widgets/printing_selector/printing_selector.h
+++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector.h
@@ -21,6 +21,7 @@
#define BATCH_SIZE 10
+class DeckStateManager;
class PrintingSelectorCardSearchWidget;
class PrintingSelectorCardSelectionWidget;
class PrintingSelectorCardSortingWidget;
@@ -33,28 +34,27 @@ class PrintingSelector : public QWidget
public:
PrintingSelector(QWidget *parent, AbstractTabDeckEditor *deckEditor);
- void setCard(const CardInfoPtr &newCard, const QString &_currentZone);
+ void setCard(const CardInfoPtr &newCard);
void getAllSetsForCurrentCard();
- [[nodiscard]] DeckListModel *getDeckModel() const
- {
- return deckModel;
- }
-
- [[nodiscard]] AbstractTabDeckEditor *getDeckEditor() const
- {
- return deckEditor;
- }
public slots:
void retranslateUi();
void updateDisplay();
- void selectPreviousCard();
- void selectNextCard();
void toggleVisibilityNavigationButtons(bool _state);
private slots:
void printingsInDeckChanged();
+signals:
+ /**
+ * Requests the previous card in the list
+ */
+ void prevCardRequested();
+ /**
+ * Requests the next card in the list
+ */
+ void nextCardRequested();
+
private:
QVBoxLayout *layout;
SettingsButtonWidget *displayOptionsWidget;
@@ -67,13 +67,10 @@ private:
CardSizeWidget *cardSizeWidget;
PrintingSelectorCardSelectionWidget *cardSelectionBar;
AbstractTabDeckEditor *deckEditor;
- DeckListModel *deckModel;
- QTreeView *deckView;
+ DeckStateManager *deckStateManager;
CardInfoPtr selectedCard;
- QString currentZone;
QTimer *widgetLoadingBufferTimer;
int currentIndex = 0;
- void selectCard(int changeBy);
};
#endif // PRINTING_SELECTOR_H
diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp
index 24c45a7e3..92cf2437c 100644
--- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp
@@ -17,30 +17,24 @@
* display.
*
* @param parent The parent widget for this display.
- * @param _deckEditor The TabDeckEditor instance for deck management.
- * @param _deckModel The DeckListModel instance providing deck data.
- * @param _deckView The QTreeView instance displaying the deck.
- * @param _cardSizeSlider The slider controlling the size of the displayed card.
- * @param _rootCard The root card object, representing the card to be displayed.
- * @param _currentZone The current zone in which the card is located.
+ * @param deckEditor The TabDeckEditor instance for deck management.
+ * @param deckStateManager The DeckStateManager instance providing deck data.
+ * @param cardSizeSlider The slider controlling the size of the displayed card.
+ * @param rootCard The root card object, representing the card to be displayed.
*/
PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *parent,
- AbstractTabDeckEditor *_deckEditor,
- DeckListModel *_deckModel,
- QTreeView *_deckView,
- QSlider *_cardSizeSlider,
- const ExactCard &_rootCard,
- QString &_currentZone)
- : QWidget(parent), deckEditor(_deckEditor), deckModel(_deckModel), deckView(_deckView),
- cardSizeSlider(_cardSizeSlider), rootCard(_rootCard), currentZone(_currentZone)
+ AbstractTabDeckEditor *deckEditor,
+ DeckStateManager *deckStateManager,
+ QSlider *cardSizeSlider,
+ const ExactCard &rootCard)
+ : QWidget(parent)
{
layout = new QVBoxLayout(this);
setLayout(layout);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// Create the overlay widget for the card display
- overlayWidget =
- new PrintingSelectorCardOverlayWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard);
+ overlayWidget = new PrintingSelectorCardOverlayWidget(this, deckEditor, deckStateManager, cardSizeSlider, rootCard);
connect(overlayWidget, &PrintingSelectorCardOverlayWidget::cardPreferenceChanged, this,
[this]() { emit cardPreferenceChanged(); });
diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h
index 2f6bd6920..2637b0e57 100644
--- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h
+++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_display_widget.h
@@ -20,12 +20,10 @@ class PrintingSelectorCardDisplayWidget : public QWidget
public:
PrintingSelectorCardDisplayWidget(QWidget *parent,
- AbstractTabDeckEditor *_deckEditor,
- DeckListModel *_deckModel,
- QTreeView *_deckView,
- QSlider *_cardSizeSlider,
- const ExactCard &_rootCard,
- QString &_currentZone);
+ AbstractTabDeckEditor *deckEditor,
+ DeckStateManager *deckStateManager,
+ QSlider *cardSizeSlider,
+ const ExactCard &rootCard);
public slots:
void clampSetNameToPicture();
@@ -36,12 +34,6 @@ signals:
private:
QVBoxLayout *layout;
SetNameAndCollectorsNumberDisplayWidget *setNameAndCollectorsNumberDisplayWidget;
- AbstractTabDeckEditor *deckEditor;
- DeckListModel *deckModel;
- QTreeView *deckView;
- QSlider *cardSizeSlider;
- ExactCard rootCard;
- QString currentZone;
PrintingSelectorCardOverlayWidget *overlayWidget;
};
diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp
index 5f3a44b30..ac36f2cf4 100644
--- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp
+++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp
@@ -22,19 +22,16 @@
*
* @param parent The parent widget for this overlay.
* @param _deckEditor The TabDeckEditor instance for deck management.
- * @param _deckModel The DeckListModel instance providing deck data.
- * @param _deckView The QTreeView instance displaying the deck.
- * @param _cardSizeSlider The slider controlling the size of the card.
+ * @param deckStateManager The DeckStateManager instance providing deck data.
+ * @param cardSizeSlider The slider controlling the size of the card.
* @param _rootCard The root card object that contains information about the card.
*/
PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *parent,
AbstractTabDeckEditor *_deckEditor,
- DeckListModel *_deckModel,
- QTreeView *_deckView,
- QSlider *_cardSizeSlider,
+ DeckStateManager *deckStateManager,
+ QSlider *cardSizeSlider,
const ExactCard &_rootCard)
- : QWidget(parent), deckEditor(_deckEditor), deckModel(_deckModel), deckView(_deckView),
- cardSizeSlider(_cardSizeSlider), rootCard(_rootCard)
+ : QWidget(parent), deckEditor(_deckEditor), rootCard(_rootCard)
{
// Set up the main layout
auto *mainLayout = new QVBoxLayout(this);
@@ -59,8 +56,7 @@ PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *pa
updatePinBadgeVisibility();
// Add AllZonesCardAmountWidget
- allZonesCardAmountWidget =
- new AllZonesCardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, _rootCard);
+ allZonesCardAmountWidget = new AllZonesCardAmountWidget(this, deckStateManager, cardSizeSlider, _rootCard);
allZonesCardAmountWidget->raise(); // Ensure it's on top of the picture
// Set initial visibility based on amounts
diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h
index 10df9d53e..8550612bd 100644
--- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h
+++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.h
@@ -20,8 +20,7 @@ class PrintingSelectorCardOverlayWidget : public QWidget
public:
explicit PrintingSelectorCardOverlayWidget(QWidget *parent,
AbstractTabDeckEditor *_deckEditor,
- DeckListModel *_deckModel,
- QTreeView *_deckView,
+ DeckStateManager *_deckStateManager,
QSlider *_cardSizeSlider,
const ExactCard &_rootCard);
@@ -48,9 +47,6 @@ private:
AllZonesCardAmountWidget *allZonesCardAmountWidget;
QLabel *pinBadge = nullptr;
AbstractTabDeckEditor *deckEditor;
- DeckListModel *deckModel;
- QTreeView *deckView;
- QSlider *cardSizeSlider;
ExactCard rootCard;
};
diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp
index fc17cecd0..2eb2ef245 100644
--- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp
+++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp
@@ -11,7 +11,9 @@
*
* @param parent The parent PrintingSelector widget responsible for managing card selection.
*/
-PrintingSelectorCardSelectionWidget::PrintingSelectorCardSelectionWidget(PrintingSelector *parent) : parent(parent)
+PrintingSelectorCardSelectionWidget::PrintingSelectorCardSelectionWidget(PrintingSelector *parent,
+ DeckStateManager *deckStateManager)
+ : parent(parent), deckStateManager(deckStateManager)
{
cardSelectionBarLayout = new QHBoxLayout(this);
cardSelectionBarLayout->setContentsMargins(9, 0, 9, 0);
@@ -42,18 +44,12 @@ PrintingSelectorCardSelectionWidget::PrintingSelectorCardSelectionWidget(Printin
*/
void PrintingSelectorCardSelectionWidget::connectSignals()
{
- connect(previousCardButton, &QPushButton::clicked, parent, &PrintingSelector::selectPreviousCard);
- connect(nextCardButton, &QPushButton::clicked, parent, &PrintingSelector::selectNextCard);
+ connect(previousCardButton, &QPushButton::clicked, parent, &PrintingSelector::prevCardRequested);
+ connect(nextCardButton, &QPushButton::clicked, parent, &PrintingSelector::nextCardRequested);
}
void PrintingSelectorCardSelectionWidget::selectSetForCards()
{
- auto *setSelectionDialog = new DlgSelectSetForCards(nullptr, parent->getDeckModel());
- connect(setSelectionDialog, &DlgSelectSetForCards::deckAboutToBeModified, parent->getDeckEditor(),
- &AbstractTabDeckEditor::onDeckHistorySaveRequested);
- connect(setSelectionDialog, &DlgSelectSetForCards::deckModified, parent->getDeckEditor(),
- &AbstractTabDeckEditor::onDeckModified);
- if (!setSelectionDialog->exec()) {
- return;
- }
+ auto *setSelectionDialog = new DlgSelectSetForCards(nullptr, deckStateManager);
+ setSelectionDialog->exec();
}
diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.h
index a1176a76c..ecd5c83e3 100644
--- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.h
+++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_selection_widget.h
@@ -18,7 +18,7 @@ class PrintingSelectorCardSelectionWidget : public QWidget
Q_OBJECT
public:
- explicit PrintingSelectorCardSelectionWidget(PrintingSelector *parent);
+ explicit PrintingSelectorCardSelectionWidget(PrintingSelector *parent, DeckStateManager *deckStateManager);
void connectSignals();
@@ -27,6 +27,7 @@ public slots:
private:
PrintingSelector *parent;
+ DeckStateManager *deckStateManager;
QHBoxLayout *cardSelectionBarLayout;
QPushButton *previousCardButton;
QPushButton *selectSetForCardsButton;
diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp
index af7cedbeb..725e5df90 100644
--- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp
+++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.cpp
@@ -183,7 +183,7 @@ QList PrintingSelectorCardSortingWidget::prependPinnedPrintings(co
*/
QList PrintingSelectorCardSortingWidget::prependPrintingsInDeck(const QList &printings,
const CardInfoPtr &selectedCard,
- DeckListModel *deckModel)
+ const DeckListModel *deckModel)
{
if (!selectedCard) {
return {};
diff --git a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.h b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.h
index d0faea4ac..b5a00b81e 100644
--- a/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.h
+++ b/cockatrice/src/interface/widgets/printing_selector/printing_selector_card_sorting_widget.h
@@ -23,7 +23,7 @@ public:
QList prependPinnedPrintings(const QList &printings, const QString &cardName);
QList prependPrintingsInDeck(const QList &printings,
const CardInfoPtr &selectedCard,
- DeckListModel *deckModel);
+ const DeckListModel *deckModel);
public slots:
void updateSortOrder();
diff --git a/cockatrice/src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp b/cockatrice/src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
index b1a6a61c1..5962680cd 100644
--- a/cockatrice/src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
@@ -13,7 +13,7 @@ SetNameAndCollectorsNumberDisplayWidget::SetNameAndCollectorsNumberDisplayWidget
const QString &_setName,
const QString &_collectorsNumber,
QSlider *_cardSizeSlider)
- : QWidget(parent)
+ : QWidget(parent), cardSizeSlider(_cardSizeSlider)
{
// Set up the layout for the widget
layout = new QVBoxLayout(this);
@@ -35,7 +35,6 @@ SetNameAndCollectorsNumberDisplayWidget::SetNameAndCollectorsNumberDisplayWidget
collectorsNumber->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
// Store the card size slider and connect its signal to the font size adjustment slot
- cardSizeSlider = _cardSizeSlider;
connect(cardSizeSlider, &QSlider::valueChanged, this, &SetNameAndCollectorsNumberDisplayWidget::adjustFontSize);
// Add labels to the layout
diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp
index c97d32d44..50375c936 100644
--- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp
+++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp
@@ -206,7 +206,7 @@ void ChatView::appendMessage(QString message,
defaultFormat = QTextCharFormat();
if (!isUserMessage) {
if (messageType == Event_RoomSay::ChatHistory) {
- defaultFormat.setForeground(Qt::gray); // FIXME : hardcoded color
+ defaultFormat.setForeground(Qt::gray); //! \todo hardcoded color
defaultFormat.setFontWeight(QFont::Light);
defaultFormat.setFontItalic(true);
static const QRegularExpression userNameRegex("^(\\[[^\\]]*\\]\\s)(\\S+):\\s");
@@ -229,18 +229,14 @@ void ChatView::appendMessage(QString message,
message.remove(0, pos.relativePosition - 2); // do not remove semicolon
}
} else {
- defaultFormat.setForeground(Qt::darkGreen); // FIXME : hardcoded color
+ defaultFormat.setForeground(Qt::darkGreen); //! \todo hardcoded color
defaultFormat.setFontWeight(QFont::Bold);
}
}
cursor.setCharFormat(defaultFormat);
bool mentionEnabled = SettingsCache::instance().getChatMention();
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
highlightedWords = SettingsCache::instance().getHighlightWords().split(' ', Qt::SkipEmptyParts);
-#else
- highlightedWords = SettingsCache::instance().getHighlightWords().split(' ', QString::SkipEmptyParts);
-#endif
// parse the message
while (message.size()) {
diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.h b/cockatrice/src/interface/widgets/server/chat_view/chat_view.h
index 00fcc4f5e..6cf8370ed 100644
--- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.h
+++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.h
@@ -28,9 +28,6 @@ class UserListProxy;
class UserMessagePosition
{
public:
-#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
- UserMessagePosition() = default; // older qt versions require a default constructor to use in containers
-#endif
UserMessagePosition(QTextCursor &cursor);
int relativePosition;
QTextBlock block;
diff --git a/cockatrice/src/interface/widgets/server/games_model.cpp b/cockatrice/src/interface/widgets/server/games_model.cpp
index cdac71b28..05d363fee 100644
--- a/cockatrice/src/interface/widgets/server/games_model.cpp
+++ b/cockatrice/src/interface/widgets/server/games_model.cpp
@@ -421,10 +421,8 @@ bool GamesProxyModel::filterAcceptsRow(int sourceRow) const
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
static const QDate epochDate = QDateTime::fromSecsSinceEpoch(0, QTimeZone::UTC).date();
-#elif (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
- static const QDate epochDate = QDateTime::fromSecsSinceEpoch(0, Qt::UTC).date();
#else
- static const QDate epochDate = QDateTime::fromTime_t(0, Qt::UTC).date();
+ static const QDate epochDate = QDateTime::fromSecsSinceEpoch(0, Qt::UTC).date();
#endif
auto *model = qobject_cast(sourceModel());
if (!model)
diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp
index 55fa0b95c..29bcc1ab1 100644
--- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp
+++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.cpp
@@ -12,6 +12,7 @@
#include "../../../client/settings/cache_settings.h"
#include "../client/network/interfaces/deck_stats_interface.h"
#include "../client/network/interfaces/tapped_out_interface.h"
+#include "../deck_editor/deck_state_manager.h"
#include "../interface/card_picture_loader/card_picture_loader.h"
#include "../interface/pixel_map_generator.h"
#include "../interface/widgets/dialogs/dlg_load_deck.h"
@@ -52,7 +53,7 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
{
setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
- historyManager = new DeckListHistoryManager(this);
+ deckStateManager = new DeckStateManager(this);
databaseDisplayDockWidget = new DeckEditorDatabaseDisplayWidget(this);
deckDockWidget = new DeckEditorDeckDockWidget(this);
@@ -64,14 +65,8 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
});
// Connect deck signals to this tab
- connect(deckDockWidget, &DeckEditorDeckDockWidget::deckChanged, this, &AbstractTabDeckEditor::onDeckChanged);
- connect(deckDockWidget, &DeckEditorDeckDockWidget::deckModified, this, &AbstractTabDeckEditor::onDeckModified);
- connect(deckDockWidget, &DeckEditorDeckDockWidget::requestDeckHistorySave, this,
- &AbstractTabDeckEditor::onDeckHistorySaveRequested);
- connect(deckDockWidget, &DeckEditorDeckDockWidget::requestDeckHistoryClear, this,
- &AbstractTabDeckEditor::onDeckHistoryClearRequested);
- connect(deckDockWidget, &DeckEditorDeckDockWidget::cardChanged, this, &AbstractTabDeckEditor::updateCard);
- connect(this, &AbstractTabDeckEditor::decrementCard, deckDockWidget, &DeckEditorDeckDockWidget::actDecrementCard);
+ connect(deckStateManager, &DeckStateManager::isModifiedChanged, this, &AbstractTabDeckEditor::onDeckModified);
+ connect(deckDockWidget, &DeckEditorDeckDockWidget::selectedCardChanged, this, &AbstractTabDeckEditor::updateCard);
// Connect database display signals to this tab
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this,
@@ -101,13 +96,12 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
void AbstractTabDeckEditor::updateCard(const ExactCard &card)
{
cardInfoDockWidget->updateCard(card);
- printingSelectorDockWidget->printingSelector->setCard(card.getCardPtr(), DECK_ZONE_MAIN);
+ printingSelectorDockWidget->printingSelector->setCard(card.getCardPtr());
}
/** @brief Placeholder: called when the deck changes. */
void AbstractTabDeckEditor::onDeckChanged()
{
- historyManager->clear();
}
/**
@@ -115,24 +109,8 @@ void AbstractTabDeckEditor::onDeckChanged()
*/
void AbstractTabDeckEditor::onDeckModified()
{
- setModified(!isBlankNewDeck());
- deckMenu->setSaveStatus(!isBlankNewDeck());
-}
-
-/**
- * @brief Marks the tab as modified and updates the save menu status.
- */
-void AbstractTabDeckEditor::onDeckHistorySaveRequested(const QString &modificationReason)
-{
- historyManager->save(deckDockWidget->getDeckList()->createMemento(modificationReason));
-}
-
-/**
- * @brief Marks the tab as modified and updates the save menu status.
- */
-void AbstractTabDeckEditor::onDeckHistoryClearRequested()
-{
- historyManager->clear();
+ deckMenu->setSaveStatus(!deckStateManager->isBlankNewDeck());
+ emit tabTextChanged(this, getTabText());
}
/**
@@ -140,23 +118,9 @@ void AbstractTabDeckEditor::onDeckHistoryClearRequested()
* @param card Card to add.
* @param zoneName Zone to add the card to.
*/
-void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, QString zoneName)
+void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, const QString &zoneName)
{
- if (!card)
- return;
-
- if (card.getInfo().getIsToken())
- zoneName = DECK_ZONE_TOKENS;
-
- onDeckHistorySaveRequested(QString(tr("Added (%1): %2 (%3) %4"))
- .arg(zoneName, card.getName(), card.getPrinting().getSet()->getCorrectedShortName(),
- card.getPrinting().getProperty("num")));
-
- QModelIndex newCardIndex = deckDockWidget->deckModel->addCard(card, zoneName);
- deckDockWidget->expandAll();
- deckDockWidget->deckView->clearSelection();
- deckDockWidget->deckView->setCurrentIndex(newCardIndex);
- setModified(true);
+ deckStateManager->addCard(card, zoneName);
databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length());
}
@@ -184,91 +148,41 @@ void AbstractTabDeckEditor::actAddCardToSideboard(const ExactCard &card)
/** @brief Decrements a card from the main deck. */
void AbstractTabDeckEditor::actDecrementCard(const ExactCard &card)
{
- emit decrementCard(card, DECK_ZONE_MAIN);
+ deckStateManager->decrementCard(card, DECK_ZONE_MAIN);
}
/** @brief Decrements a card from the sideboard. */
void AbstractTabDeckEditor::actDecrementCardFromSideboard(const ExactCard &card)
{
- emit decrementCard(card, DECK_ZONE_SIDE);
-}
-
-/**
- * @brief Swaps a card in a deck zone.
- * @param card Card to swap.
- * @param zoneName Zone to swap in.
- */
-void AbstractTabDeckEditor::actSwapCard(const ExactCard &card, const QString &zoneName)
-{
- QString providerId = card.getPrinting().getUuid();
- QString collectorNumber = card.getPrinting().getProperty("num");
-
- QModelIndex foundCard = deckDockWidget->deckModel->findCard(card.getName(), zoneName, providerId, collectorNumber);
- if (!foundCard.isValid()) {
- foundCard = deckDockWidget->deckModel->findCard(card.getName(), zoneName);
- }
-
- deckDockWidget->swapCard(foundCard);
+ deckStateManager->decrementCard(card, DECK_ZONE_SIDE);
}
/**
* @brief Opens a deck in this tab.
- * @param deck DeckLoader object (takes ownership).
+ * @param deck The deck
*/
-void AbstractTabDeckEditor::openDeck(DeckLoader *deck)
+void AbstractTabDeckEditor::openDeck(const LoadedDeck &deck)
{
setDeck(deck);
- if (!deck->getLastLoadInfo().fileName.isEmpty()) {
- SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(deck->getLastLoadInfo().fileName);
+ if (!deck.lastLoadInfo.fileName.isEmpty()) {
+ SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(deck.lastLoadInfo.fileName);
}
}
/**
* @brief Sets the currently active deck.
- * @param _deck DeckLoader object.
+ * @param _deck The deck
*/
-void AbstractTabDeckEditor::setDeck(DeckLoader *_deck)
+void AbstractTabDeckEditor::setDeck(const LoadedDeck &_deck)
{
- deckDockWidget->setDeck(_deck);
- CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(getDeckList()->getCardRefList()));
- setModified(false);
+ deckStateManager->replaceDeck(_deck);
+ CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(_deck.deckList.getCardRefList()));
aDeckDockVisible->setChecked(true);
deckDockWidget->setVisible(aDeckDockVisible->isChecked());
}
-/** @brief Returns the currently loaded deck. */
-DeckLoader *AbstractTabDeckEditor::getDeckLoader() const
-{
- return deckDockWidget->getDeckLoader();
-}
-
-/** @brief Returns the currently loaded deck list. */
-DeckList *AbstractTabDeckEditor::getDeckList() const
-{
- return deckDockWidget->getDeckList();
-}
-
-/**
- * @brief Sets the modified state of the tab.
- * @param _modified True if tab is modified, false otherwise.
- */
-void AbstractTabDeckEditor::setModified(bool _modified)
-{
- modified = _modified;
- emit tabTextChanged(this, getTabText());
-}
-
-/**
- * @brief Returns true if the tab is a blank newly created deck.
- */
-bool AbstractTabDeckEditor::isBlankNewDeck() const
-{
- DeckLoader *deck = deckDockWidget->getDeckLoader();
- return !modified && deck->getDeckList()->isBlankDeck() && deck->hasNotBeenLoaded();
-}
-
/** @brief Creates a new deck. Handles opening in new tab if needed. */
void AbstractTabDeckEditor::actNewDeck()
{
@@ -277,7 +191,7 @@ void AbstractTabDeckEditor::actNewDeck()
return;
if (deckOpenLocation == NEW_TAB) {
- emit openDeckEditor(nullptr);
+ emit openDeckEditor(LoadedDeck());
return;
}
@@ -287,9 +201,8 @@ void AbstractTabDeckEditor::actNewDeck()
/** @brief Clears the current deck and resets modified flag. */
void AbstractTabDeckEditor::cleanDeckAndResetModified()
{
+ deckStateManager->clearDeck();
deckMenu->setSaveStatus(false);
- deckDockWidget->cleanDeck();
- setModified(false);
}
/**
@@ -300,13 +213,13 @@ void AbstractTabDeckEditor::cleanDeckAndResetModified()
AbstractTabDeckEditor::DeckOpenLocation AbstractTabDeckEditor::confirmOpen(const bool openInSameTabIfBlank)
{
if (SettingsCache::instance().getOpenDeckInNewTab()) {
- if (openInSameTabIfBlank && isBlankNewDeck())
+ if (openInSameTabIfBlank && deckStateManager->isBlankNewDeck())
return SAME_TAB;
else
return NEW_TAB;
}
- if (!modified)
+ if (!deckStateManager->isModified())
return SAME_TAB;
tabSupervisor->setCurrentWidget(this);
@@ -357,7 +270,6 @@ void AbstractTabDeckEditor::actLoadDeck()
QString fileName = dialog.selectedFiles().at(0);
openDeckFromFile(fileName, deckOpenLocation);
- deckDockWidget->updateBannerCardComboBox();
}
/**
@@ -382,17 +294,15 @@ void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLo
{
DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(fileName);
- auto *l = new DeckLoader(this);
- if (l->loadFromFile(fileName, fmt, true)) {
+ auto l = DeckLoader(this);
+ if (l.loadFromFile(fileName, fmt, true)) {
if (deckOpenLocation == NEW_TAB) {
- emit openDeckEditor(l);
- l->deleteLater();
+ emit openDeckEditor(l.getDeck());
} else {
deckMenu->setSaveStatus(false);
- openDeck(l);
+ openDeck(l.getDeck());
}
} else {
- l->deleteLater();
QMessageBox::critical(this, tr("Error"), tr("Could not open deck at %1").arg(fileName));
}
deckMenu->setSaveStatus(true);
@@ -405,16 +315,16 @@ void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLo
*/
bool AbstractTabDeckEditor::actSaveDeck()
{
- DeckLoader *const deck = getDeckLoader();
- if (deck->getLastLoadInfo().remoteDeckId != LoadedDeck::LoadInfo::NON_REMOTE_ID) {
- QString deckString = deck->getDeckList()->writeToString_Native();
+ const auto loadedDeck = deckStateManager->toLoadedDeck();
+ if (loadedDeck.lastLoadInfo.remoteDeckId != LoadedDeck::LoadInfo::NON_REMOTE_ID) {
+ QString deckString = loadedDeck.deckList.writeToString_Native();
if (deckString.length() > MAX_FILE_LENGTH) {
QMessageBox::critical(this, tr("Error"), tr("Could not save remote deck"));
return false;
}
Command_DeckUpload cmd;
- cmd.set_deck_id(static_cast(deck->getLastLoadInfo().remoteDeckId));
+ cmd.set_deck_id(static_cast(loadedDeck.lastLoadInfo.remoteDeckId));
cmd.set_deck_list(deckString.toStdString());
PendingCommand *pend = AbstractClient::prepareSessionCommand(cmd);
@@ -422,10 +332,14 @@ bool AbstractTabDeckEditor::actSaveDeck()
tabSupervisor->getClient()->sendCommand(pend);
return true;
- } else if (deck->getLastLoadInfo().fileName.isEmpty())
+ }
+ if (loadedDeck.lastLoadInfo.fileName.isEmpty())
return actSaveDeckAs();
- else if (deck->saveToFile(deck->getLastLoadInfo().fileName, deck->getLastLoadInfo().fileFormat)) {
- setModified(false);
+
+ auto deckLoader = DeckLoader(this);
+ deckLoader.setDeck(loadedDeck);
+ if (deckLoader.saveToFile(loadedDeck.lastLoadInfo.fileName, loadedDeck.lastLoadInfo.fileFormat)) {
+ deckStateManager->setModified(false);
return true;
}
@@ -441,12 +355,14 @@ bool AbstractTabDeckEditor::actSaveDeck()
*/
bool AbstractTabDeckEditor::actSaveDeckAs()
{
+ LoadedDeck loadedDeck = deckStateManager->toLoadedDeck();
+
QFileDialog dialog(this, tr("Save deck"));
dialog.setDirectory(SettingsCache::instance().getDeckPath());
dialog.setAcceptMode(QFileDialog::AcceptSave);
dialog.setDefaultSuffix("cod");
dialog.setNameFilters(DeckLoader::FILE_NAME_FILTERS);
- dialog.selectFile(getDeckList()->getName().trimmed());
+ dialog.selectFile(loadedDeck.deckList.getName().trimmed());
if (!dialog.exec())
return false;
@@ -454,14 +370,18 @@ bool AbstractTabDeckEditor::actSaveDeckAs()
QString fileName = dialog.selectedFiles().at(0);
DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(fileName);
- if (!getDeckLoader()->saveToFile(fileName, fmt)) {
+ DeckLoader deckLoader = DeckLoader(this);
+ deckLoader.setDeck(loadedDeck);
+ if (!deckLoader.saveToFile(fileName, fmt)) {
QMessageBox::critical(
this, tr("Error"),
tr("The deck could not be saved.\nPlease check that the directory is writable and try again."));
return false;
}
- setModified(false);
+ deckStateManager->setLastLoadInfo({.fileName = fileName, .fileFormat = fmt});
+
+ deckStateManager->setModified(false);
SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName);
return true;
}
@@ -475,7 +395,7 @@ void AbstractTabDeckEditor::saveDeckRemoteFinished(const Response &response)
if (response.response_code() != Response::RespOk)
QMessageBox::critical(this, tr("Error"), tr("The deck could not be saved."));
else
- setModified(false);
+ deckStateManager->setModified(false);
}
/**
@@ -493,10 +413,10 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard()
return;
if (deckOpenLocation == NEW_TAB) {
- emit openDeckEditor(dlg.getDeckList());
+ emit openDeckEditor({.deckList = dlg.getDeckList()});
} else {
- setDeck(dlg.getDeckList());
- setModified(true);
+ setDeck({.deckList = dlg.getDeckList()});
+ deckStateManager->setModified(true);
}
deckMenu->setSaveStatus(true);
@@ -508,12 +428,13 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard()
*/
void AbstractTabDeckEditor::editDeckInClipboard(bool annotated)
{
- DlgEditDeckInClipboard dlg(getDeckLoader(), annotated, this);
+ LoadedDeck loadedDeck = deckStateManager->toLoadedDeck();
+ DlgEditDeckInClipboard dlg(loadedDeck.deckList, annotated, this);
if (!dlg.exec())
return;
- setDeck(dlg.getDeckList());
- setModified(true);
+ setDeck({dlg.getDeckList(), loadedDeck.lastLoadInfo});
+ deckStateManager->setModified(true);
deckMenu->setSaveStatus(true);
}
@@ -532,25 +453,25 @@ void AbstractTabDeckEditor::actEditDeckInClipboardRaw()
/** @brief Saves deck to clipboard with set info and annotation. */
void AbstractTabDeckEditor::actSaveDeckToClipboard()
{
- DeckLoader::saveToClipboard(getDeckList(), true, true);
+ DeckLoader::saveToClipboard(deckStateManager->getDeckList(), true, true);
}
/** @brief Saves deck to clipboard with annotation, without set info. */
void AbstractTabDeckEditor::actSaveDeckToClipboardNoSetInfo()
{
- DeckLoader::saveToClipboard(getDeckList(), true, false);
+ DeckLoader::saveToClipboard(deckStateManager->getDeckList(), true, false);
}
/** @brief Saves deck to clipboard without annotations, with set info. */
void AbstractTabDeckEditor::actSaveDeckToClipboardRaw()
{
- DeckLoader::saveToClipboard(getDeckList(), false, true);
+ DeckLoader::saveToClipboard(deckStateManager->getDeckList(), false, true);
}
/** @brief Saves deck to clipboard without annotations or set info. */
void AbstractTabDeckEditor::actSaveDeckToClipboardRawNoSetInfo()
{
- DeckLoader::saveToClipboard(getDeckList(), false, false);
+ DeckLoader::saveToClipboard(deckStateManager->getDeckList(), false, false);
}
/** @brief Prints the deck using a QPrintPreviewDialog. */
@@ -558,7 +479,7 @@ void AbstractTabDeckEditor::actPrintDeck()
{
auto *dlg = new QPrintPreviewDialog(this);
connect(dlg, &QPrintPreviewDialog::paintRequested, this,
- [this](QPrinter *printer) { DeckLoader::printDeckList(printer, getDeckList()); });
+ [this](QPrinter *printer) { DeckLoader::printDeckList(printer, deckStateManager->getDeckList()); });
dlg->exec();
}
@@ -576,10 +497,10 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite()
return;
if (deckOpenLocation == NEW_TAB) {
- emit openDeckEditor(dlg.getDeck());
+ emit openDeckEditor({.deckList = dlg.getDeck()});
} else {
- setDeck(dlg.getDeck());
- setModified(true);
+ setDeck({.deckList = dlg.getDeck()});
+ deckStateManager->setModified(true);
}
deckMenu->setSaveStatus(true);
@@ -591,26 +512,21 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite()
*/
void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite 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.
- QMessageBox::critical(this, tr("Error"), tr("There are no cards in your deck to be exported"));
- return;
- }
-
- // Encode the string recieved from the model to make sure all characters are encoded.
- // first we put it into a qurl object
- QUrl decklistUrl = QUrl(decklistUrlString);
- // we get the correctly encoded url.
- decklistUrlString = decklistUrl.toEncoded();
- // We open the url in the user's default browser
- QDesktopServices::openUrl(decklistUrlString);
- } else {
- // if there's no deck loader object, return an error
- QMessageBox::critical(this, tr("Error"), tr("No deck was selected to be exported."));
+ QString decklistUrlString = DeckLoader::exportDeckToDecklist(deckStateManager->getDeckList(), website);
+ // Check to make sure the string isn't empty.
+ if (decklistUrlString.isEmpty()) {
+ // Show an error if the deck is empty, and return.
+ QMessageBox::critical(this, tr("Error"), tr("There are no cards in your deck to be exported"));
+ return;
}
+
+ // Encode the string recieved from the model to make sure all characters are encoded.
+ // first we put it into a qurl object
+ QUrl decklistUrl = QUrl(decklistUrlString);
+ // we get the correctly encoded url.
+ decklistUrlString = decklistUrl.toEncoded();
+ // We open the url in the user's default browser
+ QDesktopServices::openUrl(decklistUrlString);
}
/** @brief Exports deck to www.decklist.org. */
@@ -629,14 +545,14 @@ void AbstractTabDeckEditor::actExportDeckDecklistXyz()
void AbstractTabDeckEditor::actAnalyzeDeckDeckstats()
{
auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this);
- interface->analyzeDeck(getDeckList());
+ interface->analyzeDeck(deckStateManager->getDeckList());
}
/** @brief Analyzes the deck using TappedOut. */
void AbstractTabDeckEditor::actAnalyzeDeckTappedout()
{
auto *interface = new TappedOutInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this);
- interface->analyzeDeck(getDeckList());
+ interface->analyzeDeck(deckStateManager->getDeckList());
}
/** @brief Applies a new filter tree to the database display. */
@@ -695,7 +611,7 @@ bool AbstractTabDeckEditor::eventFilter(QObject *o, QEvent *e)
/** @brief Shows a confirmation dialog before closing. */
bool AbstractTabDeckEditor::confirmClose()
{
- if (modified) {
+ if (deckStateManager->isModified()) {
tabSupervisor->setCurrentWidget(this);
int ret = createSaveConfirmationWindow()->exec();
if (ret == QMessageBox::Save)
diff --git a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h
index 8544e6c00..69b2b4a1c 100644
--- a/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h
+++ b/cockatrice/src/interface/widgets/tabs/abstract_tab_deck_editor.h
@@ -19,6 +19,7 @@
#include
+class DeckStateManager;
class CardDatabaseModel;
class CardDatabaseDisplayModel;
@@ -77,7 +78,6 @@ class QAction;
*
* - actAddCard(const ExactCard &card) — Adds a card to the deck.
* - actDecrementCard(const ExactCard &card) — Removes a single instance of a card from the deck.
- * - actSwapCard(const ExactCard &card, const QString &zone) — Swaps a card between zones.
* - actRemoveCard() — Removes the currently selected card from the deck.
* - actSaveDeckAs() — Performs a "Save As" action for the deck.
* - updateCard(const ExactCard &card) — Updates the currently displayed card info in the dock.
@@ -114,34 +114,17 @@ public:
virtual void retranslateUi() override = 0;
/** @brief Opens a deck in this tab.
- * @param deck Pointer to a DeckLoader object.
+ * @param deck The deck to open
*/
- void openDeck(DeckLoader *deck);
-
- /** @brief Returns the currently active deck loader. */
- DeckLoader *getDeckLoader() const;
-
- /** @brief Returns the currently active deck list. */
- DeckList *getDeckList() const;
-
- /** @brief Sets the modified state of the tab.
- * @param _windowModified Whether the tab is modified.
- */
- void setModified(bool _windowModified);
+ void openDeck(const LoadedDeck &deck);
DeckEditorDeckDockWidget *getDeckDockWidget() const
{
return deckDockWidget;
}
- DeckListHistoryManager *getHistoryManager() const
- {
- return historyManager;
- }
-
- DeckListHistoryManager *historyManager;
-
// UI Elements
+ DeckStateManager *deckStateManager;
DeckEditorMenu *deckMenu; ///< Menu for deck operations
DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget; ///< Database dock
DeckEditorCardInfoDockWidget *cardInfoDockWidget; ///< Card info dock
@@ -156,14 +139,6 @@ public slots:
/** @brief Called when the deck is modified. */
virtual void onDeckModified();
- /** @brief Called when a widget is about to modify the state of the DeckList.
- * @param modificationReason The reason for the state modification
- */
- virtual void onDeckHistorySaveRequested(const QString &modificationReason);
-
- /** @brief Called when a widget would like to clear the history. */
- virtual void onDeckHistoryClearRequested();
-
/** @brief Updates the card info panel.
* @param card The card to display.
*/
@@ -198,14 +173,11 @@ public slots:
signals:
/** @brief Emitted when a deck should be opened in a new editor tab. */
- void openDeckEditor(DeckLoader *deckLoader);
+ void openDeckEditor(const LoadedDeck &deck);
/** @brief Emitted before the tab is closed. */
void deckEditorClosing(AbstractTabDeckEditor *tab);
- /** @brief Emitted when a card should be decremented. */
- void decrementCard(const ExactCard &card, QString zoneName);
-
protected slots:
/** @brief Starts a new deck in this tab. */
virtual void actNewDeck();
@@ -286,7 +258,7 @@ private:
/** @brief Sets the deck for this tab.
* @param _deck The deck object.
*/
- virtual void setDeck(DeckLoader *_deck);
+ virtual void setDeck(const LoadedDeck &_deck);
/** @brief Helper for editing decks from the clipboard. */
void editDeckInClipboard(bool annotated);
@@ -316,14 +288,8 @@ protected:
*/
QMessageBox *createSaveConfirmationWindow();
- /** @brief Returns true if the tab is a blank newly created deck. */
- bool isBlankNewDeck() const;
-
/** @brief Helper function to add a card to a specific deck zone. */
- void addCardHelper(const ExactCard &card, QString zoneName);
-
- /** @brief Swaps a card in the deck view. */
- void actSwapCard(const ExactCard &card, const QString &zoneName);
+ void addCardHelper(const ExactCard &card, const QString &zoneName);
/** @brief Opens a deck from a file. */
virtual void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation);
@@ -334,8 +300,6 @@ protected:
QAction *aResetLayout;
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating;
QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating;
-
- bool modified = false; ///< Whether the deck/tab has unsaved changes
};
#endif // TAB_GENERIC_DECK_EDITOR_H
diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h
new file mode 100644
index 000000000..ac1b66e14
--- /dev/null
+++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h
@@ -0,0 +1,227 @@
+#ifndef COCKATRICE_ARCHIDEKT_FORMATS_H
+#define COCKATRICE_ARCHIDEKT_FORMATS_H
+#include
+#include
+
+namespace ArchidektFormats
+{
+enum class DeckFormat
+{
+ Standard = 0,
+ Modern = 1,
+ Commander = 2,
+ Legacy = 3,
+ Vintage = 4,
+ Pauper = 5,
+ Custom = 6,
+ Frontier = 7,
+ FutureStandard = 8,
+ PennyDreadful = 9,
+ Commander1v1 = 10,
+ DualCommander = 11,
+ Brawl = 12,
+
+ // Values outside Archidekt range
+ Alchemy = 1000,
+ Historic = 1001,
+ Gladiator = 1002,
+ Oathbreaker = 1003,
+ OldSchool = 1004,
+ PauperCommander = 1005,
+ Pioneer = 1006,
+ PreDH = 1007,
+ Premodern = 1008,
+ StandardBrawl = 1009,
+ Timeless = 1010,
+ Unknown = 1011
+};
+
+inline static QString formatToApiName(DeckFormat format)
+{
+ switch (format) {
+ case DeckFormat::Standard:
+ return "Standard";
+ case DeckFormat::Modern:
+ return "Modern";
+ case DeckFormat::Commander:
+ return "Commander";
+ case DeckFormat::Legacy:
+ return "Legacy";
+ case DeckFormat::Vintage:
+ return "Vintage";
+ case DeckFormat::Pauper:
+ return "Pauper";
+ case DeckFormat::Custom:
+ return "Custom";
+ case DeckFormat::Frontier:
+ return "Frontier";
+ case DeckFormat::FutureStandard:
+ return "Future Std";
+ case DeckFormat::PennyDreadful:
+ return "Penny Dreadful";
+ case DeckFormat::Commander1v1:
+ return "1v1 Commander";
+ case DeckFormat::DualCommander:
+ return "Dual Commander";
+ case DeckFormat::Brawl:
+ return "Brawl";
+
+ case DeckFormat::Alchemy:
+ return "Alchemy";
+ case DeckFormat::Historic:
+ return "Historic";
+ case DeckFormat::Gladiator:
+ return "Gladiator";
+ case DeckFormat::Oathbreaker:
+ return "Oathbreaker";
+ case DeckFormat::OldSchool:
+ return "Old School";
+ case DeckFormat::PauperCommander:
+ return "Pauper Commander";
+ case DeckFormat::Pioneer:
+ return "Pioneer";
+ case DeckFormat::PreDH:
+ return "PreDH";
+ case DeckFormat::Premodern:
+ return "Premodern";
+ case DeckFormat::StandardBrawl:
+ return "Standard Brawl";
+ case DeckFormat::Timeless:
+ return "Timeless";
+
+ default:
+ return "Unknown";
+ }
+}
+
+inline static DeckFormat apiNameToFormat(const QString &name)
+{
+ const QString n = name.trimmed();
+
+ if (n.compare("Standard", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Standard;
+ if (n.compare("Modern", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Modern;
+ if (n.compare("Commander", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Commander;
+ if (n.compare("Legacy", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Legacy;
+ if (n.compare("Vintage", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Vintage;
+ if (n.compare("Pauper", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Pauper;
+ if (n.compare("Custom", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Custom;
+ if (n.compare("Frontier", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Frontier;
+ if (n.compare("Future Std", Qt::CaseInsensitive) == 0)
+ return DeckFormat::FutureStandard;
+ if (n.compare("Penny Dreadful", Qt::CaseInsensitive) == 0)
+ return DeckFormat::PennyDreadful;
+ if (n.compare("1v1 Commander", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Commander1v1;
+ if (n.compare("Dual Commander", Qt::CaseInsensitive) == 0)
+ return DeckFormat::DualCommander;
+ if (n.compare("Brawl", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Brawl;
+
+ if (n.compare("Alchemy", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Alchemy;
+ if (n.compare("Historic", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Historic;
+ if (n.compare("Gladiator", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Gladiator;
+ if (n.compare("Oathbreaker", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Oathbreaker;
+ if (n.compare("Old School", Qt::CaseInsensitive) == 0)
+ return DeckFormat::OldSchool;
+ if (n.compare("Pauper Commander", Qt::CaseInsensitive) == 0)
+ return DeckFormat::PauperCommander;
+ if (n.compare("Pioneer", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Pioneer;
+ if (n.compare("PreDH", Qt::CaseInsensitive) == 0)
+ return DeckFormat::PreDH;
+ if (n.compare("Premodern", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Premodern;
+ if (n.compare("Standard Brawl", Qt::CaseInsensitive) == 0)
+ return DeckFormat::StandardBrawl;
+ if (n.compare("Timeless", Qt::CaseInsensitive) == 0)
+ return DeckFormat::Timeless;
+
+ return DeckFormat::Unknown;
+}
+
+inline static QString formatToCockatriceName(DeckFormat format)
+{
+ switch (format) {
+ case DeckFormat::Standard:
+ return "standard";
+ case DeckFormat::Modern:
+ return "modern";
+ case DeckFormat::Commander:
+ return "commander";
+ case DeckFormat::Legacy:
+ return "legacy";
+ case DeckFormat::Vintage:
+ return "vintage";
+ case DeckFormat::Pauper:
+ return "pauper";
+ case DeckFormat::Brawl:
+ return "brawl";
+ case DeckFormat::PennyDreadful:
+ return "penny";
+ case DeckFormat::FutureStandard:
+ return "future";
+ case DeckFormat::Commander1v1:
+ return "duel";
+ case DeckFormat::DualCommander:
+ return "duel";
+ case DeckFormat::Alchemy:
+ return "alchemy";
+ case DeckFormat::Historic:
+ return "historic";
+ case DeckFormat::Gladiator:
+ return "gladiator";
+ case DeckFormat::Oathbreaker:
+ return "oathbreaker";
+ case DeckFormat::OldSchool:
+ return "oldschool";
+ case DeckFormat::PauperCommander:
+ return "paupercommander";
+ case DeckFormat::Pioneer:
+ return "pioneer";
+ case DeckFormat::PreDH:
+ return "predh";
+ case DeckFormat::Premodern:
+ return "premodern";
+ case DeckFormat::StandardBrawl:
+ return "standardbrawl";
+ case DeckFormat::Timeless:
+ return "timeless";
+
+ default:
+ return {};
+ }
+}
+
+inline static DeckFormat cockatriceNameToFormat(const QString &apiName)
+{
+ static const QHash map = {
+ {"standard", DeckFormat::Standard}, {"modern", DeckFormat::Modern},
+ {"commander", DeckFormat::Commander}, {"legacy", DeckFormat::Legacy},
+ {"vintage", DeckFormat::Vintage}, {"pauper", DeckFormat::Pauper},
+ {"brawl", DeckFormat::Brawl}, {"penny", DeckFormat::PennyDreadful},
+ {"future", DeckFormat::FutureStandard}, {"duel", DeckFormat::Commander1v1},
+ {"alchemy", DeckFormat::Alchemy}, {"historic", DeckFormat::Historic},
+ {"gladiator", DeckFormat::Gladiator}, {"oathbreaker", DeckFormat::Oathbreaker},
+ {"oldschool", DeckFormat::OldSchool}, {"paupercommander", DeckFormat::PauperCommander},
+ {"pioneer", DeckFormat::Pioneer}, {"predh", DeckFormat::PreDH},
+ {"premodern", DeckFormat::Premodern}, {"standardbrawl", DeckFormat::StandardBrawl},
+ {"timeless", DeckFormat::Timeless}};
+
+ return map.value(apiName.toLower(), DeckFormat::Unknown);
+}
+
+} // namespace ArchidektFormats
+
+#endif // COCKATRICE_ARCHIDEKT_FORMATS_H
diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp
index 909c4d9eb..5b879f8ae 100644
--- a/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp
@@ -21,16 +21,16 @@ void ArchidektApiResponseCard::fromJson(const QJsonObject &json)
edition.fromJson(json.value("edition").toObject());
flavor = json.value("flavor").toString();
- // TODO but not really important
- // games = {""};
- // options = {""};
+ //! \todo but not really important
+ //! \todo games = {""};
+ //! \todo options = {""};
scryfallImageHash = json.value("scryfallImageHash").toString();
oracleCard = json.value("oracleCard").toObject();
owned = json.value("owned").toInt();
pinnedStatus = json.value("pinnedStatus").toInt();
rarity = json.value("rarity").toString();
- // TODO but not really important
- // globalCategories = {""};
+ //! \todo but not really important
+ //! \todo globalCategories = {""};
}
void ArchidektApiResponseCard::debugPrint() const
diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.h b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.h
index 8cb81cac3..fce437751 100644
--- a/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.h
+++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.h
@@ -39,6 +39,11 @@ public:
return name;
};
+ int getDeckFormat() const
+ {
+ return deckFormat;
+ }
+
private:
int id;
QString name;
diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp
index 63d732c9a..3a2468368 100644
--- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp
@@ -6,6 +6,7 @@
#include "../../../../cards/card_size_widget.h"
#include "../../../../cards/deck_card_zone_display_widget.h"
#include "../../../../visual_deck_editor/visual_deck_display_options_widget.h"
+#include "../api_response/archidekt_formats.h"
#include "../api_response/deck/archidekt_api_response_deck.h"
#include
@@ -67,11 +68,12 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi
model = new DeckListModel(this);
connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset);
- model->getDeckList()->loadFromStream_Plain(deckStream, false);
- model->getDeckList()->forEachCard(CardNodeFunction::ResolveProviderId());
+ auto decklist = QSharedPointer(new DeckList);
+ decklist->loadFromStream_Plain(deckStream, false);
+ model->setDeckList(decklist);
- model->rebuildTree();
+ model->forEachCard(CardNodeFunction::ResolveProviderId());
retranslateUi();
}
@@ -84,17 +86,19 @@ void ArchidektApiResponseDeckDisplayWidget::retranslateUi()
void ArchidektApiResponseDeckDisplayWidget::onGroupCriteriaChange(const QString &activeGroupCriteria)
{
model->setActiveGroupCriteria(DeckListModelGroupCriteria::fromString(activeGroupCriteria));
- model->sort(1, Qt::AscendingOrder);
+ model->sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
}
void ArchidektApiResponseDeckDisplayWidget::actOpenInDeckEditor()
{
- auto loader = new DeckLoader(this);
- loader->getDeckList()->loadFromString_Native(model->getDeckList()->writeToString_Native());
+ DeckList deckList(*model->getDeckList());
+ deckList.setName(response.getDeckName());
+ deckList.setGameFormat(
+ ArchidektFormats::formatToCockatriceName(ArchidektFormats::DeckFormat(response.getDeckFormat() - 1)));
- loader->getDeckList()->setName(response.getDeckName());
+ LoadedDeck loadedDeck = {deckList, {}};
- emit openInDeckEditor(loader);
+ emit openInDeckEditor(loadedDeck);
}
void ArchidektApiResponseDeckDisplayWidget::clearAllDisplayWidgets()
@@ -119,7 +123,7 @@ void ArchidektApiResponseDeckDisplayWidget::constructZoneWidgetsFromDeckListMode
QSortFilterProxyModel proxy;
proxy.setSourceModel(model);
proxy.setSortRole(Qt::EditRole);
- proxy.sort(1, Qt::AscendingOrder);
+ proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
for (int i = 0; i < proxy.rowCount(); ++i) {
QModelIndex proxyIndex = proxy.index(i, 0);
@@ -132,10 +136,12 @@ void ArchidektApiResponseDeckDisplayWidget::constructZoneWidgetsFromDeckListMode
continue;
}
+ QString zoneName =
+ persistent.sibling(persistent.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
+
DeckCardZoneDisplayWidget *zoneDisplayWidget =
- new DeckCardZoneDisplayWidget(zoneContainer, model, nullptr, persistent,
- model->data(persistent.sibling(persistent.row(), 1), Qt::EditRole).toString(),
- "maintype", {"name"}, DisplayType::Overlap, 20, 10, cardSizeSlider);
+ new DeckCardZoneDisplayWidget(zoneContainer, model, nullptr, persistent, zoneName, "maintype", {"name"},
+ DisplayType::Overlap, 20, 10, cardSizeSlider);
connect(displayOptionsWidget, &VisualDeckDisplayOptionsWidget::sortCriteriaChanged, zoneDisplayWidget,
&DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged);
diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.h b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.h
index 3d624d293..58aca429e 100644
--- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.h
+++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.h
@@ -31,7 +31,7 @@
*
* ### Signals
* - `requestNavigation(QString url)` — triggered when navigation to a deck URL is requested.
- * - `openInDeckEditor(DeckLoader *loader)` — emitted when the user chooses to open the deck
+ * - `openInDeckEditor(const LoadedDeck &deck)` — emitted when the user chooses to open the deck
* in the deck editor.
*
* ### Features
@@ -52,9 +52,9 @@ signals:
/**
* @brief Emitted when the deck should be opened in the deck editor.
- * @param loader Initialized DeckLoader containing the deck data.
+ * @param deck LoadedDeck containing the deck data.
*/
- void openInDeckEditor(DeckLoader *loader);
+ void openInDeckEditor(const LoadedDeck &deck);
public:
/**
@@ -75,7 +75,7 @@ public:
void retranslateUi();
/**
- * @brief Opens the deck in the deck editor via DeckLoader.
+ * @brief Opens the deck in the deck editor.
*/
void actOpenInDeckEditor();
diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp
index 7a838d1ff..2998a03bc 100644
--- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp
@@ -16,7 +16,7 @@
#define ARCHIDEKT_DEFAULT_IMAGE "https://storage.googleapis.com/topdekt-user/images/archidekt_deck_card_shadow.jpg"
-QString timeAgo(const QString ×tamp)
+static QString timeAgo(const QString ×tamp)
{
QDateTime dt = QDateTime::fromString(timestamp, Qt::ISODate);
diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp
index 5747ce90d..8746093c7 100644
--- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp
@@ -16,10 +16,7 @@ ArchidektApiResponseDeckListingsDisplayWidget::ArchidektApiResponseDeckListingsD
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
imageNetworkManager = new QNetworkAccessManager(this);
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
imageNetworkManager->setTransferTimeout(); // Use Qt's default timeout
-#endif
-
imageNetworkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
// Add widgets for deck listings
diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp
index e6615fa7b..3769cd9a2 100644
--- a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp
@@ -27,10 +27,7 @@
TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
{
networkManager = new QNetworkAccessManager(this);
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
networkManager->setTransferTimeout(); // Use Qt's default timeout
-#endif
-
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(processApiJson(QNetworkReply *)));
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
index ceafb719b..a744a9b14 100644
--- a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
@@ -14,10 +14,8 @@ void EdhrecDeckApiResponse::fromJson(const QJsonArray &json)
deckList += cardlistValue.toString() + "\n";
}
- deckLoader = new DeckLoader(nullptr);
-
QTextStream stream(&deckList);
- deckLoader->getDeckList()->loadFromStream_Plain(stream, true);
+ deck.loadFromStream_Plain(stream, true);
}
void EdhrecDeckApiResponse::debugPrint() const
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h
index 96205e149..cd4f9d99c 100644
--- a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.h
@@ -21,7 +21,7 @@ public:
// Debug method for logging
void debugPrint() const;
- DeckLoader *deckLoader;
+ DeckList deck;
};
#endif // EDHREC_DECK_API_RESPONSE_H
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
index 26ade96dd..c01c7fa43 100644
--- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
@@ -19,17 +19,17 @@ EdhrecApiResponseCardDetailsDisplayWidget::EdhrecApiResponseCardDetailsDisplayWi
nameLabel = new QLabel(this);
nameLabel->setText(toDisplay.name);
nameLabel->setAlignment(Qt::AlignHCenter);
+ nameLabel->setStyleSheet("font-size: 20px; font-weight: bold");
inclusionDisplayWidget = new EdhrecApiResponseCardInclusionDisplayWidget(this, toDisplay);
synergyDisplayWidget = new EdhrecApiResponseCardSynergyDisplayWidget(this, toDisplay);
- layout->addWidget(nameLabel);
- layout->addWidget(cardPictureWidget);
-
backgroundPlateWidget = new BackgroundPlateWidget(this);
auto plateLayout = new QVBoxLayout(backgroundPlateWidget);
+ plateLayout->addWidget(nameLabel);
+ plateLayout->addWidget(cardPictureWidget);
plateLayout->addWidget(inclusionDisplayWidget);
plateLayout->addWidget(synergyDisplayWidget);
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
index b15d559f5..515475f3e 100644
--- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
@@ -3,6 +3,7 @@
#include "../../../../../cards/card_info_picture_widget.h"
#include "../../tab_edhrec_main.h"
#include "../card_prices/edhrec_api_response_card_prices_display_widget.h"
+#include "edhrec_commander_api_response_bracket_navigation_widget.h"
#include
@@ -12,9 +13,14 @@ EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCom
QString baseUrl)
: QWidget(parent), commanderDetails(_commanderDetails)
{
- layout = new QVBoxLayout(this);
+ layout = new QHBoxLayout(this);
setLayout(layout);
+ commanderLayout = new QHBoxLayout();
+ commanderDetailsLayout = new QVBoxLayout();
+ commanderDetailsLayout->setAlignment(Qt::AlignCenter);
+ navigationAndPricesLayout = new QVBoxLayout();
+
commanderPicture = new CardInfoPictureWidget(this);
commanderPicture->setCard(CardDatabaseManager::query()->getCard({commanderDetails.getName()}));
@@ -36,20 +42,35 @@ EdhrecCommanderResponseCommanderDetailsDisplayWidget::EdhrecCommanderResponseCom
commanderDetails.debugPrint();
+ commanderName = new QLabel(this);
+ commanderName->setText(commanderDetails.getName());
+ commanderName->setAlignment(Qt::AlignCenter);
+ commanderName->setStyleSheet("font-size: 28px; font-weight: bold");
+
label = new QLabel(this);
label->setAlignment(Qt::AlignCenter);
+ label->setStyleSheet("font-size: 16px");
+
salt = new QLabel(this);
salt->setAlignment(Qt::AlignCenter);
+ salt->setStyleSheet("font-size: 16px");
cardPricesDisplayWidget = new EdhrecApiResponseCardPricesDisplayWidget(this, commanderDetails.getPrices());
navigationWidget = new EdhrecCommanderApiResponseNavigationWidget(this, commanderDetails, baseUrl);
- layout->addWidget(commanderPicture);
- layout->addWidget(label);
- layout->addWidget(salt);
- layout->addWidget(cardPricesDisplayWidget);
- layout->addWidget(navigationWidget);
+ commanderLayout->addWidget(commanderPicture);
+ commanderDetailsLayout->addWidget(commanderName);
+ commanderDetailsLayout->addSpacing(1);
+ commanderDetailsLayout->addWidget(label);
+ commanderDetailsLayout->addWidget(salt);
+ commanderDetailsLayout->addWidget(cardPricesDisplayWidget);
+ commanderLayout->addLayout(commanderDetailsLayout);
+ navigationAndPricesLayout->addWidget(navigationWidget);
+ // navigationAndPricesLayout->addWidget(cardPricesDisplayWidget);
+
+ layout->addLayout(commanderLayout);
+ layout->addLayout(navigationAndPricesLayout);
retranslateUi();
}
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.h b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.h
index 295e4228b..8e74588e2 100644
--- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.h
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.h
@@ -29,8 +29,12 @@ public:
private:
EdhrecCommanderApiResponseCommanderDetails commanderDetails;
- QVBoxLayout *layout;
+ QHBoxLayout *layout;
+ QHBoxLayout *commanderLayout;
+ QVBoxLayout *commanderDetailsLayout;
+ QVBoxLayout *navigationAndPricesLayout;
CardInfoPictureWidget *commanderPicture;
+ QLabel *commanderName;
QLabel *label;
QLabel *salt;
EdhrecApiResponseCardPricesDisplayWidget *cardPricesDisplayWidget;
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp
new file mode 100644
index 000000000..c3ab23e41
--- /dev/null
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp
@@ -0,0 +1,95 @@
+#include "edhrec_commander_api_response_bracket_navigation_widget.h"
+
+#include
+
+EdhrecCommanderApiResponseBracketNavigationWidget::EdhrecCommanderApiResponseBracketNavigationWidget(
+ QWidget *parent,
+ const QString &baseUrl)
+ : QWidget(parent)
+{
+ layout = new QGridLayout(this);
+ setLayout(layout);
+
+ gameChangerLabel = new QLabel(this);
+
+ layout->addWidget(gameChangerLabel, 1, 0, 1, 2);
+
+ for (int i = 0; i < gameChangerOptions.length(); i++) {
+ QString option = gameChangerOptions.at(i);
+ QString label = option.isEmpty() ? "All" : option.at(0).toUpper() + option.mid(1);
+ QPushButton *optionButton = new QPushButton(label, this);
+ optionButton->setMinimumHeight(84);
+ optionButton->setStyleSheet("font-size: 24px");
+ gameChangerButtons[option] = optionButton;
+ layout->addWidget(optionButton, 2, i);
+ connect(optionButton, &QPushButton::clicked, this, [=, this]() {
+ selectedGameChanger = option;
+ updateOptionButtonSelection(gameChangerButtons, option);
+ emit requestNavigation();
+ });
+ }
+
+ updateOptionButtonSelection(gameChangerButtons, "");
+
+ retranslateUi();
+ applyOptionsFromUrl(baseUrl);
+}
+
+void EdhrecCommanderApiResponseBracketNavigationWidget::retranslateUi()
+{
+ gameChangerLabel->setText(tr("Game Changers"));
+}
+
+void EdhrecCommanderApiResponseBracketNavigationWidget::applyOptionsFromUrl(const QString &url)
+{
+ QString cleanedUrl = url;
+
+ // Remove base and file extension
+ if (cleanedUrl.startsWith("https://json.edhrec.com/pages/")) {
+ cleanedUrl = cleanedUrl.mid(QString("https://json.edhrec.com/pages/").length());
+ }
+ if (cleanedUrl.endsWith(".json")) {
+ cleanedUrl.chop(5);
+ }
+
+ // Expecting something like: "commanders/the-ur-dragon/core/expensive"
+ QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts);
+
+ if (parts.size() < 2) {
+ return;
+ }
+
+ QString commanderName = parts[1];
+ QString gameChangerOpt;
+
+ // Define valid sets
+ QSet validGameChangers = {"exhibition", "core", "upgraded", "optimized", "cedh"};
+
+ // Check remaining parts after commander
+ for (int i = 2; i < parts.size(); ++i) {
+ QString part = parts[i].toLower();
+ if (validGameChangers.contains(part)) {
+ gameChangerOpt = part;
+ }
+ }
+
+ // Validate and apply
+ if (!gameChangerButtons.contains(gameChangerOpt)) {
+ gameChangerOpt.clear();
+ }
+
+ selectedGameChanger = gameChangerOpt;
+
+ updateOptionButtonSelection(gameChangerButtons, selectedGameChanger);
+}
+
+void EdhrecCommanderApiResponseBracketNavigationWidget::updateOptionButtonSelection(
+ QMap &buttons,
+ const QString &selectedKey)
+{
+ for (auto it = buttons.begin(); it != buttons.end(); ++it) {
+ it.value()->setStyleSheet(it.key() == selectedKey
+ ? "background-color: lightblue; font-weight: bold; font-size: 24px;"
+ : "font-size: 24px");
+ }
+}
\ No newline at end of file
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h
new file mode 100644
index 000000000..713ef2791
--- /dev/null
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h
@@ -0,0 +1,37 @@
+#ifndef COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BRACKET_NAVIGATION_WIDGET_H
+#define COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BRACKET_NAVIGATION_WIDGET_H
+
+#include
+#include
+#include
+#include
+#include
+
+class EdhrecCommanderApiResponseBracketNavigationWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit EdhrecCommanderApiResponseBracketNavigationWidget(QWidget *parent, const QString &baseUrl);
+ void retranslateUi();
+ void applyOptionsFromUrl(const QString &url);
+ QString getSelectedGameChanger() const
+ {
+ return selectedGameChanger;
+ }
+
+signals:
+ void requestNavigation();
+
+private:
+ QGridLayout *layout;
+ QLabel *gameChangerLabel;
+
+ QStringList gameChangerOptions = {"", "exhibition", "core", "upgraded", "optimized", "cedh"};
+ QString selectedGameChanger;
+
+ QMap gameChangerButtons;
+
+ void updateOptionButtonSelection(QMap &buttons, const QString &selectedKey);
+};
+
+#endif // COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BRACKET_NAVIGATION_WIDGET_H
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp
new file mode 100644
index 000000000..1845c020d
--- /dev/null
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp
@@ -0,0 +1,97 @@
+#include "edhrec_commander_api_response_budget_navigation_widget.h"
+
+#include
+
+EdhrecCommanderApiResponseBudgetNavigationWidget::EdhrecCommanderApiResponseBudgetNavigationWidget(
+ QWidget *parent,
+ const QString &baseUrl)
+ : QWidget(parent)
+{
+ layout = new QGridLayout(this);
+ setLayout(layout);
+
+ budgetLabel = new QLabel(this);
+
+ layout->addWidget(budgetLabel, 3, 0, 1, 2);
+
+ for (int i = 0; i < budgetOptions.length(); i++) {
+ QString option = budgetOptions.at(i);
+ QString label = option.isEmpty() ? "Any" : option.at(0).toUpper() + option.mid(1);
+ QPushButton *btn = new QPushButton(label, this);
+ btn->setMinimumHeight(84);
+ btn->setStyleSheet("font-size: 24px");
+ budgetButtons[option] = btn;
+ layout->addWidget(btn, 4, i);
+ connect(btn, &QPushButton::clicked, this, [=, this]() {
+ selectedBudget = option;
+ updateOptionButtonSelection(budgetButtons, option);
+ emit requestNavigation();
+ });
+ }
+
+ updateOptionButtonSelection(budgetButtons, "");
+
+ retranslateUi();
+ applyOptionsFromUrl(baseUrl);
+}
+
+void EdhrecCommanderApiResponseBudgetNavigationWidget::retranslateUi()
+{
+ budgetLabel->setText(tr("Budget"));
+}
+
+void EdhrecCommanderApiResponseBudgetNavigationWidget::applyOptionsFromUrl(const QString &url)
+{
+ QString cleanedUrl = url;
+
+ // Remove base and file extension
+ if (cleanedUrl.startsWith("https://json.edhrec.com/pages/")) {
+ cleanedUrl = cleanedUrl.mid(QString("https://json.edhrec.com/pages/").length());
+ }
+ if (cleanedUrl.endsWith(".json")) {
+ cleanedUrl.chop(5);
+ }
+
+ // Expecting something like: "commanders/the-ur-dragon/core/expensive"
+ QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts);
+
+ if (parts.size() < 2) {
+ return;
+ }
+
+ QString commanderName = parts[1];
+ QString gameChangerOpt, budgetOpt;
+
+ // Define valid sets
+ QSet validGameChangers = {"exhibition", "core", "upgraded", "optimized", "cedh"};
+ QSet validBudgets = {"budget", "expensive"};
+
+ // Check remaining parts after commander
+ for (int i = 2; i < parts.size(); ++i) {
+ QString part = parts[i].toLower();
+ if (validGameChangers.contains(part)) {
+ gameChangerOpt = part;
+ } else if (validBudgets.contains(part)) {
+ budgetOpt = part;
+ }
+ }
+
+ if (!budgetButtons.contains(budgetOpt)) {
+ budgetOpt.clear();
+ }
+
+ selectedBudget = budgetOpt;
+
+ updateOptionButtonSelection(budgetButtons, selectedBudget);
+}
+
+void EdhrecCommanderApiResponseBudgetNavigationWidget::updateOptionButtonSelection(
+ QMap &buttons,
+ const QString &selectedKey)
+{
+ for (auto it = buttons.begin(); it != buttons.end(); ++it) {
+ it.value()->setStyleSheet(it.key() == selectedKey
+ ? "background-color: lightblue; font-weight: bold; font-size: 24px;"
+ : "font-size: 24px");
+ }
+}
\ No newline at end of file
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h
new file mode 100644
index 000000000..666edba16
--- /dev/null
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h
@@ -0,0 +1,37 @@
+#ifndef COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BUDGET_NAVIGATION_WIDGET_H
+#define COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BUDGET_NAVIGATION_WIDGET_H
+
+#include
+#include
+#include
+#include
+#include
+
+class EdhrecCommanderApiResponseBudgetNavigationWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit EdhrecCommanderApiResponseBudgetNavigationWidget(QWidget *parent, const QString &baseUrl);
+ void retranslateUi();
+ void applyOptionsFromUrl(const QString &url);
+ QString getSelectedBudget() const
+ {
+ return selectedBudget;
+ }
+
+signals:
+ void requestNavigation();
+
+private:
+ QGridLayout *layout;
+ QLabel *budgetLabel;
+
+ QStringList budgetOptions = {"", "budget", "expensive"};
+ QString selectedBudget;
+
+ QMap budgetButtons;
+
+ void updateOptionButtonSelection(QMap &buttons, const QString &selectedKey);
+};
+
+#endif // COCKATRICE_EDHREC_COMMANDER_API_RESPONSE_BUDGET_NAVIGATION_WIDGET_H
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
index 3f3b9f1ba..c42d2ed90 100644
--- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
@@ -11,47 +11,28 @@ EdhrecCommanderApiResponseNavigationWidget::EdhrecCommanderApiResponseNavigation
layout = new QGridLayout(this);
setLayout(layout);
- gameChangerLabel = new QLabel(this);
- budgetLabel = new QLabel(this);
+ bracketNavigationWidget = new EdhrecCommanderApiResponseBracketNavigationWidget(this, baseUrl);
+
+ connect(bracketNavigationWidget, &EdhrecCommanderApiResponseBracketNavigationWidget::requestNavigation, this,
+ &EdhrecCommanderApiResponseNavigationWidget::actRequestCommanderNavigation);
+
+ budgetNavigationWidget = new EdhrecCommanderApiResponseBudgetNavigationWidget(this, baseUrl);
+
+ connect(budgetNavigationWidget, &EdhrecCommanderApiResponseBudgetNavigationWidget::requestNavigation, this,
+ &EdhrecCommanderApiResponseNavigationWidget::actRequestCommanderNavigation);
comboPushButton = new QPushButton(this);
+ comboPushButton->setMinimumHeight(84);
+ comboPushButton->setStyleSheet("font-size: 24px");
averageDeckPushButton = new QPushButton(this);
+ averageDeckPushButton->setMinimumHeight(84);
+ averageDeckPushButton->setStyleSheet("font-size: 24px");
layout->addWidget(comboPushButton, 0, 0, 1, 1);
layout->addWidget(averageDeckPushButton, 0, 1, 1, 1);
- layout->addWidget(gameChangerLabel, 1, 0, 1, 2);
-
- for (int i = 0; i < gameChangerOptions.length(); i++) {
- QString option = gameChangerOptions.at(i);
- QString label = option.isEmpty() ? "All" : option.at(0).toUpper() + option.mid(1);
- QPushButton *optionButton = new QPushButton(label, this);
- gameChangerButtons[option] = optionButton;
- layout->addWidget(optionButton, 2, i);
- connect(optionButton, &QPushButton::clicked, this, [=, this]() {
- selectedGameChanger = option;
- updateOptionButtonSelection(gameChangerButtons, option);
- actRequestCommanderNavigation();
- });
- }
-
- layout->addWidget(budgetLabel, 3, 0, 1, 2);
-
- for (int i = 0; i < budgetOptions.length(); i++) {
- QString option = budgetOptions.at(i);
- QString label = option.isEmpty() ? "Any" : option.at(0).toUpper() + option.mid(1);
- QPushButton *btn = new QPushButton(label, this);
- budgetButtons[option] = btn;
- layout->addWidget(btn, 4, i);
- connect(btn, &QPushButton::clicked, this, [=, this]() {
- selectedBudget = option;
- updateOptionButtonSelection(budgetButtons, option);
- actRequestCommanderNavigation();
- });
- }
-
- updateOptionButtonSelection(gameChangerButtons, "");
- updateOptionButtonSelection(budgetButtons, "");
+ layout->addWidget(bracketNavigationWidget, 1, 0, 1, 2);
+ layout->addWidget(budgetNavigationWidget, 2, 0, 1, 2);
QWidget *currentParent = parentWidget();
TabEdhRecMain *parentTab = nullptr;
@@ -73,87 +54,21 @@ EdhrecCommanderApiResponseNavigationWidget::EdhrecCommanderApiResponseNavigation
}
retranslateUi();
- applyOptionsFromUrl(baseUrl);
}
void EdhrecCommanderApiResponseNavigationWidget::retranslateUi()
{
comboPushButton->setText(tr("Combos"));
averageDeckPushButton->setText(tr("Average Deck"));
- gameChangerLabel->setText(tr("Game Changers"));
- budgetLabel->setText(tr("Budget"));
-}
-
-void EdhrecCommanderApiResponseNavigationWidget::applyOptionsFromUrl(const QString &url)
-{
- QString cleanedUrl = url;
-
- // Remove base and file extension
- if (cleanedUrl.startsWith("https://json.edhrec.com/pages/")) {
- cleanedUrl = cleanedUrl.mid(QString("https://json.edhrec.com/pages/").length());
- }
- if (cleanedUrl.endsWith(".json")) {
- cleanedUrl.chop(5);
- }
-
- // Expecting something like: "commanders/the-ur-dragon/core/expensive"
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
- QStringList parts = cleanedUrl.split('/', Qt::SkipEmptyParts);
-#else
- QStringList parts = cleanedUrl.split('/', QString::SkipEmptyParts);
-#endif
-
- if (parts.size() < 2) {
- return;
- }
-
- QString commanderName = parts[1];
- QString gameChangerOpt, budgetOpt;
-
- // Define valid sets
- QSet validGameChangers = {"core", "upgraded", "optimized"};
- QSet validBudgets = {"budget", "expensive"};
-
- // Check remaining parts after commander
- for (int i = 2; i < parts.size(); ++i) {
- QString part = parts[i].toLower();
- if (validGameChangers.contains(part)) {
- gameChangerOpt = part;
- } else if (validBudgets.contains(part)) {
- budgetOpt = part;
- }
- }
-
- // Validate and apply
- if (!gameChangerButtons.contains(gameChangerOpt)) {
- gameChangerOpt.clear();
- }
- if (!budgetButtons.contains(budgetOpt)) {
- budgetOpt.clear();
- }
-
- selectedGameChanger = gameChangerOpt;
- selectedBudget = budgetOpt;
-
- updateOptionButtonSelection(gameChangerButtons, selectedGameChanger);
- updateOptionButtonSelection(budgetButtons, selectedBudget);
-}
-
-void EdhrecCommanderApiResponseNavigationWidget::updateOptionButtonSelection(QMap &buttons,
- const QString &selectedKey)
-{
- for (auto it = buttons.begin(); it != buttons.end(); ++it) {
- it.value()->setStyleSheet(it.key() == selectedKey ? "background-color: lightblue; font-weight: bold;" : "");
- }
}
QString EdhrecCommanderApiResponseNavigationWidget::addNavigationOptionsToUrl(QString baseUrl)
{
- if (!selectedGameChanger.isEmpty()) {
- baseUrl += "/" + selectedGameChanger;
+ if (!bracketNavigationWidget->getSelectedGameChanger().isEmpty()) {
+ baseUrl += "/" + bracketNavigationWidget->getSelectedGameChanger();
}
- if (!selectedBudget.isEmpty()) {
- baseUrl += "/" + selectedBudget;
+ if (!budgetNavigationWidget->getSelectedBudget().isEmpty()) {
+ baseUrl += "/" + budgetNavigationWidget->getSelectedBudget();
}
return baseUrl;
}
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h
index b97e095f7..10dfa8223 100644
--- a/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.h
@@ -8,6 +8,8 @@
#define EDHREC_COMMANDER_API_RESPONSE_NAVIGATION_WIDGET_H
#include "edhrec_api_response_commander_details_display_widget.h"
+#include "edhrec_commander_api_response_bracket_navigation_widget.h"
+#include "edhrec_commander_api_response_budget_navigation_widget.h"
#include
#include
@@ -23,7 +25,6 @@ public:
const EdhrecCommanderApiResponseCommanderDetails &_commanderDetails,
QString baseUrl);
void retranslateUi();
- void applyOptionsFromUrl(const QString &url);
public slots:
void actRequestCommanderNavigation();
@@ -35,24 +36,14 @@ signals:
private:
QGridLayout *layout;
- QLabel *gameChangerLabel;
- QLabel *budgetLabel;
-
- QStringList gameChangerOptions = {"", "core", "upgraded", "optimized"};
- QStringList budgetOptions = {"", "budget", "expensive"};
-
- QString selectedGameChanger;
- QString selectedBudget;
-
- QMap gameChangerButtons;
- QMap budgetButtons;
+ EdhrecCommanderApiResponseBracketNavigationWidget *bracketNavigationWidget;
+ EdhrecCommanderApiResponseBudgetNavigationWidget *budgetNavigationWidget;
QPushButton *comboPushButton;
QPushButton *averageDeckPushButton;
EdhrecCommanderApiResponseCommanderDetails commanderDetails;
- void updateOptionButtonSelection(QMap &buttons, const QString &selectedKey);
QString addNavigationOptionsToUrl(QString baseUrl);
QString buildComboUrl() const;
};
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp
index 93dbd6bc5..10389f4eb 100644
--- a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp
@@ -15,10 +15,7 @@
TabEdhRec::TabEdhRec(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
{
networkManager = new QNetworkAccessManager(this);
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
networkManager->setTransferTimeout(); // Use Qt's default timeout
-#endif
-
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
connect(networkManager, &QNetworkAccessManager::finished, this, &TabEdhRec::processApiJson);
}
diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp
index 92163997c..f861d8afd 100644
--- a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp
+++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp
@@ -37,10 +37,7 @@ static bool canBeCommander(const CardInfoPtr &cardInfo)
TabEdhRecMain::TabEdhRecMain(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
{
networkManager = new QNetworkAccessManager(this);
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
networkManager->setTransferTimeout(); // Use Qt's default timeout
-#endif
-
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(processApiJson(QNetworkReply *)));
@@ -363,7 +360,7 @@ void TabEdhRecMain::processAverageDeckResponse(QJsonObject reply)
{
EdhrecAverageDeckApiResponse deckData;
deckData.fromJson(reply);
- tabSupervisor->openDeckInNewTab(deckData.deck.deckLoader);
+ tabSupervisor->openDeckInNewTab({deckData.deck.deck, {}});
}
void TabEdhRecMain::prettyPrintJson(const QJsonValue &value, int indentLevel)
diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp b/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp
index 4fa12259c..286252e13 100644
--- a/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp
+++ b/cockatrice/src/interface/widgets/tabs/tab_deck_editor.cpp
@@ -1,6 +1,7 @@
#include "tab_deck_editor.h"
#include "../../../client/settings/cache_settings.h"
+#include "../deck_editor/deck_state_manager.h"
#include "../filters/filter_builder.h"
#include "../interface/pixel_map_generator.h"
#include "../interface/widgets/cards/card_info_frame_widget.h"
@@ -114,8 +115,8 @@ void TabDeckEditor::createMenus()
*/
QString TabDeckEditor::getTabText() const
{
- QString result = tr("Deck: %1").arg(deckDockWidget->getSimpleDeckName());
- if (modified)
+ QString result = tr("Deck: %1").arg(deckStateManager->getSimpleDeckName());
+ if (deckStateManager->isModified())
result.prepend("* ");
return result;
}
@@ -158,8 +159,7 @@ void TabDeckEditor::refreshShortcuts()
*/
void TabDeckEditor::showPrintingSelector()
{
- printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getCard().getCardPtr(),
- DECK_ZONE_MAIN);
+ printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getCard().getCardPtr());
printingSelectorDockWidget->printingSelector->updateDisplay();
aPrintingSelectorDockVisible->setChecked(true);
printingSelectorDockWidget->setVisible(true);
diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_editor.h b/cockatrice/src/interface/widgets/tabs/tab_deck_editor.h
index 9e00099a0..b3ffb8d90 100644
--- a/cockatrice/src/interface/widgets/tabs/tab_deck_editor.h
+++ b/cockatrice/src/interface/widgets/tabs/tab_deck_editor.h
@@ -12,7 +12,6 @@ class CardDatabaseDisplayModel;
class DeckListModel;
class QLabel;
-class DeckLoader;
/**
* @class TabDeckEditor
diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp
index a54c16258..73b3fc0ac 100644
--- a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp
+++ b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.cpp
@@ -245,7 +245,7 @@ void TabDeckStorage::actOpenLocalDeck()
if (!deckLoader->loadFromFile(filePath, DeckFileFormat::Cockatrice, true))
continue;
- emit openDeckEditor(deckLoader);
+ emit openDeckEditor(deckLoader->getDeck());
}
}
@@ -307,13 +307,15 @@ void TabDeckStorage::uploadDeck(const QString &filePath, const QString &targetPa
QFile deckFile(filePath);
QFileInfo deckFileInfo(deckFile);
- DeckLoader deck(this);
- if (!deck.loadFromFile(filePath, DeckFileFormat::Cockatrice)) {
+ DeckLoader deckLoader(this);
+ if (!deckLoader.loadFromFile(filePath, DeckFileFormat::Cockatrice)) {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck file"));
return;
}
- if (deck.getDeckList()->getName().isEmpty()) {
+ DeckList deck = deckLoader.getDeck().deckList;
+
+ if (deck.getName().isEmpty()) {
bool ok;
QString deckName =
getTextWithMax(this, tr("Enter deck name"), tr("This decklist does not have a name.\nPlease enter a name:"),
@@ -322,12 +324,12 @@ void TabDeckStorage::uploadDeck(const QString &filePath, const QString &targetPa
return;
if (deckName.isEmpty())
deckName = tr("Unnamed deck");
- deck.getDeckList()->setName(deckName);
+ deck.setName(deckName);
} else {
- deck.getDeckList()->setName(deck.getDeckList()->getName().left(MAX_NAME_LENGTH));
+ deck.setName(deck.getName().left(MAX_NAME_LENGTH));
}
- QString deckString = deck.getDeckList()->writeToString_Native();
+ QString deckString = deck.writeToString_Native();
if (deckString.length() > MAX_FILE_LENGTH) {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck file"));
return;
@@ -436,7 +438,7 @@ void TabDeckStorage::openRemoteDeckFinished(const Response &r, const CommandCont
if (!loader.loadFromRemote(QString::fromStdString(resp.deck()), cmd.deck_id()))
return;
- emit openDeckEditor(&loader);
+ emit openDeckEditor(loader.getDeck());
}
void TabDeckStorage::actDownload()
@@ -492,8 +494,12 @@ void TabDeckStorage::downloadFinished(const Response &r,
const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext);
QString filePath = extraData.toString();
- DeckLoader deck(this, new DeckList(QString::fromStdString(resp.deck())));
- deck.saveToFile(filePath, DeckFileFormat::Cockatrice);
+ DeckList deckList = DeckList(QString::fromStdString(resp.deck()));
+
+ DeckLoader deckLoader(this);
+ deckLoader.setDeck({deckList, {}});
+
+ deckLoader.saveToFile(filePath, DeckFileFormat::Cockatrice);
}
void TabDeckStorage::actNewFolder()
diff --git a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h
index 0226785e3..f8c0497f7 100644
--- a/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h
+++ b/cockatrice/src/interface/widgets/tabs/tab_deck_storage.h
@@ -13,6 +13,7 @@
#include
+struct LoadedDeck;
class ServerInfo_User;
class AbstractClient;
class QTreeView;
@@ -23,7 +24,6 @@ class QTreeWidgetItem;
class QGroupBox;
class CommandContainer;
class Response;
-class DeckLoader;
class TabDeckStorage : public Tab
{
@@ -87,7 +87,7 @@ public:
return tr("Deck Storage");
}
signals:
- void openDeckEditor(DeckLoader *deckLoader);
+ void openDeckEditor(const LoadedDeck &deck);
};
#endif
diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.cpp b/cockatrice/src/interface/widgets/tabs/tab_game.cpp
index 2f83ba2e6..5b6c8d52a 100644
--- a/cockatrice/src/interface/widgets/tabs/tab_game.cpp
+++ b/cockatrice/src/interface/widgets/tabs/tab_game.cpp
@@ -749,11 +749,10 @@ void TabGame::loadDeckForLocalPlayer(Player *localPlayer, int playerId, ServerIn
{
TabbedDeckViewContainer *deckViewContainer = deckViewContainers.value(playerId);
if (playerInfo.has_deck_list()) {
- DeckLoader newDeck(this, new DeckList(QString::fromStdString(playerInfo.deck_list())));
- CardPictureLoader::cacheCardPixmaps(
- CardDatabaseManager::query()->getCards(newDeck.getDeckList()->getCardRefList()));
- deckViewContainer->playerDeckView->setDeck(newDeck);
- localPlayer->setDeck(newDeck);
+ DeckList deckList = DeckList(QString::fromStdString(playerInfo.deck_list()));
+ CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(deckList.getCardRefList()));
+ deckViewContainer->playerDeckView->setDeck(deckList);
+ localPlayer->setDeck(deckList);
}
}
diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.h b/cockatrice/src/interface/widgets/tabs/tab_game.h
index b6fe5b51c..27374ef2a 100644
--- a/cockatrice/src/interface/widgets/tabs/tab_game.h
+++ b/cockatrice/src/interface/widgets/tabs/tab_game.h
@@ -45,7 +45,6 @@ class ReplayTimelineWidget;
class CardZone;
class AbstractCardItem;
class CardItem;
-class DeckLoader;
class QVBoxLayout;
class QHBoxLayout;
class GameReplay;
@@ -119,7 +118,7 @@ signals:
void containerProcessingStarted(const GameEventContext &context);
void containerProcessingDone();
void openMessageDialog(const QString &userName, bool focus);
- void openDeckEditor(DeckLoader *deck);
+ void openDeckEditor(const LoadedDeck &deck);
void notIdle();
void phaseChanged(int phase);
diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp
index df9387a6c..b10d615ff 100644
--- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp
+++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp
@@ -133,10 +133,10 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *
// create tabs menu actions
aTabDeckEditor = new QAction(this);
- connect(aTabDeckEditor, &QAction::triggered, this, [this] { addDeckEditorTab(nullptr); });
+ connect(aTabDeckEditor, &QAction::triggered, this, [this] { addDeckEditorTab(LoadedDeck()); });
aTabVisualDeckEditor = new QAction(this);
- connect(aTabVisualDeckEditor, &QAction::triggered, this, [this] { addVisualDeckEditorTab(nullptr); });
+ connect(aTabVisualDeckEditor, &QAction::triggered, this, [this] { addVisualDeckEditorTab(LoadedDeck()); });
aTabEdhRec = new QAction(this);
connect(aTabEdhRec, &QAction::triggered, this, [this] { addEdhrecMainTab(); });
@@ -846,9 +846,9 @@ void TabSupervisor::talkLeft(TabMessage *tab)
/**
* Creates a new deck editor tab and loads the deck into it.
* Creates either a classic or visual deck editor tab depending on settings
- * @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance.
+ * @param deckToOpen The deck to open in the tab.
*/
-void TabSupervisor::openDeckInNewTab(DeckLoader *deckToOpen)
+void TabSupervisor::openDeckInNewTab(const LoadedDeck &deckToOpen)
{
int type = SettingsCache::instance().getDefaultDeckEditorType();
switch (type) {
@@ -868,13 +868,12 @@ void TabSupervisor::openDeckInNewTab(DeckLoader *deckToOpen)
/**
* Creates a new deck editor tab
- * @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance.
+ * @param deckToOpen The deck to open in the tab.
*/
-TabDeckEditor *TabSupervisor::addDeckEditorTab(DeckLoader *deckToOpen)
+TabDeckEditor *TabSupervisor::addDeckEditorTab(const LoadedDeck &deckToOpen)
{
auto *tab = new TabDeckEditor(this);
- if (deckToOpen)
- tab->openDeck(deckToOpen);
+ tab->openDeck(deckToOpen);
connect(tab, &AbstractTabDeckEditor::deckEditorClosing, this, &TabSupervisor::deckEditorClosed);
connect(tab, &AbstractTabDeckEditor::openDeckEditor, this, &TabSupervisor::addDeckEditorTab);
myAddTab(tab);
@@ -883,11 +882,10 @@ TabDeckEditor *TabSupervisor::addDeckEditorTab(DeckLoader *deckToOpen)
return tab;
}
-TabDeckEditorVisual *TabSupervisor::addVisualDeckEditorTab(DeckLoader *deckToOpen)
+TabDeckEditorVisual *TabSupervisor::addVisualDeckEditorTab(const LoadedDeck &deckToOpen)
{
auto *tab = new TabDeckEditorVisual(this);
- if (deckToOpen)
- tab->openDeck(deckToOpen);
+ tab->openDeck(deckToOpen);
connect(tab, &AbstractTabDeckEditor::deckEditorClosing, this, &TabSupervisor::deckEditorClosed);
connect(tab, &AbstractTabDeckEditor::openDeckEditor, this, &TabSupervisor::addVisualDeckEditorTab);
myAddTab(tab);
diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h
index 948ac6d7e..0c4428f83 100644
--- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.h
+++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.h
@@ -168,9 +168,9 @@ signals:
void showWindowIfHidden();
public slots:
- void openDeckInNewTab(DeckLoader *deckToOpen);
- TabDeckEditor *addDeckEditorTab(DeckLoader *deckToOpen);
- TabDeckEditorVisual *addVisualDeckEditorTab(DeckLoader *deckToOpen);
+ void openDeckInNewTab(const LoadedDeck &deckToOpen);
+ TabDeckEditor *addDeckEditorTab(const LoadedDeck &deckToOpen);
+ TabDeckEditorVisual *addVisualDeckEditorTab(const LoadedDeck &deckToOpen);
TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab();
TabEdhRecMain *addEdhrecMainTab();
TabArchidekt *addArchidektTab();
diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
index 6dd55d545..f3d573d27 100644
--- a/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
+++ b/cockatrice/src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
@@ -1,6 +1,7 @@
#include "tab_deck_editor_visual.h"
#include "../../../../client/settings/cache_settings.h"
+#include "../../deck_editor/deck_state_manager.h"
#include "../../filters/filter_builder.h"
#include "../../interface/pixel_map_generator.h"
#include "../../interface/widgets/cards/card_info_frame_widget.h"
@@ -61,7 +62,7 @@ void TabDeckEditorVisual::createCentralFrame()
centralFrame = new QVBoxLayout;
centralWidget->setLayout(centralFrame);
- tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, deckDockWidget->deckModel,
+ tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, deckStateManager->getModel(),
databaseDisplayDockWidget->databaseModel,
databaseDisplayDockWidget->databaseDisplayModel);
@@ -85,7 +86,7 @@ void TabDeckEditorVisual::onDeckChanged()
AbstractTabDeckEditor::onDeckModified();
tabContainer->visualDeckView->constructZoneWidgetsFromDeckListModel();
tabContainer->deckAnalytics->refreshDisplays();
- tabContainer->sampleHandWidget->setDeckModel(deckDockWidget->deckModel);
+ tabContainer->sampleHandWidget->setDeckModel(deckStateManager->getModel());
}
/** @brief Creates menus for deck editing and view options, including dock actions. */
@@ -149,8 +150,8 @@ void TabDeckEditorVisual::createMenus()
/** @brief Returns the tab text, prepending a mark if the deck has unsaved changes. */
QString TabDeckEditorVisual::getTabText() const
{
- QString result = tr("Visual Deck: %1").arg(deckDockWidget->getSimpleDeckName());
- if (modified)
+ QString result = tr("Visual Deck: %1").arg(deckStateManager->getSimpleDeckName());
+ if (deckStateManager->isModified())
result.prepend("* ");
return result;
}
@@ -166,9 +167,9 @@ void TabDeckEditorVisual::changeModelIndexAndCardInfo(const ExactCard &activeCar
void TabDeckEditorVisual::changeModelIndexToCard(const ExactCard &activeCard)
{
QString cardName = activeCard.getName();
- QModelIndex index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_MAIN);
+ QModelIndex index = deckStateManager->getModel()->findCard(cardName, DECK_ZONE_MAIN);
if (!index.isValid()) {
- index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_SIDE);
+ index = deckStateManager->getModel()->findCard(cardName, DECK_ZONE_SIDE);
}
if (!deckDockWidget->getSelectionModel()->hasSelection()) {
deckDockWidget->getSelectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
@@ -182,7 +183,7 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event,
auto card = instance->getCard();
// Get the model index for the card
- QModelIndex idx = deckDockWidget->deckModel->findCard(card.getName(), zoneName);
+ QModelIndex idx = deckStateManager->getModel()->findCard(card.getName(), zoneName);
if (!idx.isValid()) {
return;
}
@@ -191,8 +192,8 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event,
// Double click = swap
if (event->type() == QEvent::MouseButtonDblClick && event->button() == Qt::LeftButton) {
- actSwapCard(card, zoneName);
- idx = deckDockWidget->deckModel->findCard(card.getName(), zoneName);
+ deckStateManager->swapCardAtIndex(idx);
+ idx = deckStateManager->getModel()->findCard(card.getName(), zoneName);
sel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
return;
}
@@ -266,8 +267,7 @@ bool TabDeckEditorVisual::actSaveDeckAs()
/** @brief Shows the printing selector dock and updates it with the current card. */
void TabDeckEditorVisual::showPrintingSelector()
{
- printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getCard().getCardPtr(),
- DECK_ZONE_MAIN);
+ printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getCard().getCardPtr());
printingSelectorDockWidget->printingSelector->updateDisplay();
aPrintingSelectorDockVisible->setChecked(true);
printingSelectorDockWidget->setVisible(true);
diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp b/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
index 69c80bb35..9570702ed 100644
--- a/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
+++ b/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
@@ -24,11 +24,11 @@ TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor)
void TabDeckStorageVisual::actOpenLocalDeck(const QString &filePath)
{
- auto deckLoader = new DeckLoader(this);
- if (!deckLoader->loadFromFile(filePath, DeckFileFormat::getFormatFromName(filePath), true)) {
+ auto deckLoader = DeckLoader(this);
+ if (!deckLoader.loadFromFile(filePath, DeckFileFormat::getFormatFromName(filePath), true)) {
QMessageBox::critical(this, tr("Error"), tr("Could not open deck at %1").arg(filePath));
return;
}
- emit openDeckEditor(deckLoader);
+ emit openDeckEditor(deckLoader.getDeck());
}
diff --git a/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.h b/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.h
index a56ce4c24..ccf752e67 100644
--- a/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.h
+++ b/cockatrice/src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.h
@@ -9,9 +9,9 @@
#include "../tab.h"
+struct LoadedDeck;
class AbstractClient;
class CommandContainer;
-class DeckLoader;
class DeckPreviewWidget;
class QFileSystemModel;
class QGroupBox;
@@ -39,7 +39,7 @@ public slots:
void actOpenLocalDeck(const QString &filePath);
signals:
- void openDeckEditor(DeckLoader *deckLoader);
+ void openDeckEditor(const LoadedDeck &deck);
private:
VisualDeckStorageWidget *visualDeckStorageWidget;
diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
index 9bc506056..5098696dd 100644
--- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
+++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
@@ -2,6 +2,7 @@
#include "../../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h"
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
+#include "../deck_editor/deck_state_manager.h"
#include
@@ -60,18 +61,14 @@ void VisualDatabaseDisplayNameFilterWidget::retranslateUi()
void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck()
{
- DeckListModel *deckListModel = deckEditor->deckDockWidget->deckModel;
+ DeckListModel *deckListModel = deckEditor->deckStateManager->getModel();
if (!deckListModel)
return;
- DeckList *decklist = deckListModel->getDeckList();
- if (!decklist)
- return;
- QList cardsInDeck = decklist->getCardNodes();
-
- for (auto currentCard : cardsInDeck) {
- createNameFilter(currentCard->getName());
+ QList cardNames = deckListModel->getCardNames();
+ for (auto cardName : cardNames) {
+ createNameFilter(cardName);
}
updateFilterModel();
@@ -83,7 +80,7 @@ void VisualDatabaseDisplayNameFilterWidget::actLoadFromClipboard()
if (!dlg.exec())
return;
- QStringList cardsInClipboard = dlg.getDeckList()->getDeckList()->getCardList();
+ QStringList cardsInClipboard = dlg.getDeckList().getCardList();
for (QString cardName : cardsInClipboard) {
createNameFilter(cardName);
}
diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp
index 778706f9a..0badb76ff 100644
--- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp
+++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp
@@ -76,25 +76,11 @@ void VisualDeckEditorSampleHandWidget::updateDisplay()
QList VisualDeckEditorSampleHandWidget::getRandomCards(int amountToGet)
{
- QList mainDeckCards;
QList randomCards;
if (!deckListModel)
return randomCards;
- DeckList *decklist = deckListModel->getDeckList();
- if (!decklist)
- return randomCards;
- QList cardsInDeck = decklist->getCardNodes({DECK_ZONE_MAIN});
-
- // Collect all cards in the main deck, allowing duplicates based on their count
- for (auto currentCard : cardsInDeck) {
- for (int k = 0; k < currentCard->getNumber(); ++k) {
- ExactCard card = CardDatabaseManager::query()->getCard(currentCard->toCardRef());
- if (card) {
- mainDeckCards.append(card);
- }
- }
- }
+ QList mainDeckCards = deckListModel->getCardsForZone(DECK_ZONE_MAIN);
if (mainDeckCards.isEmpty())
return randomCards;
diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp
index 76cfe8c8e..7b70e2b6d 100644
--- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp
+++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp
@@ -264,9 +264,10 @@ void VisualDeckEditorWidget::onCardRemoval(const QModelIndex &parent, int first,
void VisualDeckEditorWidget::constructZoneWidgetForIndex(QPersistentModelIndex persistent)
{
+ QString zoneName =
+ persistent.sibling(persistent.row(), DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
DeckCardZoneDisplayWidget *zoneDisplayWidget = new DeckCardZoneDisplayWidget(
- zoneContainer, deckListModel, selectionModel, persistent,
- deckListModel->data(persistent.sibling(persistent.row(), 1), Qt::EditRole).toString(),
+ zoneContainer, deckListModel, selectionModel, persistent, zoneName,
displayOptionsWidget->getActiveGroupCriteria(), displayOptionsWidget->getActiveSortCriteria(),
displayOptionsWidget->getDisplayType(), 20, 10, cardSizeWidget);
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover);
@@ -290,7 +291,7 @@ void VisualDeckEditorWidget::constructZoneWidgetsFromDeckListModel()
QSortFilterProxyModel proxy;
proxy.setSourceModel(deckListModel);
proxy.setSortRole(Qt::EditRole);
- proxy.sort(1, Qt::AscendingOrder);
+ proxy.sort(DeckListModelColumns::CARD_NAME, Qt::AscendingOrder);
for (int i = 0; i < proxy.rowCount(); ++i) {
QModelIndex proxyIndex = proxy.index(i, 0);
diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
index d3858a135..d57cda1c6 100644
--- a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
@@ -80,7 +80,7 @@ static QStringList findAllKnownTags()
auto loader = DeckLoader(nullptr);
for (const QString &file : allFiles) {
loader.loadFromFile(file, DeckFileFormat::getFormatFromName(file), false);
- QStringList tags = loader.getDeckList()->getTags();
+ QStringList tags = loader.getDeck().deckList.getTags();
knownTags.append(tags);
knownTags.removeDuplicates();
}
@@ -125,7 +125,7 @@ static bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath)
static void convertFileToCockatriceFormat(DeckPreviewWidget *deckPreviewWidget)
{
deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath);
- deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastLoadInfo().fileName;
+ deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getDeck().lastLoadInfo.fileName;
deckPreviewWidget->refreshBannerCardText();
}
diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
index 68243100c..4c782c989 100644
--- a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
+++ b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
@@ -74,16 +74,16 @@ void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess)
if (!deckLoadSuccess) {
return;
}
- auto bannerCard = deckLoader->getDeckList()->getBannerCard().name.isEmpty()
+ auto bannerCard = deckLoader->getDeck().deckList.getBannerCard().name.isEmpty()
? ExactCard()
- : CardDatabaseManager::query()->getCard(deckLoader->getDeckList()->getBannerCard());
+ : CardDatabaseManager::query()->getCard(deckLoader->getDeck().deckList.getBannerCard());
bannerCardDisplayWidget->setCard(bannerCard);
bannerCardDisplayWidget->setFontSize(24);
- setFilePath(deckLoader->getLastLoadInfo().fileName);
+ setFilePath(deckLoader->getDeck().lastLoadInfo.fileName);
colorIdentityWidget = new ColorIdentityWidget(this, getColorIdentity());
- deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckLoader->getDeckList()->getTags());
+ deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckLoader->getDeck().deckList.getTags());
connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, this, &DeckPreviewWidget::setTags);
bannerCardLabel = new QLabel(this);
@@ -91,7 +91,7 @@ void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess)
bannerCardComboBox = new QComboBox(this);
bannerCardComboBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
bannerCardComboBox->setObjectName("bannerCardComboBox");
- bannerCardComboBox->setCurrentText(deckLoader->getDeckList()->getBannerCard().name);
+ bannerCardComboBox->setCurrentText(deckLoader->getDeck().deckList.getBannerCard().name);
bannerCardComboBox->installEventFilter(new NoScrollFilter());
connect(bannerCardComboBox, QOverload::of(&QComboBox::currentIndexChanged), this,
&DeckPreviewWidget::setBannerCard);
@@ -153,7 +153,7 @@ void DeckPreviewWidget::updateTagsVisibility(bool visible)
QString DeckPreviewWidget::getColorIdentity()
{
- QStringList cardList = deckLoader->getDeckList()->getCardList();
+ QStringList cardList = deckLoader->getDeck().deckList.getCardList();
if (cardList.isEmpty()) {
return {};
}
@@ -187,8 +187,8 @@ QString DeckPreviewWidget::getColorIdentity()
*/
QString DeckPreviewWidget::getDisplayName() const
{
- return deckLoader->getDeckList()->getName().isEmpty() ? QFileInfo(deckLoader->getLastLoadInfo().fileName).fileName()
- : deckLoader->getDeckList()->getName();
+ QString deckName = deckLoader->getDeck().deckList.getName();
+ return !deckName.isEmpty() ? deckName : QFileInfo(deckLoader->getDeck().lastLoadInfo.fileName).fileName();
}
void DeckPreviewWidget::setFilePath(const QString &_filePath)
@@ -235,7 +235,7 @@ void DeckPreviewWidget::updateBannerCardComboBox()
// Prepare the new items with deduplication
QSet> bannerCardSet;
- QList cardsInDeck = deckLoader->getDeckList()->getCardNodes();
+ QList cardsInDeck = deckLoader->getDeck().deckList.getCardNodes();
for (auto currentCard : cardsInDeck) {
for (int k = 0; k < currentCard->getNumber(); ++k) {
@@ -269,7 +269,7 @@ void DeckPreviewWidget::updateBannerCardComboBox()
bannerCardComboBox->setCurrentIndex(restoredIndex);
} else {
// Add a placeholder "-" and set it as the current selection
- int bannerIndex = bannerCardComboBox->findText(deckLoader->getDeckList()->getBannerCard().name);
+ int bannerIndex = bannerCardComboBox->findText(deckLoader->getDeck().deckList.getBannerCard().name);
if (bannerIndex != -1) {
bannerCardComboBox->setCurrentIndex(bannerIndex);
} else {
@@ -287,7 +287,7 @@ void DeckPreviewWidget::setBannerCard(int /* changedIndex */)
{
auto [name, id] = bannerCardComboBox->currentData().value>();
CardRef cardRef = {name, id};
- deckLoader->getDeckList()->setBannerCard(cardRef);
+ deckLoader->getDeck().deckList.setBannerCard(cardRef);
deckLoader->saveToFile(filePath, DeckFileFormat::getFormatFromName(filePath));
bannerCardDisplayWidget->setCard(CardDatabaseManager::query()->getCard(cardRef));
}
@@ -310,7 +310,7 @@ void DeckPreviewWidget::imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewC
void DeckPreviewWidget::setTags(const QStringList &tags)
{
- deckLoader->getDeckList()->setTags(tags);
+ deckLoader->getDeck().deckList.setTags(tags);
deckLoader->saveToFile(filePath, DeckFileFormat::Cockatrice);
}
@@ -320,7 +320,7 @@ QMenu *DeckPreviewWidget::createRightClickMenu()
menu->setAttribute(Qt::WA_DeleteOnClose);
connect(menu->addAction(tr("Open in deck editor")), &QAction::triggered, this,
- [this] { emit openDeckEditor(deckLoader); });
+ [this] { emit openDeckEditor(deckLoader->getDeck()); });
connect(menu->addAction(tr("Edit Tags")), &QAction::triggered, deckTagsDisplayWidget,
&DeckPreviewDeckTagsDisplayWidget::openTagEditDlg);
@@ -334,13 +334,13 @@ QMenu *DeckPreviewWidget::createRightClickMenu()
auto saveToClipboardMenu = menu->addMenu(tr("Save Deck to Clipboard"));
connect(saveToClipboardMenu->addAction(tr("Annotated")), &QAction::triggered, this,
- [this] { DeckLoader::saveToClipboard(deckLoader->getDeckList(), true, true); });
+ [this] { DeckLoader::saveToClipboard(deckLoader->getDeck().deckList, true, true); });
connect(saveToClipboardMenu->addAction(tr("Annotated (No set info)")), &QAction::triggered, this,
- [this] { DeckLoader::saveToClipboard(deckLoader->getDeckList(), true, false); });
+ [this] { DeckLoader::saveToClipboard(deckLoader->getDeck().deckList, true, false); });
connect(saveToClipboardMenu->addAction(tr("Not Annotated")), &QAction::triggered, this,
- [this] { DeckLoader::saveToClipboard(deckLoader->getDeckList(), false, true); });
+ [this] { DeckLoader::saveToClipboard(deckLoader->getDeck().deckList, false, true); });
connect(saveToClipboardMenu->addAction(tr("Not Annotated (No set info)")), &QAction::triggered, this,
- [this] { DeckLoader::saveToClipboard(deckLoader->getDeckList(), false, false); });
+ [this] { DeckLoader::saveToClipboard(deckLoader->getDeck().deckList, false, false); });
menu->addSeparator();
@@ -376,7 +376,7 @@ void DeckPreviewWidget::addSetBannerCardMenu(QMenu *menu)
void DeckPreviewWidget::actRenameDeck()
{
// read input
- const QString oldName = deckLoader->getDeckList()->getName();
+ const QString oldName = deckLoader->getDeck().deckList.getName();
bool ok;
QString newName = QInputDialog::getText(this, "Rename deck", tr("New name:"), QLineEdit::Normal, oldName, &ok);
@@ -385,7 +385,7 @@ void DeckPreviewWidget::actRenameDeck()
}
// write change
- deckLoader->getDeckList()->setName(newName);
+ deckLoader->getDeck().deckList.setName(newName);
deckLoader->saveToFile(filePath, DeckFileFormat::getFormatFromName(filePath));
// update VDS
@@ -416,9 +416,7 @@ void DeckPreviewWidget::actRenameFile()
return;
}
- LoadedDeck::LoadInfo lastLoadInfo = deckLoader->getLastLoadInfo();
- lastLoadInfo.fileName = newFilePath;
- deckLoader->setLastLoadInfo(lastLoadInfo);
+ deckLoader->getDeck().lastLoadInfo.fileName = newFilePath;
// update VDS
setFilePath(newFilePath);
diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h
index 6e6fdb9af..e5975db71 100644
--- a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h
+++ b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h
@@ -51,7 +51,7 @@ public:
signals:
void deckLoadRequested(const QString &filePath);
- void openDeckEditor(DeckLoader *deck);
+ void openDeckEditor(const LoadedDeck &deck);
public slots:
void setFilePath(const QString &filePath);
diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp
index 883682749..fdb44dff4 100644
--- a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp
+++ b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp
@@ -211,7 +211,7 @@ QStringList VisualDeckStorageFolderDisplayWidget::gatherAllTagsFromFlowWidget()
// Iterate through all DeckPreviewWidgets
for (DeckPreviewWidget *display : flowWidget->findChildren()) {
// Get tags from each DeckPreviewWidget
- QStringList tags = display->deckLoader->getDeckList()->getTags();
+ QStringList tags = display->deckLoader->getDeck().deckList.getTags();
// Add tags to the list while avoiding duplicates
allTags.append(tags);
diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
index cbc78ba50..0eb45cdcc 100644
--- a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
+++ b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
@@ -95,14 +95,17 @@ QList VisualDeckStorageSortWidget::filterFiles(QListdeckLoader->getDeckList()->getName() < widget2->deckLoader->getDeckList()->getName();
+ return widget1->deckLoader->getDeck().deckList.getName() <
+ widget2->deckLoader->getDeck().deckList.getName();
case Alphabetical:
return QString::localeAwareCompare(info1.fileName(), info2.fileName()) <= 0;
case ByLastModified:
return info1.lastModified() > info2.lastModified();
case ByLastLoaded: {
- QDateTime time1 = QDateTime::fromString(widget1->deckLoader->getDeckList()->getLastLoadedTimestamp());
- QDateTime time2 = QDateTime::fromString(widget2->deckLoader->getDeckList()->getLastLoadedTimestamp());
+ QDateTime time1 =
+ QDateTime::fromString(widget1->deckLoader->getDeck().deckList.getLastLoadedTimestamp());
+ QDateTime time2 =
+ QDateTime::fromString(widget2->deckLoader->getDeck().deckList.getLastLoadedTimestamp());
return time1 > time2;
}
}
diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
index 1686de054..28fd7a5ca 100644
--- a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
+++ b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
@@ -57,7 +57,7 @@ void VisualDeckStorageTagFilterWidget::filterDecksBySelectedTags(const QListdeckLoader->getDeckList()->getTags();
+ QStringList deckTags = deckPreview->deckLoader->getDeck().deckList.getTags();
bool hasAllSelected = std::all_of(selectedTags.begin(), selectedTags.end(),
[&deckTags](const QString &tag) { return deckTags.contains(tag); });
@@ -153,7 +153,7 @@ QSet VisualDeckStorageTagFilterWidget::gatherAllTags() const
for (DeckPreviewWidget *widget : deckWidgets) {
if (widget->checkVisibility()) {
- for (const QString &tag : widget->deckLoader->getDeckList()->getTags()) {
+ for (const QString &tag : widget->deckLoader->getDeck().deckList.getTags()) {
allTags.insert(tag);
}
}
diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.h b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.h
index b205cb67e..b13c51700 100644
--- a/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.h
+++ b/cockatrice/src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.h
@@ -53,7 +53,7 @@ public slots:
signals:
void bannerCardsRefreshed();
void deckLoadRequested(const QString &filePath);
- void openDeckEditor(DeckLoader *deck);
+ void openDeckEditor(const LoadedDeck &deck);
private:
QVBoxLayout *layout;
diff --git a/cockatrice/src/interface/window_main.cpp b/cockatrice/src/interface/window_main.cpp
index 4c9922c80..2e135d170 100644
--- a/cockatrice/src/interface/window_main.cpp
+++ b/cockatrice/src/interface/window_main.cpp
@@ -499,11 +499,7 @@ QString MainWindow::extractInvalidUsernameMessage(QString &in)
if (words.startsWith("\n")) {
out += tr("no unacceptable language as specified by these server rules:",
"note that the following lines will not be translated");
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
for (QString &line : words.split("\n", Qt::SkipEmptyParts)) {
-#else
- for (QString &line : words.split("\n", QString::SkipEmptyParts)) {
-#endif
out += "" + line + "";
}
} else {
@@ -942,9 +938,25 @@ void MainWindow::startupConfigCheck()
const auto reloadOk0 = QtConcurrent::run([] { CardDatabaseManager::getInstance()->loadCardDatabases(); });
}
- qCInfo(WindowMainStartupShortcutsLog) << "[MainWindow] Migrating shortcuts after update detected.";
+ qCInfo(WindowMainStartupShortcutsLog) << "Migrating shortcuts after update detected.";
SettingsCache::instance().shortcuts().migrateShortcuts();
+ if (SettingsCache::instance().getCheckUpdatesOnStartup()) {
+ if (QString(VERSION_STRING).contains("custom", Qt::CaseInsensitive)) {
+ qCInfo(WindowMainStartupShortcutsLog) << "Update has changed to custom version, disabling auto update";
+ SettingsCache::instance().setCheckUpdatesOnStartup(Qt::Unchecked);
+ } else {
+ int channel = 0;
+ if (QString(VERSION_STRING).contains("beta", Qt::CaseInsensitive)) {
+ channel = 1;
+ }
+ if (SettingsCache::instance().getUpdateReleaseChannelIndex() != channel) {
+ qCInfo(WindowMainStartupShortcutsLog) << "Update has changed beta state, updating release channel.";
+ SettingsCache::instance().setUpdateReleaseChannelIndex(channel);
+ }
+ }
+ }
+
SettingsCache::instance().setClientVersion(VERSION_STRING);
} else {
// previous config from this version found
diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp
index 29dacee74..7092a3fd7 100644
--- a/cockatrice/src/main.cpp
+++ b/cockatrice/src/main.cpp
@@ -260,10 +260,8 @@ int main(int argc, char *argv[])
qCInfo(MainLog) << "MainWindow constructor finished";
ui.setWindowIcon(QPixmap("theme:cockatrice"));
-#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
// set name of the app desktop file; used by wayland to load the window icon
QGuiApplication::setDesktopFileName("cockatrice");
-#endif
SettingsCache::instance().setClientID(generateClientID());
diff --git a/dbconverter/CMakeLists.txt b/dbconverter/CMakeLists.txt
deleted file mode 100644
index b2de9dcb8..000000000
--- a/dbconverter/CMakeLists.txt
+++ /dev/null
@@ -1,133 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-project(Dbconverter VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
-
-# ------------------------
-# Sources
-# ------------------------
-set(dbconverter_SOURCES src/main.cpp src/mocks.cpp ${VERSION_STRING_CPP})
-
-# ------------------------
-# Qt configuration
-# ------------------------
-set(QT_DONT_USE_QTGUI TRUE)
-set(CMAKE_AUTOMOC ON)
-set(CMAKE_AUTOUIC ON)
-set(CMAKE_AUTORCC ON)
-
-# ------------------------
-# Build executable
-# ------------------------
-add_executable(dbconverter MACOSX_BUNDLE ${dbconverter_SOURCES})
-
-# ------------------------
-# Link libraries
-# ------------------------
-target_link_libraries(
- dbconverter
- PRIVATE libcockatrice_card
- PRIVATE libcockatrice_interfaces
- PRIVATE libcockatrice_settings
- PRIVATE ${DB_CONVERTER_QT_MODULES}
-)
-
-# ------------------------
-# Install rules
-# ------------------------
-if(UNIX)
- if(APPLE)
- set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}")
- set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}")
- set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}")
- set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME})
- set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION})
- set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION})
-
- install(TARGETS dbconverter BUNDLE DESTINATION ./)
- else()
- # Linux
- install(TARGETS dbconverter RUNTIME DESTINATION bin/)
- endif()
-elseif(WIN32)
- install(TARGETS dbconverter RUNTIME DESTINATION ./)
-endif()
-
-# ------------------------
-# Qt plugin handling
-# ------------------------
-if(APPLE)
- set(plugin_dest_dir dbconverter.app/Contents/Plugins)
- set(qtconf_dest_dir dbconverter.app/Contents/Resources)
-
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/"
- DESTINATION ${plugin_dest_dir}
- COMPONENT Runtime
- FILES_MATCHING
- PATTERN "*.dSYM" EXCLUDE
- PATTERN "*_debug.dylib" EXCLUDE
- PATTERN "platforms/*.dylib"
- )
-
- install(
- CODE "
- file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths]
-Plugins = Plugins
-Translations = Resources/translations\")
- "
- COMPONENT Runtime
- )
-
- install(
- CODE "
- file(GLOB_RECURSE QTPLUGINS
- \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\")
- set(BU_CHMOD_BUNDLE_ITEMS ON)
- include(BundleUtilities)
- fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/dbconverter.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\")
- "
- COMPONENT Runtime
- )
-endif()
-
-if(WIN32)
- set(plugin_dest_dir Plugins)
- set(qtconf_dest_dir .)
-
- install(
- DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/"
- DESTINATION ./
- FILES_MATCHING
- PATTERN "*.dll"
- )
-
- install(
- DIRECTORY "${QT_PLUGINS_DIR}/"
- DESTINATION ${plugin_dest_dir}
- COMPONENT Runtime
- FILES_MATCHING
- PATTERN "platforms/qdirect2d.dll"
- PATTERN "platforms/qminimal.dll"
- PATTERN "platforms/qoffscreen.dll"
- PATTERN "platforms/qwindows.dll"
- )
-
- install(
- CODE "
- file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths]
-Plugins = Plugins
-Translations = Resources/translations\")
- "
- COMPONENT Runtime
- )
-
- install(
- CODE "
- file(GLOB_RECURSE QTPLUGINS
- \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dll\")
- set(BU_CHMOD_BUNDLE_ITEMS ON)
- include(BundleUtilities)
- fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/dbconverter.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
- "
- COMPONENT Runtime
- )
-endif()
diff --git a/dbconverter/src/main.cpp b/dbconverter/src/main.cpp
deleted file mode 100644
index 67748fa45..000000000
--- a/dbconverter/src/main.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/***************************************************************************
- * Copyright (C) 2019 by Fabio Bas *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the *
- * Free Software Foundation, Inc., *
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
- ***************************************************************************/
-
-#include "main.h"
-
-#include "mocks.h"
-#include "version_string.h"
-
-#include
-#include
-#include
-#include
-#include
-
-int main(int argc, char *argv[])
-{
- QCoreApplication app(argc, argv);
- app.setOrganizationName("Cockatrice");
- app.setApplicationName("Dbconverter");
- app.setApplicationVersion(VERSION_STRING);
-
- QCommandLineParser parser;
- parser.addPositionalArgument("olddb", "Read existing card database from ");
- parser.addPositionalArgument("newdb", "Write new card database to ");
- parser.addHelpOption();
- parser.addVersionOption();
- parser.process(app);
-
- QString oldDbPath;
- QString newDbPath;
- QStringList args = parser.positionalArguments();
- if (args.count() == 2) {
- oldDbPath = QFileInfo(args.at(0)).absoluteFilePath();
- newDbPath = QFileInfo(args.at(1)).absoluteFilePath();
- } else {
- qCritical() << "Usage: dbconverter ";
- parser.showHelp(1);
- exit(0);
- }
-
- CardDatabaseConverter *db = new CardDatabaseConverter;
-
- qInfo() << "---------------------------------------------";
- qInfo() << "Loading cards from" << oldDbPath;
- db->loadCardDatabase(oldDbPath);
- qInfo() << "---------------------------------------------";
- qInfo() << "Saving cards to" << newDbPath;
- db->saveCardDatabase(newDbPath);
- qInfo() << "---------------------------------------------";
-}
diff --git a/dbconverter/src/main.h b/dbconverter/src/main.h
deleted file mode 100644
index d6e734316..000000000
--- a/dbconverter/src/main.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef MAIN_H
-#define MAIN_H
-
-#include
-#include
-#include
-
-static const QList kConstructedCounts = {{4, "legal"}, {0, "banned"}};
-
-static const QList kSingletonCounts = {{1, "legal"}, {0, "banned"}};
-
-class CardDatabaseConverter : public CardDatabase
-{
-public:
- explicit CardDatabaseConverter()
- {
- // Replace querier with one that ignores SettingsCache
- delete querier;
- querier = new CardDatabaseQuerier(this, this, new NoopCardPreferenceProvider());
- };
-
- LoadStatus loadCardDatabase(const QString &path)
- {
- return loader->loadCardDatabase(path);
- }
-
- bool saveCardDatabase(const QString &fileName)
- {
- CockatriceXml4Parser parser(new NoopCardPreferenceProvider());
-
- return parser.saveToFile(createDefaultMagicFormats(), sets, cards, fileName);
- }
-
- FormatRulesNameMap createDefaultMagicFormats()
- {
- // Predefined common exceptions
- CardCondition superTypeIsBasic;
- superTypeIsBasic.field = "type";
- superTypeIsBasic.matchType = "contains";
- superTypeIsBasic.value = "Basic Land";
-
- ExceptionRule basicLands;
- basicLands.conditions.append(superTypeIsBasic);
-
- CardCondition anyNumberAllowed;
- anyNumberAllowed.field = "text";
- anyNumberAllowed.matchType = "contains";
- anyNumberAllowed.value = "A deck can have any number of";
-
- ExceptionRule mayContainAnyNumber;
- mayContainAnyNumber.conditions.append(anyNumberAllowed);
-
- // Map to store default rules
- FormatRulesNameMap defaultFormatRulesNameMap;
-
- // ----------------- Helper lambda to create format -----------------
- auto makeFormat = [&](const QString &name, int minDeck = 60, int maxDeck = -1, int maxSideboardSize = 15,
- const QList &allowedCounts = kConstructedCounts) -> FormatRulesPtr {
- FormatRulesPtr f(new FormatRules);
- f->formatName = name;
- f->allowedCounts = allowedCounts;
- f->minDeckSize = minDeck;
- f->maxDeckSize = maxDeck;
- f->maxSideboardSize = maxSideboardSize;
- f->exceptions.append(basicLands);
- f->exceptions.append(mayContainAnyNumber);
- defaultFormatRulesNameMap.insert(name.toLower(), f);
- return f;
- };
-
- // ----------------- Standard formats -----------------
- makeFormat("Standard");
- makeFormat("Modern");
- makeFormat("Legacy");
- makeFormat("Pioneer");
- makeFormat("Historic");
- makeFormat("Timeless");
- makeFormat("Future");
- makeFormat("OldSchool");
- makeFormat("Premodern");
- makeFormat("Pauper");
- makeFormat("Penny");
-
- // ----------------- Singleton formats -----------------
- makeFormat("Commander", 100, 100, 15, kSingletonCounts);
- makeFormat("Duel", 100, 100, 15, kSingletonCounts);
- makeFormat("Brawl", 60, 60, 15, kSingletonCounts);
- makeFormat("StandardBrawl", 60, 60, 15, kSingletonCounts);
- makeFormat("Oathbreaker", 60, 60, 15, kSingletonCounts);
- makeFormat("PauperCommander", 100, 100, 15, kSingletonCounts);
- makeFormat("Predh", 100, 100, 15, kSingletonCounts);
-
- // ----------------- Restricted formats -----------------
- makeFormat("Vintage", 60, -1, 15, {{4, "legal"}, {1, "restricted"}, {0, "banned"}});
-
- return defaultFormatRulesNameMap;
- }
-};
-
-#endif
diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp
deleted file mode 100644
index b97bd09c7..000000000
--- a/dbconverter/src/mocks.cpp
+++ /dev/null
@@ -1,451 +0,0 @@
-
-#include "mocks.h"
-
-CardDatabaseSettings::CardDatabaseSettings(const QString &settingPath, QObject *parent)
- : SettingsManager(settingPath + "cardDatabase.ini", QString(), QString(), parent)
-{
-}
-void CardDatabaseSettings::setSortKey(QString /* shortName */, unsigned int /* sortKey */)
-{
-}
-void CardDatabaseSettings::setEnabled(QString /* shortName */, bool /* enabled */)
-{
-}
-void CardDatabaseSettings::setIsKnown(QString /* shortName */, bool /* isknown */)
-{
-}
-unsigned int CardDatabaseSettings::getSortKey(QString /* shortName */)
-{
- return 0;
-};
-bool CardDatabaseSettings::isEnabled(QString /* shortName */)
-{
- return true;
-};
-bool CardDatabaseSettings::isKnown(QString /* shortName */)
-{
- return true;
-};
-
-QString SettingsCache::getDataPath()
-{
- return "";
-}
-QString SettingsCache::getSettingsPath()
-{
- return "";
-}
-void SettingsCache::translateLegacySettings()
-{
-}
-QString SettingsCache::getSafeConfigPath(QString /* configEntry */, QString defaultPath) const
-{
- return defaultPath;
-}
-QString SettingsCache::getSafeConfigFilePath(QString /* configEntry */, QString defaultPath) const
-{
- return defaultPath;
-}
-
-void SettingsCache::setUseTearOffMenus(bool /* _useTearOffMenus */)
-{
-}
-void SettingsCache::setCardViewInitialRowsMax(int /* _cardViewInitialRowsMax */)
-{
-}
-void SettingsCache::setCardViewExpandedRowsMax(int /* value */)
-{
-}
-void SettingsCache::setCloseEmptyCardView(const QT_STATE_CHANGED_T /* value */)
-{
-}
-void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T /* value */)
-{
-}
-void SettingsCache::setKnownMissingFeatures(const QString & /* _knownMissingFeatures */)
-{
-}
-void SettingsCache::setCardInfoViewMode(const int /* _viewMode */)
-{
-}
-void SettingsCache::setHighlightWords(const QString & /* _highlightWords */)
-{
-}
-void SettingsCache::setMasterVolume(int /* _masterVolume */)
-{
-}
-void SettingsCache::setLeftJustified(const QT_STATE_CHANGED_T /* _leftJustified */)
-{
-}
-void SettingsCache::setCardScaling(const QT_STATE_CHANGED_T /* _scaleCards */)
-{
-}
-void SettingsCache::setStackCardOverlapPercent(const int /* _verticalCardOverlapPercent */)
-{
-}
-void SettingsCache::setShowMessagePopups(const QT_STATE_CHANGED_T /* _showMessagePopups */)
-{
-}
-void SettingsCache::setShowMentionPopups(const QT_STATE_CHANGED_T /* _showMentionPopus */)
-{
-}
-void SettingsCache::setRoomHistory(const QT_STATE_CHANGED_T /* _roomHistory */)
-{
-}
-void SettingsCache::setLang(const QString & /* _lang */)
-{
-}
-void SettingsCache::setShowTipsOnStartup(bool /* _showTipsOnStartup */)
-{
-}
-void SettingsCache::setSeenTips(const QList & /* _seenTips */)
-{
-}
-void SettingsCache::setDeckPath(const QString & /* _deckPath */)
-{
-}
-void SettingsCache::setFiltersPath(const QString & /*_filtersPath */)
-{
-}
-void SettingsCache::setReplaysPath(const QString & /* _replaysPath */)
-{
-}
-void SettingsCache::setThemesPath(const QString & /* _themesPath */)
-{
-}
-void SettingsCache::setPicsPath(const QString & /* _picsPath */)
-{
-}
-void SettingsCache::setCardDatabasePath(const QString & /* _cardDatabasePath */)
-{
-}
-void SettingsCache::setCustomCardDatabasePath(const QString & /* _customCardDatabasePath */)
-{
-}
-void SettingsCache::setSpoilerDatabasePath(const QString & /* _spoilerDatabasePath */)
-{
-}
-void SettingsCache::setTokenDatabasePath(const QString & /* _tokenDatabasePath */)
-{
-}
-void SettingsCache::setThemeName(const QString & /* _themeName */)
-{
-}
-void SettingsCache::setHomeTabBackgroundSource(const QString & /* _backgroundSource */)
-{
-}
-void SettingsCache::setHomeTabBackgroundShuffleFrequency(int /* frequency */)
-{
-}
-void SettingsCache::setTabVisualDeckStorageOpen(bool /*value*/)
-{
-}
-void SettingsCache::setTabServerOpen(bool /*value*/)
-{
-}
-void SettingsCache::setTabAccountOpen(bool /*value*/)
-{
-}
-void SettingsCache::setTabDeckStorageOpen(bool /*value*/)
-{
-}
-void SettingsCache::setTabReplaysOpen(bool /*value*/)
-{
-}
-void SettingsCache::setTabAdminOpen(bool /*value*/)
-{
-}
-void SettingsCache::setTabLogOpen(bool /*value*/)
-{
-}
-void SettingsCache::setPicDownload(QT_STATE_CHANGED_T /* _picDownload */)
-{
-}
-void SettingsCache::setShowStatusBar(bool /* value */)
-{
-}
-void SettingsCache::setNotificationsEnabled(QT_STATE_CHANGED_T /* _notificationsEnabled */)
-{
-}
-void SettingsCache::setSpectatorNotificationsEnabled(QT_STATE_CHANGED_T /* _spectatorNotificationsEnabled */)
-{
-}
-void SettingsCache::setBuddyConnectNotificationsEnabled(QT_STATE_CHANGED_T /* _buddyConnectNotificationsEnabled */)
-{
-}
-void SettingsCache::setDoubleClickToPlay(QT_STATE_CHANGED_T /* _doubleClickToPlay */)
-{
-}
-void SettingsCache::setClickPlaysAllSelected(QT_STATE_CHANGED_T /* _clickPlaysAllSelected */)
-{
-}
-void SettingsCache::setPlayToStack(QT_STATE_CHANGED_T /* _playToStack */)
-{
-}
-void SettingsCache::setDoNotDeleteArrowsInSubPhases(QT_STATE_CHANGED_T /* _doNotDeleteArrowsInSubPhases */)
-{
-}
-void SettingsCache::setStartingHandSize(int /* _startingHandSize */)
-{
-}
-void SettingsCache::setAnnotateTokens(QT_STATE_CHANGED_T /* _annotateTokens */)
-{
-}
-void SettingsCache::setTabGameSplitterSizes(const QByteArray & /* _tabGameSplitterSizes */)
-{
-}
-void SettingsCache::setShowShortcuts(QT_STATE_CHANGED_T /* _showShortcuts */)
-{
-}
-void SettingsCache::setDisplayCardNames(QT_STATE_CHANGED_T /* _displayCardNames */)
-{
-}
-void SettingsCache::setOverrideAllCardArtWithPersonalPreference(QT_STATE_CHANGED_T /* _displayCardNames */)
-{
-}
-void SettingsCache::setBumpSetsWithCardsInDeckToTop(QT_STATE_CHANGED_T /* _bumpSetsWithCardsInDeckToTop */)
-{
-}
-void SettingsCache::setPrintingSelectorSortOrder(int /* _printingSelectorSortOrder */)
-{
-}
-void SettingsCache::setPrintingSelectorCardSize(int /* _printingSelectorCardSize */)
-{
-}
-void SettingsCache::setIncludeRebalancedCards(bool /* _includeRebalancedCards */)
-{
-}
-void SettingsCache::setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED_T /* _navigationButtonsVisible */)
-{
-}
-void SettingsCache::setDeckEditorBannerCardComboBoxVisible(
- QT_STATE_CHANGED_T /* _deckEditorBannerCardComboBoxVisible */)
-{
-}
-void SettingsCache::setDeckEditorTagsWidgetVisible(QT_STATE_CHANGED_T /* _deckEditorTagsWidgetVisible */)
-{
-}
-void SettingsCache::setVisualDeckStorageSortingOrder(int /* _visualDeckStorageSortingOrder */)
-{
-}
-void SettingsCache::setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T /* value */)
-{
-}
-void SettingsCache::setVisualDeckStorageShowTagFilter(QT_STATE_CHANGED_T /* _showTags */)
-{
-}
-void SettingsCache::setVisualDeckStorageDefaultTagsList(QStringList /* _defaultTagsList */)
-{
-}
-void SettingsCache::setVisualDeckStorageSearchFolderNames(QT_STATE_CHANGED_T /* value */)
-{
-}
-void SettingsCache::setVisualDeckStorageShowBannerCardComboBox(QT_STATE_CHANGED_T /* _showBannerCardComboBox */)
-{
-}
-void SettingsCache::setVisualDeckStorageShowTagsOnDeckPreviews(QT_STATE_CHANGED_T /* _showTags */)
-{
-}
-void SettingsCache::setVisualDeckStorageCardSize(int /* _visualDeckStorageCardSize */)
-{
-}
-void SettingsCache::setVisualDeckStorageDrawUnusedColorIdentities(
- QT_STATE_CHANGED_T /* _visualDeckStorageDrawUnusedColorIdentities */)
-{
-}
-void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity(
- int /* _visualDeckStorageUnusedColorIdentitiesOpacity */)
-{
-}
-void SettingsCache::setVisualDeckStorageTooltipType(int /* value */)
-{
-}
-void SettingsCache::setVisualDeckStoragePromptForConversion(bool /* _visualDeckStoragePromptForConversion */)
-{
-}
-void SettingsCache::setVisualDeckStorageAlwaysConvert(bool /* _visualDeckStorageAlwaysConvert */)
-{
-}
-void SettingsCache::setVisualDeckStorageInGame(QT_STATE_CHANGED_T /* value */)
-{
-}
-void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T /* value */)
-{
-}
-void SettingsCache::setDefaultDeckEditorType(int /* value */)
-{
-}
-void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T /* _enabled */)
-{
-}
-void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsAmount(int /* _amount */)
-{
-}
-void SettingsCache::setVisualDeckEditorSampleHandSize(int /* _amount */)
-{
-}
-void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T /* _horizontalHand */)
-{
-}
-void SettingsCache::setInvertVerticalCoordinate(QT_STATE_CHANGED_T /* _invertVerticalCoordinate */)
-{
-}
-void SettingsCache::setMinPlayersForMultiColumnLayout(int /* _minPlayersForMultiColumnLayout */)
-{
-}
-void SettingsCache::setTapAnimation(QT_STATE_CHANGED_T /* _tapAnimation */)
-{
-}
-void SettingsCache::setAutoRotateSidewaysLayoutCards(QT_STATE_CHANGED_T /* _autoRotateSidewaysLayoutCards */)
-{
-}
-void SettingsCache::setOpenDeckInNewTab(QT_STATE_CHANGED_T /* _openDeckInNewTab */)
-{
-}
-void SettingsCache::setRewindBufferingMs(int /* _rewindBufferingMs */)
-{
-}
-void SettingsCache::setChatMention(QT_STATE_CHANGED_T /* _chatMention */)
-{
-}
-void SettingsCache::setChatMentionCompleter(const QT_STATE_CHANGED_T /* _enableMentionCompleter */)
-{
-}
-void SettingsCache::setChatMentionForeground(QT_STATE_CHANGED_T /* _chatMentionForeground */)
-{
-}
-void SettingsCache::setChatHighlightForeground(QT_STATE_CHANGED_T /* _chatHighlightForeground */)
-{
-}
-void SettingsCache::setChatMentionColor(const QString & /* _chatMentionColor */)
-{
-}
-void SettingsCache::setChatHighlightColor(const QString & /* _chatHighlightColor */)
-{
-}
-void SettingsCache::setZoneViewGroupByIndex(int /* _zoneViewGroupByIndex */)
-{
-}
-void SettingsCache::setZoneViewSortByIndex(int /* _zoneViewSortByIndex */)
-{
-}
-void SettingsCache::setZoneViewPileView(QT_STATE_CHANGED_T /* _zoneViewPileView */)
-{
-}
-void SettingsCache::setSoundEnabled(QT_STATE_CHANGED_T /* _soundEnabled */)
-{
-}
-void SettingsCache::setSoundThemeName(const QString & /* _soundThemeName */)
-{
-}
-void SettingsCache::setIgnoreUnregisteredUsers(QT_STATE_CHANGED_T /* _ignoreUnregisteredUsers */)
-{
-}
-void SettingsCache::setIgnoreUnregisteredUserMessages(QT_STATE_CHANGED_T /* _ignoreUnregisteredUserMessages */)
-{
-}
-void SettingsCache::setMainWindowGeometry(const QByteArray & /* _mainWindowGeometry */)
-{
-}
-void SettingsCache::setTokenDialogGeometry(const QByteArray & /* _tokenDialogGeometry */)
-{
-}
-void SettingsCache::setSetsDialogGeometry(const QByteArray & /* _setsDialogGeometry */)
-{
-}
-void SettingsCache::setPixmapCacheSize(const int /* _pixmapCacheSize */)
-{
-}
-void SettingsCache::setNetworkCacheSizeInMB(const int /* _networkCacheSize */)
-{
-}
-void SettingsCache::setNetworkRedirectCacheTtl(const int /* _redirectCacheTtl */)
-{
-}
-void SettingsCache::setClientID(const QString & /* _clientID */)
-{
-}
-void SettingsCache::setClientVersion(const QString & /* _clientVersion */)
-{
-}
-QStringList SettingsCache::getCountries() const
-{
- static QStringList countries = QStringList() << "us";
- return countries;
-}
-void SettingsCache::setGameDescription(const QString /* _gameDescription */)
-{
-}
-void SettingsCache::setMaxPlayers(const int /* _maxPlayers */)
-{
-}
-void SettingsCache::setGameTypes(const QString /* _gameTypes */)
-{
-}
-void SettingsCache::setOnlyBuddies(const bool /* _onlyBuddies */)
-{
-}
-void SettingsCache::setOnlyRegistered(const bool /* _onlyRegistered */)
-{
-}
-void SettingsCache::setSpectatorsAllowed(const bool /* _spectatorsAllowed */)
-{
-}
-void SettingsCache::setSpectatorsNeedPassword(const bool /* _spectatorsNeedPassword */)
-{
-}
-void SettingsCache::setSpectatorsCanTalk(const bool /* _spectatorsCanTalk */)
-{
-}
-void SettingsCache::setSpectatorsCanSeeEverything(const bool /* _spectatorsCanSeeEverything */)
-{
-}
-void SettingsCache::setCreateGameAsSpectator(const bool /* _createGameAsSpectator */)
-{
-}
-void SettingsCache::setDefaultStartingLifeTotal(const int /* _startingLifeTotal */)
-{
-}
-void SettingsCache::setShareDecklistsOnLoad(const bool /* _shareDecklistsOnLoad */)
-{
-}
-void SettingsCache::setRememberGameSettings(const bool /* _rememberGameSettings */)
-{
-}
-void SettingsCache::setCheckUpdatesOnStartup(QT_STATE_CHANGED_T /* value */)
-{
-}
-void SettingsCache::setStartupCardUpdateCheckPromptForUpdate(bool /* value */)
-{
-}
-void SettingsCache::setStartupCardUpdateCheckAlwaysUpdate(bool /* value */)
-{
-}
-void SettingsCache::setCardUpdateCheckInterval(int /* value */)
-{
-}
-void SettingsCache::setLastCardUpdateCheck(QDate /* value */)
-{
-}
-void SettingsCache::setNotifyAboutUpdate(QT_STATE_CHANGED_T /* _notifyaboutupdate */)
-{
-}
-void SettingsCache::setNotifyAboutNewVersion(QT_STATE_CHANGED_T /* _notifyaboutnewversion */)
-{
-}
-void SettingsCache::setDownloadSpoilerStatus(bool /* _spoilerStatus */)
-{
-}
-void SettingsCache::setUpdateReleaseChannelIndex(int /* value */)
-{
-}
-void SettingsCache::setMaxFontSize(int /* _max */)
-{
-}
-void SettingsCache::setRoundCardCorners(bool /* _roundCardCorners */)
-{
-}
-
-void CardPictureLoader::clearPixmapCache(CardInfoPtr /* card */)
-{
-}
diff --git a/dbconverter/src/mocks.h b/dbconverter/src/mocks.h
deleted file mode 100644
index 929092bd7..000000000
--- a/dbconverter/src/mocks.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Beware of this preprocessor hack used to redefine the settingCache class
- * instead of including it and all of its dependencies.
- * Always set header guards of mocked objects before including any headers
- * with mocked objects.
- */
-
-#include
-#include
-
-#define PICTURELOADER_H
-
-#include "../../cockatrice/src/client/settings/cache_settings.h"
-
-#include
-#include
-
-extern SettingsCache *settingsCache;
-
-class CardPictureLoader
-{
-public:
- static void clearPixmapCache(CardInfoPtr card);
-};
diff --git a/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md b/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md
index 6c5e3512b..7b4bdafc3 100644
--- a/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md
+++ b/doc/doxygen/extra-pages/developer_documentation/card_database_schema_and_parsing.md
@@ -1 +1,16 @@
-@page card_database_schema_and_parsing Card Database Schema and Parsing
\ No newline at end of file
+@page card_database_schema_and_parsing Card Database Schema and Parsing
+
+# Card Database Schemas
+
+Cockatrice uses `XML files` to store information of available cards to be used in the app (`cards.xml`).
+The token file follows the schema of the card database (`tokens.xml`), too.
+
+Saved decks (`.xml`) use a different schema.
+
+- [XSD Schema for `Card Databases`](https://github.com/Cockatrice/Cockatrice/blob/master/doc/carddatabase_v4/cards.xsd) v4
+- [XSD Schema for `Decks`](https://github.com/Cockatrice/Cockatrice/blob/master/doc/deck.xsd) v1
+
+
+# Card Database Parsing
+
+TODO
diff --git a/doc/doxygen/extra-pages/index.md b/doc/doxygen/extra-pages/index.md
index 66113117f..c370a6fc2 100644
--- a/doc/doxygen/extra-pages/index.md
+++ b/doc/doxygen/extra-pages/index.md
@@ -5,4 +5,6 @@
This is the **main landing page** of the Cockatrice documentation.
- Go to the @subpage user_reference page
-- Or check out the @subpage developer_reference
\ No newline at end of file
+- Review the @subpage developer_reference
+
+Or check out the [Cockatrice Webpage](https://cockatrice.github.io/).
diff --git a/doc/doxygen/extra-pages/user_documentation/deck_management/creating_decks.md b/doc/doxygen/extra-pages/user_documentation/deck_management/creating_decks.md
index bfe578afd..153320c3d 100644
--- a/doc/doxygen/extra-pages/user_documentation/deck_management/creating_decks.md
+++ b/doc/doxygen/extra-pages/user_documentation/deck_management/creating_decks.md
@@ -8,10 +8,8 @@ by selecting "Deck Editor" or "Visual Deck Editor" under the "Tabs" application
# Further References
-See @ref editing_decks for information on how to modify the attributes and contents of a deck in the Deck Editor
+- See @ref editing_decks for information on how to modify the attributes and contents of a deck in the Deck Editor
widgets.
-
-See @ref exporting_decks for information on how to store and persist your deck either in-client or to external
+- See @ref exporting_decks for information on how to store and persist your deck either in-client or to external
services.
-
-See @ref importing_decks for information on how to import existing decks either in-client or from external services
\ No newline at end of file
+- See @ref importing_decks for information on how to import existing decks either in-client or from external services
diff --git a/doc/doxygen/extra-pages/user_documentation/deck_management/editing_decks.md b/doc/doxygen/extra-pages/user_documentation/deck_management/editing_decks.md
index 030811306..37b613461 100644
--- a/doc/doxygen/extra-pages/user_documentation/deck_management/editing_decks.md
+++ b/doc/doxygen/extra-pages/user_documentation/deck_management/editing_decks.md
@@ -1,7 +1,6 @@
@page editing_decks Editing Decks
-@subpage editing_decks_classic
+ - @subpage editing_decks_classic
+ - @subpage editing_decks_visual
-@subpage editing_decks_visual
-
-@subpage editing_decks_printings
\ No newline at end of file
+ - @subpage editing_decks_printings
diff --git a/doc/doxygen/extra-pages/user_documentation/index.md b/doc/doxygen/extra-pages/user_documentation/index.md
index 8a5c88c37..33104991a 100644
--- a/doc/doxygen/extra-pages/user_documentation/index.md
+++ b/doc/doxygen/extra-pages/user_documentation/index.md
@@ -1,13 +1,9 @@
@page user_reference User Reference
-@subpage search_syntax_help
-
-@subpage deck_search_syntax_help
-
-@subpage creating_decks
-
-@subpage importing_decks
-
-@subpage editing_decks
-
-@subpage exporting_decks
\ No newline at end of file
+- @subpage search_syntax_help
+- @subpage deck_search_syntax_help
+
+- @subpage creating_decks
+- @subpage importing_decks
+- @subpage editing_decks
+- @subpage exporting_decks
diff --git a/format.sh b/format.sh
index 51fccbdcf..f8c183dfc 100755
--- a/format.sh
+++ b/format.sh
@@ -14,7 +14,6 @@ cd "${BASH_SOURCE%/*}/" || exit 2 # could not find path, this could happen with
# defaults
include=("cockatrice/src" \
-"dbconverter/src" \
"libcockatrice_card" \
"libcockatrice_deck_list" \
"libcockatrice_network" \
diff --git a/libcockatrice_card/libcockatrice/card/card_info.cpp b/libcockatrice_card/libcockatrice/card/card_info.cpp
index 3054a10cf..ae73ef2db 100644
--- a/libcockatrice_card/libcockatrice/card/card_info.cpp
+++ b/libcockatrice_card/libcockatrice/card/card_info.cpp
@@ -75,7 +75,17 @@ QString CardInfo::getCorrectedName() const
return result.remove(rmrx).replace(spacerx, space);
}
-void CardInfo::addToSet(const CardSetPtr &_set, const PrintingInfo _info)
+bool CardInfo::isLegalInFormat(const QString &format) const
+{
+ if (format.isEmpty()) {
+ return true;
+ }
+
+ QString formatLegality = getProperty("format-" + format);
+ return formatLegality == "legal" || formatLegality == "restricted";
+}
+
+void CardInfo::addToSet(const CardSetPtr &_set, const PrintingInfo &_info)
{
if (!_set->contains(smartThis)) {
_set->append(smartThis);
@@ -156,7 +166,7 @@ QString CardInfo::simplifyName(const QString &name)
return simpleName;
}
-const QChar CardInfo::getColorChar() const
+QChar CardInfo::getColorChar() const
{
QString colors = getColors();
switch (colors.size()) {
@@ -178,7 +188,7 @@ void CardInfo::resetReverseRelatedCards2Me()
}
// Back-compatibility methods. Remove ASAP
-const QString CardInfo::getCardType() const
+QString CardInfo::getCardType() const
{
return getProperty(Mtg::CardType);
}
@@ -186,11 +196,11 @@ void CardInfo::setCardType(const QString &value)
{
setProperty(Mtg::CardType, value);
}
-const QString CardInfo::getCmc() const
+QString CardInfo::getCmc() const
{
return getProperty(Mtg::ConvertedManaCost);
}
-const QString CardInfo::getColors() const
+QString CardInfo::getColors() const
{
return getProperty(Mtg::Colors);
}
@@ -198,19 +208,19 @@ void CardInfo::setColors(const QString &value)
{
setProperty(Mtg::Colors, value);
}
-const QString CardInfo::getLoyalty() const
+QString CardInfo::getLoyalty() const
{
return getProperty(Mtg::Loyalty);
}
-const QString CardInfo::getMainCardType() const
+QString CardInfo::getMainCardType() const
{
return getProperty(Mtg::MainCardType);
}
-const QString CardInfo::getManaCost() const
+QString CardInfo::getManaCost() const
{
return getProperty(Mtg::ManaCost);
}
-const QString CardInfo::getPowTough() const
+QString CardInfo::getPowTough() const
{
return getProperty(Mtg::PowTough);
}
diff --git a/libcockatrice_card/libcockatrice/card/card_info.h b/libcockatrice_card/libcockatrice/card/card_info.h
index 00e8fec37..b81cf51dc 100644
--- a/libcockatrice_card/libcockatrice/card/card_info.h
+++ b/libcockatrice_card/libcockatrice/card/card_info.h
@@ -267,18 +267,18 @@ public:
}
//@}
- [[nodiscard]] const QChar getColorChar() const;
+ [[nodiscard]] QChar getColorChar() const;
/** @name Legacy/Convenience Property Accessors */ //@{
- [[nodiscard]] const QString getCardType() const;
+ [[nodiscard]] QString getCardType() const;
void setCardType(const QString &value);
- [[nodiscard]] const QString getCmc() const;
- [[nodiscard]] const QString getColors() const;
+ [[nodiscard]] QString getCmc() const;
+ [[nodiscard]] QString getColors() const;
void setColors(const QString &value);
- [[nodiscard]] const QString getLoyalty() const;
- [[nodiscard]] const QString getMainCardType() const;
- [[nodiscard]] const QString getManaCost() const;
- [[nodiscard]] const QString getPowTough() const;
+ [[nodiscard]] QString getLoyalty() const;
+ [[nodiscard]] QString getMainCardType() const;
+ [[nodiscard]] QString getManaCost() const;
+ [[nodiscard]] QString getPowTough() const;
void setPowTough(const QString &value);
//@}
@@ -291,6 +291,15 @@ public:
*/
[[nodiscard]] QString getCorrectedName() const;
+ /**
+ * @brief Checks if the card is legal in the given format.
+ * A card is considered legal in a format if its properties map contains an entry for "format-", with value
+ * "legal" or "restricted".
+ * @param format The format's name. If empty, will always return true.
+ * @return Whether the card is legal in the given format.
+ */
+ [[nodiscard]] bool isLegalInFormat(const QString &format) const;
+
/**
* @brief Adds a printing to a specific set.
*
@@ -299,7 +308,7 @@ public:
* @param _set The set to which the card should be added.
* @param _info Optional printing information.
*/
- void addToSet(const CardSetPtr &_set, PrintingInfo _info = PrintingInfo());
+ void addToSet(const CardSetPtr &_set, const PrintingInfo &_info = PrintingInfo());
/**
* @brief Combines legality properties from a provided map.
diff --git a/libcockatrice_card/libcockatrice/card/database/card_database.cpp b/libcockatrice_card/libcockatrice/card/database/card_database.cpp
index de84ad814..5c4b408b3 100644
--- a/libcockatrice_card/libcockatrice/card/database/card_database.cpp
+++ b/libcockatrice_card/libcockatrice/card/database/card_database.cpp
@@ -21,7 +21,7 @@ CardDatabase::CardDatabase(QObject *parent,
qRegisterMetaType("CardSetPtr");
// create loader and wire it up
- loader = new CardDatabaseLoader(this, this, pathProvider, prefs);
+ loader = new CardDatabaseLoader(this, this, pathProvider, prefs, setPriorityController);
// re-emit loader signals (so other code doesn't need to know about internals)
connect(loader, &CardDatabaseLoader::loadingFinished, this, &CardDatabase::cardDatabaseLoadingFinished);
connect(loader, &CardDatabaseLoader::loadingFailed, this, &CardDatabase::cardDatabaseLoadingFailed);
diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp b/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp
index 91bb4c741..716477a59 100644
--- a/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp
+++ b/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp
@@ -12,12 +12,13 @@
CardDatabaseLoader::CardDatabaseLoader(QObject *parent,
CardDatabase *db,
ICardDatabasePathProvider *_pathProvider,
- ICardPreferenceProvider *_preferenceProvider)
+ ICardPreferenceProvider *_preferenceProvider,
+ ICardSetPriorityController *_priorityController)
: QObject(parent), database(db), pathProvider(_pathProvider)
{
// instantiate available parsers here and connect them to the database
- availableParsers << new CockatriceXml4Parser(_preferenceProvider);
- availableParsers << new CockatriceXml3Parser;
+ availableParsers << new CockatriceXml4Parser(_preferenceProvider, _priorityController);
+ availableParsers << new CockatriceXml3Parser(_priorityController);
for (auto *p : availableParsers) {
// connect parser outputs to the database adders
diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_loader.h b/libcockatrice_card/libcockatrice/card/database/card_database_loader.h
index f4e690428..861cc95b0 100644
--- a/libcockatrice_card/libcockatrice/card/database/card_database_loader.h
+++ b/libcockatrice_card/libcockatrice/card/database/card_database_loader.h
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
inline Q_LOGGING_CATEGORY(CardDatabaseLoadingLog, "card_database.loading");
inline Q_LOGGING_CATEGORY(CardDatabaseLoadingSuccessOrFailureLog, "card_database.loading.success_or_failure");
@@ -52,7 +53,8 @@ public:
explicit CardDatabaseLoader(QObject *parent,
CardDatabase *db,
ICardDatabasePathProvider *pathProvider,
- ICardPreferenceProvider *preferenceProvider);
+ ICardPreferenceProvider *preferenceProvider,
+ ICardSetPriorityController *_priorityController);
/** @brief Destructor cleans up allocated parsers. */
~CardDatabaseLoader() override;
diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp b/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp
index b2a675b99..26e515a2d 100644
--- a/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp
+++ b/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp
@@ -328,11 +328,7 @@ QMap CardDatabaseQuerier::getAllSubCardTypesWithCount() const
QStringList parts = type.split(" — ");
if (parts.size() > 1) { // Ensure there are subtypes
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QStringList subtypes = parts[1].split(" ", Qt::SkipEmptyParts);
-#else
- QStringList subtypes = parts[1].split(" ", QString::SkipEmptyParts);
-#endif
for (const QString &subtype : subtypes) {
typeCounts[subtype]++;
diff --git a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp b/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp
index e9686f816..f7e6bbfcf 100644
--- a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp
+++ b/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp
@@ -4,6 +4,10 @@
SetNameMap ICardDatabaseParser::sets;
+ICardDatabaseParser::ICardDatabaseParser(ICardSetPriorityController *_cardSetPriorityController)
+ : cardSetPriorityController(_cardSetPriorityController)
+{
+}
void ICardDatabaseParser::clearSetlist()
{
sets.clear();
@@ -19,7 +23,7 @@ CardSetPtr ICardDatabaseParser::internalAddSet(const QString &setName,
return sets.value(setName);
}
- CardSetPtr newSet = CardSet::newInstance(new NoopCardSetPriorityController(), setName);
+ CardSetPtr newSet = CardSet::newInstance(cardSetPriorityController, setName);
newSet->setLongName(longName);
newSet->setSetType(setType);
newSet->setReleaseDate(releaseDate);
diff --git a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h b/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h
index 93d46a3cc..a8eceab5a 100644
--- a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h
+++ b/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h
@@ -20,6 +20,7 @@ class ICardDatabaseParser : public QObject
{
Q_OBJECT
public:
+ ICardDatabaseParser(ICardSetPriorityController *cardSetPriorityController);
~ICardDatabaseParser() override = default;
/**
@@ -59,6 +60,7 @@ public:
protected:
/** @brief Cached global list of sets shared between all parsers. */
static SetNameMap sets;
+ ICardSetPriorityController *cardSetPriorityController;
/**
* @brief Internal helper to add a set to the global set cache.
diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp
index 9df396c74..ba27d63c4 100644
--- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp
+++ b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp
@@ -14,6 +14,11 @@
#define COCKATRICE_XML3_SCHEMALOCATION \
"https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v3/cards.xsd"
+CockatriceXml3Parser::CockatriceXml3Parser(ICardSetPriorityController *_cardSetPriorityController)
+ : ICardDatabaseParser(_cardSetPriorityController)
+{
+}
+
bool CockatriceXml3Parser::getCanParseFile(const QString &fileName, QIODevice &device)
{
qCInfo(CockatriceXml3Log) << "Trying to parse: " << fileName;
diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h
index c3a261739..a01b705aa 100644
--- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h
+++ b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h
@@ -26,7 +26,7 @@ class CockatriceXml3Parser : public ICardDatabaseParser
{
Q_OBJECT
public:
- CockatriceXml3Parser() = default;
+ CockatriceXml3Parser(ICardSetPriorityController *cardSetPriorityController);
~CockatriceXml3Parser() override = default;
/**
diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp
index 0d46aad0f..cc0220526 100644
--- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp
+++ b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp
@@ -14,8 +14,9 @@
#define COCKATRICE_XML4_SCHEMALOCATION \
"https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v4/cards.xsd"
-CockatriceXml4Parser::CockatriceXml4Parser(ICardPreferenceProvider *_cardPreferenceProvider)
- : cardPreferenceProvider(_cardPreferenceProvider)
+CockatriceXml4Parser::CockatriceXml4Parser(ICardPreferenceProvider *_cardPreferenceProvider,
+ ICardSetPriorityController *_cardSetPriorityController)
+ : ICardDatabaseParser(_cardSetPriorityController), cardPreferenceProvider(_cardPreferenceProvider)
{
}
diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h
index 5b47e9090..189e79535 100644
--- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h
+++ b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h
@@ -29,7 +29,8 @@ class CockatriceXml4Parser : public ICardDatabaseParser
{
Q_OBJECT
public:
- explicit CockatriceXml4Parser(ICardPreferenceProvider *cardPreferenceProvider);
+ explicit CockatriceXml4Parser(ICardPreferenceProvider *cardPreferenceProvider,
+ ICardSetPriorityController *cardSetPriorityController);
~CockatriceXml4Parser() override = default;
/**
diff --git a/libcockatrice_deck_list/CMakeLists.txt b/libcockatrice_deck_list/CMakeLists.txt
index 5dffff3f7..a7dd01702 100644
--- a/libcockatrice_deck_list/CMakeLists.txt
+++ b/libcockatrice_deck_list/CMakeLists.txt
@@ -27,6 +27,8 @@ add_library(
libcockatrice/deck_list/tree/inner_deck_list_node.cpp
libcockatrice/deck_list/deck_list.cpp
libcockatrice/deck_list/deck_list_history_manager.cpp
+ libcockatrice/deck_list/deck_list_node_tree.cpp
+ libcockatrice/deck_list/deck_list_node_tree.h
)
add_dependencies(libcockatrice_deck_list libcockatrice_protocol)
diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp
index 09326e619..b453a57ef 100644
--- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp
+++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp
@@ -83,26 +83,23 @@ bool DeckList::Metadata::isEmpty() const
}
DeckList::DeckList()
-{
- root = new InnerDecklistNode;
-}
-
-DeckList::DeckList(const DeckList &other)
- : metadata(other.metadata), sideboardPlans(other.sideboardPlans), root(new InnerDecklistNode(other.getRoot())),
- cachedDeckHash(other.cachedDeckHash)
{
}
DeckList::DeckList(const QString &nativeString)
{
- root = new InnerDecklistNode;
loadFromString_Native(nativeString);
}
+DeckList::DeckList(const Metadata &metadata,
+ const DecklistNodeTree &tree,
+ const QMap &sideboardPlans)
+ : metadata(metadata), sideboardPlans(sideboardPlans), tree(tree)
+{
+}
+
DeckList::~DeckList()
{
- delete root;
-
QMapIterator i(sideboardPlans);
while (i.hasNext())
delete i.next().value();
@@ -152,8 +149,7 @@ bool DeckList::readElement(QXmlStreamReader *xml)
}
}
} else if (childName == "zone") {
- InnerDecklistNode *newZone = getZoneObjFromName(xml->attributes().value("name").toString());
- newZone->readElement(xml);
+ tree.readZoneElement(xml);
} else if (childName == "sideboard_plan") {
SideboardPlan *newSideboardPlan = new SideboardPlan;
if (newSideboardPlan->readElement(xml)) {
@@ -168,7 +164,7 @@ bool DeckList::readElement(QXmlStreamReader *xml)
return true;
}
-void writeMetadata(QXmlStreamWriter *xml, const DeckList::Metadata &metadata)
+static void writeMetadata(QXmlStreamWriter *xml, const DeckList::Metadata &metadata)
{
xml->writeTextElement("lastLoadedTimestamp", metadata.lastLoadedTimestamp);
xml->writeTextElement("deckname", metadata.name);
@@ -195,9 +191,7 @@ void DeckList::write(QXmlStreamWriter *xml) const
writeMetadata(xml, metadata);
// Write zones
- for (int i = 0; i < root->size(); i++) {
- root->at(i)->writeElement(xml);
- }
+ tree.write(xml);
// Write sideboard plans
QMapIterator i(sideboardPlans);
@@ -456,25 +450,13 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata)
QString zoneName = sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN;
// make new entry in decklist
- new DecklistCardNode(cardName, amount, getZoneObjFromName(zoneName), -1, setCode, collectorNumber);
+ tree.addCard(cardName, amount, zoneName, -1, setCode, collectorNumber);
}
refreshDeckHash();
return true;
}
-InnerDecklistNode *DeckList::getZoneObjFromName(const QString &zoneName)
-{
- for (int i = 0; i < root->size(); i++) {
- auto *node = dynamic_cast(root->at(i));
- if (node->getName() == zoneName) {
- return node;
- }
- }
-
- return new InnerDecklistNode(zoneName, root);
-}
-
bool DeckList::loadFromFile_Plain(QIODevice *device)
{
QTextStream in(device);
@@ -519,7 +501,7 @@ QString DeckList::writeToString_Plain(bool prefixSideboardCards, bool slashTappe
*/
void DeckList::cleanList(bool preserveMetadata)
{
- root->clearTree();
+ tree.clear();
if (!preserveMetadata) {
metadata = {};
}
@@ -528,7 +510,7 @@ void DeckList::cleanList(bool preserveMetadata)
QStringList DeckList::getCardList() const
{
- auto nodes = getCardNodes();
+ auto nodes = tree.getCardNodes();
QStringList result;
std::transform(nodes.cbegin(), nodes.cend(), std::back_inserter(result), [](auto node) { return node->getName(); });
@@ -538,7 +520,7 @@ QStringList DeckList::getCardList() const
QList DeckList::getCardRefList() const
{
- auto nodes = getCardNodes();
+ auto nodes = tree.getCardNodes();
QList result;
std::transform(nodes.cbegin(), nodes.cend(), std::back_inserter(result),
@@ -547,42 +529,19 @@ QList DeckList::getCardRefList() const
return result;
}
-QList DeckList::getCardNodes(const QStringList &restrictToZones) const
+QList DeckList::getCardNodes(const QSet &restrictToZones) const
{
- QList result;
-
- auto zoneNodes = getZoneNodes();
- for (auto *zoneNode : zoneNodes) {
- if (!restrictToZones.isEmpty() && !restrictToZones.contains(zoneNode->getName())) {
- continue;
- }
- for (auto *cardNode : *zoneNode) {
- auto *cardCardNode = dynamic_cast(cardNode);
- if (cardCardNode != nullptr) {
- result.append(cardCardNode);
- }
- }
- }
-
- return result;
+ return tree.getCardNodes(restrictToZones);
}
QList DeckList::getZoneNodes() const
{
- QList zones;
- for (auto *node : *root) {
- InnerDecklistNode *currentZone = dynamic_cast(node);
- if (!currentZone)
- continue;
- zones.append(currentZone);
- }
-
- return zones;
+ return tree.getZoneNodes();
}
int DeckList::getSideboardSize() const
{
- auto cards = getCardNodes({DECK_ZONE_SIDE});
+ auto cards = tree.getCardNodes({DECK_ZONE_SIDE});
int size = 0;
for (auto card : cards) {
@@ -594,93 +553,18 @@ int DeckList::getSideboardSize() const
DecklistCardNode *DeckList::addCard(const QString &cardName,
const QString &zoneName,
- const int position,
+ int position,
const QString &cardSetName,
const QString &cardSetCollectorNumber,
const QString &cardProviderId,
- const bool formatLegal)
+ bool formatLegal)
{
- auto *zoneNode = dynamic_cast(root->findChild(zoneName));
- if (zoneNode == nullptr) {
- zoneNode = new InnerDecklistNode(zoneName, root);
- }
-
- auto *node = new DecklistCardNode(cardName, 1, zoneNode, position, cardSetName, cardSetCollectorNumber,
- cardProviderId, formatLegal);
+ auto node =
+ tree.addCard(cardName, 1, zoneName, position, cardSetName, cardSetCollectorNumber, cardProviderId, formatLegal);
refreshDeckHash();
-
return node;
}
-bool DeckList::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode)
-{
- if (node == root) {
- return true;
- }
-
- bool updateHash = false;
- if (rootNode == nullptr) {
- rootNode = root;
- updateHash = true;
- }
-
- int index = rootNode->indexOf(node);
- if (index != -1) {
- delete rootNode->takeAt(index);
-
- if (rootNode->empty()) {
- deleteNode(rootNode, rootNode->getParent());
- }
-
- if (updateHash) {
- refreshDeckHash();
- }
-
- return true;
- }
-
- for (int i = 0; i < rootNode->size(); i++) {
- auto *inner = dynamic_cast(rootNode->at(i));
- if (inner) {
- if (deleteNode(node, inner)) {
- if (updateHash) {
- refreshDeckHash();
- }
-
- return true;
- }
- }
- }
-
- return false;
-}
-
-static QString computeDeckHash(const DeckList &deckList)
-{
- auto mainDeckNodes = deckList.getCardNodes({DECK_ZONE_MAIN});
- auto sideDeckNodes = deckList.getCardNodes({DECK_ZONE_SIDE});
-
- static auto nodesToCardList = [](const QList &nodes, const QString &prefix = {}) {
- QStringList result;
- for (auto node : nodes) {
- for (int i = 0; i < node->getNumber(); ++i) {
- result.append(prefix + node->getName().toLower());
- }
- }
- return result;
- };
-
- QStringList cardList = nodesToCardList(mainDeckNodes) + nodesToCardList(sideDeckNodes, "SB:");
-
- cardList.sort();
- QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1);
- quint64 number = (((quint64)(unsigned char)deckHashArray[0]) << 32) +
- (((quint64)(unsigned char)deckHashArray[1]) << 24) +
- (((quint64)(unsigned char)deckHashArray[2] << 16)) +
- (((quint64)(unsigned char)deckHashArray[3]) << 8) + (quint64)(unsigned char)deckHashArray[4];
- return QString::number(number, 32).rightJustified(8, '0');
-}
-
/**
* Gets the deck hash.
* The hash is computed on the first call to this method, and is cached until the decklist is modified.
@@ -693,7 +577,7 @@ QString DeckList::getDeckHash() const
return cachedDeckHash;
}
- cachedDeckHash = computeDeckHash(*this);
+ cachedDeckHash = tree.computeDeckHash();
return cachedDeckHash;
}
@@ -710,15 +594,7 @@ void DeckList::refreshDeckHash()
*/
void DeckList::forEachCard(const std::function &func) const
{
- // Support for this is only possible if the internal structure
- // doesn't get more complicated.
- for (int i = 0; i < root->size(); i++) {
- InnerDecklistNode *node = dynamic_cast(root->at(i));
- for (int j = 0; j < node->size(); j++) {
- DecklistCardNode *card = dynamic_cast(node->at(j));
- func(node, card);
- }
- }
+ tree.forEachCard(func);
}
DeckListMemento DeckList::createMemento(const QString &reason) const
diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h
index e4206ebc6..e8c57be67 100644
--- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h
+++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h
@@ -11,6 +11,7 @@
#define DECKLIST_H
#include "deck_list_memento.h"
+#include "deck_list_node_tree.h"
#include "tree/inner_deck_list_node.h"
#include
@@ -107,14 +108,14 @@ public:
* - Provide hashing for deck identity (deck hash).
*
* ### Ownership:
- * - Owns the root `InnerDecklistNode` tree.
+ * - Owns the `DecklistNodeTree`.
* - Owns `SideboardPlan` instances stored in `sideboardPlans`.
*
* ### Example workflow:
* ```
* DeckList deck;
* deck.setName("Mono Red Aggro");
- * deck.addCard("Lightning Bolt", "main", -1);
+ * deck.addCard("Lightning Bolt", "main");
* deck.addTag("Aggro");
* deck.saveToFile_Native(device);
* ```
@@ -140,7 +141,7 @@ public:
private:
Metadata metadata; ///< Deck metadata that is stored in the deck file
QMap sideboardPlans; ///< Named sideboard plans.
- InnerDecklistNode *root; ///< Root of the deck tree (zones + cards).
+ DecklistNodeTree tree; ///< The deck tree (zones + cards).
/**
* @brief Cached deck hash, recalculated lazily.
@@ -148,9 +149,6 @@ private:
*/
mutable QString cachedDeckHash;
- // Helpers for traversing the tree
- InnerDecklistNode *getZoneObjFromName(const QString &zoneName);
-
public:
/// @name Metadata setters
///@{
@@ -190,16 +188,28 @@ public:
/// @brief Construct an empty deck.
explicit DeckList();
- /// @brief Copy constructor (deep copies the node tree)
- DeckList(const DeckList &other);
/// @brief Construct from a serialized native-format string.
explicit DeckList(const QString &nativeString);
+ /// @brief Construct from components
+ DeckList(const Metadata &metadata,
+ const DecklistNodeTree &tree,
+ const QMap &sideboardPlans = {});
virtual ~DeckList();
+ /**
+ * @brief Gets a pointer to the underlying node tree.
+ * Note: DO NOT call this method unless the object needs to have access to the underlying model.
+ * For now, only the DeckListModel should be calling this.
+ */
+ DecklistNodeTree *getTree()
+ {
+ return &tree;
+ }
+
/// @name Metadata getters
/// The individual metadata getters still exist for backwards compatibility.
- /// TODO: Figure out when we can remove them.
///@{
+ //! \todo Figure out when we can remove them.
const Metadata &getMetadata() const
{
return metadata;
@@ -270,25 +280,21 @@ public:
void cleanList(bool preserveMetadata = false);
bool isEmpty() const
{
- return root->isEmpty() && metadata.isEmpty() && sideboardPlans.isEmpty();
+ return tree.isEmpty() && metadata.isEmpty() && sideboardPlans.isEmpty();
}
QStringList getCardList() const;
QList getCardRefList() const;
- QList getCardNodes(const QStringList &restrictToZones = QStringList()) const;
+ QList getCardNodes(const QSet &restrictToZones = {}) const;
QList getZoneNodes() const;
int getSideboardSize() const;
- InnerDecklistNode *getRoot() const
- {
- return root;
- }
+
DecklistCardNode *addCard(const QString &cardName,
const QString &zoneName,
- int position,
+ int position = -1,
const QString &cardSetName = QString(),
const QString &cardSetCollectorNumber = QString(),
const QString &cardProviderId = QString(),
const bool formatLegal = true);
- bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr);
///@}
/// @name Deck identity
diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.cpp
new file mode 100644
index 000000000..8f651a061
--- /dev/null
+++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.cpp
@@ -0,0 +1,186 @@
+#include "deck_list_node_tree.h"
+
+#include "tree/deck_list_card_node.h"
+
+#include
+#include
+
+DecklistNodeTree::DecklistNodeTree() : root(new InnerDecklistNode())
+{
+}
+
+DecklistNodeTree::DecklistNodeTree(const DecklistNodeTree &other) : root(new InnerDecklistNode(other.root))
+{
+}
+
+DecklistNodeTree &DecklistNodeTree::operator=(const DecklistNodeTree &other)
+{
+ if (this != &other) {
+ delete root;
+ root = new InnerDecklistNode(other.root);
+ }
+ return *this;
+}
+
+DecklistNodeTree::~DecklistNodeTree()
+{
+ delete root;
+}
+
+bool DecklistNodeTree::isEmpty() const
+{
+ return root->isEmpty();
+}
+
+void DecklistNodeTree::clear()
+{
+ root->clearTree();
+}
+
+QList DecklistNodeTree::getCardNodes(const QSet &restrictToZones) const
+{
+ QList result;
+
+ for (auto *zoneNode : getZoneNodes()) {
+ if (!restrictToZones.isEmpty() && !restrictToZones.contains(zoneNode->getName())) {
+ continue;
+ }
+ for (auto *cardNode : *zoneNode) {
+ auto *cardCardNode = dynamic_cast(cardNode);
+ if (cardCardNode) {
+ result.append(cardCardNode);
+ }
+ }
+ }
+
+ return result;
+}
+
+QList DecklistNodeTree::getZoneNodes() const
+{
+ QList zones;
+ for (auto *node : *root) {
+ InnerDecklistNode *currentZone = dynamic_cast(node);
+ if (!currentZone)
+ continue;
+ zones.append(currentZone);
+ }
+
+ return zones;
+}
+
+QString DecklistNodeTree::computeDeckHash() const
+{
+ auto mainDeckNodes = getCardNodes({DECK_ZONE_MAIN});
+ auto sideDeckNodes = getCardNodes({DECK_ZONE_SIDE});
+
+ static auto nodesToCardList = [](const QList &nodes, const QString &prefix = {}) {
+ QStringList result;
+ for (auto node : nodes) {
+ for (int i = 0; i < node->getNumber(); ++i) {
+ result.append(prefix + node->getName().toLower());
+ }
+ }
+ return result;
+ };
+
+ QStringList cardList = nodesToCardList(mainDeckNodes) + nodesToCardList(sideDeckNodes, "SB:");
+
+ cardList.sort();
+ QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1);
+ quint64 number = (((quint64)(unsigned char)deckHashArray[0]) << 32) +
+ (((quint64)(unsigned char)deckHashArray[1]) << 24) +
+ (((quint64)(unsigned char)deckHashArray[2] << 16)) +
+ (((quint64)(unsigned char)deckHashArray[3]) << 8) + (quint64)(unsigned char)deckHashArray[4];
+ return QString::number(number, 32).rightJustified(8, '0');
+}
+
+void DecklistNodeTree::write(QXmlStreamWriter *xml) const
+{
+ for (int i = 0; i < root->size(); i++) {
+ root->at(i)->writeElement(xml);
+ }
+}
+
+void DecklistNodeTree::readZoneElement(QXmlStreamReader *xml)
+{
+ QString zoneName = xml->attributes().value("name").toString();
+ InnerDecklistNode *newZone = getZoneObjFromName(zoneName);
+ newZone->readElement(xml);
+}
+
+DecklistCardNode *DecklistNodeTree::addCard(const QString &cardName,
+ int amount,
+ const QString &zoneName,
+ int position,
+ const QString &cardSetName,
+ const QString &cardSetCollectorNumber,
+ const QString &cardProviderId,
+ const bool formatLegal)
+{
+ auto *zoneNode = getZoneObjFromName(zoneName);
+ auto *node = new DecklistCardNode(cardName, amount, zoneNode, position, cardSetName, cardSetCollectorNumber,
+ cardProviderId, formatLegal);
+ return node;
+}
+
+bool DecklistNodeTree::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode)
+{
+ if (node == root) {
+ return true;
+ }
+
+ if (rootNode == nullptr) {
+ rootNode = root;
+ }
+
+ int index = rootNode->indexOf(node);
+ if (index != -1) {
+ delete rootNode->takeAt(index);
+
+ if (rootNode->empty()) {
+ deleteNode(rootNode, rootNode->getParent());
+ }
+
+ return true;
+ }
+
+ for (int i = 0; i < rootNode->size(); i++) {
+ auto *inner = dynamic_cast(rootNode->at(i));
+ if (inner) {
+ if (deleteNode(node, inner)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void DecklistNodeTree::forEachCard(const std::function &func) const
+{
+ // Support for this is only possible if the internal structure
+ // doesn't get more complicated.
+ for (int i = 0; i < root->size(); i++) {
+ InnerDecklistNode *node = dynamic_cast(root->at(i));
+ for (int j = 0; j < node->size(); j++) {
+ DecklistCardNode *card = dynamic_cast(node->at(j));
+ func(node, card);
+ }
+ }
+}
+
+/**
+ * Gets the InnerDecklistNode that is the root node for the given zone, creating a new node if it doesn't exist.
+ */
+InnerDecklistNode *DecklistNodeTree::getZoneObjFromName(const QString &zoneName) const
+{
+ for (int i = 0; i < root->size(); i++) {
+ auto *node = dynamic_cast(root->at(i));
+ if (node->getName() == zoneName) {
+ return node;
+ }
+ }
+
+ return new InnerDecklistNode(zoneName, root);
+}
diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.h
new file mode 100644
index 000000000..5cfd4944d
--- /dev/null
+++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.h
@@ -0,0 +1,87 @@
+#ifndef COCKATRICE_DECKLIST_NODE_TREE_H
+#define COCKATRICE_DECKLIST_NODE_TREE_H
+
+#include "libcockatrice/utility/card_ref.h"
+#include "tree/deck_list_card_node.h"
+#include "tree/inner_deck_list_node.h"
+
+#include
+
+class DecklistNodeTree
+{
+ InnerDecklistNode *root; ///< Root of the deck tree (zones + cards).
+
+public:
+ /// @brief Constructs an empty DecklistNodeTree
+ explicit DecklistNodeTree();
+ /// @brief Copy constructor. Deep copies the tree
+ explicit DecklistNodeTree(const DecklistNodeTree &other);
+ /// @brief Copy-assignment operator. Deep copies the tree
+ DecklistNodeTree &operator=(const DecklistNodeTree &other);
+
+ virtual ~DecklistNodeTree();
+
+ /**
+ * @brief Gets a pointer to the underlying root node.
+ * Note: DO NOT call this method unless the object needs to have access to the underlying model.
+ * For now, only the DeckListModel should be calling this.
+ */
+ InnerDecklistNode *getRoot() const
+ {
+ return root;
+ }
+
+ bool isEmpty() const;
+
+ /**
+ * @brief Deletes all nodes except the root.
+ */
+ void clear();
+
+ /**
+ * Gets all card nodes in the tree
+ * @param restrictToZones Only get the nodes in these zones
+ * @return A QList containing all the card nodes in the zone.
+ */
+ QList getCardNodes(const QSet &restrictToZones = {}) const;
+
+ QList getZoneNodes() const;
+
+ /**
+ * @brief Computes the deck hash
+ */
+ QString computeDeckHash() const;
+
+ /**
+ *@brief Writes the contents of the deck to xml
+ */
+ void write(QXmlStreamWriter *xml) const;
+
+ /**
+ * @brief Reads a "zone" section of the xml to this tree
+ */
+ void readZoneElement(QXmlStreamReader *xml);
+
+ DecklistCardNode *addCard(const QString &cardName,
+ int amount,
+ const QString &zoneName,
+ int position,
+ const QString &cardSetName = QString(),
+ const QString &cardSetCollectorNumber = QString(),
+ const QString &cardProviderId = QString(),
+ const bool formatLegal = true);
+ bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr);
+
+ /**
+ * @brief Apply a function to every card in the deck tree. This can modify the cards.
+ *
+ * @param func Function taking (zone node, card node).
+ */
+ void forEachCard(const std::function &func) const;
+
+private:
+ // Helpers for traversing the tree
+ InnerDecklistNode *getZoneObjFromName(const QString &zoneName) const;
+};
+
+#endif // COCKATRICE_DECKLIST_NODE_TREE_H
diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h
index a4f3184cc..46a5897f7 100644
--- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h
+++ b/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h
@@ -1,6 +1,8 @@
#ifndef COCKATRICE_INTERFACE_CARD_SET_PRIORITY_CONTROLLER_H
#define COCKATRICE_INTERFACE_CARD_SET_PRIORITY_CONTROLLER_H
+#include
+
class ICardSetPriorityController
{
public:
diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h b/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h
index b2410781c..949ab5a91 100644
--- a/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h
+++ b/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h
@@ -6,25 +6,25 @@
class NoopCardSetPriorityController : public ICardSetPriorityController
{
public:
- void setSortKey(QString /* shortName */, unsigned int /* sortKey */)
+ void setSortKey(QString /* shortName */, unsigned int /* sortKey */) override
{
}
- void setEnabled(QString /* shortName */, bool /* enabled */)
+ void setEnabled(QString /* shortName */, bool /* enabled */) override
{
}
- void setIsKnown(QString /* shortName */, bool /* isknown */)
+ void setIsKnown(QString /* shortName */, bool /* isknown */) override
{
}
- unsigned int getSortKey(QString /* shortName */)
+ unsigned int getSortKey(QString /* shortName */) override
{
return 0;
}
- bool isEnabled(QString /* shortName */)
+ bool isEnabled(QString /* shortName */) override
{
return true;
}
- bool isKnown(QString /* shortName */)
+ bool isKnown(QString /* shortName */) override
{
return true;
}
diff --git a/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp b/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp
index 2af815246..b678e8276 100644
--- a/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp
+++ b/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp
@@ -283,11 +283,7 @@ bool SetsDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex &source
auto nameIndex = sourceModel()->index(sourceRow, SetsModel::LongNameCol, sourceParent);
auto shortNameIndex = sourceModel()->index(sourceRow, SetsModel::ShortNameCol, sourceParent);
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
const auto filter = filterRegularExpression();
-#else
- const auto filter = filterRegExp();
-#endif
return (sourceModel()->data(typeIndex).toString().contains(filter) ||
sourceModel()->data(nameIndex).toString().contains(filter) ||
diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp
index 05ee480a5..ece3bc2f8 100644
--- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp
+++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp
@@ -5,26 +5,33 @@
DeckListModel::DeckListModel(QObject *parent)
: QAbstractItemModel(parent), lastKnownColumn(1), lastKnownOrder(Qt::AscendingOrder)
{
- // This class will leak the decklist object. We cannot safely delete it in the dtor because the deckList field is a
- // non-owning pointer and another deckList might have been assigned to it.
- // `DeckListModel::cleanList` also leaks for the same reason.
- // TODO: fix the leak
- deckList = new DeckList;
+ deckList = QSharedPointer(new DeckList());
root = new InnerDecklistNode;
}
+DeckListModel::DeckListModel(QObject *parent, const QSharedPointer &deckList) : DeckListModel(parent)
+{
+ setDeckList(deckList);
+}
+
DeckListModel::~DeckListModel()
{
delete root;
}
-QString DeckListModel::getGroupCriteriaForCard(CardInfoPtr info) const
+/**
+ * @brief Extract the value from the card that is used for the group criteria.
+ * @param info Pointer to card information.
+ * @param criteria The group criteria
+ * @return String representing the value of the criteria.
+ */
+static QString extractGroupCriteriaValue(const CardInfoPtr &info, DeckListModelGroupCriteria::Type criteria)
{
if (!info) {
return "unknown";
}
- switch (activeGroupCriteria) {
+ switch (criteria) {
case DeckListModelGroupCriteria::MAIN_TYPE:
return info->getMainCardType();
case DeckListModelGroupCriteria::MANA_COST:
@@ -41,7 +48,7 @@ void DeckListModel::rebuildTree()
beginResetModel();
root->clearTree();
- InnerDecklistNode *listRoot = deckList->getRoot();
+ InnerDecklistNode *listRoot = deckList->getTree()->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
auto *currentZone = dynamic_cast(listRoot->at(i));
@@ -56,7 +63,7 @@ void DeckListModel::rebuildTree()
}
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(currentCard->getName());
- QString groupCriteria = getGroupCriteriaForCard(info);
+ QString groupCriteria = extractGroupCriteriaValue(info, activeGroupCriteria);
auto *groupNode = dynamic_cast(node->findChild(groupCriteria));
@@ -178,22 +185,6 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const
}
}
-void DeckListModel::emitBackgroundUpdates(const QModelIndex &parent)
-{
- int rows = rowCount(parent);
- if (rows == 0)
- return;
-
- QModelIndex topLeft = index(0, 0, parent);
- QModelIndex bottomRight = index(rows - 1, columnCount() - 1, parent);
- emit dataChanged(topLeft, bottomRight, {Qt::BackgroundRole});
-
- for (int r = 0; r < rows; ++r) {
- QModelIndex child = index(r, 0, parent);
- emitBackgroundUpdates(child);
- }
-}
-
QVariant DeckListModel::headerData(const int section, const Qt::Orientation orientation, const int role) const
{
if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal)) {
@@ -252,6 +243,22 @@ Qt::ItemFlags DeckListModel::flags(const QModelIndex &index) const
return result;
}
+void DeckListModel::emitBackgroundUpdates(const QModelIndex &parent)
+{
+ int rows = rowCount(parent);
+ if (rows == 0)
+ return;
+
+ QModelIndex topLeft = index(0, 0, parent);
+ QModelIndex bottomRight = index(rows - 1, columnCount() - 1, parent);
+ emit dataChanged(topLeft, bottomRight, {Qt::BackgroundRole});
+
+ for (int r = 0; r < rows; ++r) {
+ QModelIndex child = index(r, 0, parent);
+ emitBackgroundUpdates(child);
+ }
+}
+
void DeckListModel::emitRecursiveUpdates(const QModelIndex &index)
{
if (!index.isValid()) {
@@ -294,7 +301,6 @@ bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, con
deckList->refreshDeckHash();
emit deckHashChanged();
- emit dataChanged(index, index);
return true;
}
@@ -313,7 +319,7 @@ bool DeckListModel::removeRows(int row, int count, const QModelIndex &parent)
for (int i = 0; i < count; i++) {
AbstractDecklistNode *toDelete = node->takeAt(row);
if (auto *temp = dynamic_cast(toDelete)) {
- deckList->deleteNode(temp->getDataNode());
+ deckList->getTree()->deleteNode(temp->getDataNode());
}
delete toDelete;
}
@@ -354,7 +360,7 @@ DecklistModelCardNode *DeckListModel::findCardNode(const QString &cardName,
return nullptr;
}
- QString groupCriteria = getGroupCriteriaForCard(info);
+ QString groupCriteria = extractGroupCriteriaValue(info, activeGroupCriteria);
InnerDecklistNode *groupNode = dynamic_cast(zoneNode->findChild(groupCriteria));
if (!groupNode) {
return nullptr;
@@ -407,7 +413,7 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam
CardInfoPtr cardInfo = card.getCardPtr();
PrintingInfo printingInfo = card.getPrinting();
- QString groupCriteria = getGroupCriteriaForCard(cardInfo);
+ QString groupCriteria = extractGroupCriteriaValue(cardInfo, activeGroupCriteria);
InnerDecklistNode *groupNode = createNodeIfNeeded(groupCriteria, zoneNode);
const QModelIndex parentIndex = nodeToIndex(groupNode);
@@ -421,7 +427,7 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam
auto *decklistCard =
deckList->addCard(cardInfo->getName(), zoneName, insertRow, cardSetName, printingInfo.getProperty("num"),
- printingInfo.getProperty("uuid"), isCardLegalForCurrentFormat(cardInfo));
+ printingInfo.getProperty("uuid"), cardInfo->isLegalInFormat(deckList->getGameFormat()));
beginInsertRows(parentIndex, insertRow, insertRow);
cardNode = new DecklistModelCardNode(decklistCard, groupNode, insertRow);
@@ -436,10 +442,44 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam
}
sort(lastKnownColumn, lastKnownOrder);
emitRecursiveUpdates(parentIndex);
- return nodeToIndex(cardNode);
+ auto index = nodeToIndex(cardNode);
+
+ emit cardAddedAt(index);
+
+ return index;
}
-int DeckListModel::findSortedInsertRow(InnerDecklistNode *parent, CardInfoPtr cardInfo) const
+bool DeckListModel::offsetCountAtIndex(const QModelIndex &idx, int offset)
+{
+ if (!idx.isValid()) {
+ return false;
+ }
+
+ auto *node = static_cast(idx.internalPointer());
+ auto *card = dynamic_cast(node);
+
+ if (!card) {
+ return false;
+ }
+
+ const QModelIndex numberIndex = idx.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT);
+ const int count = numberIndex.data(Qt::EditRole).toInt();
+ const int newCount = count + offset;
+
+ if (newCount <= 0) {
+ removeRow(idx.row(), idx.parent());
+ } else {
+ setData(numberIndex, newCount, Qt::EditRole);
+ }
+
+ if (offset > 0) {
+ emit cardAddedAt(idx);
+ }
+
+ return true;
+}
+
+int DeckListModel::findSortedInsertRow(const InnerDecklistNode *parent, const CardInfoPtr &cardInfo) const
{
if (!cardInfo) {
return parent->size(); // fallback: append at end
@@ -547,56 +587,74 @@ void DeckListModel::setActiveFormat(const QString &_format)
void DeckListModel::cleanList()
{
- setDeckList(new DeckList);
+ setDeckList(QSharedPointer(new DeckList()));
}
/**
* @param _deck The deck.
*/
-void DeckListModel::setDeckList(DeckList *_deck)
+void DeckListModel::setDeckList(const QSharedPointer &_deck)
{
if (deckList != _deck) {
deckList = _deck;
}
rebuildTree();
+ emit deckReplaced();
+}
+
+void DeckListModel::forEachCard(const std::function &func)
+{
+ deckList->forEachCard(func);
+}
+
+static QList cardNodesToExactCards(QList nodes)
+{
+ QList cards;
+ for (auto node : nodes) {
+ ExactCard card = CardDatabaseManager::query()->getCard(node->toCardRef());
+ if (card) {
+ for (int k = 0; k < node->getNumber(); ++k) {
+ cards.append(card);
+ }
+ } else {
+ qDebug() << "Card not found in database!";
+ }
+ }
+
+ return cards;
}
QList