Compare commits

..

11 Commits

Author SHA1 Message Date
tooomm
9d803387b7 move doxygen files in one location 2025-10-05 19:22:42 +02:00
tooomm
5905787d3e Update workflow trigger 2025-10-05 18:58:14 +02:00
tooomm
50ba36e2d5 simplify 2025-10-05 18:58:14 +02:00
tooomm
fd67ee6d1b add links to existing resources 2025-10-05 18:58:14 +02:00
tooomm
e41059fa1b remove comment wrapping 2025-10-05 18:58:13 +02:00
tooomm
5df911938b add contributing to input 2025-10-05 18:58:13 +02:00
tooomm
057c7f0d3f fix contributing and layout 2025-10-05 18:58:13 +02:00
tooomm
07339efa3c add doxygen-extra-pages to INPUT 2025-10-05 18:58:13 +02:00
tooomm
2012bfd04b Don't build latex 2025-10-04 19:17:33 +02:00
tooomm
27f177fe08 upload artifacts 2025-10-04 19:17:33 +02:00
tooomm
5d61cfb133 Use tag ref as dynamic project number 2025-10-04 19:15:54 +02:00
1014 changed files with 72148 additions and 106311 deletions

View File

@@ -1,4 +1,4 @@
FROM fedora:43
FROM fedora:41
RUN dnf install -y \
ccache \

View File

@@ -1,21 +0,0 @@
FROM debian:11
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential \
ccache \
clang-format \
cmake \
file \
g++ \
git \
libmariadb-dev-compat \
libprotobuf-dev \
libqt5sql5-mysql \
libqt5websockets5-dev \
ninja-build \
protobuf-compiler \
qttools5-dev \
qttools5-dev-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -11,8 +11,7 @@
# --debug or --release sets the build type ie CMAKE_BUILD_TYPE
# --ccache [<size>] uses ccache and shows stats, optionally provide size
# --dir <dir> sets the name of the build dir, default is "build"
# --target-macos-version <version> sets the min os version - only used for macOS builds
# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_NO_CLIENT MAKE_TEST USE_CCACHE CCACHE_SIZE BUILD_DIR CMAKE_GENERATOR TARGET_MACOS_VERSION
# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_TEST USE_CCACHE CCACHE_SIZE BUILD_DIR CMAKE_GENERATOR
# (correspond to args: --debug/--release --install --package <package type> --suffix <suffix> --server --test --ccache <ccache_size> --dir <dir>)
# exitcode: 1 for failure, 3 for invalid arguments
@@ -47,10 +46,6 @@ while [[ $# != 0 ]]; do
MAKE_SERVER=1
shift
;;
'--no-client')
MAKE_NO_CLIENT=1
shift
;;
'--test')
MAKE_TEST=1
shift
@@ -71,10 +66,6 @@ while [[ $# != 0 ]]; do
shift
fi
;;
'--vcpkg')
USE_VCPKG=1
shift
;;
'--dir')
shift
if [[ $# == 0 ]]; then
@@ -84,15 +75,6 @@ while [[ $# != 0 ]]; do
BUILD_DIR="$1"
shift
;;
'--target-macos-version')
shift
if [[ $# == 0 ]]; then
echo "::error file=$0::--target-macos-version expects an argument"
exit 3
fi
TARGET_MACOS_VERSION="$1"
shift
;;
*)
echo "::error file=$0::unrecognized option: $1"
exit 3
@@ -121,9 +103,6 @@ flags=("-DCMAKE_BUILD_TYPE=$BUILDTYPE")
if [[ $MAKE_SERVER ]]; then
flags+=("-DWITH_SERVER=1")
fi
if [[ $MAKE_NO_CLIENT ]]; then
flags+=("-DWITH_CLIENT=0" "-DWITH_ORACLE=0" "-DWITH_DBCONVERTER=0")
fi
if [[ $MAKE_TEST ]]; then
flags+=("-DTEST=1")
fi
@@ -137,9 +116,6 @@ fi
if [[ $PACKAGE_TYPE ]]; then
flags+=("-DCPACK_GENERATOR=$PACKAGE_TYPE")
fi
if [[ $USE_VCPKG ]]; then
flags+=("-DUSE_VCPKG=1")
fi
# Add cmake --build flags
buildflags=(--config "$BUILDTYPE")
@@ -156,35 +132,9 @@ function ccachestatsverbose() {
# Compile
if [[ $RUNNER_OS == macOS ]]; then
if [[ $TARGET_MACOS_VERSION ]]; then
# CMAKE_OSX_DEPLOYMENT_TARGET is a vanilla cmake flag needed to compile to target macOS version
flags+=("-DCMAKE_OSX_DEPLOYMENT_TARGET=$TARGET_MACOS_VERSION")
# vcpkg dependencies need a vcpkg triplet file to compile to the target macOS version
# an easy way is to copy the x64-osx.cmake file and modify it
triplets_dir="/tmp/cmake/triplets"
triplet_version="custom-triplet"
triplet_file="$triplets_dir/$triplet_version.cmake"
arch=$(uname -m)
if [[ $arch == x86_64 ]]; then
arch="x64"
fi
mkdir -p "$triplets_dir"
cp "../vcpkg/triplets/$arch-osx.cmake" "$triplet_file"
echo "set(VCPKG_CMAKE_SYSTEM_VERSION $TARGET_MACOS_VERSION)" >>"$triplet_file"
echo "set(VCPKG_OSX_DEPLOYMENT_TARGET $TARGET_MACOS_VERSION)" >>"$triplet_file"
flags+=("-DVCPKG_OVERLAY_TRIPLETS=$triplets_dir")
flags+=("-DVCPKG_HOST_TRIPLET=$triplet_version")
flags+=("-DVCPKG_TARGET_TRIPLET=$triplet_version")
echo "::group::Generated triplet $triplet_file"
cat "$triplet_file"
echo "::endgroup::"
fi
echo "::group::Signing Certificate"
if [[ -n "$MACOS_CERTIFICATE_NAME" ]]; then
echo "$MACOS_CERTIFICATE" | base64 --decode >"certificate.p12"
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
security default-keychain -s build.keychain
security set-keychain-settings -t 3600 -l build.keychain
@@ -196,29 +146,6 @@ if [[ $RUNNER_OS == macOS ]]; then
echo "No signing certificate configured. Skipping set up of keychain in macOS environment."
fi
echo "::endgroup::"
if [[ $MAKE_PACKAGE ]]; then
# Workaround https://github.com/actions/runner-images/issues/7522
# have hdiutil repeat the command 10 times in hope of success
hdiutil_script="/tmp/hdiutil.sh"
# shellcheck disable=SC2016
echo '#!/bin/bash
i=0
while ! hdiutil "$@"; do
if (( ++i >= 10 )); then
echo "Error: hdiutil failed $i times!" >&2
break
fi
sleep 1
done' >"$hdiutil_script"
chmod +x "$hdiutil_script"
flags+=(-DCPACK_COMMAND_HDIUTIL="$hdiutil_script")
fi
elif [[ $RUNNER_OS == Windows ]]; then
# Enable MTT, see https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
# and https://devblogs.microsoft.com/cppblog/cpp-build-throughput-investigation-and-tune-up/#multitooltask-mtt
buildflags+=(-- -p:UseMultiToolTask=true -p:EnableClServerMode=true)
fi
if [[ $USE_CCACHE ]]; then
@@ -229,13 +156,17 @@ fi
echo "::group::Configure cmake"
cmake --version
echo "Running cmake with flags: ${flags[*]}"
cmake .. "${flags[@]}"
echo "::endgroup::"
echo "::group::Build project"
echo "Running cmake --build with flags: ${buildflags[*]}"
cmake --build . "${buildflags[@]}"
if [[ $RUNNER_OS == Windows ]]; then
# Enable MTT, see https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
# and https://devblogs.microsoft.com/cppblog/cpp-build-throughput-investigation-and-tune-up/#multitooltask-mtt
cmake --build . "${buildflags[@]}" -- -p:UseMultiToolTask=true -p:EnableClServerMode=true
else
cmake --build . "${buildflags[@]}"
fi
echo "::endgroup::"
if [[ $USE_CCACHE ]]; then
@@ -244,19 +175,6 @@ if [[ $USE_CCACHE ]]; then
echo "::endgroup::"
fi
if [[ $RUNNER_OS == macOS ]]; then
echo "::group::Inspect Mach-O binaries"
for app in cockatrice oracle servatrice dbconverter; do
binary="$GITHUB_WORKSPACE/build/$app/$app.app/Contents/MacOS/$app"
echo "Inspecting $app..."
vtool -show-build "$binary"
file "$binary"
lipo -info "$binary"
echo ""
done
echo "::endgroup::"
fi
if [[ $MAKE_TEST ]]; then
echo "::group::Run tests"
ctest -C "$BUILDTYPE" --output-on-failure
@@ -271,6 +189,12 @@ fi
if [[ $MAKE_PACKAGE ]]; then
echo "::group::Create package"
if [[ $RUNNER_OS == macOS ]]; then
# Workaround https://github.com/actions/runner-images/issues/7522
echo "killing XProtectBehaviorService"; sudo pkill -9 XProtect >/dev/null || true;
echo "waiting for XProtectBehaviorService kill"; while pgrep "XProtect"; do sleep 3; done;
fi
cmake --build . --target package --config "$BUILDTYPE"
echo "::endgroup::"

View File

@@ -137,11 +137,10 @@ if [[ $SAVE ]]; then
fi
# Set compile function, runs the compile script on the image, passes arguments to the script
# shellcheck disable=2120
function RUN ()
{
echo "running image:"
if [[ $(docker images) =~ $IMAGE_NAME ]]; then
if [[ $(docker images) =~ "$IMAGE_NAME" ]]; then
local args=(--mount "type=bind,source=$PWD,target=/src")
args+=(--workdir "/src")
args+=(--user "$(id -u):$(id -g)")
@@ -152,7 +151,6 @@ function RUN ()
if [[ -n "$CMAKE_GENERATOR" ]]; then
args+=(--env "CMAKE_GENERATOR=$CMAKE_GENERATOR")
fi
# shellcheck disable=2086
docker run "${args[@]}" $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS "$@"
return $?
else
@@ -166,6 +164,5 @@ function RUN ()
if [[ $INTERACTIVE ]]; then
export BUILD_SCRIPT="-i"
export RUN_ARGS="$RUN_ARGS -it"
# shellcheck disable=2119
RUN
fi

View File

@@ -11,19 +11,11 @@ if ! git merge-base origin/master HEAD; then
fi
# Check formatting using format.sh
echo "Checking your code using format.sh..."
echo "Checking your code using clang-format/cmake-format..."
diff="$(./format.sh --diff --cmake --shell --print-version --branch origin/master)"
diff="$(./format.sh --diff --cmake --cf-version --branch origin/master)"
err=$?
sep="
----------
"
used_version="${diff%%"$sep"*}"
diff="${diff#*"$sep"}"
changes_to_make="${diff%%"$sep"*}"
files_to_edit="${diff#*"$sep"}"
case $err in
1)
cat <<EOM
@@ -41,13 +33,14 @@ case $err in
***********************************************************
Used version:
$used_version
Affected files:
$files_to_edit
${diff%%
----------
*}
The following changes should be made:
$changes_to_make
${diff#*
----------
}
Exiting...
EOM
@@ -65,9 +58,6 @@ EOM
*** ***
***********************************************************
Used version:
$used_version
Exiting...
EOM
exit 0

View File

@@ -23,8 +23,8 @@ Available pre-compiled binaries for installation:
• <kbd>Debian 13</kbd> <sub><i>Trixie</i></sub>
• <kbd>Debian 12</kbd> <sub><i>Bookworm</i></sub>
• <kbd>Debian 11</kbd> <sub><i>Bullseye</i></sub>
• <kbd>Fedora 43</kbd>
• <kbd>Fedora 42</kbd>
• <kbd>Fedora 41</kbd>
<sub>We are also packaged in <kbd>Arch Linux</kbd>'s <a href="https://archlinux.org/packages/extra/x86_64/cockatrice">official extra repository</a>, courtesy of @FFY00.</sub>
<sub>General Linux support is available via a <kbd>flatpak</kbd> package at <a href="https://flathub.org/apps/io.github.Cockatrice.cockatrice">Flathub</a>!</sub>

View File

@@ -25,9 +25,6 @@ IndentCaseLabels: true
PointerAlignment: Right
SortIncludes: true
IncludeBlocks: Regroup
StatementAttributeLikeMacros: [emit]
# requires clang-format 16
# RemoveSemicolon: true
---
Language: Proto
AllowShortFunctionsOnASingleLine: None

View File

@@ -1,4 +1,4 @@
<!--! @page contributing Contributing -->
@page contributing Contributing
&nbsp; [Introduction](#contributing-to-cockatrice) | [Code Style Guide](
#code-style-guide) | [Translations](#translations) | [Release Management](

View File

@@ -4,39 +4,25 @@ permissions:
contents: write
id-token: write
attestations: write
actions: write # needed for ccache action to be able to delete gha caches
on:
push:
branches:
- master
paths:
- '*/**' # matches all files not in root
- '!**.md'
- '!.github/**'
- '!.husky/**'
- '!.tx/**'
- '!doc/**'
- '!webclient/**'
- '.github/workflows/desktop-build.yml'
- 'CMakeLists.txt'
- 'vcpkg.json'
- 'vcpkg'
paths-ignore:
- '**.md'
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
- '.github/workflows/docker-release.yml'
tags:
- '*'
pull_request:
paths:
- '*/**' # matches all files not in root
- '!**.md'
- '!.github/**'
- '!.husky/**'
- '!.tx/**'
- '!doc/**'
- '!webclient/**'
- '.github/workflows/desktop-build.yml'
- 'CMakeLists.txt'
- 'vcpkg.json'
- 'vcpkg'
paths-ignore:
- '**.md'
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on master)
concurrency:
@@ -70,7 +56,7 @@ jobs:
- name: Checkout
if: steps.configure.outputs.tag != null
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
fetch-depth: 0
@@ -114,12 +100,7 @@ jobs:
- distro: Debian
version: 11
package: DEB
- distro: Servatrice_Debian
version: 11
package: DEB
test: skip
server_only: yes
test: skip # Running tests on all distros is superfluous
- distro: Debian
version: 12
@@ -131,12 +112,12 @@ jobs:
package: DEB
- distro: Fedora
version: 42
version: 41
package: RPM
test: skip # Running tests on all distros is superfluous
- distro: Fedora
version: 43
version: 42
package: RPM
- distro: Ubuntu
@@ -162,7 +143,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Restore compiler cache (ccache)
id: ccache_restore
@@ -196,11 +177,10 @@ jobs:
SUFFIX: '-${{matrix.distro}}${{matrix.version}}'
package: '${{matrix.package}}'
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
NO_CLIENT: ${{matrix.server_only == 'yes' && '--no-client' || '' }}
run: |
source .ci/docker.sh
RUN --server --release --package "$package" --dir "$BUILD_DIR" \
--ccache "$CCACHE_SIZE" $NO_CLIENT
--ccache "$CCACHE_SIZE"
.ci/name_build.sh
- name: Save compiler cache (ccache)
@@ -213,7 +193,7 @@ jobs:
- name: Upload artifact
id: upload_artifact
if: matrix.package != 'skip'
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: ${{matrix.distro}}${{matrix.version}}-package
path: ${{steps.build.outputs.path}}
@@ -245,135 +225,78 @@ jobs:
GH_TOKEN: ${{github.token}}
run: gh attestation verify ${{steps.build.outputs.path}} -R Cockatrice/Cockatrice
build-vcpkg:
build-macos:
strategy:
fail-fast: false
matrix:
include:
- os: macOS
target: 13
runner: macos-15-intel
- target: 13
soc: Intel
xcode: "16.4"
os: macos-13
xcode: "14.3.1"
type: Release
override_target: 13
make_package: 1
package_suffix: "-macOS13_Intel"
artifact_name: macOS13_Intel-package
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false # qt caches take too much space for macOS (1.1Gi)
cmake_generator: Ninja
use_ccache: 1
- os: macOS
target: 14
runner: macos-14
- target: 14
soc: Apple
os: macos-14
xcode: "15.4"
type: Release
make_package: 1
package_suffix: "-macOS14"
artifact_name: macOS14-package
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false
cmake_generator: Ninja
use_ccache: 1
- os: macOS
target: 15
runner: macos-15
- target: 15
soc: Apple
xcode: "16.4"
os: macos-15
xcode: "16.2"
type: Release
make_package: 1
package_suffix: "-macOS15"
artifact_name: macOS15-package
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false
cmake_generator: Ninja
use_ccache: 1
- os: macOS
target: 15
runner: macos-15
- target: 15
soc: Apple
xcode: "16.4"
os: macos-15
xcode: "16.2"
type: Debug
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false
cmake_generator: Ninja
use_ccache: 1
- os: Windows
target: 7
runner: windows-2022
type: Release
make_package: 1
package_suffix: "-Win7"
artifact_name: Windows7-installer
qt_version: 5.15.*
qt_arch: win64_msvc2019_64
cache_qt: true
cmake_generator: "Visual Studio 17 2022"
cmake_generator_platform: x64
- os: Windows
target: 10
runner: windows-2022
type: Release
make_package: 1
package_suffix: "-Win10"
artifact_name: Windows10-installer
qt_version: 6.6.*
qt_arch: win64_msvc2019_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: true
cmake_generator: "Visual Studio 17 2022"
cmake_generator_platform: x64
name: ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
name: macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
needs: configure
runs-on: ${{matrix.runner}}
runs-on: ${{matrix.os}}
continue-on-error: ${{matrix.allow-failure == 'yes'}}
env:
# Common parameters for all macOS builds
QT_VERSION: 6.6.*
QT_ARCH: clang_64
QT_MODULES: "qtimageformats qtmultimedia qtwebsockets"
# Build-specific environment variables
CCACHE_DIR: ${{github.workspace}}/.ccache/${{matrix.os}}-${{matrix.type}}
CCACHE_SIZE: 500M
DEVELOPER_DIR:
/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer
CMAKE_GENERATOR: 'Ninja'
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
- name: Add msbuild to PATH
if: matrix.os == 'Windows'
id: add-msbuild
uses: microsoft/setup-msbuild@v2
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
uses: jianmingyong/ccache-action@v1
with:
install-type: "binary"
ccache-key-prefix: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}
max-size: 500M
ccache-key-prefix: ccache-${{matrix.os}}-${{matrix.soc}}-${{matrix.type}}
gh-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Qt ${{matrix.qt_version}}
# Using jurplel/install-qt-action to install Qt without using brew
# qt build using vcpkg either just fails or takes too long to build
- name: Install Qt ${{env.QT_VERSION}}
uses: jurplel/install-qt-action@v4
with:
version: ${{matrix.qt_version}}
arch: ${{matrix.qt_arch}}
modules: ${{matrix.qt_modules}}
cache: ${{matrix.cache_qt}}
cache: false # qt caches take too much space for macOS (1.1Gi)
version: ${{env.QT_VERSION}}
arch: ${{env.QT_ARCH}}
modules: ${{env.QT_MODULES}}
- name: Setup vcpkg cache
id: vcpkg-cache
@@ -381,30 +304,24 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}
# uses environment variables, see compile.sh for more details
- name: Build Cockatrice
id: build
- name: Build on Xcode ${{matrix.xcode}}
shell: bash
id: build
env:
BUILDTYPE: '${{matrix.type}}'
MAKE_PACKAGE: '${{matrix.make_package}}'
PACKAGE_SUFFIX: '${{matrix.package_suffix}}'
CMAKE_GENERATOR: ${{matrix.cmake_generator}}
CMAKE_GENERATOR_PLATFORM: ${{matrix.cmake_generator_platform}}
USE_CCACHE: ${{matrix.use_ccache}}
VCPKG_DISABLE_METRICS: 1
VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite'
# macOS-specific environment variables, will be ignored on Windows
PACKAGE_SUFFIX: '-macOS${{matrix.target}}_${{matrix.soc}}'
MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
DEVELOPER_DIR: '/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer'
TARGET_MACOS_VERSION: ${{ matrix.override_target }}
run: .ci/compile.sh --server --test --vcpkg
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
VCPKG_DISABLE_METRICS: 1
VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite'
run: .ci/compile.sh --server --test --ccache "$CCACHE_SIZE"
- name: Sign app bundle
if: matrix.os == 'macOS' && matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null)
if: matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null)
env:
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
@@ -416,7 +333,7 @@ jobs:
fi
- name: Notarize app bundle
if: matrix.os == 'macOS' && matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null)
if: matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null)
env:
MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}
@@ -427,20 +344,20 @@ jobs:
# Store the notarization credentials so that we can prevent a UI password dialog from blocking the CI
echo "Create keychain profile"
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$MACOS_NOTARIZATION_APPLE_ID" --team-id "$MACOS_NOTARIZATION_TEAM_ID" --password "$MACOS_NOTARIZATION_PWD"
# We can't notarize an app bundle directly, but we need to compress it as an archive.
# Therefore, we create a zip file containing our app bundle, so that we can send it to the
# notarization service
echo "Creating temp notarization archive"
ditto -c -k --keepParent ${{steps.build.outputs.path}} "notarization.zip"
# Here we send the notarization request to the Apple's Notarization service, waiting for the result.
# This typically takes a few seconds inside a CI environment, but it might take more depending on the App
# characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if
# you're curious
echo "Notarize app"
xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait
# Finally, we need to "attach the staple" to our executable, which will allow our app to be
# validated by macOS even when an internet connection is not available.
echo "Attach staple"
@@ -450,15 +367,109 @@ jobs:
- name: Upload artifact
id: upload_artifact
if: matrix.make_package
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: ${{matrix.artifact_name}}
name: macOS${{matrix.target}}${{ matrix.soc == 'Intel' && '_Intel' || '' }}${{ matrix.type == 'Debug' && '_Debug' || '' }}-package
path: ${{steps.build.outputs.path}}
if-no-files-found: error
- name: Upload to release
id: upload_release
if: matrix.make_package && needs.configure.outputs.tag != null
shell: bash
env:
GH_TOKEN: ${{github.token}}
tag_name: ${{needs.configure.outputs.tag}}
asset_path: ${{steps.build.outputs.path}}
asset_name: ${{steps.build.outputs.name}}
run: gh release upload "$tag_name" "$asset_path#$asset_name"
- name: Attest binary provenance
id: attestation
if: steps.upload_release.outcome == 'success'
uses: actions/attest-build-provenance@v3
with:
subject-name: ${{steps.build.outputs.name}}
subject-digest: sha256:${{ steps.upload_artifact.outputs.artifact-digest }}
- name: Verify binary attestation
if: steps.attestation.outcome == 'success'
shell: bash
env:
GH_TOKEN: ${{github.token}}
run: gh attestation verify ${{steps.build.outputs.path}} -R Cockatrice/Cockatrice
build-windows:
strategy:
fail-fast: false
matrix:
include:
- target: 7
qt_version: 5.15.*
qt_arch: msvc2019_64
- target: 10
qt_version: 6.6.*
qt_arch: msvc2019_64
qt_modules: "qtimageformats qtmultimedia qtwebsockets"
name: Windows ${{matrix.target}}
needs: configure
runs-on: windows-2022
env:
CMAKE_GENERATOR: 'Visual Studio 17 2022'
steps:
- name: Add msbuild to PATH
id: add-msbuild
uses: microsoft/setup-msbuild@v2
with:
msbuild-architecture: x64
- name: Checkout
uses: actions/checkout@v5
with:
submodules: recursive
- name: Install Qt ${{matrix.qt_version}}
uses: jurplel/install-qt-action@v4
with:
cache: true
setup-python: true
version: ${{matrix.qt_version}}
arch: win64_${{matrix.qt_arch}}
modules: ${{matrix.qt_modules}}
- name: Setup vcpkg cache
id: vcpkg-cache
uses: TAServers/vcpkg-cache@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build Cockatrice
id: build
shell: bash
env:
PACKAGE_SUFFIX: '-Win${{matrix.target}}'
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
CMAKE_GENERATOR_PLATFORM: 'x64'
QTDIR: '${{github.workspace}}\Qt\${{matrix.qt_version}}\win64_${{matrix.qt_arch}}'
VCPKG_DISABLE_METRICS: 1
VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite'
# No need for --parallel flag, MTT is added in the compile script to let cmake/msbuild manage core count,
# project and process parallelism: https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
run: .ci/compile.sh --server --release --test --package
- name: Upload artifact
id: upload_artifact
uses: actions/upload-artifact@v4
with:
name: Windows${{matrix.target}}-installer
path: ${{steps.build.outputs.path}}
if-no-files-found: error
- name: Upload pdb database
if: matrix.os == 'Windows'
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: Windows${{matrix.target}}-debug-pdbs
path: |

