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 \ RUN dnf install -y \
ccache \ 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 # --debug or --release sets the build type ie CMAKE_BUILD_TYPE
# --ccache [<size>] uses ccache and shows stats, optionally provide size # --ccache [<size>] uses ccache and shows stats, optionally provide size
# --dir <dir> sets the name of the build dir, default is "build" # --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_TEST USE_CCACHE CCACHE_SIZE BUILD_DIR CMAKE_GENERATOR
# 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
# (correspond to args: --debug/--release --install --package <package type> --suffix <suffix> --server --test --ccache <ccache_size> --dir <dir>) # (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 # exitcode: 1 for failure, 3 for invalid arguments
@@ -47,10 +46,6 @@ while [[ $# != 0 ]]; do
MAKE_SERVER=1 MAKE_SERVER=1
shift shift
;; ;;
'--no-client')
MAKE_NO_CLIENT=1
shift
;;
'--test') '--test')
MAKE_TEST=1 MAKE_TEST=1
shift shift
@@ -71,10 +66,6 @@ while [[ $# != 0 ]]; do
shift shift
fi fi
;; ;;
'--vcpkg')
USE_VCPKG=1
shift
;;
'--dir') '--dir')
shift shift
if [[ $# == 0 ]]; then if [[ $# == 0 ]]; then
@@ -84,15 +75,6 @@ while [[ $# != 0 ]]; do
BUILD_DIR="$1" BUILD_DIR="$1"
shift 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" echo "::error file=$0::unrecognized option: $1"
exit 3 exit 3
@@ -121,9 +103,6 @@ flags=("-DCMAKE_BUILD_TYPE=$BUILDTYPE")
if [[ $MAKE_SERVER ]]; then if [[ $MAKE_SERVER ]]; then
flags+=("-DWITH_SERVER=1") flags+=("-DWITH_SERVER=1")
fi fi
if [[ $MAKE_NO_CLIENT ]]; then
flags+=("-DWITH_CLIENT=0" "-DWITH_ORACLE=0" "-DWITH_DBCONVERTER=0")
fi
if [[ $MAKE_TEST ]]; then if [[ $MAKE_TEST ]]; then
flags+=("-DTEST=1") flags+=("-DTEST=1")
fi fi
@@ -137,9 +116,6 @@ fi
if [[ $PACKAGE_TYPE ]]; then if [[ $PACKAGE_TYPE ]]; then
flags+=("-DCPACK_GENERATOR=$PACKAGE_TYPE") flags+=("-DCPACK_GENERATOR=$PACKAGE_TYPE")
fi fi
if [[ $USE_VCPKG ]]; then
flags+=("-DUSE_VCPKG=1")
fi
# Add cmake --build flags # Add cmake --build flags
buildflags=(--config "$BUILDTYPE") buildflags=(--config "$BUILDTYPE")
@@ -156,35 +132,9 @@ function ccachestatsverbose() {
# Compile # Compile
if [[ $RUNNER_OS == macOS ]]; then 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" echo "::group::Signing Certificate"
if [[ -n "$MACOS_CERTIFICATE_NAME" ]]; then 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 create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
security default-keychain -s build.keychain security default-keychain -s build.keychain
security set-keychain-settings -t 3600 -l 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." echo "No signing certificate configured. Skipping set up of keychain in macOS environment."
fi fi
echo "::endgroup::" 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 fi
if [[ $USE_CCACHE ]]; then if [[ $USE_CCACHE ]]; then
@@ -229,13 +156,17 @@ fi
echo "::group::Configure cmake" echo "::group::Configure cmake"
cmake --version cmake --version
echo "Running cmake with flags: ${flags[*]}"
cmake .. "${flags[@]}" cmake .. "${flags[@]}"
echo "::endgroup::" echo "::endgroup::"
echo "::group::Build project" echo "::group::Build project"
echo "Running cmake --build with flags: ${buildflags[*]}" if [[ $RUNNER_OS == Windows ]]; then
cmake --build . "${buildflags[@]}" # 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::" echo "::endgroup::"
if [[ $USE_CCACHE ]]; then if [[ $USE_CCACHE ]]; then
@@ -244,19 +175,6 @@ if [[ $USE_CCACHE ]]; then
echo "::endgroup::" echo "::endgroup::"
fi 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 if [[ $MAKE_TEST ]]; then
echo "::group::Run tests" echo "::group::Run tests"
ctest -C "$BUILDTYPE" --output-on-failure ctest -C "$BUILDTYPE" --output-on-failure
@@ -271,6 +189,12 @@ fi
if [[ $MAKE_PACKAGE ]]; then if [[ $MAKE_PACKAGE ]]; then
echo "::group::Create package" 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" cmake --build . --target package --config "$BUILDTYPE"
echo "::endgroup::" echo "::endgroup::"

View File

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

View File

@@ -11,19 +11,11 @@ if ! git merge-base origin/master HEAD; then
fi fi
# Check formatting using format.sh # 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=$? err=$?
sep="
----------
"
used_version="${diff%%"$sep"*}"
diff="${diff#*"$sep"}"
changes_to_make="${diff%%"$sep"*}"
files_to_edit="${diff#*"$sep"}"
case $err in case $err in
1) 1)
cat <<EOM cat <<EOM
@@ -41,13 +33,14 @@ case $err in
*********************************************************** ***********************************************************
Used version: Used version:
$used_version ${diff%%
----------
Affected files: *}
$files_to_edit
The following changes should be made: The following changes should be made:
$changes_to_make ${diff#*
----------
}
Exiting... Exiting...
EOM EOM
@@ -65,9 +58,6 @@ EOM
*** *** *** ***
*********************************************************** ***********************************************************
Used version:
$used_version
Exiting... Exiting...
EOM EOM
exit 0 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 13</kbd> <sub><i>Trixie</i></sub>
• <kbd>Debian 12</kbd> <sub><i>Bookworm</i></sub> • <kbd>Debian 12</kbd> <sub><i>Bookworm</i></sub>
• <kbd>Debian 11</kbd> <sub><i>Bullseye</i></sub> • <kbd>Debian 11</kbd> <sub><i>Bullseye</i></sub>
• <kbd>Fedora 43</kbd>
• <kbd>Fedora 42</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>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> <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 PointerAlignment: Right
SortIncludes: true SortIncludes: true
IncludeBlocks: Regroup IncludeBlocks: Regroup
StatementAttributeLikeMacros: [emit]
# requires clang-format 16
# RemoveSemicolon: true
--- ---
Language: Proto Language: Proto
AllowShortFunctionsOnASingleLine: None AllowShortFunctionsOnASingleLine: None

View File

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

View File

@@ -4,39 +4,25 @@ permissions:
contents: write contents: write
id-token: write id-token: write
attestations: write attestations: write
actions: write # needed for ccache action to be able to delete gha caches
on: on:
push: push:
branches: branches:
- master - master
paths: paths-ignore:
- '*/**' # matches all files not in root - '**.md'
- '!**.md' - 'webclient/**'
- '!.github/**' - '.github/workflows/web-*.yml'
- '!.husky/**' - '.github/workflows/translations-*.yml'
- '!.tx/**' - '.github/workflows/docker-release.yml'
- '!doc/**'
- '!webclient/**'
- '.github/workflows/desktop-build.yml'
- 'CMakeLists.txt'
- 'vcpkg.json'
- 'vcpkg'
tags: tags:
- '*' - '*'
pull_request: pull_request:
paths: paths-ignore:
- '*/**' # matches all files not in root - '**.md'
- '!**.md' - 'webclient/**'
- '!.github/**' - '.github/workflows/web-*.yml'
- '!.husky/**' - '.github/workflows/translations-*.yml'
- '!.tx/**'
- '!doc/**'
- '!webclient/**'
- '.github/workflows/desktop-build.yml'
- 'CMakeLists.txt'
- 'vcpkg.json'
- 'vcpkg'
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on master) # Cancel earlier, unfinished runs of this workflow on the same branch (unless on master)
concurrency: concurrency:
@@ -70,7 +56,7 @@ jobs:
- name: Checkout - name: Checkout
if: steps.configure.outputs.tag != null if: steps.configure.outputs.tag != null
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -114,12 +100,7 @@ jobs:
- distro: Debian - distro: Debian
version: 11 version: 11
package: DEB package: DEB
test: skip # Running tests on all distros is superfluous
- distro: Servatrice_Debian
version: 11
package: DEB
test: skip
server_only: yes
- distro: Debian - distro: Debian
version: 12 version: 12
@@ -131,12 +112,12 @@ jobs:
package: DEB package: DEB
- distro: Fedora - distro: Fedora
version: 42 version: 41
package: RPM package: RPM
test: skip # Running tests on all distros is superfluous test: skip # Running tests on all distros is superfluous
- distro: Fedora - distro: Fedora
version: 43 version: 42
package: RPM package: RPM
- distro: Ubuntu - distro: Ubuntu
@@ -162,7 +143,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Restore compiler cache (ccache) - name: Restore compiler cache (ccache)
id: ccache_restore id: ccache_restore
@@ -196,11 +177,10 @@ jobs:
SUFFIX: '-${{matrix.distro}}${{matrix.version}}' SUFFIX: '-${{matrix.distro}}${{matrix.version}}'
package: '${{matrix.package}}' package: '${{matrix.package}}'
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}' CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
NO_CLIENT: ${{matrix.server_only == 'yes' && '--no-client' || '' }}
run: | run: |
source .ci/docker.sh source .ci/docker.sh
RUN --server --release --package "$package" --dir "$BUILD_DIR" \ RUN --server --release --package "$package" --dir "$BUILD_DIR" \
--ccache "$CCACHE_SIZE" $NO_CLIENT --ccache "$CCACHE_SIZE"
.ci/name_build.sh .ci/name_build.sh
- name: Save compiler cache (ccache) - name: Save compiler cache (ccache)
@@ -213,7 +193,7 @@ jobs:
- name: Upload artifact - name: Upload artifact
id: upload_artifact id: upload_artifact
if: matrix.package != 'skip' if: matrix.package != 'skip'
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v4
with: with:
name: ${{matrix.distro}}${{matrix.version}}-package name: ${{matrix.distro}}${{matrix.version}}-package
path: ${{steps.build.outputs.path}} path: ${{steps.build.outputs.path}}
@@ -245,135 +225,78 @@ jobs:
GH_TOKEN: ${{github.token}} GH_TOKEN: ${{github.token}}
run: gh attestation verify ${{steps.build.outputs.path}} -R Cockatrice/Cockatrice run: gh attestation verify ${{steps.build.outputs.path}} -R Cockatrice/Cockatrice
build-vcpkg: build-macos:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: macOS - target: 13
target: 13
runner: macos-15-intel
soc: Intel soc: Intel
xcode: "16.4" os: macos-13
xcode: "14.3.1"
type: Release type: Release
override_target: 13
make_package: 1 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
target: 14
runner: macos-14
soc: Apple soc: Apple
os: macos-14
xcode: "15.4" xcode: "15.4"
type: Release type: Release
make_package: 1 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
target: 15
runner: macos-15
soc: Apple soc: Apple
xcode: "16.4" os: macos-15
xcode: "16.2"
type: Release type: Release
make_package: 1 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
target: 15
runner: macos-15
soc: Apple soc: Apple
xcode: "16.4" os: macos-15
xcode: "16.2"
type: Debug 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 name: macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
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' || '' }}
needs: configure 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: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
submodules: recursive 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 # 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 # It tries to download a binary of ccache from GitHub Release and falls back to building from source if it fails
- name: Setup ccache - name: Setup ccache
if: matrix.use_ccache == 1
uses: jianmingyong/ccache-action@v1 uses: jianmingyong/ccache-action@v1
with: with:
install-type: "binary" install-type: "binary"
ccache-key-prefix: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}} ccache-key-prefix: ccache-${{matrix.os}}-${{matrix.soc}}-${{matrix.type}}
max-size: 500M
gh-token: ${{ secrets.GITHUB_TOKEN }} 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 uses: jurplel/install-qt-action@v4
with: with:
version: ${{matrix.qt_version}} cache: false # qt caches take too much space for macOS (1.1Gi)
arch: ${{matrix.qt_arch}} version: ${{env.QT_VERSION}}
modules: ${{matrix.qt_modules}} arch: ${{env.QT_ARCH}}
cache: ${{matrix.cache_qt}} modules: ${{env.QT_MODULES}}
- name: Setup vcpkg cache - name: Setup vcpkg cache
id: vcpkg-cache id: vcpkg-cache
@@ -381,30 +304,24 @@ jobs:
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
# uses environment variables, see compile.sh for more details - name: Build on Xcode ${{matrix.xcode}}
- name: Build Cockatrice
id: build
shell: bash shell: bash
id: build
env: env:
BUILDTYPE: '${{matrix.type}}' BUILDTYPE: '${{matrix.type}}'
MAKE_PACKAGE: '${{matrix.make_package}}' MAKE_PACKAGE: '${{matrix.make_package}}'
PACKAGE_SUFFIX: '${{matrix.package_suffix}}' PACKAGE_SUFFIX: '-macOS${{matrix.target}}_${{matrix.soc}}'
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
MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }} MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }} MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
DEVELOPER_DIR: '/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer' CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
TARGET_MACOS_VERSION: ${{ matrix.override_target }} VCPKG_DISABLE_METRICS: 1
run: .ci/compile.sh --server --test --vcpkg VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite'
run: .ci/compile.sh --server --test --ccache "$CCACHE_SIZE"
- name: Sign app bundle - 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: env:
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
@@ -416,7 +333,7 @@ jobs:
fi fi
- name: Notarize app bundle - 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: env:
MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_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 # Store the notarization credentials so that we can prevent a UI password dialog from blocking the CI
echo "Create keychain profile" 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" 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. # 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 # Therefore, we create a zip file containing our app bundle, so that we can send it to the
# notarization service # notarization service
echo "Creating temp notarization archive" echo "Creating temp notarization archive"
ditto -c -k --keepParent ${{steps.build.outputs.path}} "notarization.zip" 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. # 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 # 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 # characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if
# you're curious # you're curious
echo "Notarize app" echo "Notarize app"
xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait 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 # 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. # validated by macOS even when an internet connection is not available.
echo "Attach staple" echo "Attach staple"
@@ -450,15 +367,109 @@ jobs:
- name: Upload artifact - name: Upload artifact
id: upload_artifact id: upload_artifact
if: matrix.make_package if: matrix.make_package
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v4
with: 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}} path: ${{steps.build.outputs.path}}
if-no-files-found: error if-no-files-found: error
- name: Upload pdb database - name: Upload pdb database
if: matrix.os == 'Windows' uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with: with:
name: Windows${{matrix.target}}-debug-pdbs name: Windows${{matrix.target}}-debug-pdbs
path: | path: |

