Compare commits
388 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bec39d89e1 | ||
|
|
467f1449ab | ||
|
|
53c659cbda | ||
|
|
5deee18247 | ||
|
|
60a991bcb0 | ||
|
|
d3c5ed08ab | ||
|
|
2bdce7dce6 | ||
|
|
41b2e7c9c7 | ||
|
|
6021923bf1 | ||
|
|
839cedde65 | ||
|
|
92d10fd34c | ||
|
|
25e0e4b5d3 | ||
|
|
43cc2145a8 | ||
|
|
c3c46f8e29 | ||
|
|
0744f43a0e | ||
|
|
73470c3a95 | ||
|
|
a6d0fa433e | ||
|
|
a9fa49e372 | ||
|
|
2cb69f0c2b | ||
|
|
1da9bfc643 | ||
|
|
78227cd947 | ||
|
|
456a949d01 | ||
|
|
7f598e358a | ||
|
|
a058e75a6d | ||
|
|
48362cd4bc | ||
|
|
bd653dfdff | ||
|
|
bc03c80d8d | ||
|
|
2dcbe15f8b | ||
|
|
b364cbbd69 | ||
|
|
3e374eda51 | ||
|
|
0ae63b072c | ||
|
|
f718410df3 | ||
|
|
74e120bc7b | ||
|
|
5f8ede194b | ||
|
|
4d321b1cf0 | ||
|
|
84858b8940 | ||
|
|
92f95fa9eb | ||
|
|
5d8888bb8b | ||
|
|
4988d21819 | ||
|
|
94964c35ce | ||
|
|
d605b18ec0 | ||
|
|
590e6c8e9d | ||
|
|
67572b0d9a | ||
|
|
5554867553 | ||
|
|
f3102573ea | ||
|
|
0e7ce97da7 | ||
|
|
9d789c2657 | ||
|
|
468975e9f3 | ||
|
|
145dda843e | ||
|
|
6e69069ba2 | ||
|
|
be2b2cbf7e | ||
|
|
0e4d85a5e5 | ||
|
|
653fd1bb63 | ||
|
|
741501d1d9 | ||
|
|
89e1291d86 | ||
|
|
25e3674819 | ||
|
|
c84f99cf3a | ||
|
|
820c7ba84e | ||
|
|
6735605f21 | ||
|
|
d5a2daa8c9 | ||
|
|
25177d9022 | ||
|
|
f24ce584db | ||
|
|
1ed83cf193 | ||
|
|
aaf61dfbad | ||
|
|
be58e23775 | ||
|
|
db631acf80 | ||
|
|
74dc4e0c62 | ||
|
|
99bb09374d | ||
|
|
a6e96f9a28 | ||
|
|
cd3c1c6dc0 | ||
|
|
3971f27c66 | ||
|
|
c4357c7ad3 | ||
|
|
1da9385fe2 | ||
|
|
9ba65e9ca0 | ||
|
|
a12bc60ef5 | ||
|
|
f76dc74040 | ||
|
|
932ed20c4b | ||
|
|
b353af00c9 | ||
|
|
ce2204e120 | ||
|
|
4b3a9fac67 | ||
|
|
54ca5772f8 | ||
|
|
2dacf1b2ef | ||
|
|
817e9aa43d | ||
|
|
dba677dcc7 | ||
|
|
24dcde2f87 | ||
|
|
77fbf19a72 | ||
|
|
3cd9d4b5ec | ||
|
|
d3bf489bea | ||
|
|
efb2972d28 | ||
|
|
3fa3428b4b | ||
|
|
84e9e37353 | ||
|
|
5c0f278775 | ||
|
|
8759d637ff | ||
|
|
95023ca770 | ||
|
|
3f49796c43 | ||
|
|
aa8f62da9d | ||
|
|
eba2a98703 | ||
|
|
d36f6a0322 | ||
|
|
9ec3d24fec | ||
|
|
bf182697b9 | ||
|
|
e87084715d | ||
|
|
3bd1bbc77a | ||
|
|
8aaa70a05a | ||
|
|
9b247ce5ed | ||
|
|
09ff556aa6 | ||
|
|
a26d82eec6 | ||
|
|
bb2e683dea | ||
|
|
d7a67a6a1c | ||
|
|
8c6e72b21a | ||
|
|
0b5500fe75 | ||
|
|
2845f48430 | ||
|
|
4ba959e6e1 | ||
|
|
72d9ac508a | ||
|
|
386600baf9 | ||
|
|
eb1825defd | ||
|
|
33069739ee | ||
|
|
dd8c526fb7 | ||
|
|
2701e63406 | ||
|
|
b972da059a | ||
|
|
81862215b4 | ||
|
|
a34e998c42 | ||
|
|
6d1193a751 | ||
|
|
0cd7399dc9 | ||
|
|
f1b0b60340 | ||
|
|
d1cf43f9a4 | ||
|
|
a0b3b1affd | ||
|
|
2a7fdfc832 | ||
|
|
672070e618 | ||
|
|
6a2b1bb48d | ||
|
|
db121c0839 | ||
|
|
ca9db9fd3f | ||
|
|
ecccf673a9 | ||
|
|
e0d7cae3fe | ||
|
|
241526f127 | ||
|
|
4148099115 | ||
|
|
5edf200157 | ||
|
|
bd7ba3acb1 | ||
|
|
e024d9bbb0 | ||
|
|
3d7b8b3162 | ||
|
|
8887272577 | ||
|
|
c86257568f | ||
|
|
ae25538706 | ||
|
|
21e417433b | ||
|
|
cb272be805 | ||
|
|
b79d8d6c82 | ||
|
|
be8aed6a95 | ||
|
|
d06337dddc | ||
|
|
0f3ec47074 | ||
|
|
3a5bad4b7a | ||
|
|
2aecd1035e | ||
|
|
5d04842a80 | ||
|
|
59c73a4814 | ||
|
|
25affe5484 | ||
|
|
399aed4087 | ||
|
|
31dbe2278e | ||
|
|
9ed064b7fc | ||
|
|
a59353df9f | ||
|
|
8acd47bf2f | ||
|
|
be53cb027c | ||
|
|
6ff8c1de05 | ||
|
|
2ebbf062d0 | ||
|
|
fd538bc6c8 | ||
|
|
c0959ceaeb | ||
|
|
6c382c647c | ||
|
|
8f9bed0443 | ||
|
|
dc3996c077 | ||
|
|
5bfd5e4495 | ||
|
|
d646de7184 | ||
|
|
bc2b0f8392 | ||
|
|
f2a7cf5c64 | ||
|
|
0df628dc7a | ||
|
|
a5d1fbff98 | ||
|
|
5cae57601a | ||
|
|
2b7fb7a9a6 | ||
|
|
feb238c5d9 | ||
|
|
0d888eea85 | ||
|
|
bfb6ecbac7 | ||
|
|
2152fe6a79 | ||
|
|
b113696fe6 | ||
|
|
b65a24df96 | ||
|
|
c655bf852f | ||
|
|
be495c17bc | ||
|
|
9785ff4be6 | ||
|
|
5ec37e9112 | ||
|
|
f9f84fd407 | ||
|
|
ed93669693 | ||
|
|
e79ddf2459 | ||
|
|
5efe47cfb6 | ||
|
|
b8045cbcc7 | ||
|
|
aa1090a585 | ||
|
|
0a5589f869 | ||
|
|
383b29dbd8 | ||
|
|
13a9b967e9 | ||
|
|
b798654ca7 | ||
|
|
a5e6890b77 | ||
|
|
2a3a38531b | ||
|
|
4ebf6ebb6f | ||
|
|
6fe736c9fc | ||
|
|
ef682e7023 | ||
|
|
5c3f667fc3 | ||
|
|
a95a055acd | ||
|
|
846499a4fb | ||
|
|
6afb3f70bb | ||
|
|
c063d94aa5 | ||
|
|
261720c941 | ||
|
|
7b3c452316 | ||
|
|
aa00f9bd34 | ||
|
|
b0ff443eac | ||
|
|
a886c598c1 | ||
|
|
2ce4abfdfe | ||
|
|
18c42954ae | ||
|
|
b57b4fa0f8 | ||
|
|
e66cde398a | ||
|
|
3706be77ea | ||
|
|
f481f1e142 | ||
|
|
84b09795f3 | ||
|
|
95954c4b1f | ||
|
|
a56a12a1ef | ||
|
|
5c4b459f57 | ||
|
|
9a616edf2a | ||
|
|
92332b45ea | ||
|
|
5a7fcc3a22 | ||
|
|
6c441353c4 | ||
|
|
b742c95516 | ||
|
|
a59c3da3d0 | ||
|
|
8399f4189f | ||
|
|
67b9fe846b | ||
|
|
0f4ccce0e1 | ||
|
|
01d763eed1 | ||
|
|
41c60419f1 | ||
|
|
195164d9d4 | ||
|
|
518d1da736 | ||
|
|
aade3d4f27 | ||
|
|
22356eb01c | ||
|
|
b5eb88a32f | ||
|
|
6887993f92 | ||
|
|
71b733a018 | ||
|
|
e2eb9cea00 | ||
|
|
99e96a5b13 | ||
|
|
7093254b66 | ||
|
|
f4544b0964 | ||
|
|
c27456277e | ||
|
|
2e824ace00 | ||
|
|
dafa286b18 | ||
|
|
003d01206f | ||
|
|
f0963a332a | ||
|
|
91bdb66f0d | ||
|
|
ffb48c42b0 | ||
|
|
8171828c2a | ||
|
|
a8aa66d191 | ||
|
|
e631d98230 | ||
|
|
1940c67a09 | ||
|
|
c574119718 | ||
|
|
648cd44ddd | ||
|
|
fde8dd9607 | ||
|
|
f1e0cd6c1d | ||
|
|
44df10d6cb | ||
|
|
b6a757842a | ||
|
|
4636c74586 | ||
|
|
2208621050 | ||
|
|
f133dd396c | ||
|
|
ccd04b98b9 | ||
|
|
d37b65e624 | ||
|
|
3e9b811875 | ||
|
|
44c562d8ba | ||
|
|
93c548c013 | ||
|
|
a7d9281768 | ||
|
|
3b369fc98e | ||
|
|
e5cd7e6d40 | ||
|
|
99d05ba967 | ||
|
|
ec9512899e | ||
|
|
26ea6c4e63 | ||
|
|
8e44a7f5c4 | ||
|
|
a093cb6274 | ||
|
|
64f2f330f6 | ||
|
|
473e30e594 | ||
|
|
dc95756ec9 | ||
|
|
2a935868a8 | ||
|
|
f48d8538c0 | ||
|
|
3372dfcd3e | ||
|
|
78d00bcf22 | ||
|
|
9dc9e019f6 | ||
|
|
178c0078c1 | ||
|
|
8478abe378 | ||
|
|
bfc9e43eb4 | ||
|
|
e174025a92 | ||
|
|
89f4408029 | ||
|
|
d7dc277003 | ||
|
|
32bc723745 | ||
|
|
c2ad765424 | ||
|
|
9799afa064 | ||
|
|
e880c14d61 | ||
|
|
e70465c633 | ||
|
|
4ed1835d32 | ||
|
|
19bbccd1a7 | ||
|
|
9d8b80cbce | ||
|
|
dec8264625 | ||
|
|
fe618bd652 | ||
|
|
d208236994 | ||
|
|
4d987f7278 | ||
|
|
193e454fd4 | ||
|
|
bdfb9b4143 | ||
|
|
c9716f3c72 | ||
|
|
7c1704e68b | ||
|
|
771a51224e | ||
|
|
cf275b152a | ||
|
|
98cb85df66 | ||
|
|
032694ad9e | ||
|
|
5437215677 | ||
|
|
5ec80238a0 | ||
|
|
ae6c152988 | ||
|
|
7d15cca1ee | ||
|
|
83c5a67af5 | ||
|
|
c9ad2cd1aa | ||
|
|
fe1f198d99 | ||
|
|
818662c2e6 | ||
|
|
05d0d2a6d6 | ||
|
|
c1a0dad2b7 | ||
|
|
4caf26d069 | ||
|
|
83bc6fb39c | ||
|
|
dbb7568b92 | ||
|
|
45fda44924 | ||
|
|
26f1ade5ba | ||
|
|
9dd93a77a0 | ||
|
|
331d1baad6 | ||
|
|
d3159d86da | ||
|
|
f9e1513491 | ||
|
|
b6e37b7730 | ||
|
|
f9d56eb4c1 | ||
|
|
95565c39ed | ||
|
|
890aacaeee | ||
|
|
e2f0886950 | ||
|
|
fe21b5a502 | ||
|
|
e7043c4d63 | ||
|
|
aae2101131 | ||
|
|
64afed0fb2 | ||
|
|
4d9464ed87 | ||
|
|
b43191506a | ||
|
|
c47781c25a | ||
|
|
f0c4868b3e | ||
|
|
35e5c2329b | ||
|
|
74fc5524ab | ||
|
|
2d4c99a6f7 | ||
|
|
69c73d0a2c | ||
|
|
58510e0208 | ||
|
|
0681d10c51 | ||
|
|
add2356c8a | ||
|
|
c66b2b2291 | ||
|
|
769923c6cc | ||
|
|
27528d084f | ||
|
|
299aa69058 | ||
|
|
d1cc09f40e | ||
|
|
a133b43eed | ||
|
|
9b57725b8c | ||
|
|
34b0f6f014 | ||
|
|
f5cc93ec64 | ||
|
|
4f3263d979 | ||
|
|
b28e374e06 | ||
|
|
918d58ef0a | ||
|
|
5a388d9dde | ||
|
|
0f4909621b | ||
|
|
55d3d8adc3 | ||
|
|
d2d6a6daa4 | ||
|
|
a7f4ac2797 | ||
|
|
25b5027dc7 | ||
|
|
93a967dc74 | ||
|
|
3cfc3dcecf | ||
|
|
e09510ea9f | ||
|
|
4c289fc8fb | ||
|
|
58cf0fa2b1 | ||
|
|
5ca12a73c3 | ||
|
|
fd8e9dad92 | ||
|
|
df0fe0a460 | ||
|
|
646d15b257 | ||
|
|
2eb6848eb8 | ||
|
|
9b3c103f16 | ||
|
|
6a2a13d346 | ||
|
|
b6edf4561d | ||
|
|
c2744700c0 | ||
|
|
f506a5ea6c | ||
|
|
2f702148e3 | ||
|
|
e10c928207 | ||
|
|
a1baa23a52 | ||
|
|
a70e72f75d | ||
|
|
4111da1dd0 | ||
|
|
e7804d014d | ||
|
|
bf87de3706 | ||
|
|
ebc2043055 |
@@ -72,10 +72,6 @@ GOTO:EOF
|
||||
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION
|
||||
)
|
||||
|
||||
echo %BUILD_VERSION% | findstr /C:"disable_unity_integration">nul && (
|
||||
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_UNITY_INTEGRATION
|
||||
)
|
||||
|
||||
echo %BUILD_VERSION% | findstr /C:"disable_gtk_integration">nul && (
|
||||
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
)
|
||||
|
||||
@@ -14,7 +14,6 @@ env:
|
||||
- BUILD_VERSION="disable_crash_reports"
|
||||
- BUILD_VERSION="disable_network_proxy"
|
||||
- BUILD_VERSION="disable_desktop_file_generation"
|
||||
- BUILD_VERSION="disable_unity_integration"
|
||||
- BUILD_VERSION="disable_gtk_integration"
|
||||
|
||||
matrix:
|
||||
@@ -45,7 +44,7 @@ addons:
|
||||
- libopus-dev
|
||||
- libpulse-dev
|
||||
- libssl-dev
|
||||
- libunity-dev
|
||||
- libdee-dev
|
||||
- libva-dev
|
||||
- libvdpau-dev
|
||||
- libxcb-xkb-dev
|
||||
|
||||
@@ -116,10 +116,6 @@ build() {
|
||||
GYP_DEFINES+=",TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION"
|
||||
fi
|
||||
|
||||
if [[ $BUILD_VERSION == *"disable_unity_integration"* ]]; then
|
||||
GYP_DEFINES+=",TDESKTOP_DISABLE_UNITY_INTEGRATION"
|
||||
fi
|
||||
|
||||
if [[ $BUILD_VERSION == *"disable_gtk_integration"* ]]; then
|
||||
GYP_DEFINES+=",TDESKTOP_DISABLE_GTK_INTEGRATION"
|
||||
fi
|
||||
@@ -318,6 +314,7 @@ buildVdpau() {
|
||||
git clone git://anongit.freedesktop.org/vdpau/libvdpau
|
||||
|
||||
cd "$EXTERNAL/libvdpau"
|
||||
git checkout libvdpau-1.2
|
||||
./autogen.sh --prefix=$VDPAU_PATH --enable-static
|
||||
make $MAKE_ARGS
|
||||
sudo make install
|
||||
|
||||
@@ -44,7 +44,7 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
|
||||
|
||||
## Build instructions
|
||||
|
||||
* [Visual Studio 2017][msvc]
|
||||
* [Visual Studio 2019][msvc]
|
||||
* [Xcode 10][xcode]
|
||||
* [GYP/CMake on GNU/Linux][cmake]
|
||||
|
||||
|
||||
@@ -12,7 +12,108 @@ pacman --noconfirm -S pkg-config
|
||||
|
||||
PKG_CONFIG_PATH="/mingw64/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
|
||||
./configure --toolchain=msvc --disable-programs --disable-doc --disable-everything --enable-protocol=file --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=wavpack --enable-decoder=opus --enable-decoder=pcm_alaw --enable-decoder=pcm_alaw_at --enable-decoder=pcm_f32be --enable-decoder=pcm_f32le --enable-decoder=pcm_f64be --enable-decoder=pcm_f64le --enable-decoder=pcm_lxf --enable-decoder=pcm_mulaw --enable-decoder=pcm_mulaw_at --enable-decoder=pcm_s16be --enable-decoder=pcm_s16be_planar --enable-decoder=pcm_s16le --enable-decoder=pcm_s16le_planar --enable-decoder=pcm_s24be --enable-decoder=pcm_s24daud --enable-decoder=pcm_s24le --enable-decoder=pcm_s24le_planar --enable-decoder=pcm_s32be --enable-decoder=pcm_s32le --enable-decoder=pcm_s32le_planar --enable-decoder=pcm_s64be --enable-decoder=pcm_s64le --enable-decoder=pcm_s8 --enable-decoder=pcm_s8_planar --enable-decoder=pcm_u16be --enable-decoder=pcm_u16le --enable-decoder=pcm_u24be --enable-decoder=pcm_u24le --enable-decoder=pcm_u32be --enable-decoder=pcm_u32le --enable-decoder=pcm_u8 --enable-decoder=pcm_zork --enable-decoder=vorbis --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=h264_d3d11va --enable-hwaccel=h264_dxva2 --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus --extra-ldflags="-libpath:$FullExecPath/../opus/win32/VS2015/Win32/Release"
|
||||
./configure --toolchain=msvc \
|
||||
--extra-ldflags="-libpath:$FullExecPath/../opus/win32/VS2015/Win32/Release" \
|
||||
--disable-programs \
|
||||
--disable-doc \
|
||||
--disable-network \
|
||||
--disable-everything \
|
||||
--enable-hwaccel=h264_d3d11va \
|
||||
--enable-hwaccel=h264_d3d11va2 \
|
||||
--enable-hwaccel=h264_dxva2 \
|
||||
--enable-hwaccel=hevc_d3d11va \
|
||||
--enable-hwaccel=hevc_d3d11va2 \
|
||||
--enable-hwaccel=hevc_dxva2 \
|
||||
--enable-hwaccel=mpeg2_d3d11va \
|
||||
--enable-hwaccel=mpeg2_d3d11va2 \
|
||||
--enable-hwaccel=mpeg2_dxva2 \
|
||||
--enable-protocol=file --enable-libopus \
|
||||
--enable-decoder=aac \
|
||||
--enable-decoder=aac_at \
|
||||
--enable-decoder=aac_fixed \
|
||||
--enable-decoder=aac_latm \
|
||||
--enable-decoder=aasc \
|
||||
--enable-decoder=alac \
|
||||
--enable-decoder=alac_at \
|
||||
--enable-decoder=flac \
|
||||
--enable-decoder=gif \
|
||||
--enable-decoder=h264 \
|
||||
--enable-decoder=hevc \
|
||||
--enable-decoder=mp1 \
|
||||
--enable-decoder=mp1float \
|
||||
--enable-decoder=mp2 \
|
||||
--enable-decoder=mp2float \
|
||||
--enable-decoder=mp3 \
|
||||
--enable-decoder=mp3adu \
|
||||
--enable-decoder=mp3adufloat \
|
||||
--enable-decoder=mp3float \
|
||||
--enable-decoder=mp3on4 \
|
||||
--enable-decoder=mp3on4float \
|
||||
--enable-decoder=mpeg4 \
|
||||
--enable-decoder=msmpeg4v2 \
|
||||
--enable-decoder=msmpeg4v3 \
|
||||
--enable-decoder=opus \
|
||||
--enable-decoder=pcm_alaw \
|
||||
--enable-decoder=pcm_alaw_at \
|
||||
--enable-decoder=pcm_f32be \
|
||||
--enable-decoder=pcm_f32le \
|
||||
--enable-decoder=pcm_f64be \
|
||||
--enable-decoder=pcm_f64le \
|
||||
--enable-decoder=pcm_lxf \
|
||||
--enable-decoder=pcm_mulaw \
|
||||
--enable-decoder=pcm_mulaw_at \
|
||||
--enable-decoder=pcm_s16be \
|
||||
--enable-decoder=pcm_s16be_planar \
|
||||
--enable-decoder=pcm_s16le \
|
||||
--enable-decoder=pcm_s16le_planar \
|
||||
--enable-decoder=pcm_s24be \
|
||||
--enable-decoder=pcm_s24daud \
|
||||
--enable-decoder=pcm_s24le \
|
||||
--enable-decoder=pcm_s24le_planar \
|
||||
--enable-decoder=pcm_s32be \
|
||||
--enable-decoder=pcm_s32le \
|
||||
--enable-decoder=pcm_s32le_planar \
|
||||
--enable-decoder=pcm_s64be \
|
||||
--enable-decoder=pcm_s64le \
|
||||
--enable-decoder=pcm_s8 \
|
||||
--enable-decoder=pcm_s8_planar \
|
||||
--enable-decoder=pcm_u16be \
|
||||
--enable-decoder=pcm_u16le \
|
||||
--enable-decoder=pcm_u24be \
|
||||
--enable-decoder=pcm_u24le \
|
||||
--enable-decoder=pcm_u32be \
|
||||
--enable-decoder=pcm_u32le \
|
||||
--enable-decoder=pcm_u8 \
|
||||
--enable-decoder=pcm_zork \
|
||||
--enable-decoder=vorbis \
|
||||
--enable-decoder=wavpack \
|
||||
--enable-decoder=wmalossless \
|
||||
--enable-decoder=wmapro \
|
||||
--enable-decoder=wmav1 \
|
||||
--enable-decoder=wmav2 \
|
||||
--enable-decoder=wmavoice \
|
||||
--enable-encoder=libopus \
|
||||
--enable-parser=aac \
|
||||
--enable-parser=aac_latm \
|
||||
--enable-parser=flac \
|
||||
--enable-parser=h264 \
|
||||
--enable-parser=hevc \
|
||||
--enable-parser=mpeg4video \
|
||||
--enable-parser=mpegaudio \
|
||||
--enable-parser=opus \
|
||||
--enable-parser=vorbis \
|
||||
--enable-demuxer=aac \
|
||||
--enable-demuxer=flac \
|
||||
--enable-demuxer=gif \
|
||||
--enable-demuxer=h264 \
|
||||
--enable-demuxer=hevc \
|
||||
--enable-demuxer=m4v \
|
||||
--enable-demuxer=mov \
|
||||
--enable-demuxer=mp3 \
|
||||
--enable-demuxer=ogg \
|
||||
--enable-demuxer=wav \
|
||||
--enable-muxer=ogg \
|
||||
--enable-muxer=opus
|
||||
|
||||
make -j4
|
||||
make -j4 install
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/mkspecs/common/msvc-desktop.conf b/mkspecs/common/msvc-desktop.conf
|
||||
index eec9e1f..7ae53c7 100644
|
||||
index eec9e1f688..7ae53c7a1e 100644
|
||||
--- a/mkspecs/common/msvc-desktop.conf
|
||||
+++ b/mkspecs/common/msvc-desktop.conf
|
||||
@@ -30,9 +30,10 @@ QMAKE_YACCFLAGS = -d
|
||||
@@ -17,7 +17,7 @@ index eec9e1f..7ae53c7 100644
|
||||
QMAKE_CFLAGS_LTCG = -GL
|
||||
QMAKE_CFLAGS_SSE2 = -arch:SSE2
|
||||
diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp
|
||||
index 391fbcc..d07802b 100644
|
||||
index 391fbcc519..d07802bb7a 100644
|
||||
--- a/src/corelib/io/qfsfileengine_win.cpp
|
||||
+++ b/src/corelib/io/qfsfileengine_win.cpp
|
||||
@@ -427,11 +427,12 @@ qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
|
||||
@@ -36,7 +36,7 @@ index 391fbcc..d07802b 100644
|
||||
// Note: Only return error if the first WriteFile failed.
|
||||
q->setError(QFile::WriteError, qt_error_string());
|
||||
diff --git a/src/corelib/tools/qunicodetables.cpp b/src/corelib/tools/qunicodetables.cpp
|
||||
index 14e4fd1..0619a17 100644
|
||||
index 14e4fd10aa..0619a176a7 100644
|
||||
--- a/src/corelib/tools/qunicodetables.cpp
|
||||
+++ b/src/corelib/tools/qunicodetables.cpp
|
||||
@@ -6227,7 +6227,8 @@ static const Properties uc_properties[] = {
|
||||
@@ -50,7 +50,7 @@ index 14e4fd1..0619a17 100644
|
||||
{ 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 },
|
||||
{ 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 },
|
||||
diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp
|
||||
index 2d00b9d..eeba86e 100644
|
||||
index 2d00b9dce9..eeba86e936 100644
|
||||
--- a/src/gui/kernel/qhighdpiscaling.cpp
|
||||
+++ b/src/gui/kernel/qhighdpiscaling.cpp
|
||||
@@ -51,6 +51,9 @@ static const char screenFactorsEnvVar[] = "QT_SCREEN_SCALE_FACTORS";
|
||||
@@ -64,7 +64,7 @@ index 2d00b9d..eeba86e 100644
|
||||
qreal result = 1;
|
||||
if (qEnvironmentVariableIsSet(scaleFactorEnvVar)) {
|
||||
diff --git a/src/gui/kernel/qplatformdialoghelper.h b/src/gui/kernel/qplatformdialoghelper.h
|
||||
index 5b2f4ec..790db46 100644
|
||||
index 5b2f4ece77..790db46d25 100644
|
||||
--- a/src/gui/kernel/qplatformdialoghelper.h
|
||||
+++ b/src/gui/kernel/qplatformdialoghelper.h
|
||||
@@ -386,6 +386,10 @@ public:
|
||||
@@ -79,7 +79,7 @@ index 5b2f4ec..790db46 100644
|
||||
virtual void selectNameFilter(const QString &filter) = 0;
|
||||
virtual QString selectedNameFilter() const = 0;
|
||||
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
|
||||
index bcd29b6..bcb0672 100644
|
||||
index bcd29b6fe1..bcb0672f69 100644
|
||||
--- a/src/gui/kernel/qwindow.cpp
|
||||
+++ b/src/gui/kernel/qwindow.cpp
|
||||
@@ -2525,7 +2525,8 @@ void QWindowPrivate::setCursor(const QCursor *newCursor)
|
||||
@@ -93,7 +93,7 @@ index bcd29b6..bcb0672 100644
|
||||
QCursor *c = QGuiApplication::overrideCursor();
|
||||
if (!c && hasCursor)
|
||||
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
|
||||
index 918c989..4158259 100644
|
||||
index 918c98997b..4158259743 100644
|
||||
--- a/src/gui/painting/qpaintengine_p.h
|
||||
+++ b/src/gui/painting/qpaintengine_p.h
|
||||
@@ -80,8 +80,18 @@ public:
|
||||
@@ -117,7 +117,7 @@ index 918c989..4158259 100644
|
||||
|
||||
// Make sure we're inside the viewport.
|
||||
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
|
||||
index 7e507bb..936e7a9 100644
|
||||
index 7e507bba2d..936e7a92cb 100644
|
||||
--- a/src/gui/text/qtextengine_p.h
|
||||
+++ b/src/gui/text/qtextengine_p.h
|
||||
@@ -283,7 +283,8 @@ private:
|
||||
@@ -131,7 +131,7 @@ index 7e507bb..936e7a9 100644
|
||||
public:
|
||||
inline QTextItemInt()
|
||||
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
|
||||
index aca475a..5fa0be2 100644
|
||||
index aca475a581..5fa0be2c45 100644
|
||||
--- a/src/gui/text/qtextlayout.cpp
|
||||
+++ b/src/gui/text/qtextlayout.cpp
|
||||
@@ -694,6 +694,9 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
|
||||
@@ -208,7 +208,7 @@ index aca475a..5fa0be2 100644
|
||||
|
||||
static const QFixed RightBearingNotCalculated;
|
||||
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
|
||||
index f74d4d4..8ad672c 100644
|
||||
index f74d4d4229..8ad672c9fe 100644
|
||||
--- a/src/gui/text/qtextlayout.h
|
||||
+++ b/src/gui/text/qtextlayout.h
|
||||
@@ -196,6 +196,9 @@ private:
|
||||
@@ -222,7 +222,7 @@ index f74d4d4..8ad672c 100644
|
||||
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
|
||||
index c4cb8e6..45793e3 100644
|
||||
index c4cb8e65c0..45793e364f 100644
|
||||
--- a/src/network/access/qhttpnetworkconnection.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnection.cpp
|
||||
@@ -110,6 +110,8 @@ QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
|
||||
@@ -235,7 +235,7 @@ index c4cb8e6..45793e3 100644
|
||||
delete channels[i].socket;
|
||||
}
|
||||
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
|
||||
index 41834b2..8cdf4ab 100644
|
||||
index 41834b21ae..8cdf4ab145 100644
|
||||
--- a/src/network/socket/qnativesocketengine_win.cpp
|
||||
+++ b/src/network/socket/qnativesocketengine_win.cpp
|
||||
@@ -675,6 +675,13 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin
|
||||
@@ -252,8 +252,28 @@ index 41834b2..8cdf4ab 100644
|
||||
if (value == WSAEADDRNOTAVAIL) {
|
||||
setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString);
|
||||
socketState = QAbstractSocket::UnconnectedState;
|
||||
diff --git a/src/platformsupport/cglconvenience/cglconvenience.mm b/src/platformsupport/cglconvenience/cglconvenience.mm
|
||||
index fb609ae485..7cca45ded4 100644
|
||||
--- a/src/platformsupport/cglconvenience/cglconvenience.mm
|
||||
+++ b/src/platformsupport/cglconvenience/cglconvenience.mm
|
||||
@@ -128,7 +128,14 @@ void *qcgl_createNSOpenGLPixelFormat(const QSurfaceFormat &format)
|
||||
if (format.stereo())
|
||||
attrs << NSOpenGLPFAStereo;
|
||||
|
||||
- attrs << NSOpenGLPFAAllowOfflineRenderers;
|
||||
+ // Patch: Fix macOS regression. It crashes on GPU switches.
|
||||
+ // See https://bugreports.qt.io/browse/QTCREATORBUG-22215
|
||||
+ static const QAppleOperatingSystemVersion version = qt_apple_os_version();
|
||||
+ if ((version.major < 10)
|
||||
+ || (version.major == 10 && version.minor < 14)
|
||||
+ || (version.major == 10 && version.minor == 14 && version.patch < 4)) {
|
||||
+ attrs << NSOpenGLPFAAllowOfflineRenderers;
|
||||
+ }
|
||||
|
||||
QByteArray useLayer = qgetenv("QT_MAC_WANTS_LAYER");
|
||||
if (!useLayer.isEmpty() && useLayer.toInt() > 0) {
|
||||
diff --git a/src/platformsupport/dbustray/qdbustrayicon.cpp b/src/platformsupport/dbustray/qdbustrayicon.cpp
|
||||
index 4d6e707..9bdb0be 100644
|
||||
index 4d6e70720d..9bdb0beb67 100644
|
||||
--- a/src/platformsupport/dbustray/qdbustrayicon.cpp
|
||||
+++ b/src/platformsupport/dbustray/qdbustrayicon.cpp
|
||||
@@ -58,9 +58,18 @@ QT_BEGIN_NAMESPACE
|
||||
@@ -290,7 +310,7 @@ index 4d6e707..9bdb0be 100644
|
||||
}
|
||||
if (!necessary)
|
||||
diff --git a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp
|
||||
index 728b166..1dc6459 100644
|
||||
index 728b166b71..1dc64593e1 100644
|
||||
--- a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp
|
||||
+++ b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp
|
||||
@@ -172,6 +172,79 @@ void QBasicFontDatabase::releaseHandle(void *handle)
|
||||
@@ -388,7 +408,7 @@ index 728b166..1dc6459 100644
|
||||
if (error != FT_Err_Ok) {
|
||||
qDebug() << "FT_New_Face failed with index" << index << ':' << hex << error;
|
||||
diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
|
||||
index 8ebabf3..7bb8abd 100644
|
||||
index 8ebabf3419..7bb8abd0d0 100644
|
||||
--- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
|
||||
+++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
|
||||
@@ -375,6 +375,17 @@ static void populateFromPattern(FcPattern *pattern)
|
||||
@@ -450,7 +470,7 @@ index 8ebabf3..7bb8abd 100644
|
||||
}
|
||||
populateFromPattern(pattern);
|
||||
diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
|
||||
index 566abf2..5c5fde9 100644
|
||||
index 566abf2126..5c5fde9813 100644
|
||||
--- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
|
||||
+++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
|
||||
@@ -265,6 +265,13 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd)
|
||||
@@ -482,7 +502,7 @@ index 566abf2..5c5fde9 100644
|
||||
if (CFNumberRef italic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSlantTrait)) {
|
||||
double d;
|
||||
diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
|
||||
index 7b45958..2ed2fd9 100644
|
||||
index 7b459584ea..2ed2fd9b3b 100644
|
||||
--- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
|
||||
+++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
|
||||
@@ -764,7 +764,8 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gl
|
||||
@@ -496,7 +516,7 @@ index 7b45958..2ed2fd9 100644
|
||||
|
||||
QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const
|
||||
diff --git a/src/plugins/platforminputcontexts/compose/compose.pro b/src/plugins/platforminputcontexts/compose/compose.pro
|
||||
index 86bdd47..9b9c8de 100644
|
||||
index 86bdd4729b..9b9c8ded08 100644
|
||||
--- a/src/plugins/platforminputcontexts/compose/compose.pro
|
||||
+++ b/src/plugins/platforminputcontexts/compose/compose.pro
|
||||
@@ -15,7 +15,8 @@ HEADERS += $$PWD/qcomposeplatforminputcontext.h \
|
||||
@@ -510,7 +530,7 @@ index 86bdd47..9b9c8de 100644
|
||||
LIBS += $$QMAKE_LIBS_XKBCOMMON
|
||||
QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XKBCOMMON
|
||||
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
|
||||
index d1bea9a..36a15a6 100644
|
||||
index d1bea9af23..36a15a6473 100644
|
||||
--- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
|
||||
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
|
||||
@@ -232,6 +232,12 @@ bool QComposeInputContext::checkComposeTable()
|
||||
@@ -527,7 +547,7 @@ index d1bea9a..36a15a6 100644
|
||||
event.setCommitString(QChar(character));
|
||||
QCoreApplication::sendEvent(m_focusObject, &event);
|
||||
diff --git a/src/plugins/platforminputcontexts/platforminputcontexts.pro b/src/plugins/platforminputcontexts/platforminputcontexts.pro
|
||||
index faea54b..fe4a837 100644
|
||||
index faea54b874..fe4a837511 100644
|
||||
--- a/src/plugins/platforminputcontexts/platforminputcontexts.pro
|
||||
+++ b/src/plugins/platforminputcontexts/platforminputcontexts.pro
|
||||
@@ -1,7 +1,8 @@
|
||||
@@ -541,7 +561,7 @@ index faea54b..fe4a837 100644
|
||||
|
||||
contains(QT_CONFIG, xcb-plugin): SUBDIRS += compose
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
|
||||
index caa8884..9dc3bc1 100644
|
||||
index caa8884661..9dc3bc1661 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
|
||||
@@ -210,7 +210,8 @@ QT_END_NAMESPACE
|
||||
@@ -572,7 +592,7 @@ index caa8884..9dc3bc1 100644
|
||||
}
|
||||
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
|
||||
index 934f68a..3ece698 100644
|
||||
index 934f68ad18..3ece6984ac 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.h
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
|
||||
@@ -64,6 +64,9 @@ public:
|
||||
@@ -586,7 +606,7 @@ index 934f68a..3ece698 100644
|
||||
|
||||
QT_END_NAMESPACE
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
|
||||
index ca92103..f27ea15 100644
|
||||
index ca92103826..f27ea15bad 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
|
||||
@@ -38,7 +38,8 @@
|
||||
@@ -624,7 +644,7 @@ index ca92103..f27ea15 100644
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
const QVector<QRect> rects = region.rects();
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
|
||||
index 058209d..6af61e7 100644
|
||||
index 058209da7e..6af61e7dab 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
|
||||
@@ -546,9 +546,9 @@ OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGIm
|
||||
@@ -654,7 +674,7 @@ index 058209d..6af61e7 100644
|
||||
}
|
||||
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.mm b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
|
||||
index c2d206f..9b97398 100644
|
||||
index c2d206fb45..9b9739862d 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoakeymapper.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
|
||||
@@ -384,6 +384,12 @@ bool QCocoaKeyMapper::updateKeyboard()
|
||||
@@ -681,7 +701,7 @@ index c2d206f..9b97398 100644
|
||||
}
|
||||
return ret;
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
index 8152c57..87ba2f3 100644
|
||||
index 8152c57ffd..87ba2f3f72 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
@@ -94,6 +94,8 @@ QT_USE_NAMESPACE
|
||||
@@ -821,7 +841,7 @@ index 8152c57..87ba2f3 100644
|
||||
}
|
||||
}
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
|
||||
index c0d5904..f3c2047 100644
|
||||
index c0d5904367..f3c2047196 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
|
||||
@@ -141,7 +141,8 @@ static bool isMouseEvent(NSEvent *ev)
|
||||
@@ -883,7 +903,7 @@ index c0d5904..f3c2047 100644
|
||||
[iconButton setImage:image];
|
||||
[image release];
|
||||
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
|
||||
index c67bcfd..2616f42 100644
|
||||
index c67bcfd23b..6a60670aee 100644
|
||||
--- a/src/plugins/platforms/cocoa/qnsview.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qnsview.mm
|
||||
@@ -647,6 +647,12 @@ QT_WARNING_POP
|
||||
@@ -957,8 +977,19 @@ index c67bcfd..2616f42 100644
|
||||
- (void)cancelOperation:(id)sender
|
||||
{
|
||||
Q_UNUSED(sender);
|
||||
@@ -1981,6 +2006,10 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
|
||||
// change the cursor
|
||||
[nativeCursor set];
|
||||
|
||||
+ // Patch: Backport a fix from cd08753d3e. Starting with macOS Mojave this requires accessibility access.
|
||||
+ if (QSysInfo::macVersion() >= Q_MV_OSX(10, 14))
|
||||
+ return;
|
||||
+
|
||||
// Make sure the cursor is updated correctly if the mouse does not move and window is under cursor
|
||||
// by creating a fake move event
|
||||
if (m_updatingDrag)
|
||||
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
|
||||
index 94bb71e..16ab51e 100644
|
||||
index 94bb71e429..16ab51e166 100644
|
||||
--- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
|
||||
+++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
|
||||
@@ -716,12 +716,20 @@ public:
|
||||
@@ -1163,7 +1194,7 @@ index 94bb71e..16ab51e 100644
|
||||
{
|
||||
m_data.setSelectedNameFilter(f); // Dialog cannot be updated at run-time.
|
||||
diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp
|
||||
index 1e58b9b..1741c21 100644
|
||||
index 1e58b9b3d4..1741c21a1c 100644
|
||||
--- a/src/plugins/platforms/windows/qwindowskeymapper.cpp
|
||||
+++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp
|
||||
@@ -1268,6 +1268,10 @@ QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const
|
||||
@@ -1178,7 +1209,7 @@ index 1e58b9b..1741c21 100644
|
||||
if (!kbItem.exists)
|
||||
return result;
|
||||
diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp
|
||||
index 1d23a9d..640cd42 100644
|
||||
index 1d23a9d9b9..640cd426ed 100644
|
||||
--- a/src/plugins/platforms/windows/qwindowsservices.cpp
|
||||
+++ b/src/plugins/platforms/windows/qwindowsservices.cpp
|
||||
@@ -127,6 +127,10 @@ static inline bool launchMail(const QUrl &url)
|
||||
@@ -1193,7 +1224,7 @@ index 1d23a9d..640cd42 100644
|
||||
// but that cannot handle a Windows command line [yet].
|
||||
command.replace(QStringLiteral("%1"), url.toString(QUrl::FullyEncoded));
|
||||
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
|
||||
index b38d7c2..34f19c4 100644
|
||||
index b38d7c29ae..34f19c4efa 100644
|
||||
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
|
||||
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
|
||||
@@ -1020,7 +1020,8 @@ void QWindowsWindow::destroyWindow()
|
||||
@@ -1247,7 +1278,7 @@ index b38d7c2..34f19c4 100644
|
||||
break;
|
||||
}
|
||||
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
|
||||
index 6fffa1e..cb1c9c1 100644
|
||||
index 6fffa1e6e9..cb1c9c1161 100644
|
||||
--- a/src/plugins/platforms/windows/qwindowswindow.h
|
||||
+++ b/src/plugins/platforms/windows/qwindowswindow.h
|
||||
@@ -265,6 +265,10 @@ private:
|
||||
@@ -1262,7 +1293,7 @@ index 6fffa1e..cb1c9c1 100644
|
||||
inline bool isDropSiteEnabled() const { return m_dropTarget != 0; }
|
||||
void setDropSiteEnabled(bool enabled);
|
||||
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
|
||||
index 09e7ecf..c0f15a4 100644
|
||||
index 09e7ecf3a3..c0f15a4242 100644
|
||||
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
|
||||
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
|
||||
@@ -79,7 +79,10 @@ static int resourceType(const QByteArray &key)
|
||||
@@ -1292,7 +1323,7 @@ index 09e7ecf..c0f15a4 100644
|
||||
break;
|
||||
}
|
||||
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h
|
||||
index f88b710..6f818a5 100644
|
||||
index f88b710864..6f818a5a72 100644
|
||||
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h
|
||||
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h
|
||||
@@ -68,7 +68,10 @@ public:
|
||||
@@ -1308,7 +1339,7 @@ index f88b710..6f818a5 100644
|
||||
|
||||
QXcbNativeInterface();
|
||||
diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp
|
||||
index bc2de89..aa8f8df 100644
|
||||
index bc2de899f5..aa8f8df4ad 100644
|
||||
--- a/src/widgets/dialogs/qfiledialog.cpp
|
||||
+++ b/src/widgets/dialogs/qfiledialog.cpp
|
||||
@@ -1200,6 +1200,15 @@ QList<QUrl> QFileDialogPrivate::userSelectedFiles() const
|
||||
@@ -1343,7 +1374,7 @@ index bc2de89..aa8f8df 100644
|
||||
Returns a list of urls containing the selected files in the dialog.
|
||||
If no files are selected, or the mode is not ExistingFiles or
|
||||
diff --git a/src/widgets/dialogs/qfiledialog.h b/src/widgets/dialogs/qfiledialog.h
|
||||
index ffe49a2..42dc563 100644
|
||||
index ffe49a2dd2..42dc563c8a 100644
|
||||
--- a/src/widgets/dialogs/qfiledialog.h
|
||||
+++ b/src/widgets/dialogs/qfiledialog.h
|
||||
@@ -108,6 +108,9 @@ public:
|
||||
@@ -1357,7 +1388,7 @@ index ffe49a2..42dc563 100644
|
||||
QList<QUrl> selectedUrls() const;
|
||||
|
||||
diff --git a/src/widgets/dialogs/qfiledialog_p.h b/src/widgets/dialogs/qfiledialog_p.h
|
||||
index f610e46..547a646 100644
|
||||
index f610e46f83..547a64695a 100644
|
||||
--- a/src/widgets/dialogs/qfiledialog_p.h
|
||||
+++ b/src/widgets/dialogs/qfiledialog_p.h
|
||||
@@ -123,6 +123,10 @@ public:
|
||||
@@ -1398,7 +1429,7 @@ index f610e46..547a646 100644
|
||||
{
|
||||
if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
|
||||
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
|
||||
index b1d80d7..42e32fd 100644
|
||||
index b1d80d7b8f..42e32fd404 100644
|
||||
--- a/src/widgets/kernel/qwidget.cpp
|
||||
+++ b/src/widgets/kernel/qwidget.cpp
|
||||
@@ -5138,6 +5138,17 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
|
||||
@@ -1451,7 +1482,7 @@ index b1d80d7..42e32fd 100644
|
||||
|| (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))
|
||||
res = focusNextPrevChild(false);
|
||||
diff --git a/src/widgets/util/qsystemtrayicon.cpp b/src/widgets/util/qsystemtrayicon.cpp
|
||||
index 704142f..7c4340e 100644
|
||||
index 704142fe5c..7c4340e459 100644
|
||||
--- a/src/widgets/util/qsystemtrayicon.cpp
|
||||
+++ b/src/widgets/util/qsystemtrayicon.cpp
|
||||
@@ -709,6 +709,10 @@ void QSystemTrayIconPrivate::updateMenu_sys_qpa()
|
||||
@@ -1466,7 +1497,7 @@ index 704142f..7c4340e 100644
|
||||
}
|
||||
|
||||
diff --git a/src/widgets/widgets/qabstractscrollarea.cpp b/src/widgets/widgets/qabstractscrollarea.cpp
|
||||
index 2e2a042..472e377 100644
|
||||
index 2e2a042bf1..472e37722b 100644
|
||||
--- a/src/widgets/widgets/qabstractscrollarea.cpp
|
||||
+++ b/src/widgets/widgets/qabstractscrollarea.cpp
|
||||
@@ -640,15 +640,22 @@ scrolling range.
|
||||
@@ -1497,7 +1528,7 @@ index 2e2a042..472e377 100644
|
||||
}
|
||||
|
||||
diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp
|
||||
index daf9f00..57499dc 100644
|
||||
index daf9f00c46..57499dc4a4 100644
|
||||
--- a/src/widgets/widgets/qwidgetlinecontrol.cpp
|
||||
+++ b/src/widgets/widgets/qwidgetlinecontrol.cpp
|
||||
@@ -40,6 +40,11 @@
|
||||
@@ -1539,7 +1570,7 @@ index daf9f00..57499dc 100644
|
||||
#ifndef QT_NO_COMPLETER
|
||||
complete(event->key());
|
||||
diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp
|
||||
index deca002..8a2023f 100644
|
||||
index deca002bf5..8a2023f503 100644
|
||||
--- a/src/widgets/widgets/qwidgettextcontrol.cpp
|
||||
+++ b/src/widgets/widgets/qwidgettextcontrol.cpp
|
||||
@@ -71,6 +71,11 @@
|
||||
|
||||
@@ -92,7 +92,7 @@ msgServiceFont: semiboldFont;
|
||||
msgServiceNameFont: semiboldFont;
|
||||
msgServicePhotoWidth: 100px;
|
||||
msgDateFont: font(13px);
|
||||
msgMinWidth: 190px;
|
||||
msgMinWidth: 160px;
|
||||
msgPhotoSize: 33px;
|
||||
msgPhotoSkip: 40px;
|
||||
msgPadding: margins(13px, 7px, 13px, 8px);
|
||||
@@ -294,4 +294,7 @@ notifyFadeRight: icon {{ "fade_horizontal", notificationBg }};
|
||||
stickerIconLeft: icon {{ "fade_horizontal-flip_horizontal", emojiPanCategories }};
|
||||
stickerIconRight: icon {{ "fade_horizontal", emojiPanCategories }};
|
||||
|
||||
emojiSuggestionsFadeLeft: icon {{ "fade_horizontal-flip_horizontal", boxBg }};
|
||||
emojiSuggestionsFadeRight: icon {{ "fade_horizontal", boxBg }};
|
||||
|
||||
transparentPlaceholderSize: 4px;
|
||||
|
||||
BIN
Telegram/Resources/icons/dialogs_bot.png
Normal file
|
After Width: | Height: | Size: 277 B |
BIN
Telegram/Resources/icons/dialogs_bot@2x.png
Normal file
|
After Width: | Height: | Size: 482 B |
BIN
Telegram/Resources/icons/dialogs_bot@3x.png
Normal file
|
After Width: | Height: | Size: 715 B |
|
Before Width: | Height: | Size: 807 B After Width: | Height: | Size: 327 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 655 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 946 B |
BIN
Telegram/Resources/icons/history_audio_cancel.png
Normal file
|
After Width: | Height: | Size: 153 B |
BIN
Telegram/Resources/icons/history_audio_cancel@2x.png
Normal file
|
After Width: | Height: | Size: 251 B |
BIN
Telegram/Resources/icons/history_audio_cancel@3x.png
Normal file
|
After Width: | Height: | Size: 487 B |
BIN
Telegram/Resources/icons/history_audio_download.png
Normal file
|
After Width: | Height: | Size: 160 B |
BIN
Telegram/Resources/icons/history_audio_download@2x.png
Normal file
|
After Width: | Height: | Size: 221 B |
BIN
Telegram/Resources/icons/history_audio_download@3x.png
Normal file
|
After Width: | Height: | Size: 453 B |
|
Before Width: | Height: | Size: 551 B After Width: | Height: | Size: 399 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 829 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 341 B |
|
Before Width: | Height: | Size: 424 B After Width: | Height: | Size: 650 B |
|
Before Width: | Height: | Size: 680 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 872 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 395 B |
|
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 737 B |
|
Before Width: | Height: | Size: 933 B After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 763 B |
|
Before Width: | Height: | Size: 923 B After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/overview_video_download.png
Normal file
|
After Width: | Height: | Size: 156 B |
BIN
Telegram/Resources/icons/overview_video_download@2x.png
Normal file
|
After Width: | Height: | Size: 221 B |
BIN
Telegram/Resources/icons/overview_video_download@3x.png
Normal file
|
After Width: | Height: | Size: 438 B |
BIN
Telegram/Resources/icons/overview_video_play.png
Normal file
|
After Width: | Height: | Size: 175 B |
BIN
Telegram/Resources/icons/overview_video_play@2x.png
Normal file
|
After Width: | Height: | Size: 321 B |
BIN
Telegram/Resources/icons/overview_video_play@3x.png
Normal file
|
After Width: | Height: | Size: 422 B |
|
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 251 B |
|
Before Width: | Height: | Size: 747 B After Width: | Height: | Size: 528 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 722 B |
|
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 401 B |
|
Before Width: | Height: | Size: 905 B After Width: | Height: | Size: 877 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -141,12 +141,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_error_cant_ban_admin" = "Sorry, you can't ban this user because they are an admin in this group and you are not allowed to demote them.";
|
||||
"lng_error_admin_limit" = "Sorry, you've reached the maximum number of admins for this group.";
|
||||
"lng_error_admin_limit_channel" = "Sorry, you've reached the maximum number of admins for this channel.";
|
||||
"lng_error_post_link_invalid" = "Unfortunately, you can't access this message. You are not a member of the chat where it was posted.";
|
||||
"lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?";
|
||||
"lng_sure_add_admin_invite_channel" = "This user is not a subscriber of this channel. Add them to the channel and promote them to admin?";
|
||||
"lng_sure_add_admin_unremove" = "This user is currently restricted or removed. Are you sure you want to promote them?";
|
||||
"lng_sure_ban_admin" = "This user is an admin. Are you sure you want to go ahead and restrict them?";
|
||||
"lng_sure_remove_user_group" = "Remove {user} from the group?";
|
||||
"lng_sure_remove_user_channel" = "Remove {user} from the channel?";
|
||||
"lng_sure_enable_socks" = "Are you sure you want to enable this proxy?\n\nServer: {server}\nPort: {port}\n\nYou can change your proxy server later in the Settings (Connection Type).";
|
||||
"lng_sure_enable" = "Enable";
|
||||
|
||||
@@ -164,6 +163,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_pinned_unpin" = "Unpin";
|
||||
"lng_pinned_notify" = "Notify all members";
|
||||
|
||||
"lng_edit_media_album_error" = "This file cannot be saved as a part of an album.";
|
||||
"lng_edit_media_invalid_file" = "Sorry, no way to use this file.";
|
||||
|
||||
"lng_intro_about" = "Welcome to the official Telegram Desktop app.\nIt's fast and secure.";
|
||||
"lng_start_msgs" = "START MESSAGING";
|
||||
|
||||
@@ -361,6 +363,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_calls_peer_to_peer" = "Peer-to-peer in calls";
|
||||
"lng_settings_groups_invite" = "Groups";
|
||||
"lng_settings_group_privacy_about" = "Change who can add you to groups and channels.";
|
||||
"lng_settings_forwards_privacy" = "Forwarded messages";
|
||||
"lng_settings_profile_photo_privacy" = "Profile photo";
|
||||
"lng_settings_sessions_about" = "Control your sessions on other devices.";
|
||||
"lng_settings_passcode_disable" = "Disable passcode";
|
||||
"lng_settings_password_disable" = "Disable cloud password";
|
||||
@@ -398,10 +402,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_theme_keep_changes" = "Keep changes";
|
||||
"lng_theme_revert" = "Revert";
|
||||
"lng_background_header" = "Background preview";
|
||||
"lng_background_text1" = "You can't swipe left or right to preview anything - this is tdesktop, sorry.";
|
||||
"lng_background_text2" = "Sounds awful.";
|
||||
"lng_background_text1" = "Ah, you kids today with techno music! You should enjoy the classics, like Hasselhoff!";
|
||||
"lng_background_text2" = "I can't even take you seriously right now.";
|
||||
"lng_background_bad_link" = "This background link appears to be invalid.";
|
||||
"lng_background_apply" = "Apply";
|
||||
"lng_background_share" = "Share";
|
||||
"lng_background_link_copied" = "Link copied to clipboard";
|
||||
"lng_background_blur" = "Blurred";
|
||||
"lng_background_sure_delete" = "Are you sure you want to delete this background?";
|
||||
|
||||
"lng_download_path_ask" = "Ask download path for each file";
|
||||
"lng_download_path" = "Download path";
|
||||
@@ -434,7 +442,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_local_storage_round#other" = "{count} video messages";
|
||||
"lng_local_storage_animation#one" = "{count} animation";
|
||||
"lng_local_storage_animation#other" = "{count} animations";
|
||||
"lng_local_storage_media" = "Media cache";
|
||||
"lng_local_storage_size_limit" = "Total size limit: {size}";
|
||||
"lng_local_storage_media_limit" = "Media cache limit: {size}";
|
||||
"lng_local_storage_time_limit" = "Clear files older than: {limit}";
|
||||
"lng_local_storage_limit_weeks#one" = "{count} week";
|
||||
"lng_local_storage_limit_weeks#other" = "{count} weeks";
|
||||
@@ -636,6 +646,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_privacy_calls_p2p_contacts" = "My contacts";
|
||||
"lng_edit_privacy_calls_p2p_nobody" = "Nobody";
|
||||
|
||||
"lng_edit_privacy_forwards_title" = "Forwarded messages privacy";
|
||||
"lng_edit_privacy_forwards_header" = "Who can add link to my account when forwarding my messages";
|
||||
"lng_edit_privacy_forwards_warning" = "When forwarded to other chats, messages you send will not link back to your account.";
|
||||
"lng_edit_privacy_forwards_always_empty" = "Always allow";
|
||||
"lng_edit_privacy_forwards_never_empty" = "Never allow";
|
||||
"lng_edit_privacy_forwards_exceptions" = "These settings will override the values above.";
|
||||
"lng_edit_privacy_forwards_always_title" = "Always allow";
|
||||
"lng_edit_privacy_forwards_never_title" = "Never allow";
|
||||
"lng_edit_privacy_forwards_sample_message" = "Reinhardt, we need to find you some new tunes 🎶";
|
||||
"lng_edit_privacy_forwards_sample_everyone" = "Link to your account.";
|
||||
"lng_edit_privacy_forwards_sample_contacts" = "Link if allowed by settings below.";
|
||||
"lng_edit_privacy_forwards_sample_nobody" = "Not a link to your account.";
|
||||
|
||||
"lng_edit_privacy_profile_photo_title" = "Profile photo privacy";
|
||||
"lng_edit_privacy_profile_photo_header" = "Who can see my profile photo";
|
||||
"lng_edit_privacy_profile_photo_always_empty" = "Always allow";
|
||||
"lng_edit_privacy_profile_photo_never_empty" = "Never allow";
|
||||
"lng_edit_privacy_profile_photo_exceptions" = "These settings will override the values above.";
|
||||
"lng_edit_privacy_profile_photo_always_title" = "Always allow";
|
||||
"lng_edit_privacy_profile_photo_never_title" = "Never allow";
|
||||
|
||||
"lng_self_destruct_title" = "Account self-destruction";
|
||||
"lng_self_destruct_description" = "If you don't come online at least once within this period, your account will be deleted along with all groups, messages and contacts.";
|
||||
"lng_self_destruct_months#one" = "{count} month";
|
||||
@@ -799,6 +830,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_manage_peer_exceptions" = "Exceptions";
|
||||
"lng_manage_peer_removed_users" = "Removed users";
|
||||
"lng_manage_peer_permissions" = "Permissions";
|
||||
|
||||
"lng_manage_peer_group_type" = "Group type";
|
||||
"lng_manage_peer_channel_type" = "Channel type";
|
||||
"lng_manage_private_peer_title" = "Private";
|
||||
"lng_manage_public_peer_title" = "Public";
|
||||
|
||||
"lng_manage_history_visibility_title" = "Chat history for new members";
|
||||
"lng_manage_history_visibility_shown" = "Visible";
|
||||
"lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined.";
|
||||
@@ -812,6 +849,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_report_message_title" = "Report message";
|
||||
"lng_report_reason_spam" = "Spam";
|
||||
"lng_report_reason_violence" = "Violence";
|
||||
"lng_report_reason_child_abuse" = "Child Abuse";
|
||||
"lng_report_reason_pornography" = "Pornography";
|
||||
"lng_report_reason_other" = "Other";
|
||||
"lng_report_reason_description" = "Description";
|
||||
@@ -856,6 +894,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_create_private_group_about" = "People can only join if they were invited or have an invite link";
|
||||
"lng_create_group_skip" = "Skip";
|
||||
|
||||
"lng_create_channel_link_about" = "You can use a-z, 0-9 and underscores.\nMinimum length is 5 characters.";
|
||||
|
||||
"lng_create_channel_link_invalid" = "This link is invalid";
|
||||
"lng_create_channel_link_occupied" = "Sorry, this link is already occupied";
|
||||
"lng_create_channel_link_too_short" = "Sorry, this link is too short";
|
||||
@@ -991,6 +1031,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_invite_no_room" = "Unable to join this group because there are too many members in it already.";
|
||||
|
||||
"lng_channel_public_link_copied" = "Link copied to clipboard.";
|
||||
"lng_context_about_private_link" = "This link will only work for members of this chat.";
|
||||
|
||||
"lng_forwarded" = "Forwarded from {user}";
|
||||
"lng_forwarded_date" = "Original: {date}";
|
||||
@@ -998,6 +1039,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_forwarded_via" = "Forwarded from {user} via {inline_bot}";
|
||||
"lng_forwarded_channel_via" = "Forwarded from {channel} via {inline_bot}";
|
||||
"lng_forwarded_signed" = "{channel} ({user})";
|
||||
"lng_forwarded_hidden" = "The account was hidden by the user.";
|
||||
"lng_signed_author" = "Author: {user}";
|
||||
"lng_in_reply_to" = "In reply to";
|
||||
"lng_edited" = "edited";
|
||||
"lng_edited_date" = "Edited: {date}";
|
||||
@@ -1035,6 +1078,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_media_animation_title" = "Animated GIFs";
|
||||
"lng_media_size_limit" = "Limit by size";
|
||||
"lng_media_size_up_to" = "up to {size}";
|
||||
"lng_media_chat_background" = "Chat background";
|
||||
|
||||
"lng_emoji_category1" = "People";
|
||||
"lng_emoji_category2" = "Nature";
|
||||
@@ -1091,6 +1135,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_stickers_count#one" = "{count} sticker";
|
||||
"lng_stickers_count#other" = "{count} stickers";
|
||||
"lng_stickers_masks_pack" = "This is a pack of mask stickers. You can use them in the photo editor on our mobile apps.";
|
||||
"lng_stickers_attached_sets" = "Sets of attached stickers";
|
||||
"lng_stickers_group_set" = "Group sticker set";
|
||||
"lng_stickers_remove_group_set" = "Remove group sticker set?";
|
||||
"lng_stickers_group_from_your" = "Choose from your stickers";
|
||||
@@ -1215,6 +1260,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_duration_played" = "{played} / {duration}";
|
||||
"lng_date_and_duration" = "{date}, {duration}";
|
||||
"lng_choose_image" = "Choose an image";
|
||||
"lng_choose_file" = "Choose a file";
|
||||
"lng_choose_files" = "Choose files";
|
||||
"lng_game_tag" = "Game";
|
||||
|
||||
@@ -1252,6 +1298,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_copy_text" = "Copy Text";
|
||||
"lng_context_open_gif" = "Open GIF";
|
||||
"lng_context_save_gif" = "Save GIF";
|
||||
"lng_context_attached_stickers" = "Attached Stickers";
|
||||
"lng_context_to_msg" = "Go To Message";
|
||||
"lng_context_reply_msg" = "Reply";
|
||||
"lng_context_edit_msg" = "Edit";
|
||||
@@ -1350,8 +1397,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_delete_for_me_chat_hint#other" = "This will delete them just for you, not for other participants of the chat.";
|
||||
"lng_delete_for_me_hint#one" = "This will delete it just for you.";
|
||||
"lng_delete_for_me_hint#other" = "This will delete them just for you.";
|
||||
"lng_selected_unsend_about_user_one" = "You can also delete the message you sent from {user}'s inbox by checking \"Unsend my messages\".";
|
||||
"lng_selected_unsend_about_user#one" = "You can also delete the {count} message you sent from {user}'s inbox by checking \"Unsend my messages\".";
|
||||
"lng_selected_unsend_about_user#other" = "You can also delete the {count} messages you sent from {user}'s inbox by checking \"Unsend my messages\".";
|
||||
"lng_selected_unsend_about_group_one" = "You can also delete the message you sent from the inboxes of other group members by checking \"Unsend my messages\".";
|
||||
"lng_selected_unsend_about_group#one" = "You can also delete the {count} message you sent from the inboxes of other group members by checking \"Unsend my messages\".";
|
||||
"lng_selected_unsend_about_group#other" = "You can also delete the {count} messages you sent from the inboxes of other group members by checking \"Unsend my messages\".";
|
||||
"lng_delete_for_everyone_check" = "Delete for everyone";
|
||||
"lng_delete_for_other_check" = "Delete for {user}";
|
||||
"lng_delete_for_other_check" = "Also delete for {user}";
|
||||
"lng_delete_for_other_my" = "Unsend my messages";
|
||||
"lng_box_delete" = "Delete";
|
||||
"lng_box_leave" = "Leave";
|
||||
|
||||
@@ -1406,7 +1460,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_menu_formatting_link_edit" = "Edit link";
|
||||
"lng_menu_formatting_clear" = "Plain text";
|
||||
"lng_formatting_link_create_title" = "Create link";
|
||||
"lng_formatting_link_edit_title" = "Create link";
|
||||
"lng_formatting_link_edit_title" = "Edit link";
|
||||
"lng_formatting_link_text" = "Text";
|
||||
"lng_formatting_link_url" = "URL";
|
||||
"lng_formatting_link_create" = "Create";
|
||||
@@ -1484,6 +1538,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_player_message_today" = "Today at {time}";
|
||||
"lng_player_message_yesterday" = "Yesterday at {time}";
|
||||
"lng_player_message_date" = "{date} at {time}";
|
||||
//"lng_player_cant_stream" = "This file can't be played before it is fully downloaded.\n\nWould you like to download it?";
|
||||
"lng_player_download" = "Download";
|
||||
|
||||
"lng_rights_edit_admin" = "Manage permissions";
|
||||
"lng_rights_edit_admin_header" = "What can this admin do?";
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
52;MX;Mexico;
|
||||
51;PE;Peru;51 XXX XXX XXX;11;
|
||||
49;DE;Germany;49 XXX XXXXXXXX;13;
|
||||
48;PL;Poland;48 XX XXX XXXX;11;
|
||||
48;PL;Poland;48 XXX XXX XXX;11;
|
||||
47;NO;Norway;47 XXXX XXXX;10;
|
||||
46;SE;Sweden;46 XX XXX XXXX;11;
|
||||
45;DK;Denmark;45 XXXX XXXX;10;
|
||||
|
||||
@@ -191,9 +191,12 @@ inputPhoto#3bb3b94a id:long access_hash:long file_reference:bytes = InputPhoto;
|
||||
|
||||
inputFileLocation#dfdaabe1 volume_id:long local_id:int secret:long file_reference:bytes = InputFileLocation;
|
||||
inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
|
||||
inputDocumentFileLocation#196683d9 id:long access_hash:long file_reference:bytes = InputFileLocation;
|
||||
inputDocumentFileLocation#bad07584 id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation;
|
||||
inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
|
||||
inputTakeoutFileLocation#29be5899 = InputFileLocation;
|
||||
inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation;
|
||||
inputPeerPhotoFileLocation#27d69997 flags:# big:flags.0?true peer:InputPeer volume_id:long local_id:int = InputFileLocation;
|
||||
inputStickerSetThumb#dbaeae9 stickerset:InputStickerSet volume_id:long local_id:int = InputFileLocation;
|
||||
|
||||
peerUser#9db1bc6d user_id:int = Peer;
|
||||
peerChat#bad0e5bb chat_id:int = Peer;
|
||||
@@ -210,14 +213,11 @@ storage.fileMov#4b09ebbc = storage.FileType;
|
||||
storage.fileMp4#b3cea0e4 = storage.FileType;
|
||||
storage.fileWebp#1081464c = storage.FileType;
|
||||
|
||||
fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation;
|
||||
fileLocation#91d11eb dc_id:int volume_id:long local_id:int secret:long file_reference:bytes = FileLocation;
|
||||
|
||||
userEmpty#200250ba id:int = User;
|
||||
user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
|
||||
user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
|
||||
|
||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||
userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto;
|
||||
userProfilePhoto#ecd75d8c photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto;
|
||||
|
||||
userStatusEmpty#9d05049 = UserStatus;
|
||||
userStatusOnline#edb93949 expires:int = UserStatus;
|
||||
@@ -243,7 +243,7 @@ chatParticipantsForbidden#fc900c2b flags:# chat_id:int self_participant:flags.0?
|
||||
chatParticipants#3f460fed chat_id:int participants:Vector<ChatParticipant> version:int = ChatParticipants;
|
||||
|
||||
chatPhotoEmpty#37c1011c = ChatPhoto;
|
||||
chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
|
||||
chatPhoto#475cdbd5 photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
|
||||
|
||||
messageEmpty#83e5de54 id:int = Message;
|
||||
message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
|
||||
@@ -278,7 +278,7 @@ messageActionHistoryClear#9fbab604 = MessageAction;
|
||||
messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
|
||||
messageActionPaymentSentMe#8f31b327 flags:# currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
|
||||
messageActionPaymentSent#40699cd0 currency:string total_amount:long = MessageAction;
|
||||
messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
|
||||
messageActionPhoneCall#80e11a7f flags:# video:flags.2?true call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
|
||||
messageActionScreenshotTaken#4792929b = MessageAction;
|
||||
messageActionCustomAction#fae69f56 message:string = MessageAction;
|
||||
messageActionBotAllowed#abe9affe domain:string = MessageAction;
|
||||
@@ -289,7 +289,7 @@ messageActionContactSignUp#f3f25f76 = MessageAction;
|
||||
dialog#e4def5db flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
|
||||
|
||||
photoEmpty#2331b22d id:long = Photo;
|
||||
photo#9c477dd8 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> = Photo;
|
||||
photo#d07504a5 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> dc_id:int = Photo;
|
||||
|
||||
photoSizeEmpty#e17e23c type:string = PhotoSize;
|
||||
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
|
||||
@@ -318,7 +318,7 @@ peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bo
|
||||
|
||||
peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings;
|
||||
|
||||
wallPaper#f04f91ec id:long flags:# creator:flags.0?true default:flags.1?true access_hash:long slug:string document:Document = WallPaper;
|
||||
wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper;
|
||||
|
||||
inputReportReasonSpam#58dbcab8 = ReportReason;
|
||||
inputReportReasonViolence#1e22c78d = ReportReason;
|
||||
@@ -446,7 +446,7 @@ updateContactsReset#7084a7be = Update;
|
||||
updateChannelAvailableMessages#70db6837 channel_id:int available_min_id:int = Update;
|
||||
updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update;
|
||||
updateUserPinnedMessage#4c43da18 user_id:int id:int = Update;
|
||||
updateChatPinnedMessage#22893b26 chat_id:int id:int = Update;
|
||||
updateChatPinnedMessage#e10db349 chat_id:int id:int version:int = Update;
|
||||
updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update;
|
||||
updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update;
|
||||
|
||||
@@ -542,11 +542,15 @@ inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey;
|
||||
inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey;
|
||||
inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey;
|
||||
inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey;
|
||||
inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey;
|
||||
inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey;
|
||||
|
||||
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
|
||||
privacyKeyChatInvite#500e6dfa = PrivacyKey;
|
||||
privacyKeyPhoneCall#3d662b7b = PrivacyKey;
|
||||
privacyKeyPhoneP2P#39491cc8 = PrivacyKey;
|
||||
privacyKeyForwards#69ec56a3 = PrivacyKey;
|
||||
privacyKeyProfilePhoto#96151fed = PrivacyKey;
|
||||
|
||||
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
|
||||
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
|
||||
@@ -612,13 +616,13 @@ chatInviteEmpty#69df3769 = ExportedChatInvite;
|
||||
chatInviteExported#fc2e05bc link:string = ExportedChatInvite;
|
||||
|
||||
chatInviteAlready#5a686d7c chat:Chat = ChatInvite;
|
||||
chatInvite#db74f558 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:ChatPhoto participants_count:int participants:flags.4?Vector<User> = ChatInvite;
|
||||
chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector<User> = ChatInvite;
|
||||
|
||||
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
|
||||
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
|
||||
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
|
||||
|
||||
stickerSet#5585a139 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet;
|
||||
stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
|
||||
|
||||
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
|
||||
|
||||
@@ -726,7 +730,7 @@ messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_off
|
||||
|
||||
exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink;
|
||||
|
||||
messageFwdHeader#559ebe6d flags:# from_id:flags.0?int date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader;
|
||||
messageFwdHeader#ec338270 flags:# from_id:flags.0?int from_name:flags.5?string date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader;
|
||||
|
||||
auth.codeTypeSms#72a3158c = auth.CodeType;
|
||||
auth.codeTypeCall#741cd3e3 = auth.CodeType;
|
||||
@@ -894,11 +898,11 @@ inputStickerSetItem#ffa0a496 flags:# document:InputDocument emoji:string mask_co
|
||||
inputPhoneCall#1e36fded id:long access_hash:long = InputPhoneCall;
|
||||
|
||||
phoneCallEmpty#5366c915 id:long = PhoneCall;
|
||||
phoneCallWaiting#1b8f4ad1 flags:# id:long access_hash:long date:int admin_id:int participant_id:int protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall;
|
||||
phoneCallRequested#83761ce4 id:long access_hash:long date:int admin_id:int participant_id:int g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall;
|
||||
phoneCallAccepted#6d003d3f id:long access_hash:long date:int admin_id:int participant_id:int g_b:bytes protocol:PhoneCallProtocol = PhoneCall;
|
||||
phoneCall#e6f9ddf3 flags:# p2p_allowed:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connection:PhoneConnection alternative_connections:Vector<PhoneConnection> start_date:int = PhoneCall;
|
||||
phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall;
|
||||
phoneCallWaiting#1b8f4ad1 flags:# video:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall;
|
||||
phoneCallRequested#87eabb53 flags:# video:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall;
|
||||
phoneCallAccepted#997c454a flags:# video:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_b:bytes protocol:PhoneCallProtocol = PhoneCall;
|
||||
phoneCall#8742ae7f flags:# p2p_allowed:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connections:Vector<PhoneConnection> start_date:int = PhoneCall;
|
||||
phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true video:flags.5?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall;
|
||||
|
||||
phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection;
|
||||
|
||||
@@ -924,7 +928,7 @@ langPackLanguage#eeca5ce3 flags:# official:flags.0?true rtl:flags.2?true beta:fl
|
||||
channelAdminLogEventActionChangeTitle#e6dfb825 prev_value:string new_value:string = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeAbout#55188a2e prev_value:string new_value:string = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeUsername#6a4afc38 prev_value:string new_value:string = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangePhoto#b82f55c3 prev_photo:ChatPhoto new_photo:ChatPhoto = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangePhoto#434bd2af prev_photo:Photo new_photo:Photo = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionToggleInvites#1b7907ae new_value:Bool = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionToggleSignatures#26ae0971 new_value:Bool = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionUpdatePinned#e9e82c18 message:Message = ChannelAdminLogEventAction;
|
||||
@@ -1112,6 +1116,25 @@ inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
|
||||
account.wallPapersNotModified#1c199183 = account.WallPapers;
|
||||
account.wallPapers#702b65a9 hash:int wallpapers:Vector<WallPaper> = account.WallPapers;
|
||||
|
||||
codeSettings#302f59f3 flags:# allow_flashcall:flags.0?true current_number:flags.1?true app_hash_persistent:flags.2?true app_hash:flags.3?string = CodeSettings;
|
||||
|
||||
wallPaperSettings#a12f40b8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int intensity:flags.3?int = WallPaperSettings;
|
||||
|
||||
autoDownloadSettings#d246fd47 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:int file_size_max:int = AutoDownloadSettings;
|
||||
|
||||
account.autoDownloadSettings#63cacf26 low:AutoDownloadSettings medium:AutoDownloadSettings high:AutoDownloadSettings = account.AutoDownloadSettings;
|
||||
|
||||
emojiKeyword#d5b3b9f9 keyword:string emoticons:Vector<string> = EmojiKeyword;
|
||||
emojiKeywordDeleted#236df622 keyword:string emoticons:Vector<string> = EmojiKeyword;
|
||||
|
||||
emojiKeywordsDifference#5cc761bd lang_code:string from_version:int version:int keywords:Vector<EmojiKeyword> = EmojiKeywordsDifference;
|
||||
|
||||
emojiURL#a575739d url:string = EmojiURL;
|
||||
|
||||
emojiLanguage#b3fb5361 lang_code:string = EmojiLanguage;
|
||||
|
||||
fileLocationToBeDeprecated#bc7fc6cd volume_id:long local_id:int = FileLocation;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@@ -1122,7 +1145,7 @@ invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
|
||||
invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
|
||||
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
|
||||
|
||||
auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode;
|
||||
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
|
||||
auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization;
|
||||
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
|
||||
auth.logOut#5717da40 = Bool;
|
||||
@@ -1154,7 +1177,7 @@ account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> =
|
||||
account.deleteAccount#418d4e0b reason:string = Bool;
|
||||
account.getAccountTTL#8fc711d = AccountDaysTTL;
|
||||
account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool;
|
||||
account.sendChangePhoneCode#8e57deb flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;
|
||||
account.sendChangePhoneCode#82574ae5 phone_number:string settings:CodeSettings = auth.SentCode;
|
||||
account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User;
|
||||
account.updateDeviceLocked#38df3532 period:int = Bool;
|
||||
account.getAuthorizations#e320c158 = account.Authorizations;
|
||||
@@ -1162,7 +1185,7 @@ account.resetAuthorization#df77f3bc hash:long = Bool;
|
||||
account.getPassword#548a30f5 = account.Password;
|
||||
account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings;
|
||||
account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool;
|
||||
account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode;
|
||||
account.sendConfirmPhoneCode#1b3faa88 hash:string settings:CodeSettings = auth.SentCode;
|
||||
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
|
||||
account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword;
|
||||
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
|
||||
@@ -1174,7 +1197,7 @@ account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long =
|
||||
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
|
||||
account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
|
||||
account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
|
||||
account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;
|
||||
account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode;
|
||||
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
|
||||
account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
|
||||
account.verifyEmail#ecba39db email:string code:string = Bool;
|
||||
@@ -1186,10 +1209,13 @@ account.cancelPasswordEmail#c1cbd5b6 = Bool;
|
||||
account.getContactSignUpNotification#9f07c728 = Bool;
|
||||
account.setContactSignUpNotification#cff43f61 silent:Bool = Bool;
|
||||
account.getNotifyExceptions#53577479 flags:# compare_sound:flags.1?true peer:flags.0?InputNotifyPeer = Updates;
|
||||
account.uploadWallPaper#c7ba9b4d file:InputFile mime_type:string = WallPaper;
|
||||
account.getWallPaper#fc8ddbea wallpaper:InputWallPaper = WallPaper;
|
||||
account.saveWallPaper#189581b3 wallpaper:InputWallPaper unsave:Bool = Bool;
|
||||
account.installWallPaper#4a0378ce wallpaper:InputWallPaper = Bool;
|
||||
account.uploadWallPaper#dd853661 file:InputFile mime_type:string settings:WallPaperSettings = WallPaper;
|
||||
account.saveWallPaper#6c5a5b37 wallpaper:InputWallPaper unsave:Bool settings:WallPaperSettings = Bool;
|
||||
account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSettings = Bool;
|
||||
account.resetWallPapers#bb3b9804 = Bool;
|
||||
account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings;
|
||||
account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool;
|
||||
|
||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
|
||||
@@ -1218,7 +1244,7 @@ messages.getDialogs#b098aee6 flags:# exclude_pinned:flags.0?true offset_date:int
|
||||
messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
|
||||
messages.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
|
||||
messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
|
||||
messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true peer:InputPeer max_id:int = messages.AffectedHistory;
|
||||
messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int = messages.AffectedHistory;
|
||||
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
|
||||
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
|
||||
@@ -1316,9 +1342,13 @@ messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true peer:InputPeer
|
||||
messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates;
|
||||
messages.getPollResults#73bb643b peer:InputPeer msg_id:int = Updates;
|
||||
messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines;
|
||||
messages.getStatsURL#83f6c0cd peer:InputPeer = StatsURL;
|
||||
messages.getStatsURL#812c2ae6 flags:# dark:flags.0?true peer:InputPeer params:string = StatsURL;
|
||||
messages.editChatAbout#def60797 peer:InputPeer about:string = Bool;
|
||||
messages.editChatDefaultBannedRights#a5866b41 peer:InputPeer banned_rights:ChatBannedRights = Updates;
|
||||
messages.getEmojiKeywords#35a0e062 lang_code:string = EmojiKeywordsDifference;
|
||||
messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int = EmojiKeywordsDifference;
|
||||
messages.getEmojiKeywordsLanguages#4e9963b2 lang_codes:Vector<string> = Vector<EmojiLanguage>;
|
||||
messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
@@ -1404,18 +1434,18 @@ stickers.changeStickerPosition#ffb6d4ca sticker:InputDocument position:int = mes
|
||||
stickers.addStickerToSet#8653febe stickerset:InputStickerSet sticker:InputStickerSetItem = messages.StickerSet;
|
||||
|
||||
phone.getCallConfig#55451fa9 = DataJSON;
|
||||
phone.requestCall#5b95b3d4 user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||
phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||
phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||
phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool;
|
||||
phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates;
|
||||
phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates;
|
||||
phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates;
|
||||
phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
|
||||
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
|
||||
|
||||
langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
|
||||
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;
|
||||
langpack.getDifference#9d51e814 lang_code:string from_version:int = LangPackDifference;
|
||||
langpack.getDifference#cd984aa5 lang_pack:string lang_code:string from_version:int = LangPackDifference;
|
||||
langpack.getLanguages#42c6978f lang_pack:string = Vector<LangPackLanguage>;
|
||||
langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage;
|
||||
|
||||
// LAYER 93
|
||||
// LAYER 98
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="1.5.8.0" />
|
||||
Version="1.6.4.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,5,8,0
|
||||
PRODUCTVERSION 1,5,8,0
|
||||
FILEVERSION 1,6,4,0
|
||||
PRODUCTVERSION 1,6,4,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -52,10 +52,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "1.5.8.0"
|
||||
VALUE "FileVersion", "1.6.4.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.5.8.0"
|
||||
VALUE "ProductVersion", "1.6.4.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,5,8,0
|
||||
PRODUCTVERSION 1,5,8,0
|
||||
FILEVERSION 1,6,4,0
|
||||
PRODUCTVERSION 1,6,4,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -43,10 +43,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "1.5.8.0"
|
||||
VALUE "FileVersion", "1.6.4.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.5.8.0"
|
||||
VALUE "ProductVersion", "1.6.4.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -7,9 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
#ifdef small
|
||||
#undef small
|
||||
#endif // small
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4091)
|
||||
#include <DbgHelp.h>
|
||||
|
||||
@@ -6,6 +6,7 @@ For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
NSString *appName = @"Telegram.app";
|
||||
NSString *appDir = nil;
|
||||
@@ -44,6 +45,20 @@ void writeLog(NSString *msg) {
|
||||
[_logFile synchronizeFile];
|
||||
}
|
||||
|
||||
void RemoveQuarantineAttribute(NSString *path) {
|
||||
const char *kQuarantineAttribute = "com.apple.quarantine";
|
||||
|
||||
writeLog([@"Removing quarantine: " stringByAppendingString:path]);
|
||||
removexattr([path fileSystemRepresentation], kQuarantineAttribute, 0);
|
||||
}
|
||||
|
||||
void RemoveQuarantineFromBundle(NSString *path) {
|
||||
RemoveQuarantineAttribute(path);
|
||||
RemoveQuarantineAttribute([path stringByAppendingString:@"/Contents/MacOS/Telegram"]);
|
||||
RemoveQuarantineAttribute([path stringByAppendingString:@"/Contents/Helpers/crashpad_handler"]);
|
||||
RemoveQuarantineAttribute([path stringByAppendingString:@"/Contents/Frameworks/Updater"]);
|
||||
}
|
||||
|
||||
void delFolder() {
|
||||
writeLog([@"Fully clearing old path: " stringByAppendingString:[workDir stringByAppendingString:@"tupdates/ready"]]);
|
||||
if (![[NSFileManager defaultManager] removeItemAtPath:[workDir stringByAppendingString:@"tupdates/ready"] error:nil]) {
|
||||
@@ -232,6 +247,9 @@ int main(int argc, const char * argv[]) {
|
||||
}
|
||||
|
||||
NSString *appPath = [[NSArray arrayWithObjects:appDir, appRealName, nil] componentsJoinedByString:@""];
|
||||
|
||||
RemoveQuarantineFromBundle(appPath);
|
||||
|
||||
NSMutableArray *args = [[NSMutableArray alloc] initWithObjects: @"-noupdate", nil];
|
||||
if (toSettings) [args addObject:@"-tosettings"];
|
||||
if (_debug) [args addObject:@"-debug"];
|
||||
@@ -248,18 +266,24 @@ int main(int argc, const char * argv[]) {
|
||||
[args addObject:workDir];
|
||||
}
|
||||
writeLog([[NSArray arrayWithObjects:@"Running application '", appPath, @"' with args '", [args componentsJoinedByString:@"' '"], @"'..", nil] componentsJoinedByString:@""]);
|
||||
NSError *error = nil;
|
||||
NSRunningApplication *result = [[NSWorkspace sharedWorkspace]
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
NSError *error = nil;
|
||||
NSRunningApplication *result = [[NSWorkspace sharedWorkspace]
|
||||
launchApplicationAtURL:[NSURL fileURLWithPath:appPath]
|
||||
options:NSWorkspaceLaunchDefault
|
||||
configuration:[NSDictionary
|
||||
dictionaryWithObject:args
|
||||
forKey:NSWorkspaceLaunchConfigurationArguments]
|
||||
error:&error];
|
||||
if (!result) {
|
||||
if (result) {
|
||||
closeLog();
|
||||
return 0;
|
||||
}
|
||||
writeLog([[NSString stringWithFormat:@"Could not run application, error %ld: ", (long)[error code]] stringByAppendingString: error ? [error localizedDescription] : @"(nil)"]);
|
||||
usleep(200000);
|
||||
}
|
||||
closeLog();
|
||||
return result ? 0 : -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class mtpFileLoader;
|
||||
|
||||
namespace Data {
|
||||
struct UpdatedFileReferences;
|
||||
struct WallPaper;
|
||||
class WallPaper;
|
||||
} // namespace Data
|
||||
|
||||
namespace InlineBots {
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
ChannelData *channel,
|
||||
MsgId msgId,
|
||||
RequestMessageDataCallback callback);
|
||||
QString exportDirectMessageLink(not_null<HistoryItem*> item);
|
||||
|
||||
void requestContacts();
|
||||
void requestDialogEntry(not_null<Data::Feed*> feed);
|
||||
@@ -172,6 +173,7 @@ public:
|
||||
void clearWebPageRequest(WebPageData *page);
|
||||
void clearWebPageRequests();
|
||||
|
||||
void requestAttachedStickerSets(not_null<PhotoData*> photo);
|
||||
void scheduleStickerSetRequest(uint64 setId, uint64 access);
|
||||
void requestStickerSets();
|
||||
void saveStickerSets(
|
||||
@@ -210,7 +212,12 @@ public:
|
||||
const MTPUserStatus &status,
|
||||
int currentOnlineTill);
|
||||
|
||||
void clearHistory(not_null<PeerData*> peer);
|
||||
void clearHistory(not_null<PeerData*> peer, bool revoke);
|
||||
void deleteConversation(not_null<PeerData*> peer, bool revoke);
|
||||
void deleteMessages(
|
||||
not_null<PeerData*> peer,
|
||||
const QVector<MTPint> &ids,
|
||||
bool revoke);
|
||||
|
||||
base::Observable<PeerData*> &fullPeerUpdated() {
|
||||
return _fullPeerUpdated;
|
||||
@@ -322,6 +329,13 @@ public:
|
||||
SendMediaType type,
|
||||
const SendOptions &options);
|
||||
|
||||
void editMedia(
|
||||
Storage::PreparedList &&list,
|
||||
SendMediaType type,
|
||||
TextWithTags &&caption,
|
||||
const SendOptions &options,
|
||||
MsgId msgIdToEdit);
|
||||
|
||||
void sendUploadedPhoto(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
@@ -331,6 +345,13 @@ public:
|
||||
const MTPInputFile &file,
|
||||
const std::optional<MTPInputFile> &thumb,
|
||||
bool silent);
|
||||
void editUploadedFile(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
const std::optional<MTPInputFile> &thumb,
|
||||
bool silent,
|
||||
bool isDocument);
|
||||
|
||||
void cancelLocalItem(not_null<HistoryItem*> item);
|
||||
|
||||
struct MessageToSend {
|
||||
@@ -378,6 +399,8 @@ public:
|
||||
Calls,
|
||||
Invites,
|
||||
CallsPeer2Peer,
|
||||
Forwards,
|
||||
ProfilePhoto,
|
||||
};
|
||||
enum class Option {
|
||||
Everyone,
|
||||
@@ -405,7 +428,7 @@ public:
|
||||
void sendPollVotes(
|
||||
FullMsgId itemId,
|
||||
const std::vector<QByteArray> &options);
|
||||
void closePoll(FullMsgId itemId);
|
||||
void closePoll(not_null<HistoryItem*> item);
|
||||
void reloadPollResults(not_null<HistoryItem*> item);
|
||||
|
||||
~ApiWrap();
|
||||
@@ -422,7 +445,7 @@ private:
|
||||
struct StickersByEmoji {
|
||||
std::vector<not_null<DocumentData*>> list;
|
||||
int32 hash = 0;
|
||||
TimeMs received = 0;
|
||||
crl::time received = 0;
|
||||
};
|
||||
|
||||
void updatesReceived(const MTPUpdates &updates);
|
||||
@@ -537,6 +560,10 @@ private:
|
||||
UserId userId,
|
||||
const SendOptions &options);
|
||||
|
||||
void deleteHistory(
|
||||
not_null<PeerData*> peer,
|
||||
bool justClear,
|
||||
bool revoke);
|
||||
void sendReadRequest(not_null<PeerData*> peer, MsgId upTo);
|
||||
int applyAffectedHistory(
|
||||
not_null<PeerData*> peer,
|
||||
@@ -728,7 +755,7 @@ private:
|
||||
|
||||
rpl::event_stream<uint64> _stickerSetInstalled;
|
||||
|
||||
base::flat_map<not_null<Data::Feed*>, TimeMs> _feedReadsDelayed;
|
||||
base::flat_map<not_null<Data::Feed*>, crl::time> _feedReadsDelayed;
|
||||
base::flat_map<not_null<Data::Feed*>, mtpRequestId> _feedReadRequests;
|
||||
base::Timer _feedReadTimer;
|
||||
|
||||
@@ -746,7 +773,7 @@ private:
|
||||
|
||||
mtpRequestId _deepLinkInfoRequestId = 0;
|
||||
|
||||
TimeMs _termsUpdateSendAt = 0;
|
||||
crl::time _termsUpdateSendAt = 0;
|
||||
mtpRequestId _termsUpdateRequestId = 0;
|
||||
|
||||
mtpRequestId _checkInviteRequestId = 0;
|
||||
@@ -794,4 +821,8 @@ private:
|
||||
std::optional<bool> _contactSignupSilent;
|
||||
rpl::event_stream<bool> _contactSignupSilentChanges;
|
||||
|
||||
mtpRequestId _attachedStickerSetsRequestId = 0;
|
||||
|
||||
base::flat_map<FullMsgId, QString> _unlikelyMessageLinks;
|
||||
|
||||
};
|
||||
|
||||
@@ -7,11 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "app.h"
|
||||
|
||||
#include "styles/style_overview.h"
|
||||
#include "styles/style_mediaview.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "data/data_channel.h"
|
||||
@@ -25,11 +20,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_location_manager.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "media/media_audio.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "inline_bots/inline_bot_layout_item.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "core/sandbox.h"
|
||||
#include "core/application.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
@@ -37,14 +34,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/localstorage.h"
|
||||
#include "storage/storage_facade.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "messenger.h"
|
||||
#include "application.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "apiwrap.h"
|
||||
#include "numbers.h"
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
#include "styles/style_overview.h"
|
||||
#include "styles/style_mediaview.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
#ifdef OS_MAC_OLD
|
||||
#include <libexif/exif-data.h>
|
||||
@@ -117,10 +117,9 @@ namespace App {
|
||||
}
|
||||
|
||||
MainWindow *wnd() {
|
||||
if (auto instance = Messenger::InstancePointer()) {
|
||||
return instance->getActiveWindow();
|
||||
}
|
||||
return nullptr;
|
||||
return Core::IsAppLaunched()
|
||||
? Core::App().getActiveWindow()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
MainWidget *main() {
|
||||
@@ -130,22 +129,6 @@ namespace App {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UserData *feedUser(const MTPUser &user) {
|
||||
return Auth().data().user(user);
|
||||
}
|
||||
|
||||
UserData *feedUsers(const MTPVector<MTPUser> &users) {
|
||||
return Auth().data().processUsers(users);
|
||||
}
|
||||
|
||||
PeerData *feedChat(const MTPChat &chat) {
|
||||
return Auth().data().chat(chat);
|
||||
}
|
||||
|
||||
PeerData *feedChats(const MTPVector<MTPChat> &chats) {
|
||||
return Auth().data().processChats(chats);
|
||||
}
|
||||
|
||||
bool checkEntitiesAndViewsUpdate(const MTPDmessage &m) {
|
||||
auto peerId = peerFromMTP(m.vto_id);
|
||||
if (m.has_from_id() && peerId == Auth().userPeerId()) {
|
||||
@@ -156,12 +139,14 @@ namespace App {
|
||||
auto entities = m.has_entities()
|
||||
? TextUtilities::EntitiesFromMTP(m.ventities.v)
|
||||
: EntitiesInText();
|
||||
const auto media = m.has_media() ? &m.vmedia : nullptr;
|
||||
existing->setText({ text, entities });
|
||||
existing->updateSentMedia(m.has_media() ? &m.vmedia : nullptr);
|
||||
existing->updateReplyMarkup(m.has_reply_markup()
|
||||
? (&m.vreply_markup)
|
||||
: nullptr);
|
||||
existing->updateForwardedInfo(m.has_fwd_from()
|
||||
? &m.vfwd_from
|
||||
: nullptr);
|
||||
existing->setViewsCount(m.has_views() ? m.vviews.v : -1);
|
||||
existing->indexAsNewItem();
|
||||
Auth().data().requestItemTextRefresh(existing);
|
||||
@@ -174,9 +159,9 @@ namespace App {
|
||||
return false;
|
||||
}
|
||||
|
||||
void updateEditedMessage(const MTPMessage &message) {
|
||||
message.match([](const MTPDmessageEmpty &) {
|
||||
}, [](const auto &message) {
|
||||
void updateEditedMessage(const MTPMessage &m) {
|
||||
m.match([](const MTPDmessageEmpty &) {
|
||||
}, [&m](const auto &message) {
|
||||
auto peerId = peerFromMTP(message.vto_id);
|
||||
if (message.has_from_id() && peerId == Auth().userPeerId()) {
|
||||
peerId = peerFromUser(message.vfrom_id);
|
||||
@@ -185,6 +170,9 @@ namespace App {
|
||||
peerToChannel(peerId),
|
||||
message.vid.v);
|
||||
if (existing) {
|
||||
if (existing->isLocalUpdateMedia()) {
|
||||
checkEntitiesAndViewsUpdate(m.c_message());
|
||||
}
|
||||
existing->applyEdition(message);
|
||||
}
|
||||
});
|
||||
@@ -242,108 +230,14 @@ namespace App {
|
||||
return feedMsgs(msgs.v, type);
|
||||
}
|
||||
|
||||
ImagePtr image(const MTPPhotoSize &size) {
|
||||
switch (size.type()) {
|
||||
case mtpc_photoSize: {
|
||||
auto &d = size.c_photoSize();
|
||||
if (d.vlocation.type() == mtpc_fileLocation) {
|
||||
auto &l = d.vlocation.c_fileLocation();
|
||||
return Images::Create(
|
||||
StorageImageLocation(
|
||||
d.vw.v,
|
||||
d.vh.v,
|
||||
l.vdc_id.v,
|
||||
l.vvolume_id.v,
|
||||
l.vlocal_id.v,
|
||||
l.vsecret.v,
|
||||
l.vfile_reference.v),
|
||||
d.vsize.v);
|
||||
}
|
||||
} break;
|
||||
case mtpc_photoCachedSize: {
|
||||
auto &d = size.c_photoCachedSize();
|
||||
if (d.vlocation.type() == mtpc_fileLocation) {
|
||||
auto &l = d.vlocation.c_fileLocation();
|
||||
auto bytes = qba(d.vbytes);
|
||||
return Images::Create(
|
||||
StorageImageLocation(
|
||||
d.vw.v,
|
||||
d.vh.v,
|
||||
l.vdc_id.v,
|
||||
l.vvolume_id.v,
|
||||
l.vlocal_id.v,
|
||||
l.vsecret.v,
|
||||
l.vfile_reference.v),
|
||||
bytes);
|
||||
} else if (d.vlocation.type() == mtpc_fileLocationUnavailable) {
|
||||
const auto bytes = qba(d.vbytes);
|
||||
if (auto image = App::readImage(bytes); !image.isNull()) {
|
||||
return Images::Create(std::move(image), "JPG");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case mtpc_photoStrippedSize: {
|
||||
const auto &d = size.c_photoStrippedSize();
|
||||
auto bytes = qba(d.vbytes);
|
||||
if (bytes.size() >= 3 && bytes[0] == '\x01') {
|
||||
const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49"
|
||||
"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c"
|
||||
"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37"
|
||||
"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3"
|
||||
"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff"
|
||||
"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35"
|
||||
"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
||||
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
||||
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
||||
"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00"
|
||||
"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01"
|
||||
"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
|
||||
"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05"
|
||||
"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06"
|
||||
"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52"
|
||||
"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28"
|
||||
"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53"
|
||||
"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
|
||||
"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
|
||||
"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6"
|
||||
"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6"
|
||||
"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4"
|
||||
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01"
|
||||
"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
|
||||
"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05"
|
||||
"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
|
||||
"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33"
|
||||
"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26"
|
||||
"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
|
||||
"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74"
|
||||
"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94"
|
||||
"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4"
|
||||
"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4"
|
||||
"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4"
|
||||
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00"
|
||||
"\x3f\x00";
|
||||
const char footer[] = "\xff\xd9";
|
||||
auto real = QByteArray(header, sizeof(header) - 1);
|
||||
real[164] = bytes[1];
|
||||
real[166] = bytes[2];
|
||||
bytes = real + bytes.mid(3) + QByteArray::fromRawData(footer, sizeof(footer) - 1);
|
||||
if (auto image = App::readImage(bytes); !image.isNull()) {
|
||||
return Images::Create(std::move(image), "JPG");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return ImagePtr();
|
||||
}
|
||||
|
||||
void feedInboxRead(const PeerId &peer, MsgId upTo) {
|
||||
if (const auto history = App::historyLoaded(peer)) {
|
||||
if (const auto history = Auth().data().historyLoaded(peer)) {
|
||||
history->inboxRead(upTo);
|
||||
}
|
||||
}
|
||||
|
||||
void feedOutboxRead(const PeerId &peer, MsgId upTo, TimeId when) {
|
||||
if (auto history = App::historyLoaded(peer)) {
|
||||
if (auto history = Auth().data().historyLoaded(peer)) {
|
||||
history->outboxRead(upTo);
|
||||
if (const auto user = history->peer->asUser()) {
|
||||
user->madeAction(when);
|
||||
@@ -371,7 +265,7 @@ namespace App {
|
||||
if (!data) return;
|
||||
|
||||
const auto affectedHistory = (channelId != NoChannel)
|
||||
? App::history(peerFromChannel(channelId)).get()
|
||||
? Auth().data().history(peerFromChannel(channelId)).get()
|
||||
: nullptr;
|
||||
|
||||
auto historiesToCheck = base::flat_set<not_null<History*>>();
|
||||
@@ -393,7 +287,7 @@ namespace App {
|
||||
}
|
||||
|
||||
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink) {
|
||||
if (const auto user = userLoaded(userId.v)) {
|
||||
if (const auto user = Auth().data().userLoaded(userId.v)) {
|
||||
const auto wasShowPhone = (user->contactStatus() == UserData::ContactStatus::CanAdd);
|
||||
switch (myLink.type()) {
|
||||
case mtpc_contactLinkContact:
|
||||
@@ -413,10 +307,10 @@ namespace App {
|
||||
user->setContactStatus(UserData::ContactStatus::CanAdd);
|
||||
}
|
||||
|
||||
const auto showPhone = !isServiceUser(user->id)
|
||||
const auto showPhone = !user->isServiceUser()
|
||||
&& !user->isSelf()
|
||||
&& user->contactStatus() == UserData::ContactStatus::CanAdd;
|
||||
const auto showPhoneChanged = !isServiceUser(user->id)
|
||||
const auto showPhoneChanged = !user->isServiceUser()
|
||||
&& !user->isSelf()
|
||||
&& (showPhone != wasShowPhone);
|
||||
if (showPhoneChanged) {
|
||||
@@ -431,62 +325,18 @@ namespace App {
|
||||
}
|
||||
}
|
||||
|
||||
not_null<PeerData*> peer(PeerId id) {
|
||||
return Auth().data().peer(id);
|
||||
}
|
||||
not_null<UserData*> user(UserId id) {
|
||||
return Auth().data().user(id);
|
||||
}
|
||||
not_null<ChatData*> chat(ChatId id) {
|
||||
return Auth().data().chat(id);
|
||||
}
|
||||
not_null<ChannelData*> channel(ChannelId id) {
|
||||
return Auth().data().channel(id);
|
||||
}
|
||||
PeerData *peerLoaded(PeerId id) {
|
||||
return Auth().data().peerLoaded(id);
|
||||
}
|
||||
UserData *userLoaded(UserId id) {
|
||||
return Auth().data().userLoaded(id);
|
||||
}
|
||||
ChatData *chatLoaded(ChatId id) {
|
||||
return Auth().data().chatLoaded(id);
|
||||
}
|
||||
ChannelData *channelLoaded(ChannelId id) {
|
||||
return Auth().data().channelLoaded(id);
|
||||
}
|
||||
|
||||
QString peerName(const PeerData *peer, bool forDialogs) {
|
||||
return peer ? ((forDialogs && peer->isUser() && !peer->asUser()->nameOrPhone.isEmpty()) ? peer->asUser()->nameOrPhone : peer->name) : lang(lng_deleted);
|
||||
}
|
||||
|
||||
not_null<History*> history(PeerId peer) {
|
||||
return Auth().data().history(peer);
|
||||
}
|
||||
|
||||
History *historyLoaded(PeerId peer) {
|
||||
return Auth().data().historyLoaded(peer);
|
||||
}
|
||||
|
||||
not_null<History*> history(not_null<const PeerData*> peer) {
|
||||
return history(peer->id);
|
||||
}
|
||||
|
||||
History *historyLoaded(const PeerData *peer) {
|
||||
return peer ? historyLoaded(peer->id) : nullptr;
|
||||
}
|
||||
|
||||
HistoryItem *histItemById(ChannelId channelId, MsgId itemId) {
|
||||
if (!itemId) return nullptr;
|
||||
|
||||
auto data = fetchMsgsData(channelId, false);
|
||||
const auto data = fetchMsgsData(channelId, false);
|
||||
if (!data) return nullptr;
|
||||
|
||||
auto i = data->constFind(itemId);
|
||||
if (i != data->cend()) {
|
||||
return i.value();
|
||||
}
|
||||
return nullptr;
|
||||
const auto i = data->constFind(itemId);
|
||||
return (i != data->cend()) ? i.value() : nullptr;
|
||||
}
|
||||
|
||||
HistoryItem *histItemById(const ChannelData *channel, MsgId itemId) {
|
||||
@@ -674,6 +524,8 @@ namespace App {
|
||||
prepareCorners(SelectedOverlayLargeCorners, st::historyMessageRadius, st::msgSelectOverlay);
|
||||
prepareCorners(DateCorners, st::dateRadius, st::msgDateImgBg);
|
||||
prepareCorners(DateSelectedCorners, st::dateRadius, st::msgDateImgBgSelected);
|
||||
prepareCorners(OverviewVideoCorners, st::overviewVideoStatusRadius, st::msgDateImgBg);
|
||||
prepareCorners(OverviewVideoSelectedCorners, st::overviewVideoStatusRadius, st::msgDateImgBgSelected);
|
||||
prepareCorners(InShadowCorners, st::historyMessageRadius, st::msgInShadow);
|
||||
prepareCorners(InSelectedShadowCorners, st::historyMessageRadius, st::msgInShadowSelected);
|
||||
prepareCorners(ForwardCorners, st::historyMessageRadius, st::historyForwardChooseBg);
|
||||
@@ -810,14 +662,11 @@ namespace App {
|
||||
setLaunchState(QuitRequested);
|
||||
|
||||
if (auto window = App::wnd()) {
|
||||
if (!Core::App().isSavingSession()) {
|
||||
if (!Core::Sandbox::Instance().isSavingSession()) {
|
||||
window->hide();
|
||||
}
|
||||
}
|
||||
if (auto mainwidget = App::main()) {
|
||||
mainwidget->saveDraftToCloud();
|
||||
}
|
||||
Messenger::QuitAttempt();
|
||||
Core::Application::QuitAttempt();
|
||||
}
|
||||
|
||||
bool quitting() {
|
||||
|
||||
@@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
enum NewMessageType : char;
|
||||
enum class ImageRoundRadius;
|
||||
class Messenger;
|
||||
class MainWindow;
|
||||
class MainWidget;
|
||||
class HistoryItem;
|
||||
@@ -36,6 +35,8 @@ enum RoundCorners {
|
||||
SelectedOverlayLargeCorners,
|
||||
DateCorners,
|
||||
DateSelectedCorners,
|
||||
OverviewVideoCorners,
|
||||
OverviewVideoSelectedCorners,
|
||||
ForwardCorners,
|
||||
MediaviewSaveCorners,
|
||||
EmojiHoverCorners,
|
||||
@@ -65,11 +66,6 @@ namespace App {
|
||||
|
||||
QString formatPhone(QString phone);
|
||||
|
||||
UserData *feedUser(const MTPUser &user);
|
||||
UserData *feedUsers(const MTPVector<MTPUser> &users); // returns last user
|
||||
PeerData *feedChat(const MTPChat &chat);
|
||||
PeerData *feedChats(const MTPVector<MTPChat> &chats); // returns last chat
|
||||
|
||||
bool checkEntitiesAndViewsUpdate(const MTPDmessage &m); // returns true if item found and it is not detached
|
||||
void updateEditedMessage(const MTPMessage &m);
|
||||
void addSavedGif(DocumentData *doc);
|
||||
@@ -81,24 +77,9 @@ namespace App {
|
||||
void feedWereDeleted(ChannelId channelId, const QVector<MTPint> &msgsIds);
|
||||
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink);
|
||||
|
||||
ImagePtr image(const MTPPhotoSize &size);
|
||||
|
||||
[[nodiscard]] not_null<PeerData*> peer(PeerId id);
|
||||
[[nodiscard]] not_null<UserData*> user(UserId userId);
|
||||
[[nodiscard]] not_null<ChatData*> chat(ChatId chatId);
|
||||
[[nodiscard]] not_null<ChannelData*> channel(ChannelId channelId);
|
||||
[[nodiscard]] PeerData *peerLoaded(PeerId id);
|
||||
[[nodiscard]] UserData *userLoaded(UserId userId);
|
||||
[[nodiscard]] ChatData *chatLoaded(ChatId chatId);
|
||||
[[nodiscard]] ChannelData *channelLoaded(ChannelId channelId);
|
||||
|
||||
[[nodiscard]] QString peerName(const PeerData *peer, bool forDialogs = false);
|
||||
|
||||
[[nodiscard]] not_null<History*> history(PeerId peer);
|
||||
[[nodiscard]] History *historyLoaded(PeerId peer);
|
||||
[[nodiscard]] HistoryItem *histItemById(ChannelId channelId, MsgId itemId);
|
||||
[[nodiscard]] not_null<History*> history(not_null<const PeerData*> peer);
|
||||
[[nodiscard]] History *historyLoaded(const PeerData *peer);
|
||||
[[nodiscard]] HistoryItem *histItemById(
|
||||
const ChannelData *channel,
|
||||
MsgId itemId);
|
||||
|
||||
@@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "auth_session.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "messenger.h"
|
||||
#include "core/application.h"
|
||||
#include "core/changelogs.h"
|
||||
#include "storage/file_download.h"
|
||||
#include "storage/file_upload.h"
|
||||
@@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kAutoLockTimeoutLateMs = TimeMs(3000);
|
||||
constexpr auto kAutoLockTimeoutLateMs = crl::time(3000);
|
||||
constexpr auto kLegacyCallsPeerToPeerNobody = 4;
|
||||
|
||||
} // namespace
|
||||
@@ -372,7 +372,7 @@ rpl::producer<int> AuthSessionSettings::thirdColumnWidthChanges() const {
|
||||
}
|
||||
|
||||
AuthSession &Auth() {
|
||||
auto result = Messenger::Instance().authSession();
|
||||
auto result = Core::App().authSession();
|
||||
Assert(result != nullptr);
|
||||
return *result;
|
||||
}
|
||||
@@ -386,17 +386,17 @@ AuthSession::AuthSession(const MTPUser &user)
|
||||
, _storage(std::make_unique<Storage::Facade>())
|
||||
, _notifications(std::make_unique<Window::Notifications::System>(this))
|
||||
, _data(std::make_unique<Data::Session>(this))
|
||||
, _user(_data->user(user))
|
||||
, _user(_data->processUser(user))
|
||||
, _changelogs(Core::Changelogs::Create(this))
|
||||
, _supportHelper(Support::Helper::Create(this)) {
|
||||
_saveDataTimer.setCallback([=] {
|
||||
Local::writeUserSettings();
|
||||
});
|
||||
Messenger::Instance().passcodeLockChanges(
|
||||
Core::App().passcodeLockChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
_shouldLockAt = 0;
|
||||
}, _lifetime);
|
||||
Messenger::Instance().lockChanges(
|
||||
Core::App().lockChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
notifications().updateAll();
|
||||
}, _lifetime);
|
||||
@@ -429,10 +429,7 @@ AuthSession::AuthSession(const MTPUser &user)
|
||||
}
|
||||
|
||||
bool AuthSession::Exists() {
|
||||
if (const auto messenger = Messenger::InstancePointer()) {
|
||||
return (messenger->authSession() != nullptr);
|
||||
}
|
||||
return false;
|
||||
return Core::IsAppLaunched() && (Core::App().authSession() != nullptr);
|
||||
}
|
||||
|
||||
base::Observable<void> &AuthSession::downloaderTaskFinished() {
|
||||
@@ -453,7 +450,7 @@ bool AuthSession::validateSelf(const MTPUser &user) {
|
||||
return false;
|
||||
} else if (user.c_user().vid.v != userId()) {
|
||||
LOG(("Auth Error: wrong self user received."));
|
||||
crl::on_main(this, [] { Messenger::Instance().logOut(); });
|
||||
crl::on_main(this, [] { Core::App().logOut(); });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -471,33 +468,47 @@ void AuthSession::moveSettingsFrom(AuthSessionSettings &&other) {
|
||||
}
|
||||
}
|
||||
|
||||
void AuthSession::saveSettingsDelayed(TimeMs delay) {
|
||||
void AuthSession::saveSettingsDelayed(crl::time delay) {
|
||||
Expects(this == &Auth());
|
||||
|
||||
_saveDataTimer.callOnce(delay);
|
||||
}
|
||||
|
||||
void AuthSession::localPasscodeChanged() {
|
||||
_shouldLockAt = 0;
|
||||
_autoLockTimer.cancel();
|
||||
checkAutoLock();
|
||||
}
|
||||
|
||||
void AuthSession::termsDeleteNow() {
|
||||
api().request(MTPaccount_DeleteAccount(
|
||||
MTP_string("Decline ToS update")
|
||||
)).send();
|
||||
}
|
||||
|
||||
void AuthSession::checkAutoLock() {
|
||||
if (!Global::LocalPasscode()
|
||||
|| Messenger::Instance().passcodeLocked()) {
|
||||
|| Core::App().passcodeLocked()) {
|
||||
_shouldLockAt = 0;
|
||||
_autoLockTimer.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
Messenger::Instance().checkLocalTime();
|
||||
auto now = getms(true);
|
||||
auto shouldLockInMs = Global::AutoLock() * 1000LL;
|
||||
auto idleForMs = psIdleTime();
|
||||
auto notPlayingVideoForMs = now - settings().lastTimeVideoPlayedAt();
|
||||
auto checkTimeMs = qMin(idleForMs, notPlayingVideoForMs);
|
||||
Core::App().checkLocalTime();
|
||||
const auto now = crl::now();
|
||||
const auto shouldLockInMs = Global::AutoLock() * 1000LL;
|
||||
const auto checkTimeMs = now - Core::App().lastNonIdleTime();
|
||||
if (checkTimeMs >= shouldLockInMs || (_shouldLockAt > 0 && now > _shouldLockAt + kAutoLockTimeoutLateMs)) {
|
||||
Messenger::Instance().lockByPasscode();
|
||||
_shouldLockAt = 0;
|
||||
_autoLockTimer.cancel();
|
||||
Core::App().lockByPasscode();
|
||||
} else {
|
||||
_shouldLockAt = now + (shouldLockInMs - checkTimeMs);
|
||||
_autoLockTimer.callOnce(shouldLockInMs - checkTimeMs);
|
||||
}
|
||||
}
|
||||
|
||||
void AuthSession::checkAutoLockIn(TimeMs time) {
|
||||
void AuthSession::checkAutoLockIn(crl::time time) {
|
||||
if (_autoLockTimer.isActive()) {
|
||||
auto remain = _autoLockTimer.remainingTime();
|
||||
if (remain > 0 && remain <= time) return;
|
||||
|
||||
@@ -139,12 +139,6 @@ public:
|
||||
bool smallDialogsList() const {
|
||||
return _variables.smallDialogsList;
|
||||
}
|
||||
void setLastTimeVideoPlayedAt(TimeMs time) {
|
||||
_lastTimeVideoPlayedAt = time;
|
||||
}
|
||||
TimeMs lastTimeVideoPlayedAt() const {
|
||||
return _lastTimeVideoPlayedAt;
|
||||
}
|
||||
void setSoundOverride(const QString &key, const QString &path) {
|
||||
_variables.soundOverrides.insert(key, path);
|
||||
}
|
||||
@@ -263,11 +257,9 @@ private:
|
||||
rpl::event_stream<bool> _tabbedReplacedWithInfoValue;
|
||||
|
||||
Variables _variables;
|
||||
TimeMs _lastTimeVideoPlayedAt = 0;
|
||||
|
||||
};
|
||||
|
||||
// One per Messenger.
|
||||
class AuthSession;
|
||||
AuthSession &Auth();
|
||||
|
||||
@@ -312,7 +304,7 @@ public:
|
||||
return _settings;
|
||||
}
|
||||
void moveSettingsFrom(AuthSessionSettings &&other);
|
||||
void saveSettingsDelayed(TimeMs delay = kDefaultSaveDelay);
|
||||
void saveSettingsDelayed(crl::time delay = kDefaultSaveDelay);
|
||||
|
||||
ApiWrap &api() {
|
||||
return *_api;
|
||||
@@ -323,7 +315,9 @@ public:
|
||||
}
|
||||
|
||||
void checkAutoLock();
|
||||
void checkAutoLockIn(TimeMs time);
|
||||
void checkAutoLockIn(crl::time time);
|
||||
void localPasscodeChanged();
|
||||
void termsDeleteNow();
|
||||
|
||||
rpl::lifetime &lifetime() {
|
||||
return _lifetime;
|
||||
@@ -339,12 +333,12 @@ public:
|
||||
~AuthSession();
|
||||
|
||||
private:
|
||||
static constexpr auto kDefaultSaveDelay = TimeMs(1000);
|
||||
static constexpr auto kDefaultSaveDelay = crl::time(1000);
|
||||
|
||||
AuthSessionSettings _settings;
|
||||
base::Timer _saveDataTimer;
|
||||
|
||||
TimeMs _shouldLockAt = 0;
|
||||
crl::time _shouldLockAt = 0;
|
||||
base::Timer _autoLockTimer;
|
||||
|
||||
const std::unique_ptr<ApiWrap> _api;
|
||||
|
||||
@@ -46,7 +46,6 @@ using uint64 = quint64;
|
||||
using float32 = float;
|
||||
using float64 = double;
|
||||
|
||||
using TimeMs = int64;
|
||||
using TimeId = int32;
|
||||
|
||||
// Define specializations for QByteArray for Qt 5.3.2, because
|
||||
|
||||
@@ -20,9 +20,11 @@ public:
|
||||
binary_guard &operator=(binary_guard &&other);
|
||||
~binary_guard();
|
||||
|
||||
bool alive() const;
|
||||
|
||||
binary_guard &operator=(std::nullptr_t);
|
||||
|
||||
bool alive() const;
|
||||
binary_guard make_guard();
|
||||
|
||||
explicit operator bool() const;
|
||||
|
||||
private:
|
||||
@@ -30,8 +32,6 @@ private:
|
||||
|
||||
std::atomic<bool> *_bothAlive = nullptr;
|
||||
|
||||
friend std::pair<binary_guard, binary_guard> make_binary_guard();
|
||||
|
||||
};
|
||||
|
||||
inline binary_guard::binary_guard(binary_guard &&other)
|
||||
@@ -46,6 +46,10 @@ inline binary_guard &binary_guard::operator=(binary_guard &&other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline binary_guard::~binary_guard() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
inline binary_guard &binary_guard::operator=(std::nullptr_t) {
|
||||
destroy();
|
||||
return *this;
|
||||
@@ -59,10 +63,6 @@ inline bool binary_guard::alive() const {
|
||||
return _bothAlive && _bothAlive->load();
|
||||
}
|
||||
|
||||
inline binary_guard::~binary_guard() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
inline void binary_guard::destroy() {
|
||||
if (const auto both = base::take(_bothAlive)) {
|
||||
auto old = true;
|
||||
@@ -72,12 +72,30 @@ inline void binary_guard::destroy() {
|
||||
}
|
||||
}
|
||||
|
||||
inline std::pair<binary_guard, binary_guard> make_binary_guard() {
|
||||
auto result = std::pair<binary_guard, binary_guard>();
|
||||
result.first._bothAlive
|
||||
= result.second._bothAlive
|
||||
= new std::atomic<bool>(true);
|
||||
inline binary_guard binary_guard::make_guard() {
|
||||
destroy();
|
||||
|
||||
auto result = binary_guard();
|
||||
_bothAlive = result._bothAlive = new std::atomic<bool>(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <typename T, typename Enable>
|
||||
struct guard_traits;
|
||||
|
||||
template <>
|
||||
struct guard_traits<base::binary_guard, void> {
|
||||
static base::binary_guard create(base::binary_guard value) {
|
||||
return value;
|
||||
}
|
||||
static bool check(const base::binary_guard &guard) {
|
||||
return guard.alive();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
||||
|
||||
@@ -26,16 +26,16 @@ QMutex EnvironmentMutex;
|
||||
class CallDelayedEvent : public QEvent {
|
||||
public:
|
||||
CallDelayedEvent(
|
||||
crl::time_type timeout,
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method);
|
||||
|
||||
crl::time_type timeout() const;
|
||||
crl::time timeout() const;
|
||||
Qt::TimerType type() const;
|
||||
FnMut<void()> takeMethod();
|
||||
|
||||
private:
|
||||
crl::time_type _timeout = 0;
|
||||
crl::time _timeout = 0;
|
||||
Qt::TimerType _type = Qt::PreciseTimer;
|
||||
FnMut<void()> _method;
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
};
|
||||
|
||||
CallDelayedEvent::CallDelayedEvent(
|
||||
crl::time_type timeout,
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method)
|
||||
: QEvent(kCallDelayedEvent)
|
||||
@@ -58,7 +58,7 @@ CallDelayedEvent::CallDelayedEvent(
|
||||
Expects(_timeout >= 0 && _timeout < std::numeric_limits<int>::max());
|
||||
}
|
||||
|
||||
crl::time_type CallDelayedEvent::timeout() const {
|
||||
crl::time CallDelayedEvent::timeout() const {
|
||||
return _timeout;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ TimerObjectWrap::~TimerObjectWrap() {
|
||||
}
|
||||
|
||||
void TimerObjectWrap::call(
|
||||
crl::time_type timeout,
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method) {
|
||||
sendEvent(std::make_unique<CallDelayedEvent>(
|
||||
@@ -268,7 +268,6 @@ ConcurrentTimer::ConcurrentTimer(
|
||||
}
|
||||
|
||||
Fn<void()> ConcurrentTimer::createAdjuster() {
|
||||
auto guards = base::make_binary_guard();
|
||||
_guard = std::make_shared<bool>(true);
|
||||
return [=, runner = _runner, guard = std::weak_ptr<bool>(_guard)] {
|
||||
runner([=] {
|
||||
@@ -281,7 +280,7 @@ Fn<void()> ConcurrentTimer::createAdjuster() {
|
||||
}
|
||||
|
||||
void ConcurrentTimer::start(
|
||||
TimeMs timeout,
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
Repeat repeat) {
|
||||
_type = type;
|
||||
@@ -290,16 +289,14 @@ void ConcurrentTimer::start(
|
||||
setTimeout(timeout);
|
||||
|
||||
cancelAndSchedule(_timeout);
|
||||
_next = crl::time() + _timeout;
|
||||
_next = crl::now() + _timeout;
|
||||
}
|
||||
|
||||
void ConcurrentTimer::cancelAndSchedule(int timeout) {
|
||||
auto guards = base::make_binary_guard();
|
||||
_running = std::move(guards.first);
|
||||
auto method = [
|
||||
=,
|
||||
runner = _runner,
|
||||
guard = std::move(guards.second)
|
||||
guard = _running.make_guard()
|
||||
]() mutable {
|
||||
if (!guard) {
|
||||
return;
|
||||
@@ -319,7 +316,7 @@ void ConcurrentTimer::timerEvent() {
|
||||
if (_adjusted) {
|
||||
start(_timeout, _type, repeat());
|
||||
} else {
|
||||
_next = crl::time() + _timeout;
|
||||
_next = crl::now() + _timeout;
|
||||
}
|
||||
} else {
|
||||
cancel();
|
||||
@@ -338,12 +335,12 @@ void ConcurrentTimer::cancel() {
|
||||
}
|
||||
}
|
||||
|
||||
TimeMs ConcurrentTimer::remainingTime() const {
|
||||
crl::time ConcurrentTimer::remainingTime() const {
|
||||
if (!isActive()) {
|
||||
return -1;
|
||||
}
|
||||
const auto now = crl::time();
|
||||
return (_next > now) ? (_next - now) : TimeMs(0);
|
||||
const auto now = crl::now();
|
||||
return (_next > now) ? (_next - now) : crl::time(0);
|
||||
}
|
||||
|
||||
void ConcurrentTimer::adjust() {
|
||||
@@ -354,7 +351,7 @@ void ConcurrentTimer::adjust() {
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentTimer::setTimeout(TimeMs timeout) {
|
||||
void ConcurrentTimer::setTimeout(crl::time timeout) {
|
||||
Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max());
|
||||
|
||||
_timeout = static_cast<unsigned int>(timeout);
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
~TimerObjectWrap();
|
||||
|
||||
void call(
|
||||
crl::time_type timeout,
|
||||
crl::time timeout,
|
||||
Qt::TimerType type,
|
||||
FnMut<void()> method);
|
||||
void cancel();
|
||||
@@ -67,8 +67,8 @@ public:
|
||||
crl::weak_on_queue<Object> weak,
|
||||
Fn<void()> callback = nullptr);
|
||||
|
||||
static Qt::TimerType DefaultType(TimeMs timeout) {
|
||||
constexpr auto kThreshold = TimeMs(1000);
|
||||
static Qt::TimerType DefaultType(crl::time timeout) {
|
||||
constexpr auto kThreshold = crl::time(1000);
|
||||
return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer;
|
||||
}
|
||||
|
||||
@@ -76,19 +76,19 @@ public:
|
||||
_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void callOnce(TimeMs timeout) {
|
||||
void callOnce(crl::time timeout) {
|
||||
callOnce(timeout, DefaultType(timeout));
|
||||
}
|
||||
|
||||
void callEach(TimeMs timeout) {
|
||||
void callEach(crl::time timeout) {
|
||||
callEach(timeout, DefaultType(timeout));
|
||||
}
|
||||
|
||||
void callOnce(TimeMs timeout, Qt::TimerType type) {
|
||||
void callOnce(crl::time timeout, Qt::TimerType type) {
|
||||
start(timeout, type, Repeat::SingleShot);
|
||||
}
|
||||
|
||||
void callEach(TimeMs timeout, Qt::TimerType type) {
|
||||
void callEach(crl::time timeout, Qt::TimerType type) {
|
||||
start(timeout, type, Repeat::Interval);
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
}
|
||||
|
||||
void cancel();
|
||||
TimeMs remainingTime() const;
|
||||
crl::time remainingTime() const;
|
||||
|
||||
private:
|
||||
enum class Repeat : unsigned {
|
||||
@@ -105,12 +105,12 @@ private:
|
||||
SingleShot = 1,
|
||||
};
|
||||
Fn<void()> createAdjuster();
|
||||
void start(TimeMs timeout, Qt::TimerType type, Repeat repeat);
|
||||
void start(crl::time timeout, Qt::TimerType type, Repeat repeat);
|
||||
void adjust();
|
||||
|
||||
void cancelAndSchedule(int timeout);
|
||||
|
||||
void setTimeout(TimeMs timeout);
|
||||
void setTimeout(crl::time timeout);
|
||||
int timeout() const;
|
||||
|
||||
void timerEvent();
|
||||
@@ -127,7 +127,7 @@ private:
|
||||
details::TimerObjectWrap _object;
|
||||
Fn<void()> _callback;
|
||||
base::binary_guard _running;
|
||||
TimeMs _next = 0;
|
||||
crl::time _next = 0;
|
||||
int _timeout = 0;
|
||||
|
||||
Qt::TimerType _type : 2;
|
||||
|
||||
@@ -686,6 +686,40 @@ public:
|
||||
}
|
||||
return { where, false };
|
||||
}
|
||||
std::pair<iterator, bool> insert_or_assign(
|
||||
const Key &key,
|
||||
const Type &value) {
|
||||
if (this->empty() || this->compare()(key, this->front().first)) {
|
||||
this->impl().emplace_front(key, value);
|
||||
return { this->begin(), true };
|
||||
} else if (this->compare()(this->back().first, key)) {
|
||||
this->impl().emplace_back(key, value);
|
||||
return { this->end() - 1, true };
|
||||
}
|
||||
auto where = this->getLowerBound(key);
|
||||
if (this->compare()(key, where->first)) {
|
||||
return { this->impl().insert(where, value_type(key, value)), true };
|
||||
}
|
||||
where->second = value;
|
||||
return { where, false };
|
||||
}
|
||||
std::pair<iterator, bool> insert_or_assign(
|
||||
const Key &key,
|
||||
Type &&value) {
|
||||
if (this->empty() || this->compare()(key, this->front().first)) {
|
||||
this->impl().emplace_front(key, std::move(value));
|
||||
return { this->begin(), true };
|
||||
} else if (this->compare()(this->back().first, key)) {
|
||||
this->impl().emplace_back(key, std::move(value));
|
||||
return { this->end() - 1, true };
|
||||
}
|
||||
auto where = this->getLowerBound(key);
|
||||
if (this->compare()(key, where->first)) {
|
||||
return { this->impl().insert(where, value_type(key, std::move(value))), true };
|
||||
}
|
||||
where->second = std::move(value);
|
||||
return { where, false };
|
||||
}
|
||||
template <typename... Args>
|
||||
std::pair<iterator, bool> emplace(
|
||||
const Key &key,
|
||||
@@ -695,6 +729,14 @@ public:
|
||||
Type(std::forward<Args>(args)...)));
|
||||
}
|
||||
template <typename... Args>
|
||||
std::pair<iterator, bool> emplace_or_assign(
|
||||
const Key &key,
|
||||
Args&&... args) {
|
||||
return this->insert_or_assign(
|
||||
key,
|
||||
Type(std::forward<Args>(args)...));
|
||||
}
|
||||
template <typename... Args>
|
||||
std::pair<iterator, bool> try_emplace(
|
||||
const Key &key,
|
||||
Args&&... args) {
|
||||
|
||||
@@ -602,36 +602,38 @@ public:
|
||||
using parent::contains;
|
||||
using parent::erase;
|
||||
|
||||
iterator insert(const Type &value) {
|
||||
std::pair<iterator, bool> insert(const Type &value) {
|
||||
if (this->empty() || this->compare()(value, this->front())) {
|
||||
this->impl().push_front(value);
|
||||
return this->begin();
|
||||
return std::make_pair(this->begin(), true);
|
||||
} else if (this->compare()(this->back(), value)) {
|
||||
this->impl().push_back(value);
|
||||
return (this->end() - 1);
|
||||
return std::make_pair(this->end() - 1, true);
|
||||
}
|
||||
auto where = this->getLowerBound(value);
|
||||
if (this->compare()(value, *where)) {
|
||||
return this->impl().insert(where, value);
|
||||
return std::make_pair(this->impl().insert(where, value), true);
|
||||
}
|
||||
return this->end();
|
||||
return std::make_pair(where, false);
|
||||
}
|
||||
iterator insert(Type &&value) {
|
||||
std::pair<iterator, bool> insert(Type &&value) {
|
||||
if (this->empty() || this->compare()(value, this->front())) {
|
||||
this->impl().push_front(std::move(value));
|
||||
return this->begin();
|
||||
return std::make_pair(this->begin(), true);
|
||||
} else if (this->compare()(this->back(), value)) {
|
||||
this->impl().push_back(std::move(value));
|
||||
return (this->end() - 1);
|
||||
return std::make_pair(this->end() - 1, true);
|
||||
}
|
||||
auto where = this->getLowerBound(value);
|
||||
if (this->compare()(value, *where)) {
|
||||
return this->impl().insert(where, std::move(value));
|
||||
return std::make_pair(
|
||||
this->impl().insert(where, std::move(value)),
|
||||
true);
|
||||
}
|
||||
return this->end();
|
||||
return std::make_pair(where, false);
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator emplace(Args&&... args) {
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
return this->insert(Type(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,20 @@ public:
|
||||
|
||||
using value_type = typename Container::value_type;
|
||||
using difference_type = typename Container::difference_type;
|
||||
using pointer = typename Container::pointer;
|
||||
using reference = typename Container::reference;
|
||||
using pointer = std::conditional_t<
|
||||
std::is_const_v<Container>,
|
||||
typename Container::const_pointer,
|
||||
typename Container::pointer>;
|
||||
using reference = std::conditional_t<
|
||||
std::is_const_v<Container>,
|
||||
typename Container::const_reference,
|
||||
typename Container::reference>;
|
||||
using base_type = std::conditional_t<
|
||||
std::is_const_v<Container>,
|
||||
typename Container::const_iterator,
|
||||
typename Container::iterator>;
|
||||
|
||||
index_based_iterator(
|
||||
Container *container,
|
||||
typename Container::iterator impl)
|
||||
index_based_iterator(Container *container, base_type impl)
|
||||
: _container(container)
|
||||
, _index(impl - _container->begin()) {
|
||||
}
|
||||
@@ -99,7 +107,7 @@ public:
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
typename Container::iterator base() const {
|
||||
base_type base() const {
|
||||
return _container->begin() + _index;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ extern "C" {
|
||||
#include <openssl/evp.h>
|
||||
} // extern "C"
|
||||
|
||||
#ifdef small
|
||||
#undef small
|
||||
#endif // small
|
||||
|
||||
namespace openssl {
|
||||
|
||||
class Context {
|
||||
|
||||
@@ -40,7 +40,7 @@ Timer::Timer(Fn<void()> callback)
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void Timer::start(TimeMs timeout, Qt::TimerType type, Repeat repeat) {
|
||||
void Timer::start(crl::time timeout, Qt::TimerType type, Repeat repeat) {
|
||||
cancel();
|
||||
|
||||
_type = type;
|
||||
@@ -49,7 +49,7 @@ void Timer::start(TimeMs timeout, Qt::TimerType type, Repeat repeat) {
|
||||
setTimeout(timeout);
|
||||
_timerId = startTimer(_timeout, _type);
|
||||
if (_timerId) {
|
||||
_next = crl::time() + _timeout;
|
||||
_next = crl::now() + _timeout;
|
||||
} else {
|
||||
_next = 0;
|
||||
}
|
||||
@@ -61,12 +61,12 @@ void Timer::cancel() {
|
||||
}
|
||||
}
|
||||
|
||||
TimeMs Timer::remainingTime() const {
|
||||
crl::time Timer::remainingTime() const {
|
||||
if (!isActive()) {
|
||||
return -1;
|
||||
}
|
||||
auto now = crl::time();
|
||||
return (_next > now) ? (_next - now) : TimeMs(0);
|
||||
const auto now = crl::now();
|
||||
return (_next > now) ? (_next - now) : crl::time(0);
|
||||
}
|
||||
|
||||
void Timer::Adjust() {
|
||||
@@ -87,7 +87,7 @@ void Timer::adjust() {
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::setTimeout(TimeMs timeout) {
|
||||
void Timer::setTimeout(crl::time timeout) {
|
||||
Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max());
|
||||
|
||||
_timeout = static_cast<unsigned int>(timeout);
|
||||
@@ -102,7 +102,7 @@ void Timer::timerEvent(QTimerEvent *e) {
|
||||
if (_adjusted) {
|
||||
start(_timeout, _type, repeat());
|
||||
} else {
|
||||
_next = crl::time() + _timeout;
|
||||
_next = crl::now() + _timeout;
|
||||
}
|
||||
} else {
|
||||
cancel();
|
||||
@@ -114,7 +114,7 @@ void Timer::timerEvent(QTimerEvent *e) {
|
||||
}
|
||||
|
||||
int DelayedCallTimer::call(
|
||||
TimeMs timeout,
|
||||
crl::time timeout,
|
||||
FnMut<void()> callback,
|
||||
Qt::TimerType type) {
|
||||
Expects(timeout >= 0);
|
||||
|
||||
@@ -21,8 +21,8 @@ public:
|
||||
Fn<void()> callback = nullptr);
|
||||
explicit Timer(Fn<void()> callback = nullptr);
|
||||
|
||||
static Qt::TimerType DefaultType(TimeMs timeout) {
|
||||
constexpr auto kThreshold = TimeMs(1000);
|
||||
static Qt::TimerType DefaultType(crl::time timeout) {
|
||||
constexpr auto kThreshold = crl::time(1000);
|
||||
return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer;
|
||||
}
|
||||
|
||||
@@ -30,19 +30,19 @@ public:
|
||||
_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void callOnce(TimeMs timeout) {
|
||||
void callOnce(crl::time timeout) {
|
||||
callOnce(timeout, DefaultType(timeout));
|
||||
}
|
||||
|
||||
void callEach(TimeMs timeout) {
|
||||
void callEach(crl::time timeout) {
|
||||
callEach(timeout, DefaultType(timeout));
|
||||
}
|
||||
|
||||
void callOnce(TimeMs timeout, Qt::TimerType type) {
|
||||
void callOnce(crl::time timeout, Qt::TimerType type) {
|
||||
start(timeout, type, Repeat::SingleShot);
|
||||
}
|
||||
|
||||
void callEach(TimeMs timeout, Qt::TimerType type) {
|
||||
void callEach(crl::time timeout, Qt::TimerType type) {
|
||||
start(timeout, type, Repeat::Interval);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
}
|
||||
|
||||
void cancel();
|
||||
TimeMs remainingTime() const;
|
||||
crl::time remainingTime() const;
|
||||
|
||||
static void Adjust();
|
||||
|
||||
@@ -63,10 +63,10 @@ private:
|
||||
Interval = 0,
|
||||
SingleShot = 1,
|
||||
};
|
||||
void start(TimeMs timeout, Qt::TimerType type, Repeat repeat);
|
||||
void start(crl::time timeout, Qt::TimerType type, Repeat repeat);
|
||||
void adjust();
|
||||
|
||||
void setTimeout(TimeMs timeout);
|
||||
void setTimeout(crl::time timeout);
|
||||
int timeout() const;
|
||||
|
||||
void setRepeat(Repeat repeat) {
|
||||
@@ -77,7 +77,7 @@ private:
|
||||
}
|
||||
|
||||
Fn<void()> _callback;
|
||||
TimeMs _next = 0;
|
||||
crl::time _next = 0;
|
||||
int _timeout = 0;
|
||||
int _timerId = 0;
|
||||
|
||||
@@ -89,7 +89,7 @@ private:
|
||||
|
||||
class DelayedCallTimer final : private QObject {
|
||||
public:
|
||||
int call(TimeMs timeout, FnMut<void()> callback) {
|
||||
int call(crl::time timeout, FnMut<void()> callback) {
|
||||
return call(
|
||||
timeout,
|
||||
std::move(callback),
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
}
|
||||
|
||||
int call(
|
||||
TimeMs timeout,
|
||||
crl::time timeout,
|
||||
FnMut<void()> callback,
|
||||
Qt::TimerType type);
|
||||
void cancel(int callId);
|
||||
|
||||
@@ -57,12 +57,21 @@ public:
|
||||
}
|
||||
|
||||
~has_weak_ptr() {
|
||||
if (auto alive = _alive.load()) {
|
||||
if (const auto alive = _alive.load()) {
|
||||
alive->value.store(nullptr);
|
||||
details::decrement(alive);
|
||||
}
|
||||
}
|
||||
|
||||
friend inline void invalidate_weak_ptrs(has_weak_ptr *object) {
|
||||
if (auto alive = object->_alive.load()) {
|
||||
if (object->_alive.compare_exchange_strong(alive, nullptr)) {
|
||||
alive->value.store(nullptr);
|
||||
details::decrement(alive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Child>
|
||||
friend class weak_ptr;
|
||||
|
||||
@@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "zip.h"
|
||||
#include "unzip.h"
|
||||
|
||||
#ifdef small
|
||||
#undef small
|
||||
#endif // small
|
||||
|
||||
namespace zlib {
|
||||
namespace internal {
|
||||
|
||||
|
||||
@@ -436,7 +436,7 @@ QPointer<Ui::IconButton> AbstractBox::addTopButton(const style::IconButton &st,
|
||||
return result;
|
||||
}
|
||||
|
||||
void AbstractBox::setDimensions(int newWidth, int maxHeight) {
|
||||
void AbstractBox::setDimensions(int newWidth, int maxHeight, bool forceCenterPosition) {
|
||||
_maxContentHeight = maxHeight;
|
||||
|
||||
auto fullHeight = countFullHeight();
|
||||
@@ -447,8 +447,13 @@ void AbstractBox::setDimensions(int newWidth, int maxHeight) {
|
||||
resize(newWidth, countRealHeight());
|
||||
auto newGeometry = geometry();
|
||||
auto parentHeight = parentWidget()->height();
|
||||
if (newGeometry.top() + newGeometry.height() + st::boxVerticalMargin > parentHeight) {
|
||||
auto newTop = qMax(parentHeight - int(st::boxVerticalMargin) - newGeometry.height(), (parentHeight - newGeometry.height()) / 2);
|
||||
if (newGeometry.top() + newGeometry.height() + st::boxVerticalMargin > parentHeight
|
||||
|| forceCenterPosition) {
|
||||
const auto top1 = parentHeight - int(st::boxVerticalMargin) - newGeometry.height();
|
||||
const auto top2 = (parentHeight - newGeometry.height()) / 2;
|
||||
const auto newTop = forceCenterPosition
|
||||
? std::min(top1, top2)
|
||||
: std::max(top1, top2);
|
||||
if (newTop != newGeometry.top()) {
|
||||
move(newGeometry.left(), newTop);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,10 @@ public:
|
||||
object_ptr<BoxContent> box,
|
||||
LayerOptions options,
|
||||
anim::type animated) = 0;
|
||||
virtual void setDimensions(int newWidth, int maxHeight) = 0;
|
||||
virtual void setDimensions(
|
||||
int newWidth,
|
||||
int maxHeight,
|
||||
bool forceCenterPosition = false) = 0;
|
||||
virtual void setNoContentMargin(bool noContentMargin) = 0;
|
||||
virtual bool isBoxShown() const = 0;
|
||||
virtual void closeBox() = 0;
|
||||
@@ -158,8 +161,11 @@ protected:
|
||||
}
|
||||
getDelegate()->setNoContentMargin(noContentMargin);
|
||||
}
|
||||
void setDimensions(int newWidth, int maxHeight) {
|
||||
getDelegate()->setDimensions(newWidth, maxHeight);
|
||||
void setDimensions(
|
||||
int newWidth,
|
||||
int maxHeight,
|
||||
bool forceCenterPosition = false) {
|
||||
getDelegate()->setDimensions(newWidth, maxHeight, forceCenterPosition);
|
||||
}
|
||||
void setDimensionsToContent(
|
||||
int newWidth,
|
||||
@@ -264,7 +270,10 @@ public:
|
||||
void updateButtonsPositions() override;
|
||||
QPointer<QWidget> outerContainer() override;
|
||||
|
||||
void setDimensions(int newWidth, int maxHeight) override;
|
||||
void setDimensions(
|
||||
int newWidth,
|
||||
int maxHeight,
|
||||
bool forceCenterPosition = false) override;
|
||||
|
||||
void setNoContentMargin(bool noContentMargin) override {
|
||||
if (_noContentMargin != noContentMargin) {
|
||||
|
||||
@@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "messenger.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "base/flat_set.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
@@ -20,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peers/edit_participant_box.h"
|
||||
#include "boxes/peers/edit_participants_box.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/application.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "apiwrap.h"
|
||||
@@ -65,7 +66,7 @@ style::InputField CreateBioFieldStyle() {
|
||||
|
||||
QString PeerFloodErrorText(PeerFloodType type) {
|
||||
auto link = textcmdLink(
|
||||
Messenger::Instance().createInternalLinkFull(qsl("spambot")),
|
||||
Core::App().createInternalLinkFull(qsl("spambot")),
|
||||
lang(lng_cant_more_info));
|
||||
if (type == PeerFloodType::InviteGroup) {
|
||||
return lng_cant_invite_not_contact(lt_more_info, link);
|
||||
@@ -321,8 +322,9 @@ bool AddContactBox::onSaveUserFail(const RPCError &error) {
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
|
||||
_addRequest = 0;
|
||||
QString err(error.type());
|
||||
QString firstName = _first->getLastText().trimmed(), lastName = _last->getLastText().trimmed();
|
||||
const auto &err = error.type();
|
||||
const auto firstName = _first->getLastText().trimmed();
|
||||
const auto lastName = _last->getLastText().trimmed();
|
||||
if (err == "CHAT_TITLE_NOT_MODIFIED") {
|
||||
_user->setName(firstName, lastName, _user->nameOrPhone, _user->username);
|
||||
closeBox();
|
||||
@@ -340,14 +342,14 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
|
||||
if (!isBoxShown() || !App::main()) return;
|
||||
|
||||
const auto &d = res.c_contacts_importedContacts();
|
||||
App::feedUsers(d.vusers);
|
||||
Auth().data().processUsers(d.vusers);
|
||||
|
||||
const auto &v = d.vimported.v;
|
||||
const auto user = [&]() -> UserData* {
|
||||
if (!v.isEmpty()) {
|
||||
auto &c = v.front().c_importedContact();
|
||||
if (c.vclient_id.v == _contactId) {
|
||||
return App::userLoaded(c.vuser_id.v);
|
||||
return Auth().data().userLoaded(c.vuser_id.v);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@@ -368,7 +370,7 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
|
||||
|
||||
void AddContactBox::onSaveUserDone(const MTPcontacts_ImportedContacts &res) {
|
||||
auto &d = res.c_contacts_importedContacts();
|
||||
App::feedUsers(d.vusers);
|
||||
Auth().data().processUsers(d.vusers);
|
||||
closeBox();
|
||||
}
|
||||
|
||||
@@ -535,7 +537,7 @@ void GroupInfoBox::createGroup(
|
||||
: std::nullopt;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return App::chat(chats->front().c_chat().vid.v);
|
||||
return Auth().data().chat(chats->front().c_chat().vid.v);
|
||||
}
|
||||
| [&](not_null<ChatData*> chat) {
|
||||
if (!image.isNull()) {
|
||||
@@ -643,7 +645,7 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
|
||||
: std::nullopt;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return App::channel(chats->front().c_channel().vid.v);
|
||||
return Auth().data().channel(chats->front().c_channel().vid.v);
|
||||
}
|
||||
| [&](not_null<ChannelData*> channel) {
|
||||
auto image = _photo->takeResultImage();
|
||||
@@ -1153,7 +1155,7 @@ void EditNameBox::save() {
|
||||
}
|
||||
|
||||
void EditNameBox::saveSelfDone(const MTPUser &user) {
|
||||
App::feedUsers(MTP_vector<MTPUser>(1, user));
|
||||
_user->owner().processUsers(MTP_vector<MTPUser>(1, user));
|
||||
closeBox();
|
||||
}
|
||||
|
||||
@@ -1194,7 +1196,7 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) :
|
||||
return data.vchats.v;
|
||||
});
|
||||
for (const auto &chat : chats) {
|
||||
if (const auto peer = App::feedChat(chat)) {
|
||||
if (const auto peer = Auth().data().processChat(chat)) {
|
||||
if (!peer->isChannel() || peer->userName().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -1207,7 +1209,7 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) :
|
||||
Ui::NameTextOptions());
|
||||
row.status.setText(
|
||||
st::defaultTextStyle,
|
||||
Messenger::Instance().createInternalLink(
|
||||
Core::App().createInternalLink(
|
||||
textcmdLink(1, peer->userName())),
|
||||
Ui::DialogTextOptions());
|
||||
_rows.push_back(std::move(row));
|
||||
@@ -1282,7 +1284,7 @@ void RevokePublicLinkBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
setCursor((_selected || _pressed) ? style::cur_pointer : style::cur_default);
|
||||
if (pressed && pressed == _selected) {
|
||||
auto text_method = pressed->isMegagroup() ? lng_channels_too_much_public_revoke_confirm_group : lng_channels_too_much_public_revoke_confirm_channel;
|
||||
auto text = text_method(lt_link, Messenger::Instance().createInternalLink(pressed->userName()), lt_group, pressed->name);
|
||||
auto text = text_method(lt_link, Core::App().createInternalLink(pressed->userName()), lt_group, pressed->name);
|
||||
auto confirmText = lang(lng_channels_too_much_public_revoke);
|
||||
_weakRevokeConfirmBox = Ui::show(Box<ConfirmBox>(text, confirmText, crl::guard(this, [this, pressed]() {
|
||||
if (_revokeRequestId) return;
|
||||
|
||||
@@ -56,8 +56,17 @@ void AutoDownloadBox::setupContent() {
|
||||
this,
|
||||
std::move(wrap)));
|
||||
|
||||
static const auto kHidden = {
|
||||
Type::Video,
|
||||
Type::Music,
|
||||
Type::VoiceMessage
|
||||
};
|
||||
|
||||
const auto values = Ui::CreateChild<base::flat_map<Type, int>>(content);
|
||||
const auto add = [&](Type type, LangKey label) {
|
||||
if (ranges::find(kHidden, type) != end(kHidden)) {
|
||||
return;
|
||||
}
|
||||
const auto value = settings->bytesLimit(_source, type);
|
||||
AddButton(
|
||||
content,
|
||||
@@ -111,16 +120,6 @@ void AutoDownloadBox::setupContent() {
|
||||
*limit = value;
|
||||
limits->fire_copy(value);
|
||||
});
|
||||
const auto save = [=](
|
||||
Type type,
|
||||
std::pair<bool, bool> pair) {
|
||||
const auto limit = [](bool checked) {
|
||||
return checked ? kMaxBytesLimit : 0;
|
||||
};
|
||||
settings->setBytesLimit(Source::User, type, limit(pair.first));
|
||||
settings->setBytesLimit(Source::Group, type, limit(pair.second));
|
||||
settings->setBytesLimit(Source::Channel, type, limit(pair.second));
|
||||
};
|
||||
|
||||
addButton(langFactory(lng_connection_save), [=] {
|
||||
auto allowMore = ranges::view::all(
|
||||
@@ -143,11 +142,26 @@ void AutoDownloadBox::setupContent() {
|
||||
return settings->bytesLimit(_source, type) != value;
|
||||
}) != end(*values);
|
||||
|
||||
const auto hiddenChanged = ranges::find_if(kHidden, [&](Type type) {
|
||||
const auto now = settings->bytesLimit(_source, type);
|
||||
return (now > 0) && (now != *limit);
|
||||
}) != end(kHidden);
|
||||
|
||||
if (changed) {
|
||||
for (const auto [type, enabled] : *values) {
|
||||
const auto value = enabled ? *limit : 0;
|
||||
settings->setBytesLimit(_source, type, value);
|
||||
}
|
||||
}
|
||||
if (hiddenChanged) {
|
||||
for (const auto type : kHidden) {
|
||||
const auto now = settings->bytesLimit(_source, type);
|
||||
if (now > 0) {
|
||||
settings->setBytesLimit(_source, type, *limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed || hiddenChanged) {
|
||||
Local::writeUserSettings();
|
||||
}
|
||||
if (allowMoreTypes.contains(Type::Photo)) {
|
||||
|
||||
@@ -8,97 +8,43 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/background_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "ui/effects/round_checkbox.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "auth_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_document.h"
|
||||
#include "boxes/background_preview_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "styles/style_overview.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kBackgroundsInRow = 3;
|
||||
constexpr auto kMaxWallPaperSlugLength = 255;
|
||||
|
||||
[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
|
||||
if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
|
||||
return false;
|
||||
QImage TakeMiddleSample(QImage original, QSize size) {
|
||||
size *= cIntRetinaFactor();
|
||||
const auto from = original.size();
|
||||
if (from.isEmpty()) {
|
||||
auto result = original.scaled(size);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
return result;
|
||||
}
|
||||
return ranges::find_if(slug, [](QChar ch) {
|
||||
return (ch != '.')
|
||||
&& (ch != '_')
|
||||
&& (ch != '-')
|
||||
&& (ch < '0' || ch > '9')
|
||||
&& (ch < 'a' || ch > 'z')
|
||||
&& (ch < 'A' || ch > 'Z');
|
||||
}) == slug.end();
|
||||
}
|
||||
|
||||
AdminLog::OwnedItem GenerateTextItem(
|
||||
not_null<HistoryView::ElementDelegate*> delegate,
|
||||
not_null<History*> history,
|
||||
const QString &text,
|
||||
bool out) {
|
||||
Expects(history->peer->isUser());
|
||||
|
||||
using Flag = MTPDmessage::Flag;
|
||||
const auto id = ServerMaxMsgId + (ServerMaxMsgId / 3) + (out ? 1 : 0);
|
||||
const auto flags = Flag::f_entities
|
||||
| Flag::f_from_id
|
||||
| (out ? Flag::f_out : Flag(0));
|
||||
const auto replyTo = 0;
|
||||
const auto viaBotId = 0;
|
||||
const auto item = new HistoryMessage(
|
||||
history,
|
||||
id,
|
||||
flags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
unixtime(),
|
||||
out ? history->session().userId() : peerToUser(history->peer->id),
|
||||
QString(),
|
||||
TextWithEntities{ TextUtilities::Clean(text) });
|
||||
return AdminLog::OwnedItem(delegate, item);
|
||||
}
|
||||
|
||||
QImage PrepareScaledFromFull(
|
||||
const QImage &image,
|
||||
Images::Option blur = Images::Option(0)) {
|
||||
const auto size = st::boxWideWidth;
|
||||
const auto width = std::max(image.width(), 1);
|
||||
const auto height = std::max(image.height(), 1);
|
||||
const auto takeWidth = (width > height)
|
||||
? (width * size / height)
|
||||
: size;
|
||||
const auto takeHeight = (width > height)
|
||||
? size
|
||||
: (height * size / width);
|
||||
return Images::prepare(
|
||||
image,
|
||||
takeWidth,
|
||||
takeHeight,
|
||||
Images::Option::Smooth | blur,
|
||||
size,
|
||||
size);
|
||||
}
|
||||
|
||||
QPixmap PrepareScaledFromThumb(ImagePtr thumb) {
|
||||
return thumb->loaded()
|
||||
? App::pixmapFromImageInPlace(PrepareScaledFromFull(
|
||||
thumb->original(),
|
||||
Images::Option::Blurred))
|
||||
: QPixmap();
|
||||
const auto take = (from.width() * size.height()
|
||||
> from.height() * size.width())
|
||||
? QSize(size.width() * from.height() / size.height(), from.height())
|
||||
: QSize(from.width(), size.height() * from.width() / size.width());
|
||||
auto result = original.copy(
|
||||
(from.width() - take.width()) / 2,
|
||||
(from.height() - take.height()) / 2,
|
||||
take.width(),
|
||||
take.height()
|
||||
).scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -110,9 +56,10 @@ class BackgroundBox::Inner
|
||||
public:
|
||||
Inner(QWidget *parent);
|
||||
|
||||
void setBackgroundChosenCallback(Fn<void(int index)> callback) {
|
||||
_backgroundChosenCallback = std::move(callback);
|
||||
}
|
||||
rpl::producer<Data::WallPaper> chooseEvents() const;
|
||||
rpl::producer<Data::WallPaper> removeRequests() const;
|
||||
|
||||
void removePaper(const Data::WallPaper &data);
|
||||
|
||||
~Inner();
|
||||
|
||||
@@ -123,14 +70,51 @@ protected:
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
private:
|
||||
void updateWallpapers();
|
||||
struct Paper {
|
||||
Data::WallPaper data;
|
||||
mutable QPixmap thumbnail;
|
||||
};
|
||||
struct Selected {
|
||||
int index = 0;
|
||||
inline bool operator==(const Selected &other) const {
|
||||
return index == other.index;
|
||||
}
|
||||
inline bool operator!=(const Selected &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
struct DeleteSelected {
|
||||
int index = 0;
|
||||
inline bool operator==(const DeleteSelected &other) const {
|
||||
return index == other.index;
|
||||
}
|
||||
inline bool operator!=(const DeleteSelected &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
using Selection = base::optional_variant<Selected, DeleteSelected>;
|
||||
|
||||
Fn<void(int index)> _backgroundChosenCallback;
|
||||
int getSelectionIndex(const Selection &selection) const;
|
||||
void repaintPaper(int index);
|
||||
void resizeToContentAndPreload();
|
||||
void updatePapers();
|
||||
void requestPapers();
|
||||
void sortPapers();
|
||||
void paintPaper(
|
||||
Painter &p,
|
||||
const Paper &paper,
|
||||
int column,
|
||||
int row) const;
|
||||
void validatePaperThumbnail(const Paper &paper) const;
|
||||
|
||||
int _over = -1;
|
||||
int _overDown = -1;
|
||||
std::vector<Paper> _papers;
|
||||
|
||||
Selection _over;
|
||||
Selection _overDown;
|
||||
|
||||
std::unique_ptr<Ui::RoundCheckbox> _check; // this is not a widget
|
||||
rpl::event_stream<Data::WallPaper> _backgroundChosen;
|
||||
rpl::event_stream<Data::WallPaper> _backgroundRemove;
|
||||
|
||||
};
|
||||
|
||||
@@ -145,64 +129,126 @@ void BackgroundBox::prepare() {
|
||||
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
|
||||
|
||||
_inner = setInnerWidget(object_ptr<Inner>(this), st::backgroundScroll);
|
||||
_inner->setBackgroundChosenCallback([=](int index) {
|
||||
backgroundChosen(index);
|
||||
});
|
||||
|
||||
_inner->chooseEvents(
|
||||
) | rpl::start_with_next([](const Data::WallPaper &paper) {
|
||||
Ui::show(Box<BackgroundPreviewBox>(paper), LayerOption::KeepOther);
|
||||
}, _inner->lifetime());
|
||||
|
||||
_inner->removeRequests(
|
||||
) | rpl::start_with_next([=](const Data::WallPaper &paper) {
|
||||
removePaper(paper);
|
||||
}, _inner->lifetime());
|
||||
}
|
||||
|
||||
void BackgroundBox::backgroundChosen(int index) {
|
||||
const auto &papers = Auth().data().wallpapers();
|
||||
if (index >= 0 && index < papers.size()) {
|
||||
App::main()->setChatBackground(papers[index]);
|
||||
}
|
||||
closeBox();
|
||||
void BackgroundBox::removePaper(const Data::WallPaper &paper) {
|
||||
const auto box = std::make_shared<QPointer<BoxContent>>();
|
||||
const auto remove = [=, weak = make_weak(this)]{
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
if (weak) {
|
||||
weak->_inner->removePaper(paper);
|
||||
}
|
||||
Auth().data().removeWallpaper(paper);
|
||||
Auth().api().request(MTPaccount_SaveWallPaper(
|
||||
paper.mtpInput(),
|
||||
MTP_bool(true),
|
||||
paper.mtpSettings()
|
||||
)).send();
|
||||
};
|
||||
*box = Ui::show(
|
||||
Box<ConfirmBox>(
|
||||
lang(lng_background_sure_delete),
|
||||
lang(lng_selected_delete),
|
||||
lang(lng_cancel),
|
||||
remove),
|
||||
LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
BackgroundBox::Inner::Inner(QWidget *parent) : RpWidget(parent)
|
||||
, _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [=] { update(); })) {
|
||||
_check->setChecked(true, Ui::RoundCheckbox::SetStyle::Fast);
|
||||
if (Auth().data().wallpapers().empty()) {
|
||||
resize(kBackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
|
||||
resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
|
||||
} else {
|
||||
updateWallpapers();
|
||||
updatePapers();
|
||||
}
|
||||
request(MTPaccount_GetWallPapers(
|
||||
MTP_int(Auth().data().wallpapersHash())
|
||||
)).done([=](const MTPaccount_WallPapers &result) {
|
||||
if (Auth().data().updateWallpapers(result)) {
|
||||
updateWallpapers();
|
||||
}
|
||||
}).send();
|
||||
requestPapers();
|
||||
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
|
||||
subscribe(Window::Theme::Background(), [=](const Window::Theme::BackgroundUpdate &update) {
|
||||
using Update = Window::Theme::BackgroundUpdate;
|
||||
subscribe(Window::Theme::Background(), [=](const Update &update) {
|
||||
if (update.paletteChanged()) {
|
||||
_check->invalidateCache();
|
||||
} else if (update.type == Update::Type::New) {
|
||||
sortPapers();
|
||||
requestPapers();
|
||||
this->update();
|
||||
}
|
||||
});
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::updateWallpapers() {
|
||||
const auto &papers = Auth().data().wallpapers();
|
||||
const auto count = papers.size();
|
||||
void BackgroundBox::Inner::requestPapers() {
|
||||
request(MTPaccount_GetWallPapers(
|
||||
MTP_int(Auth().data().wallpapersHash())
|
||||
)).done([=](const MTPaccount_WallPapers &result) {
|
||||
if (Auth().data().updateWallpapers(result)) {
|
||||
updatePapers();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::sortPapers() {
|
||||
const auto current = Window::Theme::Background()->id();
|
||||
const auto night = Window::Theme::IsNightMode();
|
||||
ranges::stable_sort(_papers, std::greater<>(), [&](const Paper &paper) {
|
||||
const auto &data = paper.data;
|
||||
return std::make_tuple(
|
||||
data.id() == current,
|
||||
night ? data.isDark() : !data.isDark(),
|
||||
!data.isDefault() && !data.isLocal(),
|
||||
!data.isDefault() && data.isLocal());
|
||||
});
|
||||
if (!_papers.empty() && _papers.front().data.id() == current) {
|
||||
_papers.front().data = _papers.front().data.withParamsFrom(
|
||||
Window::Theme::Background()->paper());
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::updatePapers() {
|
||||
_over = _overDown = Selection();
|
||||
|
||||
_papers = Auth().data().wallpapers(
|
||||
) | ranges::view::filter([](const Data::WallPaper &paper) {
|
||||
return !paper.isPattern() || paper.backgroundColor().has_value();
|
||||
}) | ranges::view::transform([](const Data::WallPaper &paper) {
|
||||
return Paper{ paper };
|
||||
}) | ranges::to_vector;
|
||||
sortPapers();
|
||||
resizeToContentAndPreload();
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::resizeToContentAndPreload() {
|
||||
const auto count = _papers.size();
|
||||
const auto rows = (count / kBackgroundsInRow)
|
||||
+ (count % kBackgroundsInRow ? 1 : 0);
|
||||
|
||||
resize(kBackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, rows * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
|
||||
resize(st::boxWideWidth, rows * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
|
||||
|
||||
const auto preload = kBackgroundsInRow * 3;
|
||||
for (const auto &paper : papers | ranges::view::take(preload)) {
|
||||
paper.thumb->load(Data::FileOrigin());
|
||||
for (const auto &paper : _papers | ranges::view::take(preload)) {
|
||||
paper.data.loadThumbnail();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
QRect r(e->rect());
|
||||
Painter p(this);
|
||||
|
||||
const auto &papers = Auth().data().wallpapers();
|
||||
if (papers.empty()) {
|
||||
if (_papers.empty()) {
|
||||
p.setFont(st::noContactsFont);
|
||||
p.setPen(st::noContactsColor);
|
||||
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center);
|
||||
@@ -210,7 +256,7 @@ void BackgroundBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
auto row = 0;
|
||||
auto column = 0;
|
||||
for (const auto &paper : papers) {
|
||||
for (const auto &paper : _papers) {
|
||||
const auto increment = gsl::finally([&] {
|
||||
++column;
|
||||
if (column == kBackgroundsInRow) {
|
||||
@@ -220,24 +266,67 @@ void BackgroundBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
});
|
||||
if ((st::backgroundSize.height() + st::backgroundPadding) * (row + 1) <= r.top()) {
|
||||
continue;
|
||||
} else if ((st::backgroundSize.height() + st::backgroundPadding) * row >= r.top() + r.height()) {
|
||||
break;
|
||||
}
|
||||
paintPaper(p, paper, column, row);
|
||||
}
|
||||
}
|
||||
|
||||
paper.thumb->load(Data::FileOrigin());
|
||||
void BackgroundBox::Inner::validatePaperThumbnail(
|
||||
const Paper &paper) const {
|
||||
Expects(paper.data.thumbnail() != nullptr);
|
||||
|
||||
int x = st::backgroundPadding + column * (st::backgroundSize.width() + st::backgroundPadding);
|
||||
int y = st::backgroundPadding + row * (st::backgroundSize.height() + st::backgroundPadding);
|
||||
const auto thumbnail = paper.data.thumbnail();
|
||||
if (!paper.thumbnail.isNull()) {
|
||||
return;
|
||||
} else if (!thumbnail->loaded()) {
|
||||
thumbnail->load(paper.data.fileOrigin());
|
||||
return;
|
||||
}
|
||||
auto original = thumbnail->original();
|
||||
if (paper.data.isPattern()) {
|
||||
const auto color = *paper.data.backgroundColor();
|
||||
original = Data::PreparePatternImage(
|
||||
std::move(original),
|
||||
color,
|
||||
Data::PatternColor(color),
|
||||
paper.data.patternIntensity());
|
||||
}
|
||||
paper.thumbnail = App::pixmapFromImageInPlace(TakeMiddleSample(
|
||||
original,
|
||||
st::backgroundSize));
|
||||
paper.thumbnail.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
|
||||
const auto &pix = paper.thumb->pix(
|
||||
Data::FileOrigin(),
|
||||
st::backgroundSize.width(),
|
||||
st::backgroundSize.height());
|
||||
p.drawPixmap(x, y, pix);
|
||||
void BackgroundBox::Inner::paintPaper(
|
||||
Painter &p,
|
||||
const Paper &paper,
|
||||
int column,
|
||||
int row) const {
|
||||
const auto x = st::backgroundPadding + column * (st::backgroundSize.width() + st::backgroundPadding);
|
||||
const auto y = st::backgroundPadding + row * (st::backgroundSize.height() + st::backgroundPadding);
|
||||
validatePaperThumbnail(paper);
|
||||
if (!paper.thumbnail.isNull()) {
|
||||
p.drawPixmap(x, y, paper.thumbnail);
|
||||
}
|
||||
|
||||
if (paper.id == Window::Theme::Background()->id()) {
|
||||
auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
_check->paint(p, getms(), checkLeft, checkTop, width());
|
||||
}
|
||||
const auto over = _overDown ? _overDown : _over;
|
||||
if (paper.data.id() == Window::Theme::Background()->id()) {
|
||||
const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
_check->paint(p, crl::now(), checkLeft, checkTop, width());
|
||||
} else if (Data::IsCloudWallPaper(paper.data)
|
||||
&& !Data::IsDefaultWallPaper(paper.data)
|
||||
&& over.has_value()
|
||||
&& (&paper == &_papers[getSelectionIndex(over)])) {
|
||||
const auto deleteSelected = over.is<DeleteSelected>();
|
||||
const auto deletePos = QPoint(x + st::backgroundSize.width() - st::stickerPanDeleteIconBg.width(), y);
|
||||
p.setOpacity(deleteSelected ? st::stickerPanDeleteOpacityBgOver : st::stickerPanDeleteOpacityBg);
|
||||
st::stickerPanDeleteIconBg.paint(p, deletePos, width());
|
||||
p.setOpacity(deleteSelected ? st::stickerPanDeleteOpacityFgOver : st::stickerPanDeleteOpacityFg);
|
||||
st::stickerPanDeleteIconFg.paint(p, deletePos, width());
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,282 +339,105 @@ void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
||||
const auto skip = st::backgroundPadding;
|
||||
const auto row = int((y - skip) / (height + skip));
|
||||
const auto column = int((x - skip) / (width + skip));
|
||||
if (y - row * (height + skip) > skip + height) {
|
||||
return -1;
|
||||
} else if (x - column * (width + skip) > skip + width) {
|
||||
return -1;
|
||||
}
|
||||
const auto result = row * kBackgroundsInRow + column;
|
||||
return (result < Auth().data().wallpapers().size()) ? result : -1;
|
||||
if (y - row * (height + skip) > skip + height) {
|
||||
return Selection();
|
||||
} else if (x - column * (width + skip) > skip + width) {
|
||||
return Selection();
|
||||
} else if (result >= _papers.size()) {
|
||||
return Selection();
|
||||
}
|
||||
const auto deleteLeft = (column + 1) * (width + skip)
|
||||
- st::stickerPanDeleteIconBg.width();
|
||||
const auto deleteBottom = row * (height + skip) + skip
|
||||
+ st::stickerPanDeleteIconBg.height();
|
||||
const auto currentId = Window::Theme::Background()->id();
|
||||
const auto inDelete = (x >= deleteLeft)
|
||||
&& (y < deleteBottom)
|
||||
&& Data::IsCloudWallPaper(_papers[result].data)
|
||||
&& !Data::IsDefaultWallPaper(_papers[result].data)
|
||||
&& (currentId != _papers[result].data.id());
|
||||
return (result >= _papers.size())
|
||||
? Selection()
|
||||
: inDelete
|
||||
? Selection(DeleteSelected{ result })
|
||||
: Selection(Selected{ result });
|
||||
}();
|
||||
if (_over != newOver) {
|
||||
repaintPaper(getSelectionIndex(_over));
|
||||
_over = newOver;
|
||||
setCursor((_over >= 0 || _overDown >= 0)
|
||||
repaintPaper(getSelectionIndex(_over));
|
||||
setCursor((_over.has_value() || _overDown.has_value())
|
||||
? style::cur_pointer
|
||||
: style::cur_default);
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::repaintPaper(int index) {
|
||||
if (index < 0 || index >= _papers.size()) {
|
||||
return;
|
||||
}
|
||||
const auto row = (index / kBackgroundsInRow);
|
||||
const auto column = (index % kBackgroundsInRow);
|
||||
const auto width = st::backgroundSize.width();
|
||||
const auto height = st::backgroundSize.height();
|
||||
const auto skip = st::backgroundPadding;
|
||||
update(
|
||||
(width + skip) * column + skip,
|
||||
(height + skip) * row + skip,
|
||||
width,
|
||||
height);
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||
_overDown = _over;
|
||||
}
|
||||
|
||||
int BackgroundBox::Inner::getSelectionIndex(
|
||||
const Selection &selection) const {
|
||||
return selection.match([](const Selected &data) {
|
||||
return data.index;
|
||||
}, [](const DeleteSelected &data) {
|
||||
return data.index;
|
||||
}, [](std::nullopt_t) {
|
||||
return -1;
|
||||
});
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (_overDown == _over && _over >= 0) {
|
||||
if (_backgroundChosenCallback) {
|
||||
_backgroundChosenCallback(_over);
|
||||
if (base::take(_overDown) == _over && _over.has_value()) {
|
||||
const auto index = getSelectionIndex(_over);
|
||||
if (index >= 0 && index < _papers.size()) {
|
||||
if (base::get_if<DeleteSelected>(&_over)) {
|
||||
_backgroundRemove.fire_copy(_papers[index].data);
|
||||
} else if (base::get_if<Selected>(&_over)) {
|
||||
_backgroundChosen.fire_copy(_papers[index].data);
|
||||
}
|
||||
}
|
||||
} else if (_over < 0) {
|
||||
} else if (!_over.has_value()) {
|
||||
setCursor(style::cur_default);
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<Data::WallPaper> BackgroundBox::Inner::chooseEvents() const {
|
||||
return _backgroundChosen.events();
|
||||
}
|
||||
|
||||
auto BackgroundBox::Inner::removeRequests() const
|
||||
-> rpl::producer<Data::WallPaper> {
|
||||
return _backgroundRemove.events();
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::removePaper(const Data::WallPaper &data) {
|
||||
const auto i = ranges::find(
|
||||
_papers,
|
||||
data.id(),
|
||||
[](const Paper &paper) { return paper.data.id(); });
|
||||
if (i != end(_papers)) {
|
||||
_papers.erase(i);
|
||||
_over = _overDown = Selection();
|
||||
resizeToContentAndPreload();
|
||||
}
|
||||
}
|
||||
|
||||
BackgroundBox::Inner::~Inner() = default;
|
||||
|
||||
BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
QWidget*,
|
||||
const Data::WallPaper &paper)
|
||||
: _text1(GenerateTextItem(
|
||||
this,
|
||||
App::history(App::user(ServiceUserId)),
|
||||
lang(lng_background_text1),
|
||||
false))
|
||||
, _text2(GenerateTextItem(
|
||||
this,
|
||||
App::history(App::user(ServiceUserId)),
|
||||
lang(lng_background_text2),
|
||||
true))
|
||||
, _paper(paper)
|
||||
, _radial(animation(this, &BackgroundPreviewBox::step_radial)) {
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::prepare() {
|
||||
setTitle(langFactory(lng_background_header));
|
||||
|
||||
addButton(langFactory(lng_background_apply), [=] { apply(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
|
||||
_scaled = PrepareScaledFromThumb(_paper.thumb);
|
||||
checkLoadedDocument();
|
||||
|
||||
if (_paper.thumb && !_paper.thumb->loaded()) {
|
||||
_paper.thumb->loadEvenCancelled(Data::FileOriginWallpaper(
|
||||
_paper.id,
|
||||
_paper.accessHash));
|
||||
}
|
||||
if (_paper.document) {
|
||||
_paper.document->save(Data::FileOriginWallpaper(
|
||||
_paper.id,
|
||||
_paper.accessHash), QString());
|
||||
if (_paper.document->loading()) {
|
||||
_radial.start(_paper.document->progress());
|
||||
}
|
||||
}
|
||||
|
||||
_text1->setDisplayDate(true);
|
||||
_text1->initDimensions();
|
||||
_text1->resizeGetHeight(st::boxWideWidth);
|
||||
_text2->initDimensions();
|
||||
_text2->resizeGetHeight(st::boxWideWidth);
|
||||
|
||||
setDimensions(st::boxWideWidth, st::boxWideWidth);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::apply() {
|
||||
App::main()->setChatBackground(_paper, std::move(_full));
|
||||
closeBox();
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
const auto ms = getms();
|
||||
|
||||
if (const auto color = Window::Theme::GetWallPaperColor(_paper.slug)) {
|
||||
p.fillRect(e->rect(), *color);
|
||||
} else {
|
||||
if (_scaled.isNull()) {
|
||||
_scaled = PrepareScaledFromThumb(_paper.thumb);
|
||||
if (_scaled.isNull()) {
|
||||
p.fillRect(e->rect(), st::boxBg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
paintImage(p);
|
||||
paintRadial(p, ms);
|
||||
}
|
||||
paintTexts(p, ms);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintImage(Painter &p) {
|
||||
Expects(!_scaled.isNull());
|
||||
|
||||
const auto factor = cIntRetinaFactor();
|
||||
const auto size = st::boxWideWidth;
|
||||
const auto from = QRect(
|
||||
0,
|
||||
(size - height()) / 2 * factor,
|
||||
size * factor,
|
||||
height() * factor);
|
||||
p.drawPixmap(rect(), _scaled, from);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintRadial(Painter &p, TimeMs ms) {
|
||||
bool radial = false;
|
||||
float64 radialOpacity = 0;
|
||||
if (_radial.animating()) {
|
||||
_radial.step(ms);
|
||||
radial = _radial.animating();
|
||||
radialOpacity = _radial.opacity();
|
||||
}
|
||||
if (!radial) {
|
||||
return;
|
||||
}
|
||||
auto inner = radialRect();
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setOpacity(radialOpacity);
|
||||
p.setBrush(st::radialBg);
|
||||
|
||||
{
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawEllipse(inner);
|
||||
}
|
||||
|
||||
p.setOpacity(1);
|
||||
QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine)));
|
||||
_radial.draw(p, arc, st::radialLine, st::radialFg);
|
||||
}
|
||||
|
||||
QRect BackgroundPreviewBox::radialRect() const {
|
||||
const auto available = height()
|
||||
- st::historyPaddingBottom
|
||||
- _text1->height()
|
||||
- _text2->height()
|
||||
- st::historyPaddingBottom;
|
||||
return QRect(
|
||||
QPoint(
|
||||
(width() - st::radialSize.width()) / 2,
|
||||
(available - st::radialSize.height()) / 2),
|
||||
st::radialSize);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintTexts(Painter &p, TimeMs ms) {
|
||||
const auto height1 = _text1->height();
|
||||
const auto height2 = _text2->height();
|
||||
const auto top = height()
|
||||
- height1
|
||||
- height2
|
||||
- st::historyPaddingBottom;
|
||||
p.translate(0, top);
|
||||
_text1->draw(p, rect(), TextSelection(), ms);
|
||||
p.translate(0, height1);
|
||||
_text2->draw(p, rect(), TextSelection(), ms);
|
||||
p.translate(0, height2);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::step_radial(TimeMs ms, bool timer) {
|
||||
Expects(_paper.document != nullptr);
|
||||
|
||||
const auto document = _paper.document;
|
||||
const auto wasAnimating = _radial.animating();
|
||||
const auto updated = _radial.update(
|
||||
document->progress(),
|
||||
!document->loading(),
|
||||
ms);
|
||||
if (timer
|
||||
&& (wasAnimating || _radial.animating())
|
||||
&& (!anim::Disabled() || updated)) {
|
||||
update(radialRect());
|
||||
}
|
||||
checkLoadedDocument();
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
const auto document = _paper.document;
|
||||
if (!document
|
||||
|| !document->loaded(DocumentData::FilePathResolveChecked)
|
||||
|| _generating) {
|
||||
return;
|
||||
}
|
||||
_generating = Data::ReadImageAsync(document, [=](
|
||||
QImage &&image) mutable {
|
||||
auto [left, right] = base::make_binary_guard();
|
||||
_generating = std::move(left);
|
||||
crl::async([
|
||||
this,
|
||||
image = std::move(image),
|
||||
guard = std::move(right)
|
||||
]() mutable {
|
||||
auto scaled = PrepareScaledFromFull(image);
|
||||
crl::on_main([
|
||||
this,
|
||||
image = std::move(image),
|
||||
scaled = std::move(scaled),
|
||||
guard = std::move(guard)
|
||||
]() mutable {
|
||||
if (!guard) {
|
||||
return;
|
||||
}
|
||||
_scaled = App::pixmapFromImageInPlace(std::move(scaled));
|
||||
_full = std::move(image);
|
||||
update();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::Start(const QString &slug, const QString &mode) {
|
||||
if (Window::Theme::GetWallPaperColor(slug)) {
|
||||
Ui::show(Box<BackgroundPreviewBox>(Data::WallPaper{
|
||||
Window::Theme::kCustomBackground,
|
||||
0ULL, // accessHash
|
||||
MTPDwallPaper::Flags(0),
|
||||
slug,
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
if (!IsValidWallPaperSlug(slug)) {
|
||||
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
|
||||
return false;
|
||||
}
|
||||
Auth().api().requestWallPaper(slug, [](const Data::WallPaper &result) {
|
||||
Ui::show(Box<BackgroundPreviewBox>(result));
|
||||
}, [](const RPCError &error) {
|
||||
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
HistoryView::Context BackgroundPreviewBox::elementContext() {
|
||||
return HistoryView::Context::ContactPreview;
|
||||
}
|
||||
|
||||
std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
|
||||
not_null<HistoryMessage*> message) {
|
||||
return std::make_unique<HistoryView::Message>(this, message);
|
||||
}
|
||||
|
||||
std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
|
||||
not_null<HistoryService*> message) {
|
||||
Unexpected("Service message in BackgroundPreviewBox.");
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::elementUnderCursor(
|
||||
not_null<const Element*> view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::elementAnimationAutoplayAsync(
|
||||
not_null<const Element*> element) {
|
||||
}
|
||||
|
||||
TimeMs BackgroundPreviewBox::elementHighlightTime(
|
||||
not_null<const Element*> element) {
|
||||
return TimeMs();
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::elementInSelectionMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,16 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/binary_guard.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "history/admin_log/history_admin_log_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
|
||||
namespace Ui {
|
||||
class RoundCheckbox;
|
||||
} // namespace Ui
|
||||
namespace Data {
|
||||
class WallPaper;
|
||||
} // namespace Data
|
||||
|
||||
class BackgroundBox : public BoxContent {
|
||||
public:
|
||||
@@ -26,55 +21,10 @@ protected:
|
||||
void prepare() override;
|
||||
|
||||
private:
|
||||
void backgroundChosen(int index);
|
||||
|
||||
class Inner;
|
||||
|
||||
void removePaper(const Data::WallPaper &paper);
|
||||
|
||||
QPointer<Inner> _inner;
|
||||
|
||||
};
|
||||
|
||||
class BackgroundPreviewBox
|
||||
: public BoxContent
|
||||
, public HistoryView::ElementDelegate {
|
||||
public:
|
||||
BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
|
||||
|
||||
static bool Start(const QString &slug, const QString &mode);
|
||||
|
||||
using Element = HistoryView::Element;
|
||||
HistoryView::Context elementContext() override;
|
||||
std::unique_ptr<Element> elementCreate(
|
||||
not_null<HistoryMessage*> message) override;
|
||||
std::unique_ptr<Element> elementCreate(
|
||||
not_null<HistoryService*> message) override;
|
||||
bool elementUnderCursor(not_null<const Element*> view) override;
|
||||
void elementAnimationAutoplayAsync(
|
||||
not_null<const Element*> element) override;
|
||||
TimeMs elementHighlightTime(
|
||||
not_null<const Element*> element) override;
|
||||
bool elementInSelectionMode() override;
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
void apply();
|
||||
void step_radial(TimeMs ms, bool timer);
|
||||
QRect radialRect() const;
|
||||
|
||||
void checkLoadedDocument();
|
||||
void paintImage(Painter &p);
|
||||
void paintRadial(Painter &p, TimeMs ms);
|
||||
void paintTexts(Painter &p, TimeMs ms);
|
||||
|
||||
AdminLog::OwnedItem _text1;
|
||||
AdminLog::OwnedItem _text2;
|
||||
Data::WallPaper _paper;
|
||||
QImage _full;
|
||||
QPixmap _scaled;
|
||||
Ui::RadialAnimation _radial;
|
||||
base::binary_guard _generating;
|
||||
|
||||
};
|
||||
|
||||
786
Telegram/SourceFiles/boxes/background_preview_box.cpp
Normal file
@@ -0,0 +1,786 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/background_preview_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "auth_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_document.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/background_preview_box.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxWallPaperSlugLength = 255;
|
||||
|
||||
class ServiceCheck : public Ui::AbstractCheckView {
|
||||
public:
|
||||
ServiceCheck(const style::ServiceCheck &st, bool checked);
|
||||
|
||||
QSize getSize() const override;
|
||||
void paint(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
int outerWidth) override;
|
||||
QImage prepareRippleMask() const override;
|
||||
bool checkRippleStartPosition(QPoint position) const override;
|
||||
|
||||
private:
|
||||
class Generator {
|
||||
public:
|
||||
Generator();
|
||||
|
||||
void paintFrame(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
not_null<const style::ServiceCheck*> st,
|
||||
float64 toggled);
|
||||
void invalidate();
|
||||
|
||||
private:
|
||||
struct Frames {
|
||||
QImage image;
|
||||
std::vector<bool> ready;
|
||||
};
|
||||
|
||||
not_null<Frames*> framesForStyle(
|
||||
not_null<const style::ServiceCheck*> st);
|
||||
static void FillFrame(
|
||||
QImage &image,
|
||||
not_null<const style::ServiceCheck*> st,
|
||||
int index,
|
||||
int count);
|
||||
static void PaintFillingFrame(
|
||||
Painter &p,
|
||||
not_null<const style::ServiceCheck*> st,
|
||||
float64 progress);
|
||||
static void PaintCheckingFrame(
|
||||
Painter &p,
|
||||
not_null<const style::ServiceCheck*> st,
|
||||
float64 progress);
|
||||
|
||||
base::flat_map<not_null<const style::ServiceCheck*>, Frames> _data;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
static Generator &Frames();
|
||||
|
||||
const style::ServiceCheck &_st;
|
||||
|
||||
};
|
||||
|
||||
ServiceCheck::Generator::Generator() {
|
||||
*_lifetime.make_state<base::Subscription>() = Window::Theme::Background(
|
||||
)->add_subscription([=](const Window::Theme::BackgroundUpdate &update) {
|
||||
if (update.paletteChanged()) {
|
||||
invalidate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto ServiceCheck::Generator::framesForStyle(
|
||||
not_null<const style::ServiceCheck*> st) -> not_null<Frames*> {
|
||||
if (const auto i = _data.find(st); i != _data.end()) {
|
||||
return &i->second;
|
||||
}
|
||||
const auto result = &_data.emplace(st, Frames()).first->second;
|
||||
const auto size = st->diameter;
|
||||
const auto count = (st->duration / AnimationTimerDelta) + 2;
|
||||
result->image = QImage(
|
||||
QSize(count * size, size) * cIntRetinaFactor(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
result->image.fill(Qt::transparent);
|
||||
result->image.setDevicePixelRatio(cRetinaFactor());
|
||||
result->ready.resize(count);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ServiceCheck::Generator::FillFrame(
|
||||
QImage &image,
|
||||
not_null<const style::ServiceCheck*> st,
|
||||
int index,
|
||||
int count) {
|
||||
Expects(count > 1);
|
||||
Expects(index >= 0 && index < count);
|
||||
|
||||
Painter p(&image);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
p.translate(index * st->diameter, 0);
|
||||
const auto progress = index / float64(count - 1);
|
||||
if (progress > 0.5) {
|
||||
PaintCheckingFrame(p, st, (progress - 0.5) * 2);
|
||||
} else {
|
||||
PaintFillingFrame(p, st, progress * 2);
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceCheck::Generator::PaintFillingFrame(
|
||||
Painter &p,
|
||||
not_null<const style::ServiceCheck*> st,
|
||||
float64 progress) {
|
||||
const auto shift = progress * st->shift;
|
||||
p.setBrush(st->color);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.drawEllipse(QRectF(
|
||||
shift,
|
||||
shift,
|
||||
st->diameter - 2 * shift,
|
||||
st->diameter - 2 * shift));
|
||||
if (progress < 1.) {
|
||||
const auto remove = progress * (st->diameter / 2. - st->thickness);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(Qt::transparent);
|
||||
p.drawEllipse(QRectF(
|
||||
st->thickness + remove,
|
||||
st->thickness + remove,
|
||||
st->diameter - 2 * (st->thickness + remove),
|
||||
st->diameter - 2 * (st->thickness + remove)));
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceCheck::Generator::PaintCheckingFrame(
|
||||
Painter &p,
|
||||
not_null<const style::ServiceCheck*> st,
|
||||
float64 progress) {
|
||||
const auto shift = (1. - progress) * st->shift;
|
||||
p.setBrush(st->color);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.drawEllipse(QRectF(
|
||||
shift,
|
||||
shift,
|
||||
st->diameter - 2 * shift,
|
||||
st->diameter - 2 * shift));
|
||||
if (progress > 0.) {
|
||||
const auto tip = QPointF(st->tip.x(), st->tip.y());
|
||||
const auto left = tip - QPointF(st->small, st->small) * progress;
|
||||
const auto right = tip - QPointF(-st->large, st->large) * progress;
|
||||
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
auto pen = QPen(Qt::transparent);
|
||||
pen.setWidth(st->stroke);
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
pen.setJoinStyle(Qt::RoundJoin);
|
||||
p.setPen(pen);
|
||||
auto path = QPainterPath();
|
||||
path.moveTo(left);
|
||||
path.lineTo(tip);
|
||||
path.lineTo(right);
|
||||
p.drawPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceCheck::Generator::paintFrame(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
not_null<const style::ServiceCheck*> st,
|
||||
float64 toggled) {
|
||||
const auto frames = framesForStyle(st);
|
||||
auto &image = frames->image;
|
||||
const auto count = int(frames->ready.size());
|
||||
const auto index = int(std::round(toggled * (count - 1)));
|
||||
Assert(index >= 0 && index < count);
|
||||
if (!frames->ready[index]) {
|
||||
frames->ready[index] = true;
|
||||
FillFrame(image, st, index, count);
|
||||
}
|
||||
const auto size = st->diameter;
|
||||
const auto part = size * cIntRetinaFactor();
|
||||
p.drawImage(
|
||||
QPoint(left, top),
|
||||
image,
|
||||
QRect(index * part, 0, part, part));
|
||||
}
|
||||
|
||||
void ServiceCheck::Generator::invalidate() {
|
||||
_data.clear();
|
||||
}
|
||||
|
||||
ServiceCheck::Generator &ServiceCheck::Frames() {
|
||||
static const auto Instance = Ui::CreateChild<Generator>(
|
||||
QApplication::instance());
|
||||
return *Instance;
|
||||
}
|
||||
|
||||
ServiceCheck::ServiceCheck(
|
||||
const style::ServiceCheck &st,
|
||||
bool checked)
|
||||
: AbstractCheckView(st.duration, checked, nullptr)
|
||||
, _st(st) {
|
||||
}
|
||||
|
||||
QSize ServiceCheck::getSize() const {
|
||||
const auto inner = QRect(0, 0, _st.diameter, _st.diameter);
|
||||
return inner.marginsAdded(_st.margin).size();
|
||||
}
|
||||
|
||||
void ServiceCheck::paint(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
int outerWidth) {
|
||||
Frames().paintFrame(
|
||||
p,
|
||||
left + _st.margin.left(),
|
||||
top + _st.margin.top(),
|
||||
&_st,
|
||||
currentAnimationValue());
|
||||
}
|
||||
|
||||
QImage ServiceCheck::prepareRippleMask() const {
|
||||
return QImage();
|
||||
}
|
||||
|
||||
bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
|
||||
if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
|
||||
return false;
|
||||
}
|
||||
return ranges::find_if(slug, [](QChar ch) {
|
||||
return (ch != '.')
|
||||
&& (ch != '_')
|
||||
&& (ch != '-')
|
||||
&& (ch < '0' || ch > '9')
|
||||
&& (ch < 'a' || ch > 'z')
|
||||
&& (ch < 'A' || ch > 'Z');
|
||||
}) == slug.end();
|
||||
}
|
||||
|
||||
AdminLog::OwnedItem GenerateTextItem(
|
||||
not_null<HistoryView::ElementDelegate*> delegate,
|
||||
not_null<History*> history,
|
||||
const QString &text,
|
||||
bool out) {
|
||||
Expects(history->peer->isUser());
|
||||
|
||||
using Flag = MTPDmessage::Flag;
|
||||
static auto id = ServerMaxMsgId + (ServerMaxMsgId / 3);
|
||||
const auto flags = Flag::f_entities
|
||||
| Flag::f_from_id
|
||||
| (out ? Flag::f_out : Flag(0));
|
||||
const auto replyTo = 0;
|
||||
const auto viaBotId = 0;
|
||||
const auto item = new HistoryMessage(
|
||||
history,
|
||||
++id,
|
||||
flags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
unixtime(),
|
||||
out ? history->session().userId() : peerToUser(history->peer->id),
|
||||
QString(),
|
||||
TextWithEntities{ TextUtilities::Clean(text) });
|
||||
return AdminLog::OwnedItem(delegate, item);
|
||||
}
|
||||
|
||||
QImage PrepareScaledNonPattern(
|
||||
const QImage &image,
|
||||
Images::Option blur) {
|
||||
const auto size = st::boxWideWidth;
|
||||
const auto width = std::max(image.width(), 1);
|
||||
const auto height = std::max(image.height(), 1);
|
||||
const auto takeWidth = (width > height)
|
||||
? (width * size / height)
|
||||
: size;
|
||||
const auto takeHeight = (width > height)
|
||||
? size
|
||||
: (height * size / width);
|
||||
return Images::prepare(
|
||||
image,
|
||||
takeWidth * cIntRetinaFactor(),
|
||||
takeHeight * cIntRetinaFactor(),
|
||||
Images::Option::Smooth
|
||||
| Images::Option::TransparentBackground
|
||||
| blur,
|
||||
size,
|
||||
size);
|
||||
}
|
||||
|
||||
QImage ColorizePattern(QImage image, QColor color) {
|
||||
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||
image = std::move(image).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
// Similar to style::colorizeImage.
|
||||
// But style::colorizeImage takes pattern with all pixels having the
|
||||
// same components value, from (0, 0, 0, 0) to (255, 255, 255, 255).
|
||||
//
|
||||
// While in patterns we have different value ranges, usually they are
|
||||
// from (0, 0, 0, 0) to (0, 0, 0, 255), so we should use only 'alpha'.
|
||||
|
||||
const auto width = image.width();
|
||||
const auto height = image.height();
|
||||
const auto pattern = anim::shifted(color);
|
||||
|
||||
const auto resultBytesPerPixel = (image.depth() >> 3);
|
||||
constexpr auto resultIntsPerPixel = 1;
|
||||
const auto resultIntsPerLine = (image.bytesPerLine() >> 2);
|
||||
const auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
|
||||
auto resultInts = reinterpret_cast<uint32*>(image.bits());
|
||||
Assert(resultIntsAdded >= 0);
|
||||
Assert(image.depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
|
||||
Assert(image.bytesPerLine() == (resultIntsPerLine << 2));
|
||||
|
||||
const auto maskBytesPerPixel = (image.depth() >> 3);
|
||||
const auto maskBytesPerLine = image.bytesPerLine();
|
||||
const auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
|
||||
|
||||
// We want to read the last byte of four available.
|
||||
// This is the difference with style::colorizeImage.
|
||||
auto maskBytes = image.constBits() + (maskBytesPerPixel - 1);
|
||||
Assert(maskBytesAdded >= 0);
|
||||
Assert(image.depth() == (maskBytesPerPixel << 3));
|
||||
for (auto y = 0; y != height; ++y) {
|
||||
for (auto x = 0; x != width; ++x) {
|
||||
auto maskOpacity = static_cast<anim::ShiftedMultiplier>(*maskBytes) + 1;
|
||||
*resultInts = anim::unshifted(pattern * maskOpacity);
|
||||
maskBytes += maskBytesPerPixel;
|
||||
resultInts += resultIntsPerPixel;
|
||||
}
|
||||
maskBytes += maskBytesAdded;
|
||||
resultInts += resultIntsAdded;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
QImage PrepareScaledFromFull(
|
||||
const QImage &image,
|
||||
std::optional<QColor> patternBackground,
|
||||
Images::Option blur = Images::Option(0)) {
|
||||
auto result = PrepareScaledNonPattern(image, blur);
|
||||
if (patternBackground) {
|
||||
result = ColorizePattern(
|
||||
std::move(result),
|
||||
Data::PatternColor(*patternBackground));
|
||||
}
|
||||
return std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
QWidget*,
|
||||
const Data::WallPaper &paper)
|
||||
: _text1(GenerateTextItem(
|
||||
delegate(),
|
||||
Auth().data().history(peerFromUser(PeerData::kServiceNotificationsId)),
|
||||
lang(lng_background_text1),
|
||||
false))
|
||||
, _text2(GenerateTextItem(
|
||||
delegate(),
|
||||
Auth().data().history(peerFromUser(PeerData::kServiceNotificationsId)),
|
||||
lang(lng_background_text2),
|
||||
true))
|
||||
, _paper(paper)
|
||||
, _radial([=](crl::time now) { radialAnimationCallback(now); }) {
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
|
||||
}
|
||||
|
||||
not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() {
|
||||
return static_cast<HistoryView::ElementDelegate*>(this);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::prepare() {
|
||||
setTitle(langFactory(lng_background_header));
|
||||
|
||||
addButton(langFactory(lng_background_apply), [=] { apply(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
if (_paper.hasShareUrl()) {
|
||||
addLeftButton(langFactory(lng_background_share), [=] { share(); });
|
||||
}
|
||||
updateServiceBg(_paper.backgroundColor());
|
||||
|
||||
_paper.loadThumbnail();
|
||||
_paper.loadDocument();
|
||||
if (_paper.document() && _paper.document()->loading()) {
|
||||
_radial.start(_paper.document()->progress());
|
||||
}
|
||||
if (_paper.thumbnail() && !_paper.isPattern()) {
|
||||
createBlurCheckbox();
|
||||
}
|
||||
setScaledFromThumb();
|
||||
checkLoadedDocument();
|
||||
|
||||
_text1->setDisplayDate(true);
|
||||
_text1->initDimensions();
|
||||
_text1->resizeGetHeight(st::boxWideWidth);
|
||||
_text2->initDimensions();
|
||||
_text2->resizeGetHeight(st::boxWideWidth);
|
||||
|
||||
setDimensions(st::boxWideWidth, st::boxWideWidth);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::createBlurCheckbox() {
|
||||
_blur.create(
|
||||
this,
|
||||
lang(lng_background_blur),
|
||||
st::backgroundCheckbox,
|
||||
std::make_unique<ServiceCheck>(
|
||||
st::backgroundCheck,
|
||||
_paper.isBlurred()));
|
||||
|
||||
rpl::combine(
|
||||
sizeValue(),
|
||||
_blur->sizeValue()
|
||||
) | rpl::start_with_next([=](QSize outer, QSize inner) {
|
||||
_blur->move(
|
||||
(outer.width() - inner.width()) / 2,
|
||||
outer.height() - st::historyPaddingBottom - inner.height());
|
||||
}, _blur->lifetime());
|
||||
|
||||
_blur->paintRequest(
|
||||
) | rpl::filter([=] {
|
||||
return _serviceBg.has_value();
|
||||
}) | rpl::start_with_next([=] {
|
||||
Painter p(_blur.data());
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(*_serviceBg);
|
||||
p.drawRoundedRect(
|
||||
_blur->rect(),
|
||||
st::historyMessageRadius,
|
||||
st::historyMessageRadius);
|
||||
}, _blur->lifetime());
|
||||
|
||||
_blur->checkedChanges(
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
checkBlurAnimationStart();
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
_blur->setDisabled(true);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::apply() {
|
||||
const auto install = (_paper.id() != Window::Theme::Background()->id())
|
||||
&& Data::IsCloudWallPaper(_paper);
|
||||
App::main()->setChatBackground(_paper, std::move(_full));
|
||||
if (install) {
|
||||
Auth().api().request(MTPaccount_InstallWallPaper(
|
||||
_paper.mtpInput(),
|
||||
_paper.mtpSettings()
|
||||
)).send();
|
||||
}
|
||||
closeBox();
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::share() {
|
||||
QApplication::clipboard()->setText(_paper.shareUrl());
|
||||
Ui::Toast::Show(lang(lng_background_link_copied));
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
const auto ms = crl::now();
|
||||
const auto color = _paper.backgroundColor();
|
||||
if (color) {
|
||||
p.fillRect(e->rect(), *color);
|
||||
}
|
||||
if (!color || _paper.isPattern()) {
|
||||
if (!_scaled.isNull() || setScaledFromThumb()) {
|
||||
paintImage(p);
|
||||
paintRadial(p);
|
||||
} else if (!color) {
|
||||
p.fillRect(e->rect(), st::boxBg);
|
||||
return;
|
||||
} else {
|
||||
// Progress of pattern loading.
|
||||
paintRadial(p);
|
||||
}
|
||||
}
|
||||
paintTexts(p, ms);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintImage(Painter &p) {
|
||||
Expects(!_scaled.isNull());
|
||||
|
||||
const auto master = _paper.isPattern()
|
||||
? std::clamp(_paper.patternIntensity() / 100., 0., 1.)
|
||||
: 1.;
|
||||
|
||||
const auto factor = cIntRetinaFactor();
|
||||
const auto size = st::boxWideWidth;
|
||||
const auto from = QRect(
|
||||
0,
|
||||
(size - height()) / 2 * factor,
|
||||
size * factor,
|
||||
height() * factor);
|
||||
const auto guard = gsl::finally([&] { p.setOpacity(1.); });
|
||||
|
||||
const auto fade = _fadeIn.value(1.);
|
||||
if (fade < 1. && !_fadeOutThumbnail.isNull()) {
|
||||
p.drawPixmap(rect(), _fadeOutThumbnail, from);
|
||||
}
|
||||
const auto &pixmap = (!_blurred.isNull() && _paper.isBlurred())
|
||||
? _blurred
|
||||
: _scaled;
|
||||
p.setOpacity(master * fade);
|
||||
p.drawPixmap(rect(), pixmap, from);
|
||||
checkBlurAnimationStart();
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintRadial(Painter &p) {
|
||||
const auto radial = _radial.animating();
|
||||
const auto radialOpacity = radial ? _radial.opacity() : 0.;
|
||||
if (!radial) {
|
||||
return;
|
||||
}
|
||||
auto inner = radialRect();
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setOpacity(radialOpacity);
|
||||
p.setBrush(st::radialBg);
|
||||
|
||||
{
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawEllipse(inner);
|
||||
}
|
||||
|
||||
p.setOpacity(1);
|
||||
QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine)));
|
||||
_radial.draw(p, arc, st::radialLine, st::radialFg);
|
||||
}
|
||||
|
||||
int BackgroundPreviewBox::textsTop() const {
|
||||
const auto bottom = _blur ? _blur->y() : height();
|
||||
return bottom
|
||||
- st::historyPaddingBottom
|
||||
- _text1->height()
|
||||
- _text2->height();
|
||||
}
|
||||
|
||||
QRect BackgroundPreviewBox::radialRect() const {
|
||||
const auto available = textsTop() - st::historyPaddingBottom;
|
||||
return QRect(
|
||||
QPoint(
|
||||
(width() - st::radialSize.width()) / 2,
|
||||
(available - st::radialSize.height()) / 2),
|
||||
st::radialSize);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
|
||||
const auto height1 = _text1->height();
|
||||
const auto height2 = _text2->height();
|
||||
p.translate(0, textsTop());
|
||||
paintDate(p);
|
||||
_text1->draw(p, rect(), TextSelection(), ms);
|
||||
p.translate(0, height1);
|
||||
_text2->draw(p, rect(), TextSelection(), ms);
|
||||
p.translate(0, height2);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::paintDate(Painter &p) {
|
||||
const auto date = _text1->Get<HistoryView::DateBadge>();
|
||||
if (!date || !_serviceBg) {
|
||||
return;
|
||||
}
|
||||
const auto text = date->text;
|
||||
const auto bubbleHeight = st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom();
|
||||
const auto bubbleTop = st::msgServiceMargin.top();
|
||||
const auto textWidth = st::msgServiceFont->width(text);
|
||||
const auto bubbleWidth = st::msgServicePadding.left() + textWidth + st::msgServicePadding.right();
|
||||
const auto bubbleLeft = (width() - bubbleWidth) / 2;
|
||||
const auto radius = bubbleHeight / 2;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(*_serviceBg);
|
||||
p.drawRoundedRect(bubbleLeft, bubbleTop, bubbleWidth, bubbleHeight, radius, radius);
|
||||
p.setPen(st::msgServiceFg);
|
||||
p.setFont(st::msgServiceFont);
|
||||
p.drawText(bubbleLeft + st::msgServicePadding.left(), bubbleTop + st::msgServicePadding.top() + st::msgServiceFont->ascent, text);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
|
||||
Expects(_paper.document() != nullptr);
|
||||
|
||||
const auto document = _paper.document();
|
||||
const auto wasAnimating = _radial.animating();
|
||||
const auto updated = _radial.update(
|
||||
document->progress(),
|
||||
!document->loading(),
|
||||
now);
|
||||
if ((wasAnimating || _radial.animating())
|
||||
&& (!anim::Disabled() || updated)) {
|
||||
update(radialRect());
|
||||
}
|
||||
checkLoadedDocument();
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::setScaledFromThumb() {
|
||||
const auto thumbnail = _paper.thumbnail();
|
||||
if (!thumbnail || !thumbnail->loaded()) {
|
||||
return false;
|
||||
} else if (_paper.isPattern() && _paper.document() != nullptr) {
|
||||
return false;
|
||||
}
|
||||
auto scaled = PrepareScaledFromFull(
|
||||
thumbnail->original(),
|
||||
patternBackgroundColor(),
|
||||
_paper.document() ? Images::Option::Blurred : Images::Option(0));
|
||||
auto blurred = (_paper.document() || _paper.isPattern())
|
||||
? QImage()
|
||||
: PrepareScaledNonPattern(
|
||||
Data::PrepareBlurredBackground(thumbnail->original()),
|
||||
Images::Option(0));
|
||||
setScaledFromImage(std::move(scaled), std::move(blurred));
|
||||
return true;
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::setScaledFromImage(
|
||||
QImage &&image,
|
||||
QImage &&blurred) {
|
||||
updateServiceBg(Window::Theme::CountAverageColor(image));
|
||||
if (!_full.isNull()) {
|
||||
startFadeInFrom(std::move(_scaled));
|
||||
}
|
||||
_scaled = App::pixmapFromImageInPlace(std::move(image));
|
||||
_blurred = App::pixmapFromImageInPlace(std::move(blurred));
|
||||
if (_blur && (!_paper.document() || !_full.isNull())) {
|
||||
_blur->setDisabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::startFadeInFrom(QPixmap previous) {
|
||||
_fadeOutThumbnail = std::move(previous);
|
||||
_fadeIn.start([=] { update(); }, 0., 1., st::backgroundCheck.duration);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::checkBlurAnimationStart() {
|
||||
if (_fadeIn.animating()
|
||||
|| _blurred.isNull()
|
||||
|| !_blur
|
||||
|| _paper.isBlurred() == _blur->checked()) {
|
||||
return;
|
||||
}
|
||||
_paper = _paper.withBlurred(_blur->checked());
|
||||
startFadeInFrom(_paper.isBlurred() ? _scaled : _blurred);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::updateServiceBg(std::optional<QColor> background) {
|
||||
if (background) {
|
||||
_serviceBg = Window::Theme::AdjustedColor(
|
||||
st::msgServiceBg->c,
|
||||
*background);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<QColor> BackgroundPreviewBox::patternBackgroundColor() const {
|
||||
return _paper.isPattern() ? _paper.backgroundColor() : std::nullopt;
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
const auto document = _paper.document();
|
||||
if (!_full.isNull()
|
||||
|| !document
|
||||
|| !document->loaded(DocumentData::FilePathResolve::Checked)
|
||||
|| _generating) {
|
||||
return;
|
||||
}
|
||||
const auto generateCallback = [=](QImage &&image) {
|
||||
crl::async([
|
||||
this,
|
||||
image = std::move(image),
|
||||
patternBackground = patternBackgroundColor(),
|
||||
guard = _generating.make_guard()
|
||||
]() mutable {
|
||||
auto scaled = PrepareScaledFromFull(image, patternBackground);
|
||||
const auto ms = crl::now();
|
||||
auto blurred = patternBackground
|
||||
? QImage()
|
||||
: PrepareScaledNonPattern(
|
||||
Data::PrepareBlurredBackground(image),
|
||||
Images::Option(0));
|
||||
crl::on_main(std::move(guard), [
|
||||
this,
|
||||
image = std::move(image),
|
||||
scaled = std::move(scaled),
|
||||
blurred = std::move(blurred)
|
||||
]() mutable {
|
||||
_full = std::move(image);
|
||||
setScaledFromImage(std::move(scaled), std::move(blurred));
|
||||
update();
|
||||
});
|
||||
});
|
||||
};
|
||||
_generating = Data::ReadImageAsync(
|
||||
document,
|
||||
Window::Theme::ProcessBackgroundImage,
|
||||
generateCallback);
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::Start(
|
||||
const QString &slug,
|
||||
const QMap<QString, QString> ¶ms) {
|
||||
if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
|
||||
Ui::show(Box<BackgroundPreviewBox>(paper->withUrlParams(params)));
|
||||
return true;
|
||||
}
|
||||
if (!IsValidWallPaperSlug(slug)) {
|
||||
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
|
||||
return false;
|
||||
}
|
||||
Auth().api().requestWallPaper(slug, [=](const Data::WallPaper &result) {
|
||||
Ui::show(Box<BackgroundPreviewBox>(result.withUrlParams(params)));
|
||||
}, [](const RPCError &error) {
|
||||
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
HistoryView::Context BackgroundPreviewBox::elementContext() {
|
||||
return HistoryView::Context::ContactPreview;
|
||||
}
|
||||
|
||||
std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
|
||||
not_null<HistoryMessage*> message) {
|
||||
return std::make_unique<HistoryView::Message>(delegate(), message);
|
||||
}
|
||||
|
||||
std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
|
||||
not_null<HistoryService*> message) {
|
||||
Unexpected("Service message in BackgroundPreviewBox.");
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::elementUnderCursor(
|
||||
not_null<const Element*> view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::elementAnimationAutoplayAsync(
|
||||
not_null<const Element*> element) {
|
||||
}
|
||||
|
||||
crl::time BackgroundPreviewBox::elementHighlightTime(
|
||||
not_null<const Element*> element) {
|
||||
return crl::time(0);
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::elementInSelectionMode() {
|
||||
return false;
|
||||
}
|
||||
82
Telegram/SourceFiles/boxes/background_preview_box.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "base/binary_guard.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "history/admin_log/history_admin_log_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
|
||||
namespace Ui {
|
||||
class Checkbox;
|
||||
} // namespace Ui
|
||||
|
||||
class BackgroundPreviewBox
|
||||
: public BoxContent
|
||||
, private HistoryView::ElementDelegate {
|
||||
public:
|
||||
BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
|
||||
|
||||
static bool Start(
|
||||
const QString &slug,
|
||||
const QMap<QString, QString> ¶ms);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
using Element = HistoryView::Element;
|
||||
not_null<HistoryView::ElementDelegate*> delegate();
|
||||
HistoryView::Context elementContext() override;
|
||||
std::unique_ptr<Element> elementCreate(
|
||||
not_null<HistoryMessage*> message) override;
|
||||
std::unique_ptr<Element> elementCreate(
|
||||
not_null<HistoryService*> message) override;
|
||||
bool elementUnderCursor(not_null<const Element*> view) override;
|
||||
void elementAnimationAutoplayAsync(
|
||||
not_null<const Element*> element) override;
|
||||
crl::time elementHighlightTime(
|
||||
not_null<const Element*> element) override;
|
||||
bool elementInSelectionMode() override;
|
||||
|
||||
void apply();
|
||||
void share();
|
||||
void radialAnimationCallback(crl::time now);
|
||||
QRect radialRect() const;
|
||||
|
||||
void checkLoadedDocument();
|
||||
bool setScaledFromThumb();
|
||||
void setScaledFromImage(QImage &&image, QImage &&blurred);
|
||||
void updateServiceBg(std::optional<QColor> background);
|
||||
std::optional<QColor> patternBackgroundColor() const;
|
||||
void paintImage(Painter &p);
|
||||
void paintRadial(Painter &p);
|
||||
void paintTexts(Painter &p, crl::time ms);
|
||||
void paintDate(Painter &p);
|
||||
void createBlurCheckbox();
|
||||
int textsTop() const;
|
||||
void startFadeInFrom(QPixmap previous);
|
||||
void checkBlurAnimationStart();
|
||||
|
||||
AdminLog::OwnedItem _text1;
|
||||
AdminLog::OwnedItem _text2;
|
||||
Data::WallPaper _paper;
|
||||
QImage _full;
|
||||
QPixmap _scaled, _blurred, _fadeOutThumbnail;
|
||||
Ui::Animations::Simple _fadeIn;
|
||||
Ui::RadialAnimation _radial;
|
||||
base::binary_guard _generating;
|
||||
std::optional<QColor> _serviceBg;
|
||||
object_ptr<Ui::Checkbox> _blur = { nullptr };
|
||||
|
||||
};
|
||||
@@ -13,6 +13,19 @@ using "intro/intro.style";
|
||||
boxDuration: 200;
|
||||
boxRadius: 3px;
|
||||
|
||||
ServiceCheck {
|
||||
margin: margins;
|
||||
diameter: pixels;
|
||||
shift: pixels;
|
||||
thickness: pixels;
|
||||
tip: point;
|
||||
small: pixels;
|
||||
large: pixels;
|
||||
stroke: pixels;
|
||||
color: color;
|
||||
duration: int;
|
||||
}
|
||||
|
||||
boxButtonFont: font(boxFontSize semibold);
|
||||
defaultBoxButton: RoundButton(defaultLightButton) {
|
||||
width: -24px;
|
||||
@@ -69,7 +82,7 @@ boxTitle: FlatLabel(defaultFlatLabel) {
|
||||
linkFontOver: font(17px semibold underline);
|
||||
}
|
||||
}
|
||||
boxTitlePosition: point(23px, 20px);
|
||||
boxTitlePosition: point(23px, 16px);
|
||||
boxTitleHeight: 56px;
|
||||
boxLayerTitlePosition: point(23px, 16px);
|
||||
boxLayerTitleHeight: 56px;
|
||||
@@ -582,6 +595,23 @@ backgroundScroll: ScrollArea(boxLayerScroll) {
|
||||
deltab: 10px;
|
||||
}
|
||||
|
||||
editMediaButtonSize: 29px;
|
||||
editMediaButtonSkip: 8px;
|
||||
editMediaButtonFileSkipRight: 1px;
|
||||
editMediaButtonFileSkipTop: 7px;
|
||||
|
||||
editMediaButtonIconFile: icon {{ "settings_edit", menuIconFg }};
|
||||
editMediaButtonIconPhoto: icon {{ "settings_edit", msgServiceFg }};
|
||||
editMediaButton: IconButton {
|
||||
width: editMediaButtonSize;
|
||||
height: editMediaButtonSize;
|
||||
|
||||
icon: editMediaButtonIconPhoto;
|
||||
|
||||
rippleAreaSize: editMediaButtonSize;
|
||||
ripple: defaultRippleAnimation;
|
||||
}
|
||||
|
||||
calendarTitleHeight: boxTitleHeight;
|
||||
calendarPrevious: IconButton {
|
||||
width: calendarTitleHeight;
|
||||
@@ -630,6 +660,8 @@ usernameTextStyle: TextStyle(boxTextStyle, passcodeTextStyle) {
|
||||
}
|
||||
usernameDefaultFg: windowSubTextFg;
|
||||
|
||||
editMediaCheckboxMargins: margins(0px, 15px, 23px, 15px);
|
||||
|
||||
downloadPathSkip: 10px;
|
||||
|
||||
colorEditWidth: 390px;
|
||||
@@ -911,3 +943,29 @@ callSettingsButton: IconButton {
|
||||
color: windowBgOver;
|
||||
}
|
||||
}
|
||||
|
||||
backgroundCheckbox: Checkbox(defaultCheckbox) {
|
||||
textFg: msgServiceFg;
|
||||
textFgActive: msgServiceFg;
|
||||
|
||||
width: -50px;
|
||||
margin: margins(0px, 0px, 0px, 0px);
|
||||
|
||||
textPosition: point(0px, 8px);
|
||||
checkPosition: point(0px, 0px);
|
||||
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
|
||||
backgroundCheck: ServiceCheck {
|
||||
margin: margins(12px, 8px, 8px, 8px);
|
||||
diameter: 18px;
|
||||
shift: 2px;
|
||||
thickness: 2px;
|
||||
tip: point(7px, 13px);
|
||||
small: 3px;
|
||||
large: 6px;
|
||||
stroke: 2px;
|
||||
color: msgServiceFg;
|
||||
duration: 200;
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ int CalendarBox::Inner::rowsTop() const {
|
||||
|
||||
void CalendarBox::Inner::paintRows(Painter &p, QRect clip) {
|
||||
p.setFont(st::calendarDaysFont);
|
||||
auto ms = getms();
|
||||
auto ms = crl::now();
|
||||
auto y = rowsTop();
|
||||
auto index = -_context->daysShift();
|
||||
auto highlightedIndex = _context->highlightedIndex();
|
||||
@@ -320,7 +320,7 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) {
|
||||
}
|
||||
return st::windowBgOver;
|
||||
};
|
||||
it->second->paint(p, innerLeft, innerTop, width(), ms, &(colorOverride()->c));
|
||||
it->second->paint(p, innerLeft, innerTop, width(), &(colorOverride()->c));
|
||||
if (it->second->empty()) {
|
||||
_ripples.erase(it);
|
||||
}
|
||||
@@ -495,16 +495,8 @@ void CalendarBox::setMaxDate(QDate date) {
|
||||
}
|
||||
|
||||
void CalendarBox::prepare() {
|
||||
_previous->setClickedCallback([this] {
|
||||
if (isPreviousEnabled()) {
|
||||
_context->skipMonth(-1);
|
||||
}
|
||||
});
|
||||
_next->setClickedCallback([this] {
|
||||
if (isNextEnabled()) {
|
||||
_context->skipMonth(1);
|
||||
}
|
||||
});
|
||||
_previous->setClickedCallback([this] { goPreviousMonth(); });
|
||||
_next->setClickedCallback([this] { goNextMonth(); });
|
||||
|
||||
// _inner = setInnerWidget(object_ptr<Inner>(this, _context.get()), st::calendarScroll, st::calendarTitleHeight);
|
||||
_inner->setDateChosenCallback(std::move(_callback));
|
||||
@@ -528,6 +520,18 @@ bool CalendarBox::isNextEnabled() const {
|
||||
return (_context->maxDayIndex() >= _context->daysCount());
|
||||
}
|
||||
|
||||
void CalendarBox::goPreviousMonth() {
|
||||
if (isPreviousEnabled()) {
|
||||
_context->skipMonth(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void CalendarBox::goNextMonth() {
|
||||
if (isNextEnabled()) {
|
||||
_context->skipMonth(1);
|
||||
}
|
||||
}
|
||||
|
||||
void CalendarBox::monthChanged(QDate month) {
|
||||
setDimensions(_st.width, st::calendarTitleHeight + _inner->countHeight());
|
||||
auto previousEnabled = isPreviousEnabled();
|
||||
@@ -548,4 +552,14 @@ void CalendarBox::resizeEvent(QResizeEvent *e) {
|
||||
BoxContent::resizeEvent(e);
|
||||
}
|
||||
|
||||
void CalendarBox::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Escape) {
|
||||
e->ignore();
|
||||
} else if (e->key() == Qt::Key_Left) {
|
||||
goPreviousMonth();
|
||||
} else if (e->key() == Qt::Key_Right) {
|
||||
goNextMonth();
|
||||
}
|
||||
}
|
||||
|
||||
CalendarBox::~CalendarBox() = default;
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
@@ -50,6 +51,9 @@ private:
|
||||
bool isPreviousEnabled() const;
|
||||
bool isNextEnabled() const;
|
||||
|
||||
void goPreviousMonth();
|
||||
void goNextMonth();
|
||||
|
||||
const style::CalendarSizes &_st;
|
||||
|
||||
class Context;
|
||||
|
||||
@@ -8,13 +8,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/change_phone_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "boxes/confirm_phone_box.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "auth_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -144,11 +146,19 @@ void ChangePhoneBox::EnterPhone::submit() {
|
||||
hideError();
|
||||
|
||||
auto phoneNumber = _phone->getLastText().trimmed();
|
||||
_requestId = MTP::send(MTPaccount_SendChangePhoneCode(MTP_flags(0), MTP_string(phoneNumber), MTP_bool(false)), rpcDone(crl::guard(this, [this, phoneNumber](const MTPauth_SentCode &result) {
|
||||
return sendPhoneDone(phoneNumber, result);
|
||||
})), rpcFail(crl::guard(this, [this, phoneNumber](const RPCError &error) {
|
||||
return sendPhoneFail(phoneNumber, error);
|
||||
})));
|
||||
_requestId = MTP::send(
|
||||
MTPaccount_SendChangePhoneCode(
|
||||
MTP_string(phoneNumber),
|
||||
MTP_codeSettings(
|
||||
MTP_flags(0),
|
||||
MTPstring())),
|
||||
rpcDone(crl::guard(this, [=](
|
||||
const MTPauth_SentCode &result) {
|
||||
return sendPhoneDone(phoneNumber, result);
|
||||
})), rpcFail(crl::guard(this, [=](
|
||||
const RPCError &error) {
|
||||
return sendPhoneFail(phoneNumber, error);
|
||||
})));
|
||||
}
|
||||
|
||||
void ChangePhoneBox::EnterPhone::sendPhoneDone(const QString &phoneNumber, const MTPauth_SentCode &result) {
|
||||
@@ -260,7 +270,7 @@ void ChangePhoneBox::EnterCode::submit() {
|
||||
MTP_string(_hash),
|
||||
MTP_string(code)
|
||||
), rpcDone([weak = make_weak(this)](const MTPUser &result) {
|
||||
App::feedUser(result);
|
||||
Auth().data().processUser(result);
|
||||
if (weak) {
|
||||
Ui::hideLayer();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "apiwrap.h"
|
||||
#include "application.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
@@ -23,8 +22,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
@@ -456,8 +457,7 @@ DeleteMessagesBox::DeleteMessagesBox(
|
||||
QWidget*,
|
||||
not_null<HistoryItem*> item,
|
||||
bool suggestModerateActions)
|
||||
: _ids(1, item->fullId())
|
||||
, _singleItem(true) {
|
||||
: _ids(1, item->fullId()) {
|
||||
if (suggestModerateActions) {
|
||||
_moderateBan = item->suggestBanReport();
|
||||
_moderateDeleteAll = item->suggestDeleteAllReport();
|
||||
@@ -475,11 +475,54 @@ DeleteMessagesBox::DeleteMessagesBox(
|
||||
Expects(!_ids.empty());
|
||||
}
|
||||
|
||||
DeleteMessagesBox::DeleteMessagesBox(
|
||||
QWidget*,
|
||||
not_null<PeerData*> peer,
|
||||
bool justClear)
|
||||
: _wipeHistoryPeer(peer)
|
||||
, _wipeHistoryJustClear(justClear) {
|
||||
}
|
||||
|
||||
void DeleteMessagesBox::prepare() {
|
||||
auto text = QString();
|
||||
if (_moderateFrom) {
|
||||
auto details = TextWithEntities();
|
||||
const auto appendDetails = [&](TextWithEntities &&text) {
|
||||
TextUtilities::Append(details, { "\n\n" });
|
||||
TextUtilities::Append(details, std::move(text));
|
||||
};
|
||||
auto deleteKey = lng_box_delete;
|
||||
auto deleteStyle = &st::defaultBoxButton;
|
||||
if (const auto peer = _wipeHistoryPeer) {
|
||||
if (_wipeHistoryJustClear) {
|
||||
details.text = peer->isSelf()
|
||||
? lang(lng_sure_delete_saved_messages)
|
||||
: peer->isUser()
|
||||
? lng_sure_delete_history(lt_contact, peer->name)
|
||||
: lng_sure_delete_group_history(lt_group, peer->name);
|
||||
deleteStyle = &st::attentionBoxButton;
|
||||
} else {
|
||||
details.text = peer->isSelf()
|
||||
? lang(lng_sure_delete_saved_messages)
|
||||
: peer->isUser()
|
||||
? lng_sure_delete_history(lt_contact, peer->name)
|
||||
: peer->isChat()
|
||||
? lng_sure_delete_and_exit(lt_group, peer->name)
|
||||
: lang(peer->isMegagroup()
|
||||
? lng_sure_leave_group
|
||||
: lng_sure_leave_channel);
|
||||
deleteKey = _wipeHistoryPeer->isUser()
|
||||
? lng_box_delete
|
||||
: lng_box_leave;
|
||||
deleteStyle = &(peer->isChannel()
|
||||
? st::defaultBoxButton
|
||||
: st::attentionBoxButton);
|
||||
}
|
||||
if (auto revoke = revokeText(peer)) {
|
||||
_revoke.create(this, revoke->checkbox, false, st::defaultBoxCheckbox);
|
||||
appendDetails(std::move(revoke->description));
|
||||
}
|
||||
} else if (_moderateFrom) {
|
||||
Assert(_moderateInChannel != nullptr);
|
||||
text = lang(lng_selected_delete_sure_this);
|
||||
details.text = lang(lng_selected_delete_sure_this);
|
||||
if (_moderateBan) {
|
||||
_banUser.create(this, lang(lng_ban_user), false, st::defaultBoxCheckbox);
|
||||
}
|
||||
@@ -488,49 +531,32 @@ void DeleteMessagesBox::prepare() {
|
||||
_deleteAll.create(this, lang(lng_delete_all_from), false, st::defaultBoxCheckbox);
|
||||
}
|
||||
} else {
|
||||
text = _singleItem ? lang(lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, _ids.size());
|
||||
auto canDeleteAllForEveryone = true;
|
||||
auto now = unixtime();
|
||||
auto deleteForUser = (UserData*)nullptr;
|
||||
auto peer = (PeerData*)nullptr;
|
||||
auto forEveryoneText = lang(lng_delete_for_everyone_check);
|
||||
for (const auto fullId : std::as_const(_ids)) {
|
||||
if (const auto item = App::histItemById(fullId)) {
|
||||
peer = item->history()->peer;
|
||||
if (!item->canDeleteForEveryone(now)) {
|
||||
canDeleteAllForEveryone = false;
|
||||
break;
|
||||
} else if (auto user = item->history()->peer->asUser()) {
|
||||
if (!deleteForUser || deleteForUser == user) {
|
||||
deleteForUser = user;
|
||||
forEveryoneText = lng_delete_for_other_check(
|
||||
lt_user,
|
||||
user->firstName);
|
||||
} else {
|
||||
forEveryoneText = lang(lng_delete_for_everyone_check);
|
||||
}
|
||||
details.text = (_ids.size() == 1)
|
||||
? lang(lng_selected_delete_sure_this)
|
||||
: lng_selected_delete_sure(lt_count, _ids.size());
|
||||
if (const auto peer = checkFromSinglePeer()) {
|
||||
auto count = int(_ids.size());
|
||||
if (auto revoke = revokeText(peer)) {
|
||||
_revoke.create(this, revoke->checkbox, false, st::defaultBoxCheckbox);
|
||||
appendDetails(std::move(revoke->description));
|
||||
} else if (peer && peer->isChannel()) {
|
||||
if (peer->isMegagroup()) {
|
||||
appendDetails({ lng_delete_for_everyone_hint(lt_count, count) });
|
||||
}
|
||||
} else {
|
||||
canDeleteAllForEveryone = false;
|
||||
} else if (peer->isChat()) {
|
||||
appendDetails({ lng_delete_for_me_chat_hint(lt_count, count) });
|
||||
} else if (!peer->isSelf()) {
|
||||
appendDetails({ lng_delete_for_me_hint(lt_count, count) });
|
||||
}
|
||||
}
|
||||
auto count = int(_ids.size());
|
||||
if (canDeleteAllForEveryone) {
|
||||
_forEveryone.create(this, forEveryoneText, false, st::defaultBoxCheckbox);
|
||||
} else if (peer && peer->isChannel()) {
|
||||
if (peer->isMegagroup()) {
|
||||
text += qsl("\n\n") + lng_delete_for_everyone_hint(lt_count, count);
|
||||
}
|
||||
} else if (peer->isChat()) {
|
||||
text += qsl("\n\n") + lng_delete_for_me_chat_hint(lt_count, count);
|
||||
} else if (!peer->isSelf()) {
|
||||
text += qsl("\n\n") + lng_delete_for_me_hint(lt_count, count);
|
||||
}
|
||||
}
|
||||
_text.create(this, text, Ui::FlatLabel::InitType::Simple, st::boxLabel);
|
||||
_text.create(this, rpl::single(std::move(details)), st::boxLabel);
|
||||
|
||||
addButton(langFactory(lng_box_delete), [this] { deleteAndClear(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
addButton(
|
||||
langFactory(deleteKey),
|
||||
[=] { deleteAndClear(); },
|
||||
*deleteStyle);
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
|
||||
auto fullHeight = st::boxPadding.top() + _text->height() + st::boxPadding.bottom();
|
||||
if (_moderateFrom) {
|
||||
@@ -542,12 +568,112 @@ void DeleteMessagesBox::prepare() {
|
||||
if (_deleteAll) {
|
||||
fullHeight += st::boxLittleSkip + _deleteAll->heightNoMargins();
|
||||
}
|
||||
} else if (_forEveryone) {
|
||||
fullHeight += st::boxMediumSkip + _forEveryone->heightNoMargins();
|
||||
} else if (_revoke) {
|
||||
fullHeight += st::boxMediumSkip + _revoke->heightNoMargins();
|
||||
}
|
||||
setDimensions(st::boxWidth, fullHeight);
|
||||
}
|
||||
|
||||
PeerData *DeleteMessagesBox::checkFromSinglePeer() const {
|
||||
auto result = (PeerData*)nullptr;
|
||||
for (const auto fullId : std::as_const(_ids)) {
|
||||
if (const auto item = App::histItemById(fullId)) {
|
||||
const auto peer = item->history()->peer;
|
||||
if (!result) {
|
||||
result = peer;
|
||||
} else if (result != peer) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto DeleteMessagesBox::revokeText(not_null<PeerData*> peer) const
|
||||
-> std::optional<RevokeConfig> {
|
||||
auto result = RevokeConfig();
|
||||
if (peer == _wipeHistoryPeer) {
|
||||
if (!peer->canRevokeFullHistory()) {
|
||||
return std::nullopt;
|
||||
} else if (const auto user = peer->asUser()) {
|
||||
result.checkbox = lng_delete_for_other_check(
|
||||
lt_user,
|
||||
user->firstName);
|
||||
} else {
|
||||
result.checkbox = lang(lng_delete_for_everyone_check);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto items = ranges::view::all(
|
||||
_ids
|
||||
) | ranges::view::transform([](FullMsgId id) {
|
||||
return App::histItemById(id);
|
||||
}) | ranges::view::filter([](HistoryItem *item) {
|
||||
return (item != nullptr);
|
||||
}) | ranges::to_vector;
|
||||
|
||||
if (items.size() != _ids.size()) {
|
||||
// We don't have information about all messages.
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto now = unixtime();
|
||||
const auto canRevoke = [&](HistoryItem * item) {
|
||||
return item->canDeleteForEveryone(now);
|
||||
};
|
||||
const auto cannotRevoke = [&](HistoryItem *item) {
|
||||
return !item->canDeleteForEveryone(now);
|
||||
};
|
||||
const auto canRevokeAll = ranges::find_if(
|
||||
items,
|
||||
cannotRevoke
|
||||
) == end(items);
|
||||
auto outgoing = items | ranges::view::filter(&HistoryItem::out);
|
||||
const auto canRevokeOutgoingCount = canRevokeAll
|
||||
? -1
|
||||
: ranges::count_if(outgoing, canRevoke);
|
||||
|
||||
if (canRevokeAll) {
|
||||
if (const auto user = peer->asUser()) {
|
||||
result.checkbox = lng_delete_for_other_check(
|
||||
lt_user,
|
||||
user->firstName);
|
||||
} else {
|
||||
result.checkbox = lang(lng_delete_for_everyone_check);
|
||||
}
|
||||
return result;
|
||||
} else if (canRevokeOutgoingCount > 0) {
|
||||
result.checkbox = lang(lng_delete_for_other_my);
|
||||
if (const auto user = peer->asUser()) {
|
||||
auto boldName = TextWithEntities{ user->firstName };
|
||||
boldName.entities.push_back(
|
||||
EntityInText(EntityInTextBold, 0, boldName.text.size()));
|
||||
if (canRevokeOutgoingCount == 1) {
|
||||
result.description = lng_selected_unsend_about_user_one__generic<TextWithEntities>(
|
||||
lt_user,
|
||||
boldName);
|
||||
} else {
|
||||
result.description = lng_selected_unsend_about_user__generic<TextWithEntities>(
|
||||
lt_count,
|
||||
canRevokeOutgoingCount,
|
||||
lt_user,
|
||||
boldName);
|
||||
}
|
||||
} else if (canRevokeOutgoingCount == 1) {
|
||||
result.description = TextWithEntities{
|
||||
lang(lng_selected_unsend_about_group_one) };
|
||||
} else {
|
||||
result.description = TextWithEntities{
|
||||
lng_selected_unsend_about_group(
|
||||
lt_count,
|
||||
canRevokeOutgoingCount) };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void DeleteMessagesBox::resizeEvent(QResizeEvent *e) {
|
||||
BoxContent::resizeEvent(e);
|
||||
|
||||
@@ -563,10 +689,10 @@ void DeleteMessagesBox::resizeEvent(QResizeEvent *e) {
|
||||
if (_deleteAll) {
|
||||
_deleteAll->moveToLeft(st::boxPadding.left(), top);
|
||||
}
|
||||
} else if (_forEveryone) {
|
||||
auto availableWidth = width() - 2 * st::boxPadding.left();
|
||||
_forEveryone->resizeToNaturalWidth(availableWidth);
|
||||
_forEveryone->moveToLeft(st::boxPadding.left(), _text->bottomNoMargins() + st::boxMediumSkip);
|
||||
} else if (_revoke) {
|
||||
const auto availableWidth = width() - 2 * st::boxPadding.left();
|
||||
_revoke->resizeToNaturalWidth(availableWidth);
|
||||
_revoke->moveToLeft(st::boxPadding.left(), _text->bottomNoMargins() + st::boxMediumSkip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,6 +705,28 @@ void DeleteMessagesBox::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
|
||||
void DeleteMessagesBox::deleteAndClear() {
|
||||
const auto revoke = _revoke ? _revoke->checked() : false;
|
||||
if (const auto peer = _wipeHistoryPeer) {
|
||||
const auto justClear = _wipeHistoryJustClear;
|
||||
closeBox();
|
||||
|
||||
if (justClear) {
|
||||
peer->session().api().clearHistory(peer, revoke);
|
||||
} else {
|
||||
const auto controller = App::wnd()->controller();
|
||||
if (controller->activeChatCurrent().peer() == peer) {
|
||||
Ui::showChatsList();
|
||||
}
|
||||
// Don't delete old history by default,
|
||||
// because Android app doesn't.
|
||||
//
|
||||
//if (const auto from = peer->migrateFrom()) {
|
||||
// peer->session().api().deleteConversation(from, false);
|
||||
//}
|
||||
peer->session().api().deleteConversation(peer, revoke);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (_moderateFrom) {
|
||||
if (_banUser && _banUser->checked()) {
|
||||
_moderateInChannel->session().api().kickParticipant(
|
||||
@@ -622,9 +770,8 @@ void DeleteMessagesBox::deleteAndClear() {
|
||||
}
|
||||
}
|
||||
|
||||
const auto revoke = _forEveryone ? _forEveryone->checked() : false;
|
||||
for (const auto &[peer, ids] : idsByPeer) {
|
||||
App::main()->deleteMessages(peer, ids, revoke);
|
||||
peer->session().api().deleteMessages(peer, ids, revoke);
|
||||
}
|
||||
Ui::hideLayer();
|
||||
Auth().data().sendHistoryChangeNotifications();
|
||||
@@ -656,24 +803,17 @@ ConfirmInviteBox::ConfirmInviteBox(
|
||||
}();
|
||||
_title->setText(title);
|
||||
_status->setText(status);
|
||||
if (data.vphoto.type() == mtpc_chatPhoto) {
|
||||
const auto &photo = data.vphoto.c_chatPhoto();
|
||||
const auto size = 160;
|
||||
const auto location = StorageImageLocation::FromMTP(
|
||||
size,
|
||||
size,
|
||||
photo.vphoto_small);
|
||||
if (!location.isNull()) {
|
||||
_photo = Images::Create(location);
|
||||
if (!_photo->loaded()) {
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] {
|
||||
update();
|
||||
});
|
||||
_photo->load(Data::FileOrigin());
|
||||
}
|
||||
|
||||
const auto photo = Auth().data().processPhoto(data.vphoto);
|
||||
if (!photo->isNull()) {
|
||||
_photo = photo->thumbnail();
|
||||
if (!_photo->loaded()) {
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] {
|
||||
update();
|
||||
});
|
||||
_photo->load(Data::FileOrigin());
|
||||
}
|
||||
}
|
||||
if (!_photo) {
|
||||
} else {
|
||||
_photoEmpty = std::make_unique<Ui::EmptyUserpic>(
|
||||
Data::PeerUserpicColor(0),
|
||||
title);
|
||||
@@ -689,7 +829,7 @@ std::vector<not_null<UserData*>> ConfirmInviteBox::GetParticipants(
|
||||
auto result = std::vector<not_null<UserData*>>();
|
||||
result.reserve(v.size());
|
||||
for (const auto &participant : v) {
|
||||
if (const auto user = App::feedUser(participant)) {
|
||||
if (const auto user = Auth().data().processUser(participant)) {
|
||||
result.push_back(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ public:
|
||||
not_null<HistoryItem*> item,
|
||||
bool suggestModerateActions);
|
||||
DeleteMessagesBox(QWidget*, MessageIdsList &&selected);
|
||||
DeleteMessagesBox(QWidget*, not_null<PeerData*> peer, bool justClear);
|
||||
|
||||
void setDeleteConfirmedCallback(Fn<void()> callback) {
|
||||
_deleteConfirmedCallback = std::move(callback);
|
||||
@@ -163,17 +164,24 @@ protected:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
private:
|
||||
struct RevokeConfig {
|
||||
QString checkbox;
|
||||
TextWithEntities description;
|
||||
};
|
||||
void deleteAndClear();
|
||||
PeerData *checkFromSinglePeer() const;
|
||||
std::optional<RevokeConfig> revokeText(not_null<PeerData*> peer) const;
|
||||
|
||||
PeerData * const _wipeHistoryPeer = nullptr;
|
||||
const bool _wipeHistoryJustClear = false;
|
||||
const MessageIdsList _ids;
|
||||
const bool _singleItem = false;
|
||||
UserData *_moderateFrom = nullptr;
|
||||
ChannelData *_moderateInChannel = nullptr;
|
||||
bool _moderateBan = false;
|
||||
bool _moderateDeleteAll = false;
|
||||
|
||||
object_ptr<Ui::FlatLabel> _text = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _forEveryone = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _revoke = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _banUser = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _reportSpam = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _deleteAll = { nullptr };
|
||||
@@ -203,7 +211,7 @@ private:
|
||||
Fn<void()> _submit;
|
||||
object_ptr<Ui::FlatLabel> _title;
|
||||
object_ptr<Ui::FlatLabel> _status;
|
||||
ImagePtr _photo;
|
||||
Image *_photo = nullptr;
|
||||
std::unique_ptr<Ui::EmptyUserpic> _photoEmpty;
|
||||
std::vector<not_null<UserData*>> _participants;
|
||||
bool _isChannel = false;
|
||||
|
||||
@@ -170,7 +170,14 @@ void ConfirmPhoneBox::checkPhoneAndHash() {
|
||||
if (_sendCodeRequestId) {
|
||||
return;
|
||||
}
|
||||
_sendCodeRequestId = MTP::send(MTPaccount_SendConfirmPhoneCode(MTP_flags(0), MTP_string(_hash), MTPBool()), rpcDone(&ConfirmPhoneBox::sendCodeDone), rpcFail(&ConfirmPhoneBox::sendCodeFail));
|
||||
_sendCodeRequestId = MTP::send(
|
||||
MTPaccount_SendConfirmPhoneCode(
|
||||
MTP_string(_hash),
|
||||
MTP_codeSettings(
|
||||
MTP_flags(0),
|
||||
MTPstring())),
|
||||
rpcDone(&ConfirmPhoneBox::sendCodeDone),
|
||||
rpcFail(&ConfirmPhoneBox::sendCodeFail));
|
||||
}
|
||||
|
||||
void ConfirmPhoneBox::sendCodeDone(const MTPauth_SentCode &result) {
|
||||
|
||||
@@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "messenger.h"
|
||||
#include "core/application.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
@@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
#include "ui/text_options.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -28,7 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kSaveSettingsDelayedTimeout = TimeMs(1000);
|
||||
constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
|
||||
|
||||
class ProxyRow : public Ui::RippleButton {
|
||||
public:
|
||||
@@ -52,8 +53,8 @@ protected:
|
||||
private:
|
||||
void setupControls(View &&view);
|
||||
int countAvailableWidth() const;
|
||||
void step_radial(TimeMs ms, bool timer);
|
||||
void paintCheck(Painter &p, TimeMs ms);
|
||||
void radialAnimationCallback();
|
||||
void paintCheck(Painter &p);
|
||||
void showMenu();
|
||||
|
||||
View _view;
|
||||
@@ -67,8 +68,8 @@ private:
|
||||
base::unique_qptr<Ui::DropdownMenu> _menu;
|
||||
|
||||
bool _set = false;
|
||||
Animation _toggled;
|
||||
Animation _setAnimation;
|
||||
Ui::Animations::Simple _toggled;
|
||||
Ui::Animations::Simple _setAnimation;
|
||||
std::unique_ptr<Ui::InfiniteRadialAnimation> _progress;
|
||||
std::unique_ptr<Ui::InfiniteRadialAnimation> _checking;
|
||||
|
||||
@@ -180,8 +181,8 @@ rpl::producer<> ProxyRow::shareClicks() const {
|
||||
|
||||
void ProxyRow::setupControls(View &&view) {
|
||||
updateFields(std::move(view));
|
||||
_toggled.finish();
|
||||
_setAnimation.finish();
|
||||
_toggled.stop();
|
||||
_setAnimation.stop();
|
||||
|
||||
_menuToggle->addClickHandler([=] { showMenu(); });
|
||||
}
|
||||
@@ -209,7 +210,7 @@ void ProxyRow::updateFields(View &&view) {
|
||||
if (state == State::Connecting) {
|
||||
if (!_progress) {
|
||||
_progress = std::make_unique<Ui::InfiniteRadialAnimation>(
|
||||
animation(this, &ProxyRow::step_radial),
|
||||
[=] { radialAnimationCallback(); },
|
||||
st::proxyCheckingAnimation);
|
||||
}
|
||||
_progress->start();
|
||||
@@ -219,7 +220,7 @@ void ProxyRow::updateFields(View &&view) {
|
||||
if (state == State::Checking) {
|
||||
if (!_checking) {
|
||||
_checking = std::make_unique<Ui::InfiniteRadialAnimation>(
|
||||
animation(this, &ProxyRow::step_radial),
|
||||
[=] { radialAnimationCallback(); },
|
||||
st::proxyCheckingAnimation);
|
||||
_checking->start();
|
||||
}
|
||||
@@ -241,8 +242,8 @@ void ProxyRow::updateFields(View &&view) {
|
||||
update();
|
||||
}
|
||||
|
||||
void ProxyRow::step_radial(TimeMs ms, bool timer) {
|
||||
if (timer && !anim::Disabled()) {
|
||||
void ProxyRow::radialAnimationCallback() {
|
||||
if (!anim::Disabled()) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
@@ -268,9 +269,8 @@ int ProxyRow::resizeGetHeight(int newWidth) {
|
||||
void ProxyRow::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
const auto ms = getms();
|
||||
if (!_view.deleted) {
|
||||
paintRipple(p, 0, 0, ms);
|
||||
paintRipple(p, 0, 0);
|
||||
}
|
||||
|
||||
const auto left = _skipLeft;
|
||||
@@ -281,7 +281,7 @@ void ProxyRow::paintEvent(QPaintEvent *e) {
|
||||
p.setOpacity(st::stickersRowDisabledOpacity);
|
||||
}
|
||||
|
||||
paintCheck(p, ms);
|
||||
paintCheck(p);
|
||||
|
||||
p.setPen(st::proxyRowTitleFg);
|
||||
p.setFont(st::semiboldFont);
|
||||
@@ -323,35 +323,29 @@ void ProxyRow::paintEvent(QPaintEvent *e) {
|
||||
|
||||
auto statusLeft = left;
|
||||
if (_checking) {
|
||||
_checking->step(ms);
|
||||
if (_checking) {
|
||||
_checking->draw(
|
||||
p,
|
||||
{
|
||||
st::proxyCheckingPosition.x() + statusLeft,
|
||||
st::proxyCheckingPosition.y() + top
|
||||
},
|
||||
width());
|
||||
statusLeft += st::proxyCheckingPosition.x()
|
||||
+ st::proxyCheckingAnimation.size.width()
|
||||
+ st::proxyCheckingSkip;
|
||||
}
|
||||
_checking->draw(
|
||||
p,
|
||||
{
|
||||
st::proxyCheckingPosition.x() + statusLeft,
|
||||
st::proxyCheckingPosition.y() + top
|
||||
},
|
||||
width());
|
||||
statusLeft += st::proxyCheckingPosition.x()
|
||||
+ st::proxyCheckingAnimation.size.width()
|
||||
+ st::proxyCheckingSkip;
|
||||
}
|
||||
p.drawTextLeft(statusLeft, top, width(), status);
|
||||
top += st::normalFont->height + st::proxyRowPadding.bottom();
|
||||
}
|
||||
|
||||
void ProxyRow::paintCheck(Painter &p, TimeMs ms) {
|
||||
if (_progress) {
|
||||
_progress->step(ms);
|
||||
}
|
||||
void ProxyRow::paintCheck(Painter &p) {
|
||||
const auto loading = _progress
|
||||
? _progress->computeState()
|
||||
: Ui::RadialState{ 0., 0, FullArcLength };
|
||||
const auto toggled = _toggled.current(ms, _view.selected ? 1. : 0.)
|
||||
const auto toggled = _toggled.value(_view.selected ? 1. : 0.)
|
||||
* (1. - loading.shown);
|
||||
const auto _st = &st::defaultRadio;
|
||||
const auto set = _setAnimation.current(ms, _set ? 1. : 0.);
|
||||
const auto set = _setAnimation.value(_set ? 1. : 0.);
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
@@ -975,7 +969,7 @@ void ProxiesBoxController::ShowApplyConfirmation(
|
||||
if (ranges::find(proxies, proxy) == end(proxies)) {
|
||||
proxies.push_back(proxy);
|
||||
}
|
||||
Messenger::Instance().setCurrentProxy(
|
||||
Core::App().setCurrentProxy(
|
||||
proxy,
|
||||
ProxyData::Settings::Enabled);
|
||||
Local::writeSettings();
|
||||
@@ -998,7 +992,7 @@ void ProxiesBoxController::refreshChecker(Item &item) {
|
||||
const auto type = (item.data.type == Type::Http)
|
||||
? Variants::Http
|
||||
: Variants::Tcp;
|
||||
const auto mtproto = Messenger::Instance().mtp();
|
||||
const auto mtproto = Core::App().mtp();
|
||||
const auto dcId = mtproto->mainDcId();
|
||||
|
||||
item.state = ItemState::Checking;
|
||||
@@ -1140,7 +1134,7 @@ void ProxiesBoxController::applyItem(int id) {
|
||||
|
||||
auto j = findByProxy(Global::SelectedProxy());
|
||||
|
||||
Messenger::Instance().setCurrentProxy(
|
||||
Core::App().setCurrentProxy(
|
||||
item->data,
|
||||
ProxyData::Settings::Enabled);
|
||||
saveDelayed();
|
||||
@@ -1163,7 +1157,7 @@ void ProxiesBoxController::setDeleted(int id, bool deleted) {
|
||||
_lastSelectedProxy = base::take(Global::RefSelectedProxy());
|
||||
if (Global::ProxySettings() == ProxyData::Settings::Enabled) {
|
||||
_lastSelectedProxyUsed = true;
|
||||
Messenger::Instance().setCurrentProxy(
|
||||
Core::App().setCurrentProxy(
|
||||
ProxyData(),
|
||||
ProxyData::Settings::System);
|
||||
saveDelayed();
|
||||
@@ -1188,7 +1182,7 @@ void ProxiesBoxController::setDeleted(int id, bool deleted) {
|
||||
Assert(Global::ProxySettings() != ProxyData::Settings::Enabled);
|
||||
|
||||
if (base::take(_lastSelectedProxyUsed)) {
|
||||
Messenger::Instance().setCurrentProxy(
|
||||
Core::App().setCurrentProxy(
|
||||
base::take(_lastSelectedProxy),
|
||||
ProxyData::Settings::Enabled);
|
||||
} else {
|
||||
@@ -1293,9 +1287,7 @@ bool ProxiesBoxController::setProxySettings(ProxyData::Settings value) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Messenger::Instance().setCurrentProxy(
|
||||
Global::SelectedProxy(),
|
||||
value);
|
||||
Core::App().setCurrentProxy(Global::SelectedProxy(), value);
|
||||
saveDelayed();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
|
||||
not_null<Ui::InputField*> field() const;
|
||||
|
||||
[[nodiscard]] PollAnswer toPollAnswer(char id) const;
|
||||
[[nodiscard]] PollAnswer toPollAnswer(int index) const;
|
||||
|
||||
[[nodiscard]] rpl::producer<Qt::MouseButton> removeClicks() const;
|
||||
|
||||
@@ -324,10 +324,12 @@ void Options::Option::removePlaceholder() const {
|
||||
field()->setPlaceholder(nullptr);
|
||||
}
|
||||
|
||||
PollAnswer Options::Option::toPollAnswer(char id) const {
|
||||
PollAnswer Options::Option::toPollAnswer(int index) const {
|
||||
Expects(index >= 0 && index < kMaxOptionsCount);
|
||||
|
||||
return PollAnswer{
|
||||
field()->getLastText().trimmed(),
|
||||
QByteArray(1, id)
|
||||
QByteArray(1, ('0' + index))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -388,9 +390,9 @@ void Options::Option::destroy(FnMut<void()> done) {
|
||||
std::vector<PollAnswer> Options::toPollAnswers() const {
|
||||
auto result = std::vector<PollAnswer>();
|
||||
result.reserve(_list.size());
|
||||
auto counter = char(0);
|
||||
auto counter = int(0);
|
||||
const auto makeAnswer = [&](const Option &option) {
|
||||
return option.toPollAnswer(++counter);
|
||||
return option.toPollAnswer(counter++);
|
||||
};
|
||||
ranges::copy(
|
||||
_list
|
||||
|
||||
@@ -7,30 +7,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/edit_caption_box.h"
|
||||
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/text_options.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "media/media_clip_reader.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_user.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "core/event_filter.h"
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "chat_helpers/tabbed_panel.h"
|
||||
#include "chat_helpers/tabbed_selector.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "core/event_filter.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/mime_type.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "layout.h"
|
||||
#include "auth_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/text_options.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "confirm_box.h"
|
||||
|
||||
EditCaptionBox::EditCaptionBox(
|
||||
QWidget*,
|
||||
@@ -41,19 +46,26 @@ EditCaptionBox::EditCaptionBox(
|
||||
Expects(item->media() != nullptr);
|
||||
Expects(item->media()->allowsEditCaption());
|
||||
|
||||
_isAllowedEditMedia = item->media()->allowsEditMedia();
|
||||
_isAlbum = !item->groupId().empty();
|
||||
|
||||
QSize dimensions;
|
||||
ImagePtr image;
|
||||
auto image = (Image*)nullptr;
|
||||
DocumentData *doc = nullptr;
|
||||
|
||||
const auto media = item->media();
|
||||
if (const auto photo = media->photo()) {
|
||||
_photo = true;
|
||||
dimensions = QSize(photo->full->width(), photo->full->height());
|
||||
image = photo->full;
|
||||
dimensions = QSize(photo->width(), photo->height());
|
||||
image = photo->large();
|
||||
} else if (const auto document = media->document()) {
|
||||
dimensions = document->dimensions;
|
||||
image = document->thumb;
|
||||
image = document->thumbnail();
|
||||
dimensions = image
|
||||
? image->size()
|
||||
: document->dimensions;
|
||||
if (document->isAnimation()) {
|
||||
_gifw = document->dimensions.width();
|
||||
_gifh = document->dimensions.height();
|
||||
_animated = true;
|
||||
} else if (document->isVideoFile()) {
|
||||
_animated = true;
|
||||
@@ -68,11 +80,11 @@ EditCaptionBox::EditCaptionBox(
|
||||
ConvertEntitiesToTextTags(original.entities)
|
||||
};
|
||||
|
||||
if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) {
|
||||
if (image->isNull()) {
|
||||
if (!_animated && (dimensions.isEmpty() || doc || !image)) {
|
||||
if (!image) {
|
||||
_thumbw = 0;
|
||||
} else {
|
||||
int32 tw = image->width(), th = image->height();
|
||||
const auto tw = image->width(), th = image->height();
|
||||
if (tw > th) {
|
||||
_thumbw = (tw * st::msgFileThumbSize) / th;
|
||||
} else {
|
||||
@@ -80,7 +92,7 @@ EditCaptionBox::EditCaptionBox(
|
||||
}
|
||||
_thumbnailImage = image;
|
||||
_refreshThumbnail = [=] {
|
||||
auto options = Images::Option::Smooth
|
||||
const auto options = Images::Option::Smooth
|
||||
| Images::Option::RoundedSmall
|
||||
| Images::Option::RoundedTopLeft
|
||||
| Images::Option::RoundedTopRight
|
||||
@@ -97,17 +109,10 @@ EditCaptionBox::EditCaptionBox(
|
||||
}
|
||||
|
||||
if (doc) {
|
||||
auto nameString = doc->isVoiceMessage()
|
||||
const auto nameString = doc->isVoiceMessage()
|
||||
? lang(lng_media_audio)
|
||||
: doc->composeNameString();
|
||||
_name.setText(
|
||||
st::semiboldTextStyle,
|
||||
nameString,
|
||||
Ui::NameTextOptions());
|
||||
_status = formatSizeText(doc->size);
|
||||
_statusw = qMax(
|
||||
_name.maxWidth(),
|
||||
st::normalFont->width(_status));
|
||||
setName(nameString, doc->size);
|
||||
_isImage = doc->isImage();
|
||||
_isAudio = (doc->isVoiceMessage() || doc->isAudioFile());
|
||||
}
|
||||
@@ -115,12 +120,15 @@ EditCaptionBox::EditCaptionBox(
|
||||
_refreshThumbnail();
|
||||
}
|
||||
} else {
|
||||
int32 maxW = 0, maxH = 0;
|
||||
if (!image) {
|
||||
image = Image::BlankMedia();
|
||||
}
|
||||
auto maxW = 0, maxH = 0;
|
||||
const auto limitW = st::sendMediaPreviewSize;
|
||||
auto limitH = std::min(st::confirmMaxHeight, _gifh ? _gifh : INT_MAX);
|
||||
if (_animated) {
|
||||
int32 limitW = st::sendMediaPreviewSize;
|
||||
int32 limitH = st::confirmMaxHeight;
|
||||
maxW = qMax(dimensions.width(), 1);
|
||||
maxH = qMax(dimensions.height(), 1);
|
||||
maxW = std::max(dimensions.width(), 1);
|
||||
maxH = std::max(dimensions.height(), 1);
|
||||
if (maxW * limitH > maxH * limitW) {
|
||||
if (maxW < limitW) {
|
||||
maxH = maxH * limitW / maxW;
|
||||
@@ -161,31 +169,58 @@ EditCaptionBox::EditCaptionBox(
|
||||
}
|
||||
_refreshThumbnail();
|
||||
|
||||
int32 tw = _thumb.width(), th = _thumb.height();
|
||||
if (!tw || !th) {
|
||||
tw = th = 1;
|
||||
}
|
||||
_thumbw = st::sendMediaPreviewSize;
|
||||
if (_thumb.width() < _thumbw) {
|
||||
_thumbw = (_thumb.width() > 20) ? _thumb.width() : 20;
|
||||
}
|
||||
int32 maxthumbh = qMin(qRound(1.5 * _thumbw), int(st::confirmMaxHeight));
|
||||
_thumbh = qRound(th * float64(_thumbw) / tw);
|
||||
if (_thumbh > maxthumbh) {
|
||||
_thumbw = qRound(_thumbw * float64(maxthumbh) / _thumbh);
|
||||
_thumbh = maxthumbh;
|
||||
if (_thumbw < 10) {
|
||||
_thumbw = 10;
|
||||
const auto resizeDimensions = [&](int &thumbWidth, int &thumbHeight, int &thumbX) {
|
||||
auto tw = thumbWidth, th = thumbHeight;
|
||||
if (!tw || !th) {
|
||||
tw = th = 1;
|
||||
}
|
||||
|
||||
// Edit media button takes place on thumb preview
|
||||
// And its height can be greater than height of thumb.
|
||||
const auto minThumbHeight = st::editMediaButtonSize
|
||||
+ st::editMediaButtonSkip * 2;
|
||||
const auto minThumbWidth = minThumbHeight * tw / th;
|
||||
|
||||
if (thumbWidth < st::sendMediaPreviewSize) {
|
||||
thumbWidth = (thumbWidth > minThumbWidth)
|
||||
? thumbWidth
|
||||
: minThumbWidth;
|
||||
} else {
|
||||
thumbWidth = st::sendMediaPreviewSize;
|
||||
}
|
||||
const auto maxThumbHeight = std::min(int(std::round(1.5 * thumbWidth)), limitH);
|
||||
thumbHeight = int(std::round(th * float64(thumbWidth) / tw));
|
||||
if (thumbHeight > maxThumbHeight) {
|
||||
thumbWidth = int(std::round(thumbWidth * float64(maxThumbHeight) / thumbHeight));
|
||||
thumbHeight = maxThumbHeight;
|
||||
if (thumbWidth < 10) {
|
||||
thumbWidth = 10;
|
||||
}
|
||||
}
|
||||
thumbX = (st::boxWideWidth - thumbWidth) / 2;
|
||||
};
|
||||
|
||||
if (doc && doc->isAnimation()) {
|
||||
resizeDimensions(_gifw, _gifh, _gifx);
|
||||
}
|
||||
limitH = std::min(st::confirmMaxHeight, _gifh ? _gifh : INT_MAX);
|
||||
|
||||
_thumbw = _thumb.width();
|
||||
_thumbh = _thumb.height();
|
||||
// If thumb's and resized gif's sizes are equal,
|
||||
// Then just take made values.
|
||||
if (_thumbw == _gifw && _thumbh == _gifh) {
|
||||
_thumbx = (st::boxWideWidth - _thumbw) / 2;
|
||||
} else {
|
||||
resizeDimensions(_thumbw, _thumbh, _thumbx);
|
||||
}
|
||||
_thumbx = (st::boxWideWidth - _thumbw) / 2;
|
||||
|
||||
const auto prepareBasicThumb = _refreshThumbnail;
|
||||
const auto scaleThumbDown = [=] {
|
||||
_thumb = App::pixmapFromImageInPlace(_thumb.toImage().scaled(
|
||||
_thumbw * cIntRetinaFactor(),
|
||||
_thumbh * cIntRetinaFactor(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
_thumb.setDevicePixelRatio(cRetinaFactor());
|
||||
};
|
||||
@@ -201,7 +236,9 @@ EditCaptionBox::EditCaptionBox(
|
||||
? _thumbnailImage->loaded()
|
||||
: true;
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] {
|
||||
if (!_thumbnailImageLoaded && _thumbnailImage->loaded()) {
|
||||
if (!_thumbnailImageLoaded
|
||||
&& _thumbnailImage
|
||||
&& _thumbnailImage->loaded()) {
|
||||
_thumbnailImageLoaded = true;
|
||||
_refreshThumbnail();
|
||||
update();
|
||||
@@ -223,6 +260,22 @@ EditCaptionBox::EditCaptionBox(
|
||||
_field->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
_field->setMarkdownReplacesEnabled(rpl::single(true));
|
||||
_field->setEditLinkCallback(DefaultEditLinkCallback(_field));
|
||||
|
||||
auto r = object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
|
||||
this,
|
||||
object_ptr<Ui::Checkbox>(
|
||||
this,
|
||||
lang(lng_send_file),
|
||||
false,
|
||||
st::defaultBoxCheckbox),
|
||||
st::editMediaCheckboxMargins);
|
||||
_wayWrap = r.data();
|
||||
_wayWrap->toggle(false, anim::type::instant);
|
||||
|
||||
r->entity()->checkedChanges(
|
||||
) | rpl::start_with_next([&](bool checked) {
|
||||
_asFile = checked;
|
||||
}, _wayWrap->lifetime());
|
||||
}
|
||||
|
||||
bool EditCaptionBox::emojiFilter(not_null<QEvent*> event) {
|
||||
@@ -244,15 +297,27 @@ void EditCaptionBox::updateEmojiPanelGeometry() {
|
||||
local.x() + _emojiToggle->width() * 3);
|
||||
}
|
||||
|
||||
void EditCaptionBox::prepareGifPreview(not_null<DocumentData*> document) {
|
||||
void EditCaptionBox::prepareGifPreview(DocumentData* document) {
|
||||
const auto newPath = getNewMediaPath();
|
||||
if (_gifPreview) {
|
||||
return;
|
||||
} else if (document->isAnimation() && document->loaded()) {
|
||||
_gifPreview = Media::Clip::MakeReader(document, _msgId, [this](Media::Clip::Notification notification) {
|
||||
clipCallback(notification);
|
||||
});
|
||||
if (_gifPreview) _gifPreview->setAutoplay();
|
||||
} else if (!document && newPath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto callback = [=](Media::Clip::Notification notification) {
|
||||
clipCallback(notification);
|
||||
};
|
||||
if (document && document->isAnimation() && document->loaded()) {
|
||||
_gifPreview = Media::Clip::MakeReader(
|
||||
document,
|
||||
_msgId,
|
||||
callback);
|
||||
} else if (!newPath.isEmpty()) {
|
||||
_gifPreview = Media::Clip::MakeReader(
|
||||
newPath,
|
||||
callback);
|
||||
}
|
||||
if (_gifPreview) _gifPreview->setAutoplay();
|
||||
}
|
||||
|
||||
void EditCaptionBox::clipCallback(Media::Clip::Notification notification) {
|
||||
@@ -264,7 +329,7 @@ void EditCaptionBox::clipCallback(Media::Clip::Notification notification) {
|
||||
}
|
||||
|
||||
if (_gifPreview && _gifPreview->ready() && !_gifPreview->started()) {
|
||||
auto s = QSize(_thumbw, _thumbh);
|
||||
const auto s = QSize(_gifw, _gifh);
|
||||
_gifPreview->start(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None);
|
||||
}
|
||||
|
||||
@@ -279,14 +344,227 @@ void EditCaptionBox::clipCallback(Media::Clip::Notification notification) {
|
||||
}
|
||||
}
|
||||
|
||||
void EditCaptionBox::updateEditPreview() {
|
||||
using Info = FileMediaInformation;
|
||||
|
||||
const auto file = &_preparedList.files.front();
|
||||
const auto fileMedia = &file->information->media;
|
||||
|
||||
const auto fileinfo = QFileInfo(file->path);
|
||||
const auto filename = fileinfo.fileName();
|
||||
|
||||
_isImage = fileIsImage(filename, file->mime);
|
||||
_isAudio = false;
|
||||
_animated = false;
|
||||
_photo = false;
|
||||
_doc = false;
|
||||
_gifPreview = nullptr;
|
||||
_thumbw = _thumbh = _thumbx = 0;
|
||||
_gifw = _gifh = _gifx = 0;
|
||||
|
||||
auto shouldAsDoc = true;
|
||||
if (const auto image = base::get_if<Info::Image>(fileMedia)) {
|
||||
shouldAsDoc = !Storage::ValidateThumbDimensions(
|
||||
image->data.width(),
|
||||
image->data.height());
|
||||
_photo = !shouldAsDoc;
|
||||
_isImage = true;
|
||||
} else if (const auto video = base::get_if<Info::Video>(fileMedia)) {
|
||||
_animated = true;
|
||||
// Never edit video as gif.
|
||||
video->isGifv = false;
|
||||
shouldAsDoc = false;
|
||||
}
|
||||
if (shouldAsDoc) {
|
||||
auto nameString = filename;
|
||||
if (const auto song = base::get_if<Info::Song>(fileMedia)) {
|
||||
nameString = DocumentData::ComposeNameString(
|
||||
filename,
|
||||
song->title,
|
||||
song->performer);
|
||||
_isAudio = true;
|
||||
}
|
||||
setName(
|
||||
nameString.isEmpty()
|
||||
? QString("file")
|
||||
: nameString,
|
||||
fileinfo.size()
|
||||
? fileinfo.size()
|
||||
: _preparedList.files.front().content.size());
|
||||
_doc = true;
|
||||
}
|
||||
|
||||
_wayWrap->toggle(_photo && !_isAlbum, anim::type::instant);
|
||||
|
||||
if (!_doc) {
|
||||
_thumb = App::pixmapFromImageInPlace(
|
||||
file->preview.scaled(
|
||||
st::sendMediaPreviewSize * cIntRetinaFactor(),
|
||||
st::confirmMaxHeight * cIntRetinaFactor(),
|
||||
Qt::KeepAspectRatio));
|
||||
_thumbw = _thumb.width() / cIntRetinaFactor();
|
||||
_thumbh = _thumb.height() / cIntRetinaFactor();
|
||||
_thumbx = (st::boxWideWidth - _thumbw) / 2;
|
||||
}
|
||||
updateEditMediaButton();
|
||||
captionResized();
|
||||
}
|
||||
|
||||
void EditCaptionBox::updateEditMediaButton() {
|
||||
const auto icon = _doc
|
||||
? &st::editMediaButtonIconFile
|
||||
: &st::editMediaButtonIconPhoto;
|
||||
const auto color = _doc ? &st::windowBgRipple : &st::callFingerprintBg;
|
||||
_editMedia->setIconOverride(icon);
|
||||
_editMedia->setRippleColorOverride(color);
|
||||
_editMedia->setForceRippled(!_doc, anim::type::instant);
|
||||
}
|
||||
|
||||
void EditCaptionBox::createEditMediaButton() {
|
||||
const auto callback = [=](FileDialog::OpenResult &&result) {
|
||||
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto isValidFile = [](QString mimeType) {
|
||||
if (mimeType == qstr("image/webp")
|
||||
|| mimeType == qstr("image/gif")) {
|
||||
Ui::show(
|
||||
Box<InformBox>(lang(lng_edit_media_invalid_file)),
|
||||
LayerOption::KeepOther);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!result.remoteContent.isEmpty()) {
|
||||
|
||||
auto list = Storage::PrepareMediaFromImage(
|
||||
QImage(),
|
||||
std::move(result.remoteContent),
|
||||
st::sendMediaPreviewSize);
|
||||
|
||||
if (!isValidFile(list.files.front().mime)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isAlbum) {
|
||||
const auto albumMimes = {
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"video/mp4",
|
||||
};
|
||||
if ((ranges::find(albumMimes, list.files.front().mime)
|
||||
== end(albumMimes))) {
|
||||
Ui::show(
|
||||
Box<InformBox>(lang(lng_edit_media_album_error)),
|
||||
LayerOption::KeepOther);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_preparedList = std::move(list);
|
||||
} else if (!result.paths.isEmpty()) {
|
||||
auto list = Storage::PrepareMediaList(
|
||||
QStringList(result.paths.front()),
|
||||
st::sendMediaPreviewSize);
|
||||
|
||||
// Don't rewrite _preparedList if new list is not valid for album.
|
||||
if (_isAlbum) {
|
||||
using Info = FileMediaInformation;
|
||||
|
||||
const auto media = &list.files.front().information->media;
|
||||
const auto valid = media->match([&](const Info::Image &data) {
|
||||
return Storage::ValidateThumbDimensions(
|
||||
data.data.width(),
|
||||
data.data.height());
|
||||
}, [&](const Info::Video &data) {
|
||||
return true;
|
||||
}, [](auto &&other) {
|
||||
return false;
|
||||
});
|
||||
if (!valid) {
|
||||
Ui::show(
|
||||
Box<InformBox>(lang(lng_edit_media_album_error)),
|
||||
LayerOption::KeepOther);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto info = QFileInfo(result.paths.front());
|
||||
if (!isValidFile(Core::MimeTypeForFile(info).name())) {
|
||||
return;
|
||||
}
|
||||
|
||||
_preparedList = std::move(list);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
updateEditPreview();
|
||||
};
|
||||
|
||||
const auto buttonCallback = [=] {
|
||||
const auto filters = _isAlbum
|
||||
? QStringList(qsl("Image and Video Files (*.png *.jpg *.mp4)"))
|
||||
: QStringList(FileDialog::AllFilesFilter());
|
||||
FileDialog::GetOpenPath(
|
||||
this,
|
||||
lang(lng_choose_file),
|
||||
filters.join(qsl(";;")),
|
||||
crl::guard(this, callback));
|
||||
};
|
||||
|
||||
_editMediaClicks.events(
|
||||
) | rpl::start_with_next(
|
||||
buttonCallback,
|
||||
lifetime());
|
||||
|
||||
// Create edit media button.
|
||||
_editMedia.create(this, st::editMediaButton);
|
||||
updateEditMediaButton();
|
||||
_editMedia->setClickedCallback(buttonCallback);
|
||||
}
|
||||
|
||||
void EditCaptionBox::prepare() {
|
||||
addButton(langFactory(lng_settings_save), [this] { save(); });
|
||||
if (_isAllowedEditMedia) {
|
||||
createEditMediaButton();
|
||||
} else {
|
||||
_preparedList.files.clear();
|
||||
}
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
|
||||
updateBoxSize();
|
||||
connect(_field, &Ui::InputField::submitted, [=] { save(); });
|
||||
connect(_field, &Ui::InputField::cancelled, [=] { closeBox(); });
|
||||
connect(_field, &Ui::InputField::resized, [=] { captionResized(); });
|
||||
_field->setMimeDataHook([=](
|
||||
not_null<const QMimeData*> data,
|
||||
Ui::InputField::MimeAction action) {
|
||||
if (action == Ui::InputField::MimeAction::Check) {
|
||||
if (!data->hasText() && !_isAllowedEditMedia) {
|
||||
return false;
|
||||
}
|
||||
if (data->hasImage()) {
|
||||
const auto image = qvariant_cast<QImage>(data->imageData());
|
||||
if (!image.isNull()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (const auto urls = data->urls(); !urls.empty()) {
|
||||
if (ranges::find_if(
|
||||
urls,
|
||||
[](const QUrl &url) { return !url.isLocalFile(); }
|
||||
) == urls.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return data->hasText();
|
||||
} else if (action == Ui::InputField::MimeAction::Insert) {
|
||||
return fileFromClipboard(data);
|
||||
}
|
||||
Unexpected("action in MimeData hook.");
|
||||
});
|
||||
Ui::Emoji::SuggestionsController::Init(
|
||||
getDelegate()->outerContainer(),
|
||||
_field);
|
||||
@@ -298,6 +576,48 @@ void EditCaptionBox::prepare() {
|
||||
_field->setTextCursor(cursor);
|
||||
}
|
||||
|
||||
bool EditCaptionBox::fileFromClipboard(not_null<const QMimeData*> data) {
|
||||
if (!_isAllowedEditMedia) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto list = [&] {
|
||||
auto url = QList<QUrl>();
|
||||
auto canAddUrl = false;
|
||||
// When we edit media, we need only 1 file.
|
||||
if (data->hasUrls()) {
|
||||
const auto first = data->urls().front();
|
||||
url.push_front(first);
|
||||
canAddUrl = first.isLocalFile();
|
||||
}
|
||||
auto result = canAddUrl
|
||||
? Storage::PrepareMediaList(url, st::sendMediaPreviewSize)
|
||||
: Storage::PreparedList(
|
||||
Storage::PreparedList::Error::EmptyFile,
|
||||
QString());
|
||||
if (result.error == Storage::PreparedList::Error::None) {
|
||||
return result;
|
||||
} else if (data->hasImage()) {
|
||||
auto image = qvariant_cast<QImage>(data->imageData());
|
||||
if (!image.isNull()) {
|
||||
_isImage = true;
|
||||
_photo = true;
|
||||
return Storage::PrepareMediaFromImage(
|
||||
std::move(image),
|
||||
QByteArray(),
|
||||
st::sendMediaPreviewSize);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
_preparedList = std::move(list);
|
||||
if (_preparedList.files.empty()) {
|
||||
return false;
|
||||
}
|
||||
updateEditPreview();
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditCaptionBox::captionResized() {
|
||||
updateBoxSize();
|
||||
resizeEvent(0);
|
||||
@@ -337,8 +657,11 @@ void EditCaptionBox::setupEmojiPanel() {
|
||||
|
||||
void EditCaptionBox::updateBoxSize() {
|
||||
auto newHeight = st::boxPhotoPadding.top() + st::boxPhotoCaptionSkip + _field->height() + errorTopSkip() + st::normalFont->height;
|
||||
if (_photo) {
|
||||
newHeight += _wayWrap->height() / 2;
|
||||
}
|
||||
if (_photo || _animated) {
|
||||
newHeight += _thumbh;
|
||||
newHeight += std::max(_thumbh, _gifh);
|
||||
} else if (_thumbw) {
|
||||
newHeight += 0 + st::msgFileThumbSize + 0;
|
||||
} else if (_doc) {
|
||||
@@ -346,7 +669,7 @@ void EditCaptionBox::updateBoxSize() {
|
||||
} else {
|
||||
newHeight += st::boxTitleFont->height;
|
||||
}
|
||||
setDimensions(st::boxWideWidth, newHeight);
|
||||
setDimensions(st::boxWideWidth, newHeight, true);
|
||||
}
|
||||
|
||||
int EditCaptionBox::errorTopSkip() const {
|
||||
@@ -359,22 +682,24 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
if (_photo || _animated) {
|
||||
const auto th = std::max(_gifh, _thumbh);
|
||||
if (_thumbx > st::boxPhotoPadding.left()) {
|
||||
p.fillRect(st::boxPhotoPadding.left(), st::boxPhotoPadding.top(), _thumbx - st::boxPhotoPadding.left(), _thumbh, st::confirmBg);
|
||||
p.fillRect(st::boxPhotoPadding.left(), st::boxPhotoPadding.top(), _thumbx - st::boxPhotoPadding.left(), th, st::confirmBg);
|
||||
}
|
||||
if (_thumbx + _thumbw < width() - st::boxPhotoPadding.right()) {
|
||||
p.fillRect(_thumbx + _thumbw, st::boxPhotoPadding.top(), width() - st::boxPhotoPadding.right() - _thumbx - _thumbw, _thumbh, st::confirmBg);
|
||||
p.fillRect(_thumbx + _thumbw, st::boxPhotoPadding.top(), width() - st::boxPhotoPadding.right() - _thumbx - _thumbw, th, st::confirmBg);
|
||||
}
|
||||
if (_gifPreview && _gifPreview->started()) {
|
||||
auto s = QSize(_thumbw, _thumbh);
|
||||
auto paused = _controller->isGifPausedAtLeastFor(Window::GifPauseReason::Layer);
|
||||
auto frame = _gifPreview->current(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None, paused ? 0 : getms());
|
||||
p.drawPixmap(_thumbx, st::boxPhotoPadding.top(), frame);
|
||||
const auto s = QSize(_gifw, _gifh);
|
||||
const auto paused = _controller->isGifPausedAtLeastFor(Window::GifPauseReason::Layer);
|
||||
const auto frame = _gifPreview->current(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None, paused ? 0 : crl::now());
|
||||
p.drawPixmap(_gifx, st::boxPhotoPadding.top(), frame);
|
||||
} else {
|
||||
p.drawPixmap(_thumbx, st::boxPhotoPadding.top(), _thumb);
|
||||
const auto offset = _gifh ? ((_gifh - _thumbh) / 2) : 0;
|
||||
p.drawPixmap(_thumbx, st::boxPhotoPadding.top() + offset, _thumb);
|
||||
}
|
||||
if (_animated && !_gifPreview) {
|
||||
QRect inner(_thumbx + (_thumbw - st::msgFileSize) / 2, st::boxPhotoPadding.top() + (_thumbh - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
|
||||
QRect inner(_thumbx + (_thumbw - st::msgFileSize) / 2, st::boxPhotoPadding.top() + (th - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::msgDateImgBg);
|
||||
|
||||
@@ -383,13 +708,13 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
|
||||
p.drawEllipse(inner);
|
||||
}
|
||||
|
||||
auto icon = &st::historyFileInPlay;
|
||||
const auto icon = &st::historyFileInPlay;
|
||||
icon->paintInCenter(p, inner);
|
||||
}
|
||||
} else if (_doc) {
|
||||
int32 w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right();
|
||||
int32 h = _thumbw ? (0 + st::msgFileThumbSize + 0) : (0 + st::msgFileSize + 0);
|
||||
int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0;
|
||||
const auto w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right();
|
||||
const auto h = _thumbw ? (0 + st::msgFileThumbSize + 0) : (0 + st::msgFileSize + 0);
|
||||
auto nameleft = 0, nametop = 0, nameright = 0, statustop = 0;
|
||||
if (_thumbw) {
|
||||
nameleft = 0 + st::msgFileThumbSize + st::msgFileThumbPadding.right();
|
||||
nametop = st::msgFileThumbNameTop - st::msgFileThumbPadding.top();
|
||||
@@ -401,12 +726,9 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
|
||||
nameright = 0;
|
||||
statustop = st::msgFileStatusTop - st::msgFilePadding.top();
|
||||
}
|
||||
int32 namewidth = w - nameleft - 0;
|
||||
if (namewidth > _statusw) {
|
||||
//w -= (namewidth - _statusw);
|
||||
//namewidth = _statusw;
|
||||
}
|
||||
int32 x = (width() - w) / 2, y = st::boxPhotoPadding.top();
|
||||
const auto editButton = _editMedia->width() + st::editMediaButtonSkip;
|
||||
const auto namewidth = w - nameleft - editButton;
|
||||
const auto x = (width() - w) / 2, y = st::boxPhotoPadding.top();
|
||||
|
||||
// App::roundRect(p, x, y, w, h, st::msgInBg, MessageInCorners, &st::msgInShadow);
|
||||
|
||||
@@ -414,7 +736,7 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
|
||||
QRect rthumb(rtlrect(x + 0, y + 0, st::msgFileThumbSize, st::msgFileThumbSize, width()));
|
||||
p.drawPixmap(rthumb.topLeft(), _thumb);
|
||||
} else {
|
||||
QRect inner(rtlrect(x + 0, y + 0, st::msgFileSize, st::msgFileSize, width()));
|
||||
const QRect inner(rtlrect(x + 0, y + 0, st::msgFileSize, st::msgFileSize, width()));
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::msgFileInBg);
|
||||
|
||||
@@ -423,14 +745,14 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
|
||||
p.drawEllipse(inner);
|
||||
}
|
||||
|
||||
auto icon = &(_isAudio ? st::historyFileInPlay : _isImage ? st::historyFileInImage : st::historyFileInDocument);
|
||||
const auto icon = &(_isAudio ? st::historyFileInPlay : _isImage ? st::historyFileInImage : st::historyFileInDocument);
|
||||
icon->paintInCenter(p, inner);
|
||||
}
|
||||
p.setFont(st::semiboldFont);
|
||||
p.setPen(st::historyFileNameInFg);
|
||||
_name.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width());
|
||||
|
||||
auto &status = st::mediaInFg;
|
||||
const auto &status = st::mediaInFg;
|
||||
p.setFont(st::normalFont);
|
||||
p.setPen(status);
|
||||
p.drawTextLeft(x + nameleft, y + statustop, width(), _status);
|
||||
@@ -445,10 +767,28 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
|
||||
p.setPen(st::boxTextFgError);
|
||||
p.drawTextLeft(_field->x(), _field->y() + _field->height() + errorTopSkip(), width(), _error);
|
||||
}
|
||||
|
||||
if (_isAllowedEditMedia) {
|
||||
_editMedia->moveToRight(
|
||||
st::boxPhotoPadding.right() + (_doc
|
||||
? st::editMediaButtonFileSkipRight
|
||||
: st::editMediaButtonSkip),
|
||||
st::boxPhotoPadding.top() + (_doc
|
||||
? st::editMediaButtonFileSkipTop
|
||||
: st::editMediaButtonSkip));
|
||||
}
|
||||
}
|
||||
|
||||
void EditCaptionBox::resizeEvent(QResizeEvent *e) {
|
||||
BoxContent::resizeEvent(e);
|
||||
|
||||
if (_photo) {
|
||||
_wayWrap->resize(st::sendMediaPreviewSize, _wayWrap->height());
|
||||
_wayWrap->moveToLeft(
|
||||
st::boxPhotoPadding.left(),
|
||||
st::boxPhotoPadding.top() + _thumbh);
|
||||
}
|
||||
|
||||
_field->resize(st::sendMediaPreviewSize, _field->height());
|
||||
_field->moveToLeft(st::boxPhotoPadding.left(), height() - st::normalFont->height - errorTopSkip() - _field->height());
|
||||
_emojiToggle->moveToLeft(
|
||||
@@ -465,7 +805,7 @@ void EditCaptionBox::setInnerFocus() {
|
||||
void EditCaptionBox::save() {
|
||||
if (_saveRequestId) return;
|
||||
|
||||
auto item = App::histItemById(_msgId);
|
||||
const auto item = App::histItemById(_msgId);
|
||||
if (!item) {
|
||||
_error = lang(lng_edit_deleted);
|
||||
update();
|
||||
@@ -493,6 +833,25 @@ void EditCaptionBox::save() {
|
||||
if (!sentEntities.v.isEmpty()) {
|
||||
flags |= MTPmessages_EditMessage::Flag::f_entities;
|
||||
}
|
||||
|
||||
if (!_preparedList.files.empty()) {
|
||||
const auto textWithTags = _field->getTextWithAppliedMarkdown();
|
||||
auto sending = TextWithEntities{
|
||||
textWithTags.text,
|
||||
ConvertTextTagsToEntities(textWithTags.tags)
|
||||
};
|
||||
item->setText(sending);
|
||||
|
||||
Auth().api().editMedia(
|
||||
std::move(_preparedList),
|
||||
(!_asFile && _photo) ? SendMediaType::Photo : SendMediaType::File,
|
||||
_field->getTextWithAppliedMarkdown(),
|
||||
ApiWrap::SendOptions(item->history()),
|
||||
item->fullId().msg);
|
||||
closeBox();
|
||||
return;
|
||||
}
|
||||
|
||||
_saveRequestId = MTP::send(
|
||||
MTPmessages_EditMessage(
|
||||
MTP_flags(flags),
|
||||
@@ -500,7 +859,7 @@ void EditCaptionBox::save() {
|
||||
MTP_int(item->id),
|
||||
MTP_string(sending.text),
|
||||
MTPInputMedia(),
|
||||
MTPnullMarkup,
|
||||
MTPReplyMarkup(),
|
||||
sentEntities),
|
||||
rpcDone(&EditCaptionBox::saveDone),
|
||||
rpcFail(&EditCaptionBox::saveFail));
|
||||
@@ -531,3 +890,20 @@ bool EditCaptionBox::saveFail(const RPCError &error) {
|
||||
update();
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditCaptionBox::setName(QString nameString, qint64 size) {
|
||||
_name.setText(
|
||||
st::semiboldTextStyle,
|
||||
nameString,
|
||||
Ui::NameTextOptions());
|
||||
_status = formatSizeText(size);
|
||||
}
|
||||
|
||||
void EditCaptionBox::keyPressEvent(QKeyEvent *e) {
|
||||
if ((e->key() == Qt::Key_E || e->key() == Qt::Key_O)
|
||||
&& e->modifiers() == Qt::ControlModifier) {
|
||||
_editMediaClicks.fire({});
|
||||
} else {
|
||||
e->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include <rpl/event_stream.h>
|
||||
|
||||
namespace ChatHelpers {
|
||||
class TabbedPanel;
|
||||
@@ -24,6 +27,8 @@ class Media;
|
||||
namespace Ui {
|
||||
class InputField;
|
||||
class EmojiButton;
|
||||
class IconButton;
|
||||
class Checkbox;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
@@ -43,10 +48,11 @@ protected:
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
private:
|
||||
void updateBoxSize();
|
||||
void prepareGifPreview(not_null<DocumentData*> document);
|
||||
void prepareGifPreview(DocumentData* document = nullptr);
|
||||
void clipCallback(Media::Clip::Notification notification);
|
||||
|
||||
void setupEmojiPanel();
|
||||
@@ -59,11 +65,24 @@ private:
|
||||
void saveDone(const MTPUpdates &updates);
|
||||
bool saveFail(const RPCError &error);
|
||||
|
||||
void setName(QString nameString, qint64 size);
|
||||
bool fileFromClipboard(not_null<const QMimeData*> data);
|
||||
void updateEditPreview();
|
||||
void updateEditMediaButton();
|
||||
|
||||
int errorTopSkip() const;
|
||||
|
||||
void createEditMediaButton();
|
||||
|
||||
inline QString getNewMediaPath() {
|
||||
return _preparedList.files.empty()
|
||||
? QString()
|
||||
: _preparedList.files.front().path;
|
||||
}
|
||||
|
||||
not_null<Window::Controller*> _controller;
|
||||
FullMsgId _msgId;
|
||||
ImagePtr _thumbnailImage;
|
||||
Image *_thumbnailImage = nullptr;
|
||||
bool _thumbnailImageLoaded = false;
|
||||
Fn<void()> _refreshThumbnail;
|
||||
bool _animated = false;
|
||||
@@ -83,13 +102,26 @@ private:
|
||||
int _thumbh = 0;
|
||||
Text _name;
|
||||
QString _status;
|
||||
int _statusw = 0;
|
||||
bool _isAudio = false;
|
||||
bool _isImage = false;
|
||||
|
||||
int _gifw = 0;
|
||||
int _gifh = 0;
|
||||
int _gifx = 0;
|
||||
|
||||
Storage::PreparedList _preparedList;
|
||||
|
||||
bool _previewCancelled = false;
|
||||
mtpRequestId _saveRequestId = 0;
|
||||
|
||||
object_ptr<Ui::IconButton> _editMedia = nullptr;
|
||||
Ui::SlideWrap<Ui::RpWidget> *_wayWrap = nullptr;
|
||||
QString _newMediaPath;
|
||||
bool _isAllowedEditMedia = false;
|
||||
bool _isAlbum = false;
|
||||
bool _asFile = false;
|
||||
rpl::event_stream<> _editMediaClicks;
|
||||
|
||||
QString _error;
|
||||
|
||||
};
|
||||
|
||||
@@ -442,7 +442,7 @@ public:
|
||||
|
||||
protected:
|
||||
void correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) override;
|
||||
void paintAdditionalPlaceholder(Painter &p, TimeMs ms) override;
|
||||
void paintAdditionalPlaceholder(Painter &p) override;
|
||||
|
||||
void wheelEvent(QWheelEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
@@ -501,7 +501,7 @@ void EditColorBox::Field::correctValue(const QString &was, int wasCursor, QStrin
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Field::paintAdditionalPlaceholder(Painter &p, TimeMs ms) {
|
||||
void EditColorBox::Field::paintAdditionalPlaceholder(Painter &p) {
|
||||
p.setFont(_st.font);
|
||||
p.setPen(_st.placeholderFg);
|
||||
auto inner = QRect(_st.textMargins.right(), _st.textMargins.top(), width() - 2 * _st.textMargins.right(), height() - _st.textMargins.top() - _st.textMargins.bottom());
|
||||
@@ -565,7 +565,7 @@ public:
|
||||
|
||||
protected:
|
||||
void correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) override;
|
||||
void paintAdditionalPlaceholder(Painter &p, TimeMs ms) override;
|
||||
void paintAdditionalPlaceholder(Painter &p) override;
|
||||
|
||||
};
|
||||
|
||||
@@ -606,7 +606,7 @@ void EditColorBox::ResultField::correctValue(const QString &was, int wasCursor,
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::ResultField::paintAdditionalPlaceholder(Painter &p, TimeMs ms) {
|
||||
void EditColorBox::ResultField::paintAdditionalPlaceholder(Painter &p) {
|
||||
p.setFont(_st.font);
|
||||
p.setPen(_st.placeholderFg);
|
||||
p.drawText(QRect(_st.textMargins.right(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), "#", style::al_topleft);
|
||||
|
||||
@@ -239,12 +239,12 @@ void EditPrivacyBox::setupContent() {
|
||||
|
||||
const auto group = std::make_shared<Ui::RadioenumGroup<Option>>(
|
||||
_value.option);
|
||||
const auto toggle = Ui::CreateChild<rpl::event_stream<>>(content);
|
||||
|
||||
const auto toggle = Ui::CreateChild<rpl::event_stream<Option>>(content);
|
||||
group->setChangedCallback([=](Option value) {
|
||||
_value.option = value;
|
||||
toggle->fire({});
|
||||
toggle->fire_copy(value);
|
||||
});
|
||||
auto optionValue = toggle->events_starting_with_copy(_value.option);
|
||||
|
||||
const auto addOptionRow = [&](Option option) {
|
||||
return (_controller->hasOption(option) || (_value.option == option))
|
||||
@@ -275,8 +275,8 @@ void EditPrivacyBox::setupContent() {
|
||||
std::move(label),
|
||||
st::settingsButton,
|
||||
text);
|
||||
button->toggleOn(toggle->events_starting_with(
|
||||
rpl::empty_value()
|
||||
button->toggleOn(rpl::duplicate(
|
||||
optionValue
|
||||
) | rpl::map([=] {
|
||||
return showExceptionLink(exception);
|
||||
}))->entity()->addClickHandler([=] {
|
||||
@@ -285,6 +285,13 @@ void EditPrivacyBox::setupContent() {
|
||||
return button;
|
||||
};
|
||||
|
||||
auto above = _controller->setupAboveWidget(
|
||||
content,
|
||||
std::move(optionValue));
|
||||
if (above) {
|
||||
content->add(std::move(above));
|
||||
}
|
||||
|
||||
AddSubsectionTitle(content, _controller->optionsTitleKey());
|
||||
addOptionRow(Option::Everyone);
|
||||
addOptionRow(Option::Contacts);
|
||||
|
||||
@@ -38,21 +38,30 @@ public:
|
||||
public:
|
||||
using Key = ApiWrap::Privacy::Key;
|
||||
|
||||
virtual Key key() = 0;
|
||||
virtual MTPInputPrivacyKey apiKey() = 0;
|
||||
[[nodiscard]] virtual Key key() = 0;
|
||||
[[nodiscard]] virtual MTPInputPrivacyKey apiKey() = 0;
|
||||
|
||||
virtual QString title() = 0;
|
||||
virtual bool hasOption(Option option) {
|
||||
[[nodiscard]] virtual QString title() = 0;
|
||||
[[nodiscard]] virtual bool hasOption(Option option) {
|
||||
return true;
|
||||
}
|
||||
virtual LangKey optionsTitleKey() = 0;
|
||||
virtual LangKey optionLabelKey(Option option);
|
||||
virtual rpl::producer<QString> warning() {
|
||||
[[nodiscard]] virtual LangKey optionsTitleKey() = 0;
|
||||
[[nodiscard]] virtual LangKey optionLabelKey(Option option);
|
||||
[[nodiscard]] virtual rpl::producer<QString> warning() {
|
||||
return rpl::never<QString>();
|
||||
}
|
||||
virtual LangKey exceptionButtonTextKey(Exception exception) = 0;
|
||||
virtual QString exceptionBoxTitle(Exception exception) = 0;
|
||||
virtual rpl::producer<QString> exceptionsDescription() = 0;
|
||||
[[nodiscard]] virtual LangKey exceptionButtonTextKey(
|
||||
Exception exception) = 0;
|
||||
[[nodiscard]] virtual QString exceptionBoxTitle(
|
||||
Exception exception) = 0;
|
||||
[[nodiscard]] virtual auto exceptionsDescription()
|
||||
-> rpl::producer<QString> = 0;
|
||||
|
||||
[[nodiscard]] virtual object_ptr<Ui::RpWidget> setupAboveWidget(
|
||||
not_null<QWidget*> parent,
|
||||
rpl::producer<Option> option) {
|
||||
return { nullptr };
|
||||
}
|
||||
|
||||
virtual void confirmSave(
|
||||
bool someAreDisallowed,
|
||||
|
||||
@@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "messenger.h"
|
||||
#include "core/application.h"
|
||||
#include "lang/lang_instance.h"
|
||||
#include "lang/lang_cloud_manager.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -772,7 +772,6 @@ int Rows::countAvailableWidth() const {
|
||||
void Rows::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
const auto ms = getms();
|
||||
const auto clip = e->rect();
|
||||
|
||||
const auto checkLeft = st::passportRowPadding.left();
|
||||
@@ -801,14 +800,14 @@ void Rows::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
|
||||
if (row.ripple) {
|
||||
row.ripple->paint(p, 0, 0, width(), ms);
|
||||
row.ripple->paint(p, 0, 0, width());
|
||||
if (row.ripple->empty()) {
|
||||
row.ripple.reset();
|
||||
}
|
||||
}
|
||||
|
||||
const auto checkTop = (row.height - st::defaultRadio.diameter) / 2;
|
||||
row.check->paint(p, checkLeft, checkTop, width(), ms);
|
||||
row.check->paint(p, checkLeft, checkTop, width());
|
||||
|
||||
auto top = st::passportRowPadding.top();
|
||||
|
||||
@@ -829,7 +828,7 @@ void Rows::paintEvent(QPaintEvent *e) {
|
||||
p.drawEllipse(menu);
|
||||
}
|
||||
if (row.menuToggleRipple) {
|
||||
row.menuToggleRipple->paint(p, menu.x(), menu.y(), width(), ms);
|
||||
row.menuToggleRipple->paint(p, menu.x(), menu.y(), width());
|
||||
if (row.menuToggleRipple->empty()) {
|
||||
row.menuToggleRipple.reset();
|
||||
}
|
||||
@@ -1132,10 +1131,10 @@ not_null<Ui::MultiSelect*> LanguageBox::createMultiSelect() {
|
||||
base::binary_guard LanguageBox::Show() {
|
||||
auto result = base::binary_guard();
|
||||
|
||||
const auto manager = Messenger::Instance().langCloudManager();
|
||||
const auto manager = Core::App().langCloudManager();
|
||||
if (manager->languageList().empty()) {
|
||||
auto guard = std::make_shared<base::binary_guard>();
|
||||
std::tie(result, *guard) = base::make_binary_guard();
|
||||
auto guard = std::make_shared<base::binary_guard>(
|
||||
result.make_guard());
|
||||
auto alive = std::make_shared<std::unique_ptr<base::Subscription>>(
|
||||
std::make_unique<base::Subscription>());
|
||||
**alive = manager->languageListChanged().add_subscription([=] {
|
||||
|
||||
@@ -26,19 +26,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kSizeLimitsCount = 20;
|
||||
constexpr auto kMegabyte = int64(1024 * 1024);
|
||||
constexpr auto kTotalSizeLimitsCount = 18;
|
||||
constexpr auto kMediaSizeLimitsCount = 18;
|
||||
constexpr auto kMinimalSizeLimit = 100 * kMegabyte;
|
||||
constexpr auto kTimeLimitsCount = 16;
|
||||
constexpr auto kMaxTimeLimitValue = std::numeric_limits<size_type>::max();
|
||||
constexpr auto kFakeMediaCacheTag = uint16(0xFFFF);
|
||||
|
||||
int64 SizeLimitInMB(int index) {
|
||||
if (index < 10) {
|
||||
return int64(index + 1) * 100;
|
||||
int64 TotalSizeLimitInMB(int index) {
|
||||
if (index < 8) {
|
||||
return int64(index + 2) * 100;
|
||||
}
|
||||
return int64(index - 9) * 1024;
|
||||
return int64(index - 7) * 1024;
|
||||
}
|
||||
|
||||
int64 SizeLimit(int index) {
|
||||
return SizeLimitInMB(index) * 1024 * 1024;
|
||||
int64 TotalSizeLimit(int index) {
|
||||
return TotalSizeLimitInMB(index) * kMegabyte;
|
||||
}
|
||||
|
||||
int64 MediaSizeLimitInMB(int index) {
|
||||
if (index < 9) {
|
||||
return int64(index + 1) * 100;
|
||||
}
|
||||
return int64(index - 8) * 1024;
|
||||
}
|
||||
|
||||
int64 MediaSizeLimit(int index) {
|
||||
return MediaSizeLimitInMB(index) * kMegabyte;
|
||||
}
|
||||
|
||||
QString SizeLimitText(int64 limit) {
|
||||
@@ -124,7 +139,7 @@ protected:
|
||||
private:
|
||||
QString titleText(const Database::TaggedSummary &data) const;
|
||||
QString sizeText(const Database::TaggedSummary &data) const;
|
||||
void step_radial(TimeMs ms, bool timer);
|
||||
void radialAnimationCallback();
|
||||
|
||||
Fn<QString(size_type)> _titleFactory;
|
||||
object_ptr<Ui::FlatLabel> _title;
|
||||
@@ -171,7 +186,7 @@ void LocalStorageBox::Row::toggleProgress(bool shown) {
|
||||
_clearing.destroy();
|
||||
} else if (!_progress) {
|
||||
_progress = std::make_unique<Ui::InfiniteRadialAnimation>(
|
||||
animation(this, &Row::step_radial),
|
||||
[=] { radialAnimationCallback(); },
|
||||
st::proxyCheckingAnimation);
|
||||
_progress->start();
|
||||
_clearing = object_ptr<Ui::FlatLabel>(
|
||||
@@ -186,8 +201,8 @@ void LocalStorageBox::Row::toggleProgress(bool shown) {
|
||||
}
|
||||
}
|
||||
|
||||
void LocalStorageBox::Row::step_radial(TimeMs ms, bool timer) {
|
||||
if (timer && !anim::Disabled()) {
|
||||
void LocalStorageBox::Row::radialAnimationCallback() {
|
||||
if (!anim::Disabled()) {
|
||||
RpWidget::update();
|
||||
}
|
||||
}
|
||||
@@ -229,11 +244,9 @@ void LocalStorageBox::Row::paintEvent(QPaintEvent *e) {
|
||||
return;
|
||||
}
|
||||
Painter p(this);
|
||||
|
||||
const auto padding = st::localStorageRowPadding;
|
||||
const auto height = st::localStorageRowHeight;
|
||||
const auto bottom = height - padding.bottom() - _description->height();
|
||||
_progress->step(crl::time());
|
||||
_progress->draw(
|
||||
p,
|
||||
{
|
||||
@@ -256,20 +269,30 @@ QString LocalStorageBox::Row::sizeText(const Database::TaggedSummary &data) cons
|
||||
LocalStorageBox::LocalStorageBox(
|
||||
QWidget*,
|
||||
not_null<Database*> db,
|
||||
not_null<Database*> dbBig,
|
||||
CreateTag)
|
||||
: _db(db) {
|
||||
: _db(db)
|
||||
, _dbBig(dbBig) {
|
||||
const auto &settings = Local::cacheSettings();
|
||||
_sizeLimit = settings.totalSizeLimit;
|
||||
const auto &settingsBig = Local::cacheBigFileSettings();
|
||||
_totalSizeLimit = settings.totalSizeLimit + settingsBig.totalSizeLimit;
|
||||
_mediaSizeLimit = settingsBig.totalSizeLimit;
|
||||
_timeLimit = settings.totalTimeLimit;
|
||||
}
|
||||
|
||||
void LocalStorageBox::Show(not_null<Database*> db) {
|
||||
void LocalStorageBox::Show(
|
||||
not_null<Database*> db,
|
||||
not_null<Database*> dbBig) {
|
||||
auto shared = std::make_shared<object_ptr<LocalStorageBox>>(
|
||||
Box<LocalStorageBox>(db, CreateTag()));
|
||||
Box<LocalStorageBox>(db, dbBig, CreateTag()));
|
||||
const auto weak = shared->data();
|
||||
db->statsOnMain(
|
||||
) | rpl::start_with_next([=](Database::Stats &&stats) {
|
||||
weak->update(std::move(stats));
|
||||
rpl::combine(
|
||||
db->statsOnMain(),
|
||||
dbBig->statsOnMain()
|
||||
) | rpl::start_with_next([=](
|
||||
Database::Stats &&stats,
|
||||
Database::Stats &&statsBig) {
|
||||
weak->update(std::move(stats), std::move(statsBig));
|
||||
if (auto &strong = *shared) {
|
||||
Ui::show(std::move(strong));
|
||||
}
|
||||
@@ -286,7 +309,7 @@ void LocalStorageBox::prepare() {
|
||||
|
||||
void LocalStorageBox::updateRow(
|
||||
not_null<Ui::SlideWrap<Row>*> row,
|
||||
Database::TaggedSummary *data) {
|
||||
const Database::TaggedSummary *data) {
|
||||
const auto summary = (_rows.find(0)->second == row);
|
||||
const auto shown = (data && data->count && data->totalSize) || summary;
|
||||
if (shown) {
|
||||
@@ -295,28 +318,45 @@ void LocalStorageBox::updateRow(
|
||||
row->toggle(shown, anim::type::normal);
|
||||
}
|
||||
|
||||
void LocalStorageBox::update(Database::Stats &&stats) {
|
||||
void LocalStorageBox::update(
|
||||
Database::Stats &&stats,
|
||||
Database::Stats &&statsBig) {
|
||||
_stats = std::move(stats);
|
||||
_statsBig = std::move(statsBig);
|
||||
if (const auto i = _rows.find(0); i != end(_rows)) {
|
||||
i->second->entity()->toggleProgress(_stats.clearing);
|
||||
i->second->entity()->toggleProgress(
|
||||
_stats.clearing || _statsBig.clearing);
|
||||
}
|
||||
for (const auto &entry : _rows) {
|
||||
if (entry.first) {
|
||||
if (entry.first == kFakeMediaCacheTag) {
|
||||
updateRow(entry.second, &_statsBig.full);
|
||||
} else if (entry.first) {
|
||||
const auto i = _stats.tagged.find(entry.first);
|
||||
updateRow(
|
||||
entry.second,
|
||||
(i != end(_stats.tagged)) ? &i->second : nullptr);
|
||||
} else {
|
||||
updateRow(entry.second, &_stats.full);
|
||||
const auto full = summary();
|
||||
updateRow(entry.second, &full);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalStorageBox::clearByTag(uint8 tag) {
|
||||
if (tag) {
|
||||
auto LocalStorageBox::summary() const -> Database::TaggedSummary {
|
||||
auto result = _stats.full;
|
||||
result.count += _statsBig.full.count;
|
||||
result.totalSize += _statsBig.full.totalSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
void LocalStorageBox::clearByTag(uint16 tag) {
|
||||
if (tag == kFakeMediaCacheTag) {
|
||||
_dbBig->clear();
|
||||
} else if (tag) {
|
||||
_db->clearByTag(tag);
|
||||
} else {
|
||||
_db->clear();
|
||||
_dbBig->clear();
|
||||
Ui::Emoji::ClearIrrelevantCache();
|
||||
}
|
||||
}
|
||||
@@ -326,7 +366,7 @@ void LocalStorageBox::setupControls() {
|
||||
object_ptr<Ui::VerticalLayout>(this),
|
||||
st::contactsMultiSelect.scroll);
|
||||
const auto createRow = [&](
|
||||
uint8 tag,
|
||||
uint16 tag,
|
||||
Fn<QString(size_type)> title,
|
||||
Fn<QString()> clear,
|
||||
const Database::TaggedSummary &data) {
|
||||
@@ -364,11 +404,14 @@ void LocalStorageBox::setupControls() {
|
||||
auto summaryTitle = [](size_type) {
|
||||
return lang(lng_local_storage_summary);
|
||||
};
|
||||
auto mediaCacheTitle = [](size_type) {
|
||||
return lang(lng_local_storage_media);
|
||||
};
|
||||
createRow(
|
||||
0,
|
||||
std::move(summaryTitle),
|
||||
langFactory(lng_local_storage_clear),
|
||||
_stats.full);
|
||||
summary());
|
||||
setupLimits(container);
|
||||
const auto shadow = container->add(object_ptr<Ui::SlideWrap<>>(
|
||||
container,
|
||||
@@ -379,6 +422,11 @@ void LocalStorageBox::setupControls() {
|
||||
createTagRow(Data::kVoiceMessageCacheTag, lng_local_storage_voice);
|
||||
createTagRow(Data::kVideoMessageCacheTag, lng_local_storage_round);
|
||||
createTagRow(Data::kAnimationCacheTag, lng_local_storage_animation);
|
||||
tracker.track(createRow(
|
||||
kFakeMediaCacheTag,
|
||||
std::move(mediaCacheTitle),
|
||||
langFactory(lng_local_storage_clear_some),
|
||||
_statsBig.full));
|
||||
shadow->toggleOn(
|
||||
std::move(tracker).atLeastOneShownValue()
|
||||
);
|
||||
@@ -394,7 +442,7 @@ template <
|
||||
typename Convert,
|
||||
typename Callback,
|
||||
typename>
|
||||
void LocalStorageBox::createLimitsSlider(
|
||||
not_null<Ui::MediaSlider*> LocalStorageBox::createLimitsSlider(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
int valuesCount,
|
||||
Convert &&convert,
|
||||
@@ -415,6 +463,60 @@ void LocalStorageBox::createLimitsSlider(
|
||||
[=, callback = std::forward<Callback>(callback)](Value value) {
|
||||
callback(label, value);
|
||||
});
|
||||
return slider;
|
||||
}
|
||||
|
||||
void LocalStorageBox::updateMediaLimit() {
|
||||
const auto good = [&](int64 mediaLimit) {
|
||||
return (_totalSizeLimit - mediaLimit >= kMinimalSizeLimit);
|
||||
};
|
||||
if (good(_mediaSizeLimit) || !_mediaSlider || !_mediaLabel) {
|
||||
return;
|
||||
}
|
||||
auto index = 1;
|
||||
while ((index < kMediaSizeLimitsCount)
|
||||
&& (MediaSizeLimit(index) * 2 <= _totalSizeLimit)) {
|
||||
++index;
|
||||
}
|
||||
--index;
|
||||
_mediaSizeLimit = MediaSizeLimit(index);
|
||||
_mediaSlider->setValue(index / float64(kMediaSizeLimitsCount - 1));
|
||||
updateMediaLabel();
|
||||
|
||||
Ensures(good(_mediaSizeLimit));
|
||||
}
|
||||
|
||||
void LocalStorageBox::updateTotalLimit() {
|
||||
const auto good = [&](int64 totalLimit) {
|
||||
return (totalLimit - _mediaSizeLimit >= kMinimalSizeLimit);
|
||||
};
|
||||
if (good(_totalSizeLimit) || !_totalSlider || !_totalLabel) {
|
||||
return;
|
||||
}
|
||||
auto index = kTotalSizeLimitsCount - 1;
|
||||
while ((index > 0)
|
||||
&& (TotalSizeLimit(index - 1) >= 2 * _mediaSizeLimit)) {
|
||||
--index;
|
||||
}
|
||||
_totalSizeLimit = TotalSizeLimit(index);
|
||||
_totalSlider->setValue(index / float64(kTotalSizeLimitsCount - 1));
|
||||
updateTotalLabel();
|
||||
|
||||
Ensures(good(_totalSizeLimit));
|
||||
}
|
||||
|
||||
void LocalStorageBox::updateTotalLabel() {
|
||||
Expects(_totalLabel != nullptr);
|
||||
|
||||
const auto text = SizeLimitText(_totalSizeLimit);
|
||||
_totalLabel->setText(lng_local_storage_size_limit(lt_size, text));
|
||||
}
|
||||
|
||||
void LocalStorageBox::updateMediaLabel() {
|
||||
Expects(_mediaLabel != nullptr);
|
||||
|
||||
const auto text = SizeLimitText(_mediaSizeLimit);
|
||||
_mediaLabel->setText(lng_local_storage_media_limit(lt_size, text));
|
||||
}
|
||||
|
||||
void LocalStorageBox::setupLimits(not_null<Ui::VerticalLayout*> container) {
|
||||
@@ -422,15 +524,29 @@ void LocalStorageBox::setupLimits(not_null<Ui::VerticalLayout*> container) {
|
||||
object_ptr<Ui::PlainShadow>(container),
|
||||
st::localStorageRowPadding);
|
||||
|
||||
createLimitsSlider(
|
||||
_totalSlider = createLimitsSlider(
|
||||
container,
|
||||
kSizeLimitsCount,
|
||||
SizeLimit,
|
||||
_sizeLimit,
|
||||
kTotalSizeLimitsCount,
|
||||
TotalSizeLimit,
|
||||
_totalSizeLimit,
|
||||
[=](not_null<Ui::LabelSimple*> label, int64 limit) {
|
||||
const auto text = SizeLimitText(limit);
|
||||
label->setText(lng_local_storage_size_limit(lt_size, text));
|
||||
_sizeLimit = limit;
|
||||
_totalSizeLimit = limit;
|
||||
_totalLabel = label;
|
||||
updateTotalLabel();
|
||||
updateMediaLimit();
|
||||
limitsChanged();
|
||||
});
|
||||
|
||||
_mediaSlider = createLimitsSlider(
|
||||
container,
|
||||
kMediaSizeLimitsCount,
|
||||
MediaSizeLimit,
|
||||
_mediaSizeLimit,
|
||||
[=](not_null<Ui::LabelSimple*> label, int64 limit) {
|
||||
_mediaSizeLimit = limit;
|
||||
_mediaLabel = label;
|
||||
updateMediaLabel();
|
||||
updateTotalLimit();
|
||||
limitsChanged();
|
||||
});
|
||||
|
||||
@@ -440,17 +556,21 @@ void LocalStorageBox::setupLimits(not_null<Ui::VerticalLayout*> container) {
|
||||
TimeLimit,
|
||||
LimitToValue(_timeLimit),
|
||||
[=](not_null<Ui::LabelSimple*> label, size_type limit) {
|
||||
const auto text = TimeLimitText(ValueToLimit(limit));
|
||||
_timeLimit = ValueToLimit(limit);
|
||||
const auto text = TimeLimitText(_timeLimit);
|
||||
label->setText(lng_local_storage_time_limit(lt_limit, text));
|
||||
_timeLimit = limit;
|
||||
limitsChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void LocalStorageBox::limitsChanged() {
|
||||
const auto &settings = Local::cacheSettings();
|
||||
const auto changed = (settings.totalSizeLimit != _sizeLimit)
|
||||
|| (settings.totalTimeLimit != _timeLimit);
|
||||
const auto &settingsBig = Local::cacheBigFileSettings();
|
||||
const auto sizeLimit = _totalSizeLimit - _mediaSizeLimit;
|
||||
const auto changed = (settings.totalSizeLimit != sizeLimit)
|
||||
|| (settingsBig.totalSizeLimit != _mediaSizeLimit)
|
||||
|| (settings.totalTimeLimit != _timeLimit)
|
||||
|| (settingsBig.totalTimeLimit != _timeLimit);
|
||||
if (_limitsChanged != changed) {
|
||||
_limitsChanged = changed;
|
||||
clearButtons();
|
||||
@@ -469,9 +589,12 @@ void LocalStorageBox::save() {
|
||||
return;
|
||||
}
|
||||
auto update = Storage::Cache::Database::SettingsUpdate();
|
||||
update.totalSizeLimit = _sizeLimit;
|
||||
update.totalSizeLimit = _totalSizeLimit - _mediaSizeLimit;
|
||||
update.totalTimeLimit = _timeLimit;
|
||||
Local::updateCacheSettings(update);
|
||||
auto updateBig = Storage::Cache::Database::SettingsUpdate();
|
||||
updateBig.totalSizeLimit = _mediaSizeLimit;
|
||||
updateBig.totalTimeLimit = _timeLimit;
|
||||
Local::updateCacheSettings(update, updateBig);
|
||||
Auth().data().cache().updateSettings(update);
|
||||
closeBox();
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ class VerticalLayout;
|
||||
template <typename Widget>
|
||||
class SlideWrap;
|
||||
class LabelSimple;
|
||||
class MediaSlider;
|
||||
} // namespace Ui
|
||||
|
||||
class LocalStorageBox : public BoxContent {
|
||||
@@ -30,9 +31,13 @@ class LocalStorageBox : public BoxContent {
|
||||
public:
|
||||
using Database = Storage::Cache::Database;
|
||||
|
||||
LocalStorageBox(QWidget*, not_null<Database*> db, CreateTag);
|
||||
LocalStorageBox(
|
||||
QWidget*,
|
||||
not_null<Database*> db,
|
||||
not_null<Database*> dbBig,
|
||||
CreateTag);
|
||||
|
||||
static void Show(not_null<Database*> db);
|
||||
static void Show(not_null<Database*> db, not_null<Database*> dbBig);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -42,16 +47,22 @@ protected:
|
||||
private:
|
||||
class Row;
|
||||
|
||||
void clearByTag(uint8 tag);
|
||||
void update(Database::Stats &&stats);
|
||||
void clearByTag(uint16 tag);
|
||||
void update(Database::Stats &&stats, Database::Stats &&statsBig);
|
||||
void updateRow(
|
||||
not_null<Ui::SlideWrap<Row>*> row,
|
||||
Database::TaggedSummary *data);
|
||||
const Database::TaggedSummary *data);
|
||||
void setupControls();
|
||||
void setupLimits(not_null<Ui::VerticalLayout*> container);
|
||||
void updateMediaLimit();
|
||||
void updateTotalLimit();
|
||||
void updateTotalLabel();
|
||||
void updateMediaLabel();
|
||||
void limitsChanged();
|
||||
void save();
|
||||
|
||||
Database::TaggedSummary summary() const;
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename Convert,
|
||||
@@ -62,7 +73,7 @@ private:
|
||||
not_null<Ui::LabelSimple*>,
|
||||
Value>
|
||||
&& std::is_same_v<Value, decltype(std::declval<Convert>()(1))>>>
|
||||
void createLimitsSlider(
|
||||
not_null<Ui::MediaSlider*> createLimitsSlider(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
int valuesCount,
|
||||
Convert &&convert,
|
||||
@@ -70,11 +81,18 @@ private:
|
||||
Callback &&callback);
|
||||
|
||||
not_null<Storage::Cache::Database*> _db;
|
||||
not_null<Storage::Cache::Database*> _dbBig;
|
||||
Database::Stats _stats;
|
||||
Database::Stats _statsBig;
|
||||
|
||||
base::flat_map<uint8, not_null<Ui::SlideWrap<Row>*>> _rows;
|
||||
base::flat_map<uint16, not_null<Ui::SlideWrap<Row>*>> _rows;
|
||||
Ui::MediaSlider *_totalSlider = nullptr;
|
||||
Ui::LabelSimple *_totalLabel = nullptr;
|
||||
Ui::MediaSlider *_mediaSlider = nullptr;
|
||||
Ui::LabelSimple *_mediaLabel = nullptr;
|
||||
|
||||
int64 _sizeLimit = 0;
|
||||
int64 _totalSizeLimit = 0;
|
||||
int64 _mediaSizeLimit = 0;
|
||||
size_type _timeLimit = 0;
|
||||
bool _limitsChanged = false;
|
||||
|
||||
|
||||
@@ -372,7 +372,7 @@ void PasscodeBox::validateEmail(
|
||||
}
|
||||
|
||||
void PasscodeBox::handleSrpIdInvalid() {
|
||||
const auto now = getms(true);
|
||||
const auto now = crl::now();
|
||||
if (_lastSrpIdInvalidTime > 0
|
||||
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
|
||||
_curRequest.id = 0;
|
||||
@@ -403,7 +403,7 @@ void PasscodeBox::save(bool force) {
|
||||
if (_turningOff) pwd = conf = QString();
|
||||
} else {
|
||||
cSetPasscodeBadTries(cPasscodeBadTries() + 1);
|
||||
cSetPasscodeLastTry(getms(true));
|
||||
cSetPasscodeLastTry(crl::now());
|
||||
badOldPasscode();
|
||||
return;
|
||||
}
|
||||
@@ -452,10 +452,13 @@ void PasscodeBox::save(bool force) {
|
||||
changeCloudPassword(old, pwd);
|
||||
}
|
||||
} else {
|
||||
const auto weak = make_weak(this);
|
||||
cSetPasscodeBadTries(0);
|
||||
Local::setPasscode(pwd.toUtf8());
|
||||
Auth().checkAutoLock();
|
||||
closeBox();
|
||||
Auth().localPasscodeChanged();
|
||||
if (weak) {
|
||||
closeBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ private:
|
||||
mtpRequestId _setRequest = 0;
|
||||
|
||||
Core::CloudPasswordCheckRequest _curRequest;
|
||||
TimeMs _lastSrpIdInvalidTime = 0;
|
||||
crl::time _lastSrpIdInvalidTime = 0;
|
||||
Core::CloudPasswordAlgo _newAlgo;
|
||||
Core::SecureSecretAlgo _newSecureSecretAlgo;
|
||||
bool _hasRecovery = false;
|
||||
|
||||