View File

@@ -1,22 +1,13 @@
name: Code Style (C++)
on:
# push trigger not needed for linting, we do not allow direct pushes to master
pull_request:
paths:
- '*/**' # matches all files not in root
- '!**.md'
- '!.ci/**'
- '!.github/**'
- '!.husky/**'
- '!.tx/**'
- '!doc/**'
- '!webclient/**'
- '.ci/lint_cpp.sh'
- '.github/workflows/desktop-lint.yml'
- '.clang-format'
- '.cmake-format.json'
- 'format.sh'
paths-ignore:
- '**.md'
- 'webclient/**'
- '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml'
- '.github/workflows/docker-release.yml'
jobs:
format:
@@ -24,7 +15,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
fetch-depth: 20 # should be enough to find merge base
@@ -32,7 +23,7 @@ jobs:
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends clang-format cmake-format shellcheck
sudo apt-get install -y --no-install-recommends clang-format cmake-format
- name: Check code formatting
shell: bash

View File

@@ -11,7 +11,6 @@ on:
- master
paths:
- '.github/workflows/docker-release.yml'
- 'Dockerfile'
jobs:
docker:
@@ -23,7 +22,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Docker metadata
id: metadata

View File

@@ -6,7 +6,8 @@ on:
- '*' # Only re-generate docs when a new tagged version is pushed
pull_request:
paths:
- 'doc/doxygen/**'
- '**.h'
- '/doc/doxygen/**'
- '.github/workflows/documentation-build.yml'
- 'Doxyfile'
workflow_dispatch:
@@ -20,42 +21,35 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Checkout
uses: actions/checkout@v5
- name: Install Graphviz
run: |
sudo apt-get install -y graphviz
dot -V
- name: Install Doxygen
uses: ssciwr/doxygen-install@v1
with:
version: "1.14.0"
- name: Update Doxygen Configuration
run: |
git diff Doxyfile
doxygen -u Doxyfile
if git diff --quiet Doxyfile; then
echo "::notice::No config changes in Doxyfile detected."
else
echo "::error::Config changes in Doxyfile detected! Please update the file by running 'doxygen -u Doxyfile'."
echo ""
git diff --color=always Doxyfile
exit 1
fi
- name: Install Doxygen and Graphviz
shell: bash
run: sudo apt-get install -y doxygen graphviz
- name: Generate Documentation
if: always()
shell: bash
run: doxygen Doxyfile
- name: Deploy to cockatrice.github.io
- name: Upload Documentation
uses: actions/upload-artifact@v4
with:
name: Cockatrice_Docs
path: ./docs/html # Main output folder + subfolder defined in Doxygen config
if-no-files-found: error
- name: Deploy to cockatrice.github.io/docs
if: github.event_name != 'pull_request'
uses: peaceiris/actions-gh-pages@v4
with:
deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }}
external_repository: Cockatrice/cockatrice.github.io
publish_branch: master
publish_dir: ./docs/html
destination_dir: docs # Docs will live under https://cockatrice.github.io/docs/
publish_dir: ./docs/html # Main output folder + subfolder defined in Doxygen config
destination_dir: docs # Subfolder of the GitHub Pages URL where the docs live
- name: Link to Documentation Page
if: github.event_name != 'pull_request'
shell: bash
run: echo "::notice title=New documentation published ::Shortly available at https://cockatrice.github.io/docs/"

View File

@@ -7,7 +7,6 @@ on:
- cron: '0 0 15 1,4,7,10 *'
pull_request:
paths:
- '.tx/**'
- '.github/workflows/translations-pull.yml'
jobs:
@@ -20,7 +19,7 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Pull translated strings from Transifex
uses: transifex/cli-action@v2

View File

@@ -7,7 +7,6 @@ on:
- cron: '0 0 1 1,4,7,10 *'
pull_request:
paths:
- '.ci/update_translation_source_strings.sh'
- '.github/workflows/translations-push.yml'
jobs:
@@ -20,7 +19,7 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Install lupdate
shell: bash
@@ -31,10 +30,10 @@ jobs:
- name: Update Cockatrice translation source
id: cockatrice
shell: bash
run: |
FILE="cockatrice/cockatrice_en@source.ts"
export DIRS="cockatrice/src $(find . -maxdepth 1 -type d -name 'libcockatrice_*')"
FILE="$FILE" DIRS="$DIRS" .ci/update_translation_source_strings.sh
env:
FILE: 'cockatrice/cockatrice_en@source.ts'
DIRS: 'cockatrice/src common'
run: .ci/update_translation_source_strings.sh
- name: Update Oracle translation source
id: oracle

View File

@@ -5,16 +5,14 @@ on:
branches:
- master
paths:
- '.husky/**'
- '.github/workflows/web-*.yml'
- 'webclient/**'
- '!**.md'
- '.github/workflows/web-build.yml'
pull_request:
paths:
- '.husky/**'
- '.github/workflows/web-*.yml'
- 'webclient/**'
- '!**.md'
- '.github/workflows/web-build.yml'
jobs:
build-web:
@@ -35,10 +33,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v5
with:
node-version: ${{matrix.node_version}}
cache: 'npm'

View File

@@ -1,12 +1,11 @@
name: Code Style (TypeScript)
on:
# push trigger not needed for linting, we do not allow direct pushes to master
pull_request:
paths:
- '.github/workflows/web-*.yml'
- 'webclient/**'
- '!**.md'
- '.github/workflows/web-lint.yml'
jobs:
ESLint:
@@ -18,10 +17,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v5
with:
cache: 'npm'
cache-dependency-path: 'webclient/package-lock.json'

1
.gitignore vendored
View File

@@ -14,4 +14,3 @@ compile_commands.json
.cache
.gdb_history
cockatrice/resources/config/qtlogging.ini
docs/

View File

@@ -24,8 +24,6 @@ option(WITH_ORACLE "build oracle" ON)
option(WITH_DBCONVERTER "build dbconverter" ON)
# Compile tests
option(TEST "build tests" OFF)
# Use vcpkg regardless of OS
option(USE_VCPKG "Use vcpkg regardless of OS" OFF)
# Default to "Release" build type
# User-provided value for CMAKE_BUILD_TYPE must be checked before the PROJECT() call
@@ -50,8 +48,8 @@ if(USE_CCACHE)
endif()
endif()
if(WIN32 OR USE_VCPKG)
# Use vcpkg toolchain on Windows (and on macOS in CI)
if(WIN32 OR APPLE)
# Use vcpkg toolchain on Windows and macOS
set(CMAKE_TOOLCHAIN_FILE
${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake
CACHE STRING "Vcpkg toolchain file"
@@ -328,19 +326,7 @@ endif()
include(CPack)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_interfaces ${CMAKE_BINARY_DIR}/libcockatrice_interfaces)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_protocol ${CMAKE_BINARY_DIR}/libcockatrice_protocol)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_network ${CMAKE_BINARY_DIR}/libcockatrice_network)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_deck_list ${CMAKE_BINARY_DIR}/libcockatrice_deck_list)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_rng ${CMAKE_BINARY_DIR}/libcockatrice_rng)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_card ${CMAKE_BINARY_DIR}/libcockatrice_card)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_utility ${CMAKE_BINARY_DIR}/libcockatrice_utility)
if(WITH_ORACLE OR WITH_CLIENT)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_settings ${CMAKE_BINARY_DIR}/libcockatrice_settings)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_models ${CMAKE_BINARY_DIR}/libcockatrice_models)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_filters ${CMAKE_BINARY_DIR}/libcockatrice_filters)
endif()
add_subdirectory(common)
if(WITH_SERVER)
add_subdirectory(servatrice)
set(CPACK_INSTALL_CMAKE_PROJECTS "Servatrice;Servatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})

View File

@@ -1,9 +1,8 @@
# -------- Build Stage --------
FROM ubuntu:24.04 AS build
FROM ubuntu:24.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
RUN apt-get update && apt-get install -y\
build-essential \
cmake \
file \
@@ -17,28 +16,20 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
qt6-tools-dev \
qt6-tools-dev-tools
WORKDIR /src
COPY . .
RUN mkdir build && cd build && \
cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 && \
make -j$(nproc) && \
make install
COPY ./CMakeLists.txt ./LICENSE ./README.md /home/servatrice/code/
COPY ./cmake /home/servatrice/code/cmake
COPY ./common /home/servatrice/code/common
COPY ./servatrice /home/servatrice/code/servatrice
WORKDIR /home/servatrice/code
# -------- Runtime Stage (clean) --------
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
libprotobuf32t64 \
libqt6sql6-mysql \
libqt6websockets6 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Only copy installed binaries, not source
COPY --from=build /usr/local /usr/local
WORKDIR build
RUN cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 &&\
make &&\
make install
WORKDIR /home/servatrice
EXPOSE 4748
ENTRYPOINT [ "servatrice", "--log-to-console" ]

View File

@@ -1,4 +1,5 @@
# Doxyfile 1.14.0
# Docs: https://www.doxygen.nl/manual
# This file describes the settings to be used by the documentation system
# Doxygen (www.doxygen.org) for a project.
@@ -42,7 +43,7 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = Cockatrice
PROJECT_NAME = "Cockatrice"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
@@ -54,7 +55,7 @@ PROJECT_NUMBER = $(COCKATRICE_REF)
# for a project that appears at the top of each page and should give viewers a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = "A cross-platform virtual tabletop for multiplayer card games"
PROJECT_BRIEF = A cross-platform virtual tabletop for multiplayer card games
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
@@ -379,7 +380,7 @@ TOC_INCLUDE_HEADINGS = 6
# The default value is: DOXYGEN.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
MARKDOWN_ID_STYLE = GITHUB
MARKDOWN_ID_STYLE = DOXYGEN
# When enabled Doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
@@ -991,7 +992,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = .
INPUT = cockatrice common doc/doxygen/extra-pages doc/doxygen/groups .github/CONTRIBUTING.md
# This tag can be used to specify the character encoding of the source files
# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses
@@ -1031,7 +1032,8 @@ INPUT_FILE_ENCODING =
# provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.cc \
FILE_PATTERNS = *.c \
*.cc \
*.cxx \
*.cxxm \
*.cpp \
@@ -1039,19 +1041,47 @@ FILE_PATTERNS = *.cc \
*.ccm \
*.c++ \
*.c++m \
*.java \
*.ii \
*.ixx \
*.ipp \
*.i++ \
*.inl \
*.idl \
*.ddl \
*.odl \
*.h \
*.hh \
*.hxx \
*.hpp \
*.h++ \
*.l \
*.cs \
*.d \
*.php \
*.php4 \
*.php5 \
*.phtml \
*.inc \
*.m \
*.markdown \
*.md \
*.dox
*.mm \
*.dox \
*.py \
*.pyw \
*.f90 \
*.f95 \
*.f03 \
*.f08 \
*.f18 \
*.f \
*.for \
*.vhd \
*.vhdl \
*.ucf \
*.qsf \
*.ice
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
@@ -1066,11 +1096,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which Doxygen is
# run.
EXCLUDE = build/ \
cmake/ \
dbconverter/ \
vcpkg/ \
webclient/
EXCLUDE = common/lib
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
@@ -1086,8 +1112,7 @@ EXCLUDE_SYMLINKS = NO
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS = .* \
.*/
EXCLUDE_PATTERNS =
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
@@ -1121,7 +1146,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH = doc/doxygen/images
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that Doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
@@ -1289,46 +1314,6 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then Doxygen will use the
# clang parser (see:
# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
# performance. This can be particularly helpful with template rich C++ code for
# which Doxygen's built-in parser lacks the necessary type information.
# Note: The availability of this option depends on whether or not Doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
# tag is set to YES then Doxygen will add the directory of each input to the
# include path.
# The default value is: YES.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_ADD_INC_PATHS = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by Doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the directory containing a file called compile_commands.json. This
# file is the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
# options used when the source files were built. This is equivalent to
# specifying the -p option to a clang tool, such as clang-check. These options
# will then be passed to the parser. Any options specified with CLANG_OPTIONS
# will be added as well.
# Note: The availability of this option depends on whether or not Doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
@@ -2623,7 +2608,7 @@ DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10,arrowhead=open, arrowtail=open, arrowsize=0.5"
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10,arrowhead=open, arrowtail=open, arrowsize=0.5"
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# around nodes set 'shape=plain' or 'shape=plaintext' <a
@@ -2631,7 +2616,7 @@ DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10,arrowhead=ope
# The default value is: shape=box,height=0.2,width=0.4.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
# You can set the path where dot can find font specified with fontname in
# DOT_COMMON_ATTR and others dot attributes.

View File

@@ -115,7 +115,4 @@ string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" ORACLE_QT_MO
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)
set(QT_CORE_MODULE "${COCKATRICE_QT_VERSION_NAME}::Core")
message(STATUS "Found Qt ${${COCKATRICE_QT_VERSION_NAME}_VERSION} at: ${${COCKATRICE_QT_VERSION_NAME}_DIR}")

View File