View File

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

View File

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

View File

@@ -6,7 +6,8 @@ on:
- '*' # Only re-generate docs when a new tagged version is pushed - '*' # Only re-generate docs when a new tagged version is pushed
pull_request: pull_request:
paths: paths:
- 'doc/doxygen/**' - '**.h'
- '/doc/doxygen/**'
- '.github/workflows/documentation-build.yml' - '.github/workflows/documentation-build.yml'
- 'Doxyfile' - 'Doxyfile'
workflow_dispatch: workflow_dispatch:
@@ -20,42 +21,35 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Install Graphviz - name: Install Doxygen and Graphviz
run: | shell: bash
sudo apt-get install -y graphviz run: sudo apt-get install -y doxygen 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: Generate Documentation - name: Generate Documentation
if: always() shell: bash
run: doxygen Doxyfile 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' if: github.event_name != 'pull_request'
uses: peaceiris/actions-gh-pages@v4 uses: peaceiris/actions-gh-pages@v4
with: with:
deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }} deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }}
external_repository: Cockatrice/cockatrice.github.io external_repository: Cockatrice/cockatrice.github.io
publish_branch: master publish_branch: master
publish_dir: ./docs/html publish_dir: ./docs/html # Main output folder + subfolder defined in Doxygen config
destination_dir: docs # Docs will live under https://cockatrice.github.io/docs/ 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 *' - cron: '0 0 15 1,4,7,10 *'
pull_request: pull_request:
paths: paths:
- '.tx/**'
- '.github/workflows/translations-pull.yml' - '.github/workflows/translations-pull.yml'
jobs: jobs:
@@ -20,7 +19,7 @@ jobs:
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Pull translated strings from Transifex - name: Pull translated strings from Transifex
uses: transifex/cli-action@v2 uses: transifex/cli-action@v2

View File

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

View File

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

View File

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

1
.gitignore vendored
View File

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

View File

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

View File

@@ -1,9 +1,8 @@
# -------- Build Stage -------- FROM ubuntu:24.04
FROM ubuntu:24.04 AS build
ARG DEBIAN_FRONTEND=noninteractive 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 \ build-essential \
cmake \ cmake \
file \ file \
@@ -17,28 +16,20 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
qt6-tools-dev \ qt6-tools-dev \
qt6-tools-dev-tools qt6-tools-dev-tools
WORKDIR /src COPY ./CMakeLists.txt ./LICENSE ./README.md /home/servatrice/code/
COPY . . COPY ./cmake /home/servatrice/code/cmake
RUN mkdir build && cd build && \ COPY ./common /home/servatrice/code/common
cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 && \ COPY ./servatrice /home/servatrice/code/servatrice
make -j$(nproc) && \
make install
WORKDIR /home/servatrice/code
# -------- Runtime Stage (clean) -------- WORKDIR build
FROM ubuntu:24.04 RUN cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 &&\
make &&\
RUN apt-get update && apt-get install -y --no-install-recommends \ make install
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 /home/servatrice WORKDIR /home/servatrice
EXPOSE 4748 EXPOSE 4748
ENTRYPOINT [ "servatrice", "--log-to-console" ] ENTRYPOINT [ "servatrice", "--log-to-console" ]

View File

