Compare commits
381 Commits
2025-05-05
...
tooomm-dox
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
067d9797be | ||
|
|
d6db21419c | ||
|
|
367507e054 | ||
|
|
715ee1d6fe | ||
|
|
ad06a81765 | ||
|
|
ebb02b27b2 | ||
|
|
d47dc35885 | ||
|
|
41aca8467a | ||
|
|
cd44392866 | ||
|
|
64bb5355ff | ||
|
|
1198db8891 | ||
|
|
9471adb4f7 | ||
|
|
b29909bdbe | ||
|
|
589e9a15a6 | ||
|
|
c218a66bcd | ||
|
|
8485bbe575 | ||
|
|
5d9d7d3aa5 | ||
|
|
ccdda39e78 | ||
|
|
2e2682aad4 | ||
|
|
a390c8ada7 | ||
|
|
da70344547 | ||
|
|
2b690f8c87 | ||
|
|
c8b419888a | ||
|
|
d3302d521f | ||
|
|
5c1bb27d5c | ||
|
|
dde36183ce | ||
|
|
7c7f2dd8d5 | ||
|
|
edb0a954e2 | ||
|
|
0a239712dd | ||
|
|
95c3434205 | ||
|
|
f0be6972cc | ||
|
|
a799cd097a | ||
|
|
b4e3f2cba9 | ||
|
|
658ae83157 | ||
|
|
d29e72ce72 | ||
|
|
30cc8ad6f9 | ||
|
|
f0ebd28148 | ||
|
|
364d0ca52b | ||
|
|
3ff2df2796 | ||
|
|
d57bec8ec6 | ||
|
|
2b64e65f45 | ||
|
|
eab4d435f8 | ||
|
|
de13c22552 | ||
|
|
8ee7163014 | ||
|
|
c5fde071e7 | ||
|
|
8abd04dab1 | ||
|
|
858361e6d3 | ||
|
|
9ece4bfd9b | ||
|
|
a1a3b02d3a | ||
|
|
bc2ae6c486 | ||
|
|
587a8bc524 | ||
|
|
122926c6cd | ||
|
|
bac6beeb50 | ||
|
|
c75a483ee6 | ||
|
|
1c5bfdbabe | ||
|
|
553952132f | ||
|
|
1931eb11a9 | ||
|
|
65aef396fb | ||
|
|
a21e45ed36 | ||
|
|
adee67115c | ||
|
|
aea468bc7f | ||
|
|
621c6a8d73 | ||
|
|
73591d5d0f | ||
|
|
846f16ddaa | ||
|
|
c46f6d1178 | ||
|
|
ab5d6db8a2 | ||
|
|
9957cb20e2 | ||
|
|
8788a7aada | ||
|
|
16392c28c5 | ||
|
|
a8ee0d7648 | ||
|
|
a405758222 | ||
|
|
537e29d937 | ||
|
|
9a3104c5ac | ||
|
|
722344967f | ||
|
|
73ce5e051c | ||
|
|
b8bbe141a0 | ||
|
|
3285596a93 | ||
|
|
73763b5ee6 | ||
|
|
27708d5964 | ||
|
|
827f22ed37 | ||
|
|
ace4063371 | ||
|
|
f62e29f5d5 | ||
|
|
5df00de246 | ||
|
|
28dfd62163 | ||
|
|
1c1599a9f4 | ||
|
|
6dff230e10 | ||
|
|
0f60824749 | ||
|
|
84e0732fb1 | ||
|
|
ae123587d7 | ||
|
|
2efcb48b7e | ||
|
|
3d9cae717d | ||
|
|
cc73a8cc85 | ||
|
|
648f028a63 | ||
|
|
840ee1379f | ||
|
|
3c85ca9cbc | ||
|
|
8e88749078 | ||
|
|
4c431e98a6 | ||
|
|
40cf3ced1a | ||
|
|
c9ccab8771 | ||
|
|
7d2700ca65 | ||
|
|
bfedc12fa8 | ||
|
|
c16267e60f | ||
|
|
0bd9b84931 | ||
|
|
e9a9475ed7 | ||
|
|
f00d415dd7 | ||
|
|
1e7ff3dbdf | ||
|
|
eb1c257484 | ||
|
|
4d652210dc | ||
|
|
9f2ac78609 | ||
|
|
484e8e64a6 | ||
|
|
e5d5dfa8d8 | ||
|
|
0ad31fea46 | ||
|
|
ec2d8f231d | ||
|
|
aeec56f800 | ||
|
|
7e6cad974f | ||
|
|
757e9f3415 | ||
|
|
6bc2293292 | ||
|
|
55aaca0e0d | ||
|
|
a8a3fca8c9 | ||
|
|
fb30515f72 | ||
|
|
9a39af6da0 | ||
|
|
6d75ce4b1c | ||
|
|
dbd1d30ca8 | ||
|
|
8f80996515 | ||
|
|
d206a70b8a | ||
|
|
bbec4d2c7e | ||
|
|
f24c36d6b1 | ||
|
|
adff828415 | ||
|
|
d914667238 | ||
|
|
1c209b3320 | ||
|
|
aa61032cdf | ||
|
|
3ae4a7d8a7 | ||
|
|
9fdecf21f2 | ||
|
|
e4d256790f | ||
|
|
d9f4faf4ec | ||
|
|
609a364971 | ||
|
|
2152ddd99b | ||
|
|
8caaf8515e | ||
|
|
ac822fa084 | ||
|
|
a265b865f6 | ||
|
|
8efc4f4817 | ||
|
|
817a3f979e | ||
|
|
8ebfc40de5 | ||
|
|
c42e953199 | ||
|
|
636aa72141 | ||
|
|
14e6e6eff4 | ||
|
|
474c1d0d89 | ||
|
|
b8983f27ab | ||
|
|
d9c65d4ae0 | ||
|
|
1ef07309d6 | ||
|
|
be1403c920 | ||
|
|
03e32f0a7c | ||
|
|
f4361d1b43 | ||
|
|
e1259e67d3 | ||
|
|
ca1b9bf75f | ||
|
|
3cff55b0bb | ||
|
|
c25b153185 | ||
|
|
9c58e6f90f | ||
|
|
cff16346ef | ||
|
|
30e6b52783 | ||
|
|
015570c833 | ||
|
|
7c31197b78 | ||
|
|
a69bfb8cb8 | ||
|
|
c5b361e94d | ||
|
|
201750c89f | ||
|
|
89a8d0f6b8 | ||
|
|
835e4af3e4 | ||
|
|
c33106eab4 | ||
|
|
bea8c3dbec | ||
|
|
b51d5d007b | ||
|
|
f8c4f774cf | ||
|
|
22c6756ce0 | ||
|
|
e318815025 | ||
|
|
0833f94502 | ||
|
|
ddbf5e1457 | ||
|
|
2a032f3116 | ||
|
|
5381562a5e | ||
|
|
f2ce5e9693 | ||
|
|
ed50fd98cd | ||
|
|
14991e1f9e | ||
|
|
5fa06746f1 | ||
|
|
d31b044529 | ||
|
|
754dd904d2 | ||
|
|
1503394662 | ||
|
|
436d69b710 | ||
|
|
891e7bf6e4 | ||
|
|
fad1280185 | ||
|
|
6187c7268f | ||
|
|
762ea47b8e | ||
|
|
23612ba6ec | ||
|
|
217646f031 | ||
|
|
91667d9ecd | ||
|
|
3501ee9a9d | ||
|
|
f0c3860032 | ||
|
|
17dcaf9afa | ||
|
|
f484c98152 | ||
|
|
46f68115b2 | ||
|
|
7ac22a6ce8 | ||
|
|
bed79ef89e | ||
|
|
54095b9a89 | ||
|
|
4b58060ab6 | ||
|
|
dbbb554735 | ||
|
|
9c3be1b851 | ||
|
|
190ab211e3 | ||
|
|
f4fbe90a72 | ||
|
|
a9cbd5a172 | ||
|
|
94ba1c83c6 | ||
|
|
9b3756e591 | ||
|
|
aff775f488 | ||
|
|
4de5274996 | ||
|
|
4e57868037 | ||
|
|
ab6b32b8ba | ||
|
|
46285a499e | ||
|
|
ce6cad5dfe | ||
|
|
d5ea86bc81 | ||
|
|
41ea424359 | ||
|
|
87b0259b97 | ||
|
|
2490e97ea0 | ||
|
|
eecfe9d387 | ||
|
|
9ca5ee52e7 | ||
|
|
fb23cc8c7a | ||
|
|
ff7ce39841 | ||
|
|
0f05d6bd74 | ||
|
|
93c15c8151 | ||
|
|
22c8268f02 | ||
|
|
216cd491cc | ||
|
|
5efc573783 | ||
|
|
bca0da6bd4 | ||
|
|
9601a1fa4e | ||
|
|
b8e545bfa4 | ||
|
|
5c16f0d027 | ||
|
|
1b4441baac | ||
|
|
0147a1d41f | ||
|
|
0f11fbe599 | ||
|
|
9c18e99fe2 | ||
|
|
6e0a7de9cc | ||
|
|
b141a65838 | ||
|
|
7f842bb1e8 | ||
|
|
bd65aae81e | ||
|
|
b8dedb568c | ||
|
|
ec94c29ed9 | ||
|
|
c77943d01c | ||
|
|
fc5fb956df | ||
|
|
2eba126ed7 | ||
|
|
da52d677c7 | ||
|
|
ab4373d025 | ||
|
|
5e88a0f0cc | ||
|
|
ba794c2b60 | ||
|
|
268559d8de | ||
|
|
473d147333 | ||
|
|
d5d9f9bedc | ||
|
|
f31d30bf84 | ||
|
|
03b216a6b4 | ||
|
|
3e6510b935 | ||
|
|
e87b35e0bb | ||
|
|
322fdb14de | ||
|
|
09381575a7 | ||
|
|
881243da6a | ||
|
|
851fad3e3f | ||
|
|
46d65f0b7e | ||
|
|
03bebbe4c2 | ||
|
|
1649f30389 | ||
|
|
38f76d449a | ||
|
|
f2cbdae829 | ||
|
|
3a42354efd | ||
|
|
fe7853a389 | ||
|
|
06738cae93 | ||
|
|
d6243a2dd2 | ||
|
|
04be0fe634 | ||
|
|
fd12a1f6be | ||
|
|
e10dd4ef42 | ||
|
|
62c02e3fce | ||
|
|
ae2c55c33b | ||
|
|
4a2a646943 | ||
|
|
ae47ee802b | ||
|
|
4fd2f1f974 | ||
|
|
b9f16e8cce | ||
|
|
70b4843bc4 | ||
|
|
95190c321c | ||
|
|
a9b3be33e0 | ||
|
|
e05dad4267 | ||
|
|
83b90d472f | ||
|
|
ee4ff6e732 | ||
|
|
2267d38352 | ||
|
|
4fbb47300e | ||
|
|
836e168a6c | ||
|
|
a9684f67cc | ||
|
|
686e90d0ed | ||
|
|
0b9b39fef7 | ||
|
|
67a3b03b07 | ||
|
|
388db4e995 | ||
|
|
a28a1aa601 | ||
|
|
ed82106359 | ||
|
|
c57b84cb17 | ||
|
|
2dfe9fcf45 | ||
|
|
51f978ac72 | ||
|
|
c216677e1e | ||
|
|
8a5d275136 | ||
|
|
a36b76ba15 | ||
|
|
f3913949b2 | ||
|
|
76fdbfaa2f | ||
|
|
6b44b9ae1e | ||
|
|
8615c4c3b0 | ||
|
|
f976bd3fff | ||
|
|
db55a2664f | ||
|
|
4f1b4b1283 | ||
|
|
208f8349a6 | ||
|
|
32e71b0386 | ||
|
|
1c687e7a45 | ||
|
|
53ed028663 | ||
|
|
2a4ebe1b3e | ||
|
|
df863355b7 | ||
|
|
66e44f3448 | ||
|
|
c1f12f52ae | ||
|
|
b69091a51a | ||
|
|
0fe30ebe49 | ||
|
|
34f5552c7d | ||
|
|
53e27ff4d3 | ||
|
|
f4569c513f | ||
|
|
90ce5f2c57 | ||
|
|
6f3a07b756 | ||
|
|
d42bfa88e1 | ||
|
|
61a6b32137 | ||
|
|
6cb4e203f1 | ||
|
|
2d27a721f8 | ||
|
|
867a8e855b | ||
|
|
d5dc70ccee | ||
|
|
18d9c1d609 | ||
|
|
c388cee1fe | ||
|
|
da2488f7d8 | ||
|
|
f059643187 | ||
|
|
30730fe632 | ||
|
|
39df168891 | ||
|
|
33946e61bb | ||
|
|
0b34d20716 | ||
|
|
8d0b36d2d4 | ||
|
|
7e08f7df67 | ||
|
|
a688a5fe72 | ||
|
|
fe57efb1a8 | ||
|
|
9af3fbc35f | ||
|
|
7495d2dc65 | ||
|
|
456da93465 | ||
|
|
e7a6126fbd | ||
|
|
aa41eb5da4 | ||
|
|
87767be4a6 | ||
|
|
1e0a356cd2 | ||
|
|
1b40c9e692 | ||
|
|
2cc7565841 | ||
|
|
cee67f4301 | ||
|
|
452bf61ef9 | ||
|
|
50d3dfb98b | ||
|
|
d729df5cba | ||
|
|
a5638ccc3b | ||
|
|
46643065ef | ||
|
|
b270562a44 | ||
|
|
cfbe59868b | ||
|
|
207211facc | ||
|
|
269523a034 | ||
|
|
1eee314d17 | ||
|
|
8cc64bf44e | ||
|
|
5dd027ad63 | ||
|
|
d51620640b | ||
|
|
17c767fa42 | ||
|
|
b2749a0c4e | ||
|
|
3b0c7a3a30 | ||
|
|
797681883b | ||
|
|
48b6e1590c | ||
|
|
b423edf2b5 | ||
|
|
9cf979d154 | ||
|
|
033c8b269d | ||
|
|
c4e42b94f9 | ||
|
|
5b9cb4fc8d | ||
|
|
99d9ce10c3 | ||
|
|
34400c7f60 | ||
|
|
05914e38f0 | ||
|
|
bddb54ef4c | ||
|
|
46e146b34a | ||
|
|
f16ba6861b | ||
|
|
fb6af544e2 | ||
|
|
4c3cfc8c2d | ||
|
|
286a7494d3 |
@@ -7,6 +7,7 @@ RUN pacman --sync --refresh --sysupgrade --needed --noconfirm \
|
|||||||
git \
|
git \
|
||||||
gtest \
|
gtest \
|
||||||
mariadb-libs \
|
mariadb-libs \
|
||||||
|
ninja \
|
||||||
protobuf \
|
protobuf \
|
||||||
qt6-base \
|
qt6-base \
|
||||||
qt6-imageformats \
|
qt6-imageformats \
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ RUN apt-get update && \
|
|||||||
libqt5sql5-mysql \
|
libqt5sql5-mysql \
|
||||||
libqt5svg5-dev \
|
libqt5svg5-dev \
|
||||||
libqt5websockets5-dev \
|
libqt5websockets5-dev \
|
||||||
|
ninja-build \
|
||||||
protobuf-compiler \
|
protobuf-compiler \
|
||||||
qt5-image-formats-plugins \
|
qt5-image-formats-plugins \
|
||||||
qtmultimedia5-dev \
|
qtmultimedia5-dev \
|
||||||
|
|||||||
@@ -15,13 +15,14 @@ RUN apt-get update && \
|
|||||||
libprotobuf-dev \
|
libprotobuf-dev \
|
||||||
libqt6multimedia6 \
|
libqt6multimedia6 \
|
||||||
libqt6sql6-mysql \
|
libqt6sql6-mysql \
|
||||||
qt6-svg-dev \
|
ninja-build \
|
||||||
qt6-websockets-dev \
|
|
||||||
protobuf-compiler \
|
protobuf-compiler \
|
||||||
qt6-image-formats-plugins \
|
qt6-image-formats-plugins \
|
||||||
qt6-l10n-tools \
|
qt6-l10n-tools \
|
||||||
qt6-multimedia-dev \
|
qt6-multimedia-dev \
|
||||||
|
qt6-svg-dev \
|
||||||
qt6-tools-dev \
|
qt6-tools-dev \
|
||||||
qt6-tools-dev-tools \
|
qt6-tools-dev-tools \
|
||||||
|
qt6-websockets-dev \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|||||||
29
.ci/Debian13/Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
FROM debian:13
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
|
ccache \
|
||||||
|
clang-format \
|
||||||
|
cmake \
|
||||||
|
file \
|
||||||
|
g++ \
|
||||||
|
git \
|
||||||
|
libgl-dev \
|
||||||
|
liblzma-dev \
|
||||||
|
libmariadb-dev-compat \
|
||||||
|
libprotobuf-dev \
|
||||||
|
libqt6multimedia6 \
|
||||||
|
libqt6sql6-mysql \
|
||||||
|
ninja-build \
|
||||||
|
protobuf-compiler \
|
||||||
|
qt6-image-formats-plugins \
|
||||||
|
qt6-l10n-tools \
|
||||||
|
qt6-multimedia-dev \
|
||||||
|
qt6-svg-dev \
|
||||||
|
qt6-tools-dev \
|
||||||
|
qt6-tools-dev-tools \
|
||||||
|
qt6-websockets-dev \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
@@ -6,6 +6,7 @@ RUN dnf install -y \
|
|||||||
gcc-c++ \
|
gcc-c++ \
|
||||||
git \
|
git \
|
||||||
mariadb-devel \
|
mariadb-devel \
|
||||||
|
ninja-build \
|
||||||
protobuf-devel \
|
protobuf-devel \
|
||||||
qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
|
qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
|
||||||
qt6-qtimageformats \
|
qt6-qtimageformats \
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM fedora:41
|
FROM fedora:43
|
||||||
|
|
||||||
RUN dnf install -y \
|
RUN dnf install -y \
|
||||||
ccache \
|
ccache \
|
||||||
@@ -6,6 +6,7 @@ RUN dnf install -y \
|
|||||||
gcc-c++ \
|
gcc-c++ \
|
||||||
git \
|
git \
|
||||||
mariadb-devel \
|
mariadb-devel \
|
||||||
|
ninja-build \
|
||||||
protobuf-devel \
|
protobuf-devel \
|
||||||
qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
|
qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \
|
||||||
qt6-qtimageformats \
|
qt6-qtimageformats \
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM ubuntu:20.04
|
FROM debian:11
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
@@ -9,17 +9,12 @@ RUN apt-get update && \
|
|||||||
file \
|
file \
|
||||||
g++ \
|
g++ \
|
||||||
git \
|
git \
|
||||||
liblzma-dev \
|
|
||||||
libmariadb-dev-compat \
|
libmariadb-dev-compat \
|
||||||
libprotobuf-dev \
|
libprotobuf-dev \
|
||||||
libqt5multimedia5-plugins \
|
|
||||||
libqt5sql5-mysql \
|
libqt5sql5-mysql \
|
||||||
libqt5svg5-dev \
|
|
||||||
libqt5websockets5-dev \
|
libqt5websockets5-dev \
|
||||||
|
ninja-build \
|
||||||
protobuf-compiler \
|
protobuf-compiler \
|
||||||
qt5-default \
|
|
||||||
qt5-image-formats-plugins \
|
|
||||||
qtmultimedia5-dev \
|
|
||||||
qttools5-dev \
|
qttools5-dev \
|
||||||
qttools5-dev-tools \
|
qttools5-dev-tools \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
@@ -17,6 +17,7 @@ RUN apt-get update && \
|
|||||||
libqt6sql6-mysql \
|
libqt6sql6-mysql \
|
||||||
libqt6svg6-dev \
|
libqt6svg6-dev \
|
||||||
libqt6websockets6-dev \
|
libqt6websockets6-dev \
|
||||||
|
ninja-build \
|
||||||
protobuf-compiler \
|
protobuf-compiler \
|
||||||
qt6-image-formats-plugins \
|
qt6-image-formats-plugins \
|
||||||
qt6-l10n-tools \
|
qt6-l10n-tools \
|
||||||
|
|||||||
@@ -15,13 +15,14 @@ RUN apt-get update && \
|
|||||||
libprotobuf-dev \
|
libprotobuf-dev \
|
||||||
libqt6multimedia6 \
|
libqt6multimedia6 \
|
||||||
libqt6sql6-mysql \
|
libqt6sql6-mysql \
|
||||||
qt6-svg-dev \
|
ninja-build \
|
||||||
qt6-websockets-dev \
|
|
||||||
protobuf-compiler \
|
protobuf-compiler \
|
||||||
qt6-image-formats-plugins \
|
qt6-image-formats-plugins \
|
||||||
qt6-l10n-tools \
|
qt6-l10n-tools \
|
||||||
qt6-multimedia-dev \
|
qt6-multimedia-dev \
|
||||||
|
qt6-svg-dev \
|
||||||
qt6-tools-dev \
|
qt6-tools-dev \
|
||||||
qt6-tools-dev-tools \
|
qt6-tools-dev-tools \
|
||||||
|
qt6-websockets-dev \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|||||||
134
.ci/compile.sh
@@ -11,9 +11,9 @@
|
|||||||
# --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"
|
||||||
# --parallel <core count> sets how many cores cmake should build with in parallel
|
# --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 PARALLEL_COUNT
|
# 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> --parallel <core_count>)
|
# (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
|
||||||
|
|
||||||
# Read arguments
|
# Read arguments
|
||||||
@@ -47,6 +47,10 @@ 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
|
||||||
@@ -67,6 +71,10 @@ while [[ $# != 0 ]]; do
|
|||||||
shift
|
shift
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
'--vcpkg')
|
||||||
|
USE_VCPKG=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
'--dir')
|
'--dir')
|
||||||
shift
|
shift
|
||||||
if [[ $# == 0 ]]; then
|
if [[ $# == 0 ]]; then
|
||||||
@@ -76,13 +84,13 @@ while [[ $# != 0 ]]; do
|
|||||||
BUILD_DIR="$1"
|
BUILD_DIR="$1"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
'--parallel')
|
'--target-macos-version')
|
||||||
shift
|
shift
|
||||||
if [[ $# == 0 ]]; then
|
if [[ $# == 0 ]]; then
|
||||||
echo "::error file=$0::--parallel expects an argument"
|
echo "::error file=$0::--target-macos-version expects an argument"
|
||||||
exit 3
|
exit 3
|
||||||
fi
|
fi
|
||||||
PARALLEL_COUNT="$1"
|
TARGET_MACOS_VERSION="$1"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -105,11 +113,17 @@ fi
|
|||||||
mkdir -p "$BUILD_DIR"
|
mkdir -p "$BUILD_DIR"
|
||||||
cd "$BUILD_DIR"
|
cd "$BUILD_DIR"
|
||||||
|
|
||||||
|
# Set minimum CMake Version
|
||||||
|
export CMAKE_POLICY_VERSION_MINIMUM=3.10
|
||||||
|
|
||||||
# Add cmake flags
|
# Add cmake flags
|
||||||
flags=("-DCMAKE_BUILD_TYPE=$BUILDTYPE")
|
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
|
||||||
@@ -123,19 +137,12 @@ 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")
|
||||||
if [[ $PARALLEL_COUNT ]]; then
|
|
||||||
if [[ $(cmake --build /not_a_dir --parallel 2>&1 | head -1) =~ parallel ]]; then
|
|
||||||
# workaround for bionic having an old cmake
|
|
||||||
echo "this version of cmake does not support --parallel, using native build tool -j instead"
|
|
||||||
buildflags+=(-- -j "$PARALLEL_COUNT")
|
|
||||||
# note, no normal build flags should be added after this
|
|
||||||
else
|
|
||||||
buildflags+=(--parallel "$PARALLEL_COUNT")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
function ccachestatsverbose() {
|
function ccachestatsverbose() {
|
||||||
# note, verbose only works on newer ccache, discard the error
|
# note, verbose only works on newer ccache, discard the error
|
||||||
@@ -148,6 +155,72 @@ function ccachestatsverbose() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Compile
|
# Compile
|
||||||
|
if [[ $RUNNER_OS == macOS ]]; then
|
||||||
|
|
||||||
|
if [[ $TARGET_MACOS_VERSION ]]; then
|
||||||
|
# CMAKE_OSX_DEPLOYMENT_TARGET is a vanilla cmake flag needed to compile to target macOS version
|
||||||
|
flags+=("-DCMAKE_OSX_DEPLOYMENT_TARGET=$TARGET_MACOS_VERSION")
|
||||||
|
|
||||||
|
# vcpkg dependencies need a vcpkg triplet file to compile to the target macOS version
|
||||||
|
# an easy way is to copy the x64-osx.cmake file and modify it
|
||||||
|
triplets_dir="/tmp/cmake/triplets"
|
||||||
|
triplet_version="custom-triplet"
|
||||||
|
triplet_file="$triplets_dir/$triplet_version.cmake"
|
||||||
|
arch=$(uname -m)
|
||||||
|
if [[ $arch == x86_64 ]]; then
|
||||||
|
arch="x64"
|
||||||
|
fi
|
||||||
|
mkdir -p "$triplets_dir"
|
||||||
|
cp "../vcpkg/triplets/$arch-osx.cmake" "$triplet_file"
|
||||||
|
echo "set(VCPKG_CMAKE_SYSTEM_VERSION $TARGET_MACOS_VERSION)" >>"$triplet_file"
|
||||||
|
echo "set(VCPKG_OSX_DEPLOYMENT_TARGET $TARGET_MACOS_VERSION)" >>"$triplet_file"
|
||||||
|
flags+=("-DVCPKG_OVERLAY_TRIPLETS=$triplets_dir")
|
||||||
|
flags+=("-DVCPKG_HOST_TRIPLET=$triplet_version")
|
||||||
|
flags+=("-DVCPKG_TARGET_TRIPLET=$triplet_version")
|
||||||
|
echo "::group::Generated triplet $triplet_file"
|
||||||
|
cat "$triplet_file"
|
||||||
|
echo "::endgroup::"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "::group::Signing Certificate"
|
||||||
|
if [[ -n "$MACOS_CERTIFICATE_NAME" ]]; then
|
||||||
|
echo "$MACOS_CERTIFICATE" | base64 --decode >"certificate.p12"
|
||||||
|
security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
||||||
|
security default-keychain -s build.keychain
|
||||||
|
security set-keychain-settings -t 3600 -l build.keychain
|
||||||
|
security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
||||||
|
security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
||||||
|
echo "macOS signing certificate successfully imported and keychain configured."
|
||||||
|
else
|
||||||
|
echo "No signing certificate configured. Skipping set up of keychain in macOS environment."
|
||||||
|
fi
|
||||||
|
echo "::endgroup::"
|
||||||
|
|
||||||
|
if [[ $MAKE_PACKAGE ]]; then
|
||||||
|
# Workaround https://github.com/actions/runner-images/issues/7522
|
||||||
|
# have hdiutil repeat the command 10 times in hope of success
|
||||||
|
hdiutil_script="/tmp/hdiutil.sh"
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
echo '#!/bin/bash
|
||||||
|
i=0
|
||||||
|
while ! hdiutil "$@"; do
|
||||||
|
if (( ++i >= 10 )); then
|
||||||
|
echo "Error: hdiutil failed $i times!" >&2
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done' >"$hdiutil_script"
|
||||||
|
chmod +x "$hdiutil_script"
|
||||||
|
flags+=(-DCPACK_COMMAND_HDIUTIL="$hdiutil_script")
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ $RUNNER_OS == Windows ]]; then
|
||||||
|
# Enable MTT, see https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
|
||||||
|
# and https://devblogs.microsoft.com/cppblog/cpp-build-throughput-investigation-and-tune-up/#multitooltask-mtt
|
||||||
|
buildflags+=(-- -p:UseMultiToolTask=true -p:EnableClServerMode=true)
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $USE_CCACHE ]]; then
|
if [[ $USE_CCACHE ]]; then
|
||||||
echo "::group::Show ccache stats"
|
echo "::group::Show ccache stats"
|
||||||
ccachestatsverbose
|
ccachestatsverbose
|
||||||
@@ -156,17 +229,13 @@ 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"
|
||||||
if [[ $RUNNER_OS == Windows ]]; then
|
echo "Running cmake --build with flags: ${buildflags[*]}"
|
||||||
# Enable MTT, see https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/
|
cmake --build . "${buildflags[@]}"
|
||||||
# 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
|
||||||
@@ -175,6 +244,19 @@ 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
|
||||||
@@ -189,12 +271,6 @@ 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::"
|
||||||
|
|
||||||
|
|||||||
@@ -137,10 +137,11 @@ 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)")
|
||||||
@@ -148,6 +149,10 @@ function RUN ()
|
|||||||
args+=(--mount "type=bind,source=$CCACHE_DIR,target=/.ccache")
|
args+=(--mount "type=bind,source=$CCACHE_DIR,target=/.ccache")
|
||||||
args+=(--env "CCACHE_DIR=/.ccache")
|
args+=(--env "CCACHE_DIR=/.ccache")
|
||||||
fi
|
fi
|
||||||
|
if [[ -n "$CMAKE_GENERATOR" ]]; then
|
||||||
|
args+=(--env "CMAKE_GENERATOR=$CMAKE_GENERATOR")
|
||||||
|
fi
|
||||||
|
# shellcheck disable=2086
|
||||||
docker run "${args[@]}" $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS "$@"
|
docker run "${args[@]}" $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS "$@"
|
||||||
return $?
|
return $?
|
||||||
else
|
else
|
||||||
@@ -161,5 +166,6 @@ 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
|
||||||
|
|||||||
@@ -11,11 +11,19 @@ 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 clang-format/cmake-format..."
|
echo "Checking your code using format.sh..."
|
||||||
|
|
||||||
diff="$(./format.sh --diff --cmake --cf-version --branch origin/master)"
|
diff="$(./format.sh --diff --cmake --shell --print-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
|
||||||
@@ -33,14 +41,13 @@ case $err in
|
|||||||
***********************************************************
|
***********************************************************
|
||||||
|
|
||||||
Used version:
|
Used version:
|
||||||
${diff%%
|
$used_version
|
||||||
----------
|
|
||||||
*}
|
Affected files:
|
||||||
|
$files_to_edit
|
||||||
|
|
||||||
The following changes should be made:
|
The following changes should be made:
|
||||||
${diff#*
|
$changes_to_make
|
||||||
----------
|
|
||||||
}
|
|
||||||
|
|
||||||
Exiting...
|
Exiting...
|
||||||
EOM
|
EOM
|
||||||
@@ -58,6 +65,9 @@ EOM
|
|||||||
*** ***
|
*** ***
|
||||||
***********************************************************
|
***********************************************************
|
||||||
|
|
||||||
|
Used version:
|
||||||
|
$used_version
|
||||||
|
|
||||||
Exiting...
|
Exiting...
|
||||||
EOM
|
EOM
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ Available pre-compiled binaries for installation:
|
|||||||
<b>Linux</b>
|
<b>Linux</b>
|
||||||
• <kbd>Ubuntu 24.04 LTS</kbd> <sub><i>Noble Numbat</i></sub>
|
• <kbd>Ubuntu 24.04 LTS</kbd> <sub><i>Noble Numbat</i></sub>
|
||||||
• <kbd>Ubuntu 22.04 LTS</kbd> <sub><i>Jammy Jellyfish</i></sub>
|
• <kbd>Ubuntu 22.04 LTS</kbd> <sub><i>Jammy Jellyfish</i></sub>
|
||||||
• <kbd>Ubuntu 20.04 LTS</kbd> <sub><i>Focal Fossa</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>
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ Last changes are based on commit {{ .commit }}.
|
|||||||
*This PR is automatically generated and updated by the workflow at `.github/workflows/translations-push.yml`. Review [action runs][2].*<br>
|
*This PR is automatically generated and updated by the workflow at `.github/workflows/translations-push.yml`. Review [action runs][2].*<br>
|
||||||
*After merging, all changes to the source language are available for translation at [Transifex][1] shortly.*
|
*After merging, all changes to the source language are available for translation at [Transifex][1] shortly.*
|
||||||
|
|
||||||
[1]: https://app.transifex.com/cockatrice/cockatrice/
|
[1]: https://explore.transifex.com/cockatrice/cockatrice/
|
||||||
[2]: https://github.com/Cockatrice/Cockatrice/actions/workflows/translations-push.yml?query=branch%3Amaster
|
[2]: https://github.com/Cockatrice/Cockatrice/actions/workflows/translations-push.yml?query=branch%3Amaster
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ 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
|
||||||
|
|||||||
2
.github/CONTRIBUTING.md
vendored
@@ -1,3 +1,5 @@
|
|||||||
|
<!--! @page contributing Contributing -->
|
||||||
|
|
||||||
[Introduction](#contributing-to-cockatrice) | [Code Style Guide](
|
[Introduction](#contributing-to-cockatrice) | [Code Style Guide](
|
||||||
#code-style-guide) | [Translations](#translations) | [Release Management](
|
#code-style-guide) | [Translations](#translations) | [Release Management](
|
||||||
#release-management)
|
#release-management)
|
||||||
|
|||||||
18
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -9,14 +9,24 @@ assignees: ''
|
|||||||
---
|
---
|
||||||
|
|
||||||
<!-- READ THIS BEFORE POSTING
|
<!-- READ THIS BEFORE POSTING
|
||||||
Go to "Help → View Debug Log" in Cockatrice and copy all information at the
|
In Cockatrice, go to "Help" → "View Debug Log" and copy all information displayed at the
|
||||||
top (above the separation line) below "System Information" in this ticket!
|
top (above the separation line "----"), to below "System Information" section in this ticket!
|
||||||
If you can't start Cockatrice to access these details, make
|
If you can't start Cockatrice to access these details, make
|
||||||
sure to post your OS and the file name of the setup binary instead. -->
|
sure to post your OS and the file name of the setup binary instead.
|
||||||
|
-->
|
||||||
|
|
||||||
**System Information:**
|
**System Information:**
|
||||||
|
<!-- Read the hint above on where to find the important information to provide here! -->
|
||||||
|
|
||||||
|
|
||||||
|
<details><summary>Debug Log:</summary>
|
||||||
|
<!--
|
||||||
|
In Cockatrice, go to "Help" → "View Debug Log", click the "Copy to clickboard" button and paste the output here.
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
_______________________________________________________________________________________
|
_______________________________________________________________________________________
|
||||||
|
|
||||||
<!-- Explain your issue in detail here! Please attach screenshots if possible. -->
|
<!-- Explain your issue in detail here! Please attach screenshots if possible. -->
|
||||||
@@ -26,7 +36,7 @@ ________________________________________________________________________________
|
|||||||
|
|
||||||
_______________________________________________________________________________________
|
_______________________________________________________________________________________
|
||||||
|
|
||||||
<!-- Describe the sequence of actions needed to experience the bug -->
|
<!-- Describe the sequence of actions needed to experience the bug. -->
|
||||||
|
|
||||||
**Steps to reproduce:**
|
**Steps to reproduce:**
|
||||||
- Do A
|
- Do A
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -4,6 +4,6 @@ contact_links:
|
|||||||
url: https://discord.gg/3Z9yzmA
|
url: https://discord.gg/3Z9yzmA
|
||||||
about: Need help with using the client? Want to find some games? Try the Discord server!
|
about: Need help with using the client? Want to find some games? Try the Discord server!
|
||||||
- name: 🌐 Translations (Help improve the localization of the app)
|
- name: 🌐 Translations (Help improve the localization of the app)
|
||||||
url: https://www.transifex.com/cockatrice/cockatrice/
|
url: https://explore.transifex.com/cockatrice/cockatrice/
|
||||||
# it is not possible to add a link to the wiki to this description
|
# it is not possible to add a link to the wiki to this description
|
||||||
about: For more information and guidance check our Translation FAQ on our wiki!
|
about: For more information and guidance check our Translation FAQ on our wiki!
|
||||||
|
|||||||
420
.github/workflows/desktop-build.yml
vendored
@@ -1,23 +1,42 @@
|
|||||||
name: Build Desktop
|
name: Build Desktop
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
id-token: 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-ignore:
|
paths:
|
||||||
- '**.md'
|
- '*/**' # matches all files not in root
|
||||||
- 'webclient/**'
|
- '!**.md'
|
||||||
- '.github/workflows/web-*.yml'
|
- '!.github/**'
|
||||||
- '.github/workflows/translations-*.yml'
|
- '!.husky/**'
|
||||||
- '.github/workflows/docker-release.yml'
|
- '!.tx/**'
|
||||||
|
- '!doc/**'
|
||||||
|
- '!webclient/**'
|
||||||
|
- '.github/workflows/desktop-build.yml'
|
||||||
|
- 'CMakeLists.txt'
|
||||||
|
- 'vcpkg.json'
|
||||||
|
- 'vcpkg'
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths:
|
||||||
- '**.md'
|
- '*/**' # matches all files not in root
|
||||||
- 'webclient/**'
|
- '!**.md'
|
||||||
- '.github/workflows/web-*.yml'
|
- '!.github/**'
|
||||||
- '.github/workflows/translations-*.yml'
|
- '!.husky/**'
|
||||||
|
- '!.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:
|
||||||
@@ -51,7 +70,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
if: steps.configure.outputs.tag != null
|
if: steps.configure.outputs.tag != null
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
@@ -95,25 +114,30 @@ 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
|
||||||
package: DEB
|
package: DEB
|
||||||
|
|
||||||
- distro: Fedora
|
|
||||||
version: 41
|
|
||||||
package: RPM
|
|
||||||
test: skip # Running tests on all distros is superfluous
|
test: skip # Running tests on all distros is superfluous
|
||||||
|
|
||||||
|
- distro: Debian
|
||||||
|
version: 13
|
||||||
|
package: DEB
|
||||||
|
|
||||||
- distro: Fedora
|
- distro: Fedora
|
||||||
version: 42
|
version: 42
|
||||||
package: RPM
|
package: RPM
|
||||||
|
test: skip # Running tests on all distros is superfluous
|
||||||
|
|
||||||
- distro: Ubuntu
|
- distro: Fedora
|
||||||
version: 20.04
|
version: 43
|
||||||
package: DEB
|
package: RPM
|
||||||
test: skip # Ubuntu 20.04 has a broken Qt for debug builds
|
|
||||||
|
|
||||||
- distro: Ubuntu
|
- distro: Ubuntu
|
||||||
version: 22.04
|
version: 22.04
|
||||||
@@ -130,29 +154,25 @@ jobs:
|
|||||||
continue-on-error: ${{matrix.allow-failure == 'yes'}}
|
continue-on-error: ${{matrix.allow-failure == 'yes'}}
|
||||||
env:
|
env:
|
||||||
NAME: ${{matrix.distro}}${{matrix.version}}
|
NAME: ${{matrix.distro}}${{matrix.version}}
|
||||||
CACHE: /tmp/${{matrix.distro}}${{matrix.version}}-cache # ${{runner.temp}} does not work?
|
CACHE: ${{github.workspace}}/.cache/${{matrix.distro}}${{matrix.version}} # directory for caching docker image and ccache
|
||||||
# Cache size over the entire repo is 10Gi:
|
# Cache size over the entire repo is 10Gi:
|
||||||
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
|
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
|
||||||
CCACHE_SIZE: 200M
|
CCACHE_SIZE: 500M
|
||||||
|
CMAKE_GENERATOR: 'Ninja'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Generate cache timestamp
|
- name: Restore compiler cache (ccache)
|
||||||
id: cache_timestamp
|
id: ccache_restore
|
||||||
shell: bash
|
uses: actions/cache/restore@v5
|
||||||
run: echo "timestamp=$(date -u '+%Y%m%d%H%M%S')" >>"$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Restore cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
env:
|
env:
|
||||||
timestamp: ${{steps.cache_timestamp.outputs.timestamp}}
|
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||||
with:
|
with:
|
||||||
path: ${{env.CACHE}}
|
path: ${{env.CACHE}}
|
||||||
key: docker-${{matrix.distro}}${{matrix.version}}-cache-${{env.timestamp}}
|
key: ccache-${{matrix.distro}}${{matrix.version}}-${{env.BRANCH_NAME}}
|
||||||
restore-keys: |
|
restore-keys: ccache-${{matrix.distro}}${{matrix.version}}-
|
||||||
docker-${{matrix.distro}}${{matrix.version}}-cache-
|
|
||||||
|
|
||||||
- name: Build ${{matrix.distro}} ${{matrix.version}} Docker image
|
- name: Build ${{matrix.distro}} ${{matrix.version}} Docker image
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -161,9 +181,11 @@ jobs:
|
|||||||
- name: Build debug and test
|
- name: Build debug and test
|
||||||
if: matrix.test != 'skip'
|
if: matrix.test != 'skip'
|
||||||
shell: bash
|
shell: bash
|
||||||
|
env:
|
||||||
|
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
|
||||||
run: |
|
run: |
|
||||||
source .ci/docker.sh
|
source .ci/docker.sh
|
||||||
RUN --server --debug --test --ccache "$CCACHE_SIZE" --parallel 4
|
RUN --server --debug --test --ccache "$CCACHE_SIZE"
|
||||||
|
|
||||||
- name: Build release package
|
- name: Build release package
|
||||||
id: build
|
id: build
|
||||||
@@ -172,22 +194,33 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
BUILD_DIR: build
|
BUILD_DIR: build
|
||||||
SUFFIX: '-${{matrix.distro}}${{matrix.version}}'
|
SUFFIX: '-${{matrix.distro}}${{matrix.version}}'
|
||||||
type: '${{matrix.package}}'
|
package: '${{matrix.package}}'
|
||||||
|
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 "$type" --dir "$BUILD_DIR" \
|
RUN --server --release --package "$package" --dir "$BUILD_DIR" \
|
||||||
--ccache "$CCACHE_SIZE" --parallel 4
|
--ccache "$CCACHE_SIZE" $NO_CLIENT
|
||||||
.ci/name_build.sh
|
.ci/name_build.sh
|
||||||
|
|
||||||
|
- name: Save compiler cache (ccache)
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
uses: actions/cache/save@v5
|
||||||
|
with:
|
||||||
|
path: ${{env.CACHE}}
|
||||||
|
key: ${{ steps.ccache_restore.outputs.cache-primary-key }}
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
|
id: upload_artifact
|
||||||
if: matrix.package != 'skip'
|
if: matrix.package != 'skip'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: ${{matrix.distro}}${{matrix.version}}-package
|
name: ${{matrix.distro}}${{matrix.version}}-package
|
||||||
path: ${{steps.build.outputs.path}}
|
path: ${{steps.build.outputs.path}}
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload to release
|
- name: Upload to release
|
||||||
|
id: upload_release
|
||||||
if: matrix.package != 'skip' && needs.configure.outputs.tag != null
|
if: matrix.package != 'skip' && needs.configure.outputs.tag != null
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
@@ -197,94 +230,181 @@ jobs:
|
|||||||
asset_name: ${{steps.build.outputs.name}}
|
asset_name: ${{steps.build.outputs.name}}
|
||||||
run: gh release upload "$tag_name" "$asset_path#$asset_name"
|
run: gh release upload "$tag_name" "$asset_path#$asset_name"
|
||||||
|
|
||||||
build-macos:
|
- 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-vcpkg:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- target: 13
|
- os: macOS
|
||||||
|
target: 13
|
||||||
|
runner: macos-15-intel
|
||||||
soc: Intel
|
soc: Intel
|
||||||
os: macos-13
|
xcode: "16.4"
|
||||||
xcode: "14.3.1"
|
|
||||||
type: Release
|
type: Release
|
||||||
core_count: 4
|
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
|
||||||
|
|
||||||
- target: 14
|
- os: macOS
|
||||||
|
target: 14
|
||||||
|
runner: macos-14
|
||||||
soc: Apple
|
soc: Apple
|
||||||
os: macos-14
|
|
||||||
xcode: "15.4"
|
xcode: "15.4"
|
||||||
type: Release
|
type: Release
|
||||||
core_count: 3
|
|
||||||
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
|
||||||
|
|
||||||
- target: 15
|
- os: macOS
|
||||||
|
target: 15
|
||||||
|
runner: macos-15
|
||||||
soc: Apple
|
soc: Apple
|
||||||
os: macos-15
|
xcode: "16.4"
|
||||||
xcode: "16.2"
|
|
||||||
type: Release
|
type: Release
|
||||||
core_count: 3
|
|
||||||
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
|
||||||
|
|
||||||
- target: 15
|
- os: macOS
|
||||||
|
target: 15
|
||||||
|
runner: macos-15
|
||||||
soc: Apple
|
soc: Apple
|
||||||
os: macos-15
|
xcode: "16.4"
|
||||||
xcode: "16.2"
|
|
||||||
type: Debug
|
type: Debug
|
||||||
core_count: 3
|
qt_version: 6.6.*
|
||||||
|
qt_arch: clang_64
|
||||||
|
qt_modules: qtimageformats qtmultimedia qtwebsockets
|
||||||
|
cache_qt: false
|
||||||
|
cmake_generator: Ninja
|
||||||
|
use_ccache: 1
|
||||||
|
|
||||||
name: macOS ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
|
- os: Windows
|
||||||
|
target: 7
|
||||||
|
runner: windows-2022
|
||||||
|
type: Release
|
||||||
|
make_package: 1
|
||||||
|
package_suffix: "-Win7"
|
||||||
|
artifact_name: Windows7-installer
|
||||||
|
qt_version: 5.15.*
|
||||||
|
qt_arch: win64_msvc2019_64
|
||||||
|
cache_qt: true
|
||||||
|
cmake_generator: "Visual Studio 17 2022"
|
||||||
|
cmake_generator_platform: x64
|
||||||
|
|
||||||
|
- os: Windows
|
||||||
|
target: 10
|
||||||
|
runner: windows-2022
|
||||||
|
type: Release
|
||||||
|
make_package: 1
|
||||||
|
package_suffix: "-Win10"
|
||||||
|
artifact_name: Windows10-installer
|
||||||
|
qt_version: 6.6.*
|
||||||
|
qt_arch: win64_msvc2019_64
|
||||||
|
qt_modules: qtimageformats qtmultimedia qtwebsockets
|
||||||
|
cache_qt: true
|
||||||
|
cmake_generator: "Visual Studio 17 2022"
|
||||||
|
cmake_generator_platform: x64
|
||||||
|
|
||||||
|
name: ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
|
||||||
needs: configure
|
needs: configure
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.runner}}
|
||||||
continue-on-error: ${{matrix.allow-failure == 'yes'}}
|
|
||||||
env:
|
|
||||||
DEVELOPER_DIR:
|
|
||||||
/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
- name: Install dependencies using Homebrew
|
- name: Add msbuild to PATH
|
||||||
shell: bash
|
if: matrix.os == 'Windows'
|
||||||
# CMake cannot find the MySQL connector
|
id: add-msbuild
|
||||||
# Neither of these works: mariadb-connector-c mysql-connector-c++
|
uses: microsoft/setup-msbuild@v2
|
||||||
env:
|
with:
|
||||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
|
msbuild-architecture: x64
|
||||||
run: |
|
|
||||||
brew update
|
|
||||||
brew install protobuf qt --force-bottle
|
|
||||||
|
|
||||||
- name: Build & Sign on Xcode ${{matrix.xcode}}
|
# Using jianmingyong/ccache-action to setup ccache without using brew
|
||||||
shell: bash
|
# It tries to download a binary of ccache from GitHub Release and falls back to building from source if it fails
|
||||||
|
- name: Setup ccache
|
||||||
|
if: matrix.use_ccache == 1
|
||||||
|
uses: jianmingyong/ccache-action@v1
|
||||||
|
with:
|
||||||
|
install-type: "binary"
|
||||||
|
ccache-key-prefix: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}
|
||||||
|
max-size: 500M
|
||||||
|
gh-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install Qt ${{matrix.qt_version}}
|
||||||
|
uses: jurplel/install-qt-action@v4
|
||||||
|
with:
|
||||||
|
version: ${{matrix.qt_version}}
|
||||||
|
arch: ${{matrix.qt_arch}}
|
||||||
|
modules: ${{matrix.qt_modules}}
|
||||||
|
cache: ${{matrix.cache_qt}}
|
||||||
|
|
||||||
|
- name: Setup vcpkg cache
|
||||||
|
id: vcpkg-cache
|
||||||
|
uses: TAServers/vcpkg-cache@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# uses environment variables, see compile.sh for more details
|
||||||
|
- name: Build Cockatrice
|
||||||
id: build
|
id: build
|
||||||
|
shell: bash
|
||||||
env:
|
env:
|
||||||
BUILDTYPE: '${{matrix.type}}'
|
BUILDTYPE: '${{matrix.type}}'
|
||||||
MAKE_TEST: 1
|
|
||||||
MAKE_PACKAGE: '${{matrix.make_package}}'
|
MAKE_PACKAGE: '${{matrix.make_package}}'
|
||||||
PACKAGE_SUFFIX: '-macOS${{matrix.target}}_${{matrix.soc}}'
|
PACKAGE_SUFFIX: '${{matrix.package_suffix}}'
|
||||||
|
CMAKE_GENERATOR: ${{matrix.cmake_generator}}
|
||||||
|
CMAKE_GENERATOR_PLATFORM: ${{matrix.cmake_generator_platform}}
|
||||||
|
USE_CCACHE: ${{matrix.use_ccache}}
|
||||||
|
VCPKG_DISABLE_METRICS: 1
|
||||||
|
VCPKG_BINARY_SOURCES: 'clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite'
|
||||||
|
# macOS-specific environment variables, will be ignored on Windows
|
||||||
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 }}
|
||||||
# macOS runner have 3 cores usually - only the macos-13 image has 4:
|
DEVELOPER_DIR: '/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer'
|
||||||
# https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories
|
TARGET_MACOS_VERSION: ${{ matrix.override_target }}
|
||||||
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
|
run: .ci/compile.sh --server --test --vcpkg
|
||||||
run: |
|
|
||||||
if [[ -n "$MACOS_CERTIFICATE_NAME" ]]
|
|
||||||
then
|
|
||||||
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
|
|
||||||
security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
|
||||||
security default-keychain -s build.keychain
|
|
||||||
security set-keychain-settings -t 3600 -l build.keychain
|
|
||||||
security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
|
||||||
security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
|
|
||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
|
||||||
fi
|
|
||||||
.ci/compile.sh --server --parallel ${{matrix.core_count}}
|
|
||||||
|
|
||||||
- name: Sign app bundle
|
- name: Sign app bundle
|
||||||
if: matrix.make_package
|
if: matrix.os == 'macOS' && 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 }}
|
||||||
@@ -296,7 +416,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Notarize app bundle
|
- name: Notarize app bundle
|
||||||
if: matrix.make_package
|
if: matrix.os == 'macOS' && 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 }}
|
||||||
@@ -307,20 +427,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"
|
||||||
@@ -328,97 +448,17 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
|
id: upload_artifact
|
||||||
if: matrix.make_package
|
if: matrix.make_package
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: macOS${{matrix.target}}${{ matrix.soc == 'Intel' && '_Intel' || '' }}${{ matrix.type == 'Debug' && '_Debug' || '' }}-package
|
name: ${{matrix.artifact_name}}
|
||||||
path: ${{steps.build.outputs.path}}
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Upload to release
|
|
||||||
if: matrix.package != 'skip' && 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"
|
|
||||||
|
|
||||||
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@v4
|
|
||||||
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}}
|
|
||||||
tools: ${{matrix.qt_tools}}
|
|
||||||
modules: ${{matrix.qt_modules}}
|
|
||||||
|
|
||||||
# TODO: re-enable when https://github.com/lukka/run-vcpkg/issues/243 is fixed
|
|
||||||
- if: false
|
|
||||||
name: Run vcpkg (disabled)
|
|
||||||
uses: lukka/run-vcpkg@v11
|
|
||||||
with:
|
|
||||||
runVcpkgInstall: true
|
|
||||||
doNotCache: false
|
|
||||||
env:
|
|
||||||
VCPKG_DEFAULT_TRIPLET: 'x64-windows'
|
|
||||||
VCPKG_DISABLE_METRICS: 1
|
|
||||||
|
|
||||||
- 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}}'
|
|
||||||
# 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
|
|
||||||
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
|
||||||
uses: actions/upload-artifact@v4
|
if: matrix.os == 'Windows'
|
||||||
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: Windows${{matrix.target}}-debug-pdbs
|
name: Windows${{matrix.target}}-debug-pdbs
|
||||||
path: |
|
path: |
|
||||||
@@ -427,7 +467,8 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload to release
|
- name: Upload to release
|
||||||
if: matrix.package != 'skip' && needs.configure.outputs.tag != null
|
id: upload_release
|
||||||
|
if: needs.configure.outputs.tag != null
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{github.token}}
|
GH_TOKEN: ${{github.token}}
|
||||||
@@ -435,3 +476,18 @@ jobs:
|
|||||||
asset_path: ${{steps.build.outputs.path}}
|
asset_path: ${{steps.build.outputs.path}}
|
||||||
asset_name: ${{steps.build.outputs.name}}
|
asset_name: ${{steps.build.outputs.name}}
|
||||||
run: gh release upload "$tag_name" "$asset_path#$asset_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
|
||||||
|
|||||||
25
.github/workflows/desktop-lint.yml
vendored
@@ -1,13 +1,22 @@
|
|||||||
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-ignore:
|
paths:
|
||||||
- '**.md'
|
- '*/**' # matches all files not in root
|
||||||
- 'webclient/**'
|
- '!**.md'
|
||||||
- '.github/workflows/web-*.yml'
|
- '!.ci/**'
|
||||||
- '.github/workflows/translations-*.yml'
|
- '!.github/**'
|
||||||
- '.github/workflows/docker-release.yml'
|
- '!.husky/**'
|
||||||
|
- '!.tx/**'
|
||||||
|
- '!doc/**'
|
||||||
|
- '!webclient/**'
|
||||||
|
- '.ci/lint_cpp.sh'
|
||||||
|
- '.github/workflows/desktop-lint.yml'
|
||||||
|
- '.clang-format'
|
||||||
|
- '.cmake-format.json'
|
||||||
|
- 'format.sh'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
format:
|
format:
|
||||||
@@ -15,7 +24,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 20 # should be enough to find merge base
|
fetch-depth: 20 # should be enough to find merge base
|
||||||
|
|
||||||
@@ -23,7 +32,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
|
sudo apt-get install -y --no-install-recommends clang-format cmake-format shellcheck
|
||||||
|
|
||||||
- name: Check code formatting
|
- name: Check code formatting
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
13
.github/workflows/docker-release.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Build Docker Container
|
name: Build Docker Image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -11,12 +11,7 @@ on:
|
|||||||
- master
|
- master
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/docker-release.yml'
|
- '.github/workflows/docker-release.yml'
|
||||||
- 'CMakeLists.txt'
|
|
||||||
- 'Dockerfile'
|
- 'Dockerfile'
|
||||||
- 'servatrice/**'
|
|
||||||
- 'common/**'
|
|
||||||
- 'cmake/**'
|
|
||||||
- '!**.md'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
@@ -28,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Docker metadata
|
- name: Docker metadata
|
||||||
id: metadata
|
id: metadata
|
||||||
@@ -48,7 +43,7 @@ jobs:
|
|||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
@@ -59,7 +54,7 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ github.token }}
|
password: ${{ github.token }}
|
||||||
|
|
||||||
- name: Build and push Docker Image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
|||||||
61
.github/workflows/documentation-build.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
name: Generate Docs
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*' # Only re-generate docs when a new tagged version is pushed
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'doc/doxygen/**'
|
||||||
|
- '.github/workflows/documentation-build.yml'
|
||||||
|
- 'Doxyfile'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
COCKATRICE_REF: ${{ github.ref_name }} # Tag name if the commit is tagged, otherwise branch name
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docs:
|
||||||
|
name: Doxygen
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Install Graphviz
|
||||||
|
run: |
|
||||||
|
sudo apt-get install -y graphviz
|
||||||
|
dot -V
|
||||||
|
|
||||||
|
- name: Install Doxygen
|
||||||
|
uses: ssciwr/doxygen-install@v1
|
||||||
|
with:
|
||||||
|
version: "1.14.0"
|
||||||
|
|
||||||
|
- name: Update Doxygen Configuration
|
||||||
|
run: |
|
||||||
|
git diff Doxyfile
|
||||||
|
doxygen -u Doxyfile
|
||||||
|
if git diff --quiet Doxyfile; then
|
||||||
|
echo "::notice::No config changes in Doxyfile detected."
|
||||||
|
else
|
||||||
|
echo "::error::Config changes in Doxyfile detected! Please update the file by running 'doxygen -u Doxyfile'."
|
||||||
|
echo ""
|
||||||
|
git diff --color=always Doxyfile
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate Documentation
|
||||||
|
if: always()
|
||||||
|
run: doxygen Doxyfile
|
||||||
|
|
||||||
|
- name: Deploy to cockatrice.github.io
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
|
with:
|
||||||
|
deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }}
|
||||||
|
external_repository: Cockatrice/cockatrice.github.io
|
||||||
|
publish_branch: master
|
||||||
|
publish_dir: ./docs/html
|
||||||
|
destination_dir: docs # Docs will live under https://cockatrice.github.io/docs/
|
||||||
7
.github/workflows/translations-pull.yml
vendored
@@ -7,6 +7,7 @@ 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:
|
||||||
@@ -19,7 +20,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Pull translated strings from Transifex
|
- name: Pull translated strings from Transifex
|
||||||
uses: transifex/cli-action@v2
|
uses: transifex/cli-action@v2
|
||||||
@@ -32,7 +33,7 @@ jobs:
|
|||||||
- name: Create pull request
|
- name: Create pull request
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
id: create_pr
|
id: create_pr
|
||||||
uses: peter-evans/create-pull-request@v7
|
uses: peter-evans/create-pull-request@v8
|
||||||
with:
|
with:
|
||||||
add-paths: |
|
add-paths: |
|
||||||
cockatrice/translations/*.ts
|
cockatrice/translations/*.ts
|
||||||
@@ -51,7 +52,7 @@ jobs:
|
|||||||
*This PR is automatically generated and updated by the workflow at `.github/workflows/translations-pull.yml`. Review [action runs][2].*<br>
|
*This PR is automatically generated and updated by the workflow at `.github/workflows/translations-pull.yml`. Review [action runs][2].*<br>
|
||||||
*After merging, all new languages and translations are available in the next build.*
|
*After merging, all new languages and translations are available in the next build.*
|
||||||
|
|
||||||
[1]: https://app.transifex.com/cockatrice/cockatrice/
|
[1]: https://explore.transifex.com/cockatrice/cockatrice/
|
||||||
[2]: https://github.com/Cockatrice/Cockatrice/actions/workflows/translations-pull.yml?query=branch%3Amaster
|
[2]: https://github.com/Cockatrice/Cockatrice/actions/workflows/translations-pull.yml?query=branch%3Amaster
|
||||||
labels: |
|
labels: |
|
||||||
CI
|
CI
|
||||||
|
|||||||
13
.github/workflows/translations-push.yml
vendored
@@ -7,6 +7,7 @@ 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:
|
||||||
@@ -19,7 +20,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install lupdate
|
- name: Install lupdate
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -30,10 +31,10 @@ jobs:
|
|||||||
- name: Update Cockatrice translation source
|
- name: Update Cockatrice translation source
|
||||||
id: cockatrice
|
id: cockatrice
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
run: |
|
||||||
FILE: 'cockatrice/cockatrice_en@source.ts'
|
FILE="cockatrice/cockatrice_en@source.ts"
|
||||||
DIRS: 'cockatrice/src common'
|
export DIRS="cockatrice/src $(find . -maxdepth 1 -type d -name 'libcockatrice_*')"
|
||||||
run: .ci/update_translation_source_strings.sh
|
FILE="$FILE" DIRS="$DIRS" .ci/update_translation_source_strings.sh
|
||||||
|
|
||||||
- name: Update Oracle translation source
|
- name: Update Oracle translation source
|
||||||
id: oracle
|
id: oracle
|
||||||
@@ -56,7 +57,7 @@ jobs:
|
|||||||
- name: Create pull request
|
- name: Create pull request
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
id: create_pr
|
id: create_pr
|
||||||
uses: peter-evans/create-pull-request@v7
|
uses: peter-evans/create-pull-request@v8
|
||||||
with:
|
with:
|
||||||
add-paths: |
|
add-paths: |
|
||||||
cockatrice/cockatrice_en@source.ts
|
cockatrice/cockatrice_en@source.ts
|
||||||
|
|||||||
10
.github/workflows/web-build.yml
vendored
@@ -5,14 +5,16 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/web-*.yml'
|
- '.husky/**'
|
||||||
- 'webclient/**'
|
- 'webclient/**'
|
||||||
- '!**.md'
|
- '!**.md'
|
||||||
|
- '.github/workflows/web-build.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/web-*.yml'
|
- '.husky/**'
|
||||||
- 'webclient/**'
|
- 'webclient/**'
|
||||||
- '!**.md'
|
- '!**.md'
|
||||||
|
- '.github/workflows/web-build.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-web:
|
build-web:
|
||||||
@@ -33,10 +35,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: ${{matrix.node_version}}
|
node-version: ${{matrix.node_version}}
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|||||||
7
.github/workflows/web-lint.yml
vendored
@@ -1,11 +1,12 @@
|
|||||||
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:
|
||||||
@@ -17,10 +18,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: 'webclient/package-lock.json'
|
cache-dependency-path: 'webclient/package-lock.json'
|
||||||
|
|||||||
1
.gitignore
vendored
@@ -14,3 +14,4 @@ compile_commands.json
|
|||||||
.cache
|
.cache
|
||||||
.gdb_history
|
.gdb_history
|
||||||
cockatrice/resources/config/qtlogging.ini
|
cockatrice/resources/config/qtlogging.ini
|
||||||
|
docs/
|
||||||
|
|||||||
4
.gitmodules
vendored
@@ -1,3 +1,7 @@
|
|||||||
[submodule "vcpkg"]
|
[submodule "vcpkg"]
|
||||||
path = vcpkg
|
path = vcpkg
|
||||||
url = https://github.com/microsoft/vcpkg.git
|
url = https://github.com/microsoft/vcpkg.git
|
||||||
|
|
||||||
|
[submodule "doxygen-awesome-css"]
|
||||||
|
path = doc/doxygen/theme
|
||||||
|
url = https://github.com/jothepro/doxygen-awesome-css.git
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ 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
|
||||||
@@ -48,8 +50,8 @@ if(USE_CCACHE)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32 OR USE_VCPKG)
|
||||||
# Use vcpkg toolchain on Windows
|
# Use vcpkg toolchain on Windows (and on macOS in CI)
|
||||||
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"
|
||||||
@@ -301,6 +303,7 @@ if(UNIX)
|
|||||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice")
|
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice")
|
||||||
if(Qt6_FOUND)
|
if(Qt6_FOUND)
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt6multimedia6, libqt6svg6, qt6-qpa-plugins, qt6-image-formats-plugins")
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt6multimedia6, libqt6svg6, qt6-qpa-plugins, qt6-image-formats-plugins")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libqt6sql6-mysql") # for connecting servatrice to a mysql db
|
||||||
elseif(Qt5_FOUND)
|
elseif(Qt5_FOUND)
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5")
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5multimedia5-plugins, libqt5svg5")
|
||||||
endif()
|
endif()
|
||||||
@@ -325,7 +328,19 @@ endif()
|
|||||||
|
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
|
||||||
add_subdirectory(common)
|
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_interfaces ${CMAKE_BINARY_DIR}/libcockatrice_interfaces)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_protocol ${CMAKE_BINARY_DIR}/libcockatrice_protocol)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_network ${CMAKE_BINARY_DIR}/libcockatrice_network)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_deck_list ${CMAKE_BINARY_DIR}/libcockatrice_deck_list)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_rng ${CMAKE_BINARY_DIR}/libcockatrice_rng)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_card ${CMAKE_BINARY_DIR}/libcockatrice_card)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_utility ${CMAKE_BINARY_DIR}/libcockatrice_utility)
|
||||||
|
if(WITH_ORACLE OR WITH_CLIENT)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_settings ${CMAKE_BINARY_DIR}/libcockatrice_settings)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_models ${CMAKE_BINARY_DIR}/libcockatrice_models)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_filters ${CMAKE_BINARY_DIR}/libcockatrice_filters)
|
||||||
|
endif()
|
||||||
|
|
||||||
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})
|
||||||
|
|||||||
33
Dockerfile
@@ -1,8 +1,9 @@
|
|||||||
FROM ubuntu:24.04
|
# -------- Build Stage --------
|
||||||
|
FROM ubuntu:24.04 AS build
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y\
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
build-essential \
|
build-essential \
|
||||||
cmake \
|
cmake \
|
||||||
file \
|
file \
|
||||||
@@ -16,20 +17,28 @@ RUN apt-get update && apt-get install -y\
|
|||||||
qt6-tools-dev \
|
qt6-tools-dev \
|
||||||
qt6-tools-dev-tools
|
qt6-tools-dev-tools
|
||||||
|
|
||||||
COPY ./CMakeLists.txt ./LICENSE ./README.md /home/servatrice/code/
|
WORKDIR /src
|
||||||
COPY ./cmake /home/servatrice/code/cmake
|
COPY . .
|
||||||
COPY ./common /home/servatrice/code/common
|
RUN mkdir build && cd build && \
|
||||||
COPY ./servatrice /home/servatrice/code/servatrice
|
cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 && \
|
||||||
|
make -j$(nproc) && \
|
||||||
|
make install
|
||||||
|
|
||||||
WORKDIR /home/servatrice/code
|
|
||||||
|
|
||||||
WORKDIR build
|
# -------- Runtime Stage (clean) --------
|
||||||
RUN cmake .. -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 -DWITH_DBCONVERTER=0 &&\
|
FROM ubuntu:24.04
|
||||||
make &&\
|
|
||||||
make install
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libprotobuf32t64 \
|
||||||
|
libqt6sql6-mysql \
|
||||||
|
libqt6websockets6 \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Only copy installed binaries, not source
|
||||||
|
COPY --from=build /usr/local /usr/local
|
||||||
|
|
||||||
WORKDIR /home/servatrice
|
WORKDIR /home/servatrice
|
||||||
|
|
||||||
EXPOSE 4748
|
EXPOSE 4748
|
||||||
|
|
||||||
ENTRYPOINT [ "servatrice", "--log-to-console" ]
|
ENTRYPOINT [ "servatrice", "--log-to-console" ]
|
||||||
|
|||||||
9
LICENSE
@@ -2,7 +2,7 @@
|
|||||||
Version 2, June 1991
|
Version 2, June 1991
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
<https://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
@@ -304,8 +304,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
You should have received a copy of the GNU General Public License along
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
@@ -329,8 +328,8 @@ necessary. Here is a sample; alter the names:
|
|||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
<signature of Ty Coon>, 1 April 1989
|
<signature of Moe Ghoul>, 1 April 1989
|
||||||
Ty Coon, President of Vice
|
Moe Ghoul, President of Vice
|
||||||
|
|
||||||
This General Public License does not permit incorporating your program into
|
This General Public License does not permit incorporating your program into
|
||||||
proprietary programs. If your program is a subroutine library, you may
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
|||||||
32
README.md
@@ -8,7 +8,7 @@
|
|||||||
<a href="#related-repositories">Related</a> <b>|</b>
|
<a href="#related-repositories">Related</a> <b>|</b>
|
||||||
<a href="#community-resources-">Community</a> <b>|</b>
|
<a href="#community-resources-">Community</a> <b>|</b>
|
||||||
<a href="#contribute">Contribute</a> <b>|</b>
|
<a href="#contribute">Contribute</a> <b>|</b>
|
||||||
<a href="#build--">Build</a> <b>|</b>
|
<a href="#build---">Build</a> <b>|</b>
|
||||||
<a href="#run">Run</a>
|
<a href="#run">Run</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -38,15 +38,16 @@ Latest <kbd>stable</kbd> release:
|
|||||||
</pre><pre>
|
</pre><pre>
|
||||||
Latest <kbd>beta</kbd> version:
|
Latest <kbd>beta</kbd> version:
|
||||||
[](https://github.com/cockatrice/cockatrice/releases)  [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice&search=0) [](https://github.com/Cockatrice/Cockatrice/pulls?q=is%3Apr+is%3Aclosed)
|
[](https://github.com/cockatrice/cockatrice/releases)  [](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice&search=0) [](https://github.com/Cockatrice/Cockatrice/pulls?q=is%3Apr+is%3Aclosed)
|
||||||
<sub><i>While incorporating the latest fixes and features, beta builds may not be stable and/or contain new bugs!</i></sub>
|
<sub><i>While incorporating the latest fixes and features, beta builds may not be stable or contain new bugs!</i></sub>
|
||||||
<sub><b><i>Please report any findings when testing them!</i></b></sub>
|
<sub><b><i>Please report any findings and open new issues when testing them!</i></b></sub>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
# Related Repositories
|
# Related Repositories
|
||||||
|
|
||||||
- [Magic-Token](https://github.com/Cockatrice/Magic-Token): MtG token data to use in Cockatrice
|
- [Magic-Token](https://github.com/Cockatrice/Magic-Token): MtG token data to use in Cockatrice
|
||||||
- [Magic-Spoiler](https://github.com/Cockatrice/Magic-Spoiler): Script to generate MtG spoiler data from [MTGJSON](https://github.com/mtgjson/mtgjson) to use in Cockatrice
|
- [Magic-Spoiler](https://github.com/Cockatrice/Magic-Spoiler): Script to generate MtG spoiler data from [MTGJSON](https://github.com/mtgjson/mtgjson) to use in Cockatrice
|
||||||
- [cockatrice.github.io](https://github.com/Cockatrice/cockatrice.github.io): Code of the official webpage of the Cockatrice project
|
- [cockatrice.github.io](https://github.com/Cockatrice/cockatrice.github.io): Code of the official Cockatrice webpage
|
||||||
|
- [Cockatrice @Flathub](https://github.com/flathub/io.github.Cockatrice.cockatrice): Configuration for our Linux `flatpak` package
|
||||||
|
|
||||||
|
|
||||||
# Community Resources [](https://discord.gg/3Z9yzmA)
|
# Community Resources [](https://discord.gg/3Z9yzmA)
|
||||||
@@ -54,6 +55,7 @@ Latest <kbd>beta</kbd> version:
|
|||||||
Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other projet contributors (`#dev` channel) or fellow users of the app. Come here to talk about the application, features, or just to hang out.
|
Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other projet contributors (`#dev` channel) or fellow users of the app. Come here to talk about the application, features, or just to hang out.
|
||||||
- [Official Website](https://cockatrice.github.io)
|
- [Official Website](https://cockatrice.github.io)
|
||||||
- [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
|
- [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
|
||||||
|
- [Official Code Documentation](https://cockatrice.github.io/docs)
|
||||||
- [Official Discord](https://discord.gg/3Z9yzmA)
|
- [Official Discord](https://discord.gg/3Z9yzmA)
|
||||||
- [reddit r/Cockatrice](https://reddit.com/r/cockatrice)
|
- [reddit r/Cockatrice](https://reddit.com/r/cockatrice)
|
||||||
|
|
||||||
@@ -75,24 +77,30 @@ This tag is used for issues that we are looking for somebody to pick up. Often t
|
|||||||
For both tags, we're willing to provide help to contributors in showing them where and how they can make changes, as well as code reviews for submitted changes.<br>
|
For both tags, we're willing to provide help to contributors in showing them where and how they can make changes, as well as code reviews for submitted changes.<br>
|
||||||
We'll happily advice on how best to implement a feature, or we can show you where the codebase is doing something similar before you get too far along - put a note on an issue you want to discuss more on!
|
We'll happily advice on how best to implement a feature, or we can show you where the codebase is doing something similar before you get too far along - put a note on an issue you want to discuss more on!
|
||||||
|
|
||||||
|
You can also have a look at our `Todo List` in our [Code Documentation](https://cockatrice.github.io/docs) or search the repo for [`\todo` comments](https://github.com/search?q=repo%3ACockatrice%2FCockatrice%20%5Ctodo&type=code).
|
||||||
|
|
||||||
Cockatrice tries to use the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide.
|
Cockatrice tries to use the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide.
|
||||||
|
|
||||||
|
#### Repository Activity
|
||||||
|

|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>Kudos to our amazing contributors ❤️</b></summary>
|
<summary><b>Kudos to all our amazing contributors ❤️</b></summary>
|
||||||
|
<br>
|
||||||
<a href="https://github.com/Cockatrice/Cockatrice/graphs/contributors">
|
<a href="https://github.com/Cockatrice/Cockatrice/graphs/contributors">
|
||||||
<img src="https://contrib.rocks/image?repo=Cockatrice/Cockatrice" />
|
<img src="https://contrib.rocks/image?repo=Cockatrice/Cockatrice" />
|
||||||
</a><br>
|
</a><br>
|
||||||
<sub><i>Made with <a href="https://contrib.rocks">contrib.rocks</a>.</i></sub>
|
<sub><i>Made with <a href="https://contrib.rocks">contrib.rocks</a></i></sub>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Translations [](https://transifex.com/cockatrice/cockatrice/)
|
### Translations [](https://explore.transifex.com/cockatrice/cockatrice/)
|
||||||
|
|
||||||
Cockatrice uses Transifex to manage translations. You can help us bring <kbd>Cockatrice</kbd>, <kbd>Oracle</kbd> and <kbd>Webatrice</kbd> to your language and just adjust single wordings right from within your browser by visiting our [Transifex project page](https://transifex.com/cockatrice/cockatrice/).<br>
|
Cockatrice uses Transifex to manage translations. You can help us bring <kbd>Cockatrice</kbd>, <kbd>Oracle</kbd> and <kbd>Webatrice</kbd> to your language and just adjust single wordings right from within your browser by visiting our [Transifex project page](https://explore.transifex.com/cockatrice/cockatrice/).<br>
|
||||||
|
|
||||||
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about getting invovled, and join a group of hundreds of others!<br>
|
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about getting invovled, and join a group of hundreds of others!<br>
|
||||||
|
|
||||||
|
|
||||||
# Build [](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml?query=branch%3Amaster+event%3Apush) [](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml?query=branch%3Amaster+event%3Apush)
|
# Build [](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml?query=branch%3Amaster+event%3Apush) [](https://github.com/Cockatrice/Cockatrice/actions/workflows/docker-release.yml?query=branch%3Amaster+event%3Apush) [](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml?query=branch%3Amaster+event%3Apush)
|
||||||
|
|
||||||
Dependencies: *(for minimum versions search our [CMake file](https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt))*
|
Dependencies: *(for minimum versions search our [CMake file](https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt))*
|
||||||
- [Qt](https://www.qt.io/developers/)
|
- [Qt](https://www.qt.io/developers/)
|
||||||
@@ -149,10 +157,10 @@ The following flags (with their non-default values) can be passed to `cmake`:
|
|||||||
|
|
||||||
#### Docker
|
#### Docker
|
||||||
|
|
||||||
You can run an instance of <kbd>Servatrice</kbd> (the Cockatrice server) using [Docker](https://www.docker.com/resources/what-container/) and our Dockerfile.<br>
|
You can build an image & deploy a <kbd>Servatrice</kbd> (Cockatrice server) container using [Docker](https://www.docker.com/resources/what-container/) and our Dockerfile yourself.<br>
|
||||||
|
|
||||||
For more information, have a look in our wiki section on [Setting up Servatrice](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).<br>
|
For more details, look into our wiki section on [Setting up Servatrice](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).<br>
|
||||||
There, you'll also find more hints on our **docker-compose** file which will configure and run both a MySQL server and Servatrice.
|
You'll also find more hints on our **pre-build image** there, or the **docker-compose** file which will configure and run both a MySQL server and Servatrice.
|
||||||
|
|
||||||
|
|
||||||
# License [](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE)
|
# License [](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE)
|
||||||
|
|||||||
@@ -115,4 +115,7 @@ 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}")
|
||||||
|
|||||||
@@ -7,244 +7,286 @@ 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/game_logic/abstract_client.cpp
|
src/client/network/update/client/update_downloader.cpp
|
||||||
src/client/game_logic/key_signals.cpp
|
src/client/network/interfaces/deck_stats_interface.cpp
|
||||||
src/client/get_text_with_max.cpp
|
src/client/network/interfaces/tapped_out_interface.cpp
|
||||||
src/client/menus/deck_editor/deck_editor_menu.cpp
|
src/client/network/parsers/deck_link_to_api_transformer.cpp
|
||||||
src/client/network/client_update_checker.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/sound_engine.cpp
|
src/client/sound_engine.cpp
|
||||||
src/client/tabs/abstract_tab_deck_editor.cpp
|
src/client/settings/cache_settings.cpp
|
||||||
src/client/tabs/api/edhrec/tab_edhrec.cpp
|
src/client/settings/card_counter_settings.cpp
|
||||||
src/client/tabs/api/edhrec/tab_edhrec_main.cpp
|
src/client/settings/shortcut_treeview.cpp
|
||||||
src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp
|
src/client/settings/shortcuts_settings.cpp
|
||||||
src/client/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
|
src/interface/deck_loader/card_node_function.cpp
|
||||||
src/client/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
|
src/interface/deck_loader/deck_file_format.cpp
|
||||||
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
|
src/interface/deck_loader/deck_loader.cpp
|
||||||
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_inclusion_display_widget.cpp
|
src/interface/deck_loader/loaded_deck.cpp
|
||||||
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
|
src/interface/widgets/dialogs/dlg_connect.cpp
|
||||||
src/client/tabs/api/edhrec/display/cards/edhrec_api_response_card_synergy_display_widget.cpp
|
src/interface/widgets/dialogs/dlg_convert_deck_to_cod_format.cpp
|
||||||
src/client/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
|
src/interface/widgets/dialogs/dlg_create_game.cpp
|
||||||
src/client/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp
|
src/interface/widgets/dialogs/dlg_default_tags_editor.cpp
|
||||||
src/client/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp
|
src/interface/widgets/dialogs/dlg_edit_avatar.cpp
|
||||||
src/client/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp
|
src/interface/widgets/dialogs/dlg_edit_password.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
|
src/interface/widgets/dialogs/dlg_edit_tokens.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp
|
src/interface/widgets/dialogs/dlg_edit_user.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp
|
src/interface/widgets/dialogs/dlg_filter_games.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp
|
src/interface/widgets/dialogs/dlg_forgot_password_challenge.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp
|
src/interface/widgets/dialogs/dlg_forgot_password_request.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp
|
src/interface/widgets/dialogs/dlg_forgot_password_reset.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp
|
src/interface/widgets/dialogs/dlg_load_deck.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp
|
src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
|
src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
|
src/interface/widgets/dialogs/dlg_load_remote_deck.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
|
src/interface/widgets/dialogs/dlg_manage_sets.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
|
src/interface/widgets/dialogs/dlg_register.cpp
|
||||||
src/client/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
|
src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp
|
||||||
src/client/tabs/tab.cpp
|
src/interface/widgets/dialogs/dlg_settings.cpp
|
||||||
src/client/tabs/tab_account.cpp
|
src/interface/widgets/dialogs/dlg_startup_card_check.cpp
|
||||||
src/client/tabs/tab_admin.cpp
|
src/interface/widgets/dialogs/dlg_tip_of_the_day.cpp
|
||||||
src/client/tabs/tab_deck_editor.cpp
|
src/interface/widgets/dialogs/dlg_update.cpp
|
||||||
src/client/tabs/tab_deck_storage.cpp
|
src/interface/widgets/dialogs/dlg_view_log.cpp
|
||||||
src/client/tabs/tab_game.cpp
|
src/interface/widgets/dialogs/tip_of_the_day.cpp
|
||||||
src/client/tabs/tab_logs.cpp
|
src/filters/deck_filter_string.cpp
|
||||||
src/client/tabs/tab_message.cpp
|
src/filters/filter_builder.cpp
|
||||||
src/client/tabs/tab_replays.cpp
|
src/filters/filter_tree_model.cpp
|
||||||
src/client/tabs/tab_room.cpp
|
src/filters/syntax_help.cpp
|
||||||
src/client/tabs/tab_server.cpp
|
src/game/abstract_game.cpp
|
||||||
src/client/tabs/tab_supervisor.cpp
|
|
||||||
src/client/tabs/tab_visual_database_display.cpp
|
|
||||||
src/client/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
|
|
||||||
src/client/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp
|
|
||||||
src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
|
|
||||||
src/client/tapped_out_interface.cpp
|
|
||||||
src/client/translate_counter_name.cpp
|
|
||||||
src/client/ui/layouts/flow_layout.cpp
|
|
||||||
src/client/ui/layouts/overlap_layout.cpp
|
|
||||||
src/client/ui/line_edit_completer.cpp
|
|
||||||
src/client/ui/phases_toolbar.cpp
|
|
||||||
src/client/ui/picture_loader/picture_loader.cpp
|
|
||||||
src/client/ui/picture_loader/picture_loader_worker.cpp
|
|
||||||
src/client/ui/picture_loader/picture_to_load.cpp
|
|
||||||
src/client/ui/pixel_map_generator.cpp
|
|
||||||
src/client/ui/theme_manager.cpp
|
|
||||||
src/client/ui/tip_of_the_day.cpp
|
|
||||||
src/client/ui/widgets/cards/additional_info/color_identity_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/additional_info/mana_cost_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/additional_info/mana_symbol_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/card_info_display_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/card_info_frame_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/card_info_picture_enlarged_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/card_info_picture_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/card_info_text_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/card_size_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/deck_card_zone_display_widget.cpp
|
|
||||||
src/client/ui/widgets/cards/deck_preview_card_picture_widget.cpp
|
|
||||||
src/client/ui/widgets/deck_analytics/deck_analytics_widget.cpp
|
|
||||||
src/client/ui/widgets/deck_analytics/mana_base_widget.cpp
|
|
||||||
src/client/ui/widgets/deck_analytics/mana_curve_widget.cpp
|
|
||||||
src/client/ui/widgets/deck_analytics/mana_devotion_widget.cpp
|
|
||||||
src/client/ui/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp
|
|
||||||
src/client/ui/widgets/deck_editor/deck_editor_database_display_widget.cpp
|
|
||||||
src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
|
|
||||||
src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.cpp
|
|
||||||
src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
|
|
||||||
src/client/ui/widgets/general/display/banner_widget.cpp
|
|
||||||
src/client/ui/widgets/general/display/bar_widget.cpp
|
|
||||||
src/client/ui/widgets/general/display/dynamic_font_size_label.cpp
|
|
||||||
src/client/ui/widgets/general/display/dynamic_font_size_push_button.cpp
|
|
||||||
src/client/ui/widgets/general/display/labeled_input.cpp
|
|
||||||
src/client/ui/widgets/general/display/percent_bar_widget.cpp
|
|
||||||
src/client/ui/widgets/general/display/shadow_background_label.cpp
|
|
||||||
src/client/ui/widgets/general/layout_containers/flow_widget.cpp
|
|
||||||
src/client/ui/widgets/general/layout_containers/overlap_control_widget.cpp
|
|
||||||
src/client/ui/widgets/general/layout_containers/overlap_widget.cpp
|
|
||||||
src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp
|
|
||||||
src/client/ui/widgets/printing_selector/card_amount_widget.cpp
|
|
||||||
src/client/ui/widgets/printing_selector/printing_selector.cpp
|
|
||||||
src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp
|
|
||||||
src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp
|
|
||||||
src/client/ui/widgets/printing_selector/printing_selector_card_search_widget.cpp
|
|
||||||
src/client/ui/widgets/printing_selector/printing_selector_card_selection_widget.cpp
|
|
||||||
src/client/ui/widgets/printing_selector/printing_selector_card_sorting_widget.cpp
|
|
||||||
src/client/ui/widgets/printing_selector/set_name_and_collectors_number_display_widget.cpp
|
|
||||||
src/client/ui/widgets/quick_settings/settings_button_widget.cpp
|
|
||||||
src/client/ui/widgets/quick_settings/settings_popup_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_database_display/visual_database_display_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_database_display/visual_database_display_sub_type_filter_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_database_display/visual_database_filter_display_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_editor/visual_deck_editor_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_item_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
|
|
||||||
src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
|
|
||||||
src/client/ui/window_main.cpp
|
|
||||||
src/client/update_downloader.cpp
|
|
||||||
src/deck/custom_line_edit.cpp
|
|
||||||
src/deck/deck_list_model.cpp
|
|
||||||
src/deck/deck_loader.cpp
|
|
||||||
src/deck/deck_stats_interface.cpp
|
|
||||||
src/dialogs/dlg_connect.cpp
|
|
||||||
src/dialogs/dlg_convert_deck_to_cod_format.cpp
|
|
||||||
src/dialogs/dlg_create_game.cpp
|
|
||||||
src/dialogs/dlg_create_token.cpp
|
|
||||||
src/dialogs/dlg_edit_avatar.cpp
|
|
||||||
src/dialogs/dlg_edit_password.cpp
|
|
||||||
src/dialogs/dlg_edit_tokens.cpp
|
|
||||||
src/dialogs/dlg_edit_user.cpp
|
|
||||||
src/dialogs/dlg_filter_games.cpp
|
|
||||||
src/dialogs/dlg_forgot_password_challenge.cpp
|
|
||||||
src/dialogs/dlg_forgot_password_request.cpp
|
|
||||||
src/dialogs/dlg_forgot_password_reset.cpp
|
|
||||||
src/dialogs/dlg_load_deck.cpp
|
|
||||||
src/dialogs/dlg_load_deck_from_clipboard.cpp
|
|
||||||
src/dialogs/dlg_load_remote_deck.cpp
|
|
||||||
src/dialogs/dlg_manage_sets.cpp
|
|
||||||
src/dialogs/dlg_move_top_cards_until.cpp
|
|
||||||
src/dialogs/dlg_register.cpp
|
|
||||||
src/dialogs/dlg_roll_dice.cpp
|
|
||||||
src/dialogs/dlg_settings.cpp
|
|
||||||
src/dialogs/dlg_tip_of_the_day.cpp
|
|
||||||
src/dialogs/dlg_update.cpp
|
|
||||||
src/dialogs/dlg_view_log.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
|
||||||
src/game/board/card_item.cpp
|
src/game/board/card_item.cpp
|
||||||
src/game/board/card_list.cpp
|
src/game/board/card_list.cpp
|
||||||
src/game/board/counter_general.cpp
|
src/game/board/counter_general.cpp
|
||||||
src/game/cards/card_completer_proxy_model.cpp
|
src/game/board/translate_counter_name.cpp
|
||||||
src/game/cards/card_database.cpp
|
|
||||||
src/game/cards/card_database_manager.cpp
|
|
||||||
src/game/cards/card_database_model.cpp
|
|
||||||
src/game/cards/card_database_parser/card_database_parser.cpp
|
|
||||||
src/game/cards/card_database_parser/cockatrice_xml_3.cpp
|
|
||||||
src/game/cards/card_database_parser/cockatrice_xml_4.cpp
|
|
||||||
src/game/cards/card_info.cpp
|
|
||||||
src/game/cards/card_search_model.cpp
|
|
||||||
src/game/deckview/deck_view.cpp
|
src/game/deckview/deck_view.cpp
|
||||||
src/game/deckview/deck_view_container.cpp
|
src/game/deckview/deck_view_container.cpp
|
||||||
src/game/filters/filter_builder.cpp
|
src/game/deckview/tabbed_deck_view_container.cpp
|
||||||
src/game/filters/filter_card.cpp
|
src/game/dialogs/dlg_create_token.cpp
|
||||||
src/game/filters/filter_string.cpp
|
src/game/dialogs/dlg_move_top_cards_until.cpp
|
||||||
src/game/filters/filter_tree.cpp
|
src/game/dialogs/dlg_roll_dice.cpp
|
||||||
src/game/filters/filter_tree_model.cpp
|
src/game/game.cpp
|
||||||
src/game/filters/syntax_help.cpp
|
src/game/game_event_handler.cpp
|
||||||
|
src/game/game_meta_info.cpp
|
||||||
src/game/game_scene.cpp
|
src/game/game_scene.cpp
|
||||||
src/game/game_selector.cpp
|
src/game/game_state.cpp
|
||||||
src/game/game_view.cpp
|
src/game/game_view.cpp
|
||||||
src/game/games_model.cpp
|
|
||||||
src/game/hand_counter.cpp
|
src/game/hand_counter.cpp
|
||||||
|
src/game/log/message_log_widget.cpp
|
||||||
src/game/phase.cpp
|
src/game/phase.cpp
|
||||||
|
src/game/phases_toolbar.cpp
|
||||||
|
src/game/player/menu/card_menu.cpp
|
||||||
|
src/game/player/menu/custom_zone_menu.cpp
|
||||||
|
src/game/player/menu/grave_menu.cpp
|
||||||
|
src/game/player/menu/hand_menu.cpp
|
||||||
|
src/game/player/menu/library_menu.cpp
|
||||||
|
src/game/player/menu/move_menu.cpp
|
||||||
|
src/game/player/menu/player_menu.cpp
|
||||||
|
src/game/player/menu/pt_menu.cpp
|
||||||
|
src/game/player/menu/rfg_menu.cpp
|
||||||
|
src/game/player/menu/say_menu.cpp
|
||||||
|
src/game/player/menu/sideboard_menu.cpp
|
||||||
|
src/game/player/menu/utility_menu.cpp
|
||||||
src/game/player/player.cpp
|
src/game/player/player.cpp
|
||||||
|
src/game/player/player_actions.cpp
|
||||||
|
src/game/player/player_area.cpp
|
||||||
|
src/game/player/player_event_handler.cpp
|
||||||
|
src/game/player/player_graphics_item.cpp
|
||||||
|
src/game/player/player_info.cpp
|
||||||
src/game/player/player_list_widget.cpp
|
src/game/player/player_list_widget.cpp
|
||||||
|
src/game/player/player_manager.cpp
|
||||||
src/game/player/player_target.cpp
|
src/game/player/player_target.cpp
|
||||||
|
src/game/replay.cpp
|
||||||
src/game/zones/card_zone.cpp
|
src/game/zones/card_zone.cpp
|
||||||
src/game/zones/hand_zone.cpp
|
src/game/zones/hand_zone.cpp
|
||||||
|
src/game/zones/logic/card_zone_logic.cpp
|
||||||
|
src/game/zones/logic/hand_zone_logic.cpp
|
||||||
|
src/game/zones/logic/pile_zone_logic.cpp
|
||||||
|
src/game/zones/logic/stack_zone_logic.cpp
|
||||||
|
src/game/zones/logic/table_zone_logic.cpp
|
||||||
|
src/game/zones/logic/view_zone_logic.cpp
|
||||||
src/game/zones/pile_zone.cpp
|
src/game/zones/pile_zone.cpp
|
||||||
src/game/zones/select_zone.cpp
|
src/game/zones/select_zone.cpp
|
||||||
src/game/zones/stack_zone.cpp
|
src/game/zones/stack_zone.cpp
|
||||||
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/overlap_layout.cpp
|
||||||
|
src/interface/widgets/utility/line_edit_completer.cpp
|
||||||
|
src/interface/pixel_map_generator.cpp
|
||||||
|
src/interface/theme_manager.cpp
|
||||||
|
src/interface/widgets/cards/additional_info/color_identity_widget.cpp
|
||||||
|
src/interface/widgets/cards/additional_info/mana_cost_widget.cpp
|
||||||
|
src/interface/widgets/cards/additional_info/mana_symbol_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_group_display_widgets/card_group_display_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_group_display_widgets/flat_card_group_display_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_group_display_widgets/overlapped_card_group_display_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_info_display_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_info_frame_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_info_picture_art_crop_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_info_picture_enlarged_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_info_picture_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_info_picture_with_text_overlay_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_info_text_widget.cpp
|
||||||
|
src/interface/widgets/cards/card_size_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/deck_analytics/deck_analytics_widget.cpp
|
||||||
|
src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp
|
||||||
|
src/interface/widgets/deck_analytics/mana_base_widget.cpp
|
||||||
|
src/interface/widgets/deck_analytics/mana_curve_widget.cpp
|
||||||
|
src/interface/widgets/deck_analytics/mana_devotion_widget.cpp
|
||||||
|
src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp
|
||||||
|
src/interface/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp
|
||||||
|
src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp
|
||||||
|
src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
|
||||||
|
src/interface/widgets/deck_editor/deck_editor_filter_dock_widget.cpp
|
||||||
|
src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
|
||||||
|
src/interface/widgets/deck_editor/deck_list_style_proxy.cpp
|
||||||
|
src/interface/widgets/general/background_sources.cpp
|
||||||
|
src/interface/widgets/general/display/background_plate_widget.cpp
|
||||||
|
src/interface/widgets/general/display/banner_widget.cpp
|
||||||
|
src/interface/widgets/general/display/bar_widget.cpp
|
||||||
|
src/interface/widgets/general/display/color_bar.cpp
|
||||||
|
src/interface/widgets/general/display/dynamic_font_size_label.cpp
|
||||||
|
src/interface/widgets/general/display/dynamic_font_size_push_button.cpp
|
||||||
|
src/interface/widgets/general/display/labeled_input.cpp
|
||||||
|
src/interface/widgets/general/display/percent_bar_widget.cpp
|
||||||
|
src/interface/widgets/general/display/shadow_background_label.cpp
|
||||||
|
src/interface/widgets/general/home_styled_button.cpp
|
||||||
|
src/interface/widgets/general/home_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_widget.cpp
|
||||||
|
src/interface/widgets/menus/deck_editor_menu.cpp
|
||||||
|
src/interface/widgets/printing_selector/all_zones_card_amount_widget.cpp
|
||||||
|
src/interface/widgets/printing_selector/card_amount_widget.cpp
|
||||||
|
src/interface/widgets/printing_selector/printing_selector.cpp
|
||||||
|
src/interface/widgets/printing_selector/printing_selector_card_display_widget.cpp
|
||||||
|
src/interface/widgets/printing_selector/printing_selector_card_overlay_widget.cpp
|
||||||
|
src/interface/widgets/printing_selector/printing_selector_card_search_widget.cpp
|
||||||
|
src/interface/widgets/printing_selector/printing_selector_card_selection_widget.cpp
|
||||||
|
src/interface/widgets/printing_selector/printing_selector_card_sorting_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_popup_widget.cpp
|
||||||
|
src/interface/widgets/replay/replay_manager.cpp
|
||||||
|
src/interface/widgets/replay/replay_timeline_widget.cpp
|
||||||
|
src/interface/widgets/server/chat_view/chat_view.cpp
|
||||||
|
src/interface/widgets/server/game_selector.cpp
|
||||||
|
src/interface/widgets/server/game_selector_quick_filter_toolbar.cpp
|
||||||
|
src/interface/widgets/server/games_model.cpp
|
||||||
|
src/interface/widgets/server/handle_public_servers.cpp
|
||||||
|
src/interface/widgets/server/remote/remote_decklist_tree_widget.cpp
|
||||||
|
src/interface/widgets/server/remote/remote_replay_list_tree_widget.cpp
|
||||||
|
src/interface/widgets/server/user/user_context_menu.cpp
|
||||||
|
src/interface/widgets/server/user/user_info_box.cpp
|
||||||
|
src/interface/widgets/server/user/user_info_connection.cpp
|
||||||
|
src/interface/widgets/server/user/user_list_manager.cpp
|
||||||
|
src/interface/widgets/server/user/user_list_widget.cpp
|
||||||
|
src/interface/widgets/utility/custom_line_edit.cpp
|
||||||
|
src/interface/widgets/utility/get_text_with_max.cpp
|
||||||
|
src/interface/widgets/utility/sequence_edit.cpp
|
||||||
|
src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
|
||||||
|
src/interface/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
|
||||||
|
src/interface/widgets/visual_database_display/visual_database_display_format_legality_filter_widget.cpp
|
||||||
|
src/interface/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
|
||||||
|
src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
|
||||||
|
src/interface/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
|
||||||
|
src/interface/widgets/visual_database_display/visual_database_display_sub_type_filter_widget.cpp
|
||||||
|
src/interface/widgets/visual_database_display/visual_database_display_widget.cpp
|
||||||
|
src/interface/widgets/visual_database_display/visual_database_filter_display_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_editor/visual_deck_display_options_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_dialog.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_display_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_tag_item_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/visual_deck_storage_folder_display_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
|
||||||
|
src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
|
||||||
|
src/interface/window_main.cpp
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/server/chat_view/chat_view.cpp
|
src/interface/widgets/tabs/abstract_tab_deck_editor.cpp
|
||||||
src/server/handle_public_servers.cpp
|
src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp
|
||||||
src/server/local_client.cpp
|
src/interface/widgets/tabs/api/archidekt/api_response/archidekt_deck_listing_api_response.cpp
|
||||||
src/server/local_server.cpp
|
src/interface/widgets/tabs/api/archidekt/api_response/archidekt_formats.h
|
||||||
src/server/local_server_interface.cpp
|
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card.cpp
|
||||||
src/server/message_log_widget.cpp
|
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_card_entry.cpp
|
||||||
src/server/pending_command.cpp
|
src/interface/widgets/tabs/api/archidekt/api_response/card/archidekt_api_response_edition.cpp
|
||||||
src/server/remote/remote_client.cpp
|
src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck.cpp
|
||||||
src/server/remote/remote_decklist_tree_widget.cpp
|
src/interface/widgets/tabs/api/archidekt/api_response/deck/archidekt_api_response_deck_category.cpp
|
||||||
src/server/remote/remote_replay_list_tree_widget.cpp
|
src/interface/widgets/tabs/api/archidekt/api_response/deck_listings/archidekt_api_response_deck_listing_container.cpp
|
||||||
src/server/user/user_context_menu.cpp
|
src/interface/widgets/tabs/api/archidekt/api_response/deck_listings/archidekt_api_response_deck_owner.cpp
|
||||||
src/server/user/user_info_box.cpp
|
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp
|
||||||
src/server/user/user_info_connection.cpp
|
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp
|
||||||
src/server/user/user_list_manager.cpp
|
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp
|
||||||
src/server/user/user_list_widget.cpp
|
src/interface/widgets/tabs/api/archidekt/display/archidekt_deck_preview_image_display_widget.cpp
|
||||||
src/settings/cache_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
|
||||||
src/settings/card_database_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
|
||||||
src/settings/card_override_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
|
||||||
src/settings/debug_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/card_prices/edhrec_api_response_card_prices.cpp
|
||||||
src/settings/download_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_container.cpp
|
||||||
src/settings/game_filters_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_details.cpp
|
||||||
src/settings/layouts_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_api_response_card_list.cpp
|
||||||
src/settings/message_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/cards/edhrec_commander_api_response_commander_details.cpp
|
||||||
src/settings/recents_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response.cpp
|
||||||
src/settings/servers_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/commander/edhrec_commander_api_response_average_deck_statistics.cpp
|
||||||
src/settings/settings_manager.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/top_cards/edhrec_top_cards_api_response.cpp
|
||||||
src/settings/shortcut_treeview.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/top_commanders/edhrec_top_commanders_api_response.cpp
|
||||||
src/settings/shortcuts_settings.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/top_tags/edhrec_top_tags_api_response.cpp
|
||||||
src/utility/card_info_comparator.cpp
|
src/interface/widgets/tabs/api/edhrec/display/card_prices/edhrec_api_response_card_prices_display_widget.cpp
|
||||||
src/utility/levenshtein.cpp
|
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_details_display_widget.cpp
|
||||||
src/utility/logger.cpp
|
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_inclusion_display_widget.cpp
|
||||||
src/utility/sequence_edit.cpp
|
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_list_display_widget.cpp
|
||||||
|
src/interface/widgets/tabs/api/edhrec/display/cards/edhrec_api_response_card_synergy_display_widget.cpp
|
||||||
|
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_api_response_commander_details_display_widget.cpp
|
||||||
|
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_display_widget.cpp
|
||||||
|
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_navigation_widget.cpp
|
||||||
|
src/interface/widgets/tabs/api/edhrec/display/top_cards/edhrec_top_cards_api_response_display_widget.cpp
|
||||||
|
src/interface/widgets/tabs/api/edhrec/display/top_commander/edhrec_top_commanders_api_response_display_widget.cpp
|
||||||
|
src/interface/widgets/tabs/api/edhrec/display/top_tags/edhrec_top_tags_api_response_display_widget.cpp
|
||||||
|
src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp
|
||||||
|
src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp
|
||||||
|
src/interface/widgets/tabs/tab.cpp
|
||||||
|
src/interface/widgets/tabs/tab_account.cpp
|
||||||
|
src/interface/widgets/tabs/tab_admin.cpp
|
||||||
|
src/interface/widgets/tabs/tab_deck_editor.cpp
|
||||||
|
src/interface/widgets/tabs/tab_deck_storage.cpp
|
||||||
|
src/interface/widgets/tabs/tab_game.cpp
|
||||||
|
src/interface/widgets/tabs/tab_home.cpp
|
||||||
|
src/interface/widgets/tabs/tab_logs.cpp
|
||||||
|
src/interface/widgets/tabs/tab_message.cpp
|
||||||
|
src/interface/widgets/tabs/tab_replays.cpp
|
||||||
|
src/interface/widgets/tabs/tab_room.cpp
|
||||||
|
src/interface/widgets/tabs/tab_server.cpp
|
||||||
|
src/interface/widgets/tabs/tab_supervisor.cpp
|
||||||
|
src/interface/widgets/tabs/tab_visual_database_display.cpp
|
||||||
|
src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual.cpp
|
||||||
|
src/interface/widgets/tabs/visual_deck_editor/tab_deck_editor_visual_tab_widget.cpp
|
||||||
|
src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
|
||||||
|
src/interface/key_signals.cpp
|
||||||
|
src/interface/logger.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(sounds)
|
add_subdirectory(sounds)
|
||||||
@@ -256,15 +298,23 @@ 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)
|
|
||||||
set(translate_SRCS ${translate_cockatrice_SRCS} ${translate_common_SRCS})
|
# All libcockatrice_* libraries (recursively)
|
||||||
|
file(GLOB_RECURSE translate_lib_SRCS ${CMAKE_SOURCE_DIR}/libcockatrice_*/**/*.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/libcockatrice_*/**/*.h
|
||||||
|
)
|
||||||
|
|
||||||
|
# Combine all sources for translation
|
||||||
|
set(translate_SRCS ${translate_cockatrice_SRCS} ${translate_lib_SRCS})
|
||||||
|
|
||||||
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(UPDATE_TRANSLATIONS)
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(cockatrice_SOURCES ${cockatrice_SOURCES} cockatrice.rc)
|
set(cockatrice_SOURCES ${cockatrice_SOURCES} cockatrice.rc)
|
||||||
@@ -294,12 +344,6 @@ 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")
|
||||||
@@ -339,9 +383,31 @@ elseif(Qt5_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(Qt5_FOUND)
|
if(Qt5_FOUND)
|
||||||
target_link_libraries(cockatrice cockatrice_common ${COCKATRICE_QT_MODULES})
|
target_link_libraries(
|
||||||
|
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(cockatrice PUBLIC cockatrice_common ${COCKATRICE_QT_MODULES})
|
target_link_libraries(
|
||||||
|
cockatrice
|
||||||
|
PUBLIC libcockatrice_card
|
||||||
|
libcockatrice_deck_list
|
||||||
|
libcockatrice_filters
|
||||||
|
libcockatrice_utility
|
||||||
|
libcockatrice_network
|
||||||
|
libcockatrice_models
|
||||||
|
libcockatrice_rng
|
||||||
|
libcockatrice_settings
|
||||||
|
${COCKATRICE_QT_MODULES}
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
|
|||||||
@@ -7,11 +7,14 @@
|
|||||||
|
|
||||||
<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>
|
||||||
@@ -25,6 +28,7 @@
|
|||||||
<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>
|
||||||
@@ -33,6 +37,7 @@
|
|||||||
<file>resources/icons/scales.svg</file>
|
<file>resources/icons/scales.svg</file>
|
||||||
<file>resources/icons/search.svg</file>
|
<file>resources/icons/search.svg</file>
|
||||||
<file>resources/icons/settings.svg</file>
|
<file>resources/icons/settings.svg</file>
|
||||||
|
<file>resources/icons/share.svg</file>
|
||||||
<file>resources/icons/spectator.svg</file>
|
<file>resources/icons/spectator.svg</file>
|
||||||
<file>resources/icons/swap.svg</file>
|
<file>resources/icons/swap.svg</file>
|
||||||
<file>resources/icons/sync.svg</file>
|
<file>resources/icons/sync.svg</file>
|
||||||
@@ -46,6 +51,8 @@
|
|||||||
<file>resources/icons/mana/U.svg</file>
|
<file>resources/icons/mana/U.svg</file>
|
||||||
<file>resources/icons/mana/W.svg</file>
|
<file>resources/icons/mana/W.svg</file>
|
||||||
|
|
||||||
|
<file>resources/backgrounds/home.png</file>
|
||||||
|
|
||||||
<file>resources/config/general.svg</file>
|
<file>resources/config/general.svg</file>
|
||||||
<file>resources/config/appearance.svg</file>
|
<file>resources/config/appearance.svg</file>
|
||||||
<file>resources/config/interface.svg</file>
|
<file>resources/config/interface.svg</file>
|
||||||
@@ -379,5 +386,6 @@
|
|||||||
<file>resources/tips/tips_of_the_day.xml</file>
|
<file>resources/tips/tips_of_the_day.xml</file>
|
||||||
|
|
||||||
<file>resources/help/search.md</file>
|
<file>resources/help/search.md</file>
|
||||||
|
<file>resources/help/deck_search.md</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
BIN
cockatrice/resources/backgrounds/home.png
Normal file
|
After Width: | Height: | Size: 12 MiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 9.9 KiB |
@@ -1,63 +1,72 @@
|
|||||||
[Rules]
|
[Rules]
|
||||||
# The default log level is info
|
# The default log level is info
|
||||||
*.debug = false
|
*.debug = false
|
||||||
|
#*.info = true
|
||||||
|
#*.warning = true
|
||||||
|
#*.critical = true
|
||||||
|
#*.fatal = true
|
||||||
|
|
||||||
# Uncomment a rule to disable logging for that category,
|
# Uncomment a rule to see debug level logs for that category,
|
||||||
# or set .debug = true for that category to see debug level logs
|
# or set <category> = false to disable logging
|
||||||
|
|
||||||
# main = false
|
#main = true
|
||||||
# qt_translator = false
|
#qt_translator = true
|
||||||
# window_main.* = false
|
#window_main.* = true
|
||||||
# release_channel = false
|
#release_channel = true
|
||||||
# spoiler_background_updater = false
|
#spoiler_background_updater = true
|
||||||
# theme_manager = false
|
#theme_manager = true
|
||||||
# sound_engine = false
|
#sound_engine = true
|
||||||
# tapped_out_interface = false
|
#tapped_out_interface = true
|
||||||
|
|
||||||
# tab_game = false
|
#tab_game = true
|
||||||
# tab_message = false
|
#tab_message = true
|
||||||
# tab_supervisor = false
|
#tab_supervisor = true
|
||||||
|
|
||||||
# dlg_edit_avatar = false
|
#dlg_edit_avatar = true
|
||||||
# dlg_settings = false
|
#dlg_load_deck_from_website = true
|
||||||
# dlg_tip_of_the_day = false
|
#dlg_settings = true
|
||||||
# dlg_update = false
|
#dlg_tip_of_the_day = true
|
||||||
|
#dlg_update = true
|
||||||
|
|
||||||
# settings_cache = false
|
#settings_cache = true
|
||||||
# servers_settings = false
|
#servers_settings = true
|
||||||
# shortcuts_settings = false
|
#shortcuts_settings = true
|
||||||
|
|
||||||
# local_client = false
|
#local_client = true
|
||||||
# remote_client = false
|
#remote_client = true
|
||||||
|
|
||||||
# player = false
|
#player = true
|
||||||
# game_scene = false
|
#game_scene = true
|
||||||
# game_scene.player_addition_removal = false
|
#game_scene.player_addition_removal = true
|
||||||
# card_zone = false
|
#card_zone = true
|
||||||
# view_zone = false
|
#view_zone = true
|
||||||
|
|
||||||
# user_info_connection = false
|
#game_event_handler = true
|
||||||
|
|
||||||
# picture_loader = false
|
#user_info_connection = true
|
||||||
# picture_loader.worker = false
|
|
||||||
# picture_loader.card_back_cache_fail = false
|
|
||||||
# picture_loader.picture_to_load = false
|
|
||||||
# deck_loader = false
|
|
||||||
# card_database = false
|
|
||||||
# card_database.loading = false
|
|
||||||
# card_database.loading.success_or_failure = false
|
|
||||||
# cockatrice_xml.* = false
|
|
||||||
# cockatrice_xml.xml_3_parser = false
|
|
||||||
# cockatrice_xml.xml_4_parser = false
|
|
||||||
# card_info = false
|
|
||||||
# card_list = false
|
|
||||||
|
|
||||||
#flow_layout = false
|
#card_picture_loader = true
|
||||||
#flow_widget = false
|
#card_picture_loader.worker = true
|
||||||
#flow_widget.size = false
|
#card_picture_loader.card_back_cache_fail = true
|
||||||
|
#card_picture_loader.picture_to_load = true
|
||||||
|
#deck_loader = true
|
||||||
|
#card_database = true
|
||||||
|
#card_database.loading = true
|
||||||
|
#card_database.loading.success_or_failure = true
|
||||||
|
#cockatrice_xml.* = true
|
||||||
|
#cockatrice_xml.xml_3_parser = true
|
||||||
|
#cockatrice_xml.xml_4_parser = true
|
||||||
|
#card_info = true
|
||||||
|
#card_list = true
|
||||||
|
|
||||||
# card_info_picture_widget = false
|
#flow_layout = true
|
||||||
|
#flow_widget = true
|
||||||
|
#flow_widget.size = true
|
||||||
|
|
||||||
# pixel_map_generator = false
|
#card_info_picture_widget = true
|
||||||
|
|
||||||
# filter_string = false
|
#pixel_map_generator = true
|
||||||
|
|
||||||
|
#deck_filter_string = true
|
||||||
|
#filter_string = true
|
||||||
|
#syntax_help = true
|
||||||
|
|||||||
45
cockatrice/resources/help/deck_search.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
@page deck_search_syntax_help Deck Search Syntax Help
|
||||||
|
|
||||||
|
## Deck Search Syntax Help
|
||||||
|
-----
|
||||||
|
The search bar recognizes a set of special commands.<br>
|
||||||
|
In this list of examples below, each entry has an explanation and can be clicked to test the query. Note that all
|
||||||
|
searches are case insensitive.
|
||||||
|
<dl>
|
||||||
|
<dt>Display Name (The deck name, or the filename if the deck name isn't set):</dt>
|
||||||
|
<dd>[red deck wins](#red deck wins) <small>(Any deck with a display name containing the words red, deck, and wins)</small></dd>
|
||||||
|
<dd>["red deck wins"](#%22red deck wins%22) <small>(Any deck with a display name containing the exact phrase "red deck wins")</small></dd>
|
||||||
|
|
||||||
|
<dt>Deck <u>N</u>ame:</dt>
|
||||||
|
<dd>[n:aggro](#n:aggro) <small>(Any deck with a name containing the word aggro)</small></dd>
|
||||||
|
<dd>[n:red n:deck n:wins](#n:red n:deck n:wins) <small>(Any deck with a name containing the words red, deck, and wins)</small></dd>
|
||||||
|
<dd>[n:"red deck wins"](#n:%22red deck wins%22) <small>(Any deck with a name containing the exact phrase "red deck wins")</small></dd>
|
||||||
|
|
||||||
|
<dt><u>F</u>ile <u>N</u>ame:</dt>
|
||||||
|
<dd>[fn:aggro](#fn:aggro) <small>(Any deck with a filename containing the word aggro)</small></dd>
|
||||||
|
<dd>[fn:red fn:deck fn:wins](#fn:red fn:deck fn:wins) <small>(Any deck with a filename containing the words red, deck, and wins)</small></dd>
|
||||||
|
<dd>[fn:"red deck wins"](#fn:%22red deck wins%22) <small>(Any deck with a filename containing the exact phrase "red deck wins")</small></dd>
|
||||||
|
|
||||||
|
<dt>Relative <u>P</u>ath (starting from the deck folder):</dt>
|
||||||
|
<dd>[p:aggro](#p:aggro) <small>(Any deck that has "aggro" somewhere in its relative path)</small></dd>
|
||||||
|
<dd>[p:edh/](#p:edh/) <small>(Any deck with "edh/" in its relative path, A.K.A. decks in the "edh" folder)</small></dd>
|
||||||
|
|
||||||
|
<dt><u>F</u>ormat:</dt>
|
||||||
|
<dd>[f:standard](#f:standard) <small>(Any deck with format set to standard)</small></dd>
|
||||||
|
|
||||||
|
<dt>Deck Contents (Uses [card search expressions](#cardSearchSyntaxHelp)):</dt>
|
||||||
|
<dd><a href="#[[plains]]">[[plains]]</a> <small>(Any deck that contains at least one card with "plains" in its name)</small></dd>
|
||||||
|
<dd><a href="#[[t:legendary]]">[[t:legendary]]</a> <small>(Any deck that contains at least one legendary)</small></dd>
|
||||||
|
<dd><a href="#[[t:legendary]]>5">[[t:legendary]]>5</a> <small>(Any card that contains at least 5 legendaries)</small></dd>
|
||||||
|
<dd><a href="#[[]]:100">[[]]:100</a> <small>(Any deck that contains exactly 100 cards)</small></dd>
|
||||||
|
|
||||||
|
<dt>Negate:</dt>
|
||||||
|
<dd>[soldier -aggro](#soldier -aggro) <small>(Any deck filename that contains "soldier", but not "aggro")</small></dd>
|
||||||
|
|
||||||
|
<dt>Branching:</dt>
|
||||||
|
<dd>[t:aggro OR o:control](#t:aggro OR o:control) <small>(Any deck filename that contains either aggro or control)</small></dd>
|
||||||
|
|
||||||
|
<dt>Grouping:</dt>
|
||||||
|
<dd><a href="#red -([[]]:100 or aggro)">red -([[]]:100 or aggro)</a> <small>(Any deck that has red in its filename but is not 100 cards or has aggro in its filename)</small></dd>
|
||||||
|
|
||||||
|
</dl>
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@page search_syntax_help Search Syntax Help
|
||||||
|
|
||||||
## Search Syntax Help
|
## Search Syntax Help
|
||||||
-----
|
-----
|
||||||
The search bar recognizes a set of special commands similar to some other card databases.<br>
|
The search bar recognizes a set of special commands similar to some other card databases.<br>
|
||||||
@@ -58,4 +60,9 @@ In this list of examples below, each entry has an explanation and can be clicked
|
|||||||
<dt>Grouping:</dt>
|
<dt>Grouping:</dt>
|
||||||
<dd><a href="#t:angel -(angel or c:w)">t:angel -(angel or c:w)</a> <small>(Any angel that doesn't have angel in its name and isn't white)</small></dd>
|
<dd><a href="#t:angel -(angel or c:w)">t:angel -(angel or c:w)</a> <small>(Any angel that doesn't have angel in its name and isn't white)</small></dd>
|
||||||
|
|
||||||
|
<dt>Regular Expression:</dt>
|
||||||
|
<dd>[/^fell/](#/^fell/) <small>(Any card name that begins with "fell")</small></dd>
|
||||||
|
<dd>[o:/counter target .* spell/](#o:/counter target .* spell/) <small>(Any card text with "counter target *something* spell")</small></dd>
|
||||||
|
<dd>[o:/for each .* and\/or .*/](#o:/for each .* and\/or .*/) <small>(/'s can be escaped with a \)</small></dd>
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
|
|||||||
8
cockatrice/resources/icons/arrow_history.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?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>
|
||||||
|
After Width: | Height: | Size: 332 B |
40
cockatrice/resources/icons/arrow_redo.svg
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?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>
|
||||||
|
After Width: | Height: | Size: 1018 B |
40
cockatrice/resources/icons/arrow_undo.svg
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?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>
|
||||||
|
After Width: | Height: | Size: 874 B |
24
cockatrice/resources/icons/pin.svg
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 736 B |
25
cockatrice/resources/icons/share.svg
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
|
||||||
|
<svg height="800"
|
||||||
|
width="800"
|
||||||
|
version="1.1"
|
||||||
|
id="_x32_"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
xml:space="preserve"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs id="defs1"/>
|
||||||
|
<style type="text/css"
|
||||||
|
id="style1">
|
||||||
|
.st0{fill:#64C0FF;stroke:black;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1}
|
||||||
|
</style>
|
||||||
|
<g id="g1"
|
||||||
|
transform="matrix(0.87097097,0,0,1.0008579,38.609049,-0.21963163)"
|
||||||
|
style="stroke-width:3.42738;stroke-dasharray:none">
|
||||||
|
<path class="st0"
|
||||||
|
d="M 512,255.995 277.045,65.394 v 103.574 c -17.255,0 -36.408,0 -57.542,0 -208.59,0 -249.35,153.44 -201.394,266.128 9.586,-103.098 142.053,-100.701 237.358,-100.701 7.247,0 14.446,0 21.578,0 v 112.211 z"
|
||||||
|
id="path1"
|
||||||
|
style="stroke-width:20;stroke:#000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -350,11 +350,11 @@
|
|||||||
style="fill-opacity:1;stroke:black;stroke-width:2.78220296;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
style="fill-opacity:1;stroke:black;stroke-width:2.78220296;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
d="M 49.84375 1.71875 C 36.719738 1.71875 26.0625 12.375988 26.0625 25.5 C 26.0625 32.977454 29.538325 39.612734 34.9375 43.96875 C 24.439951 49.943698 17.919149 62.196126 14.3125 75.65625 C 9.0380874 95.34065 30.224013 98.21875 49.84375 98.21875 C 69.463486 98.21875 90.549327 94.96715 85.375 75.65625 C 81.693381 61.916246 75.224585 49.827177 64.8125 43.9375 C 70.181573 39.580662 73.59375 32.953205 73.59375 25.5 C 73.59375 12.375988 62.967762 1.71875 49.84375 1.71875 z "
|
d="M 49.84375 1.71875 C 36.719738 1.71875 26.0625 12.375988 26.0625 25.5 C 26.0625 32.977454 29.538325 39.612734 34.9375 43.96875 C 24.439951 49.943698 17.919149 62.196126 14.3125 75.65625 C 9.0380874 95.34065 30.224013 98.21875 49.84375 98.21875 C 69.463486 98.21875 90.549327 94.96715 85.375 75.65625 C 81.693381 61.916246 75.224585 49.827177 64.8125 43.9375 C 70.181573 39.580662 73.59375 32.953205 73.59375 25.5 C 73.59375 12.375988 62.967762 1.71875 49.84375 1.71875 z "
|
||||||
transform="translate(0,952.36218)"
|
transform="translate(0,952.36218)"
|
||||||
id="right" />
|
id="left" />
|
||||||
<path
|
<path
|
||||||
style="opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.73577702;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.73577702;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="m 51.28696,1001.834 0,-46.98372 1.434151,0.16768 c 5.155008,0.60274 9.462857,2.72154 12.938257,6.36366 4.74393,4.9715 6.87913,11.35611 6.16464,18.43328 -0.53702,5.31935 -3.09008,10.59498 -6.83833,14.13074 l -1.94072,1.83069 3.04083,2.20427 c 3.58084,2.5957 7.18975,6.4912 9.55296,10.3116 4.89572,7.9144 9.23593,21.4918 8.50487,26.6055 -0.81312,5.6877 -5.43872,9.6977 -13.62216,11.8093 -3.80822,0.9826 -7.68056,1.4713 -14.763321,1.8633 l -4.471177,0.2474 0,-46.9837 z"
|
d="m 51.28696,1001.834 0,-46.98372 1.434151,0.16768 c 5.155008,0.60274 9.462857,2.72154 12.938257,6.36366 4.74393,4.9715 6.87913,11.35611 6.16464,18.43328 -0.53702,5.31935 -3.09008,10.59498 -6.83833,14.13074 l -1.94072,1.83069 3.04083,2.20427 c 3.58084,2.5957 7.18975,6.4912 9.55296,10.3116 4.89572,7.9144 9.23593,21.4918 8.50487,26.6055 -0.81312,5.6877 -5.43872,9.6977 -13.62216,11.8093 -3.80822,0.9826 -7.68056,1.4713 -14.763321,1.8633 l -4.471177,0.2474 0,-46.9837 z"
|
||||||
id="left"
|
id="right"
|
||||||
inkscape:connector-curvature="0" />
|
inkscape:connector-curvature="0" />
|
||||||
<path
|
<path
|
||||||
style="display:inline;fill:url(#linearGradient3);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.77952756;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
style="display:inline;fill:url(#linearGradient3);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.77952756;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -321,11 +321,11 @@
|
|||||||
style="fill-opacity:1;stroke:black;stroke-width:2.78220296;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
style="fill-opacity:1;stroke:black;stroke-width:2.78220296;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
d="M 49.84375 1.71875 C 36.719738 1.71875 26.0625 12.375988 26.0625 25.5 C 26.0625 32.977454 29.538325 39.612734 34.9375 43.96875 C 24.439951 49.943698 17.919149 62.196126 14.3125 75.65625 C 9.0380874 95.34065 30.224013 98.21875 49.84375 98.21875 C 69.463486 98.21875 90.549327 94.96715 85.375 75.65625 C 81.693381 61.916246 75.224585 49.827177 64.8125 43.9375 C 70.181573 39.580662 73.59375 32.953205 73.59375 25.5 C 73.59375 12.375988 62.967762 1.71875 49.84375 1.71875 z "
|
d="M 49.84375 1.71875 C 36.719738 1.71875 26.0625 12.375988 26.0625 25.5 C 26.0625 32.977454 29.538325 39.612734 34.9375 43.96875 C 24.439951 49.943698 17.919149 62.196126 14.3125 75.65625 C 9.0380874 95.34065 30.224013 98.21875 49.84375 98.21875 C 69.463486 98.21875 90.549327 94.96715 85.375 75.65625 C 81.693381 61.916246 75.224585 49.827177 64.8125 43.9375 C 70.181573 39.580662 73.59375 32.953205 73.59375 25.5 C 73.59375 12.375988 62.967762 1.71875 49.84375 1.71875 z "
|
||||||
transform="translate(0,952.36218)"
|
transform="translate(0,952.36218)"
|
||||||
id="right" />
|
id="left" />
|
||||||
<path
|
<path
|
||||||
style="opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.73577702;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.73577702;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="m 51.28696,1001.834 0,-46.98372 1.434151,0.16768 c 5.155008,0.60274 9.462857,2.72154 12.938257,6.36366 4.74393,4.9715 6.87913,11.35611 6.16464,18.43328 -0.53702,5.31935 -3.09008,10.59498 -6.83833,14.13074 l -1.94072,1.83069 3.04083,2.20427 c 3.58084,2.5957 7.18975,6.4912 9.55296,10.3116 4.89572,7.9144 9.23593,21.4918 8.50487,26.6055 -0.81312,5.6877 -5.43872,9.6977 -13.62216,11.8093 -3.80822,0.9826 -7.68056,1.4713 -14.763321,1.8633 l -4.471177,0.2474 0,-46.9837 z"
|
d="m 51.28696,1001.834 0,-46.98372 1.434151,0.16768 c 5.155008,0.60274 9.462857,2.72154 12.938257,6.36366 4.74393,4.9715 6.87913,11.35611 6.16464,18.43328 -0.53702,5.31935 -3.09008,10.59498 -6.83833,14.13074 l -1.94072,1.83069 3.04083,2.20427 c 3.58084,2.5957 7.18975,6.4912 9.55296,10.3116 4.89572,7.9144 9.23593,21.4918 8.50487,26.6055 -0.81312,5.6877 -5.43872,9.6977 -13.62216,11.8093 -3.80822,0.9826 -7.68056,1.4713 -14.763321,1.8633 l -4.471177,0.2474 0,-46.9837 z"
|
||||||
id="left"
|
id="right"
|
||||||
inkscape:connector-curvature="0" />
|
inkscape:connector-curvature="0" />
|
||||||
<path
|
<path
|
||||||
d="m 46.656521,12.167234 18.055171,18.054184 a 6.6081919,6.6078288 0 0 1 -0.126303,9.352065 6.6804126,6.6800456 0 0 1 -8.233169,1.011048 l -7.944268,7.943843 6.463762,6.445343 a 6.9331851,6.9328042 0 0 1 5.741536,2.022073 l 28.057729,28.092294 a 6.9962797,6.9958953 0 0 1 -9.894222,9.893685 L 50.719018,66.907526 A 7.0595711,7.0591833 0 0 1 49.18433,59.270613 l -5.741527,-5.741238 -7.944298,7.943843 A 6.716523,6.7161541 0 0 1 25.134866,69.832263 L 7.079684,51.778091 a 6.716523,6.7161541 0 0 1 8.39566,-10.345064 L 36.31101,20.59853 a 6.716523,6.7161541 0 0 1 10.345612,-8.431329 z"
|
d="m 46.656521,12.167234 18.055171,18.054184 a 6.6081919,6.6078288 0 0 1 -0.126303,9.352065 6.6804126,6.6800456 0 0 1 -8.233169,1.011048 l -7.944268,7.943843 6.463762,6.445343 a 6.9331851,6.9328042 0 0 1 5.741536,2.022073 l 28.057729,28.092294 a 6.9962797,6.9958953 0 0 1 -9.894222,9.893685 L 50.719018,66.907526 A 7.0595711,7.0591833 0 0 1 49.18433,59.270613 l -5.741527,-5.741238 -7.944298,7.943843 A 6.716523,6.7161541 0 0 1 25.134866,69.832263 L 7.079684,51.778091 a 6.716523,6.7161541 0 0 1 8.39566,-10.345064 L 36.31101,20.59853 a 6.716523,6.7161541 0 0 1 10.345612,-8.431329 z"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -340,11 +340,11 @@
|
|||||||
style="fill-opacity:1;stroke:black;stroke-width:2.78220296;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
style="fill-opacity:1;stroke:black;stroke-width:2.78220296;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
d="M 49.84375 1.71875 C 36.719738 1.71875 26.0625 12.375988 26.0625 25.5 C 26.0625 32.977454 29.538325 39.612734 34.9375 43.96875 C 24.439951 49.943698 17.919149 62.196126 14.3125 75.65625 C 9.0380874 95.34065 30.224013 98.21875 49.84375 98.21875 C 69.463486 98.21875 90.549327 94.96715 85.375 75.65625 C 81.693381 61.916246 75.224585 49.827177 64.8125 43.9375 C 70.181573 39.580662 73.59375 32.953205 73.59375 25.5 C 73.59375 12.375988 62.967762 1.71875 49.84375 1.71875 z "
|
d="M 49.84375 1.71875 C 36.719738 1.71875 26.0625 12.375988 26.0625 25.5 C 26.0625 32.977454 29.538325 39.612734 34.9375 43.96875 C 24.439951 49.943698 17.919149 62.196126 14.3125 75.65625 C 9.0380874 95.34065 30.224013 98.21875 49.84375 98.21875 C 69.463486 98.21875 90.549327 94.96715 85.375 75.65625 C 81.693381 61.916246 75.224585 49.827177 64.8125 43.9375 C 70.181573 39.580662 73.59375 32.953205 73.59375 25.5 C 73.59375 12.375988 62.967762 1.71875 49.84375 1.71875 z "
|
||||||
transform="translate(0,952.36218)"
|
transform="translate(0,952.36218)"
|
||||||
id="right" />
|
id="left" />
|
||||||
<path
|
<path
|
||||||
style="opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.73577702;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="opacity:1;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.73577702;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="m 51.28696,1001.834 0,-46.98372 1.434151,0.16768 c 5.155008,0.60274 9.462857,2.72154 12.938257,6.36366 4.74393,4.9715 6.87913,11.35611 6.16464,18.43328 -0.53702,5.31935 -3.09008,10.59498 -6.83833,14.13074 l -1.94072,1.83069 3.04083,2.20427 c 3.58084,2.5957 7.18975,6.4912 9.55296,10.3116 4.89572,7.9144 9.23593,21.4918 8.50487,26.6055 -0.81312,5.6877 -5.43872,9.6977 -13.62216,11.8093 -3.80822,0.9826 -7.68056,1.4713 -14.763321,1.8633 l -4.471177,0.2474 0,-46.9837 z"
|
d="m 51.28696,1001.834 0,-46.98372 1.434151,0.16768 c 5.155008,0.60274 9.462857,2.72154 12.938257,6.36366 4.74393,4.9715 6.87913,11.35611 6.16464,18.43328 -0.53702,5.31935 -3.09008,10.59498 -6.83833,14.13074 l -1.94072,1.83069 3.04083,2.20427 c 3.58084,2.5957 7.18975,6.4912 9.55296,10.3116 4.89572,7.9144 9.23593,21.4918 8.50487,26.6055 -0.81312,5.6877 -5.43872,9.6977 -13.62216,11.8093 -3.80822,0.9826 -7.68056,1.4713 -14.763321,1.8633 l -4.471177,0.2474 0,-46.9837 z"
|
||||||
id="left"
|
id="right"
|
||||||
inkscape:connector-curvature="0" />
|
inkscape:connector-curvature="0" />
|
||||||
<path
|
<path
|
||||||
sodipodi:type="star"
|
sodipodi:type="star"
|
||||||
@@ -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,-15.920285,-1400.1716)" />
|
transform="matrix(-2.3768784,0,0,2.4799382,115.920285,-1400.1716)" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,14 +1,14 @@
|
|||||||
#include "deck_stats_interface.h"
|
#include "deck_stats_interface.h"
|
||||||
|
|
||||||
#include "decklist.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>
|
||||||
|
#include <version_string.h>
|
||||||
|
|
||||||
DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent)
|
DeckStatsInterface::DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent)
|
||||||
: QObject(parent), cardDatabase(_cardDatabase)
|
: QObject(parent), cardDatabase(_cardDatabase)
|
||||||
@@ -63,6 +63,7 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck)
|
|||||||
|
|
||||||
QNetworkRequest request(QUrl("https://deckstats.net/index.php"));
|
QNetworkRequest request(QUrl("https://deckstats.net/index.php"));
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||||
|
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
|
||||||
|
|
||||||
manager->post(request, data);
|
manager->post(request, data);
|
||||||
}
|
}
|
||||||
@@ -70,9 +71,9 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck)
|
|||||||
void DeckStatsInterface::copyDeckWithoutTokens(DeckList &source, DeckList &destination)
|
void DeckStatsInterface::copyDeckWithoutTokens(DeckList &source, DeckList &destination)
|
||||||
{
|
{
|
||||||
auto copyIfNotAToken = [this, &destination](const auto node, const auto card) {
|
auto copyIfNotAToken = [this, &destination](const auto node, const auto card) {
|
||||||
CardInfoPtr dbCard = cardDatabase.getCard(card->getName());
|
CardInfoPtr dbCard = cardDatabase.query()->getCardInfo(card->getName());
|
||||||
if (dbCard && !dbCard->getIsToken()) {
|
if (dbCard && !dbCard->getIsToken()) {
|
||||||
DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName());
|
DecklistCardNode *addedCard = destination.addCard(card->getName(), node->getName(), -1);
|
||||||
addedCard->setNumber(card->getNumber());
|
addedCard->setNumber(card->getNumber());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* @file deck_stats_interface.h
|
||||||
|
* @ingroup ApiInterfaces
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef DECKSTATS_INTERFACE_H
|
#ifndef DECKSTATS_INTERFACE_H
|
||||||
#define DECKSTATS_INTERFACE_H
|
#define DECKSTATS_INTERFACE_H
|
||||||
|
|
||||||
#include "../game/cards/card_database.h"
|
#include <libcockatrice/card/database/card_database.h>
|
||||||
#include "decklist.h"
|
#include <libcockatrice/deck_list/deck_list.h>
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
#include "tapped_out_interface.h"
|
#include "tapped_out_interface.h"
|
||||||
|
|
||||||
#include "decklist.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>
|
||||||
|
#include <version_string.h>
|
||||||
|
|
||||||
TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent)
|
TappedOutInterface::TappedOutInterface(CardDatabase &_cardDatabase, QObject *parent)
|
||||||
: QObject(parent), cardDatabase(_cardDatabase)
|
: QObject(parent), cardDatabase(_cardDatabase)
|
||||||
@@ -88,6 +88,7 @@ void TappedOutInterface::analyzeDeck(DeckList *deck)
|
|||||||
|
|
||||||
QNetworkRequest request(QUrl("https://tappedout.net/mtg-decks/paste/"));
|
QNetworkRequest request(QUrl("https://tappedout.net/mtg-decks/paste/"));
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||||
|
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
|
||||||
|
|
||||||
manager->post(request, data);
|
manager->post(request, data);
|
||||||
}
|
}
|
||||||
@@ -95,15 +96,15 @@ void TappedOutInterface::analyzeDeck(DeckList *deck)
|
|||||||
void TappedOutInterface::copyDeckSplitMainAndSide(DeckList &source, DeckList &mainboard, DeckList &sideboard)
|
void TappedOutInterface::copyDeckSplitMainAndSide(DeckList &source, DeckList &mainboard, DeckList &sideboard)
|
||||||
{
|
{
|
||||||
auto copyMainOrSide = [this, &mainboard, &sideboard](const auto node, const auto card) {
|
auto copyMainOrSide = [this, &mainboard, &sideboard](const auto node, const auto card) {
|
||||||
CardInfoPtr dbCard = cardDatabase.getCard(card->getName());
|
CardInfoPtr dbCard = cardDatabase.query()->getCardInfo(card->getName());
|
||||||
if (!dbCard || dbCard->getIsToken())
|
if (!dbCard || dbCard->getIsToken())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DecklistCardNode *addedCard;
|
DecklistCardNode *addedCard;
|
||||||
if (node->getName() == DECK_ZONE_SIDE)
|
if (node->getName() == DECK_ZONE_SIDE)
|
||||||
addedCard = sideboard.addCard(card->getName(), node->getName());
|
addedCard = sideboard.addCard(card->getName(), node->getName(), -1);
|
||||||
else
|
else
|
||||||
addedCard = mainboard.addCard(card->getName(), node->getName());
|
addedCard = mainboard.addCard(card->getName(), node->getName(), -1);
|
||||||
addedCard->setNumber(card->getNumber());
|
addedCard->setNumber(card->getNumber());
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* @file tapped_out_interface.h
|
||||||
|
* @ingroup ApiInterfaces
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef TAPPEDOUT_INTERFACE_H
|
#ifndef TAPPEDOUT_INTERFACE_H
|
||||||
#define TAPPEDOUT_INTERFACE_H
|
#define TAPPEDOUT_INTERFACE_H
|
||||||
|
|
||||||
#include "../game/cards/card_database.h"
|
#include <libcockatrice/card/database/card_database.h>
|
||||||
#include "decklist.h"
|
#include <libcockatrice/deck_list/deck_list.h>
|
||||||
|
|
||||||
#include <QLoggingCategory>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
inline Q_LOGGING_CATEGORY(TappedOutInterfaceLog, "tapped_out_interface");
|
inline Q_LOGGING_CATEGORY(TappedOutInterfaceLog, "tapped_out_interface");
|
||||||
|
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#include "deck_link_to_api_transformer.h"
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
namespace DeckLinkToApiTransformer
|
||||||
|
{
|
||||||
|
|
||||||
|
static const QString TAPPEDOUT_BASE = "https://tappedout.net/mtg-decks/";
|
||||||
|
static const QString TAPPEDOUT_SUFFIX = "/?fmt=txt";
|
||||||
|
|
||||||
|
static const QString ARCHIDEKT_BASE = "https://archidekt.com/api/decks/";
|
||||||
|
static const QString ARCHIDEKT_SUFFIX = "/?format=json";
|
||||||
|
|
||||||
|
static const QString MOXFIELD_BASE = "https://api.moxfield.com/v2/decks/all/";
|
||||||
|
static const QString MOXFIELD_SUFFIX = "/";
|
||||||
|
|
||||||
|
static const QString DECKSTATS_SUFFIX = "?include_comments=1&export_mtgarena=1";
|
||||||
|
|
||||||
|
bool parseDeckUrl(const QString &url, ParsedDeckInfo &outInfo)
|
||||||
|
{
|
||||||
|
static QRegularExpression rxTappedOut("tappedout\\.net/(?:mtg-decks/)?([^/?#]+)");
|
||||||
|
static QRegularExpression rxArchidekt("archidekt\\.com/decks/(\\d+)");
|
||||||
|
static QRegularExpression rxMoxfield("moxfield\\.com/decks/([a-zA-Z0-9_-]+)");
|
||||||
|
static QRegularExpression rxDeckstats("deckstats\\.net/decks/(\\d+/[a-zA-Z0-9_-]+)");
|
||||||
|
|
||||||
|
QRegularExpressionMatch match;
|
||||||
|
|
||||||
|
if ((match = rxTappedOut.match(url)).hasMatch()) {
|
||||||
|
QString slug = match.captured(1);
|
||||||
|
outInfo = ParsedDeckInfo{.baseUrl = TAPPEDOUT_BASE,
|
||||||
|
.deckID = slug,
|
||||||
|
.fullUrl = TAPPEDOUT_BASE + slug + TAPPEDOUT_SUFFIX,
|
||||||
|
.provider = DeckProvider::TappedOut};
|
||||||
|
return true;
|
||||||
|
} else if ((match = rxArchidekt.match(url)).hasMatch()) {
|
||||||
|
QString deckID = match.captured(1);
|
||||||
|
outInfo = ParsedDeckInfo{.baseUrl = ARCHIDEKT_BASE,
|
||||||
|
.deckID = deckID,
|
||||||
|
.fullUrl = ARCHIDEKT_BASE + deckID + ARCHIDEKT_SUFFIX,
|
||||||
|
.provider = DeckProvider::Archidekt};
|
||||||
|
return true;
|
||||||
|
} else if ((match = rxMoxfield.match(url)).hasMatch()) {
|
||||||
|
QString deckID = match.captured(1);
|
||||||
|
outInfo = ParsedDeckInfo{.baseUrl = MOXFIELD_BASE,
|
||||||
|
.deckID = deckID,
|
||||||
|
.fullUrl = MOXFIELD_BASE + deckID + MOXFIELD_SUFFIX,
|
||||||
|
.provider = DeckProvider::Moxfield};
|
||||||
|
return true;
|
||||||
|
} else if ((match = rxDeckstats.match(url)).hasMatch()) {
|
||||||
|
QString deckPath = match.captured(1);
|
||||||
|
outInfo = ParsedDeckInfo{.baseUrl = "https://deckstats.net/decks/",
|
||||||
|
.deckID = deckPath,
|
||||||
|
.fullUrl = "https://deckstats.net/decks/" + deckPath + DECKSTATS_SUFFIX,
|
||||||
|
.provider = DeckProvider::Deckstats};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace DeckLinkToApiTransformer
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* @file deck_link_to_api_transformer.h
|
||||||
|
* @ingroup ApiInterfaces
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DECK_LINK_TO_API_TRANSFORMER_H
|
||||||
|
#define DECK_LINK_TO_API_TRANSFORMER_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
enum class DeckProvider
|
||||||
|
{
|
||||||
|
TappedOut,
|
||||||
|
Archidekt,
|
||||||
|
Moxfield,
|
||||||
|
Deckstats,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedDeckInfo
|
||||||
|
{
|
||||||
|
QString baseUrl;
|
||||||
|
QString deckID;
|
||||||
|
QString fullUrl;
|
||||||
|
DeckProvider provider;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace DeckLinkToApiTransformer
|
||||||
|
{
|
||||||
|
|
||||||
|
// Returns true if the input URL is recognized and fills outInfo.
|
||||||
|
bool parseDeckUrl(const QString &url, ParsedDeckInfo &outInfo);
|
||||||
|
|
||||||
|
} // namespace DeckLinkToApiTransformer
|
||||||
|
|
||||||
|
#endif // DECK_LINK_TO_API_TRANSFORMER_H
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/**
|
||||||
|
* @file interface_json_deck_parser.h
|
||||||
|
* @ingroup ApiInterfaces
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INTERFACE_JSON_DECK_PARSER_H
|
||||||
|
#define INTERFACE_JSON_DECK_PARSER_H
|
||||||
|
|
||||||
|
#include "../../../interface/deck_loader/card_node_function.h"
|
||||||
|
#include "../../../interface/deck_loader/deck_loader.h"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
class IJsonDeckParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IJsonDeckParser() = default;
|
||||||
|
|
||||||
|
virtual DeckList parse(const QJsonObject &obj) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArchidektJsonParser : public IJsonDeckParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeckList parse(const QJsonObject &obj) override
|
||||||
|
{
|
||||||
|
DeckList deckList;
|
||||||
|
|
||||||
|
QString deckName = obj.value("name").toString();
|
||||||
|
QString deckDescription = obj.value("description").toString();
|
||||||
|
|
||||||
|
deckList.setName(deckName);
|
||||||
|
deckList.setComments(deckDescription);
|
||||||
|
|
||||||
|
QString outputText;
|
||||||
|
QTextStream outStream(&outputText);
|
||||||
|
|
||||||
|
for (auto entry : obj.value("cards").toArray()) {
|
||||||
|
auto quantity = entry.toObject().value("quantity").toInt();
|
||||||
|
|
||||||
|
auto card = entry.toObject().value("card").toObject();
|
||||||
|
auto oracleCard = card.value("oracleCard").toObject();
|
||||||
|
QString cardName = oracleCard.value("name").toString();
|
||||||
|
QString setName = card.value("edition").toObject().value("editioncode").toString().toUpper();
|
||||||
|
QString collectorNumber = card.value("collectorNumber").toString();
|
||||||
|
|
||||||
|
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
deckList.loadFromStream_Plain(outStream, false);
|
||||||
|
deckList.forEachCard(CardNodeFunction::ResolveProviderId());
|
||||||
|
|
||||||
|
return deckList;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MoxfieldJsonParser : public IJsonDeckParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeckList parse(const QJsonObject &obj) override
|
||||||
|
{
|
||||||
|
DeckList deckList;
|
||||||
|
|
||||||
|
QString deckName = obj.value("name").toString();
|
||||||
|
QString deckDescription = obj.value("description").toString();
|
||||||
|
|
||||||
|
deckList.setName(deckName);
|
||||||
|
deckList.setComments(deckDescription);
|
||||||
|
|
||||||
|
QString outputText;
|
||||||
|
QTextStream outStream(&outputText);
|
||||||
|
|
||||||
|
for (auto entry : obj.value("mainboard").toObject()) {
|
||||||
|
auto quantity = entry.toObject().value("quantity").toInt();
|
||||||
|
|
||||||
|
auto card = entry.toObject().value("card").toObject();
|
||||||
|
QString cardName = card.value("name").toString();
|
||||||
|
QString setName = card.value("set").toString().toUpper();
|
||||||
|
QString collectorNumber = card.value("cn").toString();
|
||||||
|
|
||||||
|
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
outStream << '\n';
|
||||||
|
|
||||||
|
for (auto entry : obj.value("sideboard").toObject()) {
|
||||||
|
auto quantity = entry.toObject().value("quantity").toInt();
|
||||||
|
|
||||||
|
auto card = entry.toObject().value("card").toObject();
|
||||||
|
QString cardName = card.value("name").toString();
|
||||||
|
QString setName = card.value("set").toString().toUpper();
|
||||||
|
QString collectorNumber = card.value("cn").toString();
|
||||||
|
|
||||||
|
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
deckList.loadFromStream_Plain(outStream, false);
|
||||||
|
deckList.forEachCard(CardNodeFunction::ResolveProviderId());
|
||||||
|
|
||||||
|
QJsonObject commandersObj = obj.value("commanders").toObject();
|
||||||
|
if (!commandersObj.isEmpty()) {
|
||||||
|
for (auto it = commandersObj.begin(); it != commandersObj.end(); ++it) {
|
||||||
|
QJsonObject cardData = it.value().toObject().value("card").toObject();
|
||||||
|
QString commanderName = cardData.value("name").toString();
|
||||||
|
QString setName = cardData.value("set").toString().toUpper();
|
||||||
|
QString collectorNumber = cardData.value("cn").toString();
|
||||||
|
QString providerId = cardData.value("scryfall_id").toString();
|
||||||
|
|
||||||
|
deckList.setBannerCard({commanderName, providerId});
|
||||||
|
deckList.addCard(commanderName, DECK_ZONE_MAIN, -1, setName, collectorNumber, providerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deckList;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INTERFACE_JSON_DECK_PARSER_H
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
#include "spoiler_background_updater.h"
|
#include "spoiler_background_updater.h"
|
||||||
|
|
||||||
#include "../../game/cards/card_database.h"
|
#include "../../../../interface/window_main.h"
|
||||||
#include "../../game/cards/card_database_manager.h"
|
#include "../../../../main.h"
|
||||||
#include "../../main.h"
|
#include "../../../settings/cache_settings.h"
|
||||||
#include "../../settings/cache_settings.h"
|
|
||||||
#include "../ui/window_main.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>
|
||||||
|
#include <version_string.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"
|
||||||
@@ -41,7 +40,9 @@ void SpoilerBackgroundUpdater::startSpoilerDownloadProcess(QString url, bool sav
|
|||||||
void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults)
|
void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults)
|
||||||
{
|
{
|
||||||
auto *nam = new QNetworkAccessManager(this);
|
auto *nam = new QNetworkAccessManager(this);
|
||||||
QNetworkReply *reply = nam->get(QNetworkRequest(url));
|
auto request = QNetworkRequest(url);
|
||||||
|
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
|
||||||
|
QNetworkReply *reply = nam->get(request);
|
||||||
|
|
||||||
if (saveResults) {
|
if (saveResults) {
|
||||||
// This will write out to the file (used for spoiler.xml)
|
// This will write out to the file (used for spoiler.xml)
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @file spoiler_background_updater.h
|
||||||
|
* @ingroup Client
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef COCKATRICE_SPOILER_DOWNLOADER_H
|
#ifndef COCKATRICE_SPOILER_DOWNLOADER_H
|
||||||
#define COCKATRICE_SPOILER_DOWNLOADER_H
|
#define COCKATRICE_SPOILER_DOWNLOADER_H
|
||||||
|
|
||||||
@@ -16,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();
|
||||||
@@ -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)
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @file client_update_checker.h
|
||||||
|
* @ingroup ClientUpdate
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef CLIENT_UPDATE_CHECKER_H
|
#ifndef CLIENT_UPDATE_CHECKER_H
|
||||||
#define CLIENT_UPDATE_CHECKER_H
|
#define CLIENT_UPDATE_CHECKER_H
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @file release_channel.h
|
||||||
|
* @ingroup ClientUpdate
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef RELEASECHANNEL_H
|
#ifndef RELEASECHANNEL_H
|
||||||
#define RELEASECHANNEL_H
|
#define RELEASECHANNEL_H
|
||||||
|
|
||||||
@@ -51,27 +57,27 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QString getName() const
|
[[nodiscard]] QString getName() const
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
QString getDescriptionUrl() const
|
[[nodiscard]] QString getDescriptionUrl() const
|
||||||
{
|
{
|
||||||
return descriptionUrl;
|
return descriptionUrl;
|
||||||
}
|
}
|
||||||
QString getDownloadUrl() const
|
[[nodiscard]] QString getDownloadUrl() const
|
||||||
{
|
{
|
||||||
return downloadUrl;
|
return downloadUrl;
|
||||||
}
|
}
|
||||||
QString getCommitHash() const
|
[[nodiscard]] QString getCommitHash() const
|
||||||
{
|
{
|
||||||
return commitHash;
|
return commitHash;
|
||||||
}
|
}
|
||||||
QDate getPublishDate() const
|
[[nodiscard]] QDate getPublishDate() const
|
||||||
{
|
{
|
||||||
return publishDate;
|
return publishDate;
|
||||||
}
|
}
|
||||||
bool isCompatibleVersionFound() const
|
[[nodiscard]] bool isCompatibleVersionFound() const
|
||||||
{
|
{
|
||||||
return compatibleVersionFound;
|
return compatibleVersionFound;
|
||||||
}
|
}
|
||||||
@@ -91,15 +97,15 @@ protected:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
static bool downloadMatchesCurrentOS(const QString &fileName);
|
static bool downloadMatchesCurrentOS(const QString &fileName);
|
||||||
virtual QString getReleaseChannelUrl() const = 0;
|
[[nodiscard]] virtual QString getReleaseChannelUrl() const = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Release *getLastRelease()
|
Release *getLastRelease()
|
||||||
{
|
{
|
||||||
return lastRelease;
|
return lastRelease;
|
||||||
}
|
}
|
||||||
virtual QString getManualDownloadUrl() const = 0;
|
[[nodiscard]] virtual QString getManualDownloadUrl() const = 0;
|
||||||
virtual QString getName() const = 0;
|
[[nodiscard]] 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);
|
||||||
@@ -116,12 +122,12 @@ public:
|
|||||||
explicit StableReleaseChannel() = default;
|
explicit StableReleaseChannel() = default;
|
||||||
~StableReleaseChannel() override = default;
|
~StableReleaseChannel() override = default;
|
||||||
|
|
||||||
QString getManualDownloadUrl() const override;
|
[[nodiscard]] QString getManualDownloadUrl() const override;
|
||||||
|
|
||||||
QString getName() const override;
|
[[nodiscard]] QString getName() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString getReleaseChannelUrl() const override;
|
[[nodiscard]] QString getReleaseChannelUrl() const override;
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|
||||||
void releaseListFinished() override;
|
void releaseListFinished() override;
|
||||||
@@ -137,12 +143,12 @@ public:
|
|||||||
BetaReleaseChannel() = default;
|
BetaReleaseChannel() = default;
|
||||||
~BetaReleaseChannel() override = default;
|
~BetaReleaseChannel() override = default;
|
||||||
|
|
||||||
QString getManualDownloadUrl() const override;
|
[[nodiscard]] QString getManualDownloadUrl() const override;
|
||||||
|
|
||||||
QString getName() const override;
|
[[nodiscard]] QString getName() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString getReleaseChannelUrl() const override;
|
[[nodiscard]] QString getReleaseChannelUrl() const override;
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|
||||||
void releaseListFinished() override;
|
void releaseListFinished() override;
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#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)
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
//
|
/**
|
||||||
// Created by miguel on 28/12/15.
|
* @file update_downloader.h
|
||||||
//
|
* @ingroup ClientUpdate
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
#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
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
#include "cache_settings.h"
|
#include "cache_settings.h"
|
||||||
|
|
||||||
#include "../client/network/release_channel.h"
|
#include "../network/update/client/release_channel.h"
|
||||||
#include "card_override_settings.h"
|
#include "card_counter_settings.h"
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@@ -10,9 +11,15 @@
|
|||||||
#include <QGlobalStatic>
|
#include <QGlobalStatic>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <libcockatrice/settings/card_override_settings.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
Q_GLOBAL_STATIC(SettingsCache, settingsCache);
|
Q_GLOBAL_STATIC(SettingsCache, settingsCache)
|
||||||
|
|
||||||
|
SettingsCache &SettingsCache::instance()
|
||||||
|
{
|
||||||
|
return *settingsCache; // returns a QT managed singleton reference
|
||||||
|
}
|
||||||
|
|
||||||
QString SettingsCache::getDataPath()
|
QString SettingsCache::getDataPath()
|
||||||
{
|
{
|
||||||
@@ -59,9 +66,9 @@ void SettingsCache::translateLegacySettings()
|
|||||||
QStringList setsGroups = legacySetting.childGroups();
|
QStringList setsGroups = legacySetting.childGroups();
|
||||||
for (int i = 0; i < setsGroups.size(); i++) {
|
for (int i = 0; i < setsGroups.size(); i++) {
|
||||||
legacySetting.beginGroup(setsGroups.at(i));
|
legacySetting.beginGroup(setsGroups.at(i));
|
||||||
cardDatabase().setEnabled(setsGroups.at(i), legacySetting.value("enabled").toBool());
|
cardDatabase()->setEnabled(setsGroups.at(i), legacySetting.value("enabled").toBool());
|
||||||
cardDatabase().setIsKnown(setsGroups.at(i), legacySetting.value("isknown").toBool());
|
cardDatabase()->setIsKnown(setsGroups.at(i), legacySetting.value("isknown").toBool());
|
||||||
cardDatabase().setSortKey(setsGroups.at(i), legacySetting.value("sortkey").toUInt());
|
cardDatabase()->setSortKey(setsGroups.at(i), legacySetting.value("sortkey").toUInt());
|
||||||
legacySetting.endGroup();
|
legacySetting.endGroup();
|
||||||
}
|
}
|
||||||
QStringList setsKeys = legacySetting.allKeys();
|
QStringList setsKeys = legacySetting.allKeys();
|
||||||
@@ -179,6 +186,8 @@ SettingsCache::SettingsCache()
|
|||||||
cardOverrideSettings = new CardOverrideSettings(settingsPath, this);
|
cardOverrideSettings = new CardOverrideSettings(settingsPath, this);
|
||||||
debugSettings = new DebugSettings(settingsPath, this);
|
debugSettings = new DebugSettings(settingsPath, this);
|
||||||
|
|
||||||
|
cardCounterSettings = new CardCounterSettings(settingsPath, this);
|
||||||
|
|
||||||
if (!QFile(settingsPath + "global.ini").exists())
|
if (!QFile(settingsPath + "global.ini").exists())
|
||||||
translateLegacySettings();
|
translateLegacySettings();
|
||||||
|
|
||||||
@@ -190,6 +199,11 @@ SettingsCache::SettingsCache()
|
|||||||
mbDownloadSpoilers = settings->value("personal/downloadspoilers", false).toBool();
|
mbDownloadSpoilers = settings->value("personal/downloadspoilers", false).toBool();
|
||||||
|
|
||||||
checkUpdatesOnStartup = settings->value("personal/startupUpdateCheck", true).toBool();
|
checkUpdatesOnStartup = settings->value("personal/startupUpdateCheck", true).toBool();
|
||||||
|
startupCardUpdateCheckPromptForUpdate =
|
||||||
|
settings->value("personal/startupCardUpdateCheckPromptForUpdate", true).toBool();
|
||||||
|
startupCardUpdateCheckAlwaysUpdate = settings->value("personal/startupCardUpdateCheckAlwaysUpdate", false).toBool();
|
||||||
|
cardUpdateCheckInterval = settings->value("personal/cardUpdateCheckInterval", 7).toInt();
|
||||||
|
lastCardUpdateCheck = settings->value("personal/lastCardUpdateCheck", QDateTime::currentDateTime().date()).toDate();
|
||||||
notifyAboutUpdates = settings->value("personal/updatenotification", true).toBool();
|
notifyAboutUpdates = settings->value("personal/updatenotification", true).toBool();
|
||||||
notifyAboutNewVersion = settings->value("personal/newversionnotification", true).toBool();
|
notifyAboutNewVersion = settings->value("personal/newversionnotification", true).toBool();
|
||||||
updateReleaseChannel = settings->value("personal/updatereleasechannel", 0).toInt();
|
updateReleaseChannel = settings->value("personal/updatereleasechannel", 0).toInt();
|
||||||
@@ -208,6 +222,9 @@ SettingsCache::SettingsCache()
|
|||||||
|
|
||||||
themeName = settings->value("theme/name").toString();
|
themeName = settings->value("theme/name").toString();
|
||||||
|
|
||||||
|
homeTabBackgroundSource = settings->value("home/background", "themed").toString();
|
||||||
|
homeTabBackgroundShuffleFrequency = settings->value("home/background/shuffleTimer", 0).toInt();
|
||||||
|
|
||||||
tabVisualDeckStorageOpen = settings->value("tabs/visualDeckStorage", true).toBool();
|
tabVisualDeckStorageOpen = settings->value("tabs/visualDeckStorage", true).toBool();
|
||||||
tabServerOpen = settings->value("tabs/server", true).toBool();
|
tabServerOpen = settings->value("tabs/server", true).toBool();
|
||||||
tabAccountOpen = settings->value("tabs/account", true).toBool();
|
tabAccountOpen = settings->value("tabs/account", true).toBool();
|
||||||
@@ -233,6 +250,7 @@ SettingsCache::SettingsCache()
|
|||||||
redirectCacheTtl = settings->value("personal/redirectCacheTtl", NETWORK_REDIRECT_CACHE_TTL_DEFAULT).toInt();
|
redirectCacheTtl = settings->value("personal/redirectCacheTtl", NETWORK_REDIRECT_CACHE_TTL_DEFAULT).toInt();
|
||||||
|
|
||||||
picDownload = settings->value("personal/picturedownload", true).toBool();
|
picDownload = settings->value("personal/picturedownload", true).toBool();
|
||||||
|
showStatusBar = settings->value("personal/showStatusBar", false).toBool();
|
||||||
|
|
||||||
mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray();
|
mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray();
|
||||||
tokenDialogGeometry = settings->value("interface/token_dialog_geometry").toByteArray();
|
tokenDialogGeometry = settings->value("interface/token_dialog_geometry").toByteArray();
|
||||||
@@ -243,6 +261,7 @@ SettingsCache::SettingsCache()
|
|||||||
doubleClickToPlay = settings->value("interface/doubleclicktoplay", true).toBool();
|
doubleClickToPlay = settings->value("interface/doubleclicktoplay", true).toBool();
|
||||||
clickPlaysAllSelected = settings->value("interface/clickPlaysAllSelected", true).toBool();
|
clickPlaysAllSelected = settings->value("interface/clickPlaysAllSelected", true).toBool();
|
||||||
playToStack = settings->value("interface/playtostack", true).toBool();
|
playToStack = settings->value("interface/playtostack", true).toBool();
|
||||||
|
doNotDeleteArrowsInSubPhases = settings->value("interface/doNotDeleteArrowsInSubPhases", true).toBool();
|
||||||
startingHandSize = settings->value("interface/startinghandsize", 7).toInt();
|
startingHandSize = settings->value("interface/startinghandsize", 7).toInt();
|
||||||
annotateTokens = settings->value("interface/annotatetokens", false).toBool();
|
annotateTokens = settings->value("interface/annotatetokens", false).toBool();
|
||||||
tabGameSplitterSizes = settings->value("interface/tabgame_splittersizes").toByteArray();
|
tabGameSplitterSizes = settings->value("interface/tabgame_splittersizes").toByteArray();
|
||||||
@@ -271,6 +290,8 @@ SettingsCache::SettingsCache()
|
|||||||
visualDeckStorageSortingOrder = settings->value("interface/visualdeckstoragesortingorder", 0).toInt();
|
visualDeckStorageSortingOrder = settings->value("interface/visualdeckstoragesortingorder", 0).toInt();
|
||||||
visualDeckStorageShowFolders = settings->value("interface/visualdeckstorageshowfolders", true).toBool();
|
visualDeckStorageShowFolders = settings->value("interface/visualdeckstorageshowfolders", true).toBool();
|
||||||
visualDeckStorageShowTagFilter = settings->value("interface/visualdeckstorageshowtagfilter", true).toBool();
|
visualDeckStorageShowTagFilter = settings->value("interface/visualdeckstorageshowtagfilter", true).toBool();
|
||||||
|
visualDeckStorageDefaultTagsList =
|
||||||
|
settings->value("interface/visualdeckstoragedefaulttagslist", defaultTags).toStringList();
|
||||||
visualDeckStorageSearchFolderNames = settings->value("interface/visualdeckstoragesearchfoldernames", true).toBool();
|
visualDeckStorageSearchFolderNames = settings->value("interface/visualdeckstoragesearchfoldernames", true).toBool();
|
||||||
visualDeckStorageShowBannerCardComboBox =
|
visualDeckStorageShowBannerCardComboBox =
|
||||||
settings->value("interface/visualdeckstorageshowbannercardcombobox", true).toBool();
|
settings->value("interface/visualdeckstorageshowbannercardcombobox", true).toBool();
|
||||||
@@ -280,6 +301,7 @@ SettingsCache::SettingsCache()
|
|||||||
settings->value("interface/visualdeckstoragedrawunusedcoloridentities", true).toBool();
|
settings->value("interface/visualdeckstoragedrawunusedcoloridentities", true).toBool();
|
||||||
visualDeckStorageUnusedColorIdentitiesOpacity =
|
visualDeckStorageUnusedColorIdentitiesOpacity =
|
||||||
settings->value("interface/visualdeckstorageunusedcoloridentitiesopacity", 15).toInt();
|
settings->value("interface/visualdeckstorageunusedcoloridentitiesopacity", 15).toInt();
|
||||||
|
visualDeckStorageTooltipType = settings->value("interface/visualdeckstoragetooltiptype", 0).toInt();
|
||||||
visualDeckStoragePromptForConversion =
|
visualDeckStoragePromptForConversion =
|
||||||
settings->value("interface/visualdeckstoragepromptforconversion", true).toBool();
|
settings->value("interface/visualdeckstoragepromptforconversion", true).toBool();
|
||||||
visualDeckStorageAlwaysConvert = settings->value("interface/visualdeckstoragealwaysconvert", false).toBool();
|
visualDeckStorageAlwaysConvert = settings->value("interface/visualdeckstoragealwaysconvert", false).toBool();
|
||||||
@@ -288,9 +310,15 @@ SettingsCache::SettingsCache()
|
|||||||
settings->value("interface/visualdeckstorageselectionanimation", true).toBool();
|
settings->value("interface/visualdeckstorageselectionanimation", true).toBool();
|
||||||
defaultDeckEditorType = settings->value("interface/defaultDeckEditorType", 1).toInt();
|
defaultDeckEditorType = settings->value("interface/defaultDeckEditorType", 1).toInt();
|
||||||
visualDatabaseDisplayFilterToMostRecentSetsEnabled =
|
visualDatabaseDisplayFilterToMostRecentSetsEnabled =
|
||||||
settings->value("interface/visualdatabasedisplayfiltertomostrecentsetsenabled", true).toBool();
|
settings->value("interface/visualdatabasedisplayfiltertomostrecentsetsenabled", false).toBool();
|
||||||
visualDatabaseDisplayFilterToMostRecentSetsAmount =
|
visualDatabaseDisplayFilterToMostRecentSetsAmount =
|
||||||
settings->value("interface/visualdatabasedisplayfiltertomostrecentsetsamount", 10).toInt();
|
settings->value("interface/visualdatabasedisplayfiltertomostrecentsetsamount", 10).toInt();
|
||||||
|
visualDeckEditorSampleHandSize = settings->value("interface/visualdeckeditorsamplehandsize", 7).toInt();
|
||||||
|
visualDeckEditorCardSize = settings->value("interface/visualdeckeditorcardsize", 100).toInt();
|
||||||
|
visualDatabaseDisplayCardSize = settings->value("interface/visualdatabasedisplaycardsize", 100).toInt();
|
||||||
|
edhrecCardSize = settings->value("interface/edhreccardsize", 100).toInt();
|
||||||
|
archidektPreviewSize = settings->value("interface/archidektpreviewsize", 100).toInt();
|
||||||
|
|
||||||
horizontalHand = settings->value("hand/horizontal", true).toBool();
|
horizontalHand = settings->value("hand/horizontal", true).toBool();
|
||||||
invertVerticalCoordinate = settings->value("table/invert_vertical", false).toBool();
|
invertVerticalCoordinate = settings->value("table/invert_vertical", false).toBool();
|
||||||
minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 4).toInt();
|
minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 4).toInt();
|
||||||
@@ -341,6 +369,7 @@ SettingsCache::SettingsCache()
|
|||||||
spectatorsCanSeeEverything = settings->value("game/spectatorscanseeeverything", false).toBool();
|
spectatorsCanSeeEverything = settings->value("game/spectatorscanseeeverything", false).toBool();
|
||||||
createGameAsSpectator = settings->value("game/creategameasspectator", false).toBool();
|
createGameAsSpectator = settings->value("game/creategameasspectator", false).toBool();
|
||||||
defaultStartingLifeTotal = settings->value("game/defaultstartinglifetotal", 20).toInt();
|
defaultStartingLifeTotal = settings->value("game/defaultstartinglifetotal", 20).toInt();
|
||||||
|
shareDecklistsOnLoad = settings->value("game/sharedecklistsonload", false).toBool();
|
||||||
rememberGameSettings = settings->value("game/remembergamesettings", true).toBool();
|
rememberGameSettings = settings->value("game/remembergamesettings", true).toBool();
|
||||||
clientID = settings->value("personal/clientid", CLIENT_INFO_NOT_SET).toString();
|
clientID = settings->value("personal/clientid", CLIENT_INFO_NOT_SET).toString();
|
||||||
clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString();
|
clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString();
|
||||||
@@ -535,6 +564,20 @@ void SettingsCache::setThemeName(const QString &_themeName)
|
|||||||
emit themeChanged();
|
emit themeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setHomeTabBackgroundSource(const QString &_backgroundSource)
|
||||||
|
{
|
||||||
|
homeTabBackgroundSource = _backgroundSource;
|
||||||
|
settings->setValue("home/background", homeTabBackgroundSource);
|
||||||
|
emit homeTabBackgroundSourceChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setHomeTabBackgroundShuffleFrequency(int _frequency)
|
||||||
|
{
|
||||||
|
homeTabBackgroundShuffleFrequency = _frequency;
|
||||||
|
settings->setValue("home/background/shuffleTimer", homeTabBackgroundShuffleFrequency);
|
||||||
|
emit homeTabBackgroundShuffleFrequencyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setTabVisualDeckStorageOpen(bool value)
|
void SettingsCache::setTabVisualDeckStorageOpen(bool value)
|
||||||
{
|
{
|
||||||
tabVisualDeckStorageOpen = value;
|
tabVisualDeckStorageOpen = value;
|
||||||
@@ -584,6 +627,13 @@ void SettingsCache::setPicDownload(QT_STATE_CHANGED_T _picDownload)
|
|||||||
emit picDownloadChanged();
|
emit picDownloadChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setShowStatusBar(bool value)
|
||||||
|
{
|
||||||
|
showStatusBar = value;
|
||||||
|
settings->setValue("personal/showStatusBar", showStatusBar);
|
||||||
|
emit showStatusBarChanged(value);
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setNotificationsEnabled(QT_STATE_CHANGED_T _notificationsEnabled)
|
void SettingsCache::setNotificationsEnabled(QT_STATE_CHANGED_T _notificationsEnabled)
|
||||||
{
|
{
|
||||||
notificationsEnabled = static_cast<bool>(_notificationsEnabled);
|
notificationsEnabled = static_cast<bool>(_notificationsEnabled);
|
||||||
@@ -620,6 +670,12 @@ void SettingsCache::setPlayToStack(QT_STATE_CHANGED_T _playToStack)
|
|||||||
settings->setValue("interface/playtostack", playToStack);
|
settings->setValue("interface/playtostack", playToStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setDoNotDeleteArrowsInSubPhases(QT_STATE_CHANGED_T _doNotDeleteArrowsInSubPhases)
|
||||||
|
{
|
||||||
|
doNotDeleteArrowsInSubPhases = static_cast<bool>(_doNotDeleteArrowsInSubPhases);
|
||||||
|
settings->setValue("interface/doNotDeleteArrowsInSubPhases", doNotDeleteArrowsInSubPhases);
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setStartingHandSize(int _startingHandSize)
|
void SettingsCache::setStartingHandSize(int _startingHandSize)
|
||||||
{
|
{
|
||||||
startingHandSize = _startingHandSize;
|
startingHandSize = _startingHandSize;
|
||||||
@@ -729,6 +785,13 @@ void SettingsCache::setVisualDeckStorageShowTagFilter(QT_STATE_CHANGED_T _showTa
|
|||||||
emit visualDeckStorageShowTagFilterChanged(visualDeckStorageShowTagFilter);
|
emit visualDeckStorageShowTagFilterChanged(visualDeckStorageShowTagFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setVisualDeckStorageDefaultTagsList(QStringList _defaultTagsList)
|
||||||
|
{
|
||||||
|
visualDeckStorageDefaultTagsList = _defaultTagsList;
|
||||||
|
settings->setValue("interface/visualdeckstoragedefaulttagslist", visualDeckStorageDefaultTagsList);
|
||||||
|
emit visualDeckStorageDefaultTagsListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setVisualDeckStorageSearchFolderNames(QT_STATE_CHANGED_T value)
|
void SettingsCache::setVisualDeckStorageSearchFolderNames(QT_STATE_CHANGED_T value)
|
||||||
{
|
{
|
||||||
visualDeckStorageSearchFolderNames = value;
|
visualDeckStorageSearchFolderNames = value;
|
||||||
@@ -773,6 +836,12 @@ void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity(int _visual
|
|||||||
emit visualDeckStorageUnusedColorIdentitiesOpacityChanged(visualDeckStorageUnusedColorIdentitiesOpacity);
|
emit visualDeckStorageUnusedColorIdentitiesOpacityChanged(visualDeckStorageUnusedColorIdentitiesOpacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setVisualDeckStorageTooltipType(int value)
|
||||||
|
{
|
||||||
|
visualDeckStorageTooltipType = value;
|
||||||
|
settings->setValue("interface/visualdeckstoragetooltiptype", visualDeckStorageTooltipType);
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setVisualDeckStoragePromptForConversion(bool _visualDeckStoragePromptForConversion)
|
void SettingsCache::setVisualDeckStoragePromptForConversion(bool _visualDeckStoragePromptForConversion)
|
||||||
{
|
{
|
||||||
visualDeckStoragePromptForConversion = _visualDeckStoragePromptForConversion;
|
visualDeckStoragePromptForConversion = _visualDeckStoragePromptForConversion;
|
||||||
@@ -799,6 +868,34 @@ void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T va
|
|||||||
emit visualDeckStorageSelectionAnimationChanged(visualDeckStorageSelectionAnimation);
|
emit visualDeckStorageSelectionAnimationChanged(visualDeckStorageSelectionAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setVisualDeckEditorCardSize(int _visualDeckEditorCardSize)
|
||||||
|
{
|
||||||
|
visualDeckEditorCardSize = _visualDeckEditorCardSize;
|
||||||
|
settings->setValue("interface/visualdeckeditorcardsize", visualDeckEditorCardSize);
|
||||||
|
emit visualDeckEditorCardSizeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setVisualDatabaseDisplayCardSize(int _visualDatabaseDisplayCardSize)
|
||||||
|
{
|
||||||
|
visualDatabaseDisplayCardSize = _visualDatabaseDisplayCardSize;
|
||||||
|
settings->setValue("interface/visualdatabasedisplaycardsize", visualDatabaseDisplayCardSize);
|
||||||
|
emit visualDatabaseDisplayCardSizeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setEDHRecCardSize(int _edhrecCardSize)
|
||||||
|
{
|
||||||
|
edhrecCardSize = _edhrecCardSize;
|
||||||
|
settings->setValue("interface/edhreccardsize", edhrecCardSize);
|
||||||
|
emit edhRecCardSizeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setArchidektPreviewCardSize(int _archidektPreviewCardSize)
|
||||||
|
{
|
||||||
|
archidektPreviewSize = _archidektPreviewCardSize;
|
||||||
|
settings->setValue("interface/archidektpreviewsize", archidektPreviewSize);
|
||||||
|
emit archidektPreviewSizeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setDefaultDeckEditorType(int value)
|
void SettingsCache::setDefaultDeckEditorType(int value)
|
||||||
{
|
{
|
||||||
defaultDeckEditorType = value;
|
defaultDeckEditorType = value;
|
||||||
@@ -821,6 +918,13 @@ void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsAmount(int _am
|
|||||||
emit visualDatabaseDisplayFilterToMostRecentSetsAmountChanged(visualDatabaseDisplayFilterToMostRecentSetsAmount);
|
emit visualDatabaseDisplayFilterToMostRecentSetsAmountChanged(visualDatabaseDisplayFilterToMostRecentSetsAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setVisualDeckEditorSampleHandSize(int _amount)
|
||||||
|
{
|
||||||
|
visualDeckEditorSampleHandSize = _amount;
|
||||||
|
settings->setValue("interface/visualdeckeditorsamplehandsize", visualDeckEditorSampleHandSize);
|
||||||
|
emit visualDeckEditorSampleHandSizeAmountChanged(visualDeckEditorSampleHandSize);
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand)
|
void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand)
|
||||||
{
|
{
|
||||||
horizontalHand = static_cast<bool>(_horizontalHand);
|
horizontalHand = static_cast<bool>(_horizontalHand);
|
||||||
@@ -1319,7 +1423,13 @@ void SettingsCache::setDefaultStartingLifeTotal(const int _defaultStartingLifeTo
|
|||||||
{
|
{
|
||||||
defaultStartingLifeTotal = _defaultStartingLifeTotal;
|
defaultStartingLifeTotal = _defaultStartingLifeTotal;
|
||||||
settings->setValue("game/defaultstartinglifetotal", defaultStartingLifeTotal);
|
settings->setValue("game/defaultstartinglifetotal", defaultStartingLifeTotal);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setShareDecklistsOnLoad(const bool _shareDecklistsOnLoad)
|
||||||
|
{
|
||||||
|
shareDecklistsOnLoad = _shareDecklistsOnLoad;
|
||||||
|
settings->setValue("game/sharedecklistsonload", shareDecklistsOnLoad);
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value)
|
void SettingsCache::setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value)
|
||||||
{
|
{
|
||||||
@@ -1327,6 +1437,30 @@ void SettingsCache::setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value)
|
|||||||
settings->setValue("personal/startupUpdateCheck", checkUpdatesOnStartup);
|
settings->setValue("personal/startupUpdateCheck", checkUpdatesOnStartup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setStartupCardUpdateCheckPromptForUpdate(bool value)
|
||||||
|
{
|
||||||
|
startupCardUpdateCheckPromptForUpdate = value;
|
||||||
|
settings->setValue("personal/startupCardUpdateCheckPromptForUpdate", startupCardUpdateCheckPromptForUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setStartupCardUpdateCheckAlwaysUpdate(bool value)
|
||||||
|
{
|
||||||
|
startupCardUpdateCheckAlwaysUpdate = value;
|
||||||
|
settings->setValue("personal/startupCardUpdateCheckAlwaysUpdate", startupCardUpdateCheckAlwaysUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setCardUpdateCheckInterval(int value)
|
||||||
|
{
|
||||||
|
cardUpdateCheckInterval = value;
|
||||||
|
settings->setValue("personal/cardUpdateCheckInterval", cardUpdateCheckInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setLastCardUpdateCheck(QDate value)
|
||||||
|
{
|
||||||
|
lastCardUpdateCheck = value;
|
||||||
|
settings->setValue("personal/lastCardUpdateCheck", lastCardUpdateCheck);
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setRememberGameSettings(const bool _rememberGameSettings)
|
void SettingsCache::setRememberGameSettings(const bool _rememberGameSettings)
|
||||||
{
|
{
|
||||||
rememberGameSettings = _rememberGameSettings;
|
rememberGameSettings = _rememberGameSettings;
|
||||||
@@ -1411,7 +1545,7 @@ void SettingsCache::resetPaths()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsCache &SettingsCache::instance()
|
CardCounterSettings &SettingsCache::cardCounters() const
|
||||||
{
|
{
|
||||||
return *settingsCache;
|
return *cardCounterSettings;
|
||||||
}
|
}
|
||||||
56
cockatrice/src/client/settings/card_counter_settings.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include "card_counter_settings.h"
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
|
CardCounterSettings::CardCounterSettings(const QString &settingsPath, QObject *parent)
|
||||||
|
: SettingsManager(settingsPath + "global.ini", "cards", "counters", parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardCounterSettings::setColor(int counterId, const QColor &color)
|
||||||
|
{
|
||||||
|
QString key = QString("cards/counters/%1/color").arg(counterId);
|
||||||
|
|
||||||
|
if (settings.value(key).value<QColor>() == color)
|
||||||
|
return;
|
||||||
|
|
||||||
|
settings.setValue(key, color);
|
||||||
|
emit colorChanged(counterId, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor CardCounterSettings::color(int counterId) const
|
||||||
|
{
|
||||||
|
QColor defaultColor;
|
||||||
|
|
||||||
|
if (counterId < 6) {
|
||||||
|
// Preserve legacy colors
|
||||||
|
defaultColor = QColor::fromHsv(counterId * 60, 150, 255);
|
||||||
|
} else {
|
||||||
|
// Future-proof support for more counters with pseudo-random colors
|
||||||
|
int h = (counterId * 37) % 360;
|
||||||
|
int s = 128 + 64 * qSin((counterId * 97) * 0.1); // 64-192
|
||||||
|
int v = 196 + 32 * qSin((counterId * 101) * 0.07); // 164-228
|
||||||
|
|
||||||
|
defaultColor = QColor::fromHsv(h, s, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings.value(QString("cards/counters/%1/color").arg(counterId), defaultColor).value<QColor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CardCounterSettings::displayName(int counterId) const
|
||||||
|
{
|
||||||
|
// Currently, card counters name are fixed to A, B, ..., Z, AA, AB, ...
|
||||||
|
|
||||||
|
auto nChars = 1 + counterId / 26;
|
||||||
|
QString str;
|
||||||
|
str.resize(nChars);
|
||||||
|
|
||||||
|
for (auto it = str.rbegin(); it != str.rend(); ++it) {
|
||||||
|
*it = QChar('A' + (counterId) % 26);
|
||||||
|
counterId /= 26;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
33
cockatrice/src/client/settings/card_counter_settings.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* @file card_counter_settings.h
|
||||||
|
* @ingroup GameSettings
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CARD_COUNTER_SETTINGS_H
|
||||||
|
#define CARD_COUNTER_SETTINGS_H
|
||||||
|
|
||||||
|
#include <libcockatrice/settings/settings_manager.h>
|
||||||
|
|
||||||
|
class QSettings;
|
||||||
|
class QColor;
|
||||||
|
|
||||||
|
class CardCounterSettings : public SettingsManager
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CardCounterSettings(const QString &settingsPath, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
[[nodiscard]] QColor color(int counterId) const;
|
||||||
|
|
||||||
|
[[nodiscard]] QString displayName(int counterId) const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setColor(int counterId, const QColor &color);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void colorChanged(int counterId, const QColor &color);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CARD_COUNTER_SETTINGS_H
|
||||||
@@ -150,7 +150,7 @@ void ShortcutTreeView::currentChanged(const QModelIndex ¤t, const QModelIn
|
|||||||
*/
|
*/
|
||||||
void ShortcutTreeView::updateSearchString(const QString &searchString)
|
void ShortcutTreeView::updateSearchString(const QString &searchString)
|
||||||
{
|
{
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION > QT_VERSION_CHECK(5, 14, 0)
|
||||||
const auto skipEmptyParts = Qt::SkipEmptyParts;
|
const auto skipEmptyParts = Qt::SkipEmptyParts;
|
||||||
#else
|
#else
|
||||||
const auto skipEmptyParts = QString::SkipEmptyParts;
|
const auto skipEmptyParts = QString::SkipEmptyParts;
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file shortcut_treeview.h
|
||||||
|
* @ingroup CoreSettings
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef SHORTCUT_TREEVIEW_H
|
#ifndef SHORTCUT_TREEVIEW_H
|
||||||
#define SHORTCUT_TREEVIEW_H
|
#define SHORTCUT_TREEVIEW_H
|
||||||
|
|
||||||
#include <QModelIndex>
|
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
@@ -16,7 +21,7 @@ public:
|
|||||||
explicit ShortcutFilterProxyModel(QObject *parent = nullptr);
|
explicit ShortcutFilterProxyModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
[[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ShortcutTreeView : public QTreeView
|
class ShortcutTreeView : public QTreeView
|
||||||
@@ -115,6 +115,13 @@ ShortcutKey ShortcutsSettings::getShortcut(const QString &name) const
|
|||||||
return getDefaultShortcut(name);
|
return getDefaultShortcut(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the first shortcut for the given action.
|
||||||
|
*
|
||||||
|
* NOTE: In most cases you should be using ShortcutsSettings::getShortcut instead,
|
||||||
|
* as that will return all shortcuts if there are multiple shortcuts.
|
||||||
|
* The only reason to use this method is if an object does not accept multiple shortcuts, such as with QButtons.
|
||||||
|
*/
|
||||||
QKeySequence ShortcutsSettings::getSingleShortcut(const QString &name) const
|
QKeySequence ShortcutsSettings::getSingleShortcut(const QString &name) const
|
||||||
{
|
{
|
||||||
return getShortcut(name).at(0);
|
return getShortcut(name).at(0);
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @file shortcuts_settings.h
|
||||||
|
* @ingroup CoreSettings
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef SHORTCUTSSETTINGS_H
|
#ifndef SHORTCUTSSETTINGS_H
|
||||||
#define SHORTCUTSSETTINGS_H
|
#define SHORTCUTSSETTINGS_H
|
||||||
|
|
||||||
@@ -27,6 +33,7 @@ public:
|
|||||||
Move_bottom,
|
Move_bottom,
|
||||||
Gameplay,
|
Gameplay,
|
||||||
Drawing,
|
Drawing,
|
||||||
|
Hand,
|
||||||
Chat_room,
|
Chat_room,
|
||||||
Game_window,
|
Game_window,
|
||||||
Load_deck,
|
Load_deck,
|
||||||
@@ -65,6 +72,8 @@ public:
|
|||||||
return QApplication::translate("shortcutsTab", "Gameplay");
|
return QApplication::translate("shortcutsTab", "Gameplay");
|
||||||
case Drawing:
|
case Drawing:
|
||||||
return QApplication::translate("shortcutsTab", "Drawing");
|
return QApplication::translate("shortcutsTab", "Drawing");
|
||||||
|
case Hand:
|
||||||
|
return QApplication::translate("shortcutsTab", "Hand");
|
||||||
case Chat_room:
|
case Chat_room:
|
||||||
return QApplication::translate("shortcutsTab", "Chat Room");
|
return QApplication::translate("shortcutsTab", "Chat Room");
|
||||||
case Game_window:
|
case Game_window:
|
||||||
@@ -90,15 +99,15 @@ public:
|
|||||||
void setSequence(const QList &_sequence)
|
void setSequence(const QList &_sequence)
|
||||||
{
|
{
|
||||||
QList::operator=(_sequence);
|
QList::operator=(_sequence);
|
||||||
};
|
}
|
||||||
QString getName() const
|
[[nodiscard]] QString getName() const
|
||||||
{
|
{
|
||||||
return QApplication::translate("shortcutsTab", name.toUtf8().data());
|
return QApplication::translate("shortcutsTab", name.toUtf8().data());
|
||||||
};
|
}
|
||||||
QString getGroupName() const
|
[[nodiscard]] QString getGroupName() const
|
||||||
{
|
{
|
||||||
return ShortcutGroup::getGroupName(group);
|
return ShortcutGroup::getGroupName(group);
|
||||||
};
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString name;
|
QString name;
|
||||||
@@ -111,24 +120,24 @@ class ShortcutsSettings : public QObject
|
|||||||
public:
|
public:
|
||||||
explicit ShortcutsSettings(const QString &settingsFilePath, QObject *parent = nullptr);
|
explicit ShortcutsSettings(const QString &settingsFilePath, QObject *parent = nullptr);
|
||||||
|
|
||||||
ShortcutKey getDefaultShortcut(const QString &name) const;
|
[[nodiscard]] ShortcutKey getDefaultShortcut(const QString &name) const;
|
||||||
ShortcutKey getShortcut(const QString &name) const;
|
[[nodiscard]] ShortcutKey getShortcut(const QString &name) const;
|
||||||
QKeySequence getSingleShortcut(const QString &name) const;
|
[[nodiscard]] QKeySequence getSingleShortcut(const QString &name) const;
|
||||||
QString getDefaultShortcutString(const QString &name) const;
|
[[nodiscard]] QString getDefaultShortcutString(const QString &name) const;
|
||||||
QString getShortcutString(const QString &name) const;
|
[[nodiscard]] QString getShortcutString(const QString &name) const;
|
||||||
QString getShortcutFriendlyName(const QString &shortcutName) const;
|
[[nodiscard]] QString getShortcutFriendlyName(const QString &shortcutName) const;
|
||||||
QList<QString> getAllShortcutKeys() const
|
[[nodiscard]] QList<QString> getAllShortcutKeys() const
|
||||||
{
|
{
|
||||||
return shortCuts.keys();
|
return shortCuts.keys();
|
||||||
};
|
}
|
||||||
|
|
||||||
void setShortcuts(const QString &name, const QList<QKeySequence> &Sequence);
|
void setShortcuts(const QString &name, const QList<QKeySequence> &Sequence);
|
||||||
void setShortcuts(const QString &name, const QKeySequence &Sequence);
|
void setShortcuts(const QString &name, const QKeySequence &Sequence);
|
||||||
void setShortcuts(const QString &name, const QString &sequences);
|
void setShortcuts(const QString &name, const QString &sequences);
|
||||||
|
|
||||||
bool isKeyAllowed(const QString &name, const QString &sequences) const;
|
[[nodiscard]] bool isKeyAllowed(const QString &name, const QString &sequences) const;
|
||||||
bool isValid(const QString &name, const QString &sequences) const;
|
[[nodiscard]] bool isValid(const QString &name, const QString &sequences) const;
|
||||||
QStringList findOverlaps(const QString &name, const QString &sequences) const;
|
[[nodiscard]] QStringList findOverlaps(const QString &name, const QString &sequences) const;
|
||||||
|
|
||||||
void resetAllShortcuts();
|
void resetAllShortcuts();
|
||||||
void clearAllShortcuts();
|
void clearAllShortcuts();
|
||||||
@@ -143,8 +152,8 @@ private:
|
|||||||
QString settingsFilePath;
|
QString settingsFilePath;
|
||||||
QHash<QString, ShortcutKey> shortCuts;
|
QHash<QString, ShortcutKey> shortCuts;
|
||||||
|
|
||||||
QString stringifySequence(const QList<QKeySequence> &Sequence) const;
|
[[nodiscard]] QString stringifySequence(const QList<QKeySequence> &Sequence) const;
|
||||||
QList<QKeySequence> parseSequenceString(const QString &stringSequence) const;
|
[[nodiscard]] QList<QKeySequence> parseSequenceString(const QString &stringSequence) const;
|
||||||
|
|
||||||
const QHash<QString, ShortcutKey> defaultShortCuts = {
|
const QHash<QString, ShortcutKey> defaultShortCuts = {
|
||||||
{"MainWindow/aCheckCardUpdates", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Check for Card Updates..."),
|
{"MainWindow/aCheckCardUpdates", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Check for Card Updates..."),
|
||||||
@@ -173,6 +182,9 @@ private:
|
|||||||
{"MainWindow/aWatchReplay", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Watch Replay..."),
|
{"MainWindow/aWatchReplay", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Watch Replay..."),
|
||||||
parseSequenceString(""),
|
parseSequenceString(""),
|
||||||
ShortcutGroup::Main_Window)},
|
ShortcutGroup::Main_Window)},
|
||||||
|
{"MainWindow/aStatusBar", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Show Status Bar"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Main_Window)},
|
||||||
{"TabDeckEditor/aAnalyzeDeck", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Analyze Deck (deckstats.net)"),
|
{"TabDeckEditor/aAnalyzeDeck", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Analyze Deck (deckstats.net)"),
|
||||||
parseSequenceString(""),
|
parseSequenceString(""),
|
||||||
ShortcutGroup::Deck_Editor)},
|
ShortcutGroup::Deck_Editor)},
|
||||||
@@ -267,6 +279,13 @@ private:
|
|||||||
{"DeckViewContainer/loadRemoteButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Remote Deck..."),
|
{"DeckViewContainer/loadRemoteButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Remote Deck..."),
|
||||||
parseSequenceString("Ctrl+Alt+O"),
|
parseSequenceString("Ctrl+Alt+O"),
|
||||||
ShortcutGroup::Game_Lobby)},
|
ShortcutGroup::Game_Lobby)},
|
||||||
|
{"DeckViewContainer/loadFromClipboardButton",
|
||||||
|
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."),
|
||||||
|
parseSequenceString("Ctrl+Shift+V"),
|
||||||
|
ShortcutGroup::Game_Lobby)},
|
||||||
|
{"DeckViewContainer/unloadDeckButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Unload Deck"),
|
||||||
|
parseSequenceString("Ctrl+Alt+U"),
|
||||||
|
ShortcutGroup::Game_Lobby)},
|
||||||
{"DeckViewContainer/readyStartButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Ready to Start"),
|
{"DeckViewContainer/readyStartButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Ready to Start"),
|
||||||
parseSequenceString("Ctrl+Shift+S"),
|
parseSequenceString("Ctrl+Shift+S"),
|
||||||
ShortcutGroup::Game_Lobby)},
|
ShortcutGroup::Game_Lobby)},
|
||||||
@@ -274,31 +293,61 @@ private:
|
|||||||
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Toggle Sideboard Lock"),
|
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Toggle Sideboard Lock"),
|
||||||
parseSequenceString("Ctrl+Shift+B"),
|
parseSequenceString("Ctrl+Shift+B"),
|
||||||
ShortcutGroup::Game_Lobby)},
|
ShortcutGroup::Game_Lobby)},
|
||||||
{"Player/aCCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Green Counter"),
|
{"DeckViewContainer/forceStartGameButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Force Start"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Game_Lobby)},
|
||||||
|
{"Player/aCCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (F)"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Card_Counters)},
|
||||||
|
{"Player/aRCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (F)"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Card_Counters)},
|
||||||
|
{"Player/aSCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (F)..."),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Card_Counters)},
|
||||||
|
{"Player/aCCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (E)"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Card_Counters)},
|
||||||
|
{"Player/aRCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (E)"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Card_Counters)},
|
||||||
|
{"Player/aSCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (E)..."),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Card_Counters)},
|
||||||
|
{"Player/aCCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter(D)"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Card_Counters)},
|
||||||
|
{"Player/aRCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (D)"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Card_Counters)},
|
||||||
|
{"Player/aSCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (D)..."),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Card_Counters)},
|
||||||
|
{"Player/aCCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (C)"),
|
||||||
parseSequenceString("Ctrl+>"),
|
parseSequenceString("Ctrl+>"),
|
||||||
ShortcutGroup::Card_Counters)},
|
ShortcutGroup::Card_Counters)},
|
||||||
{"Player/aRCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Green Counter"),
|
{"Player/aRCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (C)"),
|
||||||
parseSequenceString("Ctrl+<"),
|
parseSequenceString("Ctrl+<"),
|
||||||
ShortcutGroup::Card_Counters)},
|
ShortcutGroup::Card_Counters)},
|
||||||
{"Player/aSCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Green Counters..."),
|
{"Player/aSCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (C)..."),
|
||||||
parseSequenceString("Ctrl+?"),
|
parseSequenceString("Ctrl+?"),
|
||||||
ShortcutGroup::Card_Counters)},
|
ShortcutGroup::Card_Counters)},
|
||||||
{"Player/aCCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Yellow Counter"),
|
{"Player/aCCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (B)"),
|
||||||
parseSequenceString("Ctrl+."),
|
parseSequenceString("Ctrl+."),
|
||||||
ShortcutGroup::Card_Counters)},
|
ShortcutGroup::Card_Counters)},
|
||||||
{"Player/aRCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Yellow Counter"),
|
{"Player/aRCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (B)"),
|
||||||
parseSequenceString("Ctrl+,"),
|
parseSequenceString("Ctrl+,"),
|
||||||
ShortcutGroup::Card_Counters)},
|
ShortcutGroup::Card_Counters)},
|
||||||
{"Player/aSCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Yellow Counters..."),
|
{"Player/aSCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (B)..."),
|
||||||
parseSequenceString("Ctrl+/"),
|
parseSequenceString("Ctrl+/"),
|
||||||
ShortcutGroup::Card_Counters)},
|
ShortcutGroup::Card_Counters)},
|
||||||
{"Player/aCCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Red Counter"),
|
{"Player/aCCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (A)"),
|
||||||
parseSequenceString("Alt+."),
|
parseSequenceString("Alt+."),
|
||||||
ShortcutGroup::Card_Counters)},
|
ShortcutGroup::Card_Counters)},
|
||||||
{"Player/aRCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Red Counter"),
|
{"Player/aRCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (A)"),
|
||||||
parseSequenceString("Alt+,"),
|
parseSequenceString("Alt+,"),
|
||||||
ShortcutGroup::Card_Counters)},
|
ShortcutGroup::Card_Counters)},
|
||||||
{"Player/aSCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Red Counters..."),
|
{"Player/aSCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (A)..."),
|
||||||
parseSequenceString("Alt+/"),
|
parseSequenceString("Alt+/"),
|
||||||
ShortcutGroup::Card_Counters)},
|
ShortcutGroup::Card_Counters)},
|
||||||
{"Player/aInc", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Life Counter"),
|
{"Player/aInc", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Life Counter"),
|
||||||
@@ -373,6 +422,10 @@ private:
|
|||||||
{"Player/aSetCounter_storm", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Other Counters..."),
|
{"Player/aSetCounter_storm", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Other Counters..."),
|
||||||
parseSequenceString("Ctrl+\\"),
|
parseSequenceString("Ctrl+\\"),
|
||||||
ShortcutGroup::Player_Counters)},
|
ShortcutGroup::Player_Counters)},
|
||||||
|
{"Player/aIncrementAllCardCounters",
|
||||||
|
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Increment all card counters"),
|
||||||
|
parseSequenceString("Ctrl+Shift+A"),
|
||||||
|
ShortcutGroup::Playing_Area)},
|
||||||
{"Player/aIncP", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Power (+1/+0)"),
|
{"Player/aIncP", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Power (+1/+0)"),
|
||||||
parseSequenceString("Ctrl++;Ctrl+="),
|
parseSequenceString("Ctrl++;Ctrl+="),
|
||||||
ShortcutGroup::Power_Toughness)},
|
ShortcutGroup::Power_Toughness)},
|
||||||
@@ -490,6 +543,9 @@ private:
|
|||||||
{"Player/aSelectColumn", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Select All Cards in Column"),
|
{"Player/aSelectColumn", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Select All Cards in Column"),
|
||||||
parseSequenceString("Ctrl+Shift+C"),
|
parseSequenceString("Ctrl+Shift+C"),
|
||||||
ShortcutGroup::Playing_Area)},
|
ShortcutGroup::Playing_Area)},
|
||||||
|
{"Player/aRevealToAll", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Reveal Selected Cards to All Players"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Playing_Area)},
|
||||||
{"Player/aMoveToBottomLibrary", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Bottom of Library"),
|
{"Player/aMoveToBottomLibrary", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Bottom of Library"),
|
||||||
parseSequenceString("Ctrl+B"),
|
parseSequenceString("Ctrl+B"),
|
||||||
ShortcutGroup::Move_selected)},
|
ShortcutGroup::Move_selected)},
|
||||||
@@ -619,6 +675,22 @@ private:
|
|||||||
{"Player/aAlwaysLookAtTopCard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Always Look At Top Card"),
|
{"Player/aAlwaysLookAtTopCard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Always Look At Top Card"),
|
||||||
parseSequenceString("Ctrl+Shift+N"),
|
parseSequenceString("Ctrl+Shift+N"),
|
||||||
ShortcutGroup::Drawing)},
|
ShortcutGroup::Drawing)},
|
||||||
|
{"Player/aSortHandByName", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Sort Hand by Name"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Hand)},
|
||||||
|
{"Player/aSortHandByType", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Sort Hand by Type"),
|
||||||
|
parseSequenceString("Ctrl+Shift+H"),
|
||||||
|
ShortcutGroup::Hand)},
|
||||||
|
{"Player/aSortHandByManaValue", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Sort Hand by Mana Value"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Hand)},
|
||||||
|
{"Player/aRevealHandToAll", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Reveal Hand to All Players"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Hand)},
|
||||||
|
{"Player/aRevealRandomHandCardToAll",
|
||||||
|
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Reveal Random Card to All Players"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Hand)},
|
||||||
{"Player/aRotateViewCW", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Rotate View Clockwise"),
|
{"Player/aRotateViewCW", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Rotate View Clockwise"),
|
||||||
parseSequenceString(""),
|
parseSequenceString(""),
|
||||||
ShortcutGroup::Gameplay)},
|
ShortcutGroup::Gameplay)},
|
||||||
@@ -660,6 +732,8 @@ private:
|
|||||||
ShortcutGroup::Replays)},
|
ShortcutGroup::Replays)},
|
||||||
{"Tabs/aTabDeckEditor",
|
{"Tabs/aTabDeckEditor",
|
||||||
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Deck Editor"), parseSequenceString(""), ShortcutGroup::Tabs)},
|
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Deck Editor"), parseSequenceString(""), ShortcutGroup::Tabs)},
|
||||||
|
{"Tabs/aTabHome",
|
||||||
|
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Home"), parseSequenceString(""), ShortcutGroup::Tabs)},
|
||||||
{"Tabs/aTabVisualDeckStorage", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Visual Deck Storage"),
|
{"Tabs/aTabVisualDeckStorage", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Visual Deck Storage"),
|
||||||
parseSequenceString(""),
|
parseSequenceString(""),
|
||||||
ShortcutGroup::Tabs)},
|
ShortcutGroup::Tabs)},
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @file sound_engine.h
|
||||||
|
* @ingroup Core
|
||||||
|
* @brief TODO: Document this.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef SOUNDENGINE_H
|
#ifndef SOUNDENGINE_H
|
||||||
#define SOUNDENGINE_H
|
#define SOUNDENGINE_H
|
||||||
|
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
#ifndef TAB_GENERIC_DECK_EDITOR_H
|
|
||||||
#define TAB_GENERIC_DECK_EDITOR_H
|
|
||||||
|
|
||||||
#include "../../game/cards/card_info.h"
|
|
||||||
#include "../menus/deck_editor/deck_editor_menu.h"
|
|
||||||
#include "../ui/widgets/deck_editor/deck_editor_card_info_dock_widget.h"
|
|
||||||
#include "../ui/widgets/deck_editor/deck_editor_database_display_widget.h"
|
|
||||||
#include "../ui/widgets/deck_editor/deck_editor_deck_dock_widget.h"
|
|
||||||
#include "../ui/widgets/deck_editor/deck_editor_filter_dock_widget.h"
|
|
||||||
#include "../ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.h"
|
|
||||||
#include "../ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
|
|
||||||
#include "tab.h"
|
|
||||||
|
|
||||||
class CardDatabaseModel;
|
|
||||||
class CardDatabaseDisplayModel;
|
|
||||||
|
|
||||||
class CardInfoFrameWidget;
|
|
||||||
class DeckLoader;
|
|
||||||
class DeckEditorMenu;
|
|
||||||
class DeckEditorCardInfoDockWidget;
|
|
||||||
class DeckEditorDatabaseDisplayWidget;
|
|
||||||
class DeckEditorDeckDockWidget;
|
|
||||||
class DeckEditorFilterDockWidget;
|
|
||||||
class DeckEditorPrintingSelectorDockWidget;
|
|
||||||
class DeckPreviewDeckTagsDisplayWidget;
|
|
||||||
class Response;
|
|
||||||
class FilterTreeModel;
|
|
||||||
class FilterBuilder;
|
|
||||||
|
|
||||||
class QTreeView;
|
|
||||||
class QTextEdit;
|
|
||||||
class QLabel;
|
|
||||||
class QComboBox;
|
|
||||||
class QGroupBox;
|
|
||||||
class QMessageBox;
|
|
||||||
class QHBoxLayout;
|
|
||||||
class QVBoxLayout;
|
|
||||||
class QPushButton;
|
|
||||||
class QDockWidget;
|
|
||||||
class QMenu;
|
|
||||||
class QAction;
|
|
||||||
|
|
||||||
class AbstractTabDeckEditor : public Tab
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
friend class DeckEditorMenu;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit AbstractTabDeckEditor(TabSupervisor *_tabSupervisor);
|
|
||||||
|
|
||||||
// UI and Navigation
|
|
||||||
virtual void createMenus() = 0;
|
|
||||||
[[nodiscard]] virtual QString getTabText() const override = 0;
|
|
||||||
bool confirmClose();
|
|
||||||
virtual void retranslateUi() override = 0;
|
|
||||||
|
|
||||||
// Deck Management
|
|
||||||
void openDeck(DeckLoader *deck);
|
|
||||||
DeckLoader *getDeckList() const;
|
|
||||||
void setModified(bool _windowModified);
|
|
||||||
|
|
||||||
// UI Elements
|
|
||||||
DeckEditorMenu *deckMenu;
|
|
||||||
DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget;
|
|
||||||
DeckEditorCardInfoDockWidget *cardInfoDockWidget;
|
|
||||||
DeckEditorDeckDockWidget *deckDockWidget;
|
|
||||||
DeckEditorFilterDockWidget *filterDockWidget;
|
|
||||||
DeckEditorPrintingSelectorDockWidget *printingSelectorDockWidget;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
virtual void onDeckChanged();
|
|
||||||
void updateCard(CardInfoPtr _card);
|
|
||||||
void actAddCard(CardInfoPtr info);
|
|
||||||
void actAddCardToSideboard(CardInfoPtr info);
|
|
||||||
void actDecrementCard(CardInfoPtr info);
|
|
||||||
void actDecrementCardFromSideboard(CardInfoPtr info);
|
|
||||||
void actOpenRecent(const QString &fileName);
|
|
||||||
void filterTreeChanged(FilterTree *filterTree);
|
|
||||||
void closeRequest(bool forced = false) override;
|
|
||||||
virtual void showPrintingSelector() = 0;
|
|
||||||
virtual void dockTopLevelChanged(bool topLevel) = 0;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void openDeckEditor(const DeckLoader *deckLoader);
|
|
||||||
void deckEditorClosing(AbstractTabDeckEditor *tab);
|
|
||||||
void decrementCard(CardInfoPtr card, QString zoneName);
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
// Deck Operations
|
|
||||||
virtual void actNewDeck();
|
|
||||||
void cleanDeckAndResetModified();
|
|
||||||
virtual void actLoadDeck();
|
|
||||||
bool actSaveDeck();
|
|
||||||
virtual bool actSaveDeckAs();
|
|
||||||
virtual void actLoadDeckFromClipboard();
|
|
||||||
void actEditDeckInClipboard();
|
|
||||||
void actEditDeckInClipboardRaw();
|
|
||||||
void actSaveDeckToClipboard();
|
|
||||||
void actSaveDeckToClipboardNoSetInfo();
|
|
||||||
void actSaveDeckToClipboardRaw();
|
|
||||||
void actSaveDeckToClipboardRawNoSetInfo();
|
|
||||||
void actPrintDeck();
|
|
||||||
void actExportDeckDecklist();
|
|
||||||
void actExportDeckDecklistXyz();
|
|
||||||
void actAnalyzeDeckDeckstats();
|
|
||||||
void actAnalyzeDeckTappedout();
|
|
||||||
|
|
||||||
// Remote Save
|
|
||||||
void saveDeckRemoteFinished(const Response &r);
|
|
||||||
|
|
||||||
// UI Layout Management
|
|
||||||
virtual void loadLayout() = 0;
|
|
||||||
virtual void restartLayout() = 0;
|
|
||||||
virtual void freeDocksSize() = 0;
|
|
||||||
virtual void refreshShortcuts() = 0;
|
|
||||||
|
|
||||||
bool eventFilter(QObject *o, QEvent *e) override;
|
|
||||||
virtual void dockVisibleTriggered() = 0;
|
|
||||||
virtual void dockFloatingTriggered() = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual void setDeck(DeckLoader *_deck);
|
|
||||||
void editDeckInClipboard(bool annotated);
|
|
||||||
void exportToDecklistWebsite(DeckLoader::DecklistWebsite website);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* @brief Enum for selecting deck open location
|
|
||||||
*/
|
|
||||||
enum DeckOpenLocation
|
|
||||||
{
|
|
||||||
CANCELLED,
|
|
||||||
SAME_TAB,
|
|
||||||
NEW_TAB
|
|
||||||
};
|
|
||||||
|
|
||||||
DeckOpenLocation confirmOpen(bool openInSameTabIfBlank = true);
|
|
||||||
QMessageBox *createSaveConfirmationWindow();
|
|
||||||
bool isBlankNewDeck() const;
|
|
||||||
|
|
||||||
// Helper functions for card actions
|
|
||||||
void addCardHelper(CardInfoPtr info, QString zoneName);
|
|
||||||
void actSwapCard(CardInfoPtr info, QString zoneName);
|
|
||||||
virtual void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation);
|
|
||||||
|
|
||||||
// UI Menu Elements
|
|
||||||
QMenu *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *printingSelectorDockMenu;
|
|
||||||
|
|
||||||
QAction *aResetLayout;
|
|
||||||
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating;
|
|
||||||
QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating;
|
|
||||||
|
|
||||||
bool modified = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // TAB_GENERIC_DECK_EDITOR_H
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#ifndef WINDOW_DECKEDITOR_H
|
|
||||||
#define WINDOW_DECKEDITOR_H
|
|
||||||
|
|
||||||
#include "../../game/cards/card_info.h"
|
|
||||||
#include "../game_logic/key_signals.h"
|
|
||||||
#include "../ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
|
|
||||||
#include "abstract_tab_deck_editor.h"
|
|
||||||
|
|
||||||
class CardDatabaseModel;
|
|
||||||
class CardDatabaseDisplayModel;
|
|
||||||
class DeckListModel;
|
|
||||||
|
|
||||||
class QLabel;
|
|
||||||
class DeckLoader;
|
|
||||||
|
|
||||||
class TabDeckEditor : public AbstractTabDeckEditor
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void loadLayout() override;
|
|
||||||
void restartLayout() override;
|
|
||||||
void freeDocksSize() override;
|
|
||||||
void refreshShortcuts() override;
|
|
||||||
|
|
||||||
bool eventFilter(QObject *o, QEvent *e) override;
|
|
||||||
void dockVisibleTriggered() override;
|
|
||||||
void dockFloatingTriggered() override;
|
|
||||||
void dockTopLevelChanged(bool topLevel) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit TabDeckEditor(TabSupervisor *_tabSupervisor);
|
|
||||||
void retranslateUi() override;
|
|
||||||
QString getTabText() const override;
|
|
||||||
void createMenus() override;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void showPrintingSelector() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,274 +0,0 @@
|
|||||||
#ifndef TAB_GAME_H
|
|
||||||
#define TAB_GAME_H
|
|
||||||
|
|
||||||
#include "../../client/tearoff_menu.h"
|
|
||||||
#include "../../game/player/player.h"
|
|
||||||
#include "../ui/widgets/visual_deck_storage/visual_deck_storage_widget.h"
|
|
||||||
#include "pb/event_leave.pb.h"
|
|
||||||
#include "pb/serverinfo_game.pb.h"
|
|
||||||
#include "tab.h"
|
|
||||||
|
|
||||||
#include <QCompleter>
|
|
||||||
#include <QLoggingCategory>
|
|
||||||
#include <QMap>
|
|
||||||
|
|
||||||
inline Q_LOGGING_CATEGORY(TabGameLog, "tab_game");
|
|
||||||
|
|
||||||
class UserListProxy;
|
|
||||||
class DeckViewContainer;
|
|
||||||
class AbstractClient;
|
|
||||||
class CardDatabase;
|
|
||||||
class GameView;
|
|
||||||
class GameScene;
|
|
||||||
class CardInfoFrameWidget;
|
|
||||||
class MessageLogWidget;
|
|
||||||
class QTimer;
|
|
||||||
class QSplitter;
|
|
||||||
class QLabel;
|
|
||||||
class QToolButton;
|
|
||||||
class QMenu;
|
|
||||||
class ZoneViewLayout;
|
|
||||||
class ZoneViewWidget;
|
|
||||||
class PhasesToolbar;
|
|
||||||
class PlayerListWidget;
|
|
||||||
class ReplayTimelineWidget;
|
|
||||||
class Response;
|
|
||||||
class GameEventContainer;
|
|
||||||
class GameEventContext;
|
|
||||||
class GameCommand;
|
|
||||||
class CommandContainer;
|
|
||||||
class Event_GameJoined;
|
|
||||||
class Event_GameStateChanged;
|
|
||||||
class Event_PlayerPropertiesChanged;
|
|
||||||
class Event_Join;
|
|
||||||
class Event_Leave;
|
|
||||||
class Event_GameHostChanged;
|
|
||||||
class Event_GameClosed;
|
|
||||||
class Event_GameStart;
|
|
||||||
class Event_SetActivePlayer;
|
|
||||||
class Event_SetActivePhase;
|
|
||||||
class Event_Ping;
|
|
||||||
class Event_GameSay;
|
|
||||||
class Event_Kicked;
|
|
||||||
class Event_ReverseTurn;
|
|
||||||
class CardZone;
|
|
||||||
class AbstractCardItem;
|
|
||||||
class CardItem;
|
|
||||||
class DeckLoader;
|
|
||||||
class QVBoxLayout;
|
|
||||||
class QHBoxLayout;
|
|
||||||
class GameReplay;
|
|
||||||
class ServerInfo_User;
|
|
||||||
class PendingCommand;
|
|
||||||
class LineEditCompleter;
|
|
||||||
class QDockWidget;
|
|
||||||
class QStackedWidget;
|
|
||||||
|
|
||||||
class TabGame : public Tab
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
private:
|
|
||||||
QTimer *gameTimer;
|
|
||||||
int secondsElapsed;
|
|
||||||
const UserListProxy *userListProxy;
|
|
||||||
QList<AbstractClient *> clients;
|
|
||||||
ServerInfo_Game gameInfo;
|
|
||||||
QMap<int, QString> roomGameTypes;
|
|
||||||
int hostId;
|
|
||||||
int localPlayerId;
|
|
||||||
const bool isLocalGame;
|
|
||||||
bool spectator;
|
|
||||||
bool judge;
|
|
||||||
QMap<int, Player *> players;
|
|
||||||
QMap<int, ServerInfo_User> spectators;
|
|
||||||
bool gameStateKnown;
|
|
||||||
bool resuming;
|
|
||||||
QStringList phasesList;
|
|
||||||
int currentPhase;
|
|
||||||
int activePlayer;
|
|
||||||
CardItem *activeCard;
|
|
||||||
bool gameClosed;
|
|
||||||
QStringList gameTypes;
|
|
||||||
QCompleter *completer;
|
|
||||||
QStringList autocompleteUserList;
|
|
||||||
QStackedWidget *mainWidget;
|
|
||||||
|
|
||||||
// Replay related members
|
|
||||||
GameReplay *replay;
|
|
||||||
int currentReplayStep;
|
|
||||||
QList<int> replayTimeline;
|
|
||||||
ReplayTimelineWidget *timelineWidget;
|
|
||||||
QToolButton *replayPlayButton, *replayFastForwardButton;
|
|
||||||
QAction *aReplaySkipForward, *aReplaySkipBackward, *aReplaySkipForwardBig, *aReplaySkipBackwardBig;
|
|
||||||
|
|
||||||
CardInfoFrameWidget *cardInfoFrameWidget;
|
|
||||||
PlayerListWidget *playerListWidget;
|
|
||||||
QLabel *timeElapsedLabel;
|
|
||||||
MessageLogWidget *messageLog;
|
|
||||||
QLabel *sayLabel;
|
|
||||||
LineEditCompleter *sayEdit;
|
|
||||||
PhasesToolbar *phasesToolbar;
|
|
||||||
GameScene *scene;
|
|
||||||
GameView *gameView;
|
|
||||||
QMap<int, DeckViewContainer *> deckViewContainers;
|
|
||||||
QVBoxLayout *cardVInfoLayout, *messageLogLayout, *gamePlayAreaVBox, *deckViewContainerLayout;
|
|
||||||
QHBoxLayout *cardHInfoLayout, *sayHLayout, *mainHLayout, *replayControlLayout;
|
|
||||||
QWidget *cardBoxLayoutWidget, *messageLogLayoutWidget, *gamePlayAreaWidget, *deckViewContainerWidget,
|
|
||||||
*replayControlWidget;
|
|
||||||
QDockWidget *cardInfoDock, *messageLayoutDock, *playerListDock, *replayDock;
|
|
||||||
QAction *playersSeparator;
|
|
||||||
QMenu *gameMenu, *viewMenu, *cardInfoDockMenu, *messageLayoutDockMenu, *playerListDockMenu, *replayDockMenu;
|
|
||||||
TearOffMenu *phasesMenu;
|
|
||||||
QAction *aGameInfo, *aConcede, *aLeaveGame, *aCloseReplay, *aNextPhase, *aNextPhaseAction, *aNextTurn,
|
|
||||||
*aReverseTurn, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, *aResetReplayLayout;
|
|
||||||
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aMessageLayoutDockVisible, *aMessageLayoutDockFloating,
|
|
||||||
*aPlayerListDockVisible, *aPlayerListDockFloating, *aReplayDockVisible, *aReplayDockFloating;
|
|
||||||
QAction *aFocusChat;
|
|
||||||
QList<QAction *> phaseActions;
|
|
||||||
|
|
||||||
Player *addPlayer(int playerId, const ServerInfo_User &info);
|
|
||||||
|
|
||||||
bool isMainPlayerConceded() const;
|
|
||||||
|
|
||||||
void startGame(bool resuming);
|
|
||||||
void stopGame();
|
|
||||||
void closeGame();
|
|
||||||
bool leaveGame();
|
|
||||||
|
|
||||||
void eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
void eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
|
|
||||||
void eventGameStateChanged(const Event_GameStateChanged &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
void eventPlayerPropertiesChanged(const Event_PlayerPropertiesChanged &event,
|
|
||||||
int eventPlayerId,
|
|
||||||
const GameEventContext &context);
|
|
||||||
void eventJoin(const Event_Join &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
void eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
void eventKicked(const Event_Kicked &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
void eventGameHostChanged(const Event_GameHostChanged &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
void eventGameClosed(const Event_GameClosed &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
Player *setActivePlayer(int id);
|
|
||||||
void eventSetActivePlayer(const Event_SetActivePlayer &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
void setActivePhase(int phase);
|
|
||||||
void eventSetActivePhase(const Event_SetActivePhase &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
void eventPing(const Event_Ping &event, int eventPlayerId, const GameEventContext &context);
|
|
||||||
void eventReverseTurn(const Event_ReverseTurn &event, int eventPlayerId, const GameEventContext & /*context*/);
|
|
||||||
void emitUserEvent();
|
|
||||||
void createMenuItems();
|
|
||||||
void createReplayMenuItems();
|
|
||||||
void createViewMenuItems();
|
|
||||||
void createCardInfoDock(bool bReplay = false);
|
|
||||||
void createPlayerListDock(bool bReplay = false);
|
|
||||||
void createMessageDock(bool bReplay = false);
|
|
||||||
void createPlayAreaWidget(bool bReplay = false);
|
|
||||||
void createDeckViewContainerWidget(bool bReplay = false);
|
|
||||||
void createReplayDock();
|
|
||||||
QString getLeaveReason(Event_Leave::LeaveReason reason);
|
|
||||||
signals:
|
|
||||||
void gameClosing(TabGame *tab);
|
|
||||||
void playerAdded(Player *player);
|
|
||||||
void playerRemoved(Player *player);
|
|
||||||
void containerProcessingStarted(const GameEventContext &context);
|
|
||||||
void containerProcessingDone();
|
|
||||||
void openMessageDialog(const QString &userName, bool focus);
|
|
||||||
void openDeckEditor(const DeckLoader *deck);
|
|
||||||
void notIdle();
|
|
||||||
private slots:
|
|
||||||
void replayNextEvent(Player::EventProcessingOptions options);
|
|
||||||
void replayFinished();
|
|
||||||
void replayPlayButtonToggled(bool checked);
|
|
||||||
void replayFastForwardButtonToggled(bool checked);
|
|
||||||
void replayRewind();
|
|
||||||
|
|
||||||
void incrementGameTime();
|
|
||||||
void adminLockChanged(bool lock);
|
|
||||||
void newCardAdded(AbstractCardItem *card);
|
|
||||||
void updateCardMenu(AbstractCardItem *card);
|
|
||||||
|
|
||||||
void actGameInfo();
|
|
||||||
void actConcede();
|
|
||||||
void actRemoveLocalArrows();
|
|
||||||
void actRotateViewCW();
|
|
||||||
void actRotateViewCCW();
|
|
||||||
void actSay();
|
|
||||||
void actPhaseAction();
|
|
||||||
void actNextPhase();
|
|
||||||
void actNextPhaseAction();
|
|
||||||
void actNextTurn();
|
|
||||||
void actReverseTurn();
|
|
||||||
|
|
||||||
void addMentionTag(const QString &value);
|
|
||||||
void linkCardToChat(const QString &cardName);
|
|
||||||
void commandFinished(const Response &response);
|
|
||||||
|
|
||||||
void refreshShortcuts();
|
|
||||||
|
|
||||||
void loadLayout();
|
|
||||||
void actCompleterChanged();
|
|
||||||
void actResetLayout();
|
|
||||||
void freeDocksSize();
|
|
||||||
|
|
||||||
void hideEvent(QHideEvent *event) override;
|
|
||||||
bool eventFilter(QObject *o, QEvent *e) override;
|
|
||||||
void dockVisibleTriggered();
|
|
||||||
void dockFloatingTriggered();
|
|
||||||
void dockTopLevelChanged(bool topLevel);
|
|
||||||
|
|
||||||
public:
|
|
||||||
TabGame(TabSupervisor *_tabSupervisor,
|
|
||||||
QList<AbstractClient *> &_clients,
|
|
||||||
const Event_GameJoined &event,
|
|
||||||
const QMap<int, QString> &_roomGameTypes);
|
|
||||||
TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay);
|
|
||||||
~TabGame() override;
|
|
||||||
void retranslateUi() override;
|
|
||||||
void updatePlayerListDockTitle();
|
|
||||||
void closeRequest(bool forced = false) override;
|
|
||||||
const QMap<int, Player *> &getPlayers() const
|
|
||||||
{
|
|
||||||
return players;
|
|
||||||
}
|
|
||||||
CardItem *getCard(int playerId, const QString &zoneName, int cardId) const;
|
|
||||||
bool isHost() const
|
|
||||||
{
|
|
||||||
return hostId == localPlayerId;
|
|
||||||
}
|
|
||||||
bool getIsLocalGame() const
|
|
||||||
{
|
|
||||||
return isLocalGame;
|
|
||||||
}
|
|
||||||
int getGameId() const
|
|
||||||
{
|
|
||||||
return gameInfo.game_id();
|
|
||||||
}
|
|
||||||
QString getTabText() const override;
|
|
||||||
bool getSpectator() const
|
|
||||||
{
|
|
||||||
return spectator;
|
|
||||||
}
|
|
||||||
bool getSpectatorsSeeEverything() const
|
|
||||||
{
|
|
||||||
return gameInfo.spectators_omniscient();
|
|
||||||
}
|
|
||||||
bool isSpectator();
|
|
||||||
Player *getActiveLocalPlayer() const;
|
|
||||||
AbstractClient *getClientForPlayer(int playerId) const;
|
|
||||||
|
|
||||||
void setActiveCard(CardItem *card);
|
|
||||||
CardItem *getActiveCard() const
|
|
||||||
{
|
|
||||||
return activeCard;
|
|
||||||
}
|
|
||||||
|
|
||||||
void processGameEventContainer(const GameEventContainer &cont,
|
|
||||||
AbstractClient *client,
|
|
||||||
Player::EventProcessingOptions options);
|
|
||||||
PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd);
|
|
||||||
PendingCommand *prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList);
|
|
||||||
public slots:
|
|
||||||
void sendGameCommand(PendingCommand *pend, int playerId = -1);
|
|
||||||
void sendGameCommand(const ::google::protobuf::Message &command, int playerId = -1);
|
|
||||||
void viewCardInfo(const QString &cardName, const QString &providerId = "") const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#ifndef TAB_VISUAL_DATABASE_DISPLAY_H
|
|
||||||
#define TAB_VISUAL_DATABASE_DISPLAY_H
|
|
||||||
|
|
||||||
#include "../ui/widgets/visual_database_display/visual_database_display_widget.h"
|
|
||||||
#include "tab.h"
|
|
||||||
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
class TabVisualDatabaseDisplay : public Tab
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private:
|
|
||||||
TabDeckEditor *deckEditor;
|
|
||||||
VisualDatabaseDisplayWidget *visualDatabaseDisplayWidget;
|
|
||||||
|
|
||||||
public:
|
|
||||||
TabVisualDatabaseDisplay(TabSupervisor *_tabSupervisor);
|
|
||||||
void retranslateUi() override;
|
|
||||||
QString getTabText() const override
|
|
||||||
{
|
|
||||||
return tr("Visual Database Display");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // TAB_VISUAL_DATABASE_DISPLAY_H
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#ifndef WINDOW_DECKEDITORVISUAL_H
|
|
||||||
#define WINDOW_DECKEDITORVISUAL_H
|
|
||||||
|
|
||||||
#include "../tab.h"
|
|
||||||
#include "tab_deck_editor_visual_tab_widget.h"
|
|
||||||
|
|
||||||
class TabDeckEditorVisual : public AbstractTabDeckEditor
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
protected slots:
|
|
||||||
void loadLayout() override;
|
|
||||||
void restartLayout() override;
|
|
||||||
void freeDocksSize() override;
|
|
||||||
void refreshShortcuts() override;
|
|
||||||
|
|
||||||
bool eventFilter(QObject *o, QEvent *e) override;
|
|
||||||
void dockVisibleTriggered() override;
|
|
||||||
void dockFloatingTriggered() override;
|
|
||||||
void dockTopLevelChanged(bool topLevel) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
TabDeckEditorVisualTabWidget *tabContainer;
|
|
||||||
|
|
||||||
QVBoxLayout *centralFrame;
|
|
||||||
QVBoxLayout *searchAndDatabaseFrame;
|
|
||||||
QHBoxLayout *searchLayout;
|
|
||||||
QDockWidget *searchAndDatabaseDock;
|
|
||||||
QDockWidget *deckAnalyticsDock;
|
|
||||||
QWidget *centralWidget;
|
|
||||||
QMenu *deckAnalyticsMenu;
|
|
||||||
QAction *aDeckAnalyticsDockVisible, *aDeckAnalyticsDockFloating;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit TabDeckEditorVisual(TabSupervisor *_tabSupervisor);
|
|
||||||
void retranslateUi() override;
|
|
||||||
QString getTabText() const override;
|
|
||||||
void changeModelIndexAndCardInfo(const CardInfoPtr &activeCard);
|
|
||||||
void changeModelIndexToCard(const CardInfoPtr &activeCard);
|
|
||||||
void createDeckAnalyticsDock();
|
|
||||||
void createMenus() override;
|
|
||||||
void createSearchAndDatabaseFrame();
|
|
||||||
void createCentralFrame();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void onDeckChanged() override;
|
|
||||||
void showPrintingSelector() override;
|
|
||||||
void
|
|
||||||
processMainboardCardClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
|
|
||||||
void processCardClickDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
|
|
||||||
bool actSaveDeckAs() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
#ifndef TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
|
|
||||||
#define TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
|
|
||||||
|
|
||||||
#include "../../ui/widgets/deck_analytics/deck_analytics_widget.h"
|
|
||||||
#include "../../ui/widgets/printing_selector/printing_selector.h"
|
|
||||||
#include "../../ui/widgets/visual_database_display/visual_database_display_widget.h"
|
|
||||||
#include "../../ui/widgets/visual_deck_editor/visual_deck_editor_sample_hand_widget.h"
|
|
||||||
#include "../../ui/widgets/visual_deck_editor/visual_deck_editor_widget.h"
|
|
||||||
#include "../abstract_tab_deck_editor.h"
|
|
||||||
|
|
||||||
#include <QTabWidget>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
class TabDeckEditorVisualTabWidget : public QTabWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit TabDeckEditorVisualTabWidget(QWidget *parent,
|
|
||||||
AbstractTabDeckEditor *_deckEditor,
|
|
||||||
DeckListModel *_deckModel,
|
|
||||||
CardDatabaseModel *_cardDatabaseModel,
|
|
||||||
CardDatabaseDisplayModel *_cardDatabaseDisplayModel);
|
|
||||||
|
|
||||||
// Utility functions
|
|
||||||
void addNewTab(QWidget *widget, const QString &title);
|
|
||||||
void removeCurrentTab();
|
|
||||||
void setTabTitle(int index, const QString &title);
|
|
||||||
QWidget *getCurrentTab() const;
|
|
||||||
int getTabCount() const;
|
|
||||||
|
|
||||||
VisualDeckEditorWidget *visualDeckView;
|
|
||||||
DeckAnalyticsWidget *deckAnalytics;
|
|
||||||
VisualDatabaseDisplayWidget *visualDatabaseDisplay;
|
|
||||||
PrintingSelector *printingSelector;
|
|
||||||
VisualDeckEditorSampleHandWidget *sampleHandWidget;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void onCardChanged(CardInfoPtr activeCard);
|
|
||||||
void onCardChangedDatabaseDisplay(CardInfoPtr activeCard);
|
|
||||||
void onCardClickedDeckEditor(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
|
|
||||||
void onCardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void cardChanged(CardInfoPtr activeCard);
|
|
||||||
void cardChangedDatabaseDisplay(CardInfoPtr activeCard);
|
|
||||||
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance, QString zoneName);
|
|
||||||
void cardClickedDatabaseDisplay(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *instance);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QVBoxLayout *layout; // Layout for the tab widget and other controls
|
|
||||||
AbstractTabDeckEditor *deckEditor;
|
|
||||||
DeckListModel *deckModel;
|
|
||||||
CardDatabaseModel *cardDatabaseModel;
|
|
||||||
CardDatabaseDisplayModel *cardDatabaseDisplayModel;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void handleTabClose(int index); // Slot for closing a tab
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // TAB_DECK_EDITOR_VISUAL_TAB_WIDGET_H
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#ifndef PICTURELOADER_H
|
|
||||||
#define PICTURELOADER_H
|
|
||||||
|
|
||||||
#include "../../../game/cards/card_info.h"
|
|
||||||
#include "picture_loader_worker.h"
|
|
||||||
|
|
||||||
#include <QLoggingCategory>
|
|
||||||
|
|
||||||
inline Q_LOGGING_CATEGORY(PictureLoaderLog, "picture_loader");
|
|
||||||
inline Q_LOGGING_CATEGORY(PictureLoaderCardBackCacheFailLog, "picture_loader.card_back_cache_fail");
|
|
||||||
|
|
||||||
class PictureLoader : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
static PictureLoader &getInstance()
|
|
||||||
{
|
|
||||||
static PictureLoader instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit PictureLoader();
|
|
||||||
~PictureLoader() override;
|
|
||||||
// Singleton - Don't implement copy constructor and assign operator
|
|
||||||
PictureLoader(PictureLoader const &);
|
|
||||||
void operator=(PictureLoader const &);
|
|
||||||
|
|
||||||
PictureLoaderWorker *worker;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void getPixmap(QPixmap &pixmap, CardInfoPtr card, QSize size);
|
|
||||||
static void getCardBackPixmap(QPixmap &pixmap, QSize size);
|
|
||||||
static void getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size);
|
|
||||||
static void getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize size);
|
|
||||||
static void clearPixmapCache(CardInfoPtr card);
|
|
||||||
static void clearPixmapCache();
|
|
||||||
static void cacheCardPixmaps(QList<CardInfoPtr> cards);
|
|
||||||
static bool hasCustomArt();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
static void clearNetworkCache();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void picDownloadChanged();
|
|
||||||
void picsPathChanged();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void imageLoaded(CardInfoPtr card, const QImage &image);
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
@@ -1,500 +0,0 @@
|
|||||||
#include "picture_loader_worker.h"
|
|
||||||
|
|
||||||
#include "../../../game/cards/card_database_manager.h"
|
|
||||||
#include "../../../settings/cache_settings.h"
|
|
||||||
|
|
||||||
#include <QBuffer>
|
|
||||||
#include <QDirIterator>
|
|
||||||
#include <QMovie>
|
|
||||||
#include <QNetworkDiskCache>
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
// Card back returned by gatherer when card is not found
|
|
||||||
QStringList PictureLoaderWorker::md5Blacklist = QStringList() << "db0c48db407a907c16ade38de048a441";
|
|
||||||
|
|
||||||
PictureLoaderWorker::PictureLoaderWorker()
|
|
||||||
: QObject(nullptr), picsPath(SettingsCache::instance().getPicsPath()),
|
|
||||||
customPicsPath(SettingsCache::instance().getCustomPicsPath()),
|
|
||||||
picDownload(SettingsCache::instance().getPicDownload()), downloadRunning(false), loadQueueRunning(false),
|
|
||||||
overrideAllCardArtWithPersonalPreference(SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference())
|
|
||||||
{
|
|
||||||
connect(this, &PictureLoaderWorker::startLoadQueue, this, &PictureLoaderWorker::processLoadQueue,
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
connect(&SettingsCache::instance(), &SettingsCache::picsPathChanged, this, &PictureLoaderWorker::picsPathChanged);
|
|
||||||
connect(&SettingsCache::instance(), &SettingsCache::picDownloadChanged, this,
|
|
||||||
&PictureLoaderWorker::picDownloadChanged);
|
|
||||||
connect(&SettingsCache::instance(), &SettingsCache::overrideAllCardArtWithPersonalPreferenceChanged, this,
|
|
||||||
&PictureLoaderWorker::setOverrideAllCardArtWithPersonalPreference);
|
|
||||||
|
|
||||||
networkManager = new QNetworkAccessManager(this);
|
|
||||||
// We need a timeout to ensure requests don't hang indefinitely in case of
|
|
||||||
// cache corruption, see related Qt bug: https://bugreports.qt.io/browse/QTBUG-111397
|
|
||||||
// Use Qt's default timeout (30s, as of 2023-02-22)
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
|
||||||
networkManager->setTransferTimeout();
|
|
||||||
#endif
|
|
||||||
auto cache = new QNetworkDiskCache(this);
|
|
||||||
cache->setCacheDirectory(SettingsCache::instance().getNetworkCachePath());
|
|
||||||
cache->setMaximumCacheSize(1024L * 1024L *
|
|
||||||
static_cast<qint64>(SettingsCache::instance().getNetworkCacheSizeInMB()));
|
|
||||||
// Note: the settings is in MB, but QNetworkDiskCache uses bytes
|
|
||||||
connect(&SettingsCache::instance(), &SettingsCache::networkCacheSizeChanged, cache,
|
|
||||||
[cache](int newSizeInMB) { cache->setMaximumCacheSize(1024L * 1024L * static_cast<qint64>(newSizeInMB)); });
|
|
||||||
networkManager->setCache(cache);
|
|
||||||
// Use a ManualRedirectPolicy since we keep track of redirects in picDownloadFinished
|
|
||||||
// We can't use NoLessSafeRedirectPolicy because it is not applied with AlwaysCache
|
|
||||||
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
|
|
||||||
connect(networkManager, &QNetworkAccessManager::finished, this, &PictureLoaderWorker::picDownloadFinished);
|
|
||||||
|
|
||||||
cacheFilePath = SettingsCache::instance().getRedirectCachePath() + REDIRECT_CACHE_FILENAME;
|
|
||||||
loadRedirectCache();
|
|
||||||
cleanStaleEntries();
|
|
||||||
|
|
||||||
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this,
|
|
||||||
&PictureLoaderWorker::saveRedirectCache);
|
|
||||||
|
|
||||||
pictureLoaderThread = new QThread;
|
|
||||||
pictureLoaderThread->start(QThread::LowPriority);
|
|
||||||
moveToThread(pictureLoaderThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
PictureLoaderWorker::~PictureLoaderWorker()
|
|
||||||
{
|
|
||||||
pictureLoaderThread->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::processLoadQueue()
|
|
||||||
{
|
|
||||||
if (loadQueueRunning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadQueueRunning = true;
|
|
||||||
while (true) {
|
|
||||||
mutex.lock();
|
|
||||||
if (loadQueue.isEmpty()) {
|
|
||||||
mutex.unlock();
|
|
||||||
loadQueueRunning = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cardBeingLoaded = loadQueue.takeFirst();
|
|
||||||
mutex.unlock();
|
|
||||||
|
|
||||||
QString setName = cardBeingLoaded.getSetName();
|
|
||||||
QString cardName = cardBeingLoaded.getCard()->getName();
|
|
||||||
QString correctedCardName = cardBeingLoaded.getCard()->getCorrectedName();
|
|
||||||
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardName << " set: " << setName << "]: Trying to load picture";
|
|
||||||
|
|
||||||
// FIXME: This is a hack so that to keep old Cockatrice behavior
|
|
||||||
// (ignoring provider ID) when the "override all card art with personal
|
|
||||||
// preference" is set.
|
|
||||||
//
|
|
||||||
// Figure out a proper way to integrate the two systems at some point.
|
|
||||||
//
|
|
||||||
// Note: need to go through a member for
|
|
||||||
// overrideAllCardArtWithPersonalPreference as reading from the
|
|
||||||
// SettingsCache instance from the PictureLoaderWorker thread could
|
|
||||||
// cause race conditions.
|
|
||||||
//
|
|
||||||
// XXX: Reading from the CardDatabaseManager instance from the
|
|
||||||
// PictureLoaderWorker thread might not be safe either
|
|
||||||
bool searchCustomPics = overrideAllCardArtWithPersonalPreference ||
|
|
||||||
CardDatabaseManager::getInstance()->isProviderIdForPreferredPrinting(
|
|
||||||
cardName, cardBeingLoaded.getCard()->getPixmapCacheKey());
|
|
||||||
if (searchCustomPics && cardImageExistsOnDisk(setName, correctedCardName, searchCustomPics)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardName << " set: " << setName << "]: No custom picture, trying to download";
|
|
||||||
cardsToDownload.append(cardBeingLoaded);
|
|
||||||
cardBeingLoaded.clear();
|
|
||||||
if (!downloadRunning) {
|
|
||||||
startNextPicDownload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PictureLoaderWorker::cardImageExistsOnDisk(QString &setName, QString &correctedCardname, bool searchCustomPics)
|
|
||||||
{
|
|
||||||
QImage image;
|
|
||||||
QImageReader imgReader;
|
|
||||||
imgReader.setDecideFormatFromContent(true);
|
|
||||||
QList<QString> picsPaths = QList<QString>();
|
|
||||||
|
|
||||||
if (searchCustomPics) {
|
|
||||||
QDirIterator it(customPicsPath, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
||||||
|
|
||||||
// Recursively check all subdirectories of the CUSTOM folder
|
|
||||||
while (it.hasNext()) {
|
|
||||||
QString thisPath(it.next());
|
|
||||||
QFileInfo thisFileInfo(thisPath);
|
|
||||||
|
|
||||||
if (thisFileInfo.isFile() &&
|
|
||||||
(thisFileInfo.fileName() == correctedCardname || thisFileInfo.completeBaseName() == correctedCardname ||
|
|
||||||
thisFileInfo.baseName() == correctedCardname)) {
|
|
||||||
picsPaths << thisPath; // Card found in the CUSTOM directory, somewhere
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!setName.isEmpty()) {
|
|
||||||
picsPaths << picsPath + "/" + setName + "/" + correctedCardname
|
|
||||||
// We no longer store downloaded images there, but don't just ignore
|
|
||||||
// stuff that old versions have put there.
|
|
||||||
<< picsPath + "/downloadedPics/" + setName + "/" + correctedCardname;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterates through the list of paths, searching for images with the desired
|
|
||||||
// name with any QImageReader-supported
|
|
||||||
// extension
|
|
||||||
for (const auto &_picsPath : picsPaths) {
|
|
||||||
imgReader.setFileName(_picsPath);
|
|
||||||
if (imgReader.read(&image)) {
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << correctedCardname << " set: " << setName << "]: Picture found on disk.";
|
|
||||||
imageLoaded(cardBeingLoaded.getCard(), image);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
imgReader.setFileName(_picsPath + ".full");
|
|
||||||
if (imgReader.read(&image)) {
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << correctedCardname << " set: " << setName << "]: Picture.full found on disk.";
|
|
||||||
imageLoaded(cardBeingLoaded.getCard(), image);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
imgReader.setFileName(_picsPath + ".xlhq");
|
|
||||||
if (imgReader.read(&image)) {
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << correctedCardname << " set: " << setName << "]: Picture.xlhq found on disk.";
|
|
||||||
imageLoaded(cardBeingLoaded.getCard(), image);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::startNextPicDownload()
|
|
||||||
{
|
|
||||||
if (cardsToDownload.isEmpty()) {
|
|
||||||
cardBeingDownloaded.clear();
|
|
||||||
downloadRunning = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadRunning = true;
|
|
||||||
|
|
||||||
cardBeingDownloaded = cardsToDownload.takeFirst();
|
|
||||||
|
|
||||||
QString picUrl = cardBeingDownloaded.getCurrentUrl();
|
|
||||||
|
|
||||||
if (picUrl.isEmpty()) {
|
|
||||||
downloadRunning = false;
|
|
||||||
picDownloadFailed();
|
|
||||||
} else {
|
|
||||||
QUrl url(picUrl);
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace() << "[card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
|
||||||
<< " set: " << cardBeingDownloaded.getSetName()
|
|
||||||
<< "]: Trying to fetch picture from url " << url.toDisplayString();
|
|
||||||
makeRequest(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::picDownloadFailed()
|
|
||||||
{
|
|
||||||
/* Take advantage of short circuiting here to call the nextUrl until one
|
|
||||||
is not available. Only once nextUrl evaluates to false will this move
|
|
||||||
on to nextSet. If the Urls for a particular card are empty, this will
|
|
||||||
effectively go through the sets for that card. */
|
|
||||||
if (cardBeingDownloaded.nextUrl() || cardBeingDownloaded.nextSet()) {
|
|
||||||
mutex.lock();
|
|
||||||
loadQueue.prepend(cardBeingDownloaded);
|
|
||||||
mutex.unlock();
|
|
||||||
} else {
|
|
||||||
qCWarning(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: Picture NOT found, "
|
|
||||||
<< (picDownload ? "download failed" : "downloads disabled")
|
|
||||||
<< ", no more url combinations to try: BAILING OUT";
|
|
||||||
imageLoaded(cardBeingDownloaded.getCard(), QImage());
|
|
||||||
cardBeingDownloaded.clear();
|
|
||||||
}
|
|
||||||
emit startLoadQueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PictureLoaderWorker::imageIsBlackListed(const QByteArray &picData)
|
|
||||||
{
|
|
||||||
QString md5sum = QCryptographicHash::hash(picData, QCryptographicHash::Md5).toHex();
|
|
||||||
return md5Blacklist.contains(md5sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply *PictureLoaderWorker::makeRequest(const QUrl &url)
|
|
||||||
{
|
|
||||||
// Check if the redirect is cached
|
|
||||||
QUrl cachedRedirect = getCachedRedirect(url);
|
|
||||||
if (!cachedRedirect.isEmpty()) {
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: Using cached redirect for " << url.toDisplayString()
|
|
||||||
<< " to " << cachedRedirect.toDisplayString();
|
|
||||||
return makeRequest(cachedRedirect); // Use the cached redirect
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkRequest req(url);
|
|
||||||
|
|
||||||
// QNetworkDiskCache leaks file descriptors when downloading compressed
|
|
||||||
// files, see https://bugreports.qt.io/browse/QTBUG-135641
|
|
||||||
//
|
|
||||||
// We can set the Accept-Encoding header manually to disable Qt's automatic
|
|
||||||
// response decompression, but then we would have to deal with decompression
|
|
||||||
// ourselves.
|
|
||||||
//
|
|
||||||
// Since we are dowloading images that are usually stored in a
|
|
||||||
// compressed format (e.g. jpeg or webp), it's not clear compression at the
|
|
||||||
// HTTP level helps; in fact, images are typically returned without
|
|
||||||
// compression. Redirects, on the other hand, are compressed and cause file
|
|
||||||
// descriptor leaks -- but since redirects have no payload, we don't really
|
|
||||||
// care either.
|
|
||||||
//
|
|
||||||
// In the end, just do the simple thing and disable HTTP compression.
|
|
||||||
req.setRawHeader("accept-encoding", "identity");
|
|
||||||
|
|
||||||
if (!picDownload) {
|
|
||||||
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply *reply = networkManager->get(req);
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply, url]() {
|
|
||||||
QVariant redirectTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
|
||||||
|
|
||||||
if (redirectTarget.isValid()) {
|
|
||||||
QUrl redirectUrl = redirectTarget.toUrl();
|
|
||||||
if (redirectUrl.isRelative()) {
|
|
||||||
redirectUrl = url.resolved(redirectUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheRedirect(url, redirectUrl);
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: Caching redirect from " << url.toDisplayString()
|
|
||||||
<< " to " << redirectUrl.toDisplayString();
|
|
||||||
}
|
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
});
|
|
||||||
|
|
||||||
return reply;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl)
|
|
||||||
{
|
|
||||||
redirectCache[originalUrl] = qMakePair(redirectUrl, QDateTime::currentDateTimeUtc());
|
|
||||||
saveRedirectCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl PictureLoaderWorker::getCachedRedirect(const QUrl &originalUrl) const
|
|
||||||
{
|
|
||||||
if (redirectCache.contains(originalUrl)) {
|
|
||||||
return redirectCache[originalUrl].first;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::loadRedirectCache()
|
|
||||||
{
|
|
||||||
QSettings settings(cacheFilePath, QSettings::IniFormat);
|
|
||||||
|
|
||||||
redirectCache.clear();
|
|
||||||
int size = settings.beginReadArray(REDIRECT_HEADER_NAME);
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
settings.setArrayIndex(i);
|
|
||||||
QUrl originalUrl = settings.value(REDIRECT_ORIGINAL_URL).toUrl();
|
|
||||||
QUrl redirectUrl = settings.value(REDIRECT_URL).toUrl();
|
|
||||||
QDateTime timestamp = settings.value(REDIRECT_TIMESTAMP).toDateTime();
|
|
||||||
|
|
||||||
if (originalUrl.isValid() && redirectUrl.isValid()) {
|
|
||||||
redirectCache[originalUrl] = qMakePair(redirectUrl, timestamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
settings.endArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::saveRedirectCache() const
|
|
||||||
{
|
|
||||||
QSettings settings(cacheFilePath, QSettings::IniFormat);
|
|
||||||
|
|
||||||
settings.beginWriteArray(REDIRECT_HEADER_NAME, static_cast<int>(redirectCache.size()));
|
|
||||||
int index = 0;
|
|
||||||
for (auto it = redirectCache.cbegin(); it != redirectCache.cend(); ++it) {
|
|
||||||
settings.setArrayIndex(index++);
|
|
||||||
settings.setValue(REDIRECT_ORIGINAL_URL, it.key());
|
|
||||||
settings.setValue(REDIRECT_URL, it.value().first);
|
|
||||||
settings.setValue(REDIRECT_TIMESTAMP, it.value().second);
|
|
||||||
}
|
|
||||||
settings.endArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::cleanStaleEntries()
|
|
||||||
{
|
|
||||||
QDateTime now = QDateTime::currentDateTimeUtc();
|
|
||||||
|
|
||||||
auto it = redirectCache.begin();
|
|
||||||
while (it != redirectCache.end()) {
|
|
||||||
if (it.value().second.addDays(SettingsCache::instance().getRedirectCacheTtl()) < now) {
|
|
||||||
it = redirectCache.erase(it); // Remove stale entry
|
|
||||||
} else {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
|
|
||||||
{
|
|
||||||
bool isFromCache = reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
|
|
||||||
|
|
||||||
if (reply->error()) {
|
|
||||||
if (isFromCache) {
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardBeingDownloaded.getCard()->getName() << " set: " << cardBeingDownloaded.getSetName()
|
|
||||||
<< "]: Removing corrupted cache file for url " << reply->url().toDisplayString() << " and retrying ("
|
|
||||||
<< reply->errorString() << ")";
|
|
||||||
|
|
||||||
networkManager->cache()->remove(reply->url());
|
|
||||||
|
|
||||||
makeRequest(reply->url());
|
|
||||||
} else {
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardBeingDownloaded.getCard()->getName() << " set: " << cardBeingDownloaded.getSetName()
|
|
||||||
<< "]: " << (picDownload ? "Download" : "Cache search") << " failed for url "
|
|
||||||
<< reply->url().toDisplayString() << " (" << reply->errorString() << ")";
|
|
||||||
|
|
||||||
picDownloadFailed();
|
|
||||||
startNextPicDownload();
|
|
||||||
}
|
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// List of status codes from https://doc.qt.io/qt-6/qnetworkreply.html#redirected
|
|
||||||
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
||||||
if (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305 || statusCode == 307 ||
|
|
||||||
statusCode == 308) {
|
|
||||||
QUrl redirectUrl = reply->header(QNetworkRequest::LocationHeader).toUrl();
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardBeingDownloaded.getCard()->getName() << " set: " << cardBeingDownloaded.getSetName()
|
|
||||||
<< "]: following " << (isFromCache ? "cached redirect" : "redirect") << " to "
|
|
||||||
<< redirectUrl.toDisplayString();
|
|
||||||
makeRequest(redirectUrl);
|
|
||||||
reply->deleteLater();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// peek is used to keep the data in the buffer for use by QImageReader
|
|
||||||
const QByteArray &picData = reply->peek(reply->size());
|
|
||||||
|
|
||||||
if (imageIsBlackListed(picData)) {
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardBeingDownloaded.getCard()->getName() << " set: " << cardBeingDownloaded.getSetName()
|
|
||||||
<< "]: Picture found, but blacklisted, will consider it as not found";
|
|
||||||
|
|
||||||
picDownloadFailed();
|
|
||||||
reply->deleteLater();
|
|
||||||
startNextPicDownload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage testImage;
|
|
||||||
|
|
||||||
QImageReader imgReader;
|
|
||||||
imgReader.setDecideFormatFromContent(true);
|
|
||||||
imgReader.setDevice(reply);
|
|
||||||
|
|
||||||
bool logSuccessMessage = false;
|
|
||||||
|
|
||||||
static const int riffHeaderSize = 12; // RIFF_HEADER_SIZE from webp/format_constants.h
|
|
||||||
auto replyHeader = reply->peek(riffHeaderSize);
|
|
||||||
|
|
||||||
if (replyHeader.startsWith("RIFF") && replyHeader.endsWith("WEBP")) {
|
|
||||||
auto imgBuf = QBuffer(this);
|
|
||||||
imgBuf.setData(reply->readAll());
|
|
||||||
|
|
||||||
auto movie = QMovie(&imgBuf);
|
|
||||||
movie.start();
|
|
||||||
movie.stop();
|
|
||||||
|
|
||||||
imageLoaded(cardBeingDownloaded.getCard(), movie.currentImage());
|
|
||||||
logSuccessMessage = true;
|
|
||||||
} else if (imgReader.read(&testImage)) {
|
|
||||||
imageLoaded(cardBeingDownloaded.getCard(), testImage);
|
|
||||||
logSuccessMessage = true;
|
|
||||||
} else {
|
|
||||||
qCDebug(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardBeingDownloaded.getCard()->getName() << " set: " << cardBeingDownloaded.getSetName()
|
|
||||||
<< "]: Possible " << (isFromCache ? "cached" : "downloaded") << " picture at "
|
|
||||||
<< reply->url().toDisplayString() << " could not be loaded: " << reply->errorString();
|
|
||||||
|
|
||||||
picDownloadFailed();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logSuccessMessage) {
|
|
||||||
qCInfo(PictureLoaderWorkerLog).nospace()
|
|
||||||
<< "[card: " << cardBeingDownloaded.getCard()->getName() << " set: " << cardBeingDownloaded.getSetName()
|
|
||||||
<< "]: Image successfully " << (isFromCache ? "loaded from cached" : "downloaded from") << " url "
|
|
||||||
<< reply->url().toDisplayString();
|
|
||||||
}
|
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
startNextPicDownload();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::enqueueImageLoad(CardInfoPtr card)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&mutex);
|
|
||||||
|
|
||||||
// avoid queueing the same card more than once
|
|
||||||
if (!card || card == cardBeingLoaded.getCard() || card == cardBeingDownloaded.getCard()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const PictureToLoad &pic : loadQueue) {
|
|
||||||
if (pic.getCard() == card)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const PictureToLoad &pic : cardsToDownload) {
|
|
||||||
if (pic.getCard() == card)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadQueue.append(PictureToLoad(card));
|
|
||||||
emit startLoadQueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::picDownloadChanged()
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&mutex);
|
|
||||||
picDownload = SettingsCache::instance().getPicDownload();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::picsPathChanged()
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&mutex);
|
|
||||||
picsPath = SettingsCache::instance().getPicsPath();
|
|
||||||
customPicsPath = SettingsCache::instance().getCustomPicsPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::setOverrideAllCardArtWithPersonalPreference(bool _overrideAllCardArtWithPersonalPreference)
|
|
||||||
{
|
|
||||||
overrideAllCardArtWithPersonalPreference = _overrideAllCardArtWithPersonalPreference;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::clearNetworkCache()
|
|
||||||
{
|
|
||||||
networkManager->cache()->clear();
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
#ifndef PICTURE_LOADER_WORKER_H
|
|
||||||
#define PICTURE_LOADER_WORKER_H
|
|
||||||
|
|
||||||
#include "../../../game/cards/card_info.h"
|
|
||||||
#include "picture_to_load.h"
|
|
||||||
|
|
||||||
#include <QLoggingCategory>
|
|
||||||
#include <QMutex>
|
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#define REDIRECT_HEADER_NAME "redirects"
|
|
||||||
#define REDIRECT_ORIGINAL_URL "original"
|
|
||||||
#define REDIRECT_URL "redirect"
|
|
||||||
#define REDIRECT_TIMESTAMP "timestamp"
|
|
||||||
#define REDIRECT_CACHE_FILENAME "cache.ini"
|
|
||||||
|
|
||||||
inline Q_LOGGING_CATEGORY(PictureLoaderWorkerLog, "picture_loader.worker");
|
|
||||||
|
|
||||||
class PictureLoaderWorker : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit PictureLoaderWorker();
|
|
||||||
~PictureLoaderWorker() override;
|
|
||||||
|
|
||||||
void enqueueImageLoad(CardInfoPtr card);
|
|
||||||
void clearNetworkCache();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static QStringList md5Blacklist;
|
|
||||||
|
|
||||||
QThread *pictureLoaderThread;
|
|
||||||
QString picsPath, customPicsPath;
|
|
||||||
QList<PictureToLoad> loadQueue;
|
|
||||||
QMutex mutex;
|
|
||||||
QNetworkAccessManager *networkManager;
|
|
||||||
QHash<QUrl, QPair<QUrl, QDateTime>> redirectCache; // Stores redirect and timestamp
|
|
||||||
QString cacheFilePath; // Path to persistent storage
|
|
||||||
static constexpr int CacheTTLInDays = 30; // TODO: Make user configurable
|
|
||||||
QList<PictureToLoad> cardsToDownload;
|
|
||||||
PictureToLoad cardBeingLoaded;
|
|
||||||
PictureToLoad cardBeingDownloaded;
|
|
||||||
bool picDownload, downloadRunning, loadQueueRunning;
|
|
||||||
bool overrideAllCardArtWithPersonalPreference;
|
|
||||||
void startNextPicDownload();
|
|
||||||
|
|
||||||
/** Emit the `imageLoaded` signal and return `true` if a picture is found on
|
|
||||||
disk, return `false` otherwise.
|
|
||||||
|
|
||||||
If `searchCustomPics` is `true`, the CUSTOM folder is searched for a
|
|
||||||
matching image first; otherwise, only the set-based folders are used. */
|
|
||||||
bool cardImageExistsOnDisk(QString &setName, QString &correctedCardName, bool searchCustomPics);
|
|
||||||
|
|
||||||
bool imageIsBlackListed(const QByteArray &);
|
|
||||||
QNetworkReply *makeRequest(const QUrl &url);
|
|
||||||
void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl);
|
|
||||||
QUrl getCachedRedirect(const QUrl &originalUrl) const;
|
|
||||||
void loadRedirectCache();
|
|
||||||
void saveRedirectCache() const;
|
|
||||||
void cleanStaleEntries();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void picDownloadFinished(QNetworkReply *reply);
|
|
||||||
void picDownloadFailed();
|
|
||||||
|
|
||||||
void picDownloadChanged();
|
|
||||||
void picsPathChanged();
|
|
||||||
void setOverrideAllCardArtWithPersonalPreference(bool _overrideAllCardArtWithPersonalPreference);
|
|
||||||
public slots:
|
|
||||||
void processLoadQueue();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void startLoadQueue();
|
|
||||||
void imageLoaded(CardInfoPtr card, const QImage &image);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // PICTURE_LOADER_WORKER_H
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#ifndef PICTURE_TO_LOAD_H
|
|
||||||
#define PICTURE_TO_LOAD_H
|
|
||||||
|
|
||||||
#include "../../../game/cards/card_info.h"
|
|
||||||
|
|
||||||
#include <QLoggingCategory>
|
|
||||||
|
|
||||||
inline Q_LOGGING_CATEGORY(PictureToLoadLog, "picture_loader.picture_to_load");
|
|
||||||
|
|
||||||
class PictureToLoad
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
CardInfoPtr card;
|
|
||||||
QList<CardSetPtr> sortedSets;
|
|
||||||
QList<QString> urlTemplates;
|
|
||||||
QList<QString> currentSetUrls;
|
|
||||||
QString currentUrl;
|
|
||||||
CardSetPtr currentSet;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit PictureToLoad(CardInfoPtr _card = CardInfoPtr());
|
|
||||||
|
|
||||||
CardInfoPtr getCard() const
|
|
||||||
{
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
card.clear();
|
|
||||||
}
|
|
||||||
QString getCurrentUrl() const
|
|
||||||
{
|
|
||||||
return currentUrl;
|
|
||||||
}
|
|
||||||
CardSetPtr getCurrentSet() const
|
|
||||||
{
|
|
||||||
return currentSet;
|
|
||||||
}
|
|
||||||
QString getSetName() const;
|
|
||||||
QString transformUrl(const QString &urlTemplate) const;
|
|
||||||
bool nextSet();
|
|
||||||
bool nextUrl();
|
|
||||||
void populateSetUrls();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // PICTURE_TO_LOAD_H
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#include "card_group_display_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../../deck/deck_list_model.h"
|
|
||||||
#include "../../../../../game/cards/card_database_manager.h"
|
|
||||||
#include "../../../../../utility/card_info_comparator.h"
|
|
||||||
#include "../card_info_picture_with_text_overlay_widget.h"
|
|
||||||
|
|
||||||
#include <QResizeEvent>
|
|
||||||
|
|
||||||
CardGroupDisplayWidget::CardGroupDisplayWidget(QWidget *parent,
|
|
||||||
DeckListModel *_deckListModel,
|
|
||||||
QString _zoneName,
|
|
||||||
QString _cardGroupCategory,
|
|
||||||
QString _activeGroupCriteria,
|
|
||||||
QStringList _activeSortCriteria,
|
|
||||||
int bannerOpacity,
|
|
||||||
CardSizeWidget *_cardSizeWidget)
|
|
||||||
: QWidget(parent), deckListModel(_deckListModel), zoneName(_zoneName), cardGroupCategory(_cardGroupCategory),
|
|
||||||
activeGroupCriteria(_activeGroupCriteria), activeSortCriteria(_activeSortCriteria),
|
|
||||||
cardSizeWidget(_cardSizeWidget)
|
|
||||||
{
|
|
||||||
layout = new QVBoxLayout(this);
|
|
||||||
setLayout(layout);
|
|
||||||
setMinimumSize(QSize(0, 0));
|
|
||||||
|
|
||||||
banner = new BannerWidget(this, cardGroupCategory, Qt::Orientation::Vertical, bannerOpacity);
|
|
||||||
|
|
||||||
layout->addWidget(banner);
|
|
||||||
CardGroupDisplayWidget::updateCardDisplays();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardGroupDisplayWidget::updateCardDisplays()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<CardInfoPtr> CardGroupDisplayWidget::getCardsMatchingGroup(QList<CardInfoPtr> cardsToSort)
|
|
||||||
{
|
|
||||||
cardsToSort = sortCardList(cardsToSort, activeSortCriteria, Qt::SortOrder::AscendingOrder);
|
|
||||||
|
|
||||||
QList<CardInfoPtr> activeList;
|
|
||||||
for (const CardInfoPtr &info : cardsToSort) {
|
|
||||||
if (info && info->getProperty(activeGroupCriteria) == cardGroupCategory) {
|
|
||||||
activeList.append(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return activeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<CardInfoPtr> CardGroupDisplayWidget::sortCardList(QList<CardInfoPtr> cardsToSort,
|
|
||||||
const QStringList properties,
|
|
||||||
Qt::SortOrder order = Qt::AscendingOrder)
|
|
||||||
{
|
|
||||||
CardInfoComparator comparator(properties, order);
|
|
||||||
std::sort(cardsToSort.begin(), cardsToSort.end(), comparator);
|
|
||||||
|
|
||||||
return cardsToSort;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardGroupDisplayWidget::onActiveSortCriteriaChanged(QStringList _activeSortCriteria)
|
|
||||||
{
|
|
||||||
if (activeSortCriteria != _activeSortCriteria) {
|
|
||||||
activeSortCriteria = _activeSortCriteria;
|
|
||||||
updateCardDisplays(); // Refresh display with new sorting
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardGroupDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card)
|
|
||||||
{
|
|
||||||
emit cardClicked(event, card);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardGroupDisplayWidget::onHover(CardInfoPtr card)
|
|
||||||
{
|
|
||||||
emit cardHovered(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardGroupDisplayWidget::resizeEvent(QResizeEvent *event)
|
|
||||||
{
|
|
||||||
QWidget::resizeEvent(event);
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#ifndef CARD_GROUP_DISPLAY_WIDGET_H
|
|
||||||
#define CARD_GROUP_DISPLAY_WIDGET_H
|
|
||||||
|
|
||||||
#include "../../../../../deck/deck_list_model.h"
|
|
||||||
#include "../../../../../game/cards/card_info.h"
|
|
||||||
#include "../../general/display/banner_widget.h"
|
|
||||||
#include "../card_info_picture_with_text_overlay_widget.h"
|
|
||||||
#include "../card_size_widget.h"
|
|
||||||
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
class CardGroupDisplayWidget : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
CardGroupDisplayWidget(QWidget *parent,
|
|
||||||
DeckListModel *deckListModel,
|
|
||||||
QString zoneName,
|
|
||||||
QString cardGroupCategory,
|
|
||||||
QString activeGroupCriteria,
|
|
||||||
QStringList activeSortCriteria,
|
|
||||||
int bannerOpacity,
|
|
||||||
CardSizeWidget *cardSizeWidget);
|
|
||||||
|
|
||||||
QList<CardInfoPtr> getCardsMatchingGroup(QList<CardInfoPtr> cardsToSort);
|
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
|
||||||
|
|
||||||
DeckListModel *deckListModel;
|
|
||||||
QString zoneName;
|
|
||||||
QString cardGroupCategory;
|
|
||||||
QString activeGroupCriteria;
|
|
||||||
QStringList activeSortCriteria;
|
|
||||||
CardSizeWidget *cardSizeWidget;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
QList<CardInfoPtr> sortCardList(QList<CardInfoPtr> cardsToSort, QStringList properties, Qt::SortOrder order);
|
|
||||||
void onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
|
|
||||||
void onHover(CardInfoPtr card);
|
|
||||||
virtual void updateCardDisplays();
|
|
||||||
void onActiveSortCriteriaChanged(QStringList activeSortCriteria);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void cardClicked(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card);
|
|
||||||
void cardHovered(CardInfoPtr card);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QVBoxLayout *layout;
|
|
||||||
BannerWidget *banner;
|
|
||||||
};
|
|
||||||
#endif // CARD_GROUP_DISPLAY_WIDGET_H
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
#include "flat_card_group_display_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../../deck/deck_list_model.h"
|
|
||||||
#include "../../../../../game/cards/card_database_manager.h"
|
|
||||||
#include "../../../../../utility/card_info_comparator.h"
|
|
||||||
#include "../card_info_picture_with_text_overlay_widget.h"
|
|
||||||
|
|
||||||
#include <QResizeEvent>
|
|
||||||
|
|
||||||
FlatCardGroupDisplayWidget::FlatCardGroupDisplayWidget(QWidget *parent,
|
|
||||||
DeckListModel *_deckListModel,
|
|
||||||
QString _zoneName,
|
|
||||||
QString _cardGroupCategory,
|
|
||||||
QString _activeGroupCriteria,
|
|
||||||
QStringList _activeSortCriteria,
|
|
||||||
int bannerOpacity,
|
|
||||||
CardSizeWidget *_cardSizeWidget)
|
|
||||||
: CardGroupDisplayWidget(parent,
|
|
||||||
_deckListModel,
|
|
||||||
_zoneName,
|
|
||||||
_cardGroupCategory,
|
|
||||||
_activeGroupCriteria,
|
|
||||||
_activeSortCriteria,
|
|
||||||
bannerOpacity,
|
|
||||||
_cardSizeWidget)
|
|
||||||
{
|
|
||||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff);
|
|
||||||
banner->setBuddy(flowWidget);
|
|
||||||
|
|
||||||
layout->addWidget(flowWidget);
|
|
||||||
FlatCardGroupDisplayWidget::updateCardDisplays();
|
|
||||||
connect(deckListModel, &DeckListModel::dataChanged, this, &FlatCardGroupDisplayWidget::updateCardDisplays);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlatCardGroupDisplayWidget::updateCardDisplays()
|
|
||||||
{
|
|
||||||
// Retrieve and sort cards
|
|
||||||
QList<CardInfoPtr> cardsInZone = getCardsMatchingGroup(deckListModel->getCardsAsCardInfoPtrsForZone(zoneName));
|
|
||||||
|
|
||||||
// Show or hide widget
|
|
||||||
bool shouldBeVisible = !cardsInZone.isEmpty();
|
|
||||||
if (shouldBeVisible != isVisible()) {
|
|
||||||
setVisible(shouldBeVisible);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve existing widgets
|
|
||||||
QList<CardInfoPictureWithTextOverlayWidget *> existingWidgets =
|
|
||||||
flowWidget->findChildren<CardInfoPictureWithTextOverlayWidget *>();
|
|
||||||
|
|
||||||
QHash<QString, QList<CardInfoPictureWithTextOverlayWidget *>> widgetMap;
|
|
||||||
for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
|
|
||||||
widgetMap[widget->getInfo()->getName()].append(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<CardInfoPictureWithTextOverlayWidget *> sortedWidgets;
|
|
||||||
QSet<CardInfoPictureWithTextOverlayWidget *> usedWidgets;
|
|
||||||
|
|
||||||
// Ensure widgets are ordered to match the sorted cards
|
|
||||||
for (const CardInfoPtr &card : cardsInZone) {
|
|
||||||
QString name = card->getName();
|
|
||||||
CardInfoPictureWithTextOverlayWidget *widget = nullptr;
|
|
||||||
|
|
||||||
if (!widgetMap[name].isEmpty()) {
|
|
||||||
// Reuse an existing widget
|
|
||||||
widget = widgetMap[name].takeFirst();
|
|
||||||
} else {
|
|
||||||
// Create a new widget if needed
|
|
||||||
widget = new CardInfoPictureWithTextOverlayWidget(flowWidget, true);
|
|
||||||
widget->setScaleFactor(cardSizeWidget->getSlider()->value());
|
|
||||||
widget->setCard(card);
|
|
||||||
|
|
||||||
connect(widget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this,
|
|
||||||
&FlatCardGroupDisplayWidget::onClick);
|
|
||||||
connect(widget, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this,
|
|
||||||
&FlatCardGroupDisplayWidget::onHover);
|
|
||||||
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, widget,
|
|
||||||
&CardInfoPictureWidget::setScaleFactor);
|
|
||||||
|
|
||||||
flowWidget->addWidget(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store in sorted order
|
|
||||||
sortedWidgets.append(widget);
|
|
||||||
usedWidgets.insert(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove extra widgets
|
|
||||||
for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
|
|
||||||
if (!usedWidgets.contains(widget)) {
|
|
||||||
flowWidget->layout()->removeWidget(widget);
|
|
||||||
widget->deleteLater();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// **Reorder widgets in place**
|
|
||||||
for (int i = 0; i < sortedWidgets.size(); ++i) {
|
|
||||||
sortedWidgets[i]->setParent(nullptr); // Temporarily detach
|
|
||||||
}
|
|
||||||
for (int i = 0; i < sortedWidgets.size(); ++i) {
|
|
||||||
flowWidget->addWidget(sortedWidgets[i]); // Reattach in correct order
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlatCardGroupDisplayWidget::resizeEvent(QResizeEvent *event)
|
|
||||||
{
|
|
||||||
QWidget::resizeEvent(event);
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
#include "overlapped_card_group_display_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../../deck/deck_list_model.h"
|
|
||||||
#include "../../../../../game/cards/card_database_manager.h"
|
|
||||||
#include "../../../../../utility/card_info_comparator.h"
|
|
||||||
#include "../card_info_picture_with_text_overlay_widget.h"
|
|
||||||
|
|
||||||
#include <QResizeEvent>
|
|
||||||
|
|
||||||
OverlappedCardGroupDisplayWidget::OverlappedCardGroupDisplayWidget(QWidget *parent,
|
|
||||||
DeckListModel *_deckListModel,
|
|
||||||
QString _zoneName,
|
|
||||||
QString _cardGroupCategory,
|
|
||||||
QString _activeGroupCriteria,
|
|
||||||
QStringList _activeSortCriteria,
|
|
||||||
int bannerOpacity,
|
|
||||||
CardSizeWidget *_cardSizeWidget)
|
|
||||||
: CardGroupDisplayWidget(parent,
|
|
||||||
_deckListModel,
|
|
||||||
_zoneName,
|
|
||||||
_cardGroupCategory,
|
|
||||||
_activeGroupCriteria,
|
|
||||||
_activeSortCriteria,
|
|
||||||
bannerOpacity,
|
|
||||||
_cardSizeWidget)
|
|
||||||
{
|
|
||||||
overlapWidget = new OverlapWidget(this, 80, 1, 1, Qt::Vertical, true);
|
|
||||||
banner->setBuddy(overlapWidget);
|
|
||||||
|
|
||||||
layout->addWidget(overlapWidget);
|
|
||||||
OverlappedCardGroupDisplayWidget::updateCardDisplays();
|
|
||||||
connect(deckListModel, &DeckListModel::dataChanged, this, &OverlappedCardGroupDisplayWidget::updateCardDisplays);
|
|
||||||
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, this,
|
|
||||||
[this]() { overlapWidget->adjustMaxColumnsAndRows(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void OverlappedCardGroupDisplayWidget::updateCardDisplays()
|
|
||||||
{
|
|
||||||
overlapWidget->setUpdatesEnabled(false);
|
|
||||||
// Retrieve and sort cards
|
|
||||||
QList<CardInfoPtr> cardsInZone = getCardsMatchingGroup(deckListModel->getCardsAsCardInfoPtrsForZone(zoneName));
|
|
||||||
|
|
||||||
// Show or hide widget
|
|
||||||
bool shouldBeVisible = !cardsInZone.isEmpty();
|
|
||||||
if (shouldBeVisible != isVisible()) {
|
|
||||||
setVisible(shouldBeVisible);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve existing widgets
|
|
||||||
QList<CardInfoPictureWithTextOverlayWidget *> existingWidgets =
|
|
||||||
overlapWidget->findChildren<CardInfoPictureWithTextOverlayWidget *>();
|
|
||||||
|
|
||||||
QHash<QString, QList<CardInfoPictureWithTextOverlayWidget *>> widgetMap;
|
|
||||||
for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
|
|
||||||
widgetMap[widget->getInfo()->getName()].append(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<CardInfoPictureWithTextOverlayWidget *> sortedWidgets;
|
|
||||||
QSet<CardInfoPictureWithTextOverlayWidget *> usedWidgets;
|
|
||||||
|
|
||||||
// Ensure widgets are ordered to match the sorted cards
|
|
||||||
for (const CardInfoPtr &card : cardsInZone) {
|
|
||||||
QString name = card->getName();
|
|
||||||
CardInfoPictureWithTextOverlayWidget *widget = nullptr;
|
|
||||||
|
|
||||||
if (!widgetMap[name].isEmpty()) {
|
|
||||||
// Reuse an existing widget
|
|
||||||
widget = widgetMap[name].takeFirst();
|
|
||||||
} else {
|
|
||||||
// Create a new widget if needed
|
|
||||||
widget = new CardInfoPictureWithTextOverlayWidget(overlapWidget, true);
|
|
||||||
widget->setScaleFactor(cardSizeWidget->getSlider()->value());
|
|
||||||
widget->setCard(card);
|
|
||||||
|
|
||||||
connect(widget, &CardInfoPictureWithTextOverlayWidget::imageClicked, this,
|
|
||||||
&OverlappedCardGroupDisplayWidget::onClick);
|
|
||||||
connect(widget, &CardInfoPictureWithTextOverlayWidget::hoveredOnCard, this,
|
|
||||||
&OverlappedCardGroupDisplayWidget::onHover);
|
|
||||||
connect(cardSizeWidget->getSlider(), &QSlider::valueChanged, widget,
|
|
||||||
&CardInfoPictureWidget::setScaleFactor);
|
|
||||||
|
|
||||||
overlapWidget->addWidget(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store in sorted order
|
|
||||||
sortedWidgets.append(widget);
|
|
||||||
usedWidgets.insert(widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove extra widgets
|
|
||||||
for (CardInfoPictureWithTextOverlayWidget *widget : existingWidgets) {
|
|
||||||
if (!usedWidgets.contains(widget)) {
|
|
||||||
overlapWidget->layout()->removeWidget(widget);
|
|
||||||
widget->deleteLater();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// **Reorder widgets in place**
|
|
||||||
for (int i = 0; i < sortedWidgets.size(); ++i) {
|
|
||||||
sortedWidgets[i]->setParent(nullptr); // Temporarily detach
|
|
||||||
}
|
|
||||||
for (int i = 0; i < sortedWidgets.size(); ++i) {
|
|
||||||
overlapWidget->addWidget(sortedWidgets[i]); // Reattach in correct order
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure proper layering
|
|
||||||
for (CardInfoPictureWithTextOverlayWidget *widget : sortedWidgets) {
|
|
||||||
widget->raise();
|
|
||||||
}
|
|
||||||
|
|
||||||
overlapWidget->adjustMaxColumnsAndRows();
|
|
||||||
overlapWidget->setUpdatesEnabled(true);
|
|
||||||
overlapWidget->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OverlappedCardGroupDisplayWidget::resizeEvent(QResizeEvent *event)
|
|
||||||
{
|
|
||||||
QWidget::resizeEvent(event);
|
|
||||||
|
|
||||||
overlapWidget->resize(event->size());
|
|
||||||
overlapWidget->adjustMaxColumnsAndRows();
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
#include "card_info_text_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../game/board/card_item.h"
|
|
||||||
#include "../../../../game/game_specific_terms.h"
|
|
||||||
|
|
||||||
#include <QGridLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QTextEdit>
|
|
||||||
|
|
||||||
CardInfoTextWidget::CardInfoTextWidget(QWidget *parent) : QFrame(parent), info(nullptr)
|
|
||||||
{
|
|
||||||
nameLabel = new QLabel;
|
|
||||||
nameLabel->setOpenExternalLinks(false);
|
|
||||||
nameLabel->setWordWrap(true);
|
|
||||||
connect(nameLabel, SIGNAL(linkActivated(const QString &)), this, SIGNAL(linkActivated(const QString &)));
|
|
||||||
|
|
||||||
textLabel = new QTextEdit();
|
|
||||||
textLabel->setReadOnly(true);
|
|
||||||
|
|
||||||
auto *grid = new QGridLayout(this);
|
|
||||||
grid->addWidget(nameLabel, 0, 0);
|
|
||||||
grid->addWidget(textLabel, 1, 0, -1, 2);
|
|
||||||
grid->setRowStretch(1, 1);
|
|
||||||
grid->setColumnStretch(1, 1);
|
|
||||||
|
|
||||||
retranslateUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardInfoTextWidget::setCard(CardInfoPtr card)
|
|
||||||
{
|
|
||||||
if (card == nullptr) {
|
|
||||||
nameLabel->setText("");
|
|
||||||
textLabel->setText("");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString text = "<table width=\"100%\" border=0 cellspacing=0 cellpadding=0>";
|
|
||||||
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>%2</td></tr>")
|
|
||||||
.arg(tr("Name:"), card->getName().toHtmlEscaped());
|
|
||||||
|
|
||||||
QStringList cardProps = card->getProperties();
|
|
||||||
for (const QString &key : cardProps) {
|
|
||||||
if (key.contains("-"))
|
|
||||||
continue;
|
|
||||||
QString keyText = Mtg::getNicePropertyName(key).toHtmlEscaped() + ":";
|
|
||||||
text +=
|
|
||||||
QString("<tr><td>%1</td><td></td><td>%2</td></tr>").arg(keyText, card->getProperty(key).toHtmlEscaped());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto relatedCards = card->getAllRelatedCards();
|
|
||||||
if (!relatedCards.empty()) {
|
|
||||||
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>").arg(tr("Related cards:"));
|
|
||||||
|
|
||||||
for (auto *relatedCard : relatedCards) {
|
|
||||||
QString tmp = relatedCard->getName().toHtmlEscaped();
|
|
||||||
text += "<a href=\"" + tmp + "\">" + tmp + "</a><br>";
|
|
||||||
}
|
|
||||||
|
|
||||||
text += "</td></tr>";
|
|
||||||
}
|
|
||||||
|
|
||||||
text += "</table>";
|
|
||||||
nameLabel->setText(text);
|
|
||||||
textLabel->setText(card->getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardInfoTextWidget::setInvalidCardName(const QString &cardName)
|
|
||||||
{
|
|
||||||
nameLabel->setText(tr("Unknown card:") + " " + cardName);
|
|
||||||
textLabel->setText("");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardInfoTextWidget::retranslateUi()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* There's no way we can really translate the text currently being rendered.
|
|
||||||
* The best we can do is invalidate the current text.
|
|
||||||
*/
|
|
||||||
setInvalidCardName("");
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
#include "deck_card_zone_display_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../deck/deck_list_model.h"
|
|
||||||
#include "../../../../utility/card_info_comparator.h"
|
|
||||||
#include "card_group_display_widgets/flat_card_group_display_widget.h"
|
|
||||||
#include "card_group_display_widgets/overlapped_card_group_display_widget.h"
|
|
||||||
|
|
||||||
#include <QResizeEvent>
|
|
||||||
|
|
||||||
DeckCardZoneDisplayWidget::DeckCardZoneDisplayWidget(QWidget *parent,
|
|
||||||
DeckListModel *_deckListModel,
|
|
||||||
QString _zoneName,
|
|
||||||
QString _activeGroupCriteria,
|
|
||||||
QStringList _activeSortCriteria,
|
|
||||||
int bannerOpacity,
|
|
||||||
int subBannerOpacity,
|
|
||||||
CardSizeWidget *_cardSizeWidget)
|
|
||||||
: QWidget(parent), deckListModel(_deckListModel), zoneName(_zoneName), activeGroupCriteria(_activeGroupCriteria),
|
|
||||||
activeSortCriteria(_activeSortCriteria), bannerOpacity(bannerOpacity), subBannerOpacity(subBannerOpacity),
|
|
||||||
cardSizeWidget(_cardSizeWidget)
|
|
||||||
{
|
|
||||||
layout = new QVBoxLayout(this);
|
|
||||||
setLayout(layout);
|
|
||||||
|
|
||||||
banner = new BannerWidget(this, zoneName, Qt::Orientation::Vertical, bannerOpacity);
|
|
||||||
layout->addWidget(banner);
|
|
||||||
|
|
||||||
cardGroupContainer = new QWidget(this);
|
|
||||||
cardGroupLayout = new QVBoxLayout(cardGroupContainer);
|
|
||||||
cardGroupContainer->setLayout(cardGroupLayout);
|
|
||||||
layout->addWidget(cardGroupContainer);
|
|
||||||
|
|
||||||
banner->setBuddy(cardGroupContainer);
|
|
||||||
|
|
||||||
displayCards();
|
|
||||||
connect(deckListModel, &DeckListModel::dataChanged, this, &DeckCardZoneDisplayWidget::displayCards);
|
|
||||||
}
|
|
||||||
void DeckCardZoneDisplayWidget::resizeEvent(QResizeEvent *event)
|
|
||||||
{
|
|
||||||
QWidget::resizeEvent(event);
|
|
||||||
for (QObject *child : layout->children()) {
|
|
||||||
QWidget *widget = qobject_cast<QWidget *>(child);
|
|
||||||
if (widget) {
|
|
||||||
widget->setMaximumWidth(width());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void DeckCardZoneDisplayWidget::onClick(QMouseEvent *event, CardInfoPictureWithTextOverlayWidget *card)
|
|
||||||
{
|
|
||||||
emit cardClicked(event, card, zoneName);
|
|
||||||
}
|
|
||||||
void DeckCardZoneDisplayWidget::onHover(CardInfoPtr card)
|
|
||||||
{
|
|
||||||
emit cardHovered(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckCardZoneDisplayWidget::displayCards()
|
|
||||||
{
|
|
||||||
addCardGroupIfItDoesNotExist();
|
|
||||||
deleteCardGroupIfItDoesNotExist();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckCardZoneDisplayWidget::refreshDisplayType(const QString &_displayType)
|
|
||||||
{
|
|
||||||
displayType = _displayType;
|
|
||||||
QLayoutItem *item;
|
|
||||||
while ((item = cardGroupLayout->takeAt(0)) != nullptr) {
|
|
||||||
if (item->widget()) {
|
|
||||||
item->widget()->deleteLater();
|
|
||||||
} else if (item->layout()) {
|
|
||||||
item->layout()->deleteLater();
|
|
||||||
}
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We gotta wait for all the deleteLater's to finish so we fire after the next event cycle
|
|
||||||
|
|
||||||
auto timer = new QTimer(this);
|
|
||||||
timer->setSingleShot(true);
|
|
||||||
connect(timer, &QTimer::timeout, this, [this]() { displayCards(); });
|
|
||||||
timer->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckCardZoneDisplayWidget::addCardGroupIfItDoesNotExist()
|
|
||||||
{
|
|
||||||
QList<CardGroupDisplayWidget *> cardGroupsDisplayWidgets =
|
|
||||||
cardGroupContainer->findChildren<CardGroupDisplayWidget *>();
|
|
||||||
|
|
||||||
QList<QString> cardGroups = getGroupCriteriaValueList();
|
|
||||||
|
|
||||||
for (QString cardGroup : cardGroups) {
|
|
||||||
bool found = false;
|
|
||||||
for (CardGroupDisplayWidget *cardGroupDisplayWidget : cardGroupsDisplayWidgets) {
|
|
||||||
if (cardGroupDisplayWidget->cardGroupCategory == cardGroup) {
|
|
||||||
found = true;
|
|
||||||
cardGroupDisplayWidget->updateCardDisplays();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displayType == "overlap") {
|
|
||||||
auto *display_widget = new OverlappedCardGroupDisplayWidget(
|
|
||||||
cardGroupContainer, deckListModel, zoneName, cardGroup, activeGroupCriteria, activeSortCriteria,
|
|
||||||
subBannerOpacity, cardSizeWidget);
|
|
||||||
connect(display_widget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
|
|
||||||
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
|
|
||||||
connect(display_widget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
|
|
||||||
connect(this, &DeckCardZoneDisplayWidget::activeSortCriteriaChanged, display_widget,
|
|
||||||
&CardGroupDisplayWidget::onActiveSortCriteriaChanged);
|
|
||||||
cardGroupLayout->addWidget(display_widget);
|
|
||||||
} else if (displayType == "flat") {
|
|
||||||
auto *display_widget = new FlatCardGroupDisplayWidget(cardGroupContainer, deckListModel, zoneName,
|
|
||||||
cardGroup, activeGroupCriteria, activeSortCriteria,
|
|
||||||
subBannerOpacity, cardSizeWidget);
|
|
||||||
connect(display_widget, SIGNAL(cardClicked(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)), this,
|
|
||||||
SLOT(onClick(QMouseEvent *, CardInfoPictureWithTextOverlayWidget *)));
|
|
||||||
connect(display_widget, SIGNAL(cardHovered(CardInfoPtr)), this, SLOT(onHover(CardInfoPtr)));
|
|
||||||
connect(this, &DeckCardZoneDisplayWidget::activeSortCriteriaChanged, display_widget,
|
|
||||||
&CardGroupDisplayWidget::onActiveSortCriteriaChanged);
|
|
||||||
cardGroupLayout->addWidget(display_widget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckCardZoneDisplayWidget::deleteCardGroupIfItDoesNotExist()
|
|
||||||
{
|
|
||||||
QList<CardGroupDisplayWidget *> cardGroupsDisplayWidgets =
|
|
||||||
cardGroupContainer->findChildren<CardGroupDisplayWidget *>();
|
|
||||||
|
|
||||||
QList<QString> validGroups = getGroupCriteriaValueList();
|
|
||||||
|
|
||||||
for (CardGroupDisplayWidget *cardGroupDisplayWidget : cardGroupsDisplayWidgets) {
|
|
||||||
if (!validGroups.contains(cardGroupDisplayWidget->cardGroupCategory)) {
|
|
||||||
cardGroupLayout->removeWidget(cardGroupDisplayWidget);
|
|
||||||
cardGroupDisplayWidget->deleteLater(); // Properly delete the widget after the event loop cycles
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged(QString _activeGroupCriteria)
|
|
||||||
{
|
|
||||||
activeGroupCriteria = _activeGroupCriteria;
|
|
||||||
displayCards();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged(QStringList _activeSortCriteria)
|
|
||||||
{
|
|
||||||
activeSortCriteria = _activeSortCriteria;
|
|
||||||
emit activeSortCriteriaChanged(activeSortCriteria);
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QString> DeckCardZoneDisplayWidget::getGroupCriteriaValueList()
|
|
||||||
{
|
|
||||||
QList<QString> groupCriteriaValues;
|
|
||||||
|
|
||||||
QList<CardInfoPtr> cardsInZone = deckListModel->getCardsAsCardInfoPtrsForZone(zoneName);
|
|
||||||
|
|
||||||
for (CardInfoPtr cardInZone : cardsInZone) {
|
|
||||||
groupCriteriaValues.append(cardInZone->getProperty(activeGroupCriteria));
|
|
||||||
}
|
|
||||||
|
|
||||||
groupCriteriaValues.removeDuplicates();
|
|
||||||
groupCriteriaValues.sort();
|
|
||||||
|
|
||||||
return groupCriteriaValues;
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#include "deck_analytics_widget.h"
|
|
||||||
|
|
||||||
DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListModel *_deckListModel)
|
|
||||||
: QWidget(parent), deckListModel(_deckListModel)
|
|
||||||
{
|
|
||||||
mainLayout = new QVBoxLayout();
|
|
||||||
setLayout(mainLayout);
|
|
||||||
|
|
||||||
manaCurveWidget = new ManaCurveWidget(this, deckListModel);
|
|
||||||
mainLayout->addWidget(manaCurveWidget);
|
|
||||||
|
|
||||||
manaDevotionWidget = new ManaDevotionWidget(this, deckListModel);
|
|
||||||
mainLayout->addWidget(manaDevotionWidget);
|
|
||||||
|
|
||||||
manaBaseWidget = new ManaBaseWidget(this, deckListModel);
|
|
||||||
mainLayout->addWidget(manaBaseWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckAnalyticsWidget::refreshDisplays(DeckListModel *_deckModel)
|
|
||||||
{
|
|
||||||
deckListModel = _deckModel;
|
|
||||||
manaCurveWidget->setDeckModel(_deckModel);
|
|
||||||
manaDevotionWidget->setDeckModel(_deckModel);
|
|
||||||
manaBaseWidget->setDeckModel(_deckModel);
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
#include "mana_base_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../deck/deck_loader.h"
|
|
||||||
#include "../../../../game/cards/card_database.h"
|
|
||||||
#include "../../../../game/cards/card_database_manager.h"
|
|
||||||
#include "../general/display/banner_widget.h"
|
|
||||||
#include "../general/display/bar_widget.h"
|
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <decklist.h>
|
|
||||||
|
|
||||||
ManaBaseWidget::ManaBaseWidget(QWidget *parent, DeckListModel *_deckListModel)
|
|
||||||
: QWidget(parent), deckListModel(_deckListModel)
|
|
||||||
{
|
|
||||||
layout = new QVBoxLayout(this);
|
|
||||||
setLayout(layout);
|
|
||||||
|
|
||||||
bannerWidget = new BannerWidget(this, tr("Mana Base"), Qt::Vertical, 100);
|
|
||||||
bannerWidget->setMaximumHeight(100);
|
|
||||||
layout->addWidget(bannerWidget);
|
|
||||||
|
|
||||||
barContainer = new QWidget(this);
|
|
||||||
barLayout = new QHBoxLayout(barContainer);
|
|
||||||
layout->addWidget(barContainer);
|
|
||||||
|
|
||||||
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaBaseWidget::analyzeManaBase);
|
|
||||||
|
|
||||||
retranslateUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaBaseWidget::retranslateUi()
|
|
||||||
{
|
|
||||||
bannerWidget->setText(tr("Mana Base"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaBaseWidget::setDeckModel(DeckListModel *deckModel)
|
|
||||||
{
|
|
||||||
deckListModel = deckModel;
|
|
||||||
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaBaseWidget::analyzeManaBase);
|
|
||||||
analyzeManaBase();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaBaseWidget::updateDisplay()
|
|
||||||
{
|
|
||||||
// Clear the layout first
|
|
||||||
QLayoutItem *item;
|
|
||||||
while ((item = barLayout->takeAt(0)) != nullptr) {
|
|
||||||
item->widget()->deleteLater();
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
int highestEntry = 0;
|
|
||||||
for (auto entry : manaBaseMap) {
|
|
||||||
if (entry > highestEntry) {
|
|
||||||
highestEntry = entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define color mapping for mana types
|
|
||||||
QHash<QString, QColor> manaColors;
|
|
||||||
manaColors.insert("W", QColor(248, 231, 185));
|
|
||||||
manaColors.insert("U", QColor(14, 104, 171));
|
|
||||||
manaColors.insert("B", QColor(21, 11, 0));
|
|
||||||
manaColors.insert("R", QColor(211, 32, 42));
|
|
||||||
manaColors.insert("G", QColor(0, 115, 62));
|
|
||||||
manaColors.insert("C", QColor(150, 150, 150));
|
|
||||||
|
|
||||||
for (auto manaColor : manaBaseMap.keys()) {
|
|
||||||
QColor barColor = manaColors.value(manaColor, Qt::gray);
|
|
||||||
BarWidget *barWidget = new BarWidget(QString(manaColor), manaBaseMap[manaColor], highestEntry, barColor, this);
|
|
||||||
barLayout->addWidget(barWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<QString, int> ManaBaseWidget::analyzeManaBase()
|
|
||||||
{
|
|
||||||
manaBaseMap.clear();
|
|
||||||
InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
|
|
||||||
for (int i = 0; i < listRoot->size(); i++) {
|
|
||||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
|
||||||
for (int j = 0; j < currentZone->size(); j++) {
|
|
||||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
|
||||||
if (!currentCard)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
|
||||||
CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName());
|
|
||||||
if (info) {
|
|
||||||
auto devotion = determineManaProduction(info->getText());
|
|
||||||
mergeManaCounts(manaBaseMap, devotion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDisplay();
|
|
||||||
return manaBaseMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<QString, int> ManaBaseWidget::determineManaProduction(const QString &rulesText)
|
|
||||||
{
|
|
||||||
QHash<QString, int> manaCounts = {{"W", 0}, {"U", 0}, {"B", 0}, {"R", 0}, {"G", 0}, {"C", 0}};
|
|
||||||
|
|
||||||
QString text = rulesText.toLower(); // Normalize case for matching
|
|
||||||
|
|
||||||
// Quick keyword-based checks for any color and colorless mana
|
|
||||||
if (text.contains("{t}: add one mana of any color") || text.contains("add one mana of any color")) {
|
|
||||||
for (const auto &color : {QStringLiteral("W"), QStringLiteral("U"), QStringLiteral("B"), QStringLiteral("R"),
|
|
||||||
QStringLiteral("G")}) {
|
|
||||||
manaCounts[color]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (text.contains("{t}: add {c}") || text.contains("add one colorless mana")) {
|
|
||||||
manaCounts["C"]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimized regex for specific mana symbols
|
|
||||||
static const QRegularExpression specificColorRegex(R"(\{T\}:\s*Add\s*\{([WUBRG])\})");
|
|
||||||
QRegularExpressionMatch match = specificColorRegex.match(rulesText);
|
|
||||||
if (match.hasMatch()) {
|
|
||||||
manaCounts[match.captured(1)]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return manaCounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaBaseWidget::mergeManaCounts(QHash<QString, int> &manaCounts1, const QHash<QString, int> &manaCounts2)
|
|
||||||
{
|
|
||||||
for (auto it = manaCounts2.constBegin(); it != manaCounts2.constEnd(); ++it) {
|
|
||||||
manaCounts1[it.key()] += it.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#ifndef MANA_BASE_WIDGET_H
|
|
||||||
#define MANA_BASE_WIDGET_H
|
|
||||||
|
|
||||||
#include "../../../../deck/deck_list_model.h"
|
|
||||||
#include "../general/display/banner_widget.h"
|
|
||||||
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QWidget>
|
|
||||||
#include <decklist.h>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
class ManaBaseWidget : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ManaBaseWidget(QWidget *parent, DeckListModel *deckListModel);
|
|
||||||
QHash<QString, int> analyzeManaBase();
|
|
||||||
void updateDisplay();
|
|
||||||
|
|
||||||
QHash<QString, int> determineManaProduction(const QString &manaString);
|
|
||||||
void mergeManaCounts(QHash<QString, int> &manaCounts1, const QHash<QString, int> &manaCounts2);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void setDeckModel(DeckListModel *deckModel);
|
|
||||||
void retranslateUi();
|
|
||||||
|
|
||||||
private:
|
|
||||||
DeckListModel *deckListModel;
|
|
||||||
BannerWidget *bannerWidget;
|
|
||||||
QHash<QString, int> manaBaseMap;
|
|
||||||
QVBoxLayout *layout;
|
|
||||||
QWidget *barContainer;
|
|
||||||
QHBoxLayout *barLayout;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MANA_BASE_WIDGET_H
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
#include "mana_curve_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../deck/deck_loader.h"
|
|
||||||
#include "../../../../game/cards/card_database.h"
|
|
||||||
#include "../../../../game/cards/card_database_manager.h"
|
|
||||||
#include "../../../../main.h"
|
|
||||||
#include "../general/display/banner_widget.h"
|
|
||||||
#include "../general/display/bar_widget.h"
|
|
||||||
|
|
||||||
#include <decklist.h>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
ManaCurveWidget::ManaCurveWidget(QWidget *parent, DeckListModel *_deckListModel)
|
|
||||||
: QWidget(parent), deckListModel(_deckListModel)
|
|
||||||
{
|
|
||||||
layout = new QVBoxLayout(this);
|
|
||||||
setLayout(layout);
|
|
||||||
|
|
||||||
bannerWidget = new BannerWidget(this, tr("Mana Curve"), Qt::Vertical, 100);
|
|
||||||
bannerWidget->setMaximumHeight(100);
|
|
||||||
layout->addWidget(bannerWidget);
|
|
||||||
|
|
||||||
barContainer = new QWidget(this);
|
|
||||||
barLayout = new QHBoxLayout(barContainer);
|
|
||||||
layout->addWidget(barContainer);
|
|
||||||
|
|
||||||
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaCurveWidget::analyzeManaCurve);
|
|
||||||
|
|
||||||
retranslateUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaCurveWidget::retranslateUi()
|
|
||||||
{
|
|
||||||
bannerWidget->setText(tr("Mana Curve"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaCurveWidget::setDeckModel(DeckListModel *deckModel)
|
|
||||||
{
|
|
||||||
deckListModel = deckModel;
|
|
||||||
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaCurveWidget::analyzeManaCurve);
|
|
||||||
analyzeManaCurve();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<int, int> ManaCurveWidget::analyzeManaCurve()
|
|
||||||
{
|
|
||||||
manaCurveMap.clear();
|
|
||||||
InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
|
|
||||||
for (int i = 0; i < listRoot->size(); i++) {
|
|
||||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
|
||||||
for (int j = 0; j < currentZone->size(); j++) {
|
|
||||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
|
||||||
if (!currentCard)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
|
||||||
CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName());
|
|
||||||
if (info) {
|
|
||||||
int cmc = info->getCmc().toInt();
|
|
||||||
manaCurveMap[cmc]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDisplay();
|
|
||||||
|
|
||||||
return manaCurveMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaCurveWidget::updateDisplay()
|
|
||||||
{
|
|
||||||
// Clear the layout first
|
|
||||||
if (barLayout != nullptr) {
|
|
||||||
QLayoutItem *item;
|
|
||||||
while ((item = barLayout->takeAt(0)) != nullptr) {
|
|
||||||
item->widget()->deleteLater();
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int highestEntry = 0;
|
|
||||||
for (const auto &entry : manaCurveMap) {
|
|
||||||
if (entry.second > highestEntry) {
|
|
||||||
highestEntry = entry.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert unordered_map to ordered map to ensure sorting by CMC
|
|
||||||
std::map<int, int> sortedManaCurve(manaCurveMap.begin(), manaCurveMap.end());
|
|
||||||
|
|
||||||
// Add new widgets to the layout in sorted order
|
|
||||||
for (const auto &entry : sortedManaCurve) {
|
|
||||||
BarWidget *barWidget =
|
|
||||||
new BarWidget(QString::number(entry.first), entry.second, highestEntry, QColor(11, 11, 11), this);
|
|
||||||
barLayout->addWidget(barWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
update(); // Update the widget display
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
#include "mana_devotion_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../deck/deck_loader.h"
|
|
||||||
#include "../../../../game/cards/card_database.h"
|
|
||||||
#include "../../../../game/cards/card_database_manager.h"
|
|
||||||
#include "../../../../main.h"
|
|
||||||
#include "../general/display/banner_widget.h"
|
|
||||||
#include "../general/display/bar_widget.h"
|
|
||||||
|
|
||||||
#include <decklist.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <regex>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
ManaDevotionWidget::ManaDevotionWidget(QWidget *parent, DeckListModel *_deckListModel)
|
|
||||||
: QWidget(parent), deckListModel(_deckListModel)
|
|
||||||
{
|
|
||||||
layout = new QVBoxLayout(this);
|
|
||||||
setLayout(layout);
|
|
||||||
|
|
||||||
bannerWidget = new BannerWidget(this, tr("Mana Devotion"), Qt::Vertical, 100);
|
|
||||||
bannerWidget->setMaximumHeight(100);
|
|
||||||
layout->addWidget(bannerWidget);
|
|
||||||
|
|
||||||
barLayout = new QHBoxLayout();
|
|
||||||
layout->addLayout(barLayout);
|
|
||||||
|
|
||||||
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaDevotionWidget::analyzeManaDevotion);
|
|
||||||
|
|
||||||
retranslateUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaDevotionWidget::retranslateUi()
|
|
||||||
{
|
|
||||||
bannerWidget->setText(tr("Mana Devotion"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaDevotionWidget::setDeckModel(DeckListModel *deckModel)
|
|
||||||
{
|
|
||||||
deckListModel = deckModel;
|
|
||||||
connect(deckListModel, &DeckListModel::dataChanged, this, &ManaDevotionWidget::analyzeManaDevotion);
|
|
||||||
analyzeManaDevotion();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<char, int> ManaDevotionWidget::analyzeManaDevotion()
|
|
||||||
{
|
|
||||||
manaDevotionMap.clear();
|
|
||||||
InnerDecklistNode *listRoot = deckListModel->getDeckList()->getRoot();
|
|
||||||
for (int i = 0; i < listRoot->size(); i++) {
|
|
||||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
|
||||||
for (int j = 0; j < currentZone->size(); j++) {
|
|
||||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
|
||||||
if (!currentCard)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
|
||||||
CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(currentCard->getName());
|
|
||||||
if (info) {
|
|
||||||
auto devotion = countManaSymbols(info->getManaCost());
|
|
||||||
mergeManaCounts(manaDevotionMap, devotion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDisplay();
|
|
||||||
return manaDevotionMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaDevotionWidget::updateDisplay()
|
|
||||||
{
|
|
||||||
// Clear the layout first
|
|
||||||
QLayoutItem *item;
|
|
||||||
while ((item = barLayout->takeAt(0)) != nullptr) {
|
|
||||||
item->widget()->deleteLater();
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
int highestEntry = 0;
|
|
||||||
for (auto entry : manaDevotionMap) {
|
|
||||||
if (highestEntry < entry.second) {
|
|
||||||
highestEntry = entry.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define color mapping for devotion bars
|
|
||||||
std::unordered_map<char, QColor> manaColors = {{'W', QColor(248, 231, 185)}, {'U', QColor(14, 104, 171)},
|
|
||||||
{'B', QColor(21, 11, 0)}, {'R', QColor(211, 32, 42)},
|
|
||||||
{'G', QColor(0, 115, 62)}, {'C', QColor(150, 150, 150)}};
|
|
||||||
|
|
||||||
for (auto entry : manaDevotionMap) {
|
|
||||||
QColor barColor = manaColors.count(entry.first) ? manaColors[entry.first] : Qt::gray;
|
|
||||||
BarWidget *barWidget = new BarWidget(QString(entry.first), entry.second, highestEntry, barColor, this);
|
|
||||||
barLayout->addWidget(barWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
update(); // Update the widget display
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<char, int> ManaDevotionWidget::countManaSymbols(const QString &manaString)
|
|
||||||
{
|
|
||||||
std::unordered_map<char, int> manaCounts = {{'W', 0}, {'U', 0}, {'B', 0}, {'R', 0}, {'G', 0}};
|
|
||||||
|
|
||||||
int len = manaString.length();
|
|
||||||
for (int i = 0; i < len; ++i) {
|
|
||||||
if (manaString[i] == '{') {
|
|
||||||
++i; // Move past '{'
|
|
||||||
if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
|
|
||||||
char mana1 = manaString[i].toLatin1();
|
|
||||||
++i; // Move to next character
|
|
||||||
if (i < len && manaString[i] == '/') {
|
|
||||||
++i; // Move past '/'
|
|
||||||
if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
|
|
||||||
char mana2 = manaString[i].toLatin1();
|
|
||||||
manaCounts[mana1]++;
|
|
||||||
manaCounts[mana2]++;
|
|
||||||
} else {
|
|
||||||
// Handle cases like "{W/}" where second part is invalid
|
|
||||||
manaCounts[mana1]++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
manaCounts[mana1]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Ensure we always skip to the closing '}'
|
|
||||||
while (i < len && manaString[i] != '}') {
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check if the character is a standalone mana symbol (not inside {})
|
|
||||||
else if (manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
|
|
||||||
manaCounts[manaString[i].toLatin1()]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return manaCounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManaDevotionWidget::mergeManaCounts(std::unordered_map<char, int> &manaCounts1,
|
|
||||||
const std::unordered_map<char, int> &manaCounts2)
|
|
||||||
{
|
|
||||||
for (const auto &pair : manaCounts2) {
|
|
||||||
manaCounts1[pair.first] += pair.second; // Add values for matching keys
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#ifndef MANA_DEVOTION_WIDGET_H
|
|
||||||
#define MANA_DEVOTION_WIDGET_H
|
|
||||||
|
|
||||||
#include "../../../../deck/deck_list_model.h"
|
|
||||||
#include "../general/display/banner_widget.h"
|
|
||||||
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QWidget>
|
|
||||||
#include <decklist.h>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
class ManaDevotionWidget : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ManaDevotionWidget(QWidget *parent, DeckListModel *deckListModel);
|
|
||||||
void updateDisplay();
|
|
||||||
|
|
||||||
std::unordered_map<char, int> countManaSymbols(const QString &manaString);
|
|
||||||
void mergeManaCounts(std::unordered_map<char, int> &manaCounts1, const std::unordered_map<char, int> &manaCounts2);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void setDeckModel(DeckListModel *deckModel);
|
|
||||||
std::unordered_map<char, int> analyzeManaDevotion();
|
|
||||||
void retranslateUi();
|
|
||||||
|
|
||||||
private:
|
|
||||||
DeckListModel *deckListModel;
|
|
||||||
BannerWidget *bannerWidget;
|
|
||||||
std::unordered_map<char, int> manaDevotionMap;
|
|
||||||
QVBoxLayout *layout;
|
|
||||||
QHBoxLayout *barLayout;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MANA_DEVOTION_WIDGET_H
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
#include "visual_deck_editor_sample_hand_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../deck/deck_loader.h"
|
|
||||||
#include "../../../../game/cards/card_database_manager.h"
|
|
||||||
#include "../cards/card_info_picture_widget.h"
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
VisualDeckEditorSampleHandWidget::VisualDeckEditorSampleHandWidget(QWidget *parent, DeckListModel *_deckListModel)
|
|
||||||
: QWidget(parent), deckListModel(_deckListModel)
|
|
||||||
{
|
|
||||||
layout = new QVBoxLayout(this);
|
|
||||||
setLayout(layout);
|
|
||||||
|
|
||||||
resetButton = new QPushButton(this);
|
|
||||||
connect(resetButton, SIGNAL(clicked()), this, SLOT(updateDisplay()));
|
|
||||||
layout->addWidget(resetButton);
|
|
||||||
|
|
||||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
|
||||||
layout->addWidget(flowWidget);
|
|
||||||
|
|
||||||
for (CardInfoPtr card : getRandomCards(7)) {
|
|
||||||
auto displayWidget = new CardInfoPictureWidget(this);
|
|
||||||
displayWidget->setCard(card);
|
|
||||||
flowWidget->addWidget(displayWidget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorSampleHandWidget::retranslateUi()
|
|
||||||
{
|
|
||||||
resetButton->setText(tr("Reset"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorSampleHandWidget::setDeckModel(DeckListModel *deckModel)
|
|
||||||
{
|
|
||||||
deckListModel = deckModel;
|
|
||||||
// connect(deckListModel, &DeckListModel::dataChanged, this, &VisualDeckEditorSampleHandWidget::updateDisplay);
|
|
||||||
updateDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorSampleHandWidget::updateDisplay()
|
|
||||||
{
|
|
||||||
flowWidget->clearLayout();
|
|
||||||
for (CardInfoPtr card : getRandomCards(7)) {
|
|
||||||
auto displayWidget = new CardInfoPictureWidget(this);
|
|
||||||
displayWidget->setCard(card);
|
|
||||||
flowWidget->addWidget(displayWidget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<CardInfoPtr> VisualDeckEditorSampleHandWidget::getRandomCards(int amountToGet)
|
|
||||||
{
|
|
||||||
QList<CardInfoPtr> mainDeckCards;
|
|
||||||
QList<CardInfoPtr> randomCards;
|
|
||||||
if (!deckListModel)
|
|
||||||
return randomCards;
|
|
||||||
DeckList *decklist = deckListModel->getDeckList();
|
|
||||||
if (!decklist)
|
|
||||||
return randomCards;
|
|
||||||
InnerDecklistNode *listRoot = decklist->getRoot();
|
|
||||||
if (!listRoot)
|
|
||||||
return randomCards;
|
|
||||||
|
|
||||||
// Collect all cards in the main deck, allowing duplicates based on their count
|
|
||||||
for (int i = 0; i < listRoot->size(); i++) {
|
|
||||||
InnerDecklistNode *currentZone = dynamic_cast<InnerDecklistNode *>(listRoot->at(i));
|
|
||||||
if (!currentZone)
|
|
||||||
continue;
|
|
||||||
if (currentZone->getName() != DECK_ZONE_MAIN)
|
|
||||||
continue; // Only process the main deck
|
|
||||||
|
|
||||||
for (int j = 0; j < currentZone->size(); j++) {
|
|
||||||
DecklistCardNode *currentCard = dynamic_cast<DecklistCardNode *>(currentZone->at(j));
|
|
||||||
if (!currentCard)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (int k = 0; k < currentCard->getNumber(); ++k) {
|
|
||||||
CardInfoPtr info = CardDatabaseManager::getInstance()->getCardByNameAndProviderId(
|
|
||||||
currentCard->getName(), currentCard->getCardProviderId());
|
|
||||||
if (info) {
|
|
||||||
mainDeckCards.append(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainDeckCards.isEmpty())
|
|
||||||
return randomCards;
|
|
||||||
|
|
||||||
// Shuffle the deck
|
|
||||||
std::random_device rd;
|
|
||||||
std::mt19937 rng(rd());
|
|
||||||
std::shuffle(mainDeckCards.begin(), mainDeckCards.end(), rng);
|
|
||||||
|
|
||||||
// Select amountToGet cards
|
|
||||||
|
|
||||||
for (int i = 0; i < qMin(amountToGet, mainDeckCards.size()); ++i) {
|
|
||||||
randomCards.append(mainDeckCards.at(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(randomCards.begin(), randomCards.end(),
|
|
||||||
[](const CardInfoPtr &a, const CardInfoPtr &b) { return a->getCmc() < b->getCmc(); });
|
|
||||||
|
|
||||||
return randomCards;
|
|
||||||
}
|
|
||||||
@@ -1,315 +0,0 @@
|
|||||||
#include "visual_deck_editor_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../deck/deck_list_model.h"
|
|
||||||
#include "../../../../deck/deck_loader.h"
|
|
||||||
#include "../../../../game/cards/card_completer_proxy_model.h"
|
|
||||||
#include "../../../../game/cards/card_database.h"
|
|
||||||
#include "../../../../game/cards/card_database_manager.h"
|
|
||||||
#include "../../../../game/cards/card_database_model.h"
|
|
||||||
#include "../../../../game/cards/card_search_model.h"
|
|
||||||
#include "../../../../main.h"
|
|
||||||
#include "../../../../utility/card_info_comparator.h"
|
|
||||||
#include "../../layouts/overlap_layout.h"
|
|
||||||
#include "../cards/card_info_picture_with_text_overlay_widget.h"
|
|
||||||
#include "../cards/deck_card_zone_display_widget.h"
|
|
||||||
#include "../general/layout_containers/flow_widget.h"
|
|
||||||
#include "../general/layout_containers/overlap_control_widget.h"
|
|
||||||
|
|
||||||
#include <QCheckBox>
|
|
||||||
#include <QCompleter>
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QLineEdit>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QResizeEvent>
|
|
||||||
#include <qscrollarea.h>
|
|
||||||
|
|
||||||
VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, DeckListModel *_deckListModel)
|
|
||||||
: QWidget(parent), deckListModel(_deckListModel)
|
|
||||||
{
|
|
||||||
connect(deckListModel, &DeckListModel::dataChanged, this, &VisualDeckEditorWidget::decklistDataChanged);
|
|
||||||
|
|
||||||
// The Main Widget and Main Layout, which contain a single Widget: The Scroll Area
|
|
||||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
||||||
mainLayout = new QVBoxLayout(this);
|
|
||||||
setLayout(mainLayout);
|
|
||||||
mainLayout->setContentsMargins(9, 0, 9, 5);
|
|
||||||
mainLayout->setSpacing(0);
|
|
||||||
|
|
||||||
searchContainer = new QWidget(this);
|
|
||||||
searchLayout = new QHBoxLayout(searchContainer);
|
|
||||||
searchContainer->setLayout(searchLayout);
|
|
||||||
|
|
||||||
searchBar = new QLineEdit(this);
|
|
||||||
connect(searchBar, &QLineEdit::returnPressed, this, [=, this]() {
|
|
||||||
if (!searchBar->hasFocus())
|
|
||||||
return;
|
|
||||||
|
|
||||||
CardInfoPtr card = CardDatabaseManager::getInstance()->getCard(searchBar->text());
|
|
||||||
if (card) {
|
|
||||||
emit cardAdditionRequested(card);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setFocusProxy(searchBar);
|
|
||||||
setFocusPolicy(Qt::ClickFocus);
|
|
||||||
|
|
||||||
cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, this);
|
|
||||||
cardDatabaseDisplayModel = new CardDatabaseDisplayModel(this);
|
|
||||||
cardDatabaseDisplayModel->setSourceModel(cardDatabaseModel);
|
|
||||||
CardSearchModel *searchModel = new CardSearchModel(cardDatabaseDisplayModel, this);
|
|
||||||
|
|
||||||
proxyModel = new CardCompleterProxyModel(this);
|
|
||||||
proxyModel->setSourceModel(searchModel);
|
|
||||||
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
|
||||||
proxyModel->setFilterRole(Qt::DisplayRole);
|
|
||||||
|
|
||||||
completer = new QCompleter(proxyModel, this);
|
|
||||||
completer->setCompletionRole(Qt::DisplayRole);
|
|
||||||
completer->setCompletionMode(QCompleter::PopupCompletion);
|
|
||||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
|
||||||
completer->setFilterMode(Qt::MatchContains);
|
|
||||||
completer->setMaxVisibleItems(15);
|
|
||||||
searchBar->setCompleter(completer);
|
|
||||||
|
|
||||||
// Update suggestions dynamically
|
|
||||||
connect(searchBar, &QLineEdit::textEdited, searchModel, &CardSearchModel::updateSearchResults);
|
|
||||||
connect(searchBar, &QLineEdit::textEdited, this, [=, this](const QString &text) {
|
|
||||||
// Ensure substring matching
|
|
||||||
QString pattern = ".*" + QRegularExpression::escape(text) + ".*";
|
|
||||||
proxyModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
|
|
||||||
|
|
||||||
if (!text.isEmpty()) {
|
|
||||||
completer->complete(); // Force the dropdown to appear
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(completer, static_cast<void (QCompleter::*)(const QString &)>(&QCompleter::activated), this,
|
|
||||||
[=, this](const QString &completion) {
|
|
||||||
// Prevent the text from changing automatically when navigating with arrow keys
|
|
||||||
if (searchBar->text() != completion) {
|
|
||||||
searchBar->setText(completion); // Set the completion explicitly
|
|
||||||
searchBar->setCursorPosition(searchBar->text().length()); // Move cursor to the end
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensure that the text stays consistent during selection
|
|
||||||
connect(searchBar, &QLineEdit::textEdited, this, [=, this](const QString &text) {
|
|
||||||
if (searchBar->hasFocus() && !searchBar->completer()->popup()->isVisible()) {
|
|
||||||
// Allow text to change when typing, but not when navigating the completer
|
|
||||||
QString pattern = ".*" + QRegularExpression::escape(text) + ".*";
|
|
||||||
proxyModel->setFilterRegularExpression(
|
|
||||||
QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Search button functionality
|
|
||||||
searchPushButton = new QPushButton(this);
|
|
||||||
connect(searchPushButton, &QPushButton::clicked, this, [=, this]() {
|
|
||||||
CardInfoPtr card = CardDatabaseManager::getInstance()->getCard(searchBar->text());
|
|
||||||
if (card) {
|
|
||||||
emit cardAdditionRequested(card);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
searchLayout->addWidget(searchBar);
|
|
||||||
searchLayout->addWidget(searchPushButton);
|
|
||||||
|
|
||||||
mainLayout->addWidget(searchContainer);
|
|
||||||
|
|
||||||
groupAndSortContainer = new QWidget(this);
|
|
||||||
groupAndSortLayout = new QHBoxLayout(groupAndSortContainer);
|
|
||||||
groupAndSortLayout->setAlignment(Qt::AlignLeft);
|
|
||||||
groupAndSortContainer->setLayout(groupAndSortLayout);
|
|
||||||
|
|
||||||
groupByComboBox = new QComboBox();
|
|
||||||
QStringList groupProperties = {"maintype", "colors", "cmc", "name"};
|
|
||||||
groupByComboBox->addItems(groupProperties);
|
|
||||||
groupByComboBox->setMinimumWidth(300);
|
|
||||||
connect(groupByComboBox, QOverload<const QString &>::of(&QComboBox::currentTextChanged), this,
|
|
||||||
&VisualDeckEditorWidget::actChangeActiveGroupCriteria);
|
|
||||||
actChangeActiveGroupCriteria();
|
|
||||||
|
|
||||||
sortCriteriaButton = new SettingsButtonWidget(this);
|
|
||||||
|
|
||||||
sortLabel = new QLabel(sortCriteriaButton);
|
|
||||||
sortLabel->setWordWrap(true);
|
|
||||||
|
|
||||||
QStringList sortProperties = {"colors", "cmc", "name", "maintype"};
|
|
||||||
sortByListWidget = new QListWidget();
|
|
||||||
sortByListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
||||||
sortByListWidget->setDragDropMode(QAbstractItemView::InternalMove);
|
|
||||||
sortByListWidget->setDefaultDropAction(Qt::MoveAction);
|
|
||||||
|
|
||||||
for (const QString &property : sortProperties) {
|
|
||||||
QListWidgetItem *item = new QListWidgetItem(property, sortByListWidget);
|
|
||||||
item->setFlags(item->flags() | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(sortByListWidget->model(), &QAbstractItemModel::rowsMoved, this,
|
|
||||||
&VisualDeckEditorWidget::actChangeActiveSortCriteria);
|
|
||||||
actChangeActiveSortCriteria();
|
|
||||||
|
|
||||||
sortByListWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
||||||
|
|
||||||
sortCriteriaButton->addSettingsWidget(sortLabel);
|
|
||||||
sortCriteriaButton->addSettingsWidget(sortByListWidget);
|
|
||||||
|
|
||||||
displayTypeButton = new QPushButton(this);
|
|
||||||
connect(displayTypeButton, &QPushButton::clicked, this, &VisualDeckEditorWidget::updateDisplayType);
|
|
||||||
|
|
||||||
groupAndSortLayout->addWidget(groupByComboBox);
|
|
||||||
groupAndSortLayout->addWidget(sortCriteriaButton);
|
|
||||||
groupAndSortLayout->addWidget(displayTypeButton);
|
|
||||||
|
|
||||||
scrollArea = new QScrollArea(this);
|
|
||||||
scrollArea->setWidgetResizable(true);
|
|
||||||
scrollArea->setMinimumSize(0, 0);
|
|
||||||
|
|
||||||
// Set scrollbar policies
|
|
||||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
||||||
|
|
||||||
zoneContainer = new QWidget(scrollArea);
|
|
||||||
zoneContainerLayout = new QVBoxLayout(zoneContainer);
|
|
||||||
zoneContainer->setLayout(zoneContainerLayout);
|
|
||||||
scrollArea->addScrollBarWidget(zoneContainer, Qt::AlignHCenter);
|
|
||||||
scrollArea->setWidget(zoneContainer);
|
|
||||||
|
|
||||||
updateZoneWidgets();
|
|
||||||
|
|
||||||
cardSizeWidget = new CardSizeWidget(this);
|
|
||||||
|
|
||||||
mainLayout->addWidget(groupAndSortContainer);
|
|
||||||
mainLayout->addWidget(scrollArea);
|
|
||||||
mainLayout->addWidget(cardSizeWidget);
|
|
||||||
|
|
||||||
retranslateUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::retranslateUi()
|
|
||||||
{
|
|
||||||
sortLabel->setText(tr("Click and drag to change the sort order within the groups"));
|
|
||||||
searchPushButton->setText(tr("Quick search and add card"));
|
|
||||||
displayTypeButton->setText(tr("Flat Layout"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::updateZoneWidgets()
|
|
||||||
{
|
|
||||||
addZoneIfDoesNotExist();
|
|
||||||
deleteZoneIfDoesNotExist();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::updateDisplayType()
|
|
||||||
{
|
|
||||||
// Toggle the display type
|
|
||||||
currentDisplayType = (currentDisplayType == DisplayType::Overlap) ? DisplayType::Flat : DisplayType::Overlap;
|
|
||||||
|
|
||||||
// Update UI and emit signal
|
|
||||||
switch (currentDisplayType) {
|
|
||||||
case DisplayType::Flat:
|
|
||||||
emit displayTypeChanged("flat");
|
|
||||||
displayTypeButton->setText(tr("Flat Layout"));
|
|
||||||
break;
|
|
||||||
case DisplayType::Overlap:
|
|
||||||
emit displayTypeChanged("overlap");
|
|
||||||
displayTypeButton->setText(tr("Overlap Layout"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::addZoneIfDoesNotExist()
|
|
||||||
{
|
|
||||||
QList<DeckCardZoneDisplayWidget *> cardZoneDisplayWidgets =
|
|
||||||
zoneContainer->findChildren<DeckCardZoneDisplayWidget *>();
|
|
||||||
for (const QString &zone : *deckListModel->getZones()) {
|
|
||||||
bool found = false;
|
|
||||||
for (DeckCardZoneDisplayWidget *displayWidget : cardZoneDisplayWidgets) {
|
|
||||||
if (displayWidget->zoneName == zone) {
|
|
||||||
found = true;
|
|
||||||
displayWidget->displayCards();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
DeckCardZoneDisplayWidget *zoneDisplayWidget = new DeckCardZoneDisplayWidget(
|
|
||||||
zoneContainer, deckListModel, zone, activeGroupCriteria, activeSortCriteria, 20, 10, cardSizeWidget);
|
|
||||||
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardHovered, this, &VisualDeckEditorWidget::onHover);
|
|
||||||
connect(zoneDisplayWidget, &DeckCardZoneDisplayWidget::cardClicked, this, &VisualDeckEditorWidget::onCardClick);
|
|
||||||
connect(this, &VisualDeckEditorWidget::activeSortCriteriaChanged, zoneDisplayWidget,
|
|
||||||
&DeckCardZoneDisplayWidget::onActiveSortCriteriaChanged);
|
|
||||||
connect(this, &VisualDeckEditorWidget::activeGroupCriteriaChanged, zoneDisplayWidget,
|
|
||||||
&DeckCardZoneDisplayWidget::onActiveGroupCriteriaChanged);
|
|
||||||
connect(this, &VisualDeckEditorWidget::displayTypeChanged, zoneDisplayWidget,
|
|
||||||
&DeckCardZoneDisplayWidget::refreshDisplayType);
|
|
||||||
zoneContainerLayout->addWidget(zoneDisplayWidget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::deleteZoneIfDoesNotExist()
|
|
||||||
{
|
|
||||||
QList<DeckCardZoneDisplayWidget *> cardZoneDisplayWidgets =
|
|
||||||
zoneContainer->findChildren<DeckCardZoneDisplayWidget *>();
|
|
||||||
for (DeckCardZoneDisplayWidget *displayWidget : cardZoneDisplayWidgets) {
|
|
||||||
bool found = false;
|
|
||||||
for (const QString &zone : *deckListModel->getZones()) {
|
|
||||||
if (displayWidget->zoneName == zone) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
zoneContainerLayout->removeWidget(displayWidget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::resizeEvent(QResizeEvent *event)
|
|
||||||
{
|
|
||||||
QWidget::resizeEvent(event);
|
|
||||||
zoneContainer->setMaximumWidth(scrollArea->viewport()->width());
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::actChangeActiveGroupCriteria()
|
|
||||||
{
|
|
||||||
activeGroupCriteria = groupByComboBox->currentText();
|
|
||||||
emit activeGroupCriteriaChanged(activeGroupCriteria);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::actChangeActiveSortCriteria()
|
|
||||||
{
|
|
||||||
QStringList selectedCriteria;
|
|
||||||
for (int i = 0; i < sortByListWidget->count(); ++i) {
|
|
||||||
QListWidgetItem *item = sortByListWidget->item(i);
|
|
||||||
selectedCriteria.append(item->text()); // Collect user-defined sort order
|
|
||||||
}
|
|
||||||
|
|
||||||
activeSortCriteria = selectedCriteria;
|
|
||||||
|
|
||||||
emit activeSortCriteriaChanged(selectedCriteria);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::decklistDataChanged(QModelIndex topLeft, QModelIndex bottomRight)
|
|
||||||
{
|
|
||||||
// Might use these at some point.
|
|
||||||
Q_UNUSED(topLeft);
|
|
||||||
Q_UNUSED(bottomRight);
|
|
||||||
// Necessary to delay this in this manner else the updateDisplay will nuke widgets while their onClick event
|
|
||||||
// hasn't returned yet. Interval of 0 means QT will schedule this after the current event loop has finished.
|
|
||||||
updateZoneWidgets();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::onHover(CardInfoPtr hoveredCard)
|
|
||||||
{
|
|
||||||
emit activeCardChanged(hoveredCard);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VisualDeckEditorWidget::onCardClick(QMouseEvent *event,
|
|
||||||
CardInfoPictureWithTextOverlayWidget *instance,
|
|
||||||
QString zoneName)
|
|
||||||
{
|
|
||||||
emit cardClicked(event, instance, zoneName);
|
|
||||||
}
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
#include "deck_preview_deck_tags_display_widget.h"
|
|
||||||
|
|
||||||
#include "../../../../../dialogs/dlg_convert_deck_to_cod_format.h"
|
|
||||||
#include "../../../../../settings/cache_settings.h"
|
|
||||||
#include "../../../../tabs/tab_deck_editor.h"
|
|
||||||
#include "../../general/layout_containers/flow_widget.h"
|
|
||||||
#include "deck_preview_tag_addition_widget.h"
|
|
||||||
#include "deck_preview_tag_dialog.h"
|
|
||||||
#include "deck_preview_tag_display_widget.h"
|
|
||||||
#include "deck_preview_widget.h"
|
|
||||||
|
|
||||||
#include <QDirIterator>
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
|
|
||||||
DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList)
|
|
||||||
: QWidget(_parent), deckList(nullptr)
|
|
||||||
{
|
|
||||||
|
|
||||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
||||||
|
|
||||||
// Create layout
|
|
||||||
auto *layout = new QHBoxLayout(this);
|
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
setFixedHeight(100);
|
|
||||||
|
|
||||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
|
||||||
|
|
||||||
if (_deckList) {
|
|
||||||
connectDeckList(_deckList);
|
|
||||||
}
|
|
||||||
|
|
||||||
layout->addWidget(flowWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckPreviewDeckTagsDisplayWidget::connectDeckList(DeckList *_deckList)
|
|
||||||
{
|
|
||||||
if (deckList) {
|
|
||||||
disconnect(deckList, &DeckList::deckTagsChanged, this, &DeckPreviewDeckTagsDisplayWidget::refreshTags);
|
|
||||||
}
|
|
||||||
|
|
||||||
deckList = _deckList;
|
|
||||||
connect(deckList, &DeckList::deckTagsChanged, this, &DeckPreviewDeckTagsDisplayWidget::refreshTags);
|
|
||||||
|
|
||||||
refreshTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckPreviewDeckTagsDisplayWidget::refreshTags()
|
|
||||||
{
|
|
||||||
flowWidget->clearLayout();
|
|
||||||
|
|
||||||
for (const QString &tag : deckList->getTags()) {
|
|
||||||
flowWidget->addWidget(new DeckPreviewTagDisplayWidget(this, tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto tagAdditionWidget = new DeckPreviewTagAdditionWidget(this, tr("Edit tags ..."));
|
|
||||||
connect(tagAdditionWidget, &DeckPreviewTagAdditionWidget::tagClicked, this,
|
|
||||||
&DeckPreviewDeckTagsDisplayWidget::openTagEditDlg);
|
|
||||||
flowWidget->addWidget(tagAdditionWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the filepath of all files (no directories) in target directory and all subdirectories
|
|
||||||
*/
|
|
||||||
static QStringList getAllFiles(const QString &filePath)
|
|
||||||
{
|
|
||||||
QStringList allFiles;
|
|
||||||
|
|
||||||
// QDirIterator with QDir::Files ensures only files are listed (no directories)
|
|
||||||
QDirIterator it(filePath, QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
||||||
|
|
||||||
while (it.hasNext()) {
|
|
||||||
allFiles << it.next(); // Add each file path to the list
|
|
||||||
}
|
|
||||||
|
|
||||||
return allFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg()
|
|
||||||
{
|
|
||||||
if (qobject_cast<DeckPreviewWidget *>(parentWidget())) {
|
|
||||||
auto *deckPreviewWidget = qobject_cast<DeckPreviewWidget *>(parentWidget());
|
|
||||||
QStringList knownTags = deckPreviewWidget->visualDeckStorageWidget->tagFilterWidget->getAllKnownTags();
|
|
||||||
QStringList activeTags = deckList->getTags();
|
|
||||||
|
|
||||||
bool canAddTags = true;
|
|
||||||
|
|
||||||
if (DeckLoader::getFormatFromName(deckPreviewWidget->filePath) != DeckLoader::CockatriceFormat) {
|
|
||||||
canAddTags = false;
|
|
||||||
// Retrieve saved preference if the prompt is disabled
|
|
||||||
if (!SettingsCache::instance().getVisualDeckStoragePromptForConversion()) {
|
|
||||||
if (SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) {
|
|
||||||
deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath);
|
|
||||||
deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastFileName();
|
|
||||||
deckPreviewWidget->refreshBannerCardText();
|
|
||||||
canAddTags = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Show the dialog to the user
|
|
||||||
DialogConvertDeckToCodFormat conversionDialog(parentWidget());
|
|
||||||
if (conversionDialog.exec() == QDialog::Accepted) {
|
|
||||||
deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath);
|
|
||||||
deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastFileName();
|
|
||||||
deckPreviewWidget->refreshBannerCardText();
|
|
||||||
canAddTags = true;
|
|
||||||
|
|
||||||
if (conversionDialog.dontAskAgain()) {
|
|
||||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(false);
|
|
||||||
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(false);
|
|
||||||
|
|
||||||
if (conversionDialog.dontAskAgain()) {
|
|
||||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(false);
|
|
||||||
} else {
|
|
||||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canAddTags) {
|
|
||||||
DeckPreviewTagDialog dialog(knownTags, activeTags);
|
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
|
||||||
QStringList updatedTags = dialog.getActiveTags();
|
|
||||||
deckList->setTags(updatedTags);
|
|
||||||
deckPreviewWidget->deckLoader->saveToFile(deckPreviewWidget->filePath, DeckLoader::CockatriceFormat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (parentWidget()) {
|
|
||||||
// If we're the child of a TabDeckEditor, we are buried under a ton of childWidgets in the DeckInfoDock.
|
|
||||||
QWidget *currentParent = parentWidget();
|
|
||||||
while (currentParent) {
|
|
||||||
if (qobject_cast<TabDeckEditor *>(currentParent)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
currentParent = currentParent->parentWidget();
|
|
||||||
}
|
|
||||||
if (qobject_cast<TabDeckEditor *>(currentParent)) {
|
|
||||||
auto *deckEditor = qobject_cast<TabDeckEditor *>(currentParent);
|
|
||||||
QStringList knownTags;
|
|
||||||
QStringList allFiles = getAllFiles(SettingsCache::instance().getDeckPath());
|
|
||||||
DeckLoader loader;
|
|
||||||
for (const QString &file : allFiles) {
|
|
||||||
loader.loadFromFile(file, DeckLoader::getFormatFromName(file), false);
|
|
||||||
QStringList tags = loader.getTags();
|
|
||||||
knownTags.append(tags);
|
|
||||||
knownTags.removeDuplicates();
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList activeTags = deckList->getTags();
|
|
||||||
|
|
||||||
DeckPreviewTagDialog dialog(knownTags, activeTags);
|
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
|
||||||
QStringList updatedTags = dialog.getActiveTags();
|
|
||||||
deckList->setTags(updatedTags);
|
|
||||||
deckEditor->setModified(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||