@@ -7,52 +7,78 @@ project(Cockatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${
set(cockatrice_SOURCES
${VERSION_STRING_CPP}
# sort by alphabetical order, so that there is no debate about where to add new sources to the list
src/client/network/update/client/update_downloader.cpp
src/client/network/interfaces/deck_stats_interface.cpp
src/client/network/interfaces/tapped_out_interface.cpp
src/card/card_info.cpp
src/card/card_relation.cpp
src/card/card_set.cpp
src/card/card_set_list.cpp
src/card/exact_card.cpp
src/card/printing_info.cpp
src/client/deck_editor_menu.cpp
src/client/get_text_with_max.cpp
src/client/network/client_update_checker.cpp
src/client/network/parsers/deck_link_to_api_transformer.cpp
src/client/network/update/client/client_update_checker.cpp
src/client/network/update/client/release_channel.cpp
src/client/network/update/card_spoiler/spoiler_background_updater.cpp
src/client/network/release_channel.cpp
src/client/network/replay_timeline_widget.cpp
src/client/network/sets_model.cpp
src/client/network/spoiler_background_updater.cpp
src/client/replay_manager.cpp
src/client/sound_engine.cpp
src/client/settings/cache_settings.cpp
src/client/settings/card_counter_settings.cpp
src/client/settings/shortcut_treeview.cpp
src/client/settings/shortcuts_settings.cpp
src/interface/deck_loader/deck_loader.cpp
src/interface/widgets/dialogs/dlg_connect.cpp
src/interface/widgets/dialogs/dlg_convert_deck_to_cod_format.cpp
src/interface/widgets/dialogs/dlg_create_game.cpp
src/interface/widgets/dialogs/dlg_default_tags_editor.cpp
src/interface/widgets/dialogs/dlg_edit_avatar.cpp
src/interface/widgets/dialogs/dlg_edit_password.cpp
src/interface/widgets/dialogs/dlg_edit_tokens.cpp
src/interface/widgets/dialogs/dlg_edit_user.cpp
src/interface/widgets/dialogs/dlg_filter_games.cpp
src/interface/widgets/dialogs/dlg_forgot_password_challenge.cpp
src/interface/widgets/dialogs/dlg_forgot_password_request.cpp
src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp
src/interface/widgets/dialogs/dlg_load_deck.cpp
src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp
src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp
src/interface/widgets/dialogs/dlg_load_remote_deck.cpp
src/interface/widgets/dialogs/dlg_manage_sets.cpp
src/interface/widgets/dialogs/dlg_register.cpp
src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp
src/interface/widgets/dialogs/dlg_settings.cpp
src/interface/widgets/dialogs/dlg_startup_card_check.cpp
src/interface/widgets/dialogs/dlg_tip_of_the_day.cpp
src/interface/widgets/dialogs/dlg_update.cpp
src/interface/widgets/dialogs/dlg_view_log.cpp
src/interface/widgets/dialogs/tip_of_the_day.cpp
src/client/tapped_out_interface.cpp
src/client/update_downloader.cpp
src/database/card_database.cpp
src/database/card_database_loader.cpp
src/database/card_database_manager.cpp
src/database/card_database_querier.cpp
src/database/model/card_database_model.cpp
src/database/model/card_database_display_model.cpp
src/database/model/card/card_completer_proxy_model.cpp
src/database/model/card/card_search_model.cpp
src/database/model/token/token_display_model.cpp
src/database/model/token/token_edit_model.cpp
src/database/parser/card_database_parser.cpp
src/database/parser/cockatrice_xml_3.cpp
src/database/parser/cockatrice_xml_4.cpp
src/deck/custom_line_edit.cpp
src/deck/deck_list_model.cpp
src/deck/deck_loader.cpp
src/deck/deck_stats_interface.cpp
src/dialogs/dlg_connect.cpp
src/dialogs/dlg_convert_deck_to_cod_format.cpp
src/dialogs/dlg_create_game.cpp
src/dialogs/dlg_default_tags_editor.cpp
src/dialogs/dlg_edit_avatar.cpp
src/dialogs/dlg_edit_password.cpp
src/dialogs/dlg_edit_tokens.cpp
src/dialogs/dlg_edit_user.cpp
src/dialogs/dlg_filter_games.cpp
src/dialogs/dlg_forgot_password_challenge.cpp
src/dialogs/dlg_forgot_password_request.cpp
src/dialogs/dlg_forgot_password_reset.cpp
src/dialogs/dlg_load_deck.cpp
src/dialogs/dlg_load_deck_from_clipboard.cpp
src/dialogs/dlg_load_deck_from_website.cpp
src/dialogs/dlg_load_remote_deck.cpp
src/dialogs/dlg_manage_sets.cpp
src/dialogs/dlg_register.cpp
src/dialogs/dlg_select_set_for_cards.cpp
src/dialogs/dlg_settings.cpp
src/dialogs/dlg_startup_card_check.cpp
src/dialogs/dlg_tip_of_the_day.cpp
src/dialogs/dlg_update.cpp
src/dialogs/dlg_view_log.cpp
src/dialogs/tip_of_the_day.cpp
src/filters/deck_filter_string.cpp
src/filters/filter_builder.cpp
src/filters/filter_card.cpp
src/filters/filter_string.cpp
src/filters/filter_tree.cpp
src/filters/filter_tree_model.cpp
src/filters/syntax_help.cpp
src/game/abstract_game.cpp
src/game/board/abstract_card_drag_item.cpp
src/game/board/abstract_card_item.cpp
src/game/board/abstract_counter.cpp
src/game/board/abstract_graphics_item.cpp
src/game/board/arrow_item.cpp
src/game/board/arrow_target.cpp
src/game/board/card_drag_item.cpp
@@ -112,17 +138,9 @@ set(cockatrice_SOURCES
src/game/zones/table_zone.cpp
src/game/zones/view_zone.cpp
src/game/zones/view_zone_widget.cpp
src/game_graphics/board/abstract_graphics_item.cpp
src/interface/card_picture_loader/card_picture_loader.cpp
src/interface/card_picture_loader/card_picture_loader_local.cpp
src/interface/card_picture_loader/card_picture_loader_request_status_display_widget.cpp
src/interface/card_picture_loader/card_picture_loader_status_bar.cpp
src/interface/card_picture_loader/card_picture_loader_worker.cpp
src/interface/card_picture_loader/card_picture_loader_worker_work.cpp
src/interface/card_picture_loader/card_picture_to_load.cpp
src/interface/layouts/flow_layout.cpp
src/interface/layouts/overlap_layout.cpp
src/interface/widgets/utility/line_edit_completer.cpp
src/interface/line_edit_completer.cpp
src/interface/pixel_map_generator.cpp
src/interface/theme_manager.cpp
src/interface/widgets/cards/additional_info/color_identity_widget.cpp
@@ -142,22 +160,17 @@ set(cockatrice_SOURCES
src/interface/widgets/cards/deck_card_zone_display_widget.cpp
src/interface/widgets/cards/deck_preview_card_picture_widget.cpp
src/interface/widgets/deck_analytics/deck_analytics_widget.cpp
src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp
src/interface/widgets/deck_analytics/mana_base_widget.cpp
src/interface/widgets/deck_analytics/mana_curve_widget.cpp
src/interface/widgets/deck_analytics/mana_devotion_widget.cpp
src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp
src/interface/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp
src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
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/general/background_sources.cpp
src/interface/widgets/general/display/background_plate_widget.cpp
src/interface/widgets/general/display/banner_widget.cpp
src/interface/widgets/general/display/bar_widget.cpp
src/interface/widgets/general/display/color_bar.cpp
src/interface/widgets/general/display/dynamic_font_size_label.cpp
src/interface/widgets/general/display/dynamic_font_size_push_button.cpp
src/interface/widgets/general/display/labeled_input.cpp
@@ -168,7 +181,6 @@ set(cockatrice_SOURCES
src/interface/widgets/general/layout_containers/flow_widget.cpp
src/interface/widgets/general/layout_containers/overlap_control_widget.cpp
src/interface/widgets/general/layout_containers/overlap_widget.cpp
src/interface/widgets/menus/deck_editor_menu.cpp
src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp
src/interface/widgets/printing_selector/card_amount_widget.cpp
src/interface/widgets/printing_selector/printing_selector.cpp
@@ -180,23 +192,6 @@ set(cockatrice_SOURCES
src/interface/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
src/interface/widgets/quick_settings/settings_button_widget.cpp
src/interface/widgets/quick_settings/settings_popup_widget.cpp
src/interface/widgets/replay/replay_manager.cpp
src/interface/widgets/replay/replay_timeline_widget.cpp
src/interface/widgets/server/chat_view/chat_view.cpp
src/interface/widgets/server/game_selector.cpp
src/interface/widgets/server/game_selector_quick_filter_toolbar.cpp
src/interface/widgets/server/games_model.cpp
src/interface/widgets/server/handle_public_servers.cpp
src/interface/widgets/server/remote/remote_decklist_tree_widget.cpp
src/interface/widgets/server/remote/remote_replay_list_tree_widget.cpp
src/interface/widgets/server/user/user_context_menu.cpp
src/interface/widgets/server/user/user_info_box.cpp
src/interface/widgets/server/user/user_info_connection.cpp
src/interface/widgets/server/user/user_list_manager.cpp
src/interface/widgets/server/user/user_list_widget.cpp
src/interface/widgets/utility/custom_line_edit.cpp
src/interface/widgets/utility/get_text_with_max.cpp
src/interface/widgets/utility/sequence_edit.cpp
src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
@@ -205,7 +200,6 @@ set(cockatrice_SOURCES
src/interface/widgets/visual_database_display/visual_database_display_sub_type_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_widget.cpp
src/interface/widgets/visual_database_display/visual_database_filter_display_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp
@@ -223,65 +217,94 @@ set(cockatrice_SOURCES
src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
src/interface/window_main.cpp
src/main.cpp
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/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
src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck_category.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck_listings/archidekt_api_response_deck_listing_container.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck_listings/archidekt_api_response_deck_owner.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_deck_preview_image_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp
src/interface/widgets/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
src/interface/widgets/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_inclusion_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_synergy_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp
src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp
src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp
src/interface/widgets/tabs/tab.cpp
src/interface/widgets/tabs/tab_account.cpp
src/interface/widgets/tabs/tab_admin.cpp
src/interface/widgets/tabs/tab_deck_editor.cpp
src/interface/widgets/tabs/tab_deck_storage.cpp
src/interface/widgets/tabs/tab_game.cpp
src/interface/widgets/tabs/tab_home.cpp
src/interface/widgets/tabs/tab_logs.cpp
src/interface/widgets/tabs/tab_message.cpp
src/interface/widgets/tabs/tab_replays.cpp
src/interface/widgets/tabs/tab_room.cpp
src/interface/widgets/tabs/tab_server.cpp
src/interface/widgets/tabs/tab_supervisor.cpp
src/interface/widgets/tabs/tab_visual_database_display.cpp
src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp
src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
src/interface/key_signals.cpp
src/interface/logger.cpp
src/picture_loader/picture_loader.cpp
src/picture_loader/picture_loader_local.cpp
src/picture_loader/picture_loader_request_status_display_widget.cpp
src/picture_loader/picture_loader_status_bar.cpp
src/picture_loader/picture_loader_worker.cpp
src/picture_loader/picture_loader_worker_work.cpp
src/picture_loader/picture_to_load.cpp
src/server/abstract_client.cpp
src/server/chat_view/chat_view.cpp
src/server/game_selector.cpp
src/server/games_model.cpp
src/server/handle_public_servers.cpp
src/server/local_client.cpp
src/server/local_server.cpp
src/server/local_server_interface.cpp
src/server/pending_command.cpp
src/server/remote/remote_client.cpp
src/server/remote/remote_decklist_tree_widget.cpp
src/server/remote/remote_replay_list_tree_widget.cpp
src/server/user/user_context_menu.cpp
src/server/user/user_info_box.cpp
src/server/user/user_info_connection.cpp
src/server/user/user_list_manager.cpp
src/server/user/user_list_widget.cpp
src/settings/cache_settings.cpp
src/settings/card_counter_settings.cpp
src/settings/card_database_settings.cpp
src/settings/card_override_settings.cpp
src/settings/debug_settings.cpp
src/settings/download_settings.cpp
src/settings/game_filters_settings.cpp
src/settings/layouts_settings.cpp
src/settings/message_settings.cpp
src/settings/recents_settings.cpp
src/settings/servers_settings.cpp
src/settings/settings_manager.cpp
src/settings/shortcut_treeview.cpp
src/settings/shortcuts_settings.cpp
src/tabs/abstract_tab_deck_editor.cpp
src/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
src/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
src/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
src/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp
src/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp
src/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp
src/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp
src/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp
src/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp
src/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp
src/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
src/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
src/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
src/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
src/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
src/tabs/api/edhrec/display/cards/edhrec_api_response_card_inclusion_display_widget.cpp
src/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
src/tabs/api/edhrec/display/cards/edhrec_api_response_card_synergy_display_widget.cpp
src/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
src/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp
src/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
src/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp
src/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp
src/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp
src/tabs/api/edhrec/tab_edhrec.cpp
src/tabs/api/edhrec/tab_edhrec_main.cpp
src/tabs/tab.cpp
src/tabs/tab_account.cpp
src/tabs/tab_admin.cpp
src/tabs/tab_deck_editor.cpp
src/tabs/tab_deck_storage.cpp
src/tabs/tab_game.cpp
src/tabs/tab_home.cpp
src/tabs/tab_logs.cpp
src/tabs/tab_message.cpp
src/tabs/tab_replays.cpp
src/tabs/tab_room.cpp
src/tabs/tab_server.cpp
src/tabs/tab_supervisor.cpp
src/tabs/tab_visual_database_display.cpp
src/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
src/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp
src/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
src/utility/card_info_comparator.cpp
src/utility/deck_list_sort_filter_proxy_model.cpp
src/utility/key_signals.cpp
src/utility/levenshtein.cpp
src/utility/logger.cpp
src/utility/sequence_edit.cpp
)
add_subdirectory(sounds)
@@ -293,23 +316,15 @@ configure_file(
set(cockatrice_RESOURCES cockatrice.qrc)
if(UPDATE_TRANSLATIONS)
# Cockatrice main sources
file(GLOB_RECURSE translate_cockatrice_SRCS ${CMAKE_SOURCE_DIR}/cockatrice/src/*.cpp
${CMAKE_SOURCE_DIR}/cockatrice/src/*.h
)
# All libcockatrice_* libraries (recursively)
file(GLOB_RECURSE translate_lib_SRCS ${CMAKE_SOURCE_DIR}/libcockatrice_*/**/*.cpp
${CMAKE_SOURCE_DIR}/libcockatrice_*/**/*.h
)
# Combine all sources for translation
set(translate_SRCS ${translate_cockatrice_SRCS} ${translate_lib_SRCS})
file(GLOB_RECURSE translate_common_SRCS ${CMAKE_SOURCE_DIR}/common/*.cpp ${CMAKE_SOURCE_DIR}/common/*.h)
set(translate_SRCS ${translate_cockatrice_SRCS} ${translate_common_SRCS})
set(cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice_en@source.ts")
else()
file(GLOB cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts")
endif()
endif(UPDATE_TRANSLATIONS)
if(WIN32)
set(cockatrice_SOURCES ${cockatrice_SOURCES} cockatrice.rc)
@@ -339,6 +354,12 @@ set(DESKTOPDIR
CACHE STRING "desktop file destination"
)
# Include directories
include_directories(../common)
include_directories(${PROTOBUF_INCLUDE_DIR})
include_directories(${CMAKE_BINARY_DIR}/common)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(COCKATRICE_MAC_QM_INSTALL_DIR "cockatrice.app/Contents/Resources/translations")
set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/cockatrice/translations")
set(COCKATRICE_WIN32_QM_INSTALL_DIR "translations")
@@ -378,31 +399,9 @@ elseif(Qt5_FOUND)
endif()
if(Qt5_FOUND)
target_link_libraries(
cockatrice
libcockatrice_card
libcockatrice_deck_list
libcockatrice_filters
libcockatrice_utility
libcockatrice_network
libcockatrice_models
libcockatrice_rng
libcockatrice_settings
${COCKATRICE_QT_MODULES}
)
target_link_libraries(cockatrice cockatrice_common ${COCKATRICE_QT_MODULES})
else()
target_link_libraries(
cockatrice
PUBLIC libcockatrice_card
libcockatrice_deck_list
libcockatrice_filters
libcockatrice_utility
libcockatrice_network
libcockatrice_models
libcockatrice_rng
libcockatrice_settings
${COCKATRICE_QT_MODULES}
)
target_link_libraries(cockatrice PUBLIC cockatrice_common ${COCKATRICE_QT_MODULES})
endif()
if(UNIX)

View File

@@ -7,14 +7,11 @@
<file>resources/icons/arrow_bottom_green.svg</file>
<file>resources/icons/arrow_down_green.svg</file>
<file>resources/icons/arrow_history.svg</file>
<file>resources/icons/arrow_left_green.svg</file>
<file>resources/icons/arrow_redo.svg</file>
<file>resources/icons/arrow_right_blue.svg</file>
<file>resources/icons/arrow_right_green.svg</file>
<file>resources/icons/arrow_top_green.svg</file>
<file>resources/icons/arrow_up_green.svg</file>
<file>resources/icons/arrow_undo.svg</file>
<file>resources/icons/clearsearch.svg</file>
<file>resources/icons/cogwheel.svg</file>
<file>resources/icons/conceded.svg</file>
@@ -28,7 +25,6 @@
<file>resources/icons/lock.svg</file>
<file>resources/icons/not_ready_start.svg</file>
<file>resources/icons/pencil.svg</file>
<file>resources/icons/pin.svg</file>
<file>resources/icons/player.svg</file>
<file>resources/icons/ready_start.svg</file>
<file>resources/icons/reload.svg</file>

File diff suppressed because it is too large Load Diff

View File

@@ -37,14 +37,12 @@
#card_zone = true
#view_zone = true
#game_event_handler = true
#user_info_connection = true
#card_picture_loader = true
#card_picture_loader.worker = true
#card_picture_loader.card_back_cache_fail = true
#card_picture_loader.picture_to_load = true
#picture_loader = true
#picture_loader.worker = true
#picture_loader.card_back_cache_fail = true
#picture_loader.picture_to_load = true
#deck_loader = true
#card_database = true
#card_database.loading = true

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<title>
history
</title>
<path d="M9 6v5h.06l2.48 2.47 1.41-1.41L11 10.11V6H9z"/>
<path d="M10 1a9 9 0 0 0-7.85 13.35L.5 16H6v-5.5l-2.38 2.38A7 7 0 1 1 10 17v2a9 9 0 0 0 0-18z"/>
</svg>

Before

Width:  |  Height:  |  Size: 332 B

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="windows-1252"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" x="0px"
y="0px" width="485.212px" height="485.212px" viewBox="0 0 485.212 485.212"
style="enable-background:new 0 0 485.212 485.212;" xml:space="preserve">
<g>
<path d="M242.607,424.559c-75.252,0-136.468-61.209-136.468-136.465c0-75.252,61.216-136.466,136.468-136.466v90.978 l151.629-121.302L242.607,0v90.978c-108.687,0-197.117,88.432-197.117,197.117c0,108.691,88.43,197.118,197.117,197.118 c108.687,0,197.114-88.427,197.114-197.118h-60.645C379.077,363.35,317.859,424.559,242.607,424.559z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1018 B

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="windows-1252"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" x="0px"
y="0px" width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;"
xml:space="preserve">
<g>
<path d="M256,448c79.406,0,144-64.594,144-144s-64.594-144-144-144v96L96,128L256,0v96c114.688,0,208,93.313,208,208 c0,114.688-93.312,208-208,208c-114.687,0-208-93.312-208-208h64C112,383.406,176.594,448,256,448z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 874 B

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="64" height="64">
<g transform="matrix(0 1 -1 0 99.465813 0)" opacity="0.7">
<path fill="#000000" fill-rule="evenodd" clip-rule="evenodd"
stroke="#ffffff"
stroke-width="4"
stroke-linejoin="round"
stroke-linecap="round"
d="M65.5 62
L78 49
C73.5 44.5 69.5 42 63 44
L45 31 C47 25 46 22 41.5 18
L19 41.5
C23 45.5 25 46.5 31 45
L44 62.5
C42.3 69 45 73.5 49 78
L61.5 65.5
L84 87
L87 87
L87.5 86.5
L87.5 83.5 Z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 736 B

View File

@@ -363,6 +363,6 @@
d="m 38.011063,984.77381 -10.143601,-5.23583 -10.063711,5.38779 1.845023,-11.2651 -8.233948,-7.90624 11.283888,-1.72639 4.974851,-10.27411 5.128803,10.19813 11.308575,1.55649 -8.114112,8.02918 z"
inkscape:transform-center-x="0.094945927"
inkscape:transform-center-y="-3.9764964"
transform="matrix(-2.3768784,0,0,2.4799382,115.920285,-1400.1716)" />
transform="matrix(2.3768784,0,0,2.4799382,-15.920285,-1400.1716)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,9 +1,10 @@
#include "card_info.h"
#include "../picture_loader/picture_loader.h"
#include "../settings/cache_settings.h"
#include "card_relation.h"
#include "game_specific_terms.h"
#include "printing/printing_info.h"
#include "relation/card_relation.h"
#include "set/card_set.h"
#include "printing_info.h"
#include <QDir>
#include <QRegularExpression>
@@ -26,18 +27,23 @@ CardInfo::CardInfo(const QString &_name,
const QList<CardRelation *> &_relatedCards,
const QList<CardRelation *> &_reverseRelatedCards,
SetToPrintingsMap _sets,
const UiAttributes _uiAttributes)
bool _cipt,
bool _landscapeOrientation,
int _tableRow,
bool _upsideDownArt)
: name(_name), text(_text), isToken(_isToken), properties(std::move(_properties)), relatedCards(_relatedCards),
reverseRelatedCards(_reverseRelatedCards), setsToPrintings(std::move(_sets)), uiAttributes(_uiAttributes)
reverseRelatedCards(_reverseRelatedCards), setsToPrintings(std::move(_sets)), cipt(_cipt),
landscapeOrientation(_landscapeOrientation), tableRow(_tableRow), upsideDownArt(_upsideDownArt)
{
simpleName = CardInfo::simplifyName(name);
refreshCachedSets();
refreshCachedSetNames();
}
CardInfoPtr CardInfo::newInstance(const QString &_name)
{
return newInstance(_name, "", false, {}, {}, {}, {}, {});
return newInstance(_name, QString(), false, QVariantHash(), QList<CardRelation *>(), QList<CardRelation *>(),
SetToPrintingsMap(), false, false, 0, false);
}
CardInfoPtr CardInfo::newInstance(const QString &_name,
@@ -47,10 +53,13 @@ CardInfoPtr CardInfo::newInstance(const QString &_name,
const QList<CardRelation *> &_relatedCards,
const QList<CardRelation *> &_reverseRelatedCards,
SetToPrintingsMap _sets,
const UiAttributes _uiAttributes)
bool _cipt,
bool _landscapeOrientation,
int _tableRow,
bool _upsideDownArt)
{
CardInfoPtr ptr(new CardInfo(_name, _text, _isToken, std::move(_properties), _relatedCards, _reverseRelatedCards,
_sets, _uiAttributes));
_sets, _cipt, _landscapeOrientation, _tableRow, _upsideDownArt));
ptr->setSmartPointer(ptr);
for (const auto &printings : _sets) {
@@ -84,7 +93,7 @@ void CardInfo::addToSet(const CardSetPtr &_set, const PrintingInfo _info)
setsToPrintings[_set->getShortName()].append(_info);
}
refreshCachedSets();
refreshCachedSetNames();
}
void CardInfo::combineLegalities(const QVariantHash &props)
@@ -98,12 +107,6 @@ void CardInfo::combineLegalities(const QVariantHash &props)
}
}
void CardInfo::refreshCachedSets()
{
refreshCachedSetNames();
refreshCachedAltNames();
}
void CardInfo::refreshCachedSetNames()
{
QStringList setList;
@@ -119,21 +122,6 @@ void CardInfo::refreshCachedSetNames()
setsNames = setList.join(", ");
}
void CardInfo::refreshCachedAltNames()
{
altNames.clear();
// update the altNames with the flavorNames
for (const auto &printings : setsToPrintings) {
for (const auto &printing : printings) {
QString flavorName = printing.getFlavorName();
if (!flavorName.isEmpty()) {
altNames.insert(flavorName);
}
}
}
}
QString CardInfo::simplifyName(const QString &name)
{
static const QRegularExpression spaceOrSplit("(\\s+|\\/\\/.*)");

View File

@@ -1,7 +1,13 @@
/**
* @file card_info.h
* @ingroup Cards
* @brief TODO: Document this.
*/
#ifndef CARD_INFO_H
#define CARD_INFO_H
#include "printing/printing_info.h"
#include "printing_info.h"
#include <QDate>
#include <QHash>
@@ -10,6 +16,7 @@
#include <QMap>
#include <QMetaType>
#include <QSharedPointer>
#include <QStringList>
#include <QVariant>
#include <utility>
@@ -46,26 +53,7 @@ class CardInfo : public QObject
{
Q_OBJECT
public:
/**
* @class CardInfo::UiAttributes
* @ingroup Cards
*
* @brief Attributes of the card that affect display and game logic.
*/
struct UiAttributes
{
bool cipt = false; ///< Positioning flag used by UI.
bool landscapeOrientation = false; ///< Orientation flag for rendering.
int tableRow = 0; ///< Row index in a table or visual representation.
bool upsideDownArt = false; ///< Whether artwork is flipped for visual purposes.
};
private:
/** @name Private Card Properties
* @anchor PrivateCardProperties
*/
///@{
CardInfoPtr smartThis; ///< Smart pointer to self for safe cross-references.
QString name; ///< Full name of the card.
QString simpleName; ///< Simplified name for fuzzy matching.
@@ -76,10 +64,11 @@ private:
QList<CardRelation *> reverseRelatedCards; ///< Cards that refer back to this card.
QList<CardRelation *> reverseRelatedCardsToMe; ///< Cards that consider this card as related.
SetToPrintingsMap setsToPrintings; ///< Mapping from set names to printing variations.
UiAttributes uiAttributes; ///< Attributes that affect display and game logic
QString setsNames; ///< Cached, human-readable list of set names.
QSet<QString> altNames; ///< Cached set of alternate names, used when searching
///@}
bool cipt; ///< Positioning flag used by UI.
bool landscapeOrientation; ///< Orientation flag for rendering.
int tableRow; ///< Row index in a table or visual representation.
bool upsideDownArt; ///< Whether artwork is flipped for visual purposes.
public:
/**
@@ -92,7 +81,10 @@ public:
* @param _relatedCards Forward references to related cards.
* @param _reverseRelatedCards Backward references to related cards.
* @param _sets Map of set names to printing information.
* @param _uiAttributes Attributes that affect display and game logic
* @param _cipt UI positioning flag.
* @param _landscapeOrientation UI rendering orientation.
* @param _tableRow Row index for table placement.
* @param _upsideDownArt Whether the artwork should be displayed upside down.
*/
explicit CardInfo(const QString &_name,
const QString &_text,
@@ -101,7 +93,10 @@ public:
const QList<CardRelation *> &_relatedCards,
const QList<CardRelation *> &_reverseRelatedCards,
SetToPrintingsMap _sets,
UiAttributes _uiAttributes);
bool _cipt,
bool _landscapeOrientation,
int _tableRow,
bool _upsideDownArt);
/**
* @brief Copy constructor for CardInfo.
@@ -114,8 +109,8 @@ public:
: QObject(other.parent()), name(other.name), simpleName(other.simpleName), text(other.text),
isToken(other.isToken), properties(other.properties), relatedCards(other.relatedCards),
reverseRelatedCards(other.reverseRelatedCards), reverseRelatedCardsToMe(other.reverseRelatedCardsToMe),
setsToPrintings(other.setsToPrintings), uiAttributes(other.uiAttributes), setsNames(other.setsNames),
altNames(other.altNames)
setsToPrintings(other.setsToPrintings), setsNames(other.setsNames), cipt(other.cipt),
landscapeOrientation(other.landscapeOrientation), tableRow(other.tableRow), upsideDownArt(other.upsideDownArt)
{
}
@@ -139,7 +134,10 @@ public:
* @param _relatedCards Forward relationships.
* @param _reverseRelatedCards Reverse relationships.
* @param _sets Printing information per set.
* @param _uiAttributes Attributes that affect display and game logic
* @param _cipt UI positioning flag.
* @param _landscapeOrientation UI rendering orientation.
* @param _tableRow Row index for table placement.
* @param _upsideDownArt Artwork orientation flag.
* @return Shared pointer to the new CardInfo instance.
*/
static CardInfoPtr newInstance(const QString &_name,
@@ -149,7 +147,10 @@ public:
const QList<CardRelation *> &_relatedCards,
const QList<CardRelation *> &_reverseRelatedCards,
SetToPrintingsMap _sets,
UiAttributes _uiAttributes);
bool _cipt,
bool _landscapeOrientation,
int _tableRow,
bool _upsideDownArt);
/**
* @brief Clones the current CardInfo instance.
@@ -158,9 +159,9 @@ public:
*
* @return Shared pointer to the cloned CardInfo.
*/
[[nodiscard]] CardInfoPtr clone() const
CardInfoPtr clone() const
{
auto newCardInfo = CardInfoPtr(new CardInfo(*this));
CardInfoPtr newCardInfo = CardInfoPtr(new CardInfo(*this));
newCardInfo->setSmartPointer(newCardInfo); // Set the smart pointer for the new instance
return newCardInfo;
}
@@ -178,19 +179,15 @@ public:
}
/** @name Basic Properties Accessors */ //@{
[[nodiscard]] inline const QString &getName() const
inline const QString &getName() const
{
return name;
}
[[nodiscard]] const QString &getSimpleName() const
const QString &getSimpleName() const
{
return simpleName;
}
const QSet<QString> &getAltNames()
{
return altNames;
}
[[nodiscard]] const QString &getText() const
const QString &getText() const
{
return text;
}
@@ -199,15 +196,15 @@ public:
text = _text;
emit cardInfoChanged(smartThis);
}
[[nodiscard]] bool getIsToken() const
bool getIsToken() const
{
return isToken;
}
[[nodiscard]] QStringList getProperties() const
QStringList getProperties() const
{
return properties.keys();
}
[[nodiscard]] QString getProperty(const QString &propertyName) const
QString getProperty(const QString &propertyName) const
{
return properties.value(propertyName).toString();
}
@@ -216,34 +213,34 @@ public:
properties.insert(_name, _value);
emit cardInfoChanged(smartThis);
}
[[nodiscard]] bool hasProperty(const QString &propertyName) const
bool hasProperty(const QString &propertyName) const
{
return properties.contains(propertyName);
}
[[nodiscard]] const SetToPrintingsMap &getSets() const
const SetToPrintingsMap &getSets() const
{
return setsToPrintings;
}
[[nodiscard]] const QString &getSetsNames() const
const QString &getSetsNames() const
{
return setsNames;
}
//@}
/** @name Related Cards Accessors */ //@{
[[nodiscard]] const QList<CardRelation *> &getRelatedCards() const
const QList<CardRelation *> &getRelatedCards() const
{
return relatedCards;
}
[[nodiscard]] const QList<CardRelation *> &getReverseRelatedCards() const
const QList<CardRelation *> &getReverseRelatedCards() const
{
return reverseRelatedCards;
}
[[nodiscard]] const QList<CardRelation *> &getReverseRelatedCards2Me() const
const QList<CardRelation *> &getReverseRelatedCards2Me() const
{
return reverseRelatedCardsToMe;
}
[[nodiscard]] QList<CardRelation *> getAllRelatedCards() const
QList<CardRelation *> getAllRelatedCards() const
{
QList<CardRelation *> result;
result.append(getRelatedCards());
@@ -258,24 +255,39 @@ public:
//@}
/** @name UI Positioning */ //@{
[[nodiscard]] const UiAttributes &getUiAttributes() const
bool getCipt() const
{
return uiAttributes;
return cipt;
}
bool getLandscapeOrientation() const
{
return landscapeOrientation;
}
int getTableRow() const
{
return tableRow;
}
void setTableRow(int _tableRow)
{
tableRow = _tableRow;
}
bool getUpsideDownArt() const
{
return upsideDownArt;
}
const QChar getColorChar() const;
//@}
[[nodiscard]] const QChar getColorChar() const;
/** @name Legacy/Convenience Property Accessors */ //@{
[[nodiscard]] const QString getCardType() const;
const QString getCardType() const;
void setCardType(const QString &value);
[[nodiscard]] const QString getCmc() const;
[[nodiscard]] const QString getColors() const;
const QString getCmc() const;
const 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;
const QString getLoyalty() const;
const QString getMainCardType() const;
const QString getManaCost() const;
const QString getPowTough() const;
void setPowTough(const QString &value);
//@}
@@ -286,7 +298,7 @@ public:
*
* @return Corrected card name as a QString.
*/
[[nodiscard]] QString getCorrectedName() const;
QString getCorrectedName() const;
/**
* @brief Adds a printing to a specific set.
@@ -308,11 +320,11 @@ public:
void combineLegalities(const QVariantHash &props);
/**
* @brief Refreshes all cached fields that are calculated from the contained sets and printings.
* @brief Refreshes the cached, human-readable list of set names.
*
* Typically called after adding or modifying set memberships or printings.
* Typically called after adding or modifying set memberships.
*/
void refreshCachedSets();
void refreshCachedSetNames();
/**
* @brief Simplifies a name for fuzzy matching.
@@ -324,21 +336,6 @@ public:
*/
static QString simplifyName(const QString &name);
private:
/**
* @brief Refreshes the cached, human-readable list of set names.
*
* Typically called after adding or modifying set memberships.
*/
void refreshCachedSetNames();
/**
* @brief Refreshes the cached list of alt names for the card.
*
* Typically called after adding or modifying the contained printings.
*/
void refreshCachedAltNames();
signals:
/**
* @brief Emitted when a pixmap for this card has been updated or finished loading.

View File

@@ -1,7 +1,5 @@
#include "card_relation.h"
#include "card_relation_type.h"
CardRelation::CardRelation(const QString &_name,
CardRelationType _attachType,
bool _isCreateAllExclusion,

View File

@@ -0,0 +1,73 @@
#ifndef COCKATRICE_CARD_RELATION_H
#define COCKATRICE_CARD_RELATION_H
#include "card_relation_type.h"
#include <QObject>
#include <QString>
class CardRelation : public QObject
{
Q_OBJECT
private:
QString name;
CardRelationType attachType;
bool isCreateAllExclusion;
bool isVariableCount;
int defaultCount;
bool isPersistent;
public:
explicit CardRelation(const QString &_name = QString(),
CardRelationType _attachType = CardRelationType::DoesNotAttach,
bool _isCreateAllExclusion = false,
bool _isVariableCount = false,
int _defaultCount = 1,
bool _isPersistent = false);
const QString &getName() const
{
return name;
}
CardRelationType getAttachType() const
{
return attachType;
}
bool getDoesAttach() const
{
return attachType != CardRelationType::DoesNotAttach;
}
bool getDoesTransform() const
{
return attachType == CardRelationType::TransformInto;
}
QString getAttachTypeAsString() const
{
return cardAttachTypeToString(attachType);
}
bool getCanCreateAnother() const
{
return !getDoesAttach();
}
bool getIsCreateAllExclusion() const
{
return isCreateAllExclusion;
}
bool getIsVariable() const
{
return isVariableCount;
}
int getDefaultCount() const
{
return defaultCount;
}
bool getIsPersistent() const
{
return isPersistent;
}
};
#endif // COCKATRICE_CARD_RELATION_H

View File

@@ -4,13 +4,7 @@
#include <QString>
/**
* @enum CardRelationType
* @ingroup Cards
* @brief Types of attachments between cards.
*
* DoesNotAttach: No attachment is present.
* AttachTo: This card attaches to another card.
* TransformInto: This card transforms into another card.
* Represents how a card relates to another (attach, transform, etc.).
*/
enum class CardRelationType
{
@@ -19,7 +13,7 @@ enum class CardRelationType
TransformInto = 2,
};
// Helper function to transform the enum values into human-readable strings
// Optional helper
inline QString cardAttachTypeToString(CardRelationType type)
{
switch (type) {

View File

@@ -1,30 +1,26 @@
#include "card_set.h"
#include <QSet>
#include <utility>
#include "../settings/cache_settings.h"
const char *CardSet::TOKENS_SETNAME = "TK";
CardSet::CardSet(ICardSetPriorityController *_priorityController,
const QString &_shortName,
CardSet::CardSet(const QString &_shortName,
const QString &_longName,
const QString &_setType,
const QDate &_releaseDate,
const CardSet::Priority _priority)
: priorityController(std::move(_priorityController)), shortName(_shortName), longName(_longName),
releaseDate(_releaseDate), setType(_setType), priority(_priority)
: shortName(_shortName), longName(_longName), releaseDate(_releaseDate), setType(_setType), priority(_priority)
{
loadSetOptions();
}
CardSetPtr CardSet::newInstance(ICardSetPriorityController *_priorityController,
const QString &_shortName,
CardSetPtr CardSet::newInstance(const QString &_shortName,
const QString &_longName,
const QString &_setType,
const QDate &_releaseDate,
const Priority _priority)
{
CardSetPtr ptr(new CardSet(_priorityController, _shortName, _longName, _setType, _releaseDate, _priority));
CardSetPtr ptr(new CardSet(_shortName, _longName, _setType, _releaseDate, _priority));
// ptr->setSmartPointer(ptr);
return ptr;
}
@@ -61,25 +57,25 @@ QString CardSet::getCorrectedShortName() const
void CardSet::loadSetOptions()
{
sortKey = priorityController->getSortKey(shortName);
enabled = priorityController->isEnabled(shortName);
isknown = priorityController->isKnown(shortName);
sortKey = SettingsCache::instance().cardDatabase().getSortKey(shortName);
enabled = SettingsCache::instance().cardDatabase().isEnabled(shortName);
isknown = SettingsCache::instance().cardDatabase().isKnown(shortName);
}
void CardSet::setSortKey(unsigned int _sortKey)
{
sortKey = _sortKey;
priorityController->setSortKey(shortName, _sortKey);
SettingsCache::instance().cardDatabase().setSortKey(shortName, _sortKey);
}
void CardSet::setEnabled(bool _enabled)
{
enabled = _enabled;
priorityController->setEnabled(shortName, _enabled);
SettingsCache::instance().cardDatabase().setEnabled(shortName, _enabled);
}
void CardSet::setIsKnown(bool _isknown)
{
isknown = _isknown;
priorityController->setIsKnown(shortName, _isknown);
SettingsCache::instance().cardDatabase().setIsKnown(shortName, _isknown);
}

View File

@@ -0,0 +1,116 @@
#ifndef COCKATRICE_CARD_SET_H
#define COCKATRICE_CARD_SET_H
#include <QDate>
#include <QList>
#include <QSharedPointer>
#include <QString>
class CardInfo;
using CardInfoPtr = QSharedPointer<CardInfo>;
class CardSet;
using CardSetPtr = QSharedPointer<CardSet>;
class CardSet : public QList<CardInfoPtr>
{
public:
enum Priority
{
PriorityFallback = 0,
PriorityPrimary = 10,
PrioritySecondary = 20,
PriorityReprint = 30,
PriorityOther = 40,
PriorityLowest = 100,
};
static const char *TOKENS_SETNAME;
private:
QString shortName, longName;
unsigned int sortKey;
QDate releaseDate;
QString setType;
Priority priority;
bool enabled, isknown;
public:
explicit CardSet(const QString &_shortName = QString(),
const QString &_longName = QString(),
const QString &_setType = QString(),
const QDate &_releaseDate = QDate(),
const Priority _priority = PriorityFallback);
static CardSetPtr newInstance(const QString &_shortName = QString(),
const QString &_longName = QString(),
const QString &_setType = QString(),
const QDate &_releaseDate = QDate(),
const Priority _priority = PriorityFallback);
QString getCorrectedShortName() const;
QString getShortName() const
{
return shortName;
}
QString getLongName() const
{
return longName;
}
QString getSetType() const
{
return setType;
}
QDate getReleaseDate() const
{
return releaseDate;
}
Priority getPriority() const
{
return priority;
}
void setLongName(const QString &_longName)
{
longName = _longName;
}
void setSetType(const QString &_setType)
{
setType = _setType;
}
void setReleaseDate(const QDate &_releaseDate)
{
releaseDate = _releaseDate;
}
void setPriority(const Priority _priority)
{
priority = _priority;
}
void loadSetOptions();
int getSortKey() const
{
return sortKey;
}
void setSortKey(unsigned int _sortKey);
bool getEnabled() const
{
return enabled;
}
void setEnabled(bool _enabled);
bool getIsKnown() const
{
return isknown;
}
void setIsKnown(bool _isknown);
bool getIsKnownIgnored() const
{
return longName.length() + setType.length() + releaseDate.toString().length() == 0;
}
};
#endif // COCKATRICE_CARD_SET_H

View File

@@ -0,0 +1,26 @@
#ifndef COCKATRICE_CARD_SET_LIST_H
#define COCKATRICE_CARD_SET_LIST_H
#include "card_set.h"
#include <QList>
#include <QStringList>
class CardSetList : public QList<CardSetPtr>
{
private:
class KeyCompareFunctor;
public:
void sortByKey();
void guessSortKeys();
void enableAllUnknown();
void enableAll();
void markAllAsKnown();
int getEnabledSetsNum();
int getUnknownSetsNum();
QStringList getUnknownSetsNames();
void defaultSort();
};
#endif // COCKATRICE_CARD_SET_LIST_H

View File

@@ -1,8 +1,5 @@
#include "exact_card.h"
#include "../card_info.h"
#include "printing_info.h"
/**
* Default constructor.
* This will set the CardInfoPtr to null.

View File

@@ -0,0 +1,48 @@
#ifndef EXACT_CARD_H
#define EXACT_CARD_H
#include "card_info.h"
/**
* @class ExactCard
* @ingroup Cards
* @brief Identifies the card by its CardInfoPtr along with its exact printing by its PrintingInfo.
*/
class ExactCard
{
CardInfoPtr card;
PrintingInfo printing;
public:
ExactCard();
explicit ExactCard(const CardInfoPtr &_card, const PrintingInfo &_printing = PrintingInfo());
/**
* Gets the CardInfoPtr. Can be null.
*/
CardInfoPtr getCardPtr() const
{
return card;
}
/**
* Gets the PrintingInfo. Can be empty.
*/
PrintingInfo getPrinting() const
{
return printing;
}
bool operator==(const ExactCard &other) const;
QString getName() const;
const CardInfo &getInfo() const;
QString getPixmapCacheKey() const;
bool isEmpty() const;
explicit operator bool() const;
void emitPixmapUpdated() const;
};
#endif // EXACT_CARD_H

View File

@@ -53,6 +53,6 @@ inline static const QString getNicePropertyName(QString key)
return QCoreApplication::translate("Mtg", "Color Identity");
return key;
}
} // namespace Mtg
}; // namespace Mtg
#endif

View File

@@ -1,7 +1,5 @@
#include "printing_info.h"
#include "../set/card_set.h"
PrintingInfo::PrintingInfo(const CardSetPtr &_set) : set(_set)
{
}
@@ -12,9 +10,4 @@ PrintingInfo::PrintingInfo(const CardSetPtr &_set) : set(_set)
QString PrintingInfo::getUuid() const
{
return properties.value("uuid").toString();
}
QString PrintingInfo::getFlavorName() const
{
return properties.value("flavorName").toString();
}

View File

@@ -0,0 +1,56 @@
#ifndef COCKATRICE_PRINTING_INFO_H
#define COCKATRICE_PRINTING_INFO_H
#include "card_set.h"
#include <QList>
#include <QMap>
#include <QStringList>
#include <QVariant>
class PrintingInfo;
using SetToPrintingsMap = QMap<QString, QList<PrintingInfo>>;
/**
* Info relating to a specific printing for a card.
*/
class PrintingInfo
{
public:
explicit PrintingInfo(const CardSetPtr &_set = nullptr);
~PrintingInfo() = default;
bool operator==(const PrintingInfo &other) const
{
return this->set == other.set && this->properties == other.properties;
}
private:
CardSetPtr set;
// per-printing card properties;
QVariantHash properties;
public:
CardSetPtr getSet() const
{
return set;
}
QStringList getProperties() const
{
return properties.keys();
}
QString getProperty(const QString &propertyName) const
{
return properties.value(propertyName).toString();
}
void setProperty(const QString &_name, const QString &_value)
{
properties.insert(_name, _value);
}
QString getUuid() const;
};
#endif // COCKATRICE_PRINTING_INFO_H

View File

@@ -1,8 +1,7 @@
#include "../../../interface/widgets/menus/deck_editor_menu.h"
#include "deck_editor_menu.h"
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../tabs/abstract_tab_deck_editor.h"
#include "../settings/cache_settings.h"
#include "../settings/shortcuts_settings.h"
DeckEditorMenu::DeckEditorMenu(AbstractTabDeckEditor *parent) : QMenu(parent), deckEditor(parent)
{

View File

@@ -7,6 +7,8 @@
#ifndef DECK_EDITOR_MENU_H
#define DECK_EDITOR_MENU_H
#include "../tabs/abstract_tab_deck_editor.h"
#include <QMenu>
class AbstractTabDeckEditor;

View File

@@ -1,7 +1,5 @@
#include "get_text_with_max.h"
#include <QInputDialog>
QString getTextWithMax(QWidget *parent,
const QString &title,
const QString &label,

View File

@@ -7,9 +7,9 @@
#ifndef GETTEXTWITHMAX_H
#define GETTEXTWITHMAX_H
#include <QLineEdit>
#include <QWidget>
#include <libcockatrice/utility/trice_limits.h>
#include "trice_limits.h"
#include <QInputDialog>
QString getTextWithMax(QWidget *parent,
const QString &title,

View File

@@ -1,6 +1,6 @@
#include "client_update_checker.h"
#include "../../../settings/cache_settings.h"
#include "../../settings/cache_settings.h"
#include "release_channel.h"
ClientUpdateChecker::ClientUpdateChecker(QObject *parent) : QObject(parent)

View File

@@ -6,7 +6,7 @@
#ifndef INTERFACE_JSON_DECK_PARSER_H
#define INTERFACE_JSON_DECK_PARSER_H
#include "../../../interface/deck_loader/deck_loader.h"
#include "../../../deck/deck_loader.h"
#include <QJsonArray>
#include <QJsonObject>
@@ -24,13 +24,13 @@ class ArchidektJsonParser : public IJsonDeckParser
public:
DeckLoader *parse(const QJsonObject &obj) override
{
DeckLoader *loader = new DeckLoader(nullptr);
DeckLoader *list = new DeckLoader();
QString deckName = obj.value("name").toString();
QString deckDescription = obj.value("description").toString();
loader->getDeckList()->setName(deckName);
loader->getDeckList()->setComments(deckDescription);
list->setName(deckName);
list->setComments(deckDescription);
QString outputText;
QTextStream outStream(&outputText);
@@ -47,10 +47,10 @@ public:
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
}
loader->getDeckList()->loadFromStream_Plain(outStream, false);
DeckLoader::resolveSetNameAndNumberToProviderID(loader->getDeckList());
list->loadFromStream_Plain(outStream, false);
list->resolveSetNameAndNumberToProviderID();
return loader;
return list;
}
};
@@ -59,13 +59,13 @@ class MoxfieldJsonParser : public IJsonDeckParser
public:
DeckLoader *parse(const QJsonObject &obj) override
{
DeckLoader *loader = new DeckLoader(nullptr);
DeckLoader *list = new DeckLoader();
QString deckName = obj.value("name").toString();
QString deckDescription = obj.value("description").toString();
loader->getDeckList()->setName(deckName);
loader->getDeckList()->setComments(deckDescription);
list->setName(deckName);
list->setComments(deckDescription);
QString outputText;
QTextStream outStream(&outputText);
@@ -94,8 +94,8 @@ public:
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
}
loader->getDeckList()->loadFromStream_Plain(outStream, false);
DeckLoader::resolveSetNameAndNumberToProviderID(loader->getDeckList());
list->loadFromStream_Plain(outStream, false);
list->resolveSetNameAndNumberToProviderID();
QJsonObject commandersObj = obj.value("commanders").toObject();
if (!commandersObj.isEmpty()) {
@@ -106,12 +106,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);
list->setBannerCard({commanderName, providerId});
list->addCard(commanderName, DECK_ZONE_MAIN, -1, setName, collectorNumber, providerId);
}
}
return loader;
return list;
}
};

View File

@@ -57,27 +57,27 @@ protected:
}
public:
[[nodiscard]] QString getName() const
QString getName() const
{
return name;
}
[[nodiscard]] QString getDescriptionUrl() const
QString getDescriptionUrl() const
{
return descriptionUrl;
}
[[nodiscard]] QString getDownloadUrl() const
QString getDownloadUrl() const
{
return downloadUrl;
}
[[nodiscard]] QString getCommitHash() const
QString getCommitHash() const
{
return commitHash;
}
[[nodiscard]] QDate getPublishDate() const
QDate getPublishDate() const
{
return publishDate;
}
[[nodiscard]] bool isCompatibleVersionFound() const
bool isCompatibleVersionFound() const
{
return compatibleVersionFound;
}
@@ -97,15 +97,15 @@ protected:
protected:
static bool downloadMatchesCurrentOS(const QString &fileName);
[[nodiscard]] virtual QString getReleaseChannelUrl() const = 0;
virtual QString getReleaseChannelUrl() const = 0;
public:
Release *getLastRelease()
{
return lastRelease;
}
[[nodiscard]] virtual QString getManualDownloadUrl() const = 0;
[[nodiscard]] virtual QString getName() const = 0;
virtual QString getManualDownloadUrl() const = 0;
virtual QString getName() const = 0;
void checkForUpdates();
signals:
void finishedCheck(bool needToUpdate, bool isCompatible, Release *release);
@@ -122,12 +122,12 @@ public:
explicit StableReleaseChannel() = default;
~StableReleaseChannel() override = default;
[[nodiscard]] QString getManualDownloadUrl() const override;
QString getManualDownloadUrl() const override;
[[nodiscard]] QString getName() const override;
QString getName() const override;
protected:
[[nodiscard]] QString getReleaseChannelUrl() const override;
QString getReleaseChannelUrl() const override;
protected slots:
void releaseListFinished() override;
@@ -143,12 +143,12 @@ public:
BetaReleaseChannel() = default;
~BetaReleaseChannel() override = default;
[[nodiscard]] QString getManualDownloadUrl() const override;
QString getManualDownloadUrl() const override;
[[nodiscard]] QString getName() const override;
QString getName() const override;
protected:
[[nodiscard]] QString getReleaseChannelUrl() const override;
QString getReleaseChannelUrl() const override;
protected slots:
void releaseListFinished() override;

View File

@@ -1,9 +1,10 @@
#include "replay_timeline_widget.h"
#include "../../../client/settings/cache_settings.h"
#include "../../settings/cache_settings.h"
#include <QPainter>
#include <QPainterPath>
#include <QPalette>
#include <QTimer>
ReplayTimelineWidget::ReplayTimelineWidget(QWidget *parent)

View File

@@ -7,8 +7,9 @@
#ifndef REPLAY_TIMELINE_WIDGET
#define REPLAY_TIMELINE_WIDGET
#include "../../../game/player/event_processing_options.h"
#include "../../game/player/event_processing_options.h"
#include <QList>
#include <QMouseEvent>
#include <QWidget>
@@ -59,10 +60,10 @@ public:
explicit ReplayTimelineWidget(QWidget *parent = nullptr);
void setTimeline(const QList<int> &_replayTimeline);
[[nodiscard]] QSize sizeHint() const override;
[[nodiscard]] QSize minimumSizeHint() const override;
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
void setTimeScaleFactor(qreal _timeScaleFactor);
[[nodiscard]] int getCurrentEvent() const
int getCurrentEvent() const
{
return currentEvent;
}

View File

@@ -1,4 +1,4 @@
#include "card_sets_model.h"
#include "sets_model.h"
#include <QSortFilterProxyModel>

View File

@@ -7,11 +7,12 @@
#ifndef SETSMODEL_H
#define SETSMODEL_H
#include "../../database/card_database.h"
#include <QAbstractTableModel>
#include <QMimeData>
#include <QSet>
#include <QSortFilterProxyModel>
#include <libcockatrice/card/database/card_database.h>
class SetsProxyModel;
@@ -25,11 +26,11 @@ public:
SetsMimeData(int _oldRow) : oldRow(_oldRow)
{
}
[[nodiscard]] int getOldRow() const
int getOldRow() const
{
return oldRow;
}
[[nodiscard]] QStringList formats() const
QStringList formats() const
{
return QStringList() << "application/x-cockatricecardset";
}
@@ -64,23 +65,22 @@ public:
explicit SetsModel(CardDatabase *_db, QObject *parent = nullptr);
~SetsModel() override;
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
[[nodiscard]] int columnCount(const QModelIndex &parent = QModelIndex()) const override
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override
{
Q_UNUSED(parent);
return NUM_COLS;
}
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
[[nodiscard]] QVariant
headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
[[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override;
[[nodiscard]] Qt::DropActions supportedDropActions() const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
Qt::DropActions supportedDropActions() const override;
[[nodiscard]] QMimeData *mimeData(const QModelIndexList &indexes) const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool
dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
[[nodiscard]] QStringList mimeTypes() const override;
QStringList mimeTypes() const override;
void swapRows(int oldRow, int newRow);
void toggleRow(int row, bool enable);
void toggleRow(int row);
@@ -98,8 +98,8 @@ public:
explicit SetsDisplayModel(QObject *parent = nullptr);
protected:
[[nodiscard]] bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
[[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
void fetchMore(const QModelIndex &index) override;
};

View File

@@ -1,19 +1,21 @@
#include "spoiler_background_updater.h"
#include "../../../../interface/window_main.h"
#include "../../../../main.h"
#include "../../../settings/cache_settings.h"
#include "../../database/card_database.h"
#include "../../database/card_database_manager.h"
#include "../../interface/window_main.h"
#include "../../main.h"
#include "../../settings/cache_settings.h"
#include <QApplication>
#include <QCryptographicHash>
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QLocale>
#include <QMessageBox>
#include <QNetworkReply>
#include <QUrl>
#include <QtConcurrent>
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/card/database/card_database_manager.h>
#define SPOILERS_STATUS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/SpoilerSeasonEnabled"
#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml"

View File

@@ -22,7 +22,7 @@ public:
inline QString getCardUpdaterBinaryName()
{
return "oracle";
}
};
QByteArray getHash(const QString fileName);
QByteArray getHash(QByteArray data);
static bool deleteSpoilerFile();

View File

@@ -1,6 +1,6 @@
#include "replay_manager.h"
#include "../interface/widgets/tabs/tab_game.h"
#include "../tabs/tab_game.h"
#include <QHBoxLayout>
#include <QToolButton>

View File

@@ -8,11 +8,11 @@
#ifndef REPLAY_MANAGER_H
#define REPLAY_MANAGER_H
#include "replay_timeline_widget.h"
#include "network/replay_timeline_widget.h"
#include "pb/game_replay.pb.h"
#include <QToolButton>
#include <QWidget>
#include <libcockatrice/protocol/pb/game_replay.pb.h>
class TabGame;

View File

@@ -1,6 +1,6 @@
#include "sound_engine.h"
#include "settings/cache_settings.h"
#include "../settings/cache_settings.h"
#include <QDir>
#include <QMediaPlayer>

View File

@@ -1,13 +1,15 @@
#include "tapped_out_interface.h"
#include "deck_list.h"
#include "deck_list_card_node.h"
#include <QDesktopServices>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QRegularExpression>
#include <QUrlQuery>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent)
: QObject(parent), cardDatabase(_cardDatabase)

View File

@@ -7,8 +7,11 @@
#ifndef TAPPEDOUT_INTERFACE_H
#define TAPPEDOUT_INTERFACE_H
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/deck_list/deck_list.h>
#include "../database/card_database.h"
#include "deck_list.h"
#include <QLoggingCategory>
#include <QObject>
inline Q_LOGGING_CATEGORY(TappedOutInterfaceLog, "tapped_out_interface");

View File

@@ -1,5 +1,6 @@
#include "update_downloader.h"
#include <QDebug>
#include <QUrl>
UpdateDownloader::UpdateDownloader(QObject *parent) : QObject(parent), response(nullptr)

View File

@@ -7,7 +7,9 @@
#ifndef COCKATRICE_UPDATEDOWNLOADER_H
#define COCKATRICE_UPDATEDOWNLOADER_H
#include <QDate>
#include <QObject>
#include <QUrl>
#include <QtNetwork>
class UpdateDownloader : public QObject

View File

@@ -1,34 +1,35 @@
#include "card_database.h"
#include "../relation/card_relation.h"
#include "../card/card_relation.h"
#include "../picture_loader/picture_loader.h"
#include "../settings/cache_settings.h"
#include "parser/cockatrice_xml_3.h"
#include "parser/cockatrice_xml_4.h"
#include <QCryptographicHash>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QMessageBox>
#include <QRegularExpression>
#include <algorithm>
#include <utility>
CardDatabase::CardDatabase(QObject *parent,
ICardPreferenceProvider *prefs,
ICardDatabasePathProvider *pathProvider,
ICardSetPriorityController *_setPriorityController)
: QObject(parent), setPriorityController(_setPriorityController), loadStatus(NotLoaded)
CardDatabase::CardDatabase(QObject *parent) : QObject(parent), loadStatus(NotLoaded)
{
qRegisterMetaType<CardInfoPtr>("CardInfoPtr");
qRegisterMetaType<CardInfoPtr>("CardSetPtr");
// create loader and wire it up
loader = new CardDatabaseLoader(this, this, pathProvider, prefs);
loader = new CardDatabaseLoader(this, this);
// 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);
connect(loader, &CardDatabaseLoader::newSetsFound, this, &CardDatabase::cardDatabaseNewSetsFound);
connect(loader, &CardDatabaseLoader::allNewSetsEnabled, this, &CardDatabase::cardDatabaseAllNewSetsEnabled);
querier = new CardDatabaseQuerier(this, this, prefs);
querier = new CardDatabaseQuerier(this, this);
}
CardDatabase::~CardDatabase()
@@ -138,7 +139,7 @@ CardSetPtr CardDatabase::getSet(const QString &setName)
if (sets.contains(setName)) {
return sets.value(setName);
} else {
CardSetPtr newSet = CardSet::newInstance(setPriorityController, setName);
CardSetPtr newSet = CardSet::newInstance(setName);
sets.insert(setName, newSet);
return newSet;
}
@@ -192,9 +193,8 @@ void CardDatabase::markAllSetsAsKnown()
void CardDatabase::notifyEnabledSetsChanged()
{
// refresh the list of cached set names
for (const CardInfoPtr &card : cards) {
card->refreshCachedSets();
}
for (const CardInfoPtr &card : cards)
card->refreshCachedSetNames();
// inform the carddatabasemodels that they need to re-check their list of cards
emit cardDatabaseEnabledSetsChanged();

View File

@@ -0,0 +1,103 @@
/**
* @file card_database.h
* @ingroup CardDatabase
* @brief The CardDatabase is responsible for holding the card and set maps.
*/
#ifndef CARDDATABASE_H
#define CARDDATABASE_H
#include "../card/card_set_list.h"
#include "../card/exact_card.h"
#include "../common/card_ref.h"
#include "card_database_loader.h"
#include "card_database_querier.h"
#include <QBasicMutex>
#include <QDate>
#include <QHash>
#include <QList>
#include <QLoggingCategory>
#include <QStringList>
#include <QVector>
#include <utility>
inline Q_LOGGING_CATEGORY(CardDatabaseLog, "card_database");
class CardDatabase : public QObject
{
Q_OBJECT
protected:
/*
* The cards, indexed by name.
*/
CardNameMap cards;
/**
* The cards, indexed by their simple name.
*/
CardNameMap simpleNameCards;
/*
* The sets, indexed by short name.
*/
SetNameMap sets;
// loader responsible for file discovery & parsing
CardDatabaseLoader *loader;
LoadStatus loadStatus;
CardDatabaseQuerier *querier;
private:
void checkUnknownSets();
void refreshCachedReverseRelatedCards();
QBasicMutex *clearDatabaseMutex = new QBasicMutex(), *addCardMutex = new QBasicMutex(),
*removeCardMutex = new QBasicMutex();
public:
explicit CardDatabase(QObject *parent = nullptr);
~CardDatabase() override;
void removeCard(CardInfoPtr card);
void clear();
const CardNameMap &getCardList() const
{
return cards;
}
CardSetPtr getSet(const QString &setName);
CardSetList getSetList() const;
LoadStatus getLoadStatus() const
{
return loadStatus;
}
CardDatabaseQuerier *query() const
{
return querier;
}
void enableAllUnknownSets();
void markAllSetsAsKnown();
void notifyEnabledSetsChanged();
public slots:
void addCard(CardInfoPtr card);
void addSet(CardSetPtr set);
void loadCardDatabases();
bool saveCustomTokensToFile();
signals:
void cardDatabaseLoadingFinished();
void cardDatabaseLoadingFailed();
void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames);
void cardDatabaseAllNewSetsEnabled();
void cardDatabaseEnabledSetsChanged();
void cardAdded(CardInfoPtr card);
void cardRemoved(CardInfoPtr card);
friend class CardDatabaseLoader;
friend class CardDatabaseQuerier;
};
#endif

View File

@@ -1,5 +1,6 @@
#include "card_database_loader.h"
#include "../settings/cache_settings.h"
#include "card_database.h"
#include "parser/cockatrice_xml_3.h"
#include "parser/cockatrice_xml_4.h"
@@ -9,14 +10,10 @@
#include <QFile>
#include <QTime>
CardDatabaseLoader::CardDatabaseLoader(QObject *parent,
CardDatabase *db,
ICardDatabasePathProvider *_pathProvider,
ICardPreferenceProvider *_preferenceProvider)
: QObject(parent), database(db), pathProvider(_pathProvider)
CardDatabaseLoader::CardDatabaseLoader(QObject *parent, CardDatabase *db) : QObject(parent), database(db)
{
// instantiate available parsers here and connect them to the database
availableParsers << new CockatriceXml4Parser(_preferenceProvider);
availableParsers << new CockatriceXml4Parser;
availableParsers << new CockatriceXml3Parser;
for (auto *p : availableParsers) {
@@ -26,7 +23,7 @@ CardDatabaseLoader::CardDatabaseLoader(QObject *parent,
}
// when SettingsCache's path changes, trigger reloads
connect(pathProvider, &ICardDatabasePathProvider::cardDatabasePathChanged, this,
connect(&SettingsCache::instance(), &SettingsCache::cardDatabasePathChanged, this,
&CardDatabaseLoader::loadCardDatabases);
}
@@ -39,7 +36,8 @@ CardDatabaseLoader::~CardDatabaseLoader()
LoadStatus CardDatabaseLoader::loadFromFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
file.open(QIODevice::ReadOnly);
if (!file.isOpen()) {
return FileError;
}
@@ -86,9 +84,10 @@ LoadStatus CardDatabaseLoader::loadCardDatabases()
database->clear(); // remove old db
LoadStatus loadStatus = loadCardDatabase(pathProvider->getCardDatabasePath()); // load main card database
loadCardDatabase(pathProvider->getTokenDatabasePath()); // load tokens database
loadCardDatabase(pathProvider->getSpoilerCardDatabasePath()); // load spoilers database
LoadStatus loadStatus =
loadCardDatabase(SettingsCache::instance().getCardDatabasePath()); // load main card database
loadCardDatabase(SettingsCache::instance().getTokenDatabasePath()); // load tokens database
loadCardDatabase(SettingsCache::instance().getSpoilerCardDatabasePath()); // load spoilers database
// find all custom card databases, recursively & following symlinks
// then load them alphabetically
@@ -119,7 +118,7 @@ LoadStatus CardDatabaseLoader::loadCardDatabases()
QStringList CardDatabaseLoader::collectCustomDatabasePaths() const
{
QDirIterator it(pathProvider->getCustomCardDatabasePath(), {"*.xml"}, QDir::Files,
QDirIterator it(SettingsCache::instance().getCustomCardDatabasePath(), {"*.xml"}, QDir::Files,
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
QStringList paths;
@@ -136,7 +135,7 @@ bool CardDatabaseLoader::saveCustomTokensToFile()
return false;
}
QString fileName = pathProvider->getCustomCardDatabasePath() + "/" + CardSet::TOKENS_SETNAME + ".xml";
QString fileName = SettingsCache::instance().getCustomCardDatabasePath() + "/" + CardSet::TOKENS_SETNAME + ".xml";
SetNameMap tmpSets;
CardSetPtr customTokensSet = database->getSet(CardSet::TOKENS_SETNAME);
@@ -151,4 +150,4 @@ bool CardDatabaseLoader::saveCustomTokensToFile()
availableParsers.first()->saveToFile(tmpSets, tmpCards, fileName);
return true;
}
}

View File

@@ -0,0 +1,63 @@
/**
* @file card_database_loader.h
* @ingroup CardDatabase
* @brief The CardDatabaseLoader is responsible for populating the card database from files on disk.
*/
#ifndef COCKATRICE_CARD_DATABASE_LOADER_H
#define COCKATRICE_CARD_DATABASE_LOADER_H
#include <QBasicMutex>
#include <QList>
#include <QLoggingCategory>
#include <QObject>
inline Q_LOGGING_CATEGORY(CardDatabaseLoadingLog, "card_database.loading");
inline Q_LOGGING_CATEGORY(CardDatabaseLoadingSuccessOrFailureLog, "card_database.loading.success_or_failure");
class CardDatabase;
class ICardDatabaseParser;
enum LoadStatus
{
Ok,
VersionTooOld,
Invalid,
NotLoaded,
FileError,
NoCards
};
class CardDatabaseLoader : public QObject
{
Q_OBJECT
public:
explicit CardDatabaseLoader(QObject *parent, CardDatabase *db);
~CardDatabaseLoader() override;
public slots:
LoadStatus loadCardDatabases(); // discover & load the configured databases
LoadStatus loadCardDatabase(const QString &path); // load a single file
bool saveCustomTokensToFile(); // write tokens to custom DB path
signals:
void loadingStarted();
void loadingFinished();
void loadingFailed();
void newSetsFound(int numSets, const QStringList &setNames);
void allNewSetsEnabled();
private:
LoadStatus loadFromFile(const QString &fileName); // internal helper
QStringList collectCustomDatabasePaths() const;
CardDatabase *database; // non-owning pointer to the container
// parsers
QList<ICardDatabaseParser *> availableParsers;
QBasicMutex *loadFromFileMutex = new QBasicMutex();
QBasicMutex *reloadDatabaseMutex = new QBasicMutex();
};
#endif // COCKATRICE_CARD_DATABASE_LOADER_H

View File

@@ -0,0 +1,12 @@
#include "card_database_manager.h"
CardDatabase *CardDatabaseManager::getInstance()
{
static CardDatabase instance; // Created only once, on first access
return &instance;
}
CardDatabaseQuerier *CardDatabaseManager::query()
{
return getInstance()->query();
}

View File

@@ -0,0 +1,29 @@
/**
* @file card_database_manager.h
* @ingroup CardDatabase
* @brief The CardDatabaseManager is responsible for managing the global database singleton.
*/
#ifndef CARD_DATABASE_ACCESSOR_H
#define CARD_DATABASE_ACCESSOR_H
#pragma once
#include "card_database.h"
class CardDatabaseManager
{
public:
// Delete copy constructor and assignment operator to enforce singleton
CardDatabaseManager(const CardDatabaseManager &) = delete;
CardDatabaseManager &operator=(const CardDatabaseManager &) = delete;
// Static method to access the singleton instance
static CardDatabase *getInstance();
static CardDatabaseQuerier *query();
private:
CardDatabaseManager() = default; // Private constructor
~CardDatabaseManager() = default;
};
#endif // CARD_DATABASE_ACCESSOR_H

View File

@@ -1,16 +1,11 @@
#include "card_database_querier.h"
#include "../card_info.h"
#include "../printing/exact_card.h"
#include "../set/card_set_comparator.h"
#include "../utility/card_set_comparator.h"
#include "card_database.h"
#include <qrandom.h>
CardDatabaseQuerier::CardDatabaseQuerier(QObject *_parent,
const CardDatabase *_db,
const ICardPreferenceProvider *prefs)
: QObject(_parent), db(_db), prefs(prefs)
CardDatabaseQuerier::CardDatabaseQuerier(QObject *_parent, const CardDatabase *_db) : QObject(_parent), db(_db)
{
}
@@ -207,17 +202,6 @@ PrintingInfo CardDatabaseQuerier::getSpecificPrinting(const QString &cardName,
return PrintingInfo(nullptr);
}
/**
* Gets the card representing the preferred printing of the cardInfo
*
* @param cardName The cardName to find the preferred card and printing for
* @return A specific printing of a card
*/
ExactCard CardDatabaseQuerier::getPreferredCard(const QString &cardName) const
{
return getPreferredCard(getCardInfo(cardName));
}
/**
* Gets the card representing the preferred printing of the cardInfo
*
@@ -250,12 +234,6 @@ PrintingInfo CardDatabaseQuerier::getPreferredPrinting(const CardInfoPtr &cardIn
return PrintingInfo(nullptr);
}
const auto &pinnedPrintingProviderId = prefs->getCardPreferenceOverride(cardInfo->getName());
if (!pinnedPrintingProviderId.isEmpty()) {
return getSpecificPrinting({cardInfo->getName(), pinnedPrintingProviderId});
}
SetToPrintingsMap setMap = cardInfo->getSets();
if (setMap.empty()) {
return PrintingInfo(nullptr);

View File

@@ -0,0 +1,60 @@
/**
* @file card_database_querier.h
* @ingroup CardDatabase
* @brief The CardDatabaseQuerier is responsible for querying the database and returning data.
*/
#ifndef COCKATRICE_CARD_DATABASE_QUERIER_H
#define COCKATRICE_CARD_DATABASE_QUERIER_H
#include "../card/exact_card.h"
#include "../common/card_ref.h"
#include <QObject>
class CardDatabase;
class CardDatabaseQuerier : public QObject
{
Q_OBJECT
public:
explicit CardDatabaseQuerier(QObject *parent, const CardDatabase *db);
[[nodiscard]] CardInfoPtr getCardInfo(const QString &cardName) const;
[[nodiscard]] QList<CardInfoPtr> getCardInfos(const QStringList &cardNames) const;
/*
* Get a card by its simple name. The name will be simplified in this
* function, so you don't need to simplify it beforehand.
*/
[[nodiscard]] CardInfoPtr getCardBySimpleName(const QString &cardName) const;
[[nodiscard]] ExactCard guessCard(const CardRef &cardRef) const;
[[nodiscard]] ExactCard getCard(const CardRef &cardRef) const;
[[nodiscard]] QList<ExactCard> getCards(const QList<CardRef> &cardRefs) const;
[[nodiscard]] ExactCard getRandomCard() const;
[[nodiscard]] ExactCard getCardFromSameSet(const QString &cardName, const PrintingInfo &otherPrinting) const;
[[nodiscard]] ExactCard getPreferredCard(const CardInfoPtr &card) const;
[[nodiscard]] bool isPreferredPrinting(const CardRef &cardRef) const;
[[nodiscard]] PrintingInfo getPreferredPrinting(const CardInfoPtr &card) const;
[[nodiscard]] PrintingInfo getPreferredPrinting(const QString &cardName) const;
[[nodiscard]] QString getPreferredPrintingProviderId(const QString &cardName) const;
[[nodiscard]] PrintingInfo getSpecificPrinting(const CardRef &cardRef) const;
[[nodiscard]] PrintingInfo
getSpecificPrinting(const QString &cardName, const QString &setCode, const QString &collectorNumber) const;
[[nodiscard]] PrintingInfo findPrintingWithId(const CardInfoPtr &card, const QString &providerId) const;
[[nodiscard]] QStringList getAllMainCardTypes() const;
[[nodiscard]] QMap<QString, int> getAllMainCardTypesWithCount() const;
[[nodiscard]] QMap<QString, int> getAllSubCardTypesWithCount() const;
private:
const CardDatabase *db;
CardInfoPtr lookupCardByName(const QString &name) const;
};
#endif // COCKATRICE_CARD_DATABASE_QUERIER_H

View File

@@ -1,21 +0,0 @@
#ifndef COCKATRICE_SETTINGS_CARD_PREFERENCE_PROVIDER_H
#define COCKATRICE_SETTINGS_CARD_PREFERENCE_PROVIDER_H
#include "../../client/settings/cache_settings.h"
#include <libcockatrice/interfaces/interface_card_preference_provider.h>
class SettingsCardPreferenceProvider : public ICardPreferenceProvider
{
public:
[[nodiscard]] QString getCardPreferenceOverride(const QString &cardName) const override
{
return SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardName);
}
[[nodiscard]] bool getIncludeRebalancedCards() const override
{
return SettingsCache::instance().getIncludeRebalancedCards();
}
};
#endif // COCKATRICE_SETTINGS_CARD_PREFERENCE_PROVIDER_H

View File

@@ -16,7 +16,7 @@ public:
explicit CardCompleterProxyModel(QObject *parent = nullptr);
protected:
[[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
};
#endif // CARD_COMPLETER_PROXY_MODEL_H

View File

@@ -1,10 +1,9 @@
#include "card_search_model.h"
#include "../card_database_display_model.h"
#include "../../../utility/levenshtein.h"
#include "../card_database_model.h"
#include <algorithm>
#include <libcockatrice/utility/levenshtein.h>
CardSearchModel::CardSearchModel(CardDatabaseDisplayModel *sourceModel, QObject *parent)
: QAbstractListModel(parent), sourceModel(sourceModel)

View File

@@ -17,8 +17,8 @@ class CardSearchModel : public QAbstractListModel
public:
explicit CardSearchModel(CardDatabaseDisplayModel *sourceModel, QObject *parent = nullptr);
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void updateSearchResults(const QString &query); // Update results based on input

View File

@@ -184,20 +184,13 @@ bool CardDatabaseDisplayModel::rowMatchesCardName(CardInfoPtr info) const
void CardDatabaseDisplayModel::clearFilterAll()
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 9, 0))
beginFilterChange();
#endif
cardName.clear();
cardText.clear();
cardTypes.clear();
cardColors.clear();
if (filterTree != nullptr)
filterTree->clear();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0))
endFilterChange(QSortFilterProxyModel::Direction::Rows);
#else
invalidateFilter();
#endif
}
void CardDatabaseDisplayModel::setFilterTree(FilterTree *_filterTree)
@@ -224,4 +217,4 @@ const QString CardDatabaseDisplayModel::sanitizeCardName(const QString &dirtyNam
}
}
return QString::fromStdWString(toReturn);
}
}