@@ -1,4 +1,5 @@
# Doxyfile 1.14.0 # Doxyfile 1.14.0
# Docs: https://www.doxygen.nl/manual
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# Doxygen (www.doxygen.org) for a project. # 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. # title of most generated pages and in a few other places.
# The default value is: My Project. # 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 # 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 # 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 # 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. # 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 # 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 # 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. # The default value is: DOXYGEN.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. # 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 # When enabled Doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can # 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 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # 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 # 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 # 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, # provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. # *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.cc \ FILE_PATTERNS = *.c \
*.cc \
*.cxx \ *.cxx \
*.cxxm \ *.cxxm \
*.cpp \ *.cpp \
@@ -1039,19 +1041,47 @@ FILE_PATTERNS = *.cc \
*.ccm \ *.ccm \
*.c++ \ *.c++ \
*.c++m \ *.c++m \
*.java \
*.ii \ *.ii \
*.ixx \ *.ixx \
*.ipp \ *.ipp \
*.i++ \ *.i++ \
*.inl \ *.inl \
*.idl \
*.ddl \
*.odl \
*.h \ *.h \
*.hh \ *.hh \
*.hxx \ *.hxx \
*.hpp \ *.hpp \
*.h++ \ *.h++ \
*.l \
*.cs \
*.d \
*.php \
*.php4 \
*.php5 \
*.phtml \
*.inc \
*.m \
*.markdown \ *.markdown \
*.md \ *.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 # The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well. # 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 # Note that relative paths are relative to the directory from which Doxygen is
# run. # run.
EXCLUDE = build/ \ EXCLUDE = common/lib
cmake/ \
dbconverter/ \
vcpkg/ \
webclient/
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # 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 # 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 # 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 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 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the # (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 # that contain images that are to be included in the documentation (see the
# \image command). # \image command).
IMAGE_PATH = doc/doxygen/images IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that Doxygen should # 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 # invoke to filter for each input file. Doxygen will invoke the filter program
@@ -1289,46 +1314,6 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES 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 # 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. # The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES. # 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 # DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# around nodes set 'shape=plain' or 'shape=plaintext' <a # 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. # The default value is: shape=box,height=0.2,width=0.4.
# This tag requires that the tag HAVE_DOT is set to YES. # 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 # You can set the path where dot can find font specified with fontname in
# DOT_COMMON_ATTR and others dot attributes. # 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" DB_CONVERTER_QT_MODULES "${_DBCONVERTER_NEEDED}")
string(REGEX REPLACE "([^;]+)" "${COCKATRICE_QT_VERSION_NAME}::\\1" TEST_QT_MODULES "${_TEST_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}") 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 set(cockatrice_SOURCES
${VERSION_STRING_CPP} ${VERSION_STRING_CPP}
# sort by alphabetical order, so that there is no debate about where to add new sources to the list # 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/card/card_info.cpp
src/client/network/interfaces/deck_stats_interface.cpp src/card/card_relation.cpp
src/client/network/interfaces/tapped_out_interface.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/parsers/deck_link_to_api_transformer.cpp
src/client/network/update/client/client_update_checker.cpp src/client/network/release_channel.cpp
src/client/network/update/client/release_channel.cpp src/client/network/replay_timeline_widget.cpp
src/client/network/update/card_spoiler/spoiler_background_updater.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/sound_engine.cpp
src/client/settings/cache_settings.cpp src/client/tapped_out_interface.cpp
src/client/settings/card_counter_settings.cpp src/client/update_downloader.cpp
src/client/settings/shortcut_treeview.cpp src/database/card_database.cpp
src/client/settings/shortcuts_settings.cpp src/database/card_database_loader.cpp
src/interface/deck_loader/deck_loader.cpp src/database/card_database_manager.cpp
src/interface/widgets/dialogs/dlg_connect.cpp src/database/card_database_querier.cpp
src/interface/widgets/dialogs/dlg_convert_deck_to_cod_format.cpp src/database/model/card_database_model.cpp
src/interface/widgets/dialogs/dlg_create_game.cpp src/database/model/card_database_display_model.cpp
src/interface/widgets/dialogs/dlg_default_tags_editor.cpp src/database/model/card/card_completer_proxy_model.cpp
src/interface/widgets/dialogs/dlg_edit_avatar.cpp src/database/model/card/card_search_model.cpp
src/interface/widgets/dialogs/dlg_edit_password.cpp src/database/model/token/token_display_model.cpp
src/interface/widgets/dialogs/dlg_edit_tokens.cpp src/database/model/token/token_edit_model.cpp
src/interface/widgets/dialogs/dlg_edit_user.cpp src/database/parser/card_database_parser.cpp
src/interface/widgets/dialogs/dlg_filter_games.cpp src/database/parser/cockatrice_xml_3.cpp
src/interface/widgets/dialogs/dlg_forgot_password_challenge.cpp src/database/parser/cockatrice_xml_4.cpp
src/interface/widgets/dialogs/dlg_forgot_password_request.cpp src/deck/custom_line_edit.cpp
src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp src/deck/deck_list_model.cpp
src/interface/widgets/dialogs/dlg_load_deck.cpp src/deck/deck_loader.cpp
src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp src/deck/deck_stats_interface.cpp
src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp src/dialogs/dlg_connect.cpp
src/interface/widgets/dialogs/dlg_load_remote_deck.cpp src/dialogs/dlg_convert_deck_to_cod_format.cpp
src/interface/widgets/dialogs/dlg_manage_sets.cpp src/dialogs/dlg_create_game.cpp
src/interface/widgets/dialogs/dlg_register.cpp src/dialogs/dlg_default_tags_editor.cpp
src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp src/dialogs/dlg_edit_avatar.cpp
src/interface/widgets/dialogs/dlg_settings.cpp src/dialogs/dlg_edit_password.cpp
src/interface/widgets/dialogs/dlg_startup_card_check.cpp src/dialogs/dlg_edit_tokens.cpp
src/interface/widgets/dialogs/dlg_tip_of_the_day.cpp src/dialogs/dlg_edit_user.cpp
src/interface/widgets/dialogs/dlg_update.cpp src/dialogs/dlg_filter_games.cpp
src/interface/widgets/dialogs/dlg_view_log.cpp src/dialogs/dlg_forgot_password_challenge.cpp
src/interface/widgets/dialogs/tip_of_the_day.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/deck_filter_string.cpp
src/filters/filter_builder.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/filter_tree_model.cpp
src/filters/syntax_help.cpp src/filters/syntax_help.cpp
src/game/abstract_game.cpp src/game/abstract_game.cpp
src/game/board/abstract_card_drag_item.cpp src/game/board/abstract_card_drag_item.cpp
src/game/board/abstract_card_item.cpp src/game/board/abstract_card_item.cpp
src/game/board/abstract_counter.cpp src/game/board/abstract_counter.cpp
src/game/board/abstract_graphics_item.cpp
src/game/board/arrow_item.cpp src/game/board/arrow_item.cpp
src/game/board/arrow_target.cpp src/game/board/arrow_target.cpp
src/game/board/card_drag_item.cpp src/game/board/card_drag_item.cpp
@@ -112,17 +138,9 @@ set(cockatrice_SOURCES
src/game/zones/table_zone.cpp src/game/zones/table_zone.cpp
src/game/zones/view_zone.cpp src/game/zones/view_zone.cpp
src/game/zones/view_zone_widget.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/flow_layout.cpp
src/interface/layouts/overlap_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/pixel_map_generator.cpp
src/interface/theme_manager.cpp src/interface/theme_manager.cpp
src/interface/widgets/cards/additional_info/color_identity_widget.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_card_zone_display_widget.cpp
src/interface/widgets/cards/deck_preview_card_picture_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_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_base_widget.cpp
src/interface/widgets/deck_analytics/mana_curve_widget.cpp src/interface/widgets/deck_analytics/mana_curve_widget.cpp
src/interface/widgets/deck_analytics/mana_devotion_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_card_info_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_database_display_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_deck_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_filter_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_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/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/banner_widget.cpp
src/interface/widgets/general/display/bar_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_label.cpp
src/interface/widgets/general/display/dynamic_font_size_push_button.cpp src/interface/widgets/general/display/dynamic_font_size_push_button.cpp
src/interface/widgets/general/display/labeled_input.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/flow_widget.cpp
src/interface/widgets/general/layout_containers/overlap_control_widget.cpp src/interface/widgets/general/layout_containers/overlap_control_widget.cpp
src/interface/widgets/general/layout_containers/overlap_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/all_zones_card_amount_widget.cpp
src/interface/widgets/printing_selector/card_amount_widget.cpp src/interface/widgets/printing_selector/card_amount_widget.cpp
src/interface/widgets/printing_selector/printing_selector.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/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_button_widget.cpp
src/interface/widgets/quick_settings/settings_popup_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_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_filter_save_load_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp src/interface/widgets/visual_database_display/visual_database_display_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_sub_type_filter_widget.cpp
src/interface/widgets/visual_database_display/visual_database_display_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_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_sample_hand_widget.cpp
src/interface/widgets/visual_deck_editor/visual_deck_editor_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 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/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
src/interface/window_main.cpp src/interface/window_main.cpp
src/main.cpp src/main.cpp
src/interface/widgets/tabs/abstract_tab_deck_editor.cpp src/picture_loader/picture_loader.cpp
src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp src/picture_loader/picture_loader_local.cpp
src/interface/widgets/tabs/api/archidekt/api_response/archidekt_deck_listing_api_response.cpp src/picture_loader/picture_loader_request_status_display_widget.cpp
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp src/picture_loader/picture_loader_status_bar.cpp
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card_entry.cpp src/picture_loader/picture_loader_worker.cpp
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_edition.cpp src/picture_loader/picture_loader_worker_work.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.cpp src/picture_loader/picture_to_load.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck_category.cpp src/server/abstract_client.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck_listings/archidekt_api_response_deck_listing_container.cpp src/server/chat_view/chat_view.cpp
src/interface/widgets/tabs/api/archidekt/api_response/deck_listings/archidekt_api_response_deck_owner.cpp src/server/game_selector.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp src/server/games_model.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp src/server/handle_public_servers.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp src/server/local_client.cpp
src/interface/widgets/tabs/api/archidekt/display/archidekt_deck_preview_image_display_widget.cpp src/server/local_server.cpp
src/interface/widgets/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp src/server/local_server_interface.cpp
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp src/server/pending_command.cpp
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp src/server/remote/remote_client.cpp
src/interface/widgets/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp src/server/remote/remote_decklist_tree_widget.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp src/server/remote/remote_replay_list_tree_widget.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp src/server/user/user_context_menu.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp src/server/user/user_info_box.cpp
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp src/server/user/user_info_connection.cpp
src/interface/widgets/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp src/server/user/user_list_manager.cpp
src/interface/widgets/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp src/server/user/user_list_widget.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp src/settings/cache_settings.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp src/settings/card_counter_settings.cpp
src/interface/widgets/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp src/settings/card_database_settings.cpp
src/interface/widgets/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp src/settings/card_override_settings.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp src/settings/debug_settings.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_inclusion_display_widget.cpp src/settings/download_settings.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp src/settings/game_filters_settings.cpp
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_synergy_display_widget.cpp src/settings/layouts_settings.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp src/settings/message_settings.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp src/settings/recents_settings.cpp
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp src/settings/servers_settings.cpp
src/interface/widgets/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp src/settings/settings_manager.cpp
src/interface/widgets/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp src/settings/shortcut_treeview.cpp
src/interface/widgets/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp src/settings/shortcuts_settings.cpp
src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp src/tabs/abstract_tab_deck_editor.cpp
src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp src/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
src/interface/widgets/tabs/tab.cpp src/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
src/interface/widgets/tabs/tab_account.cpp src/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
src/interface/widgets/tabs/tab_admin.cpp src/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp
src/interface/widgets/tabs/tab_deck_editor.cpp src/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp
src/interface/widgets/tabs/tab_deck_storage.cpp src/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp
src/interface/widgets/tabs/tab_game.cpp src/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp
src/interface/widgets/tabs/tab_home.cpp src/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp
src/interface/widgets/tabs/tab_logs.cpp src/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp
src/interface/widgets/tabs/tab_message.cpp src/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp
src/interface/widgets/tabs/tab_replays.cpp src/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
src/interface/widgets/tabs/tab_room.cpp src/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
src/interface/widgets/tabs/tab_server.cpp src/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
src/interface/widgets/tabs/tab_supervisor.cpp src/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
src/interface/widgets/tabs/tab_visual_database_display.cpp src/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp src/tabs/api/edhrec/display/cards/edhrec_api_response_card_inclusion_display_widget.cpp
src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp src/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp src/tabs/api/edhrec/display/cards/edhrec_api_response_card_synergy_display_widget.cpp
src/interface/key_signals.cpp src/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
src/interface/logger.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) add_subdirectory(sounds)
@@ -293,23 +316,15 @@ configure_file(
set(cockatrice_RESOURCES cockatrice.qrc) set(cockatrice_RESOURCES cockatrice.qrc)
if(UPDATE_TRANSLATIONS) if(UPDATE_TRANSLATIONS)
# Cockatrice main sources
file(GLOB_RECURSE translate_cockatrice_SRCS ${CMAKE_SOURCE_DIR}/cockatrice/src/*.cpp file(GLOB_RECURSE translate_cockatrice_SRCS ${CMAKE_SOURCE_DIR}/cockatrice/src/*.cpp
${CMAKE_SOURCE_DIR}/cockatrice/src/*.h ${CMAKE_SOURCE_DIR}/cockatrice/src/*.h
) )
file(GLOB_RECURSE translate_common_SRCS ${CMAKE_SOURCE_DIR}/common/*.cpp ${CMAKE_SOURCE_DIR}/common/*.h)
# All libcockatrice_* libraries (recursively) set(translate_SRCS ${translate_cockatrice_SRCS} ${translate_common_SRCS})
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})
set(cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice_en@source.ts") set(cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/cockatrice_en@source.ts")
else() else()
file(GLOB cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts") file(GLOB cockatrice_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts")
endif() endif(UPDATE_TRANSLATIONS)
if(WIN32) if(WIN32)
set(cockatrice_SOURCES ${cockatrice_SOURCES} cockatrice.rc) set(cockatrice_SOURCES ${cockatrice_SOURCES} cockatrice.rc)
@@ -339,6 +354,12 @@ set(DESKTOPDIR
CACHE STRING "desktop file destination" 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_MAC_QM_INSTALL_DIR "cockatrice.app/Contents/Resources/translations")
set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/cockatrice/translations") set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/cockatrice/translations")
set(COCKATRICE_WIN32_QM_INSTALL_DIR "translations") set(COCKATRICE_WIN32_QM_INSTALL_DIR "translations")
@@ -378,31 +399,9 @@ elseif(Qt5_FOUND)
endif() endif()
if(Qt5_FOUND) if(Qt5_FOUND)
target_link_libraries( target_link_libraries(cockatrice cockatrice_common ${COCKATRICE_QT_MODULES})
cockatrice
libcockatrice_card
libcockatrice_deck_list
libcockatrice_filters
libcockatrice_utility
libcockatrice_network
libcockatrice_models
libcockatrice_rng
libcockatrice_settings
${COCKATRICE_QT_MODULES}
)
else() else()
target_link_libraries( target_link_libraries(cockatrice PUBLIC cockatrice_common ${COCKATRICE_QT_MODULES})
cockatrice
PUBLIC libcockatrice_card
libcockatrice_deck_list
libcockatrice_filters
libcockatrice_utility
libcockatrice_network
libcockatrice_models
libcockatrice_rng
libcockatrice_settings
${COCKATRICE_QT_MODULES}
)
endif() endif()
if(UNIX) if(UNIX)

View File

@@ -7,14 +7,11 @@
<file>resources/icons/arrow_bottom_green.svg</file> <file>resources/icons/arrow_bottom_green.svg</file>
<file>resources/icons/arrow_down_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_left_green.svg</file>
<file>resources/icons/arrow_redo.svg</file>
<file>resources/icons/arrow_right_blue.svg</file> <file>resources/icons/arrow_right_blue.svg</file>
<file>resources/icons/arrow_right_green.svg</file> <file>resources/icons/arrow_right_green.svg</file>
<file>resources/icons/arrow_top_green.svg</file> <file>resources/icons/arrow_top_green.svg</file>
<file>resources/icons/arrow_up_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/clearsearch.svg</file>
<file>resources/icons/cogwheel.svg</file> <file>resources/icons/cogwheel.svg</file>
<file>resources/icons/conceded.svg</file> <file>resources/icons/conceded.svg</file>
@@ -28,7 +25,6 @@
<file>resources/icons/lock.svg</file> <file>resources/icons/lock.svg</file>
<file>resources/icons/not_ready_start.svg</file> <file>resources/icons/not_ready_start.svg</file>
<file>resources/icons/pencil.svg</file> <file>resources/icons/pencil.svg</file>
<file>resources/icons/pin.svg</file>
<file>resources/icons/player.svg</file> <file>resources/icons/player.svg</file>
<file>resources/icons/ready_start.svg</file> <file>resources/icons/ready_start.svg</file>
<file>resources/icons/reload.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 #card_zone = true
#view_zone = true #view_zone = true
#game_event_handler = true
#user_info_connection = true #user_info_connection = true
#card_picture_loader = true #picture_loader = true
#card_picture_loader.worker = true #picture_loader.worker = true
#card_picture_loader.card_back_cache_fail = true #picture_loader.card_back_cache_fail = true
#card_picture_loader.picture_to_load = true #picture_loader.picture_to_load = true
#deck_loader = true #deck_loader = true
#card_database = true #card_database = true
#card_database.loading = 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" 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-x="0.094945927"
inkscape:transform-center-y="-3.9764964" 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> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,9 +1,10 @@
#include "card_info.h" #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 "game_specific_terms.h"
#include "printing/printing_info.h" #include "printing_info.h"
#include "relation/card_relation.h"
#include "set/card_set.h"
#include <QDir> #include <QDir>
#include <QRegularExpression> #include <QRegularExpression>
@@ -26,18 +27,23 @@ CardInfo::CardInfo(const QString &_name,
const QList<CardRelation *> &_relatedCards, const QList<CardRelation *> &_relatedCards,
const QList<CardRelation *> &_reverseRelatedCards, const QList<CardRelation *> &_reverseRelatedCards,
SetToPrintingsMap _sets, 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), : 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); simpleName = CardInfo::simplifyName(name);
refreshCachedSets(); refreshCachedSetNames();
} }
CardInfoPtr CardInfo::newInstance(const QString &_name) 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, CardInfoPtr CardInfo::newInstance(const QString &_name,
@@ -47,10 +53,13 @@ CardInfoPtr CardInfo::newInstance(const QString &_name,
const QList<CardRelation *> &_relatedCards, const QList<CardRelation *> &_relatedCards,
const QList<CardRelation *> &_reverseRelatedCards, const QList<CardRelation *> &_reverseRelatedCards,
SetToPrintingsMap _sets, 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, CardInfoPtr ptr(new CardInfo(_name, _text, _isToken, std::move(_properties), _relatedCards, _reverseRelatedCards,
_sets, _uiAttributes)); _sets, _cipt, _landscapeOrientation, _tableRow, _upsideDownArt));
ptr->setSmartPointer(ptr); ptr->setSmartPointer(ptr);
for (const auto &printings : _sets) { for (const auto &printings : _sets) {
@@ -84,7 +93,7 @@ void CardInfo::addToSet(const CardSetPtr &_set, const PrintingInfo _info)
setsToPrintings[_set->getShortName()].append(_info); setsToPrintings[_set->getShortName()].append(_info);
} }
refreshCachedSets(); refreshCachedSetNames();
} }
void CardInfo::combineLegalities(const QVariantHash &props) void CardInfo::combineLegalities(const QVariantHash &props)
@@ -98,12 +107,6 @@ void CardInfo::combineLegalities(const QVariantHash &props)
} }
} }
void CardInfo::refreshCachedSets()
{
refreshCachedSetNames();
refreshCachedAltNames();
}
void CardInfo::refreshCachedSetNames() void CardInfo::refreshCachedSetNames()
{ {
QStringList setList; QStringList setList;
@@ -119,21 +122,6 @@ void CardInfo::refreshCachedSetNames()
setsNames = setList.join(", "); 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) QString CardInfo::simplifyName(const QString &name)
{ {
static const QRegularExpression spaceOrSplit("(\\s+|\\/\\/.*)"); 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 #ifndef CARD_INFO_H
#define CARD_INFO_H #define CARD_INFO_H
#include "printing/printing_info.h" #include "printing_info.h"
#include <QDate> #include <QDate>
#include <QHash> #include <QHash>
@@ -10,6 +16,7 @@
#include <QMap> #include <QMap>
#include <QMetaType> #include <QMetaType>
#include <QSharedPointer> #include <QSharedPointer>
#include <QStringList>
#include <QVariant> #include <QVariant>
#include <utility> #include <utility>
@@ -46,26 +53,7 @@ class CardInfo : public QObject
{ {
Q_OBJECT 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: private:
/** @name Private Card Properties
* @anchor PrivateCardProperties
*/
///@{
CardInfoPtr smartThis; ///< Smart pointer to self for safe cross-references. CardInfoPtr smartThis; ///< Smart pointer to self for safe cross-references.
QString name; ///< Full name of the card. QString name; ///< Full name of the card.
QString simpleName; ///< Simplified name for fuzzy matching. QString simpleName; ///< Simplified name for fuzzy matching.
@@ -76,10 +64,11 @@ private:
QList<CardRelation *> reverseRelatedCards; ///< Cards that refer back to this card. QList<CardRelation *> reverseRelatedCards; ///< Cards that refer back to this card.
QList<CardRelation *> reverseRelatedCardsToMe; ///< Cards that consider this card as related. QList<CardRelation *> reverseRelatedCardsToMe; ///< Cards that consider this card as related.
SetToPrintingsMap setsToPrintings; ///< Mapping from set names to printing variations. 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. 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: public:
/** /**
@@ -92,7 +81,10 @@ public:
* @param _relatedCards Forward references to related cards. * @param _relatedCards Forward references to related cards.
* @param _reverseRelatedCards Backward references to related cards. * @param _reverseRelatedCards Backward references to related cards.
* @param _sets Map of set names to printing information. * @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, explicit CardInfo(const QString &_name,
const QString &_text, const QString &_text,
@@ -101,7 +93,10 @@ public:
const QList<CardRelation *> &_relatedCards, const QList<CardRelation *> &_relatedCards,
const QList<CardRelation *> &_reverseRelatedCards, const QList<CardRelation *> &_reverseRelatedCards,
SetToPrintingsMap _sets, SetToPrintingsMap _sets,
UiAttributes _uiAttributes); bool _cipt,
bool _landscapeOrientation,
int _tableRow,
bool _upsideDownArt);
/** /**
* @brief Copy constructor for CardInfo. * @brief Copy constructor for CardInfo.
@@ -114,8 +109,8 @@ public:
: QObject(other.parent()), name(other.name), simpleName(other.simpleName), text(other.text), : QObject(other.parent()), name(other.name), simpleName(other.simpleName), text(other.text),
isToken(other.isToken), properties(other.properties), relatedCards(other.relatedCards), isToken(other.isToken), properties(other.properties), relatedCards(other.relatedCards),
reverseRelatedCards(other.reverseRelatedCards), reverseRelatedCardsToMe(other.reverseRelatedCardsToMe), reverseRelatedCards(other.reverseRelatedCards), reverseRelatedCardsToMe(other.reverseRelatedCardsToMe),
setsToPrintings(other.setsToPrintings), uiAttributes(other.uiAttributes), setsNames(other.setsNames), setsToPrintings(other.setsToPrintings), setsNames(other.setsNames), cipt(other.cipt),
altNames(other.altNames) landscapeOrientation(other.landscapeOrientation), tableRow(other.tableRow), upsideDownArt(other.upsideDownArt)
{ {
} }
@@ -139,7 +134,10 @@ public:
* @param _relatedCards Forward relationships. * @param _relatedCards Forward relationships.
* @param _reverseRelatedCards Reverse relationships. * @param _reverseRelatedCards Reverse relationships.
* @param _sets Printing information per set. * @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. * @return Shared pointer to the new CardInfo instance.
*/ */
static CardInfoPtr newInstance(const QString &_name, static CardInfoPtr newInstance(const QString &_name,
@@ -149,7 +147,10 @@ public:
const QList<CardRelation *> &_relatedCards, const QList<CardRelation *> &_relatedCards,
const QList<CardRelation *> &_reverseRelatedCards, const QList<CardRelation *> &_reverseRelatedCards,
SetToPrintingsMap _sets, SetToPrintingsMap _sets,
UiAttributes _uiAttributes); bool _cipt,
bool _landscapeOrientation,
int _tableRow,
bool _upsideDownArt);
/** /**
* @brief Clones the current CardInfo instance. * @brief Clones the current CardInfo instance.
@@ -158,9 +159,9 @@ public:
* *
* @return Shared pointer to the cloned CardInfo. * @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 newCardInfo->setSmartPointer(newCardInfo); // Set the smart pointer for the new instance
return newCardInfo; return newCardInfo;
} }
@@ -178,19 +179,15 @@ public:
} }
/** @name Basic Properties Accessors */ //@{ /** @name Basic Properties Accessors */ //@{
[[nodiscard]] inline const QString &getName() const inline const QString &getName() const
{ {
return name; return name;
} }
[[nodiscard]] const QString &getSimpleName() const const QString &getSimpleName() const
{ {
return simpleName; return simpleName;
} }
const QSet<QString> &getAltNames() const QString &getText() const
{
return altNames;
}
[[nodiscard]] const QString &getText() const
{ {
return text; return text;
} }
@@ -199,15 +196,15 @@ public:
text = _text; text = _text;
emit cardInfoChanged(smartThis); emit cardInfoChanged(smartThis);
} }
[[nodiscard]] bool getIsToken() const bool getIsToken() const
{ {
return isToken; return isToken;
} }
[[nodiscard]] QStringList getProperties() const QStringList getProperties() const
{ {
return properties.keys(); return properties.keys();
} }
[[nodiscard]] QString getProperty(const QString &propertyName) const QString getProperty(const QString &propertyName) const
{ {
return properties.value(propertyName).toString(); return properties.value(propertyName).toString();
} }
@@ -216,34 +213,34 @@ public:
properties.insert(_name, _value); properties.insert(_name, _value);
emit cardInfoChanged(smartThis); emit cardInfoChanged(smartThis);
} }
[[nodiscard]] bool hasProperty(const QString &propertyName) const bool hasProperty(const QString &propertyName) const
{ {
return properties.contains(propertyName); return properties.contains(propertyName);
} }
[[nodiscard]] const SetToPrintingsMap &getSets() const const SetToPrintingsMap &getSets() const
{ {
return setsToPrintings; return setsToPrintings;
} }
[[nodiscard]] const QString &getSetsNames() const const QString &getSetsNames() const
{ {
return setsNames; return setsNames;
} }
//@} //@}
/** @name Related Cards Accessors */ //@{ /** @name Related Cards Accessors */ //@{
[[nodiscard]] const QList<CardRelation *> &getRelatedCards() const const QList<CardRelation *> &getRelatedCards() const
{ {
return relatedCards; return relatedCards;
} }
[[nodiscard]] const QList<CardRelation *> &getReverseRelatedCards() const const QList<CardRelation *> &getReverseRelatedCards() const
{ {
return reverseRelatedCards; return reverseRelatedCards;
} }
[[nodiscard]] const QList<CardRelation *> &getReverseRelatedCards2Me() const const QList<CardRelation *> &getReverseRelatedCards2Me() const
{ {
return reverseRelatedCardsToMe; return reverseRelatedCardsToMe;
} }
[[nodiscard]] QList<CardRelation *> getAllRelatedCards() const QList<CardRelation *> getAllRelatedCards() const
{ {
QList<CardRelation *> result; QList<CardRelation *> result;
result.append(getRelatedCards()); result.append(getRelatedCards());
@@ -258,24 +255,39 @@ public:
//@} //@}
/** @name UI Positioning */ //@{ /** @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 */ //@{ /** @name Legacy/Convenience Property Accessors */ //@{
[[nodiscard]] const QString getCardType() const; const QString getCardType() const;
void setCardType(const QString &value); void setCardType(const QString &value);
[[nodiscard]] const QString getCmc() const; const QString getCmc() const;
[[nodiscard]] const QString getColors() const; const QString getColors() const;
void setColors(const QString &value); void setColors(const QString &value);
[[nodiscard]] const QString getLoyalty() const; const QString getLoyalty() const;
[[nodiscard]] const QString getMainCardType() const; const QString getMainCardType() const;
[[nodiscard]] const QString getManaCost() const; const QString getManaCost() const;
[[nodiscard]] const QString getPowTough() const; const QString getPowTough() const;
void setPowTough(const QString &value); void setPowTough(const QString &value);
//@} //@}
@@ -286,7 +298,7 @@ public:
* *
* @return Corrected card name as a QString. * @return Corrected card name as a QString.
*/ */
[[nodiscard]] QString getCorrectedName() const; QString getCorrectedName() const;
/** /**
* @brief Adds a printing to a specific set. * @brief Adds a printing to a specific set.
@@ -308,11 +320,11 @@ public:
void combineLegalities(const QVariantHash &props); 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. * @brief Simplifies a name for fuzzy matching.
@@ -324,21 +336,6 @@ public:
*/ */
static QString simplifyName(const QString &name); 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: signals:
/** /**
* @brief Emitted when a pixmap for this card has been updated or finished loading. * @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.h"
#include "card_relation_type.h"
CardRelation::CardRelation(const QString &_name, CardRelation::CardRelation(const QString &_name,
CardRelationType _attachType, CardRelationType _attachType,
bool _isCreateAllExclusion, 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> #include <QString>
/** /**
* @enum CardRelationType * Represents how a card relates to another (attach, transform, etc.).
* @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.
*/ */
enum class CardRelationType enum class CardRelationType
{ {
@@ -19,7 +13,7 @@ enum class CardRelationType
TransformInto = 2, TransformInto = 2,
}; };
// Helper function to transform the enum values into human-readable strings // Optional helper
inline QString cardAttachTypeToString(CardRelationType type) inline QString cardAttachTypeToString(CardRelationType type)
{ {
switch (type) { switch (type) {

View File

@@ -1,30 +1,26 @@
#include "card_set.h" #include "card_set.h"
#include <QSet> #include "../settings/cache_settings.h"
#include <utility>
const char *CardSet::TOKENS_SETNAME = "TK"; const char *CardSet::TOKENS_SETNAME = "TK";
CardSet::CardSet(ICardSetPriorityController *_priorityController, CardSet::CardSet(const QString &_shortName,
const QString &_shortName,
const QString &_longName, const QString &_longName,
const QString &_setType, const QString &_setType,
const QDate &_releaseDate, const QDate &_releaseDate,
const CardSet::Priority _priority) const CardSet::Priority _priority)
: priorityController(std::move(_priorityController)), shortName(_shortName), longName(_longName), : shortName(_shortName), longName(_longName), releaseDate(_releaseDate), setType(_setType), priority(_priority)
releaseDate(_releaseDate), setType(_setType), priority(_priority)
{ {
loadSetOptions(); loadSetOptions();
} }
CardSetPtr CardSet::newInstance(ICardSetPriorityController *_priorityController, CardSetPtr CardSet::newInstance(const QString &_shortName,
const QString &_shortName,
const QString &_longName, const QString &_longName,
const QString &_setType, const QString &_setType,
const QDate &_releaseDate, const QDate &_releaseDate,
const Priority _priority) 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); // ptr->setSmartPointer(ptr);
return ptr; return ptr;
} }
@@ -61,25 +57,25 @@ QString CardSet::getCorrectedShortName() const
void CardSet::loadSetOptions() void CardSet::loadSetOptions()
{ {
sortKey = priorityController->getSortKey(shortName); sortKey = SettingsCache::instance().cardDatabase().getSortKey(shortName);
enabled = priorityController->isEnabled(shortName); enabled = SettingsCache::instance().cardDatabase().isEnabled(shortName);
isknown = priorityController->isKnown(shortName); isknown = SettingsCache::instance().cardDatabase().isKnown(shortName);
} }
void CardSet::setSortKey(unsigned int _sortKey) void CardSet::setSortKey(unsigned int _sortKey)
{ {
sortKey = _sortKey; sortKey = _sortKey;
priorityController->setSortKey(shortName, _sortKey); SettingsCache::instance().cardDatabase().setSortKey(shortName, _sortKey);
} }
void CardSet::setEnabled(bool _enabled) void CardSet::setEnabled(bool _enabled)
{ {
enabled = _enabled; enabled = _enabled;
priorityController->setEnabled(shortName, _enabled); SettingsCache::instance().cardDatabase().setEnabled(shortName, _enabled);
} }
void CardSet::setIsKnown(bool _isknown) void CardSet::setIsKnown(bool _isknown)
{ {
isknown = _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 "exact_card.h"
#include "../card_info.h"
#include "printing_info.h"
/** /**
* Default constructor. * Default constructor.
* This will set the CardInfoPtr to null. * 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 QCoreApplication::translate("Mtg", "Color Identity");
return key; return key;
} }
} // namespace Mtg }; // namespace Mtg
#endif #endif

View File

@@ -1,7 +1,5 @@
#include "printing_info.h" #include "printing_info.h"
#include "../set/card_set.h"
PrintingInfo::PrintingInfo(const CardSetPtr &_set) : set(_set) PrintingInfo::PrintingInfo(const CardSetPtr &_set) : set(_set)
{ {
} }
@@ -12,9 +10,4 @@ PrintingInfo::PrintingInfo(const CardSetPtr &_set) : set(_set)
QString PrintingInfo::getUuid() const QString PrintingInfo::getUuid() const
{ {
return properties.value("uuid").toString(); 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 "../settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h" #include "../settings/shortcuts_settings.h"
#include "../tabs/abstract_tab_deck_editor.h"
DeckEditorMenu::DeckEditorMenu(AbstractTabDeckEditor *parent) : QMenu(parent), deckEditor(parent) DeckEditorMenu::DeckEditorMenu(AbstractTabDeckEditor *parent) : QMenu(parent), deckEditor(parent)
{ {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,11 +8,11 @@
#ifndef REPLAY_MANAGER_H #ifndef REPLAY_MANAGER_H
#define 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 <QToolButton>
#include <QWidget> #include <QWidget>
#include <libcockatrice/protocol/pb/game_replay.pb.h>
class TabGame; class TabGame;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,34 +1,35 @@
#include "card_database.h" #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 "parser/cockatrice_xml_4.h"
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QDebug> #include <QDebug>
#include <QDir>
#include <QDirIterator> #include <QDirIterator>
#include <QFile> #include <QFile>
#include <QMessageBox>
#include <QRegularExpression> #include <QRegularExpression>
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
CardDatabase::CardDatabase(QObject *parent, CardDatabase::CardDatabase(QObject *parent) : QObject(parent), loadStatus(NotLoaded)
ICardPreferenceProvider *prefs,
ICardDatabasePathProvider *pathProvider,
ICardSetPriorityController *_setPriorityController)
: QObject(parent), setPriorityController(_setPriorityController), loadStatus(NotLoaded)
{ {
qRegisterMetaType<CardInfoPtr>("CardInfoPtr"); qRegisterMetaType<CardInfoPtr>("CardInfoPtr");
qRegisterMetaType<CardInfoPtr>("CardSetPtr"); qRegisterMetaType<CardInfoPtr>("CardSetPtr");
// create loader and wire it up // 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) // re-emit loader signals (so other code doesn't need to know about internals)
connect(loader, &CardDatabaseLoader::loadingFinished, this, &CardDatabase::cardDatabaseLoadingFinished); connect(loader, &CardDatabaseLoader::loadingFinished, this, &CardDatabase::cardDatabaseLoadingFinished);
connect(loader, &CardDatabaseLoader::loadingFailed, this, &CardDatabase::cardDatabaseLoadingFailed); connect(loader, &CardDatabaseLoader::loadingFailed, this, &CardDatabase::cardDatabaseLoadingFailed);
connect(loader, &CardDatabaseLoader::newSetsFound, this, &CardDatabase::cardDatabaseNewSetsFound); connect(loader, &CardDatabaseLoader::newSetsFound, this, &CardDatabase::cardDatabaseNewSetsFound);
connect(loader, &CardDatabaseLoader::allNewSetsEnabled, this, &CardDatabase::cardDatabaseAllNewSetsEnabled); connect(loader, &CardDatabaseLoader::allNewSetsEnabled, this, &CardDatabase::cardDatabaseAllNewSetsEnabled);
querier = new CardDatabaseQuerier(this, this, prefs); querier = new CardDatabaseQuerier(this, this);
} }
CardDatabase::~CardDatabase() CardDatabase::~CardDatabase()
@@ -138,7 +139,7 @@ CardSetPtr CardDatabase::getSet(const QString &setName)
if (sets.contains(setName)) { if (sets.contains(setName)) {
return sets.value(setName); return sets.value(setName);
} else { } else {
CardSetPtr newSet = CardSet::newInstance(setPriorityController, setName); CardSetPtr newSet = CardSet::newInstance(setName);
sets.insert(setName, newSet); sets.insert(setName, newSet);
return newSet; return newSet;
} }
@@ -192,9 +193,8 @@ void CardDatabase::markAllSetsAsKnown()
void CardDatabase::notifyEnabledSetsChanged() void CardDatabase::notifyEnabledSetsChanged()
{ {
// refresh the list of cached set names // refresh the list of cached set names
for (const CardInfoPtr &card : cards) { for (const CardInfoPtr &card : cards)
card->refreshCachedSets(); card->refreshCachedSetNames();
}
// inform the carddatabasemodels that they need to re-check their list of cards // inform the carddatabasemodels that they need to re-check their list of cards
emit cardDatabaseEnabledSetsChanged(); 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 "card_database_loader.h"
#include "../settings/cache_settings.h"
#include "card_database.h" #include "card_database.h"
#include "parser/cockatrice_xml_3.h" #include "parser/cockatrice_xml_3.h"
#include "parser/cockatrice_xml_4.h" #include "parser/cockatrice_xml_4.h"
@@ -9,14 +10,10 @@
#include <QFile> #include <QFile>
#include <QTime> #include <QTime>
CardDatabaseLoader::CardDatabaseLoader(QObject *parent, CardDatabaseLoader::CardDatabaseLoader(QObject *parent, CardDatabase *db) : QObject(parent), database(db)
CardDatabase *db,
ICardDatabasePathProvider *_pathProvider,
ICardPreferenceProvider *_preferenceProvider)
: QObject(parent), database(db), pathProvider(_pathProvider)
{ {
// instantiate available parsers here and connect them to the database // instantiate available parsers here and connect them to the database
availableParsers << new CockatriceXml4Parser(_preferenceProvider); availableParsers << new CockatriceXml4Parser;
availableParsers << new CockatriceXml3Parser; availableParsers << new CockatriceXml3Parser;
for (auto *p : availableParsers) { for (auto *p : availableParsers) {
@@ -26,7 +23,7 @@ CardDatabaseLoader::CardDatabaseLoader(QObject *parent,
} }
// when SettingsCache's path changes, trigger reloads // when SettingsCache's path changes, trigger reloads
connect(pathProvider, &ICardDatabasePathProvider::cardDatabasePathChanged, this, connect(&SettingsCache::instance(), &SettingsCache::cardDatabasePathChanged, this,
&CardDatabaseLoader::loadCardDatabases); &CardDatabaseLoader::loadCardDatabases);
} }
@@ -39,7 +36,8 @@ CardDatabaseLoader::~CardDatabaseLoader()
LoadStatus CardDatabaseLoader::loadFromFile(const QString &fileName) LoadStatus CardDatabaseLoader::loadFromFile(const QString &fileName)
{ {
QFile file(fileName); QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) { file.open(QIODevice::ReadOnly);
if (!file.isOpen()) {
return FileError; return FileError;
} }
@@ -86,9 +84,10 @@ LoadStatus CardDatabaseLoader::loadCardDatabases()
database->clear(); // remove old db database->clear(); // remove old db
LoadStatus loadStatus = loadCardDatabase(pathProvider->getCardDatabasePath()); // load main card database LoadStatus loadStatus =
loadCardDatabase(pathProvider->getTokenDatabasePath()); // load tokens database loadCardDatabase(SettingsCache::instance().getCardDatabasePath()); // load main card database
loadCardDatabase(pathProvider->getSpoilerCardDatabasePath()); // load spoilers database loadCardDatabase(SettingsCache::instance().getTokenDatabasePath()); // load tokens database
loadCardDatabase(SettingsCache::instance().getSpoilerCardDatabasePath()); // load spoilers database
// find all custom card databases, recursively & following symlinks // find all custom card databases, recursively & following symlinks
// then load them alphabetically // then load them alphabetically
@@ -119,7 +118,7 @@ LoadStatus CardDatabaseLoader::loadCardDatabases()
QStringList CardDatabaseLoader::collectCustomDatabasePaths() const QStringList CardDatabaseLoader::collectCustomDatabasePaths() const
{ {
QDirIterator it(pathProvider->getCustomCardDatabasePath(), {"*.xml"}, QDir::Files, QDirIterator it(SettingsCache::instance().getCustomCardDatabasePath(), {"*.xml"}, QDir::Files,
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
QStringList paths; QStringList paths;
@@ -136,7 +135,7 @@ bool CardDatabaseLoader::saveCustomTokensToFile()
return false; return false;
} }
QString fileName = pathProvider->getCustomCardDatabasePath() + "/" + CardSet::TOKENS_SETNAME + ".xml"; QString fileName = SettingsCache::instance().getCustomCardDatabasePath() + "/" + CardSet::TOKENS_SETNAME + ".xml";
SetNameMap tmpSets; SetNameMap tmpSets;
CardSetPtr customTokensSet = database->getSet(CardSet::TOKENS_SETNAME); CardSetPtr customTokensSet = database->getSet(CardSet::TOKENS_SETNAME);
@@ -151,4 +150,4 @@ bool CardDatabaseLoader::saveCustomTokensToFile()
availableParsers.first()->saveToFile(tmpSets, tmpCards, fileName); availableParsers.first()->saveToFile(tmpSets, tmpCards, fileName);
return true; 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_database_querier.h"
#include "../card_info.h" #include "../utility/card_set_comparator.h"
#include "../printing/exact_card.h"
#include "../set/card_set_comparator.h"
#include "card_database.h" #include "card_database.h"
#include <qrandom.h> #include <qrandom.h>
CardDatabaseQuerier::CardDatabaseQuerier(QObject *_parent, CardDatabaseQuerier::CardDatabaseQuerier(QObject *_parent, const CardDatabase *_db) : QObject(_parent), db(_db)
const CardDatabase *_db,
const ICardPreferenceProvider *prefs)
: QObject(_parent), db(_db), prefs(prefs)
{ {
} }
@@ -207,17 +202,6 @@ PrintingInfo CardDatabaseQuerier::getSpecificPrinting(const QString &cardName,
return PrintingInfo(nullptr); 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 * Gets the card representing the preferred printing of the cardInfo
* *
@@ -250,12 +234,6 @@ PrintingInfo CardDatabaseQuerier::getPreferredPrinting(const CardInfoPtr &cardIn
return PrintingInfo(nullptr); return PrintingInfo(nullptr);
} }
const auto &pinnedPrintingProviderId = prefs->getCardPreferenceOverride(cardInfo->getName());
if (!pinnedPrintingProviderId.isEmpty()) {
return getSpecificPrinting({cardInfo->getName(), pinnedPrintingProviderId});
}
SetToPrintingsMap setMap = cardInfo->getSets(); SetToPrintingsMap setMap = cardInfo->getSets();
if (setMap.empty()) { if (setMap.empty()) {
return PrintingInfo(nullptr); 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); explicit CardCompleterProxyModel(QObject *parent = nullptr);
protected: 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 #endif // CARD_COMPLETER_PROXY_MODEL_H

View File

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

View File

@@ -17,8 +17,8 @@ class CardSearchModel : public QAbstractListModel
public: public:
explicit CardSearchModel(CardDatabaseDisplayModel *sourceModel, QObject *parent = nullptr); explicit CardSearchModel(CardDatabaseDisplayModel *sourceModel, QObject *parent = nullptr);
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void updateSearchResults(const QString &query); // Update results based on input 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() void CardDatabaseDisplayModel::clearFilterAll()
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 9, 0))
beginFilterChange();
#endif
cardName.clear(); cardName.clear();
cardText.clear(); cardText.clear();
cardTypes.clear(); cardTypes.clear();
cardColors.clear(); cardColors.clear();
if (filterTree != nullptr) if (filterTree != nullptr)
filterTree->clear(); filterTree->clear();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0))
endFilterChange(QSortFilterProxyModel::Direction::Rows);
#else
invalidateFilter(); invalidateFilter();
#endif
} }
void CardDatabaseDisplayModel::setFilterTree(FilterTree *_filterTree) void CardDatabaseDisplayModel::setFilterTree(FilterTree *_filterTree)
@@ -224,4 +217,4 @@ const QString CardDatabaseDisplayModel::sanitizeCardName(const QString &dirtyNam
} }
} }
return QString::fromStdWString(toReturn); return QString::fromStdWString(toReturn);
} }

View File

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

View File

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

View File

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

View File

@@ -14,10 +14,10 @@ class TokenDisplayModel : public CardDatabaseDisplayModel
Q_OBJECT Q_OBJECT
public: public:
explicit TokenDisplayModel(QObject *parent = nullptr); explicit TokenDisplayModel(QObject *parent = nullptr);
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
protected: 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 #endif // COCKATRICE_TOKEN_DISPLAY_MODEL_H

View File

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

View File

@@ -14,10 +14,10 @@ class TokenEditModel : public CardDatabaseDisplayModel
Q_OBJECT Q_OBJECT
public: public:
explicit TokenEditModel(QObject *parent = nullptr); explicit TokenEditModel(QObject *parent = nullptr);
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
protected: 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 #endif // COCKATRICE_TOKEN_EDIT_MODEL_H

View File

@@ -1,7 +1,5 @@
#include "card_database_parser.h" #include "card_database_parser.h"
#include <libcockatrice/interfaces/noop_card_set_priority_controller.h>
SetNameMap ICardDatabaseParser::sets; SetNameMap ICardDatabaseParser::sets;
void ICardDatabaseParser::clearSetlist() void ICardDatabaseParser::clearSetlist()
@@ -19,7 +17,7 @@ CardSetPtr ICardDatabaseParser::internalAddSet(const QString &setName,
return sets.value(setName); return sets.value(setName);
} }
CardSetPtr newSet = CardSet::newInstance(new NoopCardSetPriorityController(), setName); CardSetPtr newSet = CardSet::newInstance(setName);
newSet->setLongName(longName); newSet->setLongName(longName);
newSet->setSetType(setType); newSet->setSetType(setType);
newSet->setReleaseDate(releaseDate); 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 "cockatrice_xml_3.h"
#include "../../relation/card_relation.h" #include "../../card/card_relation.h"
#include "../../relation/card_relation_type.h" #include "../../card/card_relation_type.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
@@ -282,13 +282,9 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
} }
properties.insert("colors", colors); properties.insert("colors", colors);
CardInfoPtr newCard =
CardInfo::UiAttributes attributes = {.cipt = cipt, CardInfo::newInstance(name, text, isToken, properties, relatedCards, reverseRelatedCards, _sets, cipt,
.landscapeOrientation = landscapeOrientation, landscapeOrientation, tableRow, upsideDown);
.tableRow = tableRow,
.upsideDownArt = upsideDown};
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
reverseRelatedCards, _sets, attributes);
emit addCard(newCard); emit addCard(newCard);
} }
} }
@@ -421,15 +417,14 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
} }
// positioning // positioning
const CardInfo::UiAttributes &attributes = info->getUiAttributes(); xml.writeTextElement("tablerow", QString::number(info->getTableRow()));
xml.writeTextElement("tablerow", QString::number(attributes.tableRow)); if (info->getCipt()) {
if (attributes.cipt) {
xml.writeTextElement("cipt", "1"); xml.writeTextElement("cipt", "1");
} }
if (attributes.landscapeOrientation) { if (info->getLandscapeOrientation()) {
xml.writeTextElement("landscapeOrientation", "1"); xml.writeTextElement("landscapeOrientation", "1");
} }
if (attributes.upsideDownArt) { if (info->getUpsideDownArt()) {
xml.writeTextElement("upsidedown", "1"); 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 "cockatrice_xml_4.h"
#include "../../relation/card_relation.h" #include "../../card/card_relation.h"
#include "../../settings/cache_settings.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
@@ -13,11 +14,6 @@
#define COCKATRICE_XML4_SCHEMALOCATION \ #define COCKATRICE_XML4_SCHEMALOCATION \
"https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v4/cards.xsd" "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) bool CockatriceXml4Parser::getCanParseFile(const QString &fileName, QIODevice &device)
{ {
qCInfo(CockatriceXml4Log) << "Trying to parse: " << fileName; qCInfo(CockatriceXml4Log) << "Trying to parse: " << fileName;
@@ -136,7 +132,7 @@ QVariantHash CockatriceXml4Parser::loadCardPropertiesFromXml(QXmlStreamReader &x
void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
{ {
bool includeRebalancedCards = cardPreferenceProvider->getIncludeRebalancedCards(); bool includeRebalancedCards = SettingsCache::instance().getIncludeRebalancedCards();
while (!xml.atEnd()) { while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) { if (xml.readNext() == QXmlStreamReader::EndElement) {
break; break;
@@ -262,12 +258,9 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
continue; continue;
} }
CardInfo::UiAttributes attributes = {.cipt = cipt, CardInfoPtr newCard =
.landscapeOrientation = landscapeOrientation, CardInfo::newInstance(name, text, isToken, properties, relatedCards, reverseRelatedCards, _sets, cipt,
.tableRow = tableRow, landscapeOrientation, tableRow, upsideDown);
.upsideDownArt = upsideDown};
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards,
reverseRelatedCards, _sets, attributes);
emit addCard(newCard); emit addCard(newCard);
} }
} }
@@ -382,15 +375,14 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
} }
// positioning // positioning
const CardInfo::UiAttributes &attributes = info->getUiAttributes(); xml.writeTextElement("tablerow", QString::number(info->getTableRow()));
xml.writeTextElement("tablerow", QString::number(attributes.tableRow)); if (info->getCipt()) {
if (attributes.cipt) {
xml.writeTextElement("cipt", "1"); xml.writeTextElement("cipt", "1");
} }
if (attributes.landscapeOrientation) { if (info->getLandscapeOrientation()) {
xml.writeTextElement("landscapeOrientation", "1"); xml.writeTextElement("landscapeOrientation", "1");
} }
if (attributes.upsideDownArt) { if (info->getUpsideDownArt()) {
xml.writeTextElement("upsidedown", "1"); 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 "custom_line_edit.h"
#include "../../../client/settings/cache_settings.h" #include "../settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h" #include "../settings/shortcuts_settings.h"
#include <QKeyEvent> #include <QKeyEvent>
#include <QLineEdit> #include <QLineEdit>

View File

@@ -1,15 +1,26 @@
#include "deck_list_model.h" #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) DeckListModel::DeckListModel(QObject *parent)
: QAbstractItemModel(parent), lastKnownColumn(1), lastKnownOrder(Qt::AscendingOrder) : 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 deckList = new DeckLoader;
// non-owning pointer and another deckList might have been assigned to it. deckList->setParent(this);
// `DeckListModel::cleanList` also leaks for the same reason. connect(deckList, &DeckLoader::deckLoaded, this, &DeckListModel::rebuildTree);
// TODO: fix the leak connect(deckList, &DeckLoader::deckHashChanged, this, &DeckListModel::deckHashChanged);
deckList = new DeckList;
root = new InnerDecklistNode; root = new InnerDecklistNode;
} }
@@ -98,95 +109,82 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const
return {}; return {};
} }
auto *node = static_cast<AbstractDecklistNode *>(index.internalPointer()); auto *temp = static_cast<AbstractDecklistNode *>(index.internalPointer());
auto *card = dynamic_cast<DecklistModelCardNode *>(node); auto *card = dynamic_cast<DecklistModelCardNode *>(temp);
if (card == nullptr) {
// Group node const auto *node = dynamic_cast<InnerDecklistNode *>(temp);
if (!card) {
const auto *group = dynamic_cast<InnerDecklistNode *>(node);
switch (role) { switch (role) {
case Qt::FontRole: {
QFont f;
f.setBold(true);
return f;
}
case Qt::DisplayRole: case Qt::DisplayRole:
case Qt::EditRole: { case Qt::EditRole: {
switch (index.column()) { switch (index.column()) {
case DeckListModelColumns::CARD_AMOUNT: case 0:
return group->recursiveCount(true); return node->recursiveCount(true);
case DeckListModelColumns::CARD_NAME: case 1: {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole)
return group->getVisibleName(); return node->getVisibleName();
} return node->getName();
return group->getName(); }
case DeckListModelColumns::CARD_SET: case 2: {
return group->getCardSetShortName(); return node->getCardSetShortName();
case DeckListModelColumns::CARD_COLLECTOR_NUMBER: }
return group->getCardCollectorNumber(); case 3: {
case DeckListModelColumns::CARD_PROVIDER_ID: return node->getCardCollectorNumber();
return group->getCardProviderId(); }
case 4: {
return node->getCardProviderId();
}
default: default:
return {}; return {};
} }
} }
case DeckRoles::IsCardRole: case Qt::UserRole + 1:
return false; return false;
case Qt::BackgroundRole: {
case DeckRoles::DepthRole: int color = 90 + 60 * node->depth();
return group->depth(); return QBrush(QColor(color, 255, color));
}
// legality does not apply to group nodes case Qt::ForegroundRole: {
case DeckRoles::IsLegalRole: return QBrush(QColor(0, 0, 0));
return true; }
default: default:
return {}; return {};
} }
} } else {
switch (role) {
// Card node case Qt::DisplayRole:
switch (role) { case Qt::EditRole: {
case Qt::DisplayRole: switch (index.column()) {
case Qt::EditRole: case 0:
switch (index.column()) { return card->getNumber();
case DeckListModelColumns::CARD_AMOUNT: case 1:
return card->getNumber(); return card->getName();
case DeckListModelColumns::CARD_NAME: case 2:
return card->getName(); return card->getCardSetShortName();
case DeckListModelColumns::CARD_SET: case 3:
return card->getCardSetShortName(); return card->getCardCollectorNumber();
case DeckListModelColumns::CARD_COLLECTOR_NUMBER: case 4:
return card->getCardCollectorNumber(); return card->getCardProviderId();
case DeckListModelColumns::CARD_PROVIDER_ID: default:
return card->getCardProviderId(); return {};
default: }
return {};
} }
case Qt::UserRole + 1:
case DeckRoles::IsCardRole: { return true;
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) { switch (section) {
case DeckListModelColumns::CARD_AMOUNT: case 0:
return tr("Count"); return tr("Count");
case DeckListModelColumns::CARD_NAME: case 1:
return tr("Card"); return tr("Card");
case DeckListModelColumns::CARD_SET: case 2:
return tr("Set"); return tr("Set");
case DeckListModelColumns::CARD_COLLECTOR_NUMBER: case 3:
return tr("Number"); return tr("Number");
case DeckListModelColumns::CARD_PROVIDER_ID: case 4:
return tr("Provider ID"); return tr("Provider ID");
default: default:
return {}; return {};
@@ -266,19 +264,19 @@ bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, con
} }
switch (index.column()) { switch (index.column()) {
case DeckListModelColumns::CARD_AMOUNT: case 0:
node->setNumber(value.toInt()); node->setNumber(value.toInt());
break; break;
case DeckListModelColumns::CARD_NAME: case 1:
node->setName(value.toString()); node->setName(value.toString());
break; break;
case DeckListModelColumns::CARD_SET: case 2:
node->setCardSetShortName(value.toString()); node->setCardSetShortName(value.toString());
break; break;
case DeckListModelColumns::CARD_COLLECTOR_NUMBER: case 3:
node->setCardCollectorNumber(value.toString()); node->setCardCollectorNumber(value.toString());
break; break;
case DeckListModelColumns::CARD_PROVIDER_ID: case 4:
node->setCardProviderId(value.toString()); node->setCardProviderId(value.toString());
break; break;
default: default:
@@ -287,7 +285,6 @@ bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, con
emitRecursiveUpdates(index); emitRecursiveUpdates(index);
deckList->refreshDeckHash(); deckList->refreshDeckHash();
emit deckHashChanged();
emit dataChanged(index, index); emit dataChanged(index, index);
return true; return true;
@@ -426,7 +423,6 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam
cardNode->setCardCollectorNumber(printingInfo.getProperty("num")); cardNode->setCardCollectorNumber(printingInfo.getProperty("num"));
cardNode->setCardProviderId(printingInfo.getProperty("uuid")); cardNode->setCardProviderId(printingInfo.getProperty("uuid"));
deckList->refreshDeckHash(); deckList->refreshDeckHash();
emit deckHashChanged();
} }
sort(lastKnownColumn, lastKnownOrder); sort(lastKnownColumn, lastKnownOrder);
emitRecursiveUpdates(parentIndex); emitRecursiveUpdates(parentIndex);
@@ -526,7 +522,7 @@ void DeckListModel::sort(int column, Qt::SortOrder order)
emit layoutChanged(); emit layoutChanged();
} }
void DeckListModel::setActiveGroupCriteria(DeckListModelGroupCriteria::Type newCriteria) void DeckListModel::setActiveGroupCriteria(DeckListModelGroupCriteria newCriteria)
{ {
activeGroupCriteria = newCriteria; activeGroupCriteria = newCriteria;
rebuildTree(); rebuildTree();
@@ -534,17 +530,19 @@ void DeckListModel::setActiveGroupCriteria(DeckListModelGroupCriteria::Type newC
void DeckListModel::cleanList() 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->deleteLater();
deckList = _deck; deckList = _deck;
} deckList->setParent(this);
connect(deckList, &DeckLoader::deckLoaded, this, &DeckListModel::rebuildTree);
connect(deckList, &DeckLoader::deckHashChanged, this, &DeckListModel::deckHashChanged);
rebuildTree(); rebuildTree();
} }
@@ -632,4 +630,98 @@ QList<QString> *DeckListModel::getZones() const
zones->append(currentZone->getName()); zones->append(currentZone->getName());
} }
return zones; 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 #ifndef DECKLISTMODEL_H
#define DECKLISTMODEL_H #define DECKLISTMODEL_H
#include <../../../../libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_card_node.h> #include "../card/exact_card.h"
#include <../../../../libcockatrice_deck_list/libcockatrice/deck_list/tree/deck_list_card_node.h> #include "abstract_deck_list_card_node.h"
#include "deck_list.h"
#include "deck_list_card_node.h"
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QList> #include <QList>
#include <libcockatrice/card/printing/exact_card.h>
#include <libcockatrice/deck_list/deck_list.h>
class DeckLoader;
class CardDatabase; class CardDatabase;
class QPrinter; class QPrinter;
class QTextCursor; class QTextCursor;
/** /**
* @namespace DeckRoles * @brief Specifies the criteria used to group cards in the DeckListModel.
* @brief Custom model roles used by the DeckListModel for data retrieval.
*
* These roles extend Qt's item data roles starting at Qt::UserRole.
*/ */
namespace DeckRoles enum DeckListModelGroupCriteria
{
/**
* @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
{ {
MAIN_TYPE, /**< Group cards by their main type (e.g., creature, instant). */ 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. */ 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 * @class DecklistModelCardNode
@@ -124,7 +49,7 @@ public:
: AbstractDecklistCardNode(_parent, position), dataNode(_dataNode) : AbstractDecklistCardNode(_parent, position), dataNode(_dataNode)
{ {
} }
[[nodiscard]] int getNumber() const override int getNumber() const override
{ {
return dataNode->getNumber(); return dataNode->getNumber();
} }
@@ -132,7 +57,7 @@ public:
{ {
dataNode->setNumber(_number); dataNode->setNumber(_number);
} }
[[nodiscard]] QString getName() const override QString getName() const override
{ {
return dataNode->getName(); return dataNode->getName();
} }
@@ -140,7 +65,7 @@ public:
{ {
dataNode->setName(_name); dataNode->setName(_name);
} }
[[nodiscard]] QString getCardProviderId() const override QString getCardProviderId() const override
{ {
return dataNode->getCardProviderId(); return dataNode->getCardProviderId();
} }
@@ -148,7 +73,7 @@ public:
{ {
dataNode->setCardProviderId(_cardProviderId); dataNode->setCardProviderId(_cardProviderId);
} }
[[nodiscard]] QString getCardSetShortName() const override QString getCardSetShortName() const override
{ {
return dataNode->getCardSetShortName(); return dataNode->getCardSetShortName();
} }
@@ -156,7 +81,7 @@ public:
{ {
dataNode->setCardSetShortName(_cardSetShortName); dataNode->setCardSetShortName(_cardSetShortName);
} }
[[nodiscard]] QString getCardCollectorNumber() const override QString getCardCollectorNumber() const override
{ {
return dataNode->getCardCollectorNumber(); return dataNode->getCardCollectorNumber();
} }
@@ -169,7 +94,7 @@ public:
* @brief Returns the underlying data node. * @brief Returns the underlying data node.
* @return Pointer to the DecklistCardNode wrapped by this node. * @return Pointer to the DecklistCardNode wrapped by this node.
*/ */
[[nodiscard]] DecklistCardNode *getDataNode() const DecklistCardNode *getDataNode() const
{ {
return dataNode; return dataNode;
} }
@@ -195,12 +120,12 @@ public:
* *
* Slots: * Slots:
* - rebuildTree(): rebuilds the model structure from the underlying DeckLoader. * - rebuildTree(): rebuilds the model structure from the underlying DeckLoader.
* - printDeckList(): renders the decklist to a QPrinter.
*/ */
class DeckListModel : public QAbstractItemModel class DeckListModel : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
private slots:
public slots:
/** /**
* @brief Rebuilds the model tree from the underlying DeckLoader. * @brief Rebuilds the model tree from the underlying DeckLoader.
* *
@@ -209,6 +134,13 @@ public slots:
*/ */
void rebuildTree(); 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: signals:
/** /**
* @brief Emitted whenever the deck hash changes due to modifications in the model. * @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. * @brief Returns the root index of the model.
* @return QModelIndex representing the root node. * @return QModelIndex representing the root node.
*/ */
[[nodiscard]] QModelIndex getRoot() const QModelIndex getRoot() const
{ {
return nodeToIndex(root); return nodeToIndex(root);
} };
/** /**
* @brief Returns the value of the grouping category for a card based on the current criteria. * @brief Returns the value of the grouping category for a card based on the current criteria.
* @param info Pointer to card information. * @param info Pointer to card information.
* @return String representing the value of the current grouping criteria for the card. * @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 // Qt model overrides
[[nodiscard]] int rowCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override;
[[nodiscard]] int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override; int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override;
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex &index, int role) const override;
void emitBackgroundUpdates(const QModelIndex &parent); QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent) const override;
[[nodiscard]] QModelIndex index(int row, int column, const QModelIndex &parent) const override; QModelIndex parent(const QModelIndex &index) const override;
[[nodiscard]] QModelIndex parent(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex &index) const override;
[[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override; bool setData(const QModelIndex &index, const QVariant &value, int role) override;
bool removeRows(int row, int count, const QModelIndex &parent) override; bool removeRows(int row, int count, const QModelIndex &parent) override;
@@ -255,10 +186,10 @@ public:
* @param cardNumber Optional collector number. * @param cardNumber Optional collector number.
* @return QModelIndex of the card, or invalid index if not found. * @return QModelIndex of the card, or invalid index if not found.
*/ */
[[nodiscard]] QModelIndex findCard(const QString &cardName, QModelIndex findCard(const QString &cardName,
const QString &zoneName, const QString &zoneName,
const QString &providerId = "", const QString &providerId = "",
const QString &cardNumber = "") const; const QString &cardNumber = "") const;
/** /**
* @brief Adds a card using the preferred printing if available. * @brief Adds a card using the preferred printing if available.
@@ -292,38 +223,40 @@ public:
* @brief Removes all cards and resets the model. * @brief Removes all cards and resets the model.
*/ */
void cleanList(); void cleanList();
[[nodiscard]] DeckList *getDeckList() const DeckLoader *getDeckList() const
{ {
return deckList; return deckList;
} }
void setDeckList(DeckList *_deck); void setDeckList(DeckLoader *_deck);
[[nodiscard]] QList<ExactCard> getCards() const; QList<ExactCard> getCards() const;
[[nodiscard]] QList<ExactCard> getCardsForZone(const QString &zoneName) const; QList<ExactCard> getCardsForZone(const QString &zoneName) const;
[[nodiscard]] QList<QString> *getZones() const; QList<QString> *getZones() const;
/** /**
* @brief Sets the criteria used to group cards in the model. * @brief Sets the criteria used to group cards in the model.
* @param newCriteria The new grouping criteria. * @param newCriteria The new grouping criteria.
*/ */
void setActiveGroupCriteria(DeckListModelGroupCriteria::Type newCriteria); void setActiveGroupCriteria(DeckListModelGroupCriteria newCriteria);
private: 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. */ 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. */ int lastKnownColumn; /**< Last column used for sorting. */
Qt::SortOrder lastKnownOrder; /**< Last known sort order. */ Qt::SortOrder lastKnownOrder; /**< Last known sort order. */
InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent); InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent);
QModelIndex nodeToIndex(AbstractDecklistNode *node) const; QModelIndex nodeToIndex(AbstractDecklistNode *node) const;
[[nodiscard]] DecklistModelCardNode *findCardNode(const QString &cardName, DecklistModelCardNode *findCardNode(const QString &cardName,
const QString &zoneName, const QString &zoneName,
const QString &providerId = "", const QString &providerId = "",
const QString &cardNumber = "") const; const QString &cardNumber = "") const;
void emitRecursiveUpdates(const QModelIndex &index); void emitRecursiveUpdates(const QModelIndex &index);
void sortHelper(InnerDecklistNode *node, Qt::SortOrder order); void sortHelper(InnerDecklistNode *node, Qt::SortOrder order);
void printDeckListNode(QTextCursor *cursor, InnerDecklistNode *node);
template <typename T> T getNode(const QModelIndex &index) const template <typename T> T getNode(const QModelIndex &index) const
{ {
if (!index.isValid()) if (!index.isValid())

View File

@@ -1,5 +1,11 @@
#include "deck_loader.h" #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 <QApplication>
#include <QClipboard> #include <QClipboard>
#include <QDebug> #include <QDebug>
@@ -7,29 +13,32 @@
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QPrinter>
#include <QRegularExpression> #include <QRegularExpression>
#include <QStringList> #include <QStringList>
#include <QTextCursor>
#include <QTextDocument>
#include <QTextStream>
#include <QTextTable>
#include <QtConcurrentRun> #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::ACCEPTED_FILE_EXTENSIONS = {"*.cod", "*.dec", "*.dek", "*.txt", "*.mwDeck"};
const QStringList DeckLoader::FILE_NAME_FILTERS = { const QStringList DeckLoader::FILE_NAME_FILTERS = {
tr("Common deck formats (%1)").arg(ACCEPTED_FILE_EXTENSIONS.join(" ")), tr("All files (*.*)")}; 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; bool result = false;
switch (fmt) { switch (fmt) {
case PlainTextFormat: case PlainTextFormat:
result = deckList->loadFromFile_Plain(&file); result = loadFromFile_Plain(&file);
break; break;
case CockatriceFormat: { case CockatriceFormat: {
result = deckList->loadFromFile_Native(&file); result = loadFromFile_Native(&file);
qCInfo(DeckLoaderLog) << "Loaded from" << fileName << "-" << result; qCInfo(DeckLoaderLog) << "Loaded from" << fileName << "-" << result;
if (!result) { if (!result) {
qCInfo(DeckLoaderLog) << "Retrying as plain format"; qCInfo(DeckLoaderLog) << "Retrying as plain format";
file.seek(0); file.seek(0);
result = deckList->loadFromFile_Plain(&file); result = loadFromFile_Plain(&file);
fmt = PlainTextFormat; fmt = PlainTextFormat;
} }
break; break;
@@ -62,10 +71,8 @@ bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt, bool user
} }
if (result) { if (result) {
lastLoadInfo = { lastFileName = fileName;
.fileName = fileName, lastFileFormat = fmt;
.fileFormat = fmt,
};
if (userRequest) { if (userRequest) {
updateLastLoadedTimestamp(fileName, fmt); updateLastLoadedTimestamp(fileName, fmt);
} }
@@ -86,10 +93,8 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, FileFormat fmt, bool
watcher->deleteLater(); watcher->deleteLater();
if (result) { if (result) {
lastLoadInfo = { lastFileName = fileName;
.fileName = fileName, lastFileFormat = fmt;
.fileFormat = fmt,
};
if (userRequest) { if (userRequest) {
updateLastLoadedTimestamp(fileName, fmt); updateLastLoadedTimestamp(fileName, fmt);
} }
@@ -107,13 +112,13 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, FileFormat fmt, bool
switch (fmt) { switch (fmt) {
case PlainTextFormat: case PlainTextFormat:
return deckList->loadFromFile_Plain(&file); return loadFromFile_Plain(&file);
case CockatriceFormat: { case CockatriceFormat: {
bool result = false; bool result = false;
result = deckList->loadFromFile_Native(&file); result = loadFromFile_Native(&file);
if (!result) { if (!result) {
file.seek(0); file.seek(0);
return deckList->loadFromFile_Plain(&file); return loadFromFile_Plain(&file);
} }
return result; return result;
} }
@@ -129,11 +134,11 @@ bool DeckLoader::loadFromFileAsync(const QString &fileName, FileFormat fmt, bool
bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId) bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId)
{ {
bool result = deckList->loadFromString_Native(nativeString); bool result = loadFromString_Native(nativeString);
if (result) { if (result) {
lastLoadInfo = { lastFileName = QString();
.remoteDeckId = remoteDeckId, lastFileFormat = CockatriceFormat;
}; lastRemoteDeckId = remoteDeckId;
emit deckLoaded(); emit deckLoaded();
} }
@@ -150,20 +155,16 @@ bool DeckLoader::saveToFile(const QString &fileName, FileFormat fmt)
bool result = false; bool result = false;
switch (fmt) { switch (fmt) {
case PlainTextFormat: case PlainTextFormat:
result = deckList->saveToFile_Plain(&file); result = saveToFile_Plain(&file);
break; break;
case CockatriceFormat: case CockatriceFormat:
result = deckList->saveToFile_Native(&file); result = saveToFile_Native(&file);
qCInfo(DeckLoaderLog) << "Saving to " << fileName << "-" << result;
break; break;
} }
if (result) { if (result) {
lastLoadInfo = { lastFileName = fileName;
.fileName = fileName, lastFileFormat = fmt;
.fileFormat = fmt,
};
qCInfo(DeckLoaderLog) << "Deck was saved -" << result;
} }
file.flush(); file.flush();
@@ -194,21 +195,19 @@ bool DeckLoader::updateLastLoadedTimestamp(const QString &fileName, FileFormat f
// Perform file modifications // Perform file modifications
switch (fmt) { switch (fmt) {
case PlainTextFormat: case PlainTextFormat:
result = deckList->saveToFile_Plain(&file); result = saveToFile_Plain(&file);
break; break;
case CockatriceFormat: case CockatriceFormat:
deckList->setLastLoadedTimestamp(QDateTime::currentDateTime().toString()); setLastLoadedTimestamp(QDateTime::currentDateTime().toString());
result = deckList->saveToFile_Native(&file); result = saveToFile_Native(&file);
break; break;
} }
file.close(); // Close the file to ensure changes are flushed file.close(); // Close the file to ensure changes are flushed
if (result) { if (result) {
lastLoadInfo = { lastFileName = fileName;
.fileName = fileName, lastFileFormat = fmt;
.fileFormat = fmt,
};
// Re-open the file and set the original timestamp // Re-open the file and set the original timestamp
if (!file.open(QIODevice::ReadWrite)) { 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 * 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 * @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 // Add the base url
QString deckString = "https://" + getDomainForWebsite(website) + "/?"; 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 // 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 // Remove the extra return at the end of the last cards
mainBoardCards.chop(3); mainBoardCards.chop(3);
sideBoardCards.chop(3); sideBoardCards.chop(3);
@@ -342,24 +339,20 @@ struct SetProviderIdToPreferred
/** /**
* This function iterates through each card in the decklist and sets the providerId * This function iterates through each card in the decklist and sets the providerId
* on each card based on its set name and collector number. * 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. // Set up the struct to call.
SetProviderIdToPreferred setProviderIdToPreferred; SetProviderIdToPreferred setProviderIdToPreferred;
// Call the forEachCard method for each card in the deck // 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. * 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) { auto setProviderId = [](const auto node, const auto card) {
Q_UNUSED(node); Q_UNUSED(node);
@@ -374,7 +367,7 @@ void DeckLoader::resolveSetNameAndNumberToProviderID(const DeckList *deckList)
card->setCardProviderId(providerId); card->setCardProviderId(providerId);
}; };
deckList->forEachCard(setProviderId); forEachCard(setProviderId);
} }
// This struct is here to support the forEachCard function call, defined in decklist. // 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. * 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) { auto clearSetNameAndNumber = [](const auto node, auto card) {
Q_UNUSED(node) Q_UNUSED(node)
@@ -411,7 +402,7 @@ void DeckLoader::clearSetNamesAndNumbers(const DeckList *deckList)
card->setCardProviderId(nullptr); card->setCardProviderId(nullptr);
}; };
deckList->forEachCard(clearSetNameAndNumber); forEachCard(clearSetNameAndNumber);
} }
DeckLoader::FileFormat DeckLoader::getFormatFromName(const QString &fileName) DeckLoader::FileFormat DeckLoader::getFormatFromName(const QString &fileName)
@@ -422,27 +413,24 @@ DeckLoader::FileFormat DeckLoader::getFormatFromName(const QString &fileName)
return PlainTextFormat; return PlainTextFormat;
} }
void DeckLoader::saveToClipboard(const DeckList *deckList, bool addComments, bool addSetNameAndNumber) void DeckLoader::saveToClipboard(bool addComments, bool addSetNameAndNumber) const
{ {
QString buffer; QString buffer;
QTextStream stream(&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::Clipboard);
QApplication::clipboard()->setText(buffer, QClipboard::Selection); QApplication::clipboard()->setText(buffer, QClipboard::Selection);
} }
bool DeckLoader::saveToStream_Plain(QTextStream &out, bool DeckLoader::saveToStream_Plain(QTextStream &out, bool addComments, bool addSetNameAndNumber) const
const DeckList *deckList,
bool addComments,
bool addSetNameAndNumber)
{ {
if (addComments) { if (addComments) {
saveToStream_DeckHeader(out, deckList); saveToStream_DeckHeader(out);
} }
// loop zones // loop zones
for (int i = 0; i < deckList->getRoot()->size(); i++) { for (int i = 0; i < getRoot()->size(); i++) {
const auto *zoneNode = dynamic_cast<InnerDecklistNode *>(deckList->getRoot()->at(i)); const auto *zoneNode = dynamic_cast<InnerDecklistNode *>(getRoot()->at(i));
saveToStream_DeckZone(out, zoneNode, addComments, addSetNameAndNumber); saveToStream_DeckZone(out, zoneNode, addComments, addSetNameAndNumber);
@@ -453,14 +441,14 @@ bool DeckLoader::saveToStream_Plain(QTextStream &out,
return true; return true;
} }
void DeckLoader::saveToStream_DeckHeader(QTextStream &out, const DeckList *deckList) void DeckLoader::saveToStream_DeckHeader(QTextStream &out) const
{ {
if (!deckList->getName().isEmpty()) { if (!getName().isEmpty()) {
out << "// " << deckList->getName() << "\n\n"; out << "// " << getName() << "\n\n";
} }
if (!deckList->getComments().isEmpty()) { if (!getComments().isEmpty()) {
QStringList commentRows = deckList->getComments().split(QRegularExpression("\n|\r\n|\r")); QStringList commentRows = getComments().split(QRegularExpression("\n|\r\n|\r"));
for (const QString &row : commentRows) { for (const QString &row : commentRows) {
out << "// " << row << "\n"; out << "// " << row << "\n";
} }
@@ -471,7 +459,7 @@ void DeckLoader::saveToStream_DeckHeader(QTextStream &out, const DeckList *deckL
void DeckLoader::saveToStream_DeckZone(QTextStream &out, void DeckLoader::saveToStream_DeckZone(QTextStream &out,
const InnerDecklistNode *zoneNode, const InnerDecklistNode *zoneNode,
bool addComments, bool addComments,
bool addSetNameAndNumber) bool addSetNameAndNumber) const
{ {
// group cards by card type and count the subtotals // group cards by card type and count the subtotals
QMultiMap<QString, DecklistCardNode *> cardsByType; QMultiMap<QString, DecklistCardNode *> cardsByType;
@@ -519,7 +507,7 @@ void DeckLoader::saveToStream_DeckZoneCards(QTextStream &out,
const InnerDecklistNode *zoneNode, const InnerDecklistNode *zoneNode,
QList<DecklistCardNode *> cards, QList<DecklistCardNode *> cards,
bool addComments, bool addComments,
bool addSetNameAndNumber) bool addSetNameAndNumber) const
{ {
// QMultiMap sorts values in reverse order // QMultiMap sorts values in reverse order
for (int i = cards.size() - 1; i >= 0; --i) { for (int i = cards.size() - 1; i >= 0; --i) {
@@ -567,7 +555,7 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName)
switch (getFormatFromName(fileName)) { switch (getFormatFromName(fileName)) {
case PlainTextFormat: case PlainTextFormat:
// Save in Cockatrice's native format // Save in Cockatrice's native format
result = deckList->saveToFile_Native(&file); result = saveToFile_Native(&file);
break; break;
case CockatriceFormat: case CockatriceFormat:
qCInfo(DeckLoaderLog) << "File is already in Cockatrice format. No conversion needed."; qCInfo(DeckLoaderLog) << "File is already in Cockatrice format. No conversion needed.";
@@ -588,16 +576,14 @@ bool DeckLoader::convertToCockatriceFormat(QString fileName)
} else { } else {
qCInfo(DeckLoaderLog) << "Original file deleted successfully:" << fileName; qCInfo(DeckLoaderLog) << "Original file deleted successfully:" << fileName;
} }
lastLoadInfo = { lastFileName = newFileName;
.fileName = newFileName, lastFileFormat = CockatriceFormat;
.fileFormat = CockatriceFormat,
};
} }
return result; return result;
} }
QString DeckLoader::getCardZoneFromName(const QString &cardName, QString currentZoneName) QString DeckLoader::getCardZoneFromName(QString cardName, QString currentZoneName)
{ {
CardInfoPtr card = CardDatabaseManager::query()->getCardInfo(cardName); CardInfoPtr card = CardDatabaseManager::query()->getCardInfo(cardName);
@@ -608,7 +594,7 @@ QString DeckLoader::getCardZoneFromName(const QString &cardName, QString current
return currentZoneName; return currentZoneName;
} }
QString DeckLoader::getCompleteCardName(const QString &cardName) QString DeckLoader::getCompleteCardName(const QString &cardName) const
{ {
if (CardDatabaseManager::getInstance()) { if (CardDatabaseManager::getInstance()) {
ExactCard temp = CardDatabaseManager::query()->guessCard({cardName}); ExactCard temp = CardDatabaseManager::query()->guessCard({cardName});
@@ -619,97 +605,3 @@ QString DeckLoader::getCompleteCardName(const QString &cardName)
return 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_stats_interface.h"
#include "deck_list.h"
#include "deck_list_card_node.h"
#include <QDesktopServices> #include <QDesktopServices>
#include <QMessageBox> #include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QRegularExpression> #include <QRegularExpression>
#include <QUrlQuery> #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) DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent)
: QObject(parent), cardDatabase(_cardDatabase) : QObject(parent), cardDatabase(_cardDatabase)

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