View File

@@ -8,9 +8,12 @@
#ifndef COCKATRICE_CARD_DATABASE_DISPLAY_MODEL_H
#define COCKATRICE_CARD_DATABASE_DISPLAY_MODEL_H
#include "../../filters/filter_string.h"
#include <QList>
#include <QSet>
#include <QSortFilterProxyModel>
#include <QTimer>
#include <libcockatrice/filters/filter_string.h>
class FilterTree;
class CardDatabaseDisplayModel : public QSortFilterProxyModel
@@ -75,17 +78,17 @@ public:
dirtyTimer.start(20);
}
void clearFilterAll();
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
[[nodiscard]] bool canFetchMore(const QModelIndex &parent) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool canFetchMore(const QModelIndex &parent) const override;
void fetchMore(const QModelIndex &parent) override;
signals:
void modelDirty();
protected:
[[nodiscard]] bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
static int lessThanNumerically(const QString &left, const QString &right);
[[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
[[nodiscard]] bool rowMatchesCardName(CardInfoPtr info) const;
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool rowMatchesCardName(CardInfoPtr info) const;
private slots:
void filterTreeChanged();

View File

@@ -1,7 +1,6 @@
#include "card_database_model.h"
#include <QMap>
#include <libcockatrice/card/database/card_database.h>
#define CARDDBMODEL_COLUMNS 6

View File

@@ -7,10 +7,11 @@
#ifndef CARDDATABASEMODEL_H
#define CARDDATABASEMODEL_H
#include "../card_database.h"
#include <QAbstractListModel>
#include <QList>
#include <QSet>
#include <libcockatrice/card/database/card_database.h>
class CardDatabaseModel : public QAbstractListModel
{
@@ -31,16 +32,15 @@ public:
};
CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromEnabledSets, QObject *parent = nullptr);
~CardDatabaseModel() override;
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
[[nodiscard]] int columnCount(const QModelIndex &parent = QModelIndex()) const override;
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
[[nodiscard]] QVariant
headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
[[nodiscard]] CardDatabase *getDatabase() const
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
CardDatabase *getDatabase() const
{
return db;
}
[[nodiscard]] CardInfoPtr getCard(int index) const
CardInfoPtr getCard(int index) const
{
return cardList[index];
}

View File

@@ -14,10 +14,10 @@ class TokenDisplayModel : public CardDatabaseDisplayModel
Q_OBJECT
public:
explicit TokenDisplayModel(QObject *parent = nullptr);
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
protected:
[[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
};
#endif // COCKATRICE_TOKEN_DISPLAY_MODEL_H

View File

@@ -1,10 +1,7 @@
#include "token_edit_model.h"
#include "../card_database_display_model.h"
#include "../card_database_model.h"
#include <libcockatrice/card/card_info.h>
TokenEditModel::TokenEditModel(QObject *parent) : CardDatabaseDisplayModel(parent)
{
}

View File

@@ -14,10 +14,10 @@ class TokenEditModel : public CardDatabaseDisplayModel
Q_OBJECT
public:
explicit TokenEditModel(QObject *parent = nullptr);
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
protected:
[[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
};
#endif // COCKATRICE_TOKEN_EDIT_MODEL_H

View File

@@ -1,7 +1,5 @@
#include "card_database_parser.h"
#include <libcockatrice/interfaces/noop_card_set_priority_controller.h>
SetNameMap ICardDatabaseParser::sets;
void ICardDatabaseParser::clearSetlist()
@@ -19,7 +17,7 @@ CardSetPtr ICardDatabaseParser::internalAddSet(const QString &setName,
return sets.value(setName);
}
CardSetPtr newSet = CardSet::newInstance(new NoopCardSetPriorityController(), setName);
CardSetPtr newSet = CardSet::newInstance(setName);
newSet->setLongName(longName);
newSet->setSetType(setType);
newSet->setReleaseDate(releaseDate);

View File

@@ -0,0 +1,51 @@
/**
* @file card_database_parser.h
* @ingroup CardDatabaseParsers
* @brief The ICardDatabaseParser defines the base interface for parser sub-classes.
*/
#ifndef CARDDATABASE_PARSER_H
#define CARDDATABASE_PARSER_H
#include "../../card/card_info.h"
#include <QIODevice>
#include <QString>
#define COCKATRICE_XML_XSI_NAMESPACE "http://www.w3.org/2001/XMLSchema-instance"
class ICardDatabaseParser : public QObject
{
Q_OBJECT
public:
~ICardDatabaseParser() override = default;
virtual bool getCanParseFile(const QString &name, QIODevice &device) = 0;
virtual void parseFile(QIODevice &device) = 0;
virtual bool saveToFile(SetNameMap sets,
CardNameMap cards,
const QString &fileName,
const QString &sourceUrl = "unknown",
const QString &sourceVersion = "unknown") = 0;
static void clearSetlist();
protected:
/*
* A cached list of the available sets, needed to cross-reference sets from cards.
* Shared between all parsers
*/
static SetNameMap sets;
CardSetPtr internalAddSet(const QString &setName,
const QString &longName = "",
const QString &setType = "",
const QDate &releaseDate = QDate(),
const CardSet::Priority priority = CardSet::PriorityFallback);
signals:
void addCard(CardInfoPtr card);
void addSet(CardSetPtr set);
};
Q_DECLARE_INTERFACE(ICardDatabaseParser, "ICardDatabaseParser")
#endif

View File

@@ -1,7 +1,7 @@
#include "cockatrice_xml_3.h"
#include "../../relation/card_relation.h"
#include "../../relation/card_relation_type.h"
#include "../../card/card_relation.h"
#include "../../card/card_relation_type.h"
#include <QCoreApplication>
#include <QDebug>
@@ -282,13 +282,9 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
}
properties.insert("colors", colors);
CardInfo::UiAttributes attributes = {.cipt = cipt,
.landscapeOrientation = landscapeOrientation,
.tableRow = tableRow,
.upsideDownArt = upsideDown};
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
reverseRelatedCards, _sets, attributes);
CardInfoPtr newCard =
CardInfo::newInstance(name, text, isToken, properties, relatedCards, reverseRelatedCards, _sets, cipt,
landscapeOrientation, tableRow, upsideDown);
emit addCard(newCard);
}
}
@@ -421,15 +417,14 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
}
// positioning
const CardInfo::UiAttributes &attributes = info->getUiAttributes();
xml.writeTextElement("tablerow", QString::number(attributes.tableRow));
if (attributes.cipt) {
xml.writeTextElement("tablerow", QString::number(info->getTableRow()));
if (info->getCipt()) {
xml.writeTextElement("cipt", "1");
}
if (attributes.landscapeOrientation) {
if (info->getLandscapeOrientation()) {
xml.writeTextElement("landscapeOrientation", "1");
}
if (attributes.upsideDownArt) {
if (info->getUpsideDownArt()) {
xml.writeTextElement("upsidedown", "1");
}

View File

@@ -0,0 +1,38 @@
/**
* @file cockatrice_xml_3.h
* @ingroup CardDatabaseParsers
* @brief The CockatriceXml3Parser is capable of parsing version 3 of the Cockatrice XML Schema.
*/
#ifndef COCKATRICE_XML3_H
#define COCKATRICE_XML3_H
#include "card_database_parser.h"
#include <QLoggingCategory>
#include <QXmlStreamReader>
inline Q_LOGGING_CATEGORY(CockatriceXml3Log, "cockatrice_xml.xml_3_parser");
class CockatriceXml3Parser : public ICardDatabaseParser
{
Q_OBJECT
Q_INTERFACES(ICardDatabaseParser)
public:
CockatriceXml3Parser() = default;
~CockatriceXml3Parser() override = default;
bool getCanParseFile(const QString &name, QIODevice &device) override;
void parseFile(QIODevice &device) override;
bool saveToFile(SetNameMap _sets,
CardNameMap cards,
const QString &fileName,
const QString &sourceUrl = "unknown",
const QString &sourceVersion = "unknown") override;
private:
void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml);
QString getMainCardType(QString &type);
};
#endif

View File

@@ -1,6 +1,7 @@
#include "cockatrice_xml_4.h"
#include "../../relation/card_relation.h"
#include "../../card/card_relation.h"
#include "../../settings/cache_settings.h"
#include <QCoreApplication>
#include <QDebug>
@@ -13,11 +14,6 @@
#define COCKATRICE_XML4_SCHEMALOCATION \
"https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v4/cards.xsd"
CockatriceXml4Parser::CockatriceXml4Parser(ICardPreferenceProvider *_cardPreferenceProvider)
: cardPreferenceProvider(_cardPreferenceProvider)
{
}
bool CockatriceXml4Parser::getCanParseFile(const QString &fileName, QIODevice &device)
{
qCInfo(CockatriceXml4Log) << "Trying to parse: " << fileName;
@@ -136,7 +132,7 @@ QVariantHash CockatriceXml4Parser::loadCardPropertiesFromXml(QXmlStreamReader &x
void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
{
bool includeRebalancedCards = cardPreferenceProvider->getIncludeRebalancedCards();
bool includeRebalancedCards = SettingsCache::instance().getIncludeRebalancedCards();
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
@@ -262,12 +258,9 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
continue;
}
CardInfo::UiAttributes attributes = {.cipt = cipt,
.landscapeOrientation = landscapeOrientation,
.tableRow = tableRow,
.upsideDownArt = upsideDown};
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
reverseRelatedCards, _sets, attributes);
CardInfoPtr newCard =
CardInfo::newInstance(name, text, isToken, properties, relatedCards, reverseRelatedCards, _sets, cipt,
landscapeOrientation, tableRow, upsideDown);
emit addCard(newCard);
}
}
@@ -382,15 +375,14 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
}
// positioning
const CardInfo::UiAttributes &attributes = info->getUiAttributes();
xml.writeTextElement("tablerow", QString::number(attributes.tableRow));
if (attributes.cipt) {
xml.writeTextElement("tablerow", QString::number(info->getTableRow()));
if (info->getCipt()) {
xml.writeTextElement("cipt", "1");
}
if (attributes.landscapeOrientation) {
if (info->getLandscapeOrientation()) {
xml.writeTextElement("landscapeOrientation", "1");
}
if (attributes.upsideDownArt) {
if (info->getUpsideDownArt()) {
xml.writeTextElement("upsidedown", "1");
}

View File

@@ -0,0 +1,38 @@
/**
* @file cockatrice_xml_4.h
* @ingroup CardDatabaseParsers
* @brief The CockatriceXml4Parser is capable of parsing version 4 of the Cockatrice XML Schema.
*/
#ifndef COCKATRICE_XML4_H
#define COCKATRICE_XML4_H
#include "card_database_parser.h"
#include <QLoggingCategory>
#include <QXmlStreamReader>
inline Q_LOGGING_CATEGORY(CockatriceXml4Log, "cockatrice_xml.xml_4_parser");
class CockatriceXml4Parser : public ICardDatabaseParser
{
Q_OBJECT
Q_INTERFACES(ICardDatabaseParser)
public:
CockatriceXml4Parser() = default;
~CockatriceXml4Parser() override = default;
bool getCanParseFile(const QString &name, QIODevice &device) override;
void parseFile(QIODevice &device) override;
bool saveToFile(SetNameMap _sets,
CardNameMap cards,
const QString &fileName,
const QString &sourceUrl = "unknown",
const QString &sourceVersion = "unknown") override;
private:
QVariantHash loadCardPropertiesFromXml(QXmlStreamReader &xml);
void loadCardsFromXml(QXmlStreamReader &xml);
void loadSetsFromXml(QXmlStreamReader &xml);
};
#endif

View File

@@ -1,7 +1,7 @@
#include "custom_line_edit.h"
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../settings/cache_settings.h"
#include "../settings/shortcuts_settings.h"
#include <QKeyEvent>
#include <QLineEdit>

View File

@@ -1,15 +1,26 @@
#include "deck_list_model.h"
#include <libcockatrice/card/database/card_database_manager.h>
#include "../database/card_database_manager.h"
#include "../main.h"
#include "../settings/cache_settings.h"
#include "deck_loader.h"
#include <QBrush>
#include <QFont>
#include <QPrinter>
#include <QProgressDialog>
#include <QTextCursor>
#include <QTextDocument>
#include <QTextStream>
#include <QTextTable>
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 = new DeckLoader;
deckList->setParent(this);
connect(deckList, &DeckLoader::deckLoaded, this, &DeckListModel::rebuildTree);
connect(deckList, &DeckLoader::deckHashChanged, this, &DeckListModel::deckHashChanged);
root = new InnerDecklistNode;
}
@@ -98,95 +109,82 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const
return {};
}
auto *node = static_cast<AbstractDecklistNode *>(index.internalPointer());
auto *card = dynamic_cast<DecklistModelCardNode *>(node);
// Group node
if (!card) {
const auto *group = dynamic_cast<InnerDecklistNode *>(node);
auto *temp = static_cast<AbstractDecklistNode *>(index.internalPointer());
auto *card = dynamic_cast<DecklistModelCardNode *>(temp);
if (card == nullptr) {
const auto *node = dynamic_cast<InnerDecklistNode *>(temp);
switch (role) {
case Qt::FontRole: {
QFont f;
f.setBold(true);
return f;
}
case Qt::DisplayRole:
case Qt::EditRole: {
switch (index.column()) {
case DeckListModelColumns::CARD_AMOUNT:
return group->recursiveCount(true);
case DeckListModelColumns::CARD_NAME:
if (role == Qt::DisplayRole) {
return group->getVisibleName();
}
return group->getName();
case DeckListModelColumns::CARD_SET:
return group->getCardSetShortName();
case DeckListModelColumns::CARD_COLLECTOR_NUMBER:
return group->getCardCollectorNumber();
case DeckListModelColumns::CARD_PROVIDER_ID:
return group->getCardProviderId();
case 0:
return node->recursiveCount(true);
case 1: {
if (role == Qt::DisplayRole)
return node->getVisibleName();
return node->getName();
}
case 2: {
return node->getCardSetShortName();
}
case 3: {
return node->getCardCollectorNumber();
}
case 4: {
return node->getCardProviderId();
}
default:
return {};
}
}
case DeckRoles::IsCardRole:
case Qt::UserRole + 1:
return false;
case DeckRoles::DepthRole:
return group->depth();
// legality does not apply to group nodes
case DeckRoles::IsLegalRole:
return true;
case Qt::BackgroundRole: {
int color = 90 + 60 * node->depth();
return QBrush(QColor(color, 255, color));
}
case Qt::ForegroundRole: {
return QBrush(QColor(0, 0, 0));
}
default:
return {};
}
}
// Card node
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch (index.column()) {
case DeckListModelColumns::CARD_AMOUNT:
return card->getNumber();
case DeckListModelColumns::CARD_NAME:
return card->getName();
case DeckListModelColumns::CARD_SET:
return card->getCardSetShortName();
case DeckListModelColumns::CARD_COLLECTOR_NUMBER:
return card->getCardCollectorNumber();
case DeckListModelColumns::CARD_PROVIDER_ID:
return card->getCardProviderId();
default:
return {};
} else {
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole: {
switch (index.column()) {
case 0:
return card->getNumber();
case 1:
return card->getName();
case 2:
return card->getCardSetShortName();
case 3:
return card->getCardCollectorNumber();
case 4:
return card->getCardProviderId();
default:
return {};
}
}
case DeckRoles::IsCardRole: {
return true;
case Qt::UserRole + 1:
return true;
case Qt::BackgroundRole: {
int color = 255 - (index.row() % 2) * 30;
return QBrush(QColor(color, color, color));
}
case Qt::ForegroundRole: {
return QBrush(QColor(0, 0, 0));
}
default:
return {};
}
case DeckRoles::DepthRole: {
return card->depth();
}
default: {
return {};
}
}
}
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);
}
}
@@ -201,15 +199,15 @@ QVariant DeckListModel::headerData(const int section, const Qt::Orientation orie
}
switch (section) {
case DeckListModelColumns::CARD_AMOUNT:
case 0:
return tr("Count");
case DeckListModelColumns::CARD_NAME:
case 1:
return tr("Card");
case DeckListModelColumns::CARD_SET:
case 2:
return tr("Set");
case DeckListModelColumns::CARD_COLLECTOR_NUMBER:
case 3:
return tr("Number");
case DeckListModelColumns::CARD_PROVIDER_ID:
case 4:
return tr("Provider ID");
default:
return {};
@@ -266,19 +264,19 @@ bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, con
}
switch (index.column()) {
case DeckListModelColumns::CARD_AMOUNT:
case 0:
node->setNumber(value.toInt());
break;
case DeckListModelColumns::CARD_NAME:
case 1:
node->setName(value.toString());
break;
case DeckListModelColumns::CARD_SET:
case 2:
node->setCardSetShortName(value.toString());
break;
case DeckListModelColumns::CARD_COLLECTOR_NUMBER:
case 3:
node->setCardCollectorNumber(value.toString());
break;
case DeckListModelColumns::CARD_PROVIDER_ID:
case 4:
node->setCardProviderId(value.toString());
break;
default:
@@ -287,7 +285,6 @@ bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, con
emitRecursiveUpdates(index);
deckList->refreshDeckHash();
emit deckHashChanged();
emit dataChanged(index, index);
return true;
@@ -426,7 +423,6 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam
cardNode->setCardCollectorNumber(printingInfo.getProperty("num"));
cardNode->setCardProviderId(printingInfo.getProperty("uuid"));
deckList->refreshDeckHash();
emit deckHashChanged();
}
sort(lastKnownColumn, lastKnownOrder);
emitRecursiveUpdates(parentIndex);
@@ -526,7 +522,7 @@ void DeckListModel::sort(int column, Qt::SortOrder order)
emit layoutChanged();
}
void DeckListModel::setActiveGroupCriteria(DeckListModelGroupCriteria::Type newCriteria)
void DeckListModel::setActiveGroupCriteria(DeckListModelGroupCriteria newCriteria)
{
activeGroupCriteria = newCriteria;
rebuildTree();
@@ -534,17 +530,19 @@ void DeckListModel::setActiveGroupCriteria(DeckListModelGroupCriteria::Type newC
void DeckListModel::cleanList()
{
setDeckList(new DeckList);
setDeckList(new DeckLoader);
}
/**
* @param _deck The deck.
* @param _deck The deck. Takes ownership of the object
*/
void DeckListModel::setDeckList(DeckList *_deck)
void DeckListModel::setDeckList(DeckLoader *_deck)
{
if (deckList != _deck) {
deckList = _deck;
}
deckList->deleteLater();
deckList = _deck;
deckList->setParent(this);
connect(deckList, &DeckLoader::deckLoaded, this, &DeckListModel::rebuildTree);
connect(deckList, &DeckLoader::deckHashChanged, this, &DeckListModel::deckHashChanged);
rebuildTree();
}
@@ -632,4 +630,98 @@ QList<QString> *DeckListModel::getZones() const
zones->append(currentZone->getName());
}
return zones;
}
}
void DeckListModel::printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node)
{
const int totalColumns = 2;
if (node->height() == 1) {
QTextBlockFormat blockFormat;
QTextCharFormat charFormat;
charFormat.setFontPointSize(11);
charFormat.setFontWeight(QFont::Bold);
cursor->insertBlock(blockFormat, charFormat);
QTextTableFormat tableFormat;
tableFormat.setCellPadding(0);
tableFormat.setCellSpacing(0);
tableFormat.setBorder(0);
QTextTable *table = cursor->insertTable(node->size() + 1, totalColumns, tableFormat);
for (int i = 0; i < node->size(); i++) {
auto *card = dynamic_cast<AbstractDecklistCardNode *>(node->at(i));
QTextCharFormat cellCharFormat;
cellCharFormat.setFontPointSize(9);
QTextTableCell cell = table->cellAt(i, 0);
cell.setFormat(cellCharFormat);
QTextCursor cellCursor = cell.firstCursorPosition();
cellCursor.insertText(QString("%1 ").arg(card->getNumber()));
cell = table->cellAt(i, 1);
cell.setFormat(cellCharFormat);
cellCursor = cell.firstCursorPosition();
cellCursor.insertText(card->getName());
}
} else if (node->height() == 2) {
QTextBlockFormat blockFormat;
QTextCharFormat charFormat;
charFormat.setFontPointSize(14);
charFormat.setFontWeight(QFont::Bold);
cursor->insertBlock(blockFormat, charFormat);
QTextTableFormat tableFormat;
tableFormat.setCellPadding(10);
tableFormat.setCellSpacing(0);
tableFormat.setBorder(0);
QVector<QTextLength> constraints;
for (int i = 0; i < totalColumns; i++) {
constraints << QTextLength(QTextLength::PercentageLength, 100.0 / totalColumns);
}
tableFormat.setColumnWidthConstraints(constraints);
QTextTable *table = cursor->insertTable(1, totalColumns, tableFormat);
for (int i = 0; i < node->size(); i++) {
QTextCursor cellCursor = table->cellAt(0, (i * totalColumns) / node->size()).lastCursorPosition();
printDeckListNode(&cellCursor, dynamic_cast<InnerDecklistNode *>(node->at(i)));
}
}
cursor->movePosition(QTextCursor::End);
}
void DeckListModel::printDeckList(QPrinter *printer)
{
QTextDocument doc;
QFont font("Serif");
font.setStyleHint(QFont::Serif);
doc.setDefaultFont(font);
QTextCursor cursor(&doc);
QTextBlockFormat headerBlockFormat;
QTextCharFormat headerCharFormat;
headerCharFormat.setFontPointSize(16);
headerCharFormat.setFontWeight(QFont::Bold);
cursor.insertBlock(headerBlockFormat, headerCharFormat);
cursor.insertText(deckList->getName());
headerCharFormat.setFontPointSize(12);
cursor.insertBlock(headerBlockFormat, headerCharFormat);
cursor.insertText(deckList->getComments());
cursor.insertBlock(headerBlockFormat, headerCharFormat);
for (int i = 0; i < root->size(); i++) {
cursor.insertHtml("<br><img src=theme:hr.jpg>");
// cursor.insertHtml("<hr>");
cursor.insertBlock(headerBlockFormat, headerCharFormat);
printDeckListNode(&cursor, dynamic_cast<InnerDecklistNode *>(root->at(i)));
}
doc.print(printer);
}

View File

@@ -1,103 +1,28 @@
#ifndef DECKLISTMODEL_H
#define DECKLISTMODEL_H
#include <../../../../libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_card_node.h>
#include <../../../../libcockatrice_deck_list/libcockatrice/deck_list/tree/deck_list_card_node.h>
#include "../card/exact_card.h"
#include "abstract_deck_list_card_node.h"
#include "deck_list.h"
#include "deck_list_card_node.h"
#include <QAbstractItemModel>
#include <QList>
#include <libcockatrice/card/printing/exact_card.h>
#include <libcockatrice/deck_list/deck_list.h>
class DeckLoader;
class CardDatabase;
class QPrinter;
class QTextCursor;
/**
* @namespace DeckRoles
* @brief Custom model roles used by the DeckListModel for data retrieval.
*
* These roles extend Qt's item data roles starting at Qt::UserRole.
* @brief Specifies the criteria used to group cards in the DeckListModel.
*/
namespace DeckRoles
{
/**
* @enum DeckRoles
* @brief Custom data roles for deck-related model items.
*
* These roles are used to retrieve specialized data from the DeckListModel.
*/
enum
{
IsCardRole = Qt::UserRole + 1, /**< Indicates whether the item represents a card. */
DepthRole, /**< Depth level within the deck's grouping hierarchy. */
IsLegalRole /**< Whether the card is legal in the current deck format. */
};
} // namespace DeckRoles
/**
* @namespace DeckListModelColumns
* @brief Column indices for the DeckListModel.
*
* These values map to the columns in the deck list table representation.
*/
namespace DeckListModelColumns
{
/**
* @enum DeckListModelColumns
* @brief Column identifiers for displaying card information in the deck list.
*/
enum
{
CARD_AMOUNT = 0, /**< The number of copies of the card. */
CARD_NAME = 1, /**< The card's name. */
CARD_SET = 2, /**< The set or expansion the card belongs to. */
CARD_COLLECTOR_NUMBER = 3, /**< Collector number of the card within the set. */
CARD_PROVIDER_ID = 4 /**< ID used by the external data provider (e.g., Scryfall). */
};
} // namespace DeckListModelColumns
/**
* @namespace DeckListModelGroupCriteria
* @brief Specifies criteria used to group cards in the DeckListModel.
*
* These values determine how cards are grouped in UI views such as the deck editor.
*/
namespace DeckListModelGroupCriteria
{
/**
* @enum DeckListModelGroupCriteria
* @brief Available grouping strategies for deck visualization.
*/
enum Type
enum DeckListModelGroupCriteria
{
MAIN_TYPE, /**< Group cards by their main type (e.g., creature, instant). */
MANA_COST, /**< Group cards by their total mana cost. */
MANA_COST, /**< Group cards by their mana cost. */
COLOR /**< Group cards by their color identity. */
};
static inline QString toString(Type t)
{
switch (t) {
case MAIN_TYPE:
return "Main Type";
case MANA_COST:
return "Mana Cost";
case COLOR:
return "Colors";
}
return {};
}
static inline Type fromString(const QString &s)
{
if (s == "Main Type")
return MAIN_TYPE;
if (s == "Mana Cost")
return MANA_COST;
if (s == "Colors")
return COLOR;
return MAIN_TYPE; // default
}
} // namespace DeckListModelGroupCriteria
/**
* @class DecklistModelCardNode
@@ -124,7 +49,7 @@ public:
: AbstractDecklistCardNode(_parent, position), dataNode(_dataNode)
{
}
[[nodiscard]] int getNumber() const override
int getNumber() const override
{
return dataNode->getNumber();
}
@@ -132,7 +57,7 @@ public:
{
dataNode->setNumber(_number);
}
[[nodiscard]] QString getName() const override
QString getName() const override
{
return dataNode->getName();
}
@@ -140,7 +65,7 @@ public:
{
dataNode->setName(_name);
}
[[nodiscard]] QString getCardProviderId() const override
QString getCardProviderId() const override
{
return dataNode->getCardProviderId();
}
@@ -148,7 +73,7 @@ public:
{
dataNode->setCardProviderId(_cardProviderId);
}
[[nodiscard]] QString getCardSetShortName() const override
QString getCardSetShortName() const override
{
return dataNode->getCardSetShortName();
}
@@ -156,7 +81,7 @@ public:
{
dataNode->setCardSetShortName(_cardSetShortName);
}
[[nodiscard]] QString getCardCollectorNumber() const override
QString getCardCollectorNumber() const override
{
return dataNode->getCardCollectorNumber();
}
@@ -169,7 +94,7 @@ public:
* @brief Returns the underlying data node.
* @return Pointer to the DecklistCardNode wrapped by this node.
*/
[[nodiscard]] DecklistCardNode *getDataNode() const
DecklistCardNode *getDataNode() const
{
return dataNode;
}
@@ -195,12 +120,12 @@ public:
*
* Slots:
* - rebuildTree(): rebuilds the model structure from the underlying DeckLoader.
* - printDeckList(): renders the decklist to a QPrinter.
*/
class DeckListModel : public QAbstractItemModel
{
Q_OBJECT
public slots:
private slots:
/**
* @brief Rebuilds the model tree from the underlying DeckLoader.
*
@@ -209,6 +134,13 @@ public slots:
*/
void rebuildTree();
public slots:
/**
* @brief Prints the decklist to the provided QPrinter.
* @param printer The printer to render the decklist to.
*/
void printDeckList(QPrinter *printer);
signals:
/**
* @brief Emitted whenever the deck hash changes due to modifications in the model.
@@ -223,27 +155,26 @@ public:
* @brief Returns the root index of the model.
* @return QModelIndex representing the root node.
*/
[[nodiscard]] QModelIndex getRoot() const
QModelIndex getRoot() const
{
return nodeToIndex(root);
}
};
/**
* @brief Returns the value of the grouping category for a card based on the current criteria.
* @param info Pointer to card information.
* @return String representing the value of the current grouping criteria for the card.
*/
[[nodiscard]] QString getGroupCriteriaForCard(CardInfoPtr info) const;
QString getGroupCriteriaForCard(CardInfoPtr info) const;
// Qt model overrides
[[nodiscard]] int rowCount(const QModelIndex &parent) const override;
[[nodiscard]] int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override;
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
void emitBackgroundUpdates(const QModelIndex &parent);
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
[[nodiscard]] QModelIndex index(int row, int column, const QModelIndex &parent) const override;
[[nodiscard]] QModelIndex parent(const QModelIndex &index) const override;
[[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &index) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
bool removeRows(int row, int count, const QModelIndex &parent) override;
@@ -255,10 +186,10 @@ public:
* @param cardNumber Optional collector number.
* @return QModelIndex of the card, or invalid index if not found.
*/
[[nodiscard]] QModelIndex findCard(const QString &cardName,
const QString &zoneName,
const QString &providerId = "",
const QString &cardNumber = "") const;
QModelIndex findCard(const QString &cardName,
const QString &zoneName,
const QString &providerId = "",
const QString &cardNumber = "") const;
/**
* @brief Adds a card using the preferred printing if available.
@@ -292,38 +223,40 @@ public:
* @brief Removes all cards and resets the model.
*/
void cleanList();
[[nodiscard]] DeckList *getDeckList() const
DeckLoader *getDeckList() const
{
return deckList;
}
void setDeckList(DeckList *_deck);
void setDeckList(DeckLoader *_deck);
[[nodiscard]] QList<ExactCard> getCards() const;
[[nodiscard]] QList<ExactCard> getCardsForZone(const QString &zoneName) const;
[[nodiscard]] QList<QString> *getZones() const;
QList<ExactCard> getCards() const;
QList<ExactCard> getCardsForZone(const QString &zoneName) const;
QList<QString> *getZones() const;
/**
* @brief Sets the criteria used to group cards in the model.
* @param newCriteria The new grouping criteria.
*/
void setActiveGroupCriteria(DeckListModelGroupCriteria::Type newCriteria);
void setActiveGroupCriteria(DeckListModelGroupCriteria newCriteria);
private:
DeckList *deckList; /**< Pointer to the deck loader providing the underlying data. */
DeckLoader *deckList; /**< Pointer to the deck loader providing the underlying data. */
InnerDecklistNode *root; /**< Root node of the model tree. */
DeckListModelGroupCriteria::Type activeGroupCriteria = DeckListModelGroupCriteria::MAIN_TYPE;
DeckListModelGroupCriteria activeGroupCriteria = DeckListModelGroupCriteria::MAIN_TYPE;
int lastKnownColumn; /**< Last column used for sorting. */
Qt::SortOrder lastKnownOrder; /**< Last known sort order. */
InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent);
QModelIndex nodeToIndex(AbstractDecklistNode *node) const;
[[nodiscard]] DecklistModelCardNode *findCardNode(const QString &cardName,
const QString &zoneName,
const QString &providerId = "",
const QString &cardNumber = "") const;
DecklistModelCardNode *findCardNode(const QString &cardName,
const QString &zoneName,
const QString &providerId = "",
const QString &cardNumber = "") const;
void emitRecursiveUpdates(const QModelIndex &index);
void sortHelper(InnerDecklistNode *node, Qt::SortOrder order);
void printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node);
template <typename T> T getNode(const QModelIndex &index) const
{
if (!index.isValid())

View File

@@ -1,5 +1,11 @@
#include "deck_loader.h"
#include "../database/card_database.h"
#include "../database/card_database_manager.h"
#include "../main.h"
#include "deck_list.h"
#include "deck_list_card_node.h"
#include <QApplication>
#include <QClipboard>
#include <QDebug>
@@ -7,29 +13,32 @@
#include <QFile>
#include <QFileInfo>
#include <QFutureWatcher>
#include <QPrinter>
#include <QRegularExpression>
#include <QStringList>
#include <QTextCursor>
#include <QTextDocument>
#include <QTextStream>
#include <QTextTable>
#include <QtConcurrentRun>
#include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
const QStringList DeckLoader::ACCEPTED_FILE_EXTENSIONS = {"*.cod", "*.dec", "*.dek", "*.txt", "*.mwDeck"};
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() : DeckList(), lastFileName(QString()), lastFileFormat(CockatriceFormat), lastRemoteDeckId(-1)
{
}
DeckLoader::DeckLoader(QObject *parent, DeckList *_deckList) : QObject(parent), deckList(_deckList)
DeckLoader::DeckLoader(const QString &nativeString)
: DeckList(nativeString), lastFileName(QString()), lastFileFormat(CockatriceFormat), lastRemoteDeckId(-1)
{
}
DeckLoader::DeckLoader(const DeckList &other)
: DeckList(other), lastFileName(QString()), lastFileFormat(CockatriceFormat), lastRemoteDeckId(-1)
{
}
DeckLoader::DeckLoader(const DeckLoader &other)
: DeckList(other), lastFileName(other.lastFileName), lastFileFormat(other.lastFileFormat),
lastRemoteDeckId(other.lastRemoteDeckId)
{
}
@@ -43,15 +52,15 @@ bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt, bool user
bool result = false;
switch (fmt) {
case PlainTextFormat:
result = deckList->loadFromFile_Plain(&file);
result = loadFromFile_Plain(&file);
break;
case CockatriceFormat: {
result = deckList->loadFromFile_Native(&file);
result = 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 = loadFromFile_Plain(&file);
fmt = PlainTextFormat;
}
break;
@@ -62,10 +71,8 @@ bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt, bool user
}
if (result) {
lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
lastFileName = fileName;
lastFileFormat = fmt;
if (userRequest) {
updateLastLoadedTimestamp(fileName, fmt);
}
@@ -86,10 +93,8 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, FileFormat fmt, bool
watcher->deleteLater();
if (result) {
lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
lastFileName = fileName;
lastFileFormat = fmt;
if (userRequest) {
updateLastLoadedTimestamp(fileName, fmt);
}
@@ -107,13 +112,13 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, FileFormat fmt, bool
switch (fmt) {
case PlainTextFormat:
return deckList->loadFromFile_Plain(&file);
return loadFromFile_Plain(&file);
case CockatriceFormat: {
bool result = false;
result = deckList->loadFromFile_Native(&file);
result = loadFromFile_Native(&file);
if (!result) {
file.seek(0);
return deckList->loadFromFile_Plain(&file);
return loadFromFile_Plain(&file);
}
return result;
}
@@ -129,11 +134,11 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, FileFormat fmt, bool
bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId)
{
bool result = deckList->loadFromString_Native(nativeString);
bool result = loadFromString_Native(nativeString);
if (result) {
lastLoadInfo = {
.remoteDeckId = remoteDeckId,
};
lastFileName = QString();
lastFileFormat = CockatriceFormat;
lastRemoteDeckId = remoteDeckId;
emit deckLoaded();
}
@@ -150,20 +155,16 @@ bool DeckLoader::saveToFile(const QString &fileName, FileFormat fmt)
bool result = false;
switch (fmt) {
case PlainTextFormat:
result = deckList->saveToFile_Plain(&file);
result = saveToFile_Plain(&file);
break;
case CockatriceFormat:
result = deckList->saveToFile_Native(&file);
qCInfo(DeckLoaderLog) << "Saving to " << fileName << "-" << result;
result = saveToFile_Native(&file);
break;
}
if (result) {
lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
qCInfo(DeckLoaderLog) << "Deck was saved -" << result;
lastFileName = fileName;
lastFileFormat = fmt;
}
file.flush();
@@ -194,21 +195,19 @@ bool DeckLoader::updateLastLoadedTimestamp(const QString &fileName, FileFormat f
// Perform file modifications
switch (fmt) {
case PlainTextFormat:
result = deckList->saveToFile_Plain(&file);
result = saveToFile_Plain(&file);
break;
case CockatriceFormat:
deckList->setLastLoadedTimestamp(QDateTime::currentDateTime().toString());
result = deckList->saveToFile_Native(&file);
setLastLoadedTimestamp(QDateTime::currentDateTime().toString());
result = saveToFile_Native(&file);
break;
}
file.close(); // Close the file to ensure changes are flushed
if (result) {
lastLoadInfo = {
.fileName = fileName,
.fileFormat = fmt,
};
lastFileName = fileName;
lastFileFormat = fmt;
// Re-open the file and set the original timestamp
if (!file.open(QIODevice::ReadWrite)) {
@@ -271,11 +270,9 @@ static QString toDecklistExportString(const DecklistCardNode *card)
/**
* Export deck to decklist function, called to format the deck in a way to be sent to a server
*
* @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(DecklistWebsite website)
{
// Add the base url
QString deckString = "https://" + getDomainForWebsite(website) + "/?";
@@ -301,7 +298,7 @@ QString DeckLoader::exportDeckToDecklist(const DeckList *deckList, DecklistWebsi
};
// call our struct function for each card in the deck
deckList->forEachCard(formatDeckListForExport);
forEachCard(formatDeckListForExport);
// Remove the extra return at the end of the last cards
mainBoardCards.chop(3);
sideBoardCards.chop(3);
@@ -342,24 +339,20 @@ struct SetProviderIdToPreferred
/**
* This function iterates through each card in the decklist and sets the providerId
* on each card based on its set name and collector number.
*
* @param deckList The decklist to modify
*/
void DeckLoader::setProviderIdToPreferredPrinting(const DeckList *deckList)
void DeckLoader::setProviderIdToPreferredPrinting()
{
// Set up the struct to call.
SetProviderIdToPreferred setProviderIdToPreferred;
// Call the forEachCard method for each card in the deck
deckList->forEachCard(setProviderIdToPreferred);
forEachCard(setProviderIdToPreferred);
}
/**
* Sets the providerId on each card in the decklist based on its set name and collector number.
*
* @param deckList The decklist to modify
*/
void DeckLoader::resolveSetNameAndNumberToProviderID(const DeckList *deckList)
void DeckLoader::resolveSetNameAndNumberToProviderID()
{
auto setProviderId = [](const auto node, const auto card) {
Q_UNUSED(node);
@@ -374,7 +367,7 @@ void DeckLoader::resolveSetNameAndNumberToProviderID(const DeckList *deckList)
card->setCardProviderId(providerId);
};
deckList->forEachCard(setProviderId);
forEachCard(setProviderId);
}
// This struct is here to support the forEachCard function call, defined in decklist.
@@ -398,10 +391,8 @@ struct ClearSetNameNumberAndProviderId
/**
* Clears the set name and numbers on each card in the decklist.
*
* @param deckList The decklist to modify
*/
void DeckLoader::clearSetNamesAndNumbers(const DeckList *deckList)
void DeckLoader::clearSetNamesAndNumbers()
{
auto clearSetNameAndNumber = [](const auto node, auto card) {
Q_UNUSED(node)
@@ -411,7 +402,7 @@ void DeckLoader::clearSetNamesAndNumbers(const DeckList *deckList)
card->setCardProviderId(nullptr);
};
deckList->forEachCard(clearSetNameAndNumber);
forEachCard(clearSetNameAndNumber);
}
DeckLoader::FileFormat DeckLoader::getFormatFromName(const QString &fileName)
@@ -422,27 +413,24 @@ DeckLoader::FileFormat DeckLoader::getFormatFromName(const QString &fileName)
return PlainTextFormat;
}
void DeckLoader::saveToClipboard(const DeckList *deckList, bool addComments, bool addSetNameAndNumber)
void DeckLoader::saveToClipboard(bool addComments, bool addSetNameAndNumber) const
{
QString buffer;
QTextStream stream(&buffer);
saveToStream_Plain(stream, deckList, addComments, addSetNameAndNumber);
saveToStream_Plain(stream, addComments, addSetNameAndNumber);
QApplication::clipboard()->setText(buffer, QClipboard::Clipboard);
QApplication::clipboard()->setText(buffer, QClipboard::Selection);
}
bool DeckLoader::saveToStream_Plain(QTextStream &out,
const DeckList *deckList,
bool addComments,
bool addSetNameAndNumber)
bool DeckLoader::saveToStream_Plain(QTextStream &out, bool addComments, bool addSetNameAndNumber) const
{
if (addComments) {
saveToStream_DeckHeader(out, deckList);
saveToStream_DeckHeader(out);
}
// loop zones
for (int i = 0; i < deckList->getRoot()->size(); i++) {
const auto *zoneNode = dynamic_cast<InnerDecklistNode *>(deckList->getRoot()->at(i));
for (int i = 0; i < getRoot()->size(); i++) {
const auto *zoneNode = dynamic_cast<InnerDecklistNode *>(getRoot()->at(i));
saveToStream_DeckZone(out, zoneNode, addComments, addSetNameAndNumber);
@@ -453,14 +441,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
{
if (!deckList->getName().isEmpty()) {
out << "// " << deckList->getName() << "\n\n";
if (!getName().isEmpty()) {
out << "// " << getName() << "\n\n";
}
if (!deckList->getComments().isEmpty()) {
QStringList commentRows = deckList->getComments().split(QRegularExpression("\n|\r\n|\r"));
if (!getComments().isEmpty()) {
QStringList commentRows = getComments().split(QRegularExpression("\n|\r\n|\r"));
for (const QString &row : commentRows) {
out << "// " << row << "\n";
}
@@ -471,7 +459,7 @@ void DeckLoader::saveToStream_DeckHeader(QTextStream &out, const DeckList *deckL
void DeckLoader::saveToStream_DeckZone(QTextStream &out,
const InnerDecklistNode *zoneNode,
bool addComments,
bool addSetNameAndNumber)
bool addSetNameAndNumber) const
{
// group cards by card type and count the subtotals
QMultiMap<QString, DecklistCardNode *> cardsByType;
@@ -519,7 +507,7 @@ void DeckLoader::saveToStream_DeckZoneCards(QTextStream &out,
const InnerDecklistNode *zoneNode,
QList<DecklistCardNode *> cards,
bool addComments,
bool addSetNameAndNumber)
bool addSetNameAndNumber) const
{
// QMultiMap sorts values in reverse order
for (int i = cards.size() - 1; i >= 0; --i) {
@@ -567,7 +555,7 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName)
switch (getFormatFromName(fileName)) {
case PlainTextFormat:
// Save in Cockatrice's native format
result = deckList->saveToFile_Native(&file);
result = saveToFile_Native(&file);
break;
case CockatriceFormat:
qCInfo(DeckLoaderLog) << "File is already in Cockatrice format. No conversion needed.";
@@ -588,16 +576,14 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName)
} else {
qCInfo(DeckLoaderLog) << "Original file deleted successfully:" << fileName;
}
lastLoadInfo = {
.fileName = newFileName,
.fileFormat = CockatriceFormat,
};
lastFileName = newFileName;
lastFileFormat = CockatriceFormat;
}
return result;
}
QString DeckLoader::getCardZoneFromName(const QString &cardName, QString currentZoneName)
QString DeckLoader::getCardZoneFromName(QString cardName, QString currentZoneName)
{
CardInfoPtr card = CardDatabaseManager::query()->getCardInfo(cardName);
@@ -608,7 +594,7 @@ QString DeckLoader::getCardZoneFromName(const QString &cardName, QString current
return currentZoneName;
}
QString DeckLoader::getCompleteCardName(const QString &cardName)
QString DeckLoader::getCompleteCardName(const QString &cardName) const
{
if (CardDatabaseManager::getInstance()) {
ExactCard temp = CardDatabaseManager::query()->guessCard({cardName});
@@ -619,97 +605,3 @@ QString DeckLoader::getCompleteCardName(const QString &cardName)
return cardName;
}
void DeckLoader::printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node)
{
const int totalColumns = 2;
if (node->height() == 1) {
QTextBlockFormat blockFormat;
QTextCharFormat charFormat;
charFormat.setFontPointSize(11);
charFormat.setFontWeight(QFont::Bold);
cursor->insertBlock(blockFormat, charFormat);
QTextTableFormat tableFormat;
tableFormat.setCellPadding(0);
tableFormat.setCellSpacing(0);
tableFormat.setBorder(0);
QTextTable *table = cursor->insertTable(node->size() + 1, totalColumns, tableFormat);
for (int i = 0; i < node->size(); i++) {
auto *card = dynamic_cast<AbstractDecklistCardNode *>(node->at(i));
QTextCharFormat cellCharFormat;
cellCharFormat.setFontPointSize(9);
QTextTableCell cell = table->cellAt(i, 0);
cell.setFormat(cellCharFormat);
QTextCursor cellCursor = cell.firstCursorPosition();
cellCursor.insertText(QString("%1 ").arg(card->getNumber()));
cell = table->cellAt(i, 1);
cell.setFormat(cellCharFormat);
cellCursor = cell.firstCursorPosition();
cellCursor.insertText(card->getName());
}
} else if (node->height() == 2) {
QTextBlockFormat blockFormat;
QTextCharFormat charFormat;
charFormat.setFontPointSize(14);
charFormat.setFontWeight(QFont::Bold);
cursor->insertBlock(blockFormat, charFormat);
QTextTableFormat tableFormat;
tableFormat.setCellPadding(10);
tableFormat.setCellSpacing(0);
tableFormat.setBorder(0);
QVector<QTextLength> constraints;
for (int i = 0; i < totalColumns; i++) {
constraints << QTextLength(QTextLength::PercentageLength, 100.0 / totalColumns);
}
tableFormat.setColumnWidthConstraints(constraints);
QTextTable *table = cursor->insertTable(1, totalColumns, tableFormat);
for (int i = 0; i < node->size(); i++) {
QTextCursor cellCursor = table->cellAt(0, (i * totalColumns) / node->size()).lastCursorPosition();
printDeckListNode(&cellCursor, dynamic_cast<InnerDecklistNode *>(node->at(i)));
}
}
cursor->movePosition(QTextCursor::End);
}
void DeckLoader::printDeckList(QPrinter *printer, const DeckList *deckList)
{
QTextDocument doc;
QFont font("Serif");
font.setStyleHint(QFont::Serif);
doc.setDefaultFont(font);
QTextCursor cursor(&doc);
QTextBlockFormat headerBlockFormat;
QTextCharFormat headerCharFormat;
headerCharFormat.setFontPointSize(16);
headerCharFormat.setFontWeight(QFont::Bold);
cursor.insertBlock(headerBlockFormat, headerCharFormat);
cursor.insertText(deckList->getName());
headerCharFormat.setFontPointSize(12);
cursor.insertBlock(headerBlockFormat, headerCharFormat);
cursor.insertText(deckList->getComments());
cursor.insertBlock(headerBlockFormat, headerCharFormat);
for (int i = 0; i < deckList->getRoot()->size(); i++) {
cursor.insertHtml("<br><img src=theme:hr.jpg>");
// cursor.insertHtml("<hr>");
cursor.insertBlock(headerBlockFormat, headerCharFormat);
printDeckListNode(&cursor, dynamic_cast<InnerDecklistNode *>(deckList->getRoot()->at(i)));
}
doc.print(printer);
}

View File

@@ -0,0 +1,112 @@
/**
* @file deck_loader.h
* @ingroup ImportExport
* @brief TODO: Document this.
*/
#ifndef DECK_LOADER_H
#define DECK_LOADER_H
#include "deck_list.h"
#include <QLoggingCategory>
inline Q_LOGGING_CATEGORY(DeckLoaderLog, "deck_loader")
class DeckLoader : public DeckList
{
Q_OBJECT
signals:
void deckLoaded();
void loadFinished(bool success);
public:
enum FileFormat
{
PlainTextFormat,
CockatriceFormat
};
/**
* Supported file extensions for decklist files
*/
static const QStringList ACCEPTED_FILE_EXTENSIONS;
/**
* For use with `QFileDialog::setNameFilters`
*/
static const QStringList FILE_NAME_FILTERS;
enum DecklistWebsite
{
DecklistOrg,
DecklistXyz
};
private:
QString lastFileName;
FileFormat lastFileFormat;
int lastRemoteDeckId;
public:
DeckLoader();
explicit DeckLoader(const QString &nativeString);
explicit DeckLoader(const DeckList &other);
DeckLoader(const DeckLoader &other);
const QString &getLastFileName() const
{
return lastFileName;
}
void setLastFileName(const QString &_lastFileName)
{
lastFileName = _lastFileName;
}
FileFormat getLastFileFormat() const
{
return lastFileFormat;
}
int getLastRemoteDeckId() const
{
return lastRemoteDeckId;
}
bool hasNotBeenLoaded() const
{
return getLastFileName().isEmpty() && getLastRemoteDeckId() == -1;
}
void clearSetNamesAndNumbers();
static FileFormat getFormatFromName(const QString &fileName);
bool loadFromFile(const QString &fileName, FileFormat fmt, bool userRequest = false);
bool loadFromFileAsync(const QString &fileName, FileFormat fmt, bool userRequest);
bool loadFromRemote(const QString &nativeString, int remoteDeckId);
bool saveToFile(const QString &fileName, FileFormat fmt);
bool updateLastLoadedTimestamp(const QString &fileName, FileFormat fmt);
QString exportDeckToDecklist(DecklistWebsite website);
void setProviderIdToPreferredPrinting();
void resolveSetNameAndNumberToProviderID();
void saveToClipboard(bool addComments = true, bool addSetNameAndNumber = true) const;
// overload
bool saveToStream_Plain(QTextStream &out, bool addComments = true, bool addSetNameAndNumber = true) const;
bool convertToCockatriceFormat(QString fileName);
protected:
void saveToStream_DeckHeader(QTextStream &out) const;
void saveToStream_DeckZone(QTextStream &out,
const InnerDecklistNode *zoneNode,
bool addComments = true,
bool addSetNameAndNumber = true) const;
void saveToStream_DeckZoneCards(QTextStream &out,
const InnerDecklistNode *zoneNode,
QList<DecklistCardNode *> cards,
bool addComments = true,
bool addSetNameAndNumber = true) const;
[[nodiscard]] QString getCardZoneFromName(QString cardName, QString currentZoneName) override;
[[nodiscard]] QString getCompleteCardName(const QString &cardName) const override;
};
#endif

View File

@@ -1,13 +1,15 @@
#include "deck_stats_interface.h"
#include "deck_list.h"
#include "deck_list_card_node.h"
#include <QDesktopServices>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QRegularExpression>
#include <QUrlQuery>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent)
: QObject(parent), cardDatabase(_cardDatabase)

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