Compare commits
128 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb1c8c4aea | ||
|
|
1ee5c14b85 | ||
|
|
01410e5d92 | ||
|
|
0e5d26a469 | ||
|
|
95e5b7be0b | ||
|
|
6d8f277904 | ||
|
|
b6325ec9d4 | ||
|
|
3373a2f382 | ||
|
|
9fcd878f1d | ||
|
|
1c50c35abe | ||
|
|
6a0ee57054 | ||
|
|
00adfa6f3d | ||
|
|
6058c8862e | ||
|
|
e5f2f68188 | ||
|
|
c6ee2772e2 | ||
|
|
16caff1ca4 | ||
|
|
ac2ae16f47 | ||
|
|
c40758f30d | ||
|
|
16aafe28d5 | ||
|
|
db96605332 | ||
|
|
31954f5266 | ||
|
|
66718de562 | ||
|
|
e2d02f4e4b | ||
|
|
e3d4bf192f | ||
|
|
e7b94f3d3a | ||
|
|
30be7af3e3 | ||
|
|
9069b4b269 | ||
|
|
040c80ae0b | ||
|
|
a87c9b15d2 | ||
|
|
635cae4f94 | ||
|
|
67e6d12384 | ||
|
|
9ccdc0e94b | ||
|
|
762c0aa579 | ||
|
|
5dc78932d7 | ||
|
|
e9c5f18142 | ||
|
|
175023b2dc | ||
|
|
0a7c42c59a | ||
|
|
42122fdea0 | ||
|
|
4bfe65d8ab | ||
|
|
1b06fe1220 | ||
|
|
1b11a7feae | ||
|
|
6b60b51775 | ||
|
|
981162e6b9 | ||
|
|
c5dd99b1f1 | ||
|
|
53c536d76d | ||
|
|
e3ab8821b9 | ||
|
|
6befea6a13 | ||
|
|
f24e3c6192 | ||
|
|
63e593b3a8 | ||
|
|
7ca4ec1bed | ||
|
|
8ed1961886 | ||
|
|
ad44e45695 | ||
|
|
962ec1e454 | ||
|
|
bcc718b5a9 | ||
|
|
ac50119dd9 | ||
|
|
e953e11b7f | ||
|
|
18361ce144 | ||
|
|
cfdacb09ac | ||
|
|
5bb83afc7a | ||
|
|
f35853c42e | ||
|
|
44492b9e2d | ||
|
|
a46bb46e54 | ||
|
|
84c190c293 | ||
|
|
9da4a21f94 | ||
|
|
2a3a351445 | ||
|
|
928cd0a8cb | ||
|
|
509d2189c1 | ||
|
|
725fa87188 | ||
|
|
147eaab59a | ||
|
|
890ec34202 | ||
|
|
92858dc7d3 | ||
|
|
4440b42848 | ||
|
|
136fd5c8e1 | ||
|
|
d92356ce28 | ||
|
|
47f673aa69 | ||
|
|
64d4a3e8a8 | ||
|
|
87e72fc7aa | ||
|
|
6d44a3ec95 | ||
|
|
eb47eabba4 | ||
|
|
cfb0de69f0 | ||
|
|
2d46cc4c11 | ||
|
|
9761c5bb56 | ||
|
|
7d6bf487a7 | ||
|
|
6fda783fc1 | ||
|
|
aeaa039542 | ||
|
|
62e85b1cf0 | ||
|
|
080a08fa76 | ||
|
|
be1eb1c693 | ||
|
|
daf5d4acdb | ||
|
|
f4781b9117 | ||
|
|
0f778431f5 | ||
|
|
2121ce1210 | ||
|
|
c54aadcac3 | ||
|
|
3fc74166de | ||
|
|
749b13adec | ||
|
|
c33ddf49ff | ||
|
|
bc1e2dcb54 | ||
|
|
9a193ed88f | ||
|
|
fb32c5bcd1 | ||
|
|
67b46d9aac | ||
|
|
aeb2ec68ef | ||
|
|
e1c304c2e5 | ||
|
|
c7de9d4668 | ||
|
|
45aa6dff75 | ||
|
|
8b0562b946 | ||
|
|
373f1a0ff0 | ||
|
|
058f6bd8de | ||
|
|
a707f7b9e7 | ||
|
|
61d1574023 | ||
|
|
fe59898e5c | ||
|
|
0df1952a04 | ||
|
|
1d8ec7c7d6 | ||
|
|
b770ea4f8d | ||
|
|
b31f2d952c | ||
|
|
370c47d95b | ||
|
|
9b3767e77c | ||
|
|
196b643e7d | ||
|
|
c9626c140c | ||
|
|
a356a4dc06 | ||
|
|
34d5c3777b | ||
|
|
ac74a08d53 | ||
|
|
47b0f901c8 | ||
|
|
18103aae75 | ||
|
|
cc9ae13297 | ||
|
|
334b3ac706 | ||
|
|
a9063eb87b | ||
|
|
175e0f71ce | ||
|
|
782c254ea0 |
72
MSVC.md
@@ -39,7 +39,7 @@ Extract to **D:\TBuild\Libraries**
|
||||
|
||||
http://www.zlib.net/ > Download [**zlib source code, version 1.2.8, zipfile format**](http://zlib.net/zlib128.zip)
|
||||
|
||||
Extract to **D:\TBuild\Libraries\**
|
||||
Extract to **D:\\TBuild\\Libraries\\**
|
||||
|
||||
#####Building library
|
||||
|
||||
@@ -66,13 +66,23 @@ or download in ZIP and extract to **D:\TBuild\Libraries\**, rename **libexif-0.6
|
||||
* Build Debug configuration
|
||||
* Build Release configuration
|
||||
|
||||
####OpenAL Soft
|
||||
####OpenAL Soft, slightly patched
|
||||
|
||||
Get sources by git – in [Git Bash](http://git-scm.com/downloads) go to **/d/tbuild/libraries** and run
|
||||
|
||||
git clone git://repo.or.cz/openal-soft.git
|
||||
|
||||
to have **D:\TBuild\Libraries\openal-soft\CMakeLists.txt**
|
||||
to have **D:\TBuild\Libraries\openal-soft\CMakeLists.txt**, then in [Git Bash](http://git-scm.com/downloads) go to **/d/tbuild/libraries/openal-soft** and run
|
||||
|
||||
git checkout 9479ea656b
|
||||
|
||||
Apply patch
|
||||
|
||||
* OR copy (with overwrite!) everything from **D:\\TBuild\\tdesktop\\\_openal\_patch\\** to **D:\\TBuild\\Libraries\\openal-soft\\**
|
||||
* OR in Git Bash go to **/d/tbuild/libraries/openal-soft/** and run
|
||||
|
||||
git apply ./../../tdesktop/Telegram/_openal_patch.diff
|
||||
|
||||
|
||||
#####Building library
|
||||
|
||||
@@ -87,35 +97,47 @@ to have **D:\TBuild\Libraries\openal-soft\CMakeLists.txt**
|
||||
* OpenAL32 Properties > C/C++ > Code Generation > Runtime Library = **Multi-threaded (/MT)** – **OK**
|
||||
* common Properties > C/C++ > Code Generation > Runtime Library = **Multi-threaded (/MT)** – **OK**
|
||||
|
||||
####libogg 1.3.2
|
||||
|
||||
Get sources from http://xiph.org/downloads/ – in [ZIP](http://downloads.xiph.org/releases/ogg/libogg-1.3.2.zip) and extract to **D:\TBuild\Libraries\**
|
||||
|
||||
#####Building library
|
||||
|
||||
* Open in VS2013 **D:\TBuild\Libraries\libogg-1.3.2\win32\VS2010\libogg_static.sln** > One-way upgrade – **OK**
|
||||
* Build Debug configuration
|
||||
* Build Release configuration
|
||||
|
||||
####Opus codec, opusfile
|
||||
####Opus codec
|
||||
|
||||
Get sources by git – in [Git Bash](http://git-scm.com/downloads) go to **/d/tbuild/libraries** and run
|
||||
|
||||
git clone git://git.opus-codec.org/opus.git
|
||||
git clone git://git.xiph.org/opusfile.git
|
||||
git clone https://github.com/telegramdesktop/opus.git
|
||||
|
||||
to have **D:\TBuild\Libraries\opus\win32**
|
||||
|
||||
#####Building libraries
|
||||
|
||||
* Open in VS2013 **D:\TBuild\Libraries\opus\win32\VS2010\opus.sln** > One-way upgrade – **OK**
|
||||
* Open in VS2013 **D:\TBuild\Libraries\opus\win32\VS2010\opus.sln**
|
||||
* Build Debug configuration
|
||||
* Build Release configuration
|
||||
* Open in VS2013 **D:\TBuild\Libraries\opusfile\win32\VS2010\opusfile.sln** > One-way upgrade – **OK**
|
||||
* For **Debug** and **Release** configurations
|
||||
* opusfile > C/C++ > General > Additional include directories > Add **../../../libogg-1.3.2/include;**
|
||||
* Build Debug configuration
|
||||
* Build Release configuration
|
||||
* Build Release configuration (it will be required in **FFmpeg** build!)
|
||||
|
||||
####FFmpeg
|
||||
|
||||
https://www.ffmpeg.org/download.html > Download [ffmpeg-2.6.3.tar.bz2](http://ffmpeg.org/releases/ffmpeg-2.6.3.tar.bz2)
|
||||
|
||||
Extract to **D:\\TBuild\\Libraries**
|
||||
|
||||
http://msys2.github.io/ > Download [msys2-x86_64-20150512.exe](http://sourceforge.net/projects/msys2/files/Base/x86_64/msys2-x86_64-20150512.exe/download) and install to **D:\\msys64**
|
||||
|
||||
#####Building libraries
|
||||
|
||||
Download [yasm for Win64](http://www.tortall.net/projects/yasm/releases/yasm-1.3.0-win64.exe) from http://yasm.tortall.net/Download.html, rename **yasm-1.3.0-win64.exe** to **yasm.exe** and place it to your Visual C++ **bin** directory, like **\\Program Files (x86)\\Microsoft Visual Studio 12\\VC\\bin\\**
|
||||
|
||||
Open **VS2013 x86 Native Tools Command Prompt.bat** (should be in **\\Program Files (x86)\\Microsoft Visual Studio 12.0\\Common7\\Tools\\Shortcuts\\** folder), go to **D:\\msys64\\** and launch **msys2_shell.bat**, there run
|
||||
|
||||
PATH="/c/Program Files (x86)/Microsoft Visual Studio 12.0/VC/BIN:$PATH"
|
||||
|
||||
cd /d/TBuild/Libraries/ffmpeg-2.6.3
|
||||
pacman -S msys/make
|
||||
pacman -S mingw64/mingw-w64-x86_64-opus
|
||||
pacman -S pkg-config
|
||||
|
||||
PKG_CONFIG_PATH="/mingw64/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
|
||||
./configure --toolchain=msvc --disable-programs --disable-everything --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --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=wavpack --enable-decoder=opus --enable-decoder=vorbis --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=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=wav --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=mov --enable-muxer=ogg --enable-muxer=opus --extra-ldflags="-libpath:/d/TBuild/Libraries/opus/win32/VS2010/Win32/Release celt.lib silk_common.lib silk_float.lib"
|
||||
|
||||
make
|
||||
make install
|
||||
|
||||
####Qt 5.4.0, slightly patched
|
||||
|
||||
@@ -126,9 +148,9 @@ Extract to **D:\TBuild\Libraries\**, rename **qt-everywhere-opensource-src-5.4.0
|
||||
Apply patch
|
||||
|
||||
* OR copy (with overwrite!) everything from **D:\TBuild\tdesktop\\\_qt\_5\_4\_0\_patch\** to **D:\TBuild\Libraries\QtStatic\**
|
||||
* OR copy **D:\TBuild\tdesktop\\\_qt\_5\_4\_0\_patch.diff** to **D:\TBuild\Libraries\QtStatic\**, go there in Git Bash and run
|
||||
* OR in Git Bash go to **/d/TBuild/Libraries/QtStatic/** and run
|
||||
|
||||
git apply _qt_5_4_0_patch.diff
|
||||
git apply ./../../tdesktop/Telegram/_qt_5_4_0_patch.diff
|
||||
|
||||
#####Building library
|
||||
|
||||
|
||||
23
QTCREATOR.md
@@ -34,27 +34,24 @@ Install dev libraries
|
||||
|
||||
Install audio libraries
|
||||
|
||||
####libogg-1.3.2
|
||||
|
||||
[Download libogg-1.3.2 sources](http://downloads.xiph.org/releases/ogg/libogg-1.3.2.tar.xz) from http://xiph.org/downloads, extract to **/home/user/TBuild/Libraries**, go to **/home/user/TBuild/Libraries/libogg-1.3.2** and run
|
||||
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
|
||||
####Opus codec 1.1
|
||||
|
||||
[Download opus-1.1 sources](http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz) from http://www.opus-codec.org/downloads, extract to **/home/user/TBuild/Libraries**, go to **/home/user/TBuild/Libraries/opus-1.1** and run
|
||||
Download [opus-1.1 sources](http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz) from http://www.opus-codec.org/downloads, extract to **/home/user/TBuild/Libraries**, go to **/home/user/TBuild/Libraries/opus-1.1** and run
|
||||
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
|
||||
####opusfile-0.6
|
||||
####FFmpeg
|
||||
|
||||
[Download opusfile-0.6 sources](http://downloads.xiph.org/releases/opus/opusfile-0.6.tar.gz) from http://www.opus-codec.org/downloads, extract to **/home/user/TBuild/Libraries**, go to **/home/user/TBuild/Libraries/opusfile-0.6** and run
|
||||
Download sources [ffmpeg-2.6.3.tar.bz2](http://ffmpeg.org/releases/ffmpeg-2.6.3.tar.bz2) from https://www.ffmpeg.org/download.html, extract to **/home/user/TBuild/Libraries** to have **/home/user/TBuild/Libraries/ffmpeg-2.6.3**, go to **/home/user/TBuild/Libraries/ffmpeg-2.6.3** and run
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get -y --force-yes install autoconf automake build-essential libass-dev libfreetype6-dev libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev
|
||||
sudo apt-get install yasm
|
||||
|
||||
./configure --prefix=/usr/local --disable-programs --disable-everything --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --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=wavpack --enable-decoder=opus --enable-decoder=vorbis --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=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=wav --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=mov --enable-muxer=ogg --enable-muxer=opus
|
||||
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
|
||||
@@ -72,7 +69,7 @@ In Terminal go to **/home/user/TBuild/Libraries** and run
|
||||
|
||||
git clone git://repo.or.cz/openal-soft.git
|
||||
|
||||
then go to **/home/user/TBuild/Libraries/openal-soft/build** and run
|
||||
then go to **/home/user/TBuild/Libraries/openal-soft/build** and run
|
||||
|
||||
sudo apt-get install cmake
|
||||
cmake -D LIBTYPE:STRING=STATIC ..
|
||||
|
||||
22
README.md
@@ -6,17 +6,9 @@ Source code is published under GPL v3, license is available [here](https://githu
|
||||
|
||||
###Supported systems
|
||||
|
||||
* Windows XP
|
||||
* Windows Vista
|
||||
* Windows 7
|
||||
* Windows 8 (**not** RT)
|
||||
* Windows 8.1 (**not** RT)
|
||||
* OS X 10.7
|
||||
* OS X 10.8
|
||||
* OS X 10.9
|
||||
* Ubuntu 12.04
|
||||
* Ubuntu 13.04
|
||||
* Ubuntu 14.04
|
||||
* Windows XP - Windows 8.1 (**not** RT)
|
||||
* Mac OS X 10.7 - Mac OS X 10.10
|
||||
* Ubuntu 12.04 - Ubuntu 14.04
|
||||
|
||||
###Third-party
|
||||
|
||||
@@ -27,15 +19,15 @@ Source code is published under GPL v3, license is available [here](https://githu
|
||||
* LZMA SDK 9.20 ([public domain](http://www.7-zip.org/sdk.html))
|
||||
* liblzma ([public domain](http://tukaani.org/xz/))
|
||||
* OpenAL Soft ([LGPL](http://kcat.strangesoft.net/openal.html))
|
||||
* Opus codec, opusfile ([BSD license](http://www.opus-codec.org/license/))
|
||||
* libogg ([BSD license](http://www.xiph.org/downloads/))
|
||||
* Opus codec ([BSD license](http://www.opus-codec.org/license/))
|
||||
* FFmpeg ([LGPL](https://www.ffmpeg.org/legal.html))
|
||||
* Open Sans font ([Apache License](http://www.apache.org/licenses/LICENSE-2.0.html))
|
||||
|
||||
###[Build instructions for Visual Studio 2013](https://github.com/telegramdesktop/tdesktop/blob/master/MSVC.md)
|
||||
|
||||
###[Build instructions for XCode 5.1.1](https://github.com/telegramdesktop/tdesktop/blob/master/XCODE.md)
|
||||
###[Build instructions for XCode 6.3.1](https://github.com/telegramdesktop/tdesktop/blob/master/XCODE.md)
|
||||
|
||||
###[Build instructions for Qt Creator 3.1.2 Ubuntu](https://github.com/telegramdesktop/tdesktop/blob/master/QTCREATOR.md)
|
||||
###[Build instructions for Qt Creator 3.2.0 Ubuntu](https://github.com/telegramdesktop/tdesktop/blob/master/QTCREATOR.md)
|
||||
|
||||
##Projects in Telegram solution
|
||||
|
||||
|
||||
@@ -52,13 +52,10 @@ Global
|
||||
{6F483617-7C84-4E7E-91D8-1FF28A4CE3A0}.Release|Win32.Build.0 = Release|Win32
|
||||
{6F483617-7C84-4E7E-91D8-1FF28A4CE3A0}.Release|x64.ActiveCfg = Release|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Deploy|Win32.ActiveCfg = Deploy|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Deploy|Win32.Build.0 = Deploy|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Deploy|x64.ActiveCfg = Release|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Release|Win32.Build.0 = Release|Win32
|
||||
{EB7D16AC-EACF-4577-B05A-F28E5F356794}.Release|x64.ActiveCfg = Release|Win32
|
||||
{6B4BA3BE-7B15-4B4C-B200-81ABFDEF2C76}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{6B4BA3BE-7B15-4B4C-B200-81ABFDEF2C76}.Debug|Win32.Build.0 = Debug|Win32
|
||||
|
||||
@@ -23,7 +23,9 @@ Replace '\-lfontconfig' '\/usr\/lib\/x86_64\-linux\-gnu\/libfontconfig\.a \/usr\
|
||||
Replace '\-lfreetype' '\/usr\/lib\/x86_64\-linux\-gnu\/libfreetype\.a'
|
||||
Replace '\-lpng' '\/usr\/lib\/x86_64\-linux\-gnu\/libpng\.a'
|
||||
Replace '\-lXext' '\/usr\/lib\/x86_64\-linux\-gnu\/libXext\.a'
|
||||
Replace '\-lopusfile' '\/usr\/local\/lib\/libopusfile\.a'
|
||||
Replace '\-lopus' '\/usr\/local\/lib\/libopus\.a'
|
||||
Replace '\-lopenal' '\/usr\/local\/lib\/libopenal\.a'
|
||||
Replace '\-logg' '\/usr\/local\/lib\/libogg\.a'
|
||||
Replace '\-lavformat' '\/usr\/local\/lib\/libavformat\.a'
|
||||
Replace '\-lavcodec' '\/usr\/local\/lib\/libavcodec\.a'
|
||||
Replace '\-lswresample' '\/usr\/local\/lib\/libswresample\.a'
|
||||
Replace '\-lavutil' '\/usr\/local\/lib\/libavutil\.a'
|
||||
|
||||
@@ -23,7 +23,9 @@ Replace '\-lfontconfig' '\/usr\/lib\/i386\-linux\-gnu\/libfontconfig\.a \/usr\/l
|
||||
Replace '\-lfreetype' '\/usr\/lib\/i386\-linux\-gnu\/libfreetype\.a'
|
||||
Replace '\-lpng' '\/usr\/lib\/i386\-linux\-gnu\/libpng\.a'
|
||||
Replace '\-lXext' '\/usr\/lib\/i386\-linux\-gnu\/libXext\.a'
|
||||
Replace '\-lopusfile' '\/usr\/local\/lib\/libopusfile\.a'
|
||||
Replace '\-lopus' '\/usr\/local\/lib\/libopus\.a'
|
||||
Replace '\-lopenal' '\/usr\/local\/lib\/libopenal\.a'
|
||||
Replace '\-logg' '\/usr\/local\/lib\/libogg\.a'
|
||||
Replace '\-lavformat' '\/usr\/local\/lib\/libavformat\.a'
|
||||
Replace '\-lavcodec' '\/usr\/local\/lib\/libavcodec\.a'
|
||||
Replace '\-lswresample' '\/usr\/local\/lib\/libswresample\.a'
|
||||
Replace '\-lavutil' '\/usr\/local\/lib\/libavutil\.a'
|
||||
|
||||
@@ -515,7 +515,7 @@
|
||||
6DB9C3763D02B1415CD9D565 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0610;
|
||||
LastUpgradeCheck = 0630;
|
||||
};
|
||||
buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "MetaEmoji" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
@@ -589,6 +589,7 @@
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
@@ -677,6 +678,7 @@
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
|
||||
@@ -516,7 +516,7 @@
|
||||
6DB9C3763D02B1415CD9D565 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0610;
|
||||
LastUpgradeCheck = 0630;
|
||||
};
|
||||
buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "MetaStyle" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
@@ -603,6 +603,7 @@
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
@@ -691,6 +692,7 @@
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@echo OFF
|
||||
|
||||
set "AppVersion=8004"
|
||||
set "AppVersionStrSmall=0.8.4"
|
||||
set "AppVersionStr=0.8.4"
|
||||
set "AppVersionStrFull=0.8.4.0"
|
||||
set "DevChannel=0"
|
||||
set "AppVersion=8023"
|
||||
set "AppVersionStrSmall=0.8.23"
|
||||
set "AppVersionStr=0.8.23"
|
||||
set "AppVersionStrFull=0.8.23.0"
|
||||
set "DevChannel=1"
|
||||
|
||||
if %DevChannel% neq 0 goto preparedev
|
||||
|
||||
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} kicked {user}";
|
||||
"lng_action_user_left" = "{from} left the group";
|
||||
"lng_action_user_joined" = "{from} joined the group";
|
||||
"lng_action_user_joined_by_link" = "{from} joined the group via invite link";
|
||||
"lng_action_user_registered" = "{from} just joined Telegram";
|
||||
"lng_action_removed_photo" = "{from} removed group photo";
|
||||
"lng_action_changed_photo" = "{from} changed group photo";
|
||||
"lng_action_changed_title" = "{from} changed group name to «{title}»";
|
||||
"lng_action_created_chat" = "{from} created group «{title}»";
|
||||
|
||||
"lng_group_invite_bad_link" = "This invite link is broken\nor has expired.";
|
||||
"lng_group_invite_want_join" = "Do you want to join the group «{title}»?";
|
||||
"lng_group_invite_join" = "Join";
|
||||
|
||||
"lng_group_invite_link" = "Invite link";
|
||||
"lng_group_invite_create" = "Create an invite link";
|
||||
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link.";
|
||||
"lng_group_invite_create_new" = "Revoke invite link";
|
||||
"lng_group_invite_about_new" = "Your previous link will be deactivated\nand we'll generate a new invite link for you.";
|
||||
"lng_group_invite_copied" = "Invite link copied to clipboard.";
|
||||
"lng_group_invite_no_room" = "Unable to join this group because there are\ntoo many members in it already.";
|
||||
|
||||
"lng_forwarded_from" = "Forwarded from";
|
||||
"lng_in_reply_to" = "In reply to";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Video file";
|
||||
"lng_media_audio" = "Voice message";
|
||||
|
||||
"lng_emoji_category0" = "Frequently used";
|
||||
"lng_emoji_category1" = "People";
|
||||
"lng_emoji_category2" = "Nature";
|
||||
"lng_emoji_category3" = "Food & Drink";
|
||||
"lng_emoji_category4" = "Celebration";
|
||||
"lng_emoji_category5" = "Activity";
|
||||
"lng_emoji_category6" = "Travel & Places";
|
||||
"lng_emoji_category7" = "Objects & Symbols";
|
||||
|
||||
"lng_switch_stickers" = "Stickers";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Custom stickers";
|
||||
"lng_stickers_remove_pack" = "Remove «{sticker_pack}»?";
|
||||
"lng_stickers_add_pack" = "Add {count:_not_used_|# Sticker|# Stickers}";
|
||||
"lng_stickers_share_pack" = "Share Stickers";
|
||||
"lng_stickers_not_found" = "Sticker pack not found.";
|
||||
"lng_stickers_copied" = "Sticker pack link copied to clipboard.";
|
||||
"lng_stickers_default_set" = "Great Minds";
|
||||
|
||||
"lng_in_dlg_photo" = "Photo";
|
||||
"lng_in_dlg_video" = "Video";
|
||||
"lng_in_dlg_contact" = "Contact";
|
||||
@@ -417,6 +450,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "Send";
|
||||
"lng_message_ph" = "Write a message..";
|
||||
"lng_record_cancel" = "Release out of here to cancel";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "Please select chat to start messaging";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
@@ -458,6 +492,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Save Video As..";
|
||||
"lng_context_open_audio" = "Open Audio";
|
||||
"lng_context_save_audio" = "Save Audio As..";
|
||||
"lng_context_pack_info" = "Pack Info";
|
||||
"lng_context_open_file" = "Open File";
|
||||
"lng_context_save_file" = "Save File As..";
|
||||
"lng_context_forward_file" = "Forward File";
|
||||
@@ -527,12 +562,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:No messages found|Found # message|Found # messages}";
|
||||
"lng_search_global_results" = "Global search results";
|
||||
|
||||
"lng_mediaview_save" = "Download";
|
||||
"lng_media_save_progress" = "{ready} of {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Save As..";
|
||||
"lng_mediaview_copy" = "Copy";
|
||||
"lng_mediaview_forward" = "Forward";
|
||||
"lng_mediaview_delete" = "Delete";
|
||||
"lng_mediaview_photos_all" = "View all photos";
|
||||
"lng_mediaview_files_all" = "View all files";
|
||||
"lng_mediaview_single_photo" = "Single Photo";
|
||||
"lng_mediaview_group_photo" = "Group Photo";
|
||||
"lng_mediaview_profile_photo" = "Profile Photo";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} of {count}";
|
||||
"lng_mediaview_n_of_count" = "Photo {n} of {count}";
|
||||
"lng_mediaview_doc_image" = "File";
|
||||
"lng_mediaview_today" = "today at {time}";
|
||||
@@ -545,7 +585,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}";
|
||||
"lng_new_version_minor" = "— Bug fixes and other minor improvements";
|
||||
"lng_new_version_text" = "— Link previews for Twitter, YouTube, Instagram and certain other links\n— Two-step verification\n— View all your Telegram sessions, terminate specific sessions";
|
||||
"lng_new_version_text" = "— Improved sticker panel\n— Bug fixes and minor stuff";
|
||||
|
||||
"lng_menu_insert_unicode" = "Insert Unicode control character";
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ semibold: 'Open Sans Semibold';
|
||||
fsize: 13px;
|
||||
|
||||
spriteFile: ':/gui/art/sprite.png' / 2:':/gui/art/sprite_125x.png' / 3:':/gui/art/sprite_150x.png' / 4:':/gui/art/sprite_200x.png';
|
||||
emojisFile: ':/gui/art/emoji.png' / 2:':/gui/art/emoji_125x.png' / 3:':/gui/art/emoji_150x.png' / 4:':/gui/art/emoji_200x.png';
|
||||
emojiImgSize: 18px; // exceptional value for retina
|
||||
emojiSize: 18px;
|
||||
emojiPadding: 0px;
|
||||
@@ -55,7 +54,7 @@ wndBG: #FFF;
|
||||
wndShadow: sprite(209px, 46px, 19px, 19px);
|
||||
wndShadowShift: 1px;
|
||||
|
||||
layerAlpha: 0.3;
|
||||
layerAlpha: 0.5;
|
||||
layerBG: #000;
|
||||
|
||||
titleBG: #6389a8;
|
||||
@@ -123,9 +122,9 @@ sysUnlock: sysButton(sysUpd) {
|
||||
img: sprite(207px, 22px, 19px, 19px);
|
||||
}
|
||||
titleBackButton: iconedButton(btnDefIconed) {
|
||||
icon: sprite(133px, 197px, 13px, 20px);
|
||||
icon: sprite(113px, 108px, 13px, 20px);
|
||||
iconPos: point(5px, 9px);
|
||||
downIcon: sprite(133px, 197px, 13px, 20px);
|
||||
downIcon: sprite(113px, 108px, 13px, 20px);
|
||||
downIconPos: point(5px, 10px);
|
||||
|
||||
bgColor: #c4d8e9;
|
||||
@@ -309,7 +308,8 @@ scrollDef: flatScroll {
|
||||
width: 10px;
|
||||
minHeight: 20px;
|
||||
deltax: 3px;
|
||||
deltay: 3px;
|
||||
deltat: 3px;
|
||||
deltab: 3px;
|
||||
|
||||
topsh: 2px;
|
||||
bottomsh: 2px;
|
||||
@@ -390,7 +390,8 @@ btnIntroNext: flatButton(btnDefNext, btnDefBig) {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
boxShadow: sprite(230px, 46px, 9px, 9px);
|
||||
boxShadow: sprite(363px, 50px, 15px, 15px);
|
||||
boxShadowShift: 2px;
|
||||
|
||||
introCountry: countryInput {
|
||||
width: 300px;
|
||||
@@ -699,6 +700,7 @@ dlgDateSkip: 5px;
|
||||
|
||||
dlgUnreadColor: #FFF;
|
||||
dlgUnreadBG: #6fc766;
|
||||
dlgUnreadMutedBG: #bbb;
|
||||
dlgUnreadFont: font(12px bold);
|
||||
dlgUnreadPaddingHor: 5px;
|
||||
dlgUnreadPaddingVer: 1px;
|
||||
@@ -773,7 +775,9 @@ historyToEndSkip: 10px;
|
||||
activeFadeInDuration: 500;
|
||||
activeFadeOutDuration: 3000;
|
||||
|
||||
msgMaxWidth: 550px;
|
||||
msgRadius: 3px;
|
||||
|
||||
msgMaxWidth: 430px;
|
||||
msgFont: font(fsize);
|
||||
msgNameFont: font(fsize semibold);
|
||||
msgServiceFont: font(fsize semibold);
|
||||
@@ -787,12 +791,11 @@ msgPadding: margins(13px, 7px, 13px, 8px);
|
||||
msgMargin: margins(13px, 4px, 53px, 4px);
|
||||
msgLnkPadding: 2px; // for media open / save links
|
||||
msgBorder: #f0f0f0;
|
||||
msgOutBG: #effdde;
|
||||
msgInBG: #fff;
|
||||
msgOutSelectBG: #b7dbdb;
|
||||
msgInSelectBG: #c2dcf2; // #358cd4 with 30% opacity
|
||||
msgOutSelectOverlay: #358cd44c;
|
||||
msgInSelectOverlay: #358cd44c;
|
||||
msgOutBg: #effdde;
|
||||
msgInBg: #fff;
|
||||
msgOutSelectBg: #b7dbdb;
|
||||
msgInSelectBg: #c2dcf2; // #358cd4 with 30% opacity
|
||||
msgSelectOverlay: #358cd44c;
|
||||
msgStickerOverlay: #358cd47f;
|
||||
msgOutServiceColor: #3a8e26;
|
||||
msgInServiceColor: #0e7acd;
|
||||
@@ -817,10 +820,8 @@ msgInReplyBarColor: #2fa9e2;
|
||||
msgOutReplyBarSelColor: #4da79f;
|
||||
msgInReplyBarSelColor: #2fa9e2;
|
||||
|
||||
msgServiceSelectBG: #fff4;
|
||||
msgServiceRadius: 2px;
|
||||
|
||||
msgServiceBG: #89a0b47f;
|
||||
msgServiceBg: #89a0b47f;
|
||||
msgServiceSelectBg: #bbc8d4a2;
|
||||
msgServiceColor: #FFF;
|
||||
msgServicePadding: margins(12px, 3px, 12px, 4px);
|
||||
msgServiceMargin: margins(10px, 7px, 80px, 7px);
|
||||
@@ -831,7 +832,8 @@ msgLinkColor: #2a6dc2;
|
||||
msgPressedLinkColor: #004bad;
|
||||
msgSkip: 40px;
|
||||
msgPtr: 8px;
|
||||
msgBG: ':/gui/art/bg.png' / 2:':/gui/art/bg_125x.png' / 3:':/gui/art/bg_150x.png' / 4:':/gui/art/bg_200x.png';
|
||||
msgBG: ':/gui/art/bg.jpg';
|
||||
msgBG0: ':/gui/art/bg0.png';
|
||||
|
||||
msgSendingRect: sprite(260px, 20px, 20px, 20px);
|
||||
msgCheckRect: sprite(320px, 0px, 20px, 20px);
|
||||
@@ -849,6 +851,7 @@ msgImgDblCheckRect: sprite(300px, 65px, 20px, 20px);
|
||||
msgDateImgDelta: 4px;
|
||||
msgDateImgColor: #fff;
|
||||
msgDateImgBg: #00000054;
|
||||
msgDateImgSelectBg: #1c4a7187;
|
||||
msgDateImgPadding: point(8px, 2px);
|
||||
msgDateImgCheckSpace: 4px;
|
||||
|
||||
@@ -860,23 +863,23 @@ defaultTextStyle: textStyle {
|
||||
lnkOverFlags: font(fsize underline);
|
||||
lnkColor: btnYesColor;
|
||||
lnkDownColor: btnYesHover;
|
||||
selectBG: msgInSelectBG;
|
||||
selectOverlay: msgInSelectOverlay;
|
||||
selectBg: msgInSelectBg;
|
||||
selectOverlay: msgSelectOverlay;
|
||||
lineHeight: 0px;
|
||||
}
|
||||
serviceTextStyle: textStyle(defaultTextStyle) {
|
||||
lnkColor: msgServiceColor;
|
||||
lnkDownColor: msgServiceColor;
|
||||
selectBG: msgServiceSelectBG;
|
||||
selectOverlay: msgServiceSelectBG;
|
||||
selectBg: msgServiceSelectBg;
|
||||
selectOverlay: msgServiceSelectBg;
|
||||
}
|
||||
inTextStyle: textStyle(defaultTextStyle) {
|
||||
selectBG: msgInSelectBG;
|
||||
selectOverlay: msgInSelectOverlay;
|
||||
selectBg: msgInSelectBg;
|
||||
selectOverlay: msgSelectOverlay;
|
||||
}
|
||||
outTextStyle: textStyle(defaultTextStyle) {
|
||||
selectBG: msgOutSelectBG;
|
||||
selectOverlay: msgOutSelectOverlay;
|
||||
selectBg: msgOutSelectBg;
|
||||
selectOverlay: msgSelectOverlay;
|
||||
}
|
||||
medviewSaveAsTextStyle: textStyle(defaultTextStyle) {
|
||||
lnkColor: #91d9ff;
|
||||
@@ -918,6 +921,12 @@ mediaInColor: msgInDateColor;
|
||||
mediaOutColor: msgOutDateColor;
|
||||
mediaInSelectColor: msgInSelectDateColor;
|
||||
mediaOutSelectColor: msgOutSelectDateColor;
|
||||
mediaOutUnreadColor: #6aad60;
|
||||
mediaOutUnreadSelectColor: #5aa382;
|
||||
mediaInUnreadColor: #999;
|
||||
mediaInUnreadSelectColor: #7b95aa;
|
||||
mediaUnreadSize: 4px;
|
||||
mediaUnreadSkip: 5px;
|
||||
mediaSaveDelta: 14px; // between bubble and download
|
||||
mediaSaveButton: flatButton(btnDefFlat) {
|
||||
color: #507da2;
|
||||
@@ -976,13 +985,22 @@ btnAttachPhoto: iconedButton(btnAttachDocument) {
|
||||
}
|
||||
btnAttachEmoji: iconedButton(btnAttachDocument) {
|
||||
overBgColor: white;
|
||||
icon: sprite(311px, 221px, 20px, 20px);
|
||||
iconPos: point(6px, 13px);
|
||||
downIcon: sprite(311px, 221px, 20px, 20px);
|
||||
downIconPos: point(6px, 13px);
|
||||
icon: sprite(363px, 344px, 21px, 22px);
|
||||
iconPos: point(6px, 12px);
|
||||
downIcon: sprite(363px, 344px, 21px, 22px);
|
||||
downIconPos: point(6px, 12px);
|
||||
|
||||
width: 32px;
|
||||
width: 33px;
|
||||
}
|
||||
btnRecordAudio: sprite(363px, 366px, 16px, 24px);
|
||||
btnRecordAudioActive: sprite(379px, 366px, 16px, 24px);
|
||||
recordSignalColor: #f17077;
|
||||
recordSignalMin: 5px;
|
||||
recordSignalMax: 10px;
|
||||
recordCancel: #aaa;
|
||||
recordCancelActive: #ec6466;
|
||||
recordFont: font(16px);
|
||||
recordTextTop: 12px;
|
||||
|
||||
replySkip: 51px;
|
||||
replyColor: #377aae;
|
||||
@@ -990,7 +1008,7 @@ replyHeight: 49px;
|
||||
replyTop: 8px;
|
||||
replyBottom: 6px;
|
||||
replyIconPos: point(13px, 13px);
|
||||
replyIcon: sprite(174px, 195px, 24px, 24px);
|
||||
replyIcon: sprite(343px, 197px, 24px, 24px);
|
||||
replyCancel: iconedButton(btnDefIconed) {
|
||||
icon: sprite(165px, 24px, 14px, 14px);
|
||||
iconPos: point(17px, 17px);
|
||||
@@ -1001,7 +1019,7 @@ replyCancel: iconedButton(btnDefIconed) {
|
||||
width: 49px;
|
||||
height: 49px;
|
||||
}
|
||||
forwardIcon: sprite(368px, 173px, 24px, 24px);
|
||||
forwardIcon: sprite(368px, 197px, 24px, 24px);
|
||||
|
||||
historyScroll: flatScroll(scrollDef) {
|
||||
barColor: #89a0b47a;
|
||||
@@ -1013,7 +1031,8 @@ historyScroll: flatScroll(scrollDef) {
|
||||
|
||||
width: 12px;
|
||||
deltax: 3px;
|
||||
deltay: 3px;
|
||||
deltat: 3px;
|
||||
deltab: 3px;
|
||||
|
||||
topsh: 0px;
|
||||
bottomsh: -1px;
|
||||
@@ -1168,10 +1187,9 @@ btnShareContact: flatButton(btnDefNext, btnDefBig) {
|
||||
}
|
||||
|
||||
forwardWidth: 364px;
|
||||
forwardRadius: 2px;
|
||||
forwardMargins: margins(30px, 10px, 30px, 10px);
|
||||
forwardFont: font(16px);
|
||||
forwardBG: rgba(0, 0, 0, 76);
|
||||
forwardBg: rgba(0, 0, 0, 76);
|
||||
btnProfileCancel: flatButton(btnDefFlat, btnDefBig) {
|
||||
color: #666d78;
|
||||
overColor: #666d78;
|
||||
@@ -1355,18 +1373,23 @@ btnInfoClose: flatButton(aboutCloseButton) {
|
||||
emojiTextFont: font(16px);
|
||||
emojiReplaceWidth: 56px;
|
||||
emojiReplaceHeight: 56px;
|
||||
emojiReplaceInnerHeight: 38px;
|
||||
emojiReplaceInnerHeight: 42px;
|
||||
|
||||
connectingBG: #fffe;
|
||||
connectingColor: #777;
|
||||
connectingPadding: margins(5px, 5px, 5px, 5px);
|
||||
|
||||
dropdownPadding: margins(10px, 10px, 10px, 10px);
|
||||
dropdownShadow: sprite(241px, 46px, 6px, 6px);
|
||||
dropdownBorder: 1px;
|
||||
dropdownBorderColor: #ebebeb;
|
||||
dropdownBackground: white;
|
||||
dropdownDuration: 150;
|
||||
dropdownDef: dropdown {
|
||||
border: 1px;
|
||||
borderColor: #ebebeb;
|
||||
|
||||
padding: margins(10px, 10px, 10px, 10px);
|
||||
shadow: sprite(241px, 46px, 6px, 6px);
|
||||
shadowShift: 1px;
|
||||
|
||||
duration: 150;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
dropdownAttachDocument: iconedButton(btnAttachDocument) {
|
||||
iconPos: point(14px, 13px);
|
||||
@@ -1441,41 +1464,90 @@ dpiFont2: linkFont;
|
||||
dpiFont3: linkFont;
|
||||
dpiFont4: linkFont;
|
||||
|
||||
emojiScroll: flatScroll(scrollDef) {
|
||||
width: 5px;
|
||||
deltax: 2px;
|
||||
deltay: 1px;
|
||||
newScroll: flatScroll(scrollDef) {
|
||||
barColor: #3f729734;
|
||||
bgColor: #214f751a;
|
||||
barOverColor: #3f729734;
|
||||
bgOverColor: #214f751a;
|
||||
|
||||
deltax: 5px;
|
||||
width: 14px;
|
||||
deltat: 6px;
|
||||
deltab: 6px;
|
||||
|
||||
topsh: 0px;
|
||||
bottomsh: 0px;
|
||||
|
||||
hiding: 0;
|
||||
}
|
||||
emojiRecentActive: sprite(290px, 287px, 20px, 20px);
|
||||
emojiRecentOver: sprite(311px, 287px, 20px, 20px);
|
||||
emojiRecent: sprite(6px, 197px, 20px, 20px);
|
||||
emojiPeopleActive: sprite(290px, 221px, 20px, 20px);
|
||||
emojiPeopleOver: sprite(311px, 221px, 20px, 20px);
|
||||
emojiPeople: sprite(27px, 197px, 20px, 20px);
|
||||
emojiNatureActive: sprite(245px, 266px, 20px, 20px);
|
||||
emojiNatureOver: sprite(266px, 266px, 20px, 20px);
|
||||
emojiNature: sprite(48px, 197px, 20px, 20px);
|
||||
emojiObjectsActive: sprite(290px, 242px, 20px, 20px);
|
||||
emojiObjectsOver: sprite(311px, 242px, 20px, 20px);
|
||||
emojiObjects: sprite(69px, 197px, 20px, 20px);
|
||||
emojiPlacesActive: sprite(245px, 287px, 20px, 20px);
|
||||
emojiPlacesOver: sprite(266px, 287px, 20px, 20px);
|
||||
emojiPlaces: sprite(90px, 197px, 20px, 20px);
|
||||
emojiSymbolsActive: sprite(290px, 266px, 20px, 20px);
|
||||
emojiSymbolsOver: sprite(311px, 266px, 20px, 20px);
|
||||
emojiSymbols: sprite(111px, 197px, 20px, 20px);
|
||||
emojiStickersActive: sprite(311px, 308px, 20px, 20px);
|
||||
emojiStickersOver: sprite(354px, 200px, 20px, 20px);
|
||||
emojiStickers: sprite(375px, 200px, 20px, 20px);
|
||||
|
||||
stickersMaxHeight: 340px;
|
||||
stickersAddOrShare: 70px;
|
||||
btnStickersAdd: flatButton(btnDefNext, btnDefBig) {
|
||||
width: 200px;
|
||||
height: 42px;
|
||||
|
||||
textTop: 9px;
|
||||
overTextTop: 9px;
|
||||
downTextTop: 10px;
|
||||
|
||||
font: font(17px);
|
||||
overFont: font(17px);
|
||||
|
||||
bgColor: #15c23c;
|
||||
overBgColor: #13a835;
|
||||
downBgColor: #13a835;
|
||||
}
|
||||
btnStickersClose: iconedButton(notifyClose) {
|
||||
iconPos: point(21px, 21px);
|
||||
downIconPos: point(21px, 22px);
|
||||
width: 52px;
|
||||
height: 48px;
|
||||
}
|
||||
stickersWidth: 344px;
|
||||
stickersPadding: 10px;
|
||||
stickersSize: size(64px, 64px);
|
||||
stickersScroll: flatScroll(newScroll) {
|
||||
deltab: 76px;
|
||||
}
|
||||
|
||||
emojiScroll: flatScroll(newScroll) {
|
||||
deltat: 48px;
|
||||
}
|
||||
emojiRecent: sprite(0px, 196px, 21px, 22px);
|
||||
emojiRecentOver: sprite(287px, 220px, 21px, 22px);
|
||||
emojiRecentActive: sprite(287px, 242px, 21px, 22px);
|
||||
emojiPeople: sprite(21px, 196px, 21px, 22px);
|
||||
emojiPeopleOver: sprite(308px, 220px, 21px, 22px);
|
||||
emojiPeopleActive: sprite(308px, 242px, 21px, 22px);
|
||||
emojiNature: sprite(42px, 196px, 21px, 22px);
|
||||
emojiNatureOver: sprite(245px, 264px, 21px, 22px);
|
||||
emojiNatureActive: sprite(245px, 286px, 21px, 22px);
|
||||
emojiFood: sprite(63px, 196px, 21px, 22px);
|
||||
emojiFoodOver: sprite(266px, 264px, 21px, 22px);
|
||||
emojiFoodActive: sprite(266px, 286px, 21px, 22px);
|
||||
emojiCelebration: sprite(84px, 196px, 21px, 22px);
|
||||
emojiCelebrationOver: sprite(287px, 264px, 21px, 22px);
|
||||
emojiCelebrationActive: sprite(287px, 286px, 21px, 22px);
|
||||
emojiActivity: sprite(126px, 196px, 21px, 22px);
|
||||
emojiActivityOver: sprite(321px, 344px, 21px, 22px);
|
||||
emojiActivityActive: sprite(321px, 366px, 21px, 22px);
|
||||
emojiTravel: sprite(105px, 196px, 21px, 22px);
|
||||
emojiTravelOver: sprite(308px, 264px, 21px, 22px);
|
||||
emojiTravelActive: sprite(308px, 286px, 21px, 22px);
|
||||
emojiObjects: sprite(147px, 196px, 21px, 22px);
|
||||
emojiObjectsOver: sprite(342px, 344px, 21px, 22px);
|
||||
emojiObjectsActive: sprite(342px, 366px, 21px, 22px);
|
||||
|
||||
emojiPanCategories: #f7f7f7;
|
||||
|
||||
rbEmoji: flatCheckbox {
|
||||
textColor: transparent;
|
||||
bgColor: transparent;
|
||||
disColor: transparent;
|
||||
bgColor: emojiPanCategories;
|
||||
disColor: emojiPanCategories;
|
||||
|
||||
width: 29px;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
height: 46px;
|
||||
|
||||
textTop: 0px;
|
||||
textLeft: 0px;
|
||||
@@ -1485,7 +1557,7 @@ rbEmoji: flatCheckbox {
|
||||
cursor: cursor(pointer);
|
||||
|
||||
disabledCursor: cursor(default);
|
||||
imagePos: point(5px, 8px);
|
||||
imagePos: point(8px, 12px);
|
||||
}
|
||||
rbEmojiRecent: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiRecent;
|
||||
@@ -1511,6 +1583,38 @@ rbEmojiNature: flatCheckbox(rbEmoji) {
|
||||
disImageRect: emojiNature;
|
||||
chkDisImageRect: emojiNatureActive;
|
||||
}
|
||||
rbEmojiFood: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiFood;
|
||||
chkImageRect: emojiFoodActive;
|
||||
overImageRect: emojiFoodOver;
|
||||
chkOverImageRect: emojiFoodActive;
|
||||
disImageRect: emojiFood;
|
||||
chkDisImageRect: emojiFoodActive;
|
||||
}
|
||||
rbEmojiCelebration: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiCelebration;
|
||||
chkImageRect: emojiCelebrationActive;
|
||||
overImageRect: emojiCelebrationOver;
|
||||
chkOverImageRect: emojiCelebrationActive;
|
||||
disImageRect: emojiCelebration;
|
||||
chkDisImageRect: emojiCelebrationActive;
|
||||
}
|
||||
rbEmojiActivity: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiActivity;
|
||||
chkImageRect: emojiActivityActive;
|
||||
overImageRect: emojiActivityOver;
|
||||
chkOverImageRect: emojiActivityActive;
|
||||
disImageRect: emojiActivity;
|
||||
chkDisImageRect: emojiActivityActive;
|
||||
}
|
||||
rbEmojiTravel: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiTravel;
|
||||
chkImageRect: emojiTravelActive;
|
||||
overImageRect: emojiTravelOver;
|
||||
chkOverImageRect: emojiTravelActive;
|
||||
disImageRect: emojiTravel;
|
||||
chkDisImageRect: emojiTravelActive;
|
||||
}
|
||||
rbEmojiObjects: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiObjects;
|
||||
chkImageRect: emojiObjectsActive;
|
||||
@@ -1519,146 +1623,148 @@ rbEmojiObjects: flatCheckbox(rbEmoji) {
|
||||
disImageRect: emojiObjects;
|
||||
chkDisImageRect: emojiObjectsActive;
|
||||
}
|
||||
rbEmojiPlaces: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiPlaces;
|
||||
chkImageRect: emojiPlacesActive;
|
||||
overImageRect: emojiPlacesOver;
|
||||
chkOverImageRect: emojiPlacesActive;
|
||||
disImageRect: emojiPlaces;
|
||||
chkDisImageRect: emojiPlacesActive;
|
||||
}
|
||||
rbEmojiSymbols: flatCheckbox(rbEmoji) {
|
||||
imageRect: emojiSymbols;
|
||||
chkImageRect: emojiSymbolsActive;
|
||||
overImageRect: emojiSymbolsOver;
|
||||
chkOverImageRect: emojiSymbolsActive;
|
||||
disImageRect: emojiSymbols;
|
||||
chkDisImageRect: emojiSymbolsActive;
|
||||
}
|
||||
rbEmojiStickers: flatCheckbox(rbEmojiRecent) {
|
||||
imageRect: emojiStickers;
|
||||
chkImageRect: emojiStickersActive;
|
||||
overImageRect: emojiStickersOver;
|
||||
chkOverImageRect: emojiStickersActive;
|
||||
disImageRect: emojiStickers;
|
||||
chkDisImageRect: emojiStickersActive;
|
||||
}
|
||||
emojiPanPadding: margins(5px, 0px, 0px, 5px);
|
||||
emojiPanSize: size(28px, 28px);
|
||||
emojiPanSub: 0px;
|
||||
emojiPanPadding: 10px;
|
||||
emojiPanSize: size(39px, 35px);
|
||||
emojiPanFullSize: size(300px, 321px);
|
||||
emojiPanDuration: 200;
|
||||
emojiPanHover: #f0f0f0;
|
||||
emojiPanRound: 2px;
|
||||
emojiPanHover: #f0f4f7;
|
||||
|
||||
stickerPanRound: 3px;
|
||||
stickerPanPadding: 2px;
|
||||
stickerPanDelete: sprite(158px, 197px, 12px, 12px);
|
||||
emojiPanHeader: 42px;
|
||||
emojiPanHeaderFont: font(fsize semibold);
|
||||
emojiPanHeaderColor: #999;
|
||||
emojiPanHeaderLeft: 17px;
|
||||
emojiPanHeaderTop: 12px;
|
||||
emojiPanHeaderBg: #fffffff2;
|
||||
|
||||
emojiColorsPadding: 5px;
|
||||
emojiColorsSep: 1px;
|
||||
emojiColorsSepColor: #d5d5d5;
|
||||
|
||||
emojiSwitchSkip: 27px;
|
||||
emojiSwitchImgSkip: 21px;
|
||||
emojiSwitchStickers: sprite(318px, 328px, 8px, 12px);
|
||||
emojiSwitchEmoji: sprite(310px, 328px, 8px, 12px);
|
||||
emojiSwitchColor: #42a8db;
|
||||
|
||||
stickerPanSize: size(55px, 55px);
|
||||
stickerPanPadding: 11px;
|
||||
stickerPanDelete: sprite(123px, 132px, 12px, 12px);
|
||||
stickerPanDeleteOpacity: 0.5;
|
||||
stickerIconPadding: 3px;
|
||||
stickerIconHover: #e8ecef;
|
||||
stickerIconSel: #dfe3e6;
|
||||
stickerIconRecent: sprite(342px, 50px, 21px, 22px);
|
||||
stickerIconLeft: sprite(342px, 72px, 40px, 1px);
|
||||
stickerIconRight: sprite(342px, 73px, 40px, 1px);
|
||||
stickerIconMove: 400;
|
||||
|
||||
medviewNavBarWidth: 132px;
|
||||
medviewLightNav: 0.5;
|
||||
medviewDarkNav: 1;
|
||||
medviewHeaderFont: font(semibold 18px);
|
||||
medviewNameFont: font(16px);
|
||||
medviewDateFont: font(14px);
|
||||
medviewNameTop: 13px;
|
||||
medviewDateTop: 39px;
|
||||
medviewLeft: sprite(340px, 79px, 28px, 48px);
|
||||
medviewRight: sprite(368px, 79px, 28px, 48px);
|
||||
medviewDeltaFromLastAction: 5px;
|
||||
medviewSwipeDistance: 80px;
|
||||
mvBgColor: #222;
|
||||
mvBgOpacity: 0.92;
|
||||
mvThickFont: font(fsize semibold);
|
||||
mvFont: font(fsize);
|
||||
|
||||
medviewSaveMsgCheck: sprite(341px, 174px, 22px, 18px);
|
||||
mvTextLeft: 16px;
|
||||
mvTextSkip: 10px;
|
||||
mvHeaderTop: 48px;
|
||||
mvTextTop: 24px;
|
||||
mvTextColor: white;
|
||||
mvTextOpacity: 0.5;
|
||||
mvTextOverOpacity: 1;
|
||||
|
||||
mvIconOpacity: 0.45;
|
||||
mvIconOverOpacity: 1;
|
||||
mvControlBgColor: black;
|
||||
mvControlBgOpacity: 0.3;
|
||||
mvControlMargin: 0px;
|
||||
mvControlSize: 90px;
|
||||
mvIconSize: size(60px, 56px);
|
||||
|
||||
mvLeft: sprite(320px, 400px, 12px, 22px);
|
||||
mvRight: sprite(332px, 400px, 12px, 22px);
|
||||
mvClose: sprite(344px, 400px, 18px, 18px);
|
||||
mvSave: sprite(362px, 400px, 14px, 19px);
|
||||
mvMore: sprite(376px, 400px, 5px, 21px);
|
||||
|
||||
mvDropdown: dropdown(dropdownDef) {
|
||||
shadow: sprite(0px, 0px, 0px, 0px);
|
||||
padding: margins(11px, 12px, 11px, 12px);
|
||||
|
||||
border: 0;
|
||||
width: 182px;
|
||||
}
|
||||
mvButton: iconedButton(btnDefIconed) {
|
||||
bgColor: #383838;
|
||||
overBgColor: #505050;
|
||||
font: font(fsize);
|
||||
|
||||
opacity: 1;
|
||||
overOpacity: 1;
|
||||
|
||||
width: -32px;
|
||||
height: 36px;
|
||||
|
||||
color: white;
|
||||
|
||||
textPos: point(16px, 9px);
|
||||
downTextPos: point(16px, 10px);
|
||||
|
||||
duration: 0;
|
||||
}
|
||||
mvContextButton: iconedButton(mvButton) {
|
||||
bgColor: #383838E6;
|
||||
overBgColor: #505050E7;
|
||||
}
|
||||
mvWaitHide: 2000;
|
||||
mvHideDuration: 1000;
|
||||
mvShowDuration: 200;
|
||||
mvFadeDuration: 150;
|
||||
|
||||
mvDocPadding: 18px;
|
||||
mvDocSize: size(340px, 116px);
|
||||
mvDocBg: white;
|
||||
mvDocNameTop: 4px;
|
||||
mvDocNameFont: font(semibold 14px);
|
||||
mvDocNameColor: black;
|
||||
mvDocSizeTop: 29px;
|
||||
mvDocSizeColor: #808080;
|
||||
mvDocExtTop: 35px;
|
||||
mvDocExtFont: font(semibold 18px);
|
||||
mvDocExtColor: white;
|
||||
mvDocExtPadding: 10px;
|
||||
mvDocLinksTop: 57px;
|
||||
mvDocRed: sprite(0px, 400px, 80px, 80px);
|
||||
mvDocRedColor: #e47272;
|
||||
mvDocYellow: sprite(80px, 400px, 80px, 80px);
|
||||
mvDocYellowColor: #efc274;
|
||||
mvDocGreen: sprite(160px, 400px, 80px, 80px);
|
||||
mvDocGreenColor: #61b96e;
|
||||
mvDocBlue: sprite(240px, 400px, 80px, 80px);
|
||||
mvDocBlueColor: #72b1df;
|
||||
mvDocLink: linkButton(btnDefLink) {
|
||||
color: #4595d3;
|
||||
overColor: #4595d3;
|
||||
downColor: #4595d3;
|
||||
}
|
||||
|
||||
mvDeltaFromLastAction: 5px;
|
||||
mvSwipeDistance: 80px;
|
||||
|
||||
mvCaptionPadding: margins(18px, 10px, 18px, 10px);
|
||||
mvCaptionMargin: size(11px, 11px);
|
||||
mvCaptionRadius: 2px;
|
||||
mvCaptionBg: #11111180;
|
||||
mvCaptionFont: font(fsize);
|
||||
|
||||
medviewSaveMsgCheck: sprite(311px, 309px, 22px, 18px);
|
||||
medviewSaveMsgFont: font(16px);
|
||||
medviewSaveMsgPadding: margins(55px, 19px, 29px, 20px);
|
||||
medviewSaveMsgCheckPos: point(23px, 21px);
|
||||
medviewSaveMsgRadius: 3px;
|
||||
medviewSaveMsgShowing: 200;
|
||||
medviewSaveMsgShown: 2000;
|
||||
medviewSaveMsgHiding: 2500;
|
||||
medviewSaveMsg: #000000b2;
|
||||
|
||||
medviewOverview: iconedButton(btnDefIconed) {
|
||||
bgColor: #0000;
|
||||
overBgColor: #00000040;
|
||||
font: font(16px);
|
||||
|
||||
opacity: 0.77;
|
||||
overOpacity: 1;
|
||||
|
||||
icon: sprite(340px, 129px, 19px, 19px);
|
||||
iconPos: point(16px, 14px);
|
||||
downIcon: sprite(340px, 129px, 19px, 19px);
|
||||
downIconPos: point(16px, 14px);
|
||||
|
||||
width: -69px;
|
||||
height: 47px;
|
||||
|
||||
color: white;
|
||||
|
||||
textPos: point(51px, 13px);
|
||||
downTextPos: point(51px, 14px);
|
||||
}
|
||||
medviewForward: iconedButton(medviewOverview) {
|
||||
icon: sprite(357px, 58px, 22px, 17px);
|
||||
iconPos: point(16px, 15px);
|
||||
downIcon: sprite(357px, 58px, 22px, 17px);
|
||||
downIconPos: point(16px, 15px);
|
||||
|
||||
width: -69px;
|
||||
}
|
||||
medviewDelete: iconedButton(medviewForward) {
|
||||
icon: sprite(340px, 58px, 15px, 19px);
|
||||
iconPos: point(16px, 14px);
|
||||
downIcon: sprite(340px, 58px, 15px, 19px);
|
||||
downIconPos: point(16px, 14px);
|
||||
}
|
||||
medviewClose: iconedButton(medviewOverview) {
|
||||
icon: sprite(340px, 0px, 56px, 56px);
|
||||
iconPos: point(0px, 0px);
|
||||
downIcon: sprite(340px, 0px, 56px, 56px);
|
||||
downIconPos: point(0px, 0px);
|
||||
|
||||
opacity: 0.6;
|
||||
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
medviewBottomBar: 87px;
|
||||
medviewBG: #272727D9;
|
||||
medviewBottomBG: #272727;
|
||||
medviewNavOpacity: 0.6;
|
||||
medviewCloseOpacity: 0.6;
|
||||
medviewNavBGOpacity: 0.4;
|
||||
medviewNavOverOpacity: 1;
|
||||
medviewCloseOverOpacity: 1;
|
||||
medviewNameColor: black;
|
||||
medviewDateColor: #999;
|
||||
medviewSaveAs: iconedButton(medviewOverview) {
|
||||
bgColor: #38abe6;
|
||||
overBgColor: #299fdc;
|
||||
|
||||
opacity: 1;
|
||||
|
||||
icon: sprite(361px, 129px, 12px, 19px);
|
||||
iconPos: point(18px, 15px);
|
||||
downIcon: sprite(361px, 129px, 12px, 19px);
|
||||
downIconPos: point(18px, 15px);
|
||||
|
||||
width: -62px;
|
||||
height: 47px;
|
||||
|
||||
textPos: point(44px, 13px);
|
||||
downTextPos: point(44px, 14px);
|
||||
}
|
||||
medviewSaveAsDisabledOpacity: 0.8;
|
||||
medviewPolaroid: margins(17px, 18px, 17px, 72px);
|
||||
medviewPolaroidMin: size(480px, 360px);
|
||||
medviewDocumentSprite: sprite(341px, 150px, 20px, 22px);
|
||||
medviewDocumentSpritePos: point(16px, 13px);
|
||||
medviewPhotoSprite: sprite(363px, 150px, 23px, 20px);
|
||||
medviewPhotoSpritePos: point(14px, 14px);
|
||||
medviewTransparentBrush: sprite(148px, 197px, 8px, 8px);
|
||||
mvTransparentBrush: sprite(113px, 128px, 8px, 8px);
|
||||
|
||||
overviewPhotoSkip: 10px;
|
||||
overviewPhotoMinSize: 100px;
|
||||
@@ -1704,6 +1810,16 @@ photoLoaderDuration1: 150; // ms fade in
|
||||
photoLoaderDuration2: 150; // ms fade out
|
||||
photoLoaderAlphaMin: 0.1; // not less than that
|
||||
|
||||
radialSize: size(50px, 50px);
|
||||
radialLine: 2px;
|
||||
radialDuration: 350;
|
||||
radialPeriod: 3000;
|
||||
radialBgOpacity: 0.4;
|
||||
radialDownload: sprite(346px, 0px, 50px, 50px);
|
||||
radialDownloadOpacity: 0.8;
|
||||
radialCancel: sprite(378px, 50px, 18px, 18px);
|
||||
radialCancelOpacity: 1.0;
|
||||
|
||||
overviewLoader: size(34px, 14px);
|
||||
overviewLoaderPoint: size(4px, 4px);
|
||||
overviewLoaderSkip: 4px;
|
||||
|
||||
@@ -20,7 +20,7 @@ textStyle {
|
||||
lnkOverFlags: font;
|
||||
lnkColor: color;
|
||||
lnkDownColor: color;
|
||||
selectBG: color;
|
||||
selectBg: color;
|
||||
selectOverlay: color;
|
||||
lineHeight: number;
|
||||
}
|
||||
@@ -173,7 +173,8 @@ flatScroll {
|
||||
width: number;
|
||||
minHeight: number;
|
||||
deltax: number;
|
||||
deltay: number;
|
||||
deltat: number;
|
||||
deltab: number;
|
||||
|
||||
topsh: number;
|
||||
bottomsh: number;
|
||||
@@ -246,3 +247,15 @@ switcher {
|
||||
|
||||
duration: number;
|
||||
}
|
||||
|
||||
dropdown {
|
||||
border: number;
|
||||
borderColor: color;
|
||||
|
||||
padding: margins;
|
||||
shadow: sprite;
|
||||
shadowShift: number;
|
||||
|
||||
duration: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
@@ -448,6 +448,7 @@ static const char *variantNames[] = { "dbisOne", "dbisOneAndQuarter", "dbisOneAn
|
||||
static const char *variantPostfixes[] = { "", "_125x", "_150x", "_200x" };
|
||||
QPixmap *spriteMax = 0;
|
||||
QImage *variantSprites = 0;
|
||||
int *spriteWidths = 0;
|
||||
QImage *variantGrids = 0;
|
||||
|
||||
void readStyleGenToken(const char *&from, const char *end, StyleGenTokenType &tokenType, string &token) {
|
||||
@@ -1353,17 +1354,22 @@ bool genStyles(const QString &classes_in, const QString &classes_out, const QStr
|
||||
}
|
||||
|
||||
QImage sprites[variantsCount];
|
||||
int widths[variantsCount] = { 0 };
|
||||
variantSprites = sprites;
|
||||
spriteWidths = widths;
|
||||
|
||||
QString sprite0(path_to_sprites + "sprite" + QString(variantPostfixes[0]) + ".png"), spriteLast(path_to_sprites + "sprite" + QString(variantPostfixes[variantsCount - 1]) + ".png");
|
||||
variantSprites[0] = QImage(sprite0);
|
||||
spriteWidths[0] = variantSprites[0].width();
|
||||
for (int i = 1; i < variantsCount - 1; ++i) {
|
||||
variantSprites[i] = QImage(adjustPx(variants[i], variantSprites[0].width(), true), adjustPx(variants[i], variantSprites[0].height(), true), QImage::Format_ARGB32_Premultiplied);
|
||||
spriteWidths[i] = variantSprites[i].width();
|
||||
QPainter p(&variantSprites[i]);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.fillRect(0, 0, variantSprites[i].width(), variantSprites[i].height(), Qt::transparent);
|
||||
}
|
||||
variantSprites[variantsCount - 1] = QImage(spriteLast);
|
||||
spriteWidths[variantsCount - 1] = variantSprites[variantsCount - 1].width();
|
||||
|
||||
QPixmap spriteMaxPix = QPixmap::fromImage(variantSprites[variantsCount - 1], Qt::ColorOnly);
|
||||
spriteMax = &spriteMaxPix;
|
||||
@@ -1567,11 +1573,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org\n\
|
||||
tcpp << "\nnamespace style {\n\n";
|
||||
tcpp << "\tFontFamilies _fontFamilies;\n";
|
||||
tcpp << "\tFontDatas _fontsMap;\n";
|
||||
tcpp << "\tColorDatas _colorsMap;\n\n";
|
||||
tcpp << "\tColorDatas _colorsMap;\n";
|
||||
tcpp << "int _spriteWidth = " << spriteWidths[0] << ";\n\n";
|
||||
tcpp << "\tvoid startManager() {\n";
|
||||
|
||||
tcpp << "\n\t\tif (cRetina()) {\n";
|
||||
tcpp << "\t\t\tcSetRealScale(dbisOne);\n\n";
|
||||
tcpp << "\t\t\tcSetRealScale(dbisOne);\n";
|
||||
tcpp << "\t\t\t_spriteWidth = " << spriteWidths[variantsCount - 1] << ";\n\n";
|
||||
for (int i = 0, l = scalars.size(); i < l; ++i) {
|
||||
Scalar &sc(scalars[i]);
|
||||
if (sc.second.first == scSprite || sc.first == "spriteFile" || sc.first == "emojisFile" || sc.first == "emojiImgSize") {
|
||||
@@ -1594,6 +1602,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org\n\
|
||||
const char *varName = variantNames[i];
|
||||
|
||||
tcpp << "\t\tcase " << varName << ":\n";
|
||||
tcpp << "\t\t\t_spriteWidth = " << spriteWidths[i] << ";\n\n";
|
||||
|
||||
typedef QMap<string, int> FontFamilies;
|
||||
FontFamilies fontFamilies;
|
||||
|
||||
@@ -96,12 +96,11 @@ void writeLog(const wstring &msg) {
|
||||
}
|
||||
}
|
||||
|
||||
void delFolder() {
|
||||
wstring delPath = L"tupdates\\ready", delFolder = L"tupdates";
|
||||
void fullClearPath(const wstring &dir) {
|
||||
WCHAR path[4096];
|
||||
memcpy(path, delPath.c_str(), (delPath.size() + 1) * sizeof(WCHAR));
|
||||
path[delPath.size() + 1] = 0;
|
||||
writeLog(L"Fully clearing path '" + delPath + L"'..");
|
||||
memcpy(path, dir.c_str(), (dir.size() + 1) * sizeof(WCHAR));
|
||||
path[dir.size() + 1] = 0;
|
||||
writeLog(L"Fully clearing path '" + dir + L"'..");
|
||||
SHFILEOPSTRUCT file_op = {
|
||||
NULL,
|
||||
FO_DELETE,
|
||||
@@ -116,13 +115,46 @@ void delFolder() {
|
||||
};
|
||||
int res = SHFileOperation(&file_op);
|
||||
if (res) writeLog(L"Error: failed to clear path! :(");
|
||||
}
|
||||
|
||||
void delFolder() {
|
||||
wstring delPathOld = L"tupdates\\ready", delPath = L"tupdates\\temp", delFolder = L"tupdates";
|
||||
fullClearPath(delPathOld);
|
||||
fullClearPath(delPath);
|
||||
RemoveDirectory(delFolder.c_str());
|
||||
}
|
||||
|
||||
DWORD versionNum = 0, versionLen = 0, readLen = 0;
|
||||
WCHAR versionStr[32] = { 0 };
|
||||
|
||||
bool update() {
|
||||
writeLog(L"Update started..");
|
||||
|
||||
wstring updDir = L"tupdates\\ready";
|
||||
wstring updDir = L"tupdates\\temp", readyFilePath = L"tupdates\\temp\\ready", tdataDir = L"tupdates\\temp\\tdata";
|
||||
{
|
||||
HANDLE readyFile = CreateFile(readyFilePath.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (readyFile != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(readyFile);
|
||||
} else {
|
||||
updDir = L"tupdates\\ready"; // old
|
||||
tdataDir = L"tupdates\\ready\\tdata";
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE versionFile = CreateFile((tdataDir + L"\\version").c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (versionFile != INVALID_HANDLE_VALUE) {
|
||||
if (ReadFile(versionFile, &versionNum, sizeof(DWORD), &readLen, NULL) != TRUE || readLen != sizeof(DWORD)) {
|
||||
versionNum = 0;
|
||||
} else if (ReadFile(versionFile, &versionLen, sizeof(DWORD), &readLen, NULL) != TRUE || readLen != sizeof(DWORD) || versionLen > 63) {
|
||||
versionNum = 0;
|
||||
} else if (ReadFile(versionFile, versionStr, versionLen, &readLen, NULL) != TRUE || readLen != versionLen) {
|
||||
versionNum = 0;
|
||||
}
|
||||
CloseHandle(versionFile);
|
||||
writeLog(L"Version file read.");
|
||||
} else {
|
||||
writeLog(L"Could not open version file to update registry :(");
|
||||
}
|
||||
|
||||
deque<wstring> dirs;
|
||||
dirs.push_back(updDir);
|
||||
@@ -154,22 +186,27 @@ bool update() {
|
||||
}
|
||||
|
||||
do {
|
||||
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
wstring fname = dir + L"\\" + findData.cFileName;
|
||||
if (fname.substr(0, tdataDir.size()) == tdataDir && (fname.size() <= tdataDir.size() || fname.at(tdataDir.size()) == '/')) {
|
||||
writeLog(L"Skipped 'tdata' path '" + fname + L"'");
|
||||
} else if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (findData.cFileName != wstring(L".") && findData.cFileName != wstring(L"..")) {
|
||||
dirs.push_back(dir + L"\\" + findData.cFileName);
|
||||
writeLog(L"Added dir '" + dir + L"\\" + findData.cFileName + L"' in update tree..");
|
||||
dirs.push_back(fname);
|
||||
writeLog(L"Added dir '" + fname + L"' in update tree..");
|
||||
}
|
||||
} else {
|
||||
wstring fname = dir + L"\\" + findData.cFileName;
|
||||
wstring tofname = updateTo + fname.substr(updDir.size() + 1);
|
||||
if (equal(tofname, exeName)) { // bad update - has Updater.exe - delete all dir
|
||||
writeLog(L"Error: bad update, has Updater.exe! '" + tofname + L"' equal '" + exeName + L"'");
|
||||
delFolder();
|
||||
return false;
|
||||
} else if (equal(fname, readyFilePath)) {
|
||||
writeLog(L"Skipped ready file '" + fname + L"'");
|
||||
} else {
|
||||
from.push_back(fname);
|
||||
to.push_back(tofname);
|
||||
writeLog(L"Added file '" + fname + L"' to be copied to '" + tofname + L"'");
|
||||
}
|
||||
from.push_back(fname);
|
||||
to.push_back(tofname);
|
||||
writeLog(L"Added file '" + fname + L"' to be copied to '" + tofname + L"'");
|
||||
}
|
||||
} while (FindNextFile(findHandle, &findData));
|
||||
DWORD errorCode = GetLastError();
|
||||
@@ -230,73 +267,57 @@ bool update() {
|
||||
}
|
||||
|
||||
void updateRegistry() {
|
||||
HANDLE versionFile = CreateFile((updateTo + L"tdata\\version").c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (versionFile != INVALID_HANDLE_VALUE) {
|
||||
if (versionNum) {
|
||||
writeLog(L"Updating registry..");
|
||||
DWORD versionNum = 0, versionLen = 0, readLen = 0;
|
||||
WCHAR versionStr[32];
|
||||
if (ReadFile(versionFile, &versionNum, sizeof(DWORD), &readLen, NULL) != TRUE || readLen != sizeof(DWORD)) {
|
||||
versionNum = 0;
|
||||
} else if (ReadFile(versionFile, &versionLen, sizeof(DWORD), &readLen, NULL) != TRUE || readLen != sizeof(DWORD) || versionLen > 63) {
|
||||
versionNum = 0;
|
||||
} else if (ReadFile(versionFile, versionStr, versionLen, &readLen, NULL) != TRUE || readLen != versionLen) {
|
||||
versionNum = 0;
|
||||
}
|
||||
CloseHandle(versionFile);
|
||||
writeLog(L"Version file read.");
|
||||
if (versionNum) {
|
||||
versionStr[versionLen / 2] = 0;
|
||||
HKEY rkey;
|
||||
LSTATUS status = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{53F49750-6209-4FBF-9CA8-7A333C87D1ED}_is1", 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &rkey);
|
||||
if (status == ERROR_SUCCESS) {
|
||||
writeLog(L"Checking registry install location..");
|
||||
static const int bufSize = 4096;
|
||||
DWORD locationType, locationSize = bufSize * 2;
|
||||
WCHAR locationStr[bufSize], exp[bufSize];
|
||||
if (RegQueryValueEx(rkey, L"InstallLocation", 0, &locationType, (BYTE*)locationStr, &locationSize) == ERROR_SUCCESS) {
|
||||
locationSize /= 2;
|
||||
if (locationStr[locationSize - 1]) {
|
||||
locationStr[locationSize++] = 0;
|
||||
versionStr[versionLen / 2] = 0;
|
||||
HKEY rkey;
|
||||
LSTATUS status = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{53F49750-6209-4FBF-9CA8-7A333C87D1ED}_is1", 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &rkey);
|
||||
if (status == ERROR_SUCCESS) {
|
||||
writeLog(L"Checking registry install location..");
|
||||
static const int bufSize = 4096;
|
||||
DWORD locationType, locationSize = bufSize * 2;
|
||||
WCHAR locationStr[bufSize], exp[bufSize];
|
||||
if (RegQueryValueEx(rkey, L"InstallLocation", 0, &locationType, (BYTE*)locationStr, &locationSize) == ERROR_SUCCESS) {
|
||||
locationSize /= 2;
|
||||
if (locationStr[locationSize - 1]) {
|
||||
locationStr[locationSize++] = 0;
|
||||
}
|
||||
if (locationType == REG_EXPAND_SZ) {
|
||||
DWORD copy = ExpandEnvironmentStrings(locationStr, exp, bufSize);
|
||||
if (copy <= bufSize) {
|
||||
memcpy(locationStr, exp, copy * sizeof(WCHAR));
|
||||
}
|
||||
if (locationType == REG_EXPAND_SZ) {
|
||||
DWORD copy = ExpandEnvironmentStrings(locationStr, exp, bufSize);
|
||||
if (copy <= bufSize) {
|
||||
memcpy(locationStr, exp, copy * sizeof(WCHAR));
|
||||
}
|
||||
}
|
||||
if (locationType == REG_EXPAND_SZ || locationType == REG_SZ) {
|
||||
if (PathCanonicalize(exp, locationStr) == TRUE) {
|
||||
memcpy(locationStr, exp, bufSize * sizeof(WCHAR));
|
||||
if (GetFullPathName(L".", bufSize, exp, 0) < bufSize) {
|
||||
wstring installpath = locationStr, mypath = exp;
|
||||
if (installpath == mypath + L"\\" || true) { // always update reg info, if we found it
|
||||
WCHAR nameStr[bufSize], dateStr[bufSize], publisherStr[bufSize], icongroupStr[bufSize];
|
||||
SYSTEMTIME stLocalTime;
|
||||
GetLocalTime(&stLocalTime);
|
||||
RegSetValueEx(rkey, L"DisplayVersion", 0, REG_SZ, (BYTE*)versionStr, ((versionLen / 2) + 1) * sizeof(WCHAR));
|
||||
wsprintf(nameStr, L"Telegram Desktop version %s", versionStr);
|
||||
RegSetValueEx(rkey, L"DisplayName", 0, REG_SZ, (BYTE*)nameStr, (wcslen(nameStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(publisherStr, L"Telegram Messenger LLP");
|
||||
RegSetValueEx(rkey, L"Publisher", 0, REG_SZ, (BYTE*)publisherStr, (wcslen(publisherStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(icongroupStr, L"Telegram Desktop");
|
||||
RegSetValueEx(rkey, L"Inno Setup: Icon Group", 0, REG_SZ, (BYTE*)icongroupStr, (wcslen(icongroupStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(dateStr, L"%04d%02d%02d", stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay);
|
||||
RegSetValueEx(rkey, L"InstallDate", 0, REG_SZ, (BYTE*)dateStr, (wcslen(dateStr) + 1) * sizeof(WCHAR));
|
||||
}
|
||||
if (locationType == REG_EXPAND_SZ || locationType == REG_SZ) {
|
||||
if (PathCanonicalize(exp, locationStr) == TRUE) {
|
||||
memcpy(locationStr, exp, bufSize * sizeof(WCHAR));
|
||||
if (GetFullPathName(L".", bufSize, exp, 0) < bufSize) {
|
||||
wstring installpath = locationStr, mypath = exp;
|
||||
if (installpath == mypath + L"\\" || true) { // always update reg info, if we found it
|
||||
WCHAR nameStr[bufSize], dateStr[bufSize], publisherStr[bufSize], icongroupStr[bufSize];
|
||||
SYSTEMTIME stLocalTime;
|
||||
GetLocalTime(&stLocalTime);
|
||||
RegSetValueEx(rkey, L"DisplayVersion", 0, REG_SZ, (BYTE*)versionStr, ((versionLen / 2) + 1) * sizeof(WCHAR));
|
||||
wsprintf(nameStr, L"Telegram Desktop version %s", versionStr);
|
||||
RegSetValueEx(rkey, L"DisplayName", 0, REG_SZ, (BYTE*)nameStr, (wcslen(nameStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(publisherStr, L"Telegram Messenger LLP");
|
||||
RegSetValueEx(rkey, L"Publisher", 0, REG_SZ, (BYTE*)publisherStr, (wcslen(publisherStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(icongroupStr, L"Telegram Desktop");
|
||||
RegSetValueEx(rkey, L"Inno Setup: Icon Group", 0, REG_SZ, (BYTE*)icongroupStr, (wcslen(icongroupStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(dateStr, L"%04d%02d%02d", stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay);
|
||||
RegSetValueEx(rkey, L"InstallDate", 0, REG_SZ, (BYTE*)dateStr, (wcslen(dateStr) + 1) * sizeof(WCHAR));
|
||||
|
||||
WCHAR *appURL = L"https://desktop.telegram.org";
|
||||
RegSetValueEx(rkey, L"HelpLink", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"URLInfoAbout", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"URLUpdateInfo", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
}
|
||||
WCHAR *appURL = L"https://desktop.telegram.org";
|
||||
RegSetValueEx(rkey, L"HelpLink", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"URLInfoAbout", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"URLUpdateInfo", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RegCloseKey(rkey);
|
||||
}
|
||||
RegCloseKey(rkey);
|
||||
}
|
||||
} else {
|
||||
writeLog(L"Could not open version file to update registry :(");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,7 +378,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParama
|
||||
updateRegistry();
|
||||
}
|
||||
if (writeprotected) { // if we can't clear all tupdates\ready (Updater.exe is there) - clear only version
|
||||
if (DeleteFile(L"tupdates\\ready\\tdata\\version")) {
|
||||
if (DeleteFile(L"tupdates\\temp\\tdata\\version") || DeleteFile(L"tupdates\\ready\\tdata\\version")) {
|
||||
writeLog(L"Version file deleted!");
|
||||
} else {
|
||||
writeLog(L"Error: could not delete version file");
|
||||
@@ -386,7 +407,6 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParama
|
||||
|
||||
HRESULT hres = CoInitialize(0);
|
||||
if (SUCCEEDED(hres)) {
|
||||
wstring lnk = L"tupdates\\ready\\temp.lnk";
|
||||
IShellLink* psl;
|
||||
HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
|
||||
if (SUCCEEDED(hres)) {
|
||||
@@ -401,7 +421,12 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParama
|
||||
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
|
||||
|
||||
if (SUCCEEDED(hres)) {
|
||||
wstring lnk = L"tupdates\\temp\\temp.lnk";
|
||||
hres = ppf->Save(lnk.c_str(), TRUE);
|
||||
if (!SUCCEEDED(hres)) {
|
||||
lnk = L"tupdates\\ready\\temp.lnk"; // old
|
||||
hres = ppf->Save(lnk.c_str(), TRUE);
|
||||
}
|
||||
ppf->Release();
|
||||
|
||||
if (SUCCEEDED(hres)) {
|
||||
|
||||
@@ -49,12 +49,13 @@ bool do_mkdir(const char *path) { // from http://stackoverflow.com/questions/675
|
||||
}
|
||||
|
||||
bool _debug = false;
|
||||
string exeName, exeDir, workDir;
|
||||
|
||||
FILE *_logFile = 0;
|
||||
void openLog() {
|
||||
if (!_debug || _logFile) return;
|
||||
|
||||
if (!do_mkdir("DebugLogs")) {
|
||||
if (!do_mkdir((workDir + "DebugLogs").c_str())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,7 +66,7 @@ void openLog() {
|
||||
|
||||
static const int maxFileLen = 65536;
|
||||
char logName[maxFileLen];
|
||||
sprintf(logName, "DebugLogs/%04d%02d%02d_%02d%02d%02d_upd.txt",
|
||||
sprintf(logName, "%sDebugLogs/%04d%02d%02d_%02d%02d%02d_upd.txt", workDir.c_str(),
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
|
||||
_logFile = fopen(logName, "w");
|
||||
}
|
||||
@@ -136,7 +137,7 @@ bool remove_directory(const string &path) { // from http://stackoverflow.com/que
|
||||
|
||||
if (!d) {
|
||||
writeLog("Could not open dir '%s'", path.c_str());
|
||||
return false;
|
||||
return (errno == ENOENT);
|
||||
}
|
||||
|
||||
while (struct dirent *p = readdir(d)) {
|
||||
@@ -192,8 +193,6 @@ bool mkpath(const char *path) {
|
||||
return do_mkdir(path);
|
||||
}
|
||||
|
||||
string exeName, exeDir, workDir;
|
||||
|
||||
bool equal(string a, string b) {
|
||||
std::transform(a.begin(), a.end(), a.begin(), ::tolower);
|
||||
std::transform(b.begin(), b.end(), b.begin(), ::tolower);
|
||||
@@ -201,18 +200,33 @@ bool equal(string a, string b) {
|
||||
}
|
||||
|
||||
void delFolder() {
|
||||
string delPath = workDir + "tupdates/ready", delFolder = workDir + "tupdates";
|
||||
writeLog("Fully clearing path '%s'..", delPath.c_str());
|
||||
if (!remove_directory(delPath)) {
|
||||
writeLog("Error: failed to clear path! :(");
|
||||
string delPathOld = workDir + "tupdates/ready", delPath = workDir + "tupdates/temp", delFolder = workDir + "tupdates";
|
||||
writeLog("Fully clearing old path '%s'..", delPathOld.c_str());
|
||||
if (!remove_directory(delPathOld)) {
|
||||
writeLog("Failed to clear old path! :( New path was used?..");
|
||||
}
|
||||
rmdir(delFolder.c_str());
|
||||
writeLog("Fully clearing path '%s'..", delPath.c_str());
|
||||
if (!remove_directory(delPath)) {
|
||||
writeLog("Error: failed to clear path! :(");
|
||||
}
|
||||
rmdir(delFolder.c_str());
|
||||
}
|
||||
|
||||
bool update() {
|
||||
writeLog("Update started..");
|
||||
|
||||
string updDir = workDir + "tupdates/ready";
|
||||
string updDir = workDir + "tupdates/temp", readyFilePath = workDir + "tupdates/temp/ready", tdataDir = workDir + "tupdates/temp/tdata";
|
||||
{
|
||||
FILE *readyFile = fopen(readyFilePath.c_str(), "rb");
|
||||
if (readyFile) {
|
||||
fclose(readyFile);
|
||||
writeLog("Ready file found! Using new path '%s'..", updDir.c_str());
|
||||
} else {
|
||||
updDir = workDir + "tupdates/ready"; // old
|
||||
tdataDir = workDir + "tupdates/ready/tdata";
|
||||
writeLog("Ready file not found! Using old path '%s'..", updDir.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
deque<string> dirs;
|
||||
dirs.push_back(updDir);
|
||||
@@ -242,7 +256,9 @@ bool update() {
|
||||
|
||||
string fname = dir + '/' + p->d_name;
|
||||
struct stat statbuf;
|
||||
if (!stat(fname.c_str(), &statbuf)) {
|
||||
if (fname.substr(0, tdataDir.size()) == tdataDir && (fname.size() <= tdataDir.size() || fname.at(tdataDir.size()) == '/')) {
|
||||
writeLog("Skipping 'tdata' path '%s'", fname.c_str());
|
||||
} else if (!stat(fname.c_str(), &statbuf)) {
|
||||
if (S_ISDIR(statbuf.st_mode)) {
|
||||
dirs.push_back(fname);
|
||||
writeLog("Added dir '%s' in update tree..", fname.c_str());
|
||||
@@ -253,9 +269,13 @@ bool update() {
|
||||
delFolder();
|
||||
return false;
|
||||
}
|
||||
from.push_back(fname);
|
||||
to.push_back(tofname);
|
||||
writeLog("Added file '%s' to be copied to '%s'", fname.c_str(), tofname.c_str());
|
||||
if (fname == readyFilePath) {
|
||||
writeLog("Skipped ready file '%s'", fname.c_str());
|
||||
} else {
|
||||
from.push_back(fname);
|
||||
to.push_back(tofname);
|
||||
writeLog("Added file '%s' to be copied to '%s'", fname.c_str(), tofname.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writeLog("Could not get stat() for file %s", fname.c_str());
|
||||
@@ -299,10 +319,6 @@ bool update() {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
openLog();
|
||||
|
||||
writeLog("Updater started..");
|
||||
|
||||
bool needupdate = true, autostart = false, debug = false, tosettings = false, startintray = false, testmode = false;
|
||||
|
||||
char *key = 0;
|
||||
@@ -313,7 +329,6 @@ int main(int argc, char *argv[]) {
|
||||
autostart = true;
|
||||
} else if (equal(argv[i], "-debug")) {
|
||||
debug = _debug = true;
|
||||
openLog();
|
||||
} else if (equal(argv[i], "-startintray")) {
|
||||
startintray = true;
|
||||
} else if (equal(argv[i], "-testmode")) {
|
||||
@@ -326,6 +341,12 @@ int main(int argc, char *argv[]) {
|
||||
workDir = argv[i];
|
||||
}
|
||||
}
|
||||
openLog();
|
||||
|
||||
writeLog("Updater started..");
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
writeLog("Argument: '%s'", argv[i]);
|
||||
}
|
||||
if (needupdate) writeLog("Need to update!");
|
||||
if (autostart) writeLog("From autostart!");
|
||||
|
||||
@@ -336,7 +357,7 @@ int main(int argc, char *argv[]) {
|
||||
exeDir = exeName.substr(0, exeName.size() - 7);
|
||||
writeLog("Exe dir is: %s", exeDir.c_str());
|
||||
if (needupdate) {
|
||||
if (workDir.empty()) { // old app launched
|
||||
if (workDir.empty()) { // old app launched, update prepared in tupdates/ready (not in tupdates/temp)
|
||||
writeLog("No workdir, trying to figure it out");
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
if (pw && pw->pw_dir && strlen(pw->pw_dir)) {
|
||||
|
||||
@@ -35,7 +35,7 @@ void openLog() {
|
||||
return;
|
||||
}
|
||||
|
||||
NSDateFormatter *fmt = [[NSDateFormatter alloc] initWithDateFormat:@"DebugLogs/%Y%m%d %H%M%S_upd.txt" allowNaturalLanguage:NO];
|
||||
NSDateFormatter *fmt = [[NSDateFormatter alloc] initWithDateFormat:@"DebugLogs/%Y%m%d_%H%M%S_upd.txt" allowNaturalLanguage:NO];
|
||||
NSString *logPath = [workDir stringByAppendingString:[fmt stringFromDate:[NSDate date]]];
|
||||
[[NSFileManager defaultManager] createFileAtPath:logPath contents:nil attributes:nil];
|
||||
_logFile = [NSFileHandle fileHandleForWritingAtPath:logPath];
|
||||
@@ -55,7 +55,14 @@ void writeLog(NSString *msg) {
|
||||
}
|
||||
|
||||
void delFolder() {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[workDir stringByAppendingString:@"tupdates/ready"] error:nil];
|
||||
writeLog([@"Fully clearing old path: " stringByAppendingString:[workDir stringByAppendingString:@"tupdates/ready"]]);
|
||||
if (![[NSFileManager defaultManager] removeItemAtPath:[workDir stringByAppendingString:@"tupdates/ready"] error:nil]) {
|
||||
writeLog(@"Failed to clear old path! :( New path was used?..");
|
||||
}
|
||||
writeLog([@"Fully clearing new path: " stringByAppendingString:[workDir stringByAppendingString:@"tupdates/temp"]]);
|
||||
if (![[NSFileManager defaultManager] removeItemAtPath:[workDir stringByAppendingString:@"tupdates/temp"] error:nil]) {
|
||||
writeLog(@"Error: failed to clear new path! :(");
|
||||
}
|
||||
rmdir([[workDir stringByAppendingString:@"tupdates"] fileSystemRepresentation]);
|
||||
}
|
||||
|
||||
@@ -132,16 +139,26 @@ int main(int argc, const char * argv[]) {
|
||||
}
|
||||
|
||||
if (update) {
|
||||
writeLog(@"Starting update files iteration!");
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *srcDir = [workDir stringByAppendingString:@"tupdates/ready/"];
|
||||
NSString *readyFilePath = [workDir stringByAppendingString:@"tupdates/temp/ready"];
|
||||
NSString *srcDir = [workDir stringByAppendingString:@"tupdates/temp/"], *srcEnum = [workDir stringByAppendingString:@"tupdates/temp"];
|
||||
if ([fileManager fileExistsAtPath:readyFilePath]) {
|
||||
writeLog([@"Ready file found! Using new path: " stringByAppendingString: srcEnum]);
|
||||
} else {
|
||||
srcDir = [workDir stringByAppendingString:@"tupdates/ready/"]; // old
|
||||
srcEnum = [workDir stringByAppendingString:@"tupdates/ready"];
|
||||
writeLog([@"Ready file not found! Using old path: " stringByAppendingString: srcEnum]);
|
||||
}
|
||||
|
||||
writeLog([@"Starting update files iteration, path: " stringByAppendingString: srcEnum]);
|
||||
|
||||
NSArray *keys = [NSArray arrayWithObject:NSURLIsDirectoryKey];
|
||||
NSDirectoryEnumerator *enumerator = [fileManager
|
||||
enumeratorAtURL:[NSURL fileURLWithPath:[workDir stringByAppendingString:@"tupdates/ready"]]
|
||||
enumeratorAtURL:[NSURL fileURLWithPath:srcEnum]
|
||||
includingPropertiesForKeys:keys
|
||||
options:0
|
||||
errorHandler:^(NSURL *url, NSError *error) {
|
||||
writeLog([[[@"Error in enumerating " stringByAppendingString:[url absoluteString]] stringByAppendingString: @" error is: "] stringByAppendingString: [error description]]);
|
||||
return NO;
|
||||
}];
|
||||
for (NSURL *url in enumerator) {
|
||||
@@ -162,18 +179,20 @@ int main(int argc, const char * argv[]) {
|
||||
NSString *dstPath = [appDirFull stringByAppendingString:[pathPart substringFromIndex:r.length]];
|
||||
NSError *error;
|
||||
NSNumber *isDirectory = nil;
|
||||
writeLog([[NSArray arrayWithObjects: @"Copying file ", srcPath, @" to ", dstPath, nil] componentsJoinedByString:@""]);
|
||||
if (![url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&error]) {
|
||||
writeLog([@"Failed to get IsDirectory for file " stringByAppendingString:[url path]]);
|
||||
delFolder();
|
||||
break;
|
||||
}
|
||||
if ([isDirectory boolValue]) {
|
||||
writeLog([[NSArray arrayWithObjects: @"Copying dir ", srcPath, @" to ", dstPath, nil] componentsJoinedByString:@""]);
|
||||
if (![fileManager createDirectoryAtPath:dstPath withIntermediateDirectories:YES attributes:nil error:nil]) {
|
||||
writeLog([@"Failed to force path for directory " stringByAppendingString:dstPath]);
|
||||
delFolder();
|
||||
break;
|
||||
}
|
||||
} else if ([srcPath isEqualToString:readyFilePath]) {
|
||||
writeLog([[NSArray arrayWithObjects: @"Skipping ready file ", srcPath, nil] componentsJoinedByString:@""]);
|
||||
} else if ([fileManager fileExistsAtPath:dstPath]) {
|
||||
if (![[NSData dataWithContentsOfFile:srcPath] writeToFile:dstPath atomically:YES]) {
|
||||
writeLog([@"Failed to edit file " stringByAppendingString:dstPath]);
|
||||
|
||||
@@ -34,7 +34,6 @@ ApiWrap::ApiWrap(QObject *parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
void ApiWrap::init() {
|
||||
App::initMedia();
|
||||
}
|
||||
|
||||
void ApiWrap::itemRemoved(HistoryItem *item) {
|
||||
@@ -85,7 +84,7 @@ void ApiWrap::requestReplyTo(HistoryReply *reply, MsgId to) {
|
||||
}
|
||||
|
||||
void ApiWrap::requestFullPeer(PeerData *peer) {
|
||||
if (_fullRequests.contains(peer)) return;
|
||||
if (!peer || _fullRequests.contains(peer)) return;
|
||||
mtpRequestId req;
|
||||
if (peer->chat) {
|
||||
req = MTP::send(MTPmessages_GetFullChat(MTP_int(App::chatFromPeer(peer->id))), rpcDone(&ApiWrap::gotChatFull, peer), rpcFail(&ApiWrap::gotPeerFailed, peer));
|
||||
@@ -116,18 +115,21 @@ void ApiWrap::clearWebPageRequests() {
|
||||
|
||||
void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) {
|
||||
const MTPDmessages_chatFull &d(result.c_messages_chatFull());
|
||||
const MTPDchatFull &f(d.vfull_chat.c_chatFull());
|
||||
App::feedUsers(d.vusers);
|
||||
App::feedChats(d.vchats);
|
||||
App::feedParticipants(d.vfull_chat.c_chatFull().vparticipants);
|
||||
PhotoData *photo = App::feedPhoto(d.vfull_chat.c_chatFull().vchat_photo);
|
||||
if (photo) {
|
||||
ChatData *chat = peer->asChat();
|
||||
if (chat) {
|
||||
App::feedParticipants(f.vparticipants);
|
||||
PhotoData *photo = App::feedPhoto(f.vchat_photo);
|
||||
ChatData *chat = peer->asChat();
|
||||
if (chat) {
|
||||
if (photo) {
|
||||
chat->photoId = photo->id;
|
||||
photo->chat = chat;
|
||||
}
|
||||
chat->invitationUrl = (f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString();
|
||||
}
|
||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vfull_chat.c_chatFull().vnotify_settings);
|
||||
|
||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
|
||||
|
||||
_fullRequests.remove(peer);
|
||||
emit fullPeerLoaded(peer);
|
||||
@@ -136,6 +138,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) {
|
||||
void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result) {
|
||||
const MTPDuserFull &d(result.c_userFull());
|
||||
App::feedUsers(MTP_vector<MTPUser>(1, d.vuser));
|
||||
App::feedPhoto(d.vprofile_photo);
|
||||
App::feedUserLink(MTP_int(App::userFromPeer(peer->id)), d.vlink.c_contacts_link().vmy_link, d.vlink.c_contacts_link().vforeign_link);
|
||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vnotify_settings);
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@ namespace {
|
||||
typedef QHash<PeerId, PeerData*> PeersData;
|
||||
PeersData peersData;
|
||||
|
||||
typedef QMap<PeerData*, bool> MutedPeers;
|
||||
MutedPeers mutedPeers;
|
||||
|
||||
typedef QHash<PhotoId, PhotoData*> PhotosData;
|
||||
PhotosData photosData;
|
||||
|
||||
@@ -70,9 +73,12 @@ namespace {
|
||||
|
||||
HistoryItem *hoveredItem = 0, *pressedItem = 0, *hoveredLinkItem = 0, *pressedLinkItem = 0, *contextItem = 0, *mousedItem = 0;
|
||||
|
||||
QPixmap *sprite = 0, *emojis = 0;
|
||||
QPixmap *sprite = 0, *emojis = 0, *emojisLarge = 0;
|
||||
|
||||
typedef QMap<uint32, QPixmap> EmojisMap;
|
||||
QPixmap *corners[RoundCornersCount][4] = { { 0 } };
|
||||
QImage *cornersMask[4] = { 0 };
|
||||
|
||||
typedef QMap<uint64, QPixmap> EmojisMap;
|
||||
EmojisMap mainEmojisMap;
|
||||
QMap<int32, EmojisMap> otherEmojisMap;
|
||||
|
||||
@@ -83,7 +89,8 @@ namespace {
|
||||
typedef QHash<PhotoData*, LastPhotosList::iterator> LastPhotosMap;
|
||||
LastPhotosMap lastPhotosMap;
|
||||
|
||||
style::color _msgServiceBG;
|
||||
style::color _msgServiceBg;
|
||||
style::color _msgServiceSelectBg;
|
||||
style::color _historyScrollBarColor;
|
||||
style::color _historyScrollBgColor;
|
||||
style::color _historyScrollBarOverColor;
|
||||
@@ -441,11 +448,12 @@ namespace App {
|
||||
return data;
|
||||
}
|
||||
|
||||
void feedChats(const MTPVector<MTPChat> &chats) {
|
||||
ChatData *feedChats(const MTPVector<MTPChat> &chats) {
|
||||
ChatData *data = 0;
|
||||
const QVector<MTPChat> &v(chats.c_vector().v);
|
||||
for (QVector<MTPChat>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
|
||||
const MTPchat &chat(*i);
|
||||
ChatData *data = 0;
|
||||
data = 0;
|
||||
QString title;
|
||||
switch (chat.type()) {
|
||||
case mtpc_chat: {
|
||||
@@ -458,7 +466,7 @@ namespace App {
|
||||
data->setPhoto(d.vphoto);
|
||||
data->date = d.vdate.v;
|
||||
data->count = d.vparticipants_count.v;
|
||||
data->left = false;
|
||||
data->left = d.vleft.v;
|
||||
data->forbidden = false;
|
||||
data->access = 0;
|
||||
if (data->version < d.vversion.v) {
|
||||
@@ -507,6 +515,7 @@ namespace App {
|
||||
|
||||
if (App::main()) App::main()->peerUpdated(data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void feedParticipants(const MTPChatParticipants &p) {
|
||||
@@ -628,7 +637,7 @@ namespace App {
|
||||
const MTPDphotoSize &d(size.c_photoSize());
|
||||
if (d.vlocation.type() == mtpc_fileLocation) {
|
||||
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
|
||||
return ImagePtr(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v, d.vsize.v);
|
||||
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v), d.vsize.v);
|
||||
}
|
||||
} break;
|
||||
case mtpc_photoCachedSize: {
|
||||
@@ -637,17 +646,43 @@ namespace App {
|
||||
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
|
||||
const string &s(d.vbytes.c_string().v);
|
||||
QByteArray bytes(s.data(), s.size());
|
||||
return ImagePtr(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v, bytes);
|
||||
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v), bytes);
|
||||
} else if (d.vlocation.type() == mtpc_fileLocationUnavailable) {
|
||||
const string &s(d.vbytes.c_string().v);
|
||||
QByteArray bytes(s.data(), s.size());
|
||||
return ImagePtr(d.vw.v, d.vh.v, 0, 0, 0, 0, bytes);
|
||||
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, 0, 0, 0, 0), bytes);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return ImagePtr();
|
||||
}
|
||||
|
||||
StorageImageLocation imageLocation(const MTPPhotoSize &size) {
|
||||
switch (size.type()) {
|
||||
case mtpc_photoSize: {
|
||||
const MTPDphotoSize &d(size.c_photoSize());
|
||||
if (d.vlocation.type() == mtpc_fileLocation) {
|
||||
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
|
||||
return StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v);
|
||||
}
|
||||
} break;
|
||||
case mtpc_photoCachedSize: {
|
||||
const MTPDphotoCachedSize &d(size.c_photoCachedSize());
|
||||
if (d.vlocation.type() == mtpc_fileLocation) {
|
||||
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
|
||||
const string &s(d.vbytes.c_string().v);
|
||||
QByteArray bytes(s.data(), s.size());
|
||||
return StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v);
|
||||
} else if (d.vlocation.type() == mtpc_fileLocationUnavailable) {
|
||||
const string &s(d.vbytes.c_string().v);
|
||||
QByteArray bytes(s.data(), s.size());
|
||||
return StorageImageLocation(d.vw.v, d.vh.v, 0, 0, 0, 0);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return StorageImageLocation();
|
||||
}
|
||||
|
||||
void feedWereRead(const QVector<MTPint> &msgsIds) {
|
||||
for (QVector<MTPint>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
|
||||
MsgsData::const_iterator j = msgsData.constFind(i->v);
|
||||
@@ -861,6 +896,18 @@ namespace App {
|
||||
return App::video(video.vid.v, convert, video.vaccess_hash.v, video.vuser_id.v, video.vdate.v, video.vduration.v, video.vw.v, video.vh.v, App::image(video.vthumb), video.vdc_id.v, video.vsize.v);
|
||||
}
|
||||
|
||||
AudioData *feedAudio(const MTPaudio &audio, AudioData *convert) {
|
||||
switch (audio.type()) {
|
||||
case mtpc_audio: {
|
||||
return feedAudio(audio.c_audio(), convert);
|
||||
} break;
|
||||
case mtpc_audioEmpty: {
|
||||
return App::audio(audio.c_audioEmpty().vid.v, convert);
|
||||
} break;
|
||||
}
|
||||
return App::audio(0);
|
||||
}
|
||||
|
||||
AudioData *feedAudio(const MTPDaudio &audio, AudioData *convert) {
|
||||
return App::audio(audio.vid.v, convert, audio.vaccess_hash.v, audio.vuser_id.v, audio.vdate.v, qs(audio.vmime_type), audio.vduration.v, audio.vdc_id.v, audio.vsize.v);
|
||||
}
|
||||
@@ -869,7 +916,7 @@ namespace App {
|
||||
switch (document.type()) {
|
||||
case mtpc_document: {
|
||||
const MTPDdocument &d(document.c_document());
|
||||
return App::document(d.vid.v, 0, d.vaccess_hash.v, d.vdate.v, d.vattributes.c_vector().v, qs(d.vmime_type), ImagePtr(thumb, "JPG"), d.vdc_id.v, d.vsize.v);
|
||||
return App::documentSet(d.vid.v, 0, d.vaccess_hash.v, d.vdate.v, d.vattributes.c_vector().v, qs(d.vmime_type), ImagePtr(thumb, "JPG"), d.vdc_id.v, d.vsize.v, StorageImageLocation());
|
||||
} break;
|
||||
case mtpc_documentEmpty: return App::document(document.c_documentEmpty().vid.v);
|
||||
}
|
||||
@@ -882,14 +929,14 @@ namespace App {
|
||||
return feedDocument(document.c_document(), convert);
|
||||
} break;
|
||||
case mtpc_documentEmpty: {
|
||||
return App::document(document.c_documentEmpty().vid.v, convert);
|
||||
return App::documentSet(document.c_documentEmpty().vid.v, convert, 0, 0, QVector<MTPDocumentAttribute>(), QString(), ImagePtr(), 0, 0, StorageImageLocation());
|
||||
} break;
|
||||
}
|
||||
return App::document(0);
|
||||
}
|
||||
|
||||
DocumentData *feedDocument(const MTPDdocument &document, DocumentData *convert) {
|
||||
return App::document(document.vid.v, convert, document.vaccess_hash.v, document.vdate.v, document.vattributes.c_vector().v, qs(document.vmime_type), App::image(document.vthumb), document.vdc_id.v, document.vsize.v);
|
||||
return App::documentSet(document.vid.v, convert, document.vaccess_hash.v, document.vdate.v, document.vattributes.c_vector().v, qs(document.vmime_type), App::image(document.vthumb), document.vdc_id.v, document.vsize.v, App::imageLocation(document.vthumb));
|
||||
}
|
||||
|
||||
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert) {
|
||||
@@ -903,7 +950,11 @@ namespace App {
|
||||
WebPageData *feedWebPage(const MTPWebPage &webpage) {
|
||||
switch (webpage.type()) {
|
||||
case mtpc_webPage: return App::feedWebPage(webpage.c_webPage());
|
||||
case mtpc_webPageEmpty: return App::webPage(webpage.c_webPageEmpty().vid.v);
|
||||
case mtpc_webPageEmpty: {
|
||||
WebPageData *page = App::webPage(webpage.c_webPageEmpty().vid.v);
|
||||
if (page->pendingTill > 0) page->pendingTill = -1; // failed
|
||||
return page;
|
||||
} break;
|
||||
case mtpc_webPagePending: return App::feedWebPage(webpage.c_webPagePending());
|
||||
}
|
||||
return 0;
|
||||
@@ -1123,7 +1174,15 @@ namespace App {
|
||||
return result;
|
||||
}
|
||||
|
||||
DocumentData *document(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) {
|
||||
DocumentData *document(const DocumentId &document) {
|
||||
DocumentsData::const_iterator i = documentsData.constFind(document);
|
||||
if (i == documentsData.cend()) {
|
||||
i = documentsData.insert(document, new DocumentData(document));
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation) {
|
||||
if (convert) {
|
||||
if (convert->id != document) {
|
||||
DocumentsData::iterator i = documentsData.find(convert->id);
|
||||
@@ -1141,12 +1200,28 @@ namespace App {
|
||||
convert->thumb = thumb;
|
||||
convert->dc = dc;
|
||||
convert->size = size;
|
||||
} else if (convert->thumb->isNull() && !thumb->isNull()) {
|
||||
convert->thumb = thumb;
|
||||
} else {
|
||||
if (!thumb->isNull() && (convert->thumb->isNull() || convert->thumb->width() < thumb->width() || convert->thumb->height() < thumb->height())) {
|
||||
convert->thumb = thumb;
|
||||
}
|
||||
if (convert->sticker && !attributes.isEmpty() && (convert->sticker->alt.isEmpty() || convert->sticker->set.type() == mtpc_inputStickerSetEmpty)) {
|
||||
for (QVector<MTPDocumentAttribute>::const_iterator i = attributes.cbegin(), e = attributes.cend(); i != e; ++i) {
|
||||
if (i->type() == mtpc_documentAttributeSticker) {
|
||||
const MTPDdocumentAttributeSticker &d(i->c_documentAttributeSticker());
|
||||
if (d.valt.c_string().v.length() > 0) {
|
||||
convert->sticker->alt = qs(d.valt);
|
||||
convert->sticker->set = d.vstickerset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (convert->sticker && !convert->sticker->loc.dc && thumbLocation.dc) {
|
||||
convert->sticker->loc = thumbLocation;
|
||||
}
|
||||
|
||||
if (convert->location.check()) {
|
||||
Local::writeFileLocation(mediaKey(mtpc_inputDocumentFileLocation, convert->dc, convert->id), convert->location);
|
||||
Local::writeFileLocation(mediaKey(DocumentFileLocation, convert->dc, convert->id), convert->location);
|
||||
}
|
||||
}
|
||||
DocumentsData::const_iterator i = documentsData.constFind(document);
|
||||
@@ -1156,6 +1231,7 @@ namespace App {
|
||||
result = convert;
|
||||
} else {
|
||||
result = new DocumentData(document, access, date, attributes, mime, thumb, dc, size);
|
||||
if (result->sticker) result->sticker->loc = thumbLocation;
|
||||
}
|
||||
documentsData.insert(document, result);
|
||||
} else {
|
||||
@@ -1170,19 +1246,23 @@ namespace App {
|
||||
result->dc = dc;
|
||||
result->size = size;
|
||||
} else {
|
||||
if (result->thumb->isNull() && !thumb->isNull()) {
|
||||
if (!thumb->isNull() && (result->thumb->isNull() || result->thumb->width() < thumb->width() || result->thumb->height() < thumb->height())) {
|
||||
result->thumb = thumb;
|
||||
}
|
||||
if (result->alt.isEmpty()) {
|
||||
if (result->sticker && !attributes.isEmpty() && (result->sticker->alt.isEmpty() || result->sticker->set.type() == mtpc_inputStickerSetEmpty)) {
|
||||
for (QVector<MTPDocumentAttribute>::const_iterator i = attributes.cbegin(), e = attributes.cend(); i != e; ++i) {
|
||||
if (i->type() == mtpc_documentAttributeSticker) {
|
||||
const MTPDdocumentAttributeSticker &d(i->c_documentAttributeSticker());
|
||||
if (d.valt.c_string().v.length() > 0) {
|
||||
result->alt = qs(d.valt);
|
||||
result->sticker->alt = qs(d.valt);
|
||||
result->sticker->set = d.vstickerset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result->sticker && !result->sticker->loc.dc && thumbLocation.dc) {
|
||||
result->sticker->loc = thumbLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1288,7 +1368,7 @@ namespace App {
|
||||
photoSizes.push_back(MTP_photoSize(MTP_string("a"), uphoto.vphoto_small, MTP_int(160), MTP_int(160), MTP_int(0)));
|
||||
photoSizes.push_back(MTP_photoSize(MTP_string("c"), uphoto.vphoto_big, MTP_int(640), MTP_int(640), MTP_int(0)));
|
||||
|
||||
return MTP_photo(uphoto.vphoto_id, MTP_long(0), userId, date, MTP_string(""), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
|
||||
return MTP_photo(uphoto.vphoto_id, MTP_long(0), userId, date, MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
|
||||
}
|
||||
return MTP_photoEmpty(MTP_long(0));
|
||||
}
|
||||
@@ -1431,6 +1511,7 @@ namespace App {
|
||||
void historyClearItems() {
|
||||
historyClearMsgs();
|
||||
randomData.clear();
|
||||
mutedPeers.clear();
|
||||
for (PeersData::const_iterator i = peersData.cbegin(), e = peersData.cend(); i != e; ++i) {
|
||||
delete *i;
|
||||
}
|
||||
@@ -1458,8 +1539,10 @@ namespace App {
|
||||
if (api()) api()->clearWebPageRequests();
|
||||
cSetRecentStickers(RecentStickerPack());
|
||||
cSetStickersHash(QByteArray());
|
||||
cSetStickers(AllStickers());
|
||||
cSetEmojiStickers(EmojiStickersMap());
|
||||
cSetStickerSets(StickerSets());
|
||||
cSetStickerSetsOrder(StickerSetsOrder());
|
||||
cSetLastStickersUpdate(0);
|
||||
::videoItems.clear();
|
||||
::audioItems.clear();
|
||||
::documentItems.clear();
|
||||
@@ -1510,19 +1593,81 @@ namespace App {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void prepareCorners(RoundCorners index, int32 radius, const style::color &color, const style::color *shadow = 0, QImage *cors = 0) {
|
||||
int32 r = radius * cIntRetinaFactor(), s = st::msgShadow * cIntRetinaFactor();
|
||||
QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4];
|
||||
{
|
||||
QPainter p(&rect);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.fillRect(QRect(0, 0, rect.width(), rect.height()), st::transparent->b);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
p.setPen(Qt::NoPen);
|
||||
if (shadow) {
|
||||
p.setBrush((*shadow)->b);
|
||||
p.drawRoundedRect(0, s, r * 3, r * 3, r, r);
|
||||
}
|
||||
p.setBrush(color->b);
|
||||
p.drawRoundedRect(0, 0, r * 3, r * 3, r, r);
|
||||
}
|
||||
if (!cors) cors = localCors;
|
||||
cors[0] = rect.copy(0, 0, r, r);
|
||||
cors[1] = rect.copy(r * 2, 0, r, r);
|
||||
cors[2] = rect.copy(0, r * 2, r, r + (shadow ? s : 0));
|
||||
cors[3] = rect.copy(r * 2, r * 2, r, r + (shadow ? s : 0));
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
::corners[index][i] = new QPixmap(QPixmap::fromImage(cors[i], Qt::ColorOnly));
|
||||
::corners[index][i]->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
}
|
||||
|
||||
void initMedia() {
|
||||
deinitMedia(false);
|
||||
audioInit();
|
||||
|
||||
if (!::sprite) {
|
||||
::sprite = new QPixmap(st::spriteFile);
|
||||
if (rtl()) {
|
||||
::sprite = new QPixmap(QPixmap::fromImage(QImage(st::spriteFile).mirrored(true, false)));
|
||||
} else {
|
||||
::sprite = new QPixmap(st::spriteFile);
|
||||
}
|
||||
if (cRetina()) ::sprite->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
emojiInit();
|
||||
if (!::emojis) {
|
||||
::emojis = new QPixmap(st::emojisFile);
|
||||
::emojis = new QPixmap(QLatin1String(EName));
|
||||
if (cRetina()) ::emojis->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
initEmoji();
|
||||
if (!::emojisLarge) {
|
||||
::emojisLarge = new QPixmap(QLatin1String(EmojiNames[EIndex + 1]));
|
||||
if (cRetina()) ::emojisLarge->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
|
||||
QImage mask[4];
|
||||
prepareCorners(MaskCorners, st::msgRadius, st::white, 0, mask);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
::cornersMask[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied));
|
||||
::cornersMask[i]->setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
prepareCorners(BlackCorners, st::msgRadius, st::black);
|
||||
prepareCorners(ServiceCorners, st::msgRadius, st::msgServiceBg);
|
||||
prepareCorners(ServiceSelectedCorners, st::msgRadius, st::msgServiceSelectBg);
|
||||
prepareCorners(SelectedOverlayCorners, st::msgRadius, st::msgSelectOverlay);
|
||||
prepareCorners(DateCorners, st::msgRadius, st::msgDateImgBg);
|
||||
prepareCorners(DateSelectedCorners, st::msgRadius, st::msgDateImgSelectBg);
|
||||
prepareCorners(InShadowCorners, st::msgRadius, st::msgInShadow);
|
||||
prepareCorners(InSelectedShadowCorners, st::msgRadius, st::msgInSelectShadow);
|
||||
prepareCorners(ForwardCorners, st::msgRadius, st::forwardBg);
|
||||
prepareCorners(MediaviewSaveCorners, st::msgRadius, st::emojiPanHover);
|
||||
prepareCorners(EmojiHoverCorners, st::msgRadius, st::emojiPanHover);
|
||||
prepareCorners(StickerHoverCorners, st::msgRadius, st::emojiPanHover);
|
||||
|
||||
prepareCorners(MessageInCorners, st::msgRadius, st::msgInBg, &st::msgInShadow);
|
||||
prepareCorners(MessageInSelectedCorners, st::msgRadius, st::msgInSelectBg, &st::msgInSelectShadow);
|
||||
prepareCorners(MessageOutCorners, st::msgRadius, st::msgOutBg, &st::msgOutShadow);
|
||||
prepareCorners(MessageOutSelectedCorners, st::msgRadius, st::msgOutSelectBg, &st::msgOutSelectShadow);
|
||||
prepareCorners(ButtonHoverCorners, st::msgRadius, st::mediaSaveButton.overBgColor, &st::msgInShadow);
|
||||
|
||||
}
|
||||
|
||||
void deinitMedia(bool completely) {
|
||||
@@ -1538,6 +1683,14 @@ namespace App {
|
||||
::sprite = 0;
|
||||
delete ::emojis;
|
||||
::emojis = 0;
|
||||
delete ::emojisLarge;
|
||||
::emojisLarge = 0;
|
||||
for (int32 j = 0; j < 4; ++j) {
|
||||
for (int32 i = 0; i < RoundCornersCount; ++i) {
|
||||
delete ::corners[i][j]; ::corners[i][j] = 0;
|
||||
}
|
||||
delete ::cornersMask[j]; ::cornersMask[j] = 0;
|
||||
}
|
||||
mainEmojisMap.clear();
|
||||
otherEmojisMap.clear();
|
||||
|
||||
@@ -1598,27 +1751,33 @@ namespace App {
|
||||
return ::mousedItem;
|
||||
}
|
||||
|
||||
QPixmap &sprite() {
|
||||
const QPixmap &sprite() {
|
||||
return *::sprite;
|
||||
}
|
||||
|
||||
QPixmap &emojis() {
|
||||
const QPixmap &emojis() {
|
||||
return *::emojis;
|
||||
}
|
||||
|
||||
const QPixmap &emojiSingle(const EmojiData *emoji, int32 fontHeight) {
|
||||
const QPixmap &emojisLarge() {
|
||||
return *::emojisLarge;
|
||||
}
|
||||
|
||||
const QPixmap &emojiSingle(EmojiPtr emoji, int32 fontHeight) {
|
||||
EmojisMap *map = &(fontHeight == st::taDefFlat.font->height ? mainEmojisMap : otherEmojisMap[fontHeight]);
|
||||
EmojisMap::const_iterator i = map->constFind(emoji->code);
|
||||
EmojisMap::const_iterator i = map->constFind(emojiKey(emoji));
|
||||
if (i == map->cend()) {
|
||||
QImage img(st::emojiImgSize + st::emojiPadding * cIntRetinaFactor() * 2, fontHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
QImage img(ESize + st::emojiPadding * cIntRetinaFactor() * 2, fontHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
if (cRetina()) img.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
QPainter p(&img);
|
||||
QPainter::CompositionMode m = p.compositionMode();
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.fillRect(0, 0, img.width(), img.height(), Qt::transparent);
|
||||
p.drawPixmap(QPoint(st::emojiPadding * cIntRetinaFactor(), (fontHeight * cIntRetinaFactor() - st::emojiImgSize) / 2), App::emojis(), QRect(emoji->x, emoji->y, st::emojiImgSize, st::emojiImgSize));
|
||||
p.setCompositionMode(m);
|
||||
emojiDraw(p, emoji, st::emojiPadding * cIntRetinaFactor(), (fontHeight * cIntRetinaFactor() - ESize) / 2);
|
||||
}
|
||||
i = map->insert(emoji->code, QPixmap::fromImage(img, Qt::ColorOnly));
|
||||
i = map->insert(emojiKey(emoji), QPixmap::fromImage(img, Qt::ColorOnly));
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
@@ -1768,6 +1927,34 @@ namespace App {
|
||||
return ::webPageItems;
|
||||
}
|
||||
|
||||
void regMuted(PeerData *peer, int32 changeIn) {
|
||||
::mutedPeers.insert(peer, true);
|
||||
if (App::main()) App::main()->updateMutedIn(changeIn);
|
||||
}
|
||||
|
||||
void unregMuted(PeerData *peer) {
|
||||
::mutedPeers.remove(peer);
|
||||
}
|
||||
|
||||
void updateMuted() {
|
||||
int32 changeInMin = 0;
|
||||
for (MutedPeers::iterator i = ::mutedPeers.begin(); i != ::mutedPeers.end();) {
|
||||
int32 changeIn = 0;
|
||||
History *h = App::history(i.key()->id);
|
||||
if (isNotifyMuted(i.key()->notify, &changeIn)) {
|
||||
h->setMute(true);
|
||||
if (changeIn && (!changeInMin || changeIn < changeInMin)) {
|
||||
changeInMin = changeIn;
|
||||
}
|
||||
++i;
|
||||
} else {
|
||||
h->setMute(false);
|
||||
i = ::mutedPeers.erase(i);
|
||||
}
|
||||
}
|
||||
if (changeInMin) App::main()->updateMutedIn(changeInMin);
|
||||
}
|
||||
|
||||
void setProxySettings(QNetworkAccessManager &manager) {
|
||||
if (cConnectionType() == dbictHttpProxy) {
|
||||
const ConnectionProxy &p(cConnectionProxy());
|
||||
@@ -1798,26 +1985,86 @@ namespace App {
|
||||
}
|
||||
}
|
||||
|
||||
void joinGroupByHash(const QString &hash) {
|
||||
if (App::main()) {
|
||||
App::main()->joinGroupByHash(hash);
|
||||
}
|
||||
}
|
||||
|
||||
void stickersBox(const QString &name) {
|
||||
if (App::main()) {
|
||||
App::main()->stickersBox(MTP_inputStickerSetShortName(MTP_string(name)));
|
||||
}
|
||||
}
|
||||
|
||||
void openLocalUrl(const QString &url) {
|
||||
if (App::main()) {
|
||||
App::main()->openLocalUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
QImage **cornersMask() {
|
||||
return ::cornersMask;
|
||||
}
|
||||
QPixmap **corners(RoundCorners index) {
|
||||
return ::corners[index];
|
||||
}
|
||||
|
||||
void roundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh) {
|
||||
QPixmap **c = ::corners[index];
|
||||
int32 cw = c[0]->width() / cIntRetinaFactor(), ch = c[0]->height() / cIntRetinaFactor();
|
||||
if (w < 2 * cw || h < 2 * ch) return;
|
||||
if (w > 2 * cw) {
|
||||
p.fillRect(QRect(x + cw, y, w - 2 * cw, ch), bg->b);
|
||||
p.fillRect(QRect(x + cw, y + h - ch, w - 2 * cw, ch), bg->b);
|
||||
if (sh) p.fillRect(QRect(x + cw, y + h, w - 2 * cw, st::msgShadow), (*sh)->b);
|
||||
}
|
||||
if (h > 2 * ch) {
|
||||
p.fillRect(QRect(x, y + ch, w, h - 2 * ch), bg->b);
|
||||
}
|
||||
p.drawPixmap(QPoint(x, y), *c[0]);
|
||||
p.drawPixmap(QPoint(x + w - cw, y), *c[1]);
|
||||
p.drawPixmap(QPoint(x, y + h - ch), *c[2]);
|
||||
p.drawPixmap(QPoint(x + w - cw, y + h - ch), *c[3]);
|
||||
}
|
||||
|
||||
void roundShadow(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index) {
|
||||
QPixmap **c = App::corners(index);
|
||||
int32 cw = c[0]->width() / cIntRetinaFactor(), ch = c[0]->height() / cIntRetinaFactor();
|
||||
p.fillRect(x + cw, y + h, w - 2 * cw, st::msgShadow, sh->b);
|
||||
p.fillRect(x, y + h - ch, cw, st::msgShadow, sh->b);
|
||||
p.fillRect(x + w - cw, y + h - ch, cw, st::msgShadow, sh->b);
|
||||
p.drawPixmap(x, y + h - ch + st::msgShadow, *c[2]);
|
||||
p.drawPixmap(x + w - cw, y + h - ch + st::msgShadow, *c[3]);
|
||||
}
|
||||
|
||||
void initBackground(int32 id, const QImage &p, bool nowrite) {
|
||||
if (Local::readBackground()) return;
|
||||
|
||||
QImage img(p);
|
||||
bool remove = false;
|
||||
if (p.isNull()) {
|
||||
img.load(st::msgBG);
|
||||
id = 0;
|
||||
if (id == DefaultChatBackground) {
|
||||
img.load(st::msgBG);
|
||||
} else {
|
||||
img.load(st::msgBG0);
|
||||
if (cRetina()) {
|
||||
img = img.scaledToWidth(img.width() * 2, Qt::SmoothTransformation);
|
||||
} else if (cScale() != dbisOne) {
|
||||
img = img.scaledToWidth(convertScale(img.width()), Qt::SmoothTransformation);
|
||||
}
|
||||
id = 0;
|
||||
}
|
||||
remove = true;
|
||||
}
|
||||
if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_ARGB32_Premultiplied && img.format() != QImage::Format_RGB32) {
|
||||
img = img.convertToFormat(QImage::Format_RGB32);
|
||||
}
|
||||
img.setDevicePixelRatio(cRetinaFactor());
|
||||
|
||||
if (!nowrite) Local::writeBackground(id, img);
|
||||
if (!nowrite) {
|
||||
Local::writeBackground(id, remove ? QImage() : img);
|
||||
}
|
||||
|
||||
delete cChatBackground();
|
||||
cSetChatBackground(new QPixmap(QPixmap::fromImage(img, Qt::ColorOnly)));
|
||||
@@ -1933,7 +2180,17 @@ namespace App {
|
||||
components[maxtomin[0]] = max;
|
||||
|
||||
uchar r = uchar(components[0]), g = uchar(components[1]), b = uchar(components[2]);
|
||||
_msgServiceBG = style::color(r, g, b, qRound(st::msgServiceBG->c.alphaF() * 0xFF));
|
||||
float64 alpha = st::msgServiceBg->c.alphaF();
|
||||
_msgServiceBg = style::color(r, g, b, qRound(alpha * 0xFF));
|
||||
|
||||
float64 alphaSel = st::msgServiceSelectBg->c.alphaF(), addSel = (1. - ((1. - alphaSel) / (1. - alpha))) * 0xFF;
|
||||
uchar rsel = snap(qRound(((1. - alphaSel) * r + addSel) / alphaSel), 0, 0xFF);
|
||||
uchar gsel = snap(qRound(((1. - alphaSel) * g + addSel) / alphaSel), 0, 0xFF);
|
||||
uchar bsel = snap(qRound(((1. - alphaSel) * b + addSel) / alphaSel), 0, 0xFF);
|
||||
_msgServiceSelectBg = style::color(r, g, b, qRound(alphaSel * 0xFF));
|
||||
|
||||
prepareCorners(ServiceCorners, st::msgRadius, _msgServiceBg);
|
||||
prepareCorners(ServiceSelectedCorners, st::msgRadius, _msgServiceSelectBg);
|
||||
|
||||
uchar rScroll = uchar(componentsScroll[0]), gScroll = uchar(componentsScroll[1]), bScroll = uchar(componentsScroll[2]);
|
||||
_historyScrollBarColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.barColor->c.alphaF() * 0xFF));
|
||||
@@ -1946,27 +2203,31 @@ namespace App {
|
||||
if (App::main()) App::main()->updateScrollColors();
|
||||
}
|
||||
|
||||
style::color msgServiceBG() {
|
||||
return _msgServiceBG;
|
||||
const style::color &msgServiceBg() {
|
||||
return _msgServiceBg;
|
||||
}
|
||||
|
||||
style::color historyScrollBarColor() {
|
||||
const style::color &msgServiceSelectBg() {
|
||||
return _msgServiceSelectBg;
|
||||
}
|
||||
|
||||
const style::color &historyScrollBarColor() {
|
||||
return _historyScrollBarColor;
|
||||
}
|
||||
|
||||
style::color historyScrollBgColor() {
|
||||
const style::color &historyScrollBgColor() {
|
||||
return _historyScrollBgColor;
|
||||
}
|
||||
|
||||
style::color historyScrollBarOverColor() {
|
||||
const style::color &historyScrollBarOverColor() {
|
||||
return _historyScrollBarOverColor;
|
||||
}
|
||||
|
||||
style::color historyScrollBgOverColor() {
|
||||
const style::color &historyScrollBgOverColor() {
|
||||
return _historyScrollBgOverColor;
|
||||
}
|
||||
|
||||
style::color introPointHoverColor() {
|
||||
const style::color &introPointHoverColor() {
|
||||
return _introPointHoverColor;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,31 @@ typedef QHash<AudioData*, HistoryItemsMap> AudioItems;
|
||||
typedef QHash<DocumentData*, HistoryItemsMap> DocumentItems;
|
||||
typedef QHash<WebPageData*, HistoryItemsMap> WebPageItems;
|
||||
|
||||
enum RoundCorners {
|
||||
MaskCorners = 0x00, // for images
|
||||
BlackCorners,
|
||||
ServiceCorners,
|
||||
ServiceSelectedCorners,
|
||||
SelectedOverlayCorners,
|
||||
DateCorners,
|
||||
DateSelectedCorners,
|
||||
ForwardCorners,
|
||||
MediaviewSaveCorners,
|
||||
EmojiHoverCorners,
|
||||
StickerHoverCorners,
|
||||
|
||||
InShadowCorners, // for photos without bg
|
||||
InSelectedShadowCorners,
|
||||
|
||||
MessageInCorners, // with shadow
|
||||
MessageInSelectedCorners,
|
||||
MessageOutCorners,
|
||||
MessageOutSelectedCorners,
|
||||
ButtonHoverCorners,
|
||||
|
||||
RoundCornersCount
|
||||
};
|
||||
|
||||
namespace App {
|
||||
Application *app();
|
||||
Window *wnd();
|
||||
@@ -72,8 +97,8 @@ namespace App {
|
||||
QString onlineText(UserData *user, int32 nowOnServer, bool precise = false);
|
||||
bool onlineColorUse(int32 online, int32 now);
|
||||
|
||||
UserData *feedUsers(const MTPVector<MTPUser> &users); // returnes last user
|
||||
void feedChats(const MTPVector<MTPChat> &chats);
|
||||
UserData *feedUsers(const MTPVector<MTPUser> &users); // returns last user
|
||||
ChatData *feedChats(const MTPVector<MTPChat> &chats); // returns last chat
|
||||
void feedParticipants(const MTPChatParticipants &p);
|
||||
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d);
|
||||
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d);
|
||||
@@ -87,11 +112,13 @@ namespace App {
|
||||
int32 maxMsgId();
|
||||
|
||||
ImagePtr image(const MTPPhotoSize &size);
|
||||
StorageImageLocation imageLocation(const MTPPhotoSize &size);
|
||||
|
||||
PhotoData *feedPhoto(const MTPPhoto &photo, const PreparedPhotoThumbs &thumbs);
|
||||
PhotoData *feedPhoto(const MTPPhoto &photo, PhotoData *convert = 0);
|
||||
PhotoData *feedPhoto(const MTPDphoto &photo, PhotoData *convert = 0);
|
||||
VideoData *feedVideo(const MTPDvideo &video, VideoData *convert = 0);
|
||||
AudioData *feedAudio(const MTPaudio &audio, AudioData *convert = 0);
|
||||
AudioData *feedAudio(const MTPDaudio &audio, AudioData *convert = 0);
|
||||
DocumentData *feedDocument(const MTPdocument &document, const QPixmap &thumb);
|
||||
DocumentData *feedDocument(const MTPdocument &document, DocumentData *convert = 0);
|
||||
@@ -117,7 +144,8 @@ namespace App {
|
||||
PhotoData *photo(const PhotoId &photo, PhotoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr());
|
||||
VideoData *video(const VideoId &video, VideoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
|
||||
AudioData *audio(const AudioId &audio, AudioData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const QString &mime = QString(), int32 duration = 0, int32 dc = 0, int32 size = 0);
|
||||
DocumentData *document(const DocumentId &document, DocumentData *convert = 0, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
|
||||
DocumentData *document(const DocumentId &document);
|
||||
DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation);
|
||||
WebPageData *webPage(const WebPageId &webPage, WebPageData *convert = 0, const QString &type = QString(), const QString &url = QString(), const QString &displayUrl = QString(), const QString &siteName = QString(), const QString &title = QString(), const QString &description = QString(), PhotoData *photo = 0, int32 duration = 0, const QString &author = QString(), int32 pendingTill = -2);
|
||||
ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type = InvalidImageLink, const QString &url = QString());
|
||||
void forgetMedia();
|
||||
@@ -154,9 +182,10 @@ namespace App {
|
||||
void mousedItem(HistoryItem *item);
|
||||
HistoryItem *mousedItem();
|
||||
|
||||
QPixmap &sprite();
|
||||
QPixmap &emojis();
|
||||
const QPixmap &emojiSingle(const EmojiData *emoji, int32 fontHeight);
|
||||
const QPixmap &sprite();
|
||||
const QPixmap &emojis();
|
||||
const QPixmap &emojisLarge();
|
||||
const QPixmap &emojiSingle(EmojiPtr emoji, int32 fontHeight);
|
||||
|
||||
void initMedia();
|
||||
void deinitMedia(bool completely = true);
|
||||
@@ -189,21 +218,39 @@ namespace App {
|
||||
void unregWebPageItem(WebPageData *data, HistoryItem *item);
|
||||
const WebPageItems &webPageItems();
|
||||
|
||||
void regMuted(PeerData *peer, int32 changeIn);
|
||||
void unregMuted(PeerData *peer);
|
||||
void updateMuted();
|
||||
|
||||
void setProxySettings(QNetworkAccessManager &manager);
|
||||
void setProxySettings(QTcpSocket &socket);
|
||||
|
||||
void searchByHashtag(const QString &tag);
|
||||
void openUserByName(const QString &username, bool toProfile = false);
|
||||
void joinGroupByHash(const QString &hash);
|
||||
void stickersBox(const QString &name);
|
||||
void openLocalUrl(const QString &url);
|
||||
|
||||
void initBackground(int32 id = 0, const QImage &p = QImage(), bool nowrite = false);
|
||||
QImage **cornersMask();
|
||||
QPixmap **corners(RoundCorners index);
|
||||
void roundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh = 0);
|
||||
inline void roundRect(QPainter &p, const QRect &rect, const style::color &bg, RoundCorners index, const style::color *sh = 0) {
|
||||
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, sh);
|
||||
}
|
||||
void roundShadow(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index);
|
||||
inline void roundShadow(QPainter &p, const QRect &rect, const style::color &sh, RoundCorners index) {
|
||||
return roundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), sh, index);
|
||||
}
|
||||
|
||||
style::color msgServiceBG();
|
||||
style::color historyScrollBarColor();
|
||||
style::color historyScrollBgColor();
|
||||
style::color historyScrollBarOverColor();
|
||||
style::color historyScrollBgOverColor();
|
||||
style::color introPointHoverColor();
|
||||
void initBackground(int32 id = DefaultChatBackground, const QImage &p = QImage(), bool nowrite = false);
|
||||
|
||||
const style::color &msgServiceBg();
|
||||
const style::color &msgServiceSelectBg();
|
||||
const style::color &historyScrollBarColor();
|
||||
const style::color &historyScrollBgColor();
|
||||
const style::color &historyScrollBarOverColor();
|
||||
const style::color &historyScrollBgOverColor();
|
||||
const style::color &introPointHoverColor();
|
||||
|
||||
struct WallPaper {
|
||||
WallPaper(int32 id, ImagePtr thumb, ImagePtr full) : id(id), thumb(thumb), full(full) {
|
||||
|
||||
@@ -29,6 +29,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include "localstorage.h"
|
||||
|
||||
#include "autoupdater.h"
|
||||
|
||||
namespace {
|
||||
Application *mainApp = 0;
|
||||
FileUploader *uploader = 0;
|
||||
@@ -59,7 +61,15 @@ namespace {
|
||||
if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) {
|
||||
App::wnd()->minimizeToTray();
|
||||
return true;
|
||||
} else {
|
||||
App::wnd()->hide();
|
||||
App::wnd()->updateIsActive(cOfflineBlurTimeout());
|
||||
App::wnd()->updateGlobalMenu();
|
||||
return true;
|
||||
}
|
||||
} else if (ev->key() == Qt::Key_M && (ev->modifiers() & (Qt::MetaModifier | Qt::ControlModifier))) {
|
||||
App::wnd()->setWindowState(Qt::WindowMinimized);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QObject::eventFilter(o, e);
|
||||
@@ -172,29 +182,6 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
|
||||
}
|
||||
}
|
||||
|
||||
void Application::onAppUpdate(const MTPhelp_AppUpdate &response) {
|
||||
updateRequestId = 0;
|
||||
|
||||
cSetLastUpdateCheck(unixtime());
|
||||
Local::writeSettings();
|
||||
if (response.type() == mtpc_help_noAppUpdate) {
|
||||
startUpdateCheck();
|
||||
} else {
|
||||
updateThread = new QThread();
|
||||
connect(updateThread, SIGNAL(finished()), updateThread, SLOT(deleteLater()));
|
||||
updateDownloader = new PsUpdateDownloader(updateThread, response.c_help_appUpdate());
|
||||
updateThread->start();
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::onAppUpdateFail() {
|
||||
updateRequestId = 0;
|
||||
cSetLastUpdateCheck(unixtime());
|
||||
Local::writeSettings();
|
||||
startUpdateCheck();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::updateGotCurrent() {
|
||||
if (!updateReply || updateThread) return;
|
||||
|
||||
@@ -205,7 +192,7 @@ void Application::updateGotCurrent() {
|
||||
if (currentVersion > AppVersion) {
|
||||
updateThread = new QThread();
|
||||
connect(updateThread, SIGNAL(finished()), updateThread, SLOT(deleteLater()));
|
||||
updateDownloader = new PsUpdateDownloader(updateThread, m.captured(2));
|
||||
updateDownloader = new UpdateDownloader(updateThread, m.captured(2));
|
||||
updateThread->start();
|
||||
}
|
||||
}
|
||||
@@ -473,13 +460,13 @@ void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId)
|
||||
|
||||
PhotoId id = MTP::nonce<PhotoId>();
|
||||
|
||||
MTPPhoto photo(MTP_photo(MTP_long(id), MTP_long(0), MTP_int(MTP::authedId()), MTP_int(unixtime()), MTP_string(""), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes)));
|
||||
MTPPhoto photo(MTP_photo(MTP_long(id), MTP_long(0), MTP_int(MTP::authedId()), MTP_int(unixtime()), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes)));
|
||||
|
||||
QString file, filename;
|
||||
int32 filesize = 0;
|
||||
QByteArray data;
|
||||
|
||||
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false, 0);
|
||||
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, MTP_audioEmpty(MTP_long(0)), photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false, 0);
|
||||
|
||||
connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), App::app(), SLOT(photoUpdated(MsgId, const MTPInputFile &)), Qt::UniqueConnection);
|
||||
|
||||
@@ -532,7 +519,6 @@ void Application::startUpdateCheck(bool forceWait) {
|
||||
updateReply = updateManager.get(checkVersion);
|
||||
connect(updateReply, SIGNAL(finished()), this, SLOT(updateGotCurrent()));
|
||||
connect(updateReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(updateFailedCurrent(QNetworkReply::NetworkError)));
|
||||
// updateRequestId = MTP::send(MTPhelp_GetAppUpdate(MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(cApiLang())), rpcDone(&Application::onAppUpdate), rpcFail(&Application::onAppUpdateFail);
|
||||
emit updateChecking();
|
||||
} else {
|
||||
updateCheckTimer.start((updateInSecs + 5) * 1000);
|
||||
@@ -640,7 +626,7 @@ void Application::socketError(QLocalSocket::LocalSocketError e) {
|
||||
return App::quit();
|
||||
}
|
||||
|
||||
if (!cNoStartUpdate() && psCheckReadyUpdate()) {
|
||||
if (!cNoStartUpdate() && checkReadyUpdate()) {
|
||||
cSetRestartingUpdate(true);
|
||||
DEBUG_LOG(("Application Info: installing update instead of starting app.."));
|
||||
return App::quit();
|
||||
@@ -650,14 +636,14 @@ void Application::socketError(QLocalSocket::LocalSocketError e) {
|
||||
}
|
||||
|
||||
void Application::checkMapVersion() {
|
||||
if (Local::oldMapVersion() < AppVersion) {
|
||||
if (Local::oldMapVersion() < AppVersion) {
|
||||
psRegisterCustomScheme();
|
||||
if (Local::oldMapVersion()) {
|
||||
QString versionFeatures;
|
||||
if (DevChannel && Local::oldMapVersion() < 8002) {
|
||||
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Link previews bugfixes\n\xe2\x80\x94 Links in preview descriptions are now clickable\n\xe2\x80\x94 Twitter and Instagram mentions and hashtags in previews are clickable\n\xe2\x80\x94 Fixed file uploading\n\xe2\x80\x94 Fixed photo, document and sticker forwarding").replace('@', qsl("@") + QChar(0x200D));
|
||||
} else if (!DevChannel && Local::oldMapVersion() < 8004) {
|
||||
versionFeatures = lang(lng_new_version_minor).trimmed();
|
||||
if (DevChannel && Local::oldMapVersion() < 8023) {
|
||||
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Improved sticker panel\n\xe2\x80\x94 Bug fixes and minor stuff");// .replace('@', qsl("@") + QChar(0x200D));
|
||||
} else if (!DevChannel && Local::oldMapVersion() < 8021) {
|
||||
versionFeatures = lang(lng_new_version_text).trimmed();
|
||||
}
|
||||
if (!versionFeatures.isEmpty()) {
|
||||
versionFeatures = lng_new_version_wrap(lt_version, QString::fromStdWString(AppVersionStr), lt_changes, versionFeatures, lt_link, qsl("https://desktop.telegram.org/#changelog"));
|
||||
@@ -672,6 +658,8 @@ void Application::startApp() {
|
||||
|
||||
DEBUG_LOG(("Application Info: starting app.."));
|
||||
|
||||
QMimeDatabase().mimeTypeForName(qsl("text/plain")); // create mime database
|
||||
|
||||
window->createWinId();
|
||||
window->init();
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
class MainWidget;
|
||||
class FileUploader;
|
||||
class Translator;
|
||||
class UpdateDownloader;
|
||||
|
||||
class Application : public PsApplication, public RPCSender {
|
||||
Q_OBJECT
|
||||
@@ -42,9 +43,6 @@ public:
|
||||
static int32 languageId();
|
||||
static MainWidget *main();
|
||||
|
||||
void onAppUpdate(const MTPhelp_AppUpdate &response);
|
||||
bool onAppUpdateFail();
|
||||
|
||||
enum UpdatingState {
|
||||
UpdatingNone,
|
||||
UpdatingDownload,
|
||||
@@ -80,6 +78,12 @@ public:
|
||||
|
||||
signals:
|
||||
|
||||
void updateChecking();
|
||||
void updateLatest();
|
||||
void updateDownloading(qint64 ready, qint64 total);
|
||||
void updateReady();
|
||||
void updateFailed();
|
||||
|
||||
void peerPhotoDone(PeerId peer);
|
||||
void peerPhotoFail(PeerId peer);
|
||||
|
||||
@@ -143,7 +147,7 @@ private:
|
||||
QNetworkReply *updateReply;
|
||||
SingleTimer updateCheckTimer;
|
||||
QThread *updateThread;
|
||||
PsUpdateDownloader *updateDownloader;
|
||||
UpdateDownloader *updateDownloader;
|
||||
|
||||
QTimer writeUserConfigTimer;
|
||||
|
||||
|
||||
BIN
Telegram/SourceFiles/art/bg.jpg
Normal file
|
After Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 354 KiB |
|
Before Width: | Height: | Size: 526 KiB |
|
Before Width: | Height: | Size: 528 KiB |
BIN
Telegram/SourceFiles/art/emoji.webp
Normal file
|
After Width: | Height: | Size: 586 KiB |
|
Before Width: | Height: | Size: 722 KiB |
BIN
Telegram/SourceFiles/art/emoji_125x.webp
Normal file
|
After Width: | Height: | Size: 789 KiB |
|
Before Width: | Height: | Size: 1003 KiB |
BIN
Telegram/SourceFiles/art/emoji_150x.webp
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.5 MiB |
BIN
Telegram/SourceFiles/art/emoji_200x.webp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
Telegram/SourceFiles/art/emoji_250x.webp
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 722 KiB |
|
Before Width: | Height: | Size: 531 KiB |
|
Before Width: | Height: | Size: 956 KiB |
|
Before Width: | Height: | Size: 402 KiB |
|
Before Width: | Height: | Size: 532 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 218 KiB |
@@ -24,33 +24,35 @@ bool audioWorks();
|
||||
void audioPlayNotify();
|
||||
void audioFinish();
|
||||
|
||||
enum VoiceMessageState {
|
||||
VoiceMessageStopped,
|
||||
VoiceMessageStarting,
|
||||
VoiceMessagePlaying,
|
||||
VoiceMessageFinishing,
|
||||
VoiceMessagePausing,
|
||||
VoiceMessagePaused,
|
||||
VoiceMessageResuming,
|
||||
enum AudioPlayerState {
|
||||
AudioPlayerStopped,
|
||||
AudioPlayerStoppedAtStart,
|
||||
AudioPlayerStarting,
|
||||
AudioPlayerPlaying,
|
||||
AudioPlayerFinishing,
|
||||
AudioPlayerPausing,
|
||||
AudioPlayerPaused,
|
||||
AudioPlayerResuming,
|
||||
};
|
||||
|
||||
class VoiceMessagesFader;
|
||||
class VoiceMessagesLoader;
|
||||
class AudioPlayerFader;
|
||||
class AudioPlayerLoaders;
|
||||
|
||||
class VoiceMessages : public QObject {
|
||||
class AudioPlayer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoiceMessages();
|
||||
AudioPlayer();
|
||||
|
||||
void play(AudioData *audio);
|
||||
void pauseresume();
|
||||
|
||||
void currentState(AudioData **audio, VoiceMessageState *state = 0, int64 *position = 0, int64 *duration = 0);
|
||||
void processContext();
|
||||
void currentState(AudioData **audio, AudioPlayerState *state = 0, int64 *position = 0, int64 *duration = 0, int32 *frequency = 0);
|
||||
void clearStoppedAtStart(AudioData *audio);
|
||||
void resumeDevice();
|
||||
|
||||
~VoiceMessages();
|
||||
~AudioPlayer();
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -70,8 +72,8 @@ private:
|
||||
bool updateCurrentStarted(int32 pos = -1);
|
||||
|
||||
struct Msg {
|
||||
Msg() : audio(0), position(0), duration(0), skipStart(0), skipEnd(0), loading(0), started(0),
|
||||
state(VoiceMessageStopped), source(0), nextBuffer(0) {
|
||||
Msg() : audio(0), position(0), duration(0), frequency(AudioVoiceMsgFrequency), skipStart(0), skipEnd(0), loading(0), started(0),
|
||||
state(AudioPlayerStopped), source(0), nextBuffer(0) {
|
||||
memset(buffers, 0, sizeof(buffers));
|
||||
memset(samplesCount, 0, sizeof(samplesCount));
|
||||
}
|
||||
@@ -79,10 +81,11 @@ private:
|
||||
QString fname;
|
||||
QByteArray data;
|
||||
int64 position, duration;
|
||||
int32 frequency;
|
||||
int64 skipStart, skipEnd;
|
||||
bool loading;
|
||||
int64 started;
|
||||
VoiceMessageState state;
|
||||
AudioPlayerState state;
|
||||
|
||||
uint32 source;
|
||||
int32 nextBuffer;
|
||||
@@ -95,25 +98,59 @@ private:
|
||||
|
||||
QMutex _mutex;
|
||||
|
||||
friend class VoiceMessagesFader;
|
||||
friend class VoiceMessagesLoader;
|
||||
friend class AudioPlayerFader;
|
||||
friend class AudioPlayerLoaders;
|
||||
|
||||
QThread _faderThread;
|
||||
QThread _loaderThread;
|
||||
VoiceMessagesFader *_fader;
|
||||
VoiceMessagesLoader *_loader;
|
||||
QThread _faderThread, _loaderThread;
|
||||
AudioPlayerFader *_fader;
|
||||
AudioPlayerLoaders *_loader;
|
||||
|
||||
};
|
||||
|
||||
VoiceMessages *audioVoice();
|
||||
class AudioCaptureInner;
|
||||
|
||||
class VoiceMessagesFader : public QObject {
|
||||
class AudioCapture : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoiceMessagesFader(QThread *thread);
|
||||
void processContext();
|
||||
AudioCapture();
|
||||
|
||||
void start();
|
||||
void stop(bool needResult);
|
||||
|
||||
bool check();
|
||||
|
||||
~AudioCapture();
|
||||
|
||||
signals:
|
||||
|
||||
void captureOnStart();
|
||||
void captureOnStop(bool needResult);
|
||||
|
||||
void onDone(QByteArray data, qint32 samples);
|
||||
void onUpdate(qint16 level, qint32 samples);
|
||||
void onError();
|
||||
|
||||
private:
|
||||
|
||||
friend class AudioCaptureInner;
|
||||
|
||||
QThread _captureThread;
|
||||
AudioCaptureInner *_capture;
|
||||
|
||||
};
|
||||
|
||||
AudioPlayer *audioPlayer();
|
||||
AudioCapture *audioCapture();
|
||||
|
||||
class AudioPlayerFader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AudioPlayerFader(QThread *thread);
|
||||
void resumeDevice();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -122,30 +159,31 @@ signals:
|
||||
void audioStopped(AudioData *audio);
|
||||
void needToPreload(AudioData *audio);
|
||||
|
||||
void stopSuspend();
|
||||
void stopPauseDevice();
|
||||
|
||||
public slots:
|
||||
|
||||
void onInit();
|
||||
void onTimer();
|
||||
void onSuspendTimer();
|
||||
void onSuspendTimerStop();
|
||||
void onPauseTimer();
|
||||
void onPauseTimerStop();
|
||||
|
||||
private:
|
||||
|
||||
QTimer _timer, _suspendTimer;
|
||||
QMutex _suspendMutex;
|
||||
bool _suspendFlag;
|
||||
QTimer _timer, _pauseTimer;
|
||||
QMutex _pauseMutex;
|
||||
bool _pauseFlag, _paused;
|
||||
|
||||
};
|
||||
|
||||
class VoiceMessagesLoader : public QObject {
|
||||
class AudioPlayerLoader;
|
||||
class AudioPlayerLoaders : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoiceMessagesLoader(QThread *thread);
|
||||
~VoiceMessagesLoader();
|
||||
AudioPlayerLoaders(QThread *thread);
|
||||
~AudioPlayerLoaders();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -161,10 +199,43 @@ public slots:
|
||||
|
||||
private:
|
||||
|
||||
struct Loader;
|
||||
typedef QMap<AudioData*, Loader*> Loaders;
|
||||
typedef QMap<AudioData*, AudioPlayerLoader*> Loaders;
|
||||
Loaders _loaders;
|
||||
|
||||
void loadError(Loaders::iterator i);
|
||||
|
||||
};
|
||||
|
||||
struct AudioCapturePrivate;
|
||||
|
||||
class AudioCaptureInner : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AudioCaptureInner(QThread *thread);
|
||||
~AudioCaptureInner();
|
||||
|
||||
signals:
|
||||
|
||||
void error();
|
||||
void update(qint16 level, qint32 samples);
|
||||
void done(QByteArray data, qint32 samples);
|
||||
|
||||
public slots:
|
||||
|
||||
void onInit();
|
||||
void onStart();
|
||||
void onStop(bool needResult);
|
||||
|
||||
void onTimeout();
|
||||
|
||||
private:
|
||||
|
||||
void writeFrame(int32 offset, int32 framesize);
|
||||
|
||||
AudioCapturePrivate *d;
|
||||
QTimer _timer;
|
||||
QByteArray _captured;
|
||||
|
||||
};
|
||||
|
||||
523
Telegram/SourceFiles/autoupdater.cpp
Normal file
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "application.h"
|
||||
#include "pspecific.h"
|
||||
#include "autoupdater.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
typedef DWORD VerInt;
|
||||
typedef WCHAR VerChar;
|
||||
#else
|
||||
typedef int VerInt;
|
||||
typedef wchar_t VerChar;
|
||||
#endif
|
||||
|
||||
UpdateDownloader::UpdateDownloader(QThread *thread, const QString &url) : reply(0), already(0), full(0) {
|
||||
updateUrl = url;
|
||||
moveToThread(thread);
|
||||
manager.moveToThread(thread);
|
||||
App::setProxySettings(manager);
|
||||
|
||||
connect(thread, SIGNAL(started()), this, SLOT(start()));
|
||||
initOutput();
|
||||
}
|
||||
|
||||
void UpdateDownloader::initOutput() {
|
||||
QString fileName;
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)")).match(updateUrl);
|
||||
if (m.hasMatch()) {
|
||||
fileName = m.captured(1).replace(QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")), QString());
|
||||
}
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = qsl("tupdate-%1").arg(rand());
|
||||
}
|
||||
QString dirStr = cWorkingDir() + qsl("tupdates/");
|
||||
fileName = dirStr + fileName;
|
||||
QFileInfo file(fileName);
|
||||
|
||||
QDir dir(dirStr);
|
||||
if (dir.exists()) {
|
||||
QFileInfoList all = dir.entryInfoList(QDir::Files);
|
||||
for (QFileInfoList::iterator i = all.begin(), e = all.end(); i != e; ++i) {
|
||||
if (i->absoluteFilePath() != file.absoluteFilePath()) {
|
||||
QFile::remove(i->absoluteFilePath());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dir.mkdir(dir.absolutePath());
|
||||
}
|
||||
outputFile.setFileName(fileName);
|
||||
if (file.exists()) {
|
||||
uint64 fullSize = file.size();
|
||||
if (fullSize < INT_MAX) {
|
||||
int32 goodSize = (int32)fullSize;
|
||||
if (goodSize % UpdateChunk) {
|
||||
goodSize = goodSize - (goodSize % UpdateChunk);
|
||||
if (goodSize) {
|
||||
if (outputFile.open(QIODevice::ReadOnly)) {
|
||||
QByteArray goodData = outputFile.readAll().mid(0, goodSize);
|
||||
outputFile.close();
|
||||
if (outputFile.open(QIODevice::WriteOnly)) {
|
||||
outputFile.write(goodData);
|
||||
outputFile.close();
|
||||
|
||||
QMutexLocker lock(&mutex);
|
||||
already = goodSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QMutexLocker lock(&mutex);
|
||||
already = goodSize;
|
||||
}
|
||||
}
|
||||
if (!already) {
|
||||
QFile::remove(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateDownloader::start() {
|
||||
sendRequest();
|
||||
}
|
||||
|
||||
void UpdateDownloader::sendRequest() {
|
||||
QNetworkRequest req(updateUrl);
|
||||
QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";
|
||||
req.setRawHeader("Range", rangeHeaderValue);
|
||||
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
|
||||
if (reply) reply->deleteLater();
|
||||
reply = manager.get(req);
|
||||
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(partFinished(qint64,qint64)));
|
||||
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(partFailed(QNetworkReply::NetworkError)));
|
||||
connect(reply, SIGNAL(metaDataChanged()), this, SLOT(partMetaGot()));
|
||||
}
|
||||
|
||||
void UpdateDownloader::partMetaGot() {
|
||||
typedef QList<QNetworkReply::RawHeaderPair> Pairs;
|
||||
Pairs pairs = reply->rawHeaderPairs();
|
||||
for (Pairs::iterator i = pairs.begin(), e = pairs.end(); i != e; ++i) {
|
||||
if (QString::fromUtf8(i->first).toLower() == "content-range") {
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(i->second));
|
||||
if (m.hasMatch()) {
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
full = m.captured(1).toInt();
|
||||
}
|
||||
emit App::app()->updateDownloading(already, full);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 UpdateDownloader::ready() {
|
||||
QMutexLocker lock(&mutex);
|
||||
return already;
|
||||
}
|
||||
|
||||
int32 UpdateDownloader::size() {
|
||||
QMutexLocker lock(&mutex);
|
||||
return full;
|
||||
}
|
||||
|
||||
void UpdateDownloader::partFinished(qint64 got, qint64 total) {
|
||||
if (!reply) return;
|
||||
|
||||
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
if (statusCode.isValid()) {
|
||||
int status = statusCode.toInt();
|
||||
if (status != 200 && status != 206 && status != 416) {
|
||||
LOG(("Update Error: Bad HTTP status received in partFinished(): %1").arg(status));
|
||||
return fatalFail();
|
||||
}
|
||||
}
|
||||
|
||||
if (!already && !full) {
|
||||
QMutexLocker lock(&mutex);
|
||||
full = total;
|
||||
}
|
||||
DEBUG_LOG(("Update Info: part %1 of %2").arg(got).arg(total));
|
||||
|
||||
if (!outputFile.isOpen()) {
|
||||
if (!outputFile.open(QIODevice::Append)) {
|
||||
LOG(("Update Error: Could not open output file '%1' for appending").arg(outputFile.fileName()));
|
||||
return fatalFail();
|
||||
}
|
||||
}
|
||||
QByteArray r = reply->readAll();
|
||||
if (!r.isEmpty()) {
|
||||
outputFile.write(r);
|
||||
|
||||
QMutexLocker lock(&mutex);
|
||||
already += r.size();
|
||||
}
|
||||
if (got >= total) {
|
||||
reply->deleteLater();
|
||||
reply = 0;
|
||||
outputFile.close();
|
||||
unpackUpdate();
|
||||
} else {
|
||||
emit App::app()->updateDownloading(already, full);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateDownloader::partFailed(QNetworkReply::NetworkError e) {
|
||||
if (!reply) return;
|
||||
|
||||
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
reply->deleteLater();
|
||||
reply = 0;
|
||||
if (statusCode.isValid()) {
|
||||
int status = statusCode.toInt();
|
||||
if (status == 416) { // Requested range not satisfiable
|
||||
outputFile.close();
|
||||
unpackUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG(("Update Error: failed to download part starting from %1, error %2").arg(already).arg(e));
|
||||
emit App::app()->updateFailed();
|
||||
}
|
||||
|
||||
void UpdateDownloader::fatalFail() {
|
||||
clearAll();
|
||||
emit App::app()->updateFailed();
|
||||
}
|
||||
|
||||
void UpdateDownloader::clearAll() {
|
||||
psDeleteDir(cWorkingDir() + qsl("tupdates"));
|
||||
}
|
||||
|
||||
//QString winapiErrorWrap() {
|
||||
// WCHAR errMsg[2048];
|
||||
// DWORD errorCode = GetLastError();
|
||||
// LPTSTR errorText = NULL, errorTextDefault = L"(Unknown error)";
|
||||
// FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, 0, 0);
|
||||
// if (!errorText) {
|
||||
// errorText = errorTextDefault;
|
||||
// }
|
||||
// StringCbPrintf(errMsg, sizeof(errMsg), L"Error code: %d, error message: %s", errorCode, errorText);
|
||||
// if (errorText != errorTextDefault) {
|
||||
// LocalFree(errorText);
|
||||
// }
|
||||
// return QString::fromWCharArray(errMsg);
|
||||
//}
|
||||
|
||||
void UpdateDownloader::unpackUpdate() {
|
||||
QByteArray packed;
|
||||
if (!outputFile.open(QIODevice::ReadOnly)) {
|
||||
LOG(("Update Error: cant read updates file!"));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN // use Lzma SDK for win
|
||||
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
|
||||
#else
|
||||
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header
|
||||
#endif
|
||||
|
||||
QByteArray compressed = outputFile.readAll();
|
||||
int32 compressedLen = compressed.size() - hSize;
|
||||
if (compressedLen <= 0) {
|
||||
LOG(("Update Error: bad compressed size: %1").arg(compressed.size()));
|
||||
return fatalFail();
|
||||
}
|
||||
outputFile.close();
|
||||
|
||||
QString tempDirPath = cWorkingDir() + qsl("tupdates/temp"), readyFilePath = cWorkingDir() + qsl("tupdates/temp/ready");
|
||||
psDeleteDir(tempDirPath);
|
||||
|
||||
QDir tempDir(tempDirPath);
|
||||
if (tempDir.exists() || QFile(readyFilePath).exists()) {
|
||||
LOG(("Update Error: cant clear tupdates/temp dir!"));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
uchar sha1Buffer[20];
|
||||
bool goodSha1 = !memcmp(compressed.constData() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, compressedLen + hPropsLen + hOriginalSizeLen, sha1Buffer), hShaLen);
|
||||
if (!goodSha1) {
|
||||
LOG(("Update Error: bad SHA1 hash of update file!"));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
RSA *pbKey = PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(DevChannel ? UpdatesPublicDevKey : UpdatesPublicKey), -1), 0, 0, 0);
|
||||
if (!pbKey) {
|
||||
LOG(("Update Error: cant read public rsa key!"));
|
||||
return fatalFail();
|
||||
}
|
||||
if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature
|
||||
RSA_free(pbKey);
|
||||
LOG(("Update Error: bad RSA signature of update file!"));
|
||||
return fatalFail();
|
||||
}
|
||||
RSA_free(pbKey);
|
||||
|
||||
QByteArray uncompressed;
|
||||
|
||||
int32 uncompressedLen;
|
||||
memcpy(&uncompressedLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen);
|
||||
uncompressed.resize(uncompressedLen);
|
||||
|
||||
size_t resultLen = uncompressed.size();
|
||||
#ifdef Q_OS_WIN // use Lzma SDK for win
|
||||
SizeT srcLen = compressedLen;
|
||||
int uncompressRes = LzmaUncompress((uchar*)uncompressed.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
|
||||
if (uncompressRes != SZ_OK) {
|
||||
LOG(("Update Error: could not uncompress lzma, code: %1").arg(uncompressRes));
|
||||
return fatalFail();
|
||||
}
|
||||
#else
|
||||
lzma_stream stream = LZMA_STREAM_INIT;
|
||||
|
||||
lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED);
|
||||
if (ret != LZMA_OK) {
|
||||
const char *msg;
|
||||
switch (ret) {
|
||||
case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
|
||||
case LZMA_OPTIONS_ERROR: msg = "Specified preset is not supported"; break;
|
||||
case LZMA_UNSUPPORTED_CHECK: msg = "Specified integrity check is not supported"; break;
|
||||
default: msg = "Unknown error, possibly a bug"; break;
|
||||
}
|
||||
LOG(("Error initializing the decoder: %1 (error code %2)").arg(msg).arg(ret));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
stream.avail_in = compressedLen;
|
||||
stream.next_in = (uint8_t*)(compressed.constData() + hSize);
|
||||
stream.avail_out = resultLen;
|
||||
stream.next_out = (uint8_t*)uncompressed.data();
|
||||
|
||||
lzma_ret res = lzma_code(&stream, LZMA_FINISH);
|
||||
if (stream.avail_in) {
|
||||
LOG(("Error in decompression, %1 bytes left in _in of %2 whole.").arg(stream.avail_in).arg(compressedLen));
|
||||
return fatalFail();
|
||||
} else if (stream.avail_out) {
|
||||
LOG(("Error in decompression, %1 bytes free left in _out of %2 whole.").arg(stream.avail_out).arg(resultLen));
|
||||
return fatalFail();
|
||||
}
|
||||
lzma_end(&stream);
|
||||
if (res != LZMA_OK && res != LZMA_STREAM_END) {
|
||||
const char *msg;
|
||||
switch (res) {
|
||||
case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
|
||||
case LZMA_FORMAT_ERROR: msg = "The input data is not in the .xz format"; break;
|
||||
case LZMA_OPTIONS_ERROR: msg = "Unsupported compression options"; break;
|
||||
case LZMA_DATA_ERROR: msg = "Compressed file is corrupt"; break;
|
||||
case LZMA_BUF_ERROR: msg = "Compressed data is truncated or otherwise corrupt"; break;
|
||||
default: msg = "Unknown error, possibly a bug"; break;
|
||||
}
|
||||
LOG(("Error in decompression: %1 (error code %2)").arg(msg).arg(res));
|
||||
return fatalFail();
|
||||
}
|
||||
#endif
|
||||
|
||||
tempDir.mkdir(tempDir.absolutePath());
|
||||
|
||||
quint32 version;
|
||||
{
|
||||
QBuffer buffer(&uncompressed);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QDataStream stream(&buffer);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
|
||||
stream >> version;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("Update Error: cant read version from downloaded stream, status: %1").arg(stream.status()));
|
||||
return fatalFail();
|
||||
}
|
||||
if (int32(version) <= AppVersion) {
|
||||
LOG(("Update Error: downloaded version %1 is not greater, than mine %2").arg(version).arg(AppVersion));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
quint32 filesCount;
|
||||
stream >> filesCount;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("Update Error: cant read files count from downloaded stream, status: %1").arg(stream.status()));
|
||||
return fatalFail();
|
||||
}
|
||||
if (!filesCount) {
|
||||
LOG(("Update Error: update is empty!"));
|
||||
return fatalFail();
|
||||
}
|
||||
for (uint32 i = 0; i < filesCount; ++i) {
|
||||
QString relativeName;
|
||||
quint32 fileSize;
|
||||
QByteArray fileInnerData;
|
||||
bool executable = false;
|
||||
|
||||
stream >> relativeName >> fileSize >> fileInnerData;
|
||||
#if defined Q_OS_MAC || defined Q_OS_LINUX
|
||||
stream >> executable;
|
||||
#endif
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status()));
|
||||
return fatalFail();
|
||||
}
|
||||
if (fileSize != quint32(fileInnerData.size())) {
|
||||
LOG(("Update Error: bad file size %1 not matching data size %2").arg(fileSize).arg(fileInnerData.size()));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
QFile f(tempDirPath + '/' + relativeName);
|
||||
if (!QDir().mkpath(QFileInfo(f).absolutePath())) {
|
||||
LOG(("Update Error: cant mkpath for file '%1'").arg(tempDirPath + '/' + relativeName));
|
||||
return fatalFail();
|
||||
}
|
||||
if (!f.open(QIODevice::WriteOnly)) {
|
||||
LOG(("Update Error: cant open file '%1' for writing").arg(tempDirPath + '/' + relativeName));
|
||||
return fatalFail();
|
||||
}
|
||||
if (f.write(fileInnerData) != fileSize) {
|
||||
f.close();
|
||||
LOG(("Update Error: cant write file '%1'").arg(tempDirPath + '/' + relativeName));
|
||||
return fatalFail();
|
||||
}
|
||||
f.close();
|
||||
if (executable) {
|
||||
QFileDevice::Permissions p = f.permissions();
|
||||
p |= QFileDevice::ExeOwner | QFileDevice::ExeUser | QFileDevice::ExeGroup | QFileDevice::ExeOther;
|
||||
f.setPermissions(p);
|
||||
}
|
||||
}
|
||||
|
||||
// create tdata/version file
|
||||
tempDir.mkdir(QDir(tempDirPath + qsl("/tdata")).absolutePath());
|
||||
std::wstring versionString = ((version % 1000) ? QString("%1.%2.%3").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000)).arg(int(version % 1000)) : QString("%1.%2").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000))).toStdWString();
|
||||
|
||||
VerInt versionNum = VerInt(version), versionLen = VerInt(versionString.size() * sizeof(VerChar));
|
||||
VerChar versionStr[32];
|
||||
memcpy(versionStr, versionString.c_str(), versionLen);
|
||||
|
||||
QFile fVersion(tempDirPath + qsl("/tdata/version"));
|
||||
if (!fVersion.open(QIODevice::WriteOnly)) {
|
||||
LOG(("Update Error: cant write version file '%1'").arg(tempDirPath + qsl("/version")));
|
||||
return fatalFail();
|
||||
}
|
||||
fVersion.write((const char*)&versionNum, sizeof(VerInt));
|
||||
fVersion.write((const char*)&versionLen, sizeof(VerInt));
|
||||
fVersion.write((const char*)&versionStr[0], versionLen);
|
||||
fVersion.close();
|
||||
}
|
||||
|
||||
QFile readyFile(readyFilePath);
|
||||
if (readyFile.open(QIODevice::WriteOnly)) {
|
||||
if (readyFile.write("1", 1)) {
|
||||
readyFile.close();
|
||||
} else {
|
||||
LOG(("Update Error: cant write ready file '%1'").arg(readyFilePath));
|
||||
return fatalFail();
|
||||
}
|
||||
} else {
|
||||
LOG(("Update Error: cant create ready file '%1'").arg(readyFilePath));
|
||||
return fatalFail();
|
||||
}
|
||||
outputFile.remove();
|
||||
|
||||
emit App::app()->updateReady();
|
||||
}
|
||||
|
||||
UpdateDownloader::~UpdateDownloader() {
|
||||
delete reply;
|
||||
reply = 0;
|
||||
}
|
||||
|
||||
bool checkReadyUpdate() {
|
||||
QString readyFilePath = cWorkingDir() + qsl("tupdates/temp/ready"), readyPath = cWorkingDir() + qsl("tupdates/temp");
|
||||
if (!QFile(readyFilePath).exists()) {
|
||||
if (QDir(cWorkingDir() + qsl("tupdates/ready")).exists() || QDir(cWorkingDir() + qsl("tupdates/temp")).exists()) {
|
||||
UpdateDownloader::clearAll();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// check ready version
|
||||
QString versionPath = readyPath + qsl("/tdata/version");
|
||||
{
|
||||
QFile fVersion(versionPath);
|
||||
if (!fVersion.open(QIODevice::ReadOnly)) {
|
||||
LOG(("Update Error: cant read version file '%1'").arg(versionPath));
|
||||
UpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
VerInt versionNum;
|
||||
if (fVersion.read((char*)&versionNum, sizeof(VerInt)) != sizeof(VerInt)) {
|
||||
LOG(("Update Error: cant read version from file '%1'").arg(versionPath));
|
||||
UpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
fVersion.close();
|
||||
if (versionNum <= AppVersion) {
|
||||
LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
|
||||
UpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QString curUpdater = (cExeDir() + qsl("Updater.exe"));
|
||||
QFileInfo updater(cWorkingDir() + qsl("tupdates/temp/Updater.exe"));
|
||||
#elif defined Q_OS_MAC
|
||||
QString curUpdater = (cExeDir() + cExeName() + qsl("/Contents/Frameworks/Updater"));
|
||||
QFileInfo updater(cWorkingDir() + qsl("tupdates/temp/Telegram.app/Contents/Frameworks/Updater"));
|
||||
#elif defined Q_OS_LINUX
|
||||
QString curUpdater = (cExeDir() + qsl("Updater"));
|
||||
QFileInfo updater(cWorkingDir() + qsl("tupdates/temp/Updater"));
|
||||
#endif
|
||||
if (!updater.exists()) {
|
||||
QFileInfo current(curUpdater);
|
||||
if (!current.exists()) {
|
||||
UpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
|
||||
UpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) {
|
||||
DWORD errorCode = GetLastError();
|
||||
if (errorCode == ERROR_ACCESS_DENIED) { // we are in write-protected dir, like Program Files
|
||||
cSetWriteProtected(true);
|
||||
return true;
|
||||
} else {
|
||||
UpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
|
||||
UpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
#elif defined Q_OS_MAC
|
||||
QDir().mkpath(QFileInfo(curUpdater).absolutePath());
|
||||
DEBUG_LOG(("Update Info: moving %1 to %2..").arg(updater.absoluteFilePath()).arg(curUpdater));
|
||||
if (!objc_moveFile(updater.absoluteFilePath(), curUpdater)) {
|
||||
UpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
#elif defined Q_OS_LINUX
|
||||
if (!linuxMoveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) {
|
||||
UpdateDownloader::clearAll();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
62
Telegram/SourceFiles/autoupdater.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
class UpdateDownloader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
UpdateDownloader(QThread *thread, const QString &url);
|
||||
|
||||
void unpackUpdate();
|
||||
|
||||
int32 ready();
|
||||
int32 size();
|
||||
|
||||
static void clearAll();
|
||||
|
||||
~UpdateDownloader();
|
||||
|
||||
public slots:
|
||||
|
||||
void start();
|
||||
void partMetaGot();
|
||||
void partFinished(qint64 got, qint64 total);
|
||||
void partFailed(QNetworkReply::NetworkError e);
|
||||
void sendRequest();
|
||||
|
||||
private:
|
||||
void initOutput();
|
||||
|
||||
void fatalFail();
|
||||
|
||||
QString updateUrl;
|
||||
QNetworkAccessManager manager;
|
||||
QNetworkReply *reply;
|
||||
int32 already, full;
|
||||
QFile outputFile;
|
||||
|
||||
QMutex mutex;
|
||||
|
||||
};
|
||||
|
||||
bool checkReadyUpdate();
|
||||
@@ -71,7 +71,7 @@ void AbstractBox::paintTitle(Painter &p, const QString &title, bool withShadow)
|
||||
// paint box title
|
||||
p.setFont(st::boxTitleFont->f);
|
||||
p.setPen(st::black->p);
|
||||
p.drawTextLeft(st::boxTitlePos.x(), st::boxTitlePos.y(), width() - 2 * st::boxTitlePos.x(), title);
|
||||
p.drawTextLeft(st::boxTitlePos.x(), st::boxTitlePos.y(), width(), title);
|
||||
}
|
||||
|
||||
void AbstractBox::paintGrayTitle(QPainter &p, const QString &title) {
|
||||
@@ -106,8 +106,10 @@ void AbstractBox::setMaxHeight(int32 maxHeight) {
|
||||
|
||||
void AbstractBox::resizeMaxHeight(int32 newWidth, int32 maxHeight) {
|
||||
if (width() != newWidth || _maxHeight != maxHeight) {
|
||||
QRect g(geometry());
|
||||
_maxHeight = maxHeight;
|
||||
resize(newWidth, countHeight());
|
||||
if (parentWidget()) parentWidget()->update(geometry().united(g).marginsAdded(QMargins(st::boxShadow.pxWidth(), st::boxShadow.pxHeight(), st::boxShadow.pxWidth(), st::boxShadow.pxHeight())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ _bgCount(0), _rows(0), _over(-1), _overDown(-1) {
|
||||
void BackgroundInner::gotWallpapers(const MTPVector<MTPWallPaper> &result) {
|
||||
App::WallPapers wallpapers;
|
||||
|
||||
wallpapers.push_back(App::WallPaper(0, ImagePtr(st::msgBG), ImagePtr(st::msgBG)));
|
||||
wallpapers.push_back(App::WallPaper(0, ImagePtr(st::msgBG0), ImagePtr(st::msgBG0)));
|
||||
const QVector<MTPWallPaper> &v(result.c_vector().v);
|
||||
for (int i = 0, l = v.size(); i < l; ++i) {
|
||||
const MTPWallPaper w(v.at(i));
|
||||
@@ -78,7 +78,7 @@ void BackgroundInner::gotWallpapers(const MTPVector<MTPWallPaper> &result) {
|
||||
}
|
||||
}
|
||||
if (thumb && full && full->type() != mtpc_photoSizeEmpty) {
|
||||
wallpapers.push_back(App::WallPaper(d.vid.v, App::image(*thumb), App::image(*full)));
|
||||
wallpapers.push_back(App::WallPaper(d.vid.v ? d.vid.v : INT_MAX, App::image(*thumb), App::image(*full)));
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
@@ -68,12 +68,12 @@ namespace {
|
||||
const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace), replacesInRow = 7;
|
||||
}
|
||||
|
||||
EmojiBox::EmojiBox() : _done(this, lang(lng_about_done), st::aboutCloseButton) {
|
||||
EmojiBox::EmojiBox() : _esize(EmojiSizes[EIndex + 1]), _done(this, lang(lng_about_done), st::aboutCloseButton) {
|
||||
fillBlocks();
|
||||
|
||||
_blockHeight = st::emojiReplaceInnerHeight;
|
||||
|
||||
resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + (st::emojiReplaceWidth - st::emojiSize), st::boxPadding.top() + st::boxFont->height + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + _done.height());
|
||||
resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + (st::emojiReplaceWidth - _esize), st::boxPadding.top() + st::boxFont->height + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + _done.height());
|
||||
|
||||
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
|
||||
|
||||
@@ -84,7 +84,21 @@ void EmojiBox::fillBlocks() {
|
||||
BlockRow currentRow;
|
||||
currentRow.reserve(replacesInRow);
|
||||
for (uint32 i = 0; i < replacesCount; ++i) {
|
||||
Block block(getEmoji(replaces[i].code), QString::fromUtf8(replaces[i].replace));
|
||||
EmojiPtr emoji = emojiGet(replaces[i].code);
|
||||
if (!emoji || emoji == TwoSymbolEmoji) continue;
|
||||
if (emoji->color) {
|
||||
EmojiColorVariants::const_iterator it = cEmojiVariants().constFind(emoji->code);
|
||||
if (it != cEmojiVariants().cend()) {
|
||||
EmojiPtr replace = emojiFromKey(it.value());
|
||||
if (replace) {
|
||||
if (replace != TwoSymbolEmoji && replace->code == emoji->code && replace->code2 == emoji->code2) {
|
||||
emoji = replace;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Block block(emoji, QString::fromUtf8(replaces[i].replace));
|
||||
currentRow.push_back(block);
|
||||
if (uint32(currentRow.size()) == replacesInRow) {
|
||||
_blocks.push_back(currentRow);
|
||||
@@ -125,8 +139,7 @@ void EmojiBox::paintEvent(QPaintEvent *e) {
|
||||
int32 rowSize = i->size(), left = (width() - rowSize * st::emojiReplaceWidth) / 2;
|
||||
for (BlockRow::const_iterator j = i->cbegin(), en = i->cend(); j != en; ++j) {
|
||||
if (j->emoji) {
|
||||
QPoint pos(left + (st::emojiReplaceWidth - st::emojiSize) / 2, top + (st::emojiReplaceHeight - _blockHeight) / 2);
|
||||
p.drawPixmap(pos, App::emojis(), QRect(j->emoji->x, j->emoji->y, st::emojiImgSize, st::emojiImgSize));
|
||||
p.drawPixmap(QPoint(left + (st::emojiReplaceWidth - _esize) / 2, top + (st::emojiReplaceHeight - _blockHeight) / 2), App::emojisLarge(), QRect(j->emoji->x * _esize, j->emoji->y * _esize, _esize, _esize));
|
||||
}
|
||||
QRect trect(left, top + (st::emojiReplaceHeight + _blockHeight) / 2 - st::emojiTextFont->height, st::emojiReplaceWidth, st::emojiTextFont->height);
|
||||
p.drawText(trect, j->text, QTextOption(Qt::AlignHCenter | Qt::AlignTop));
|
||||
|
||||
@@ -38,6 +38,7 @@ private:
|
||||
|
||||
void fillBlocks();
|
||||
|
||||
int32 _esize;
|
||||
BottomButton _done;
|
||||
|
||||
int32 _blockHeight;
|
||||
|
||||
@@ -147,8 +147,7 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
int32 x = (width() - w) / 2, y = st::boxPadding.top() * 2 + st::boxFont->height;
|
||||
|
||||
p.fillRect(QRect(x, y, w, h), st::msgOutBG->b);
|
||||
p.fillRect(x, y + h, w, st::msgShadow, st::msgOutShadow->b);
|
||||
App::roundRect(p, x, y, w, h, st::msgOutBg, MessageOutCorners, &st::msgOutShadow);
|
||||
if (_thumbw) {
|
||||
int32 rf(cIntRetinaFactor());
|
||||
p.drawPixmap(QPoint(x + st::mediaPadding.left(), y + st::mediaPadding.top()), _thumb, QRect(_thumbx * rf, _thumby * rf, st::mediaThumbSize * rf, st::mediaThumbSize * rf));
|
||||
|
||||
@@ -37,7 +37,7 @@ void SessionsInner::paintEvent(QPaintEvent *e) {
|
||||
p.fillRect(r, st::white->b);
|
||||
p.setFont(st::linkFont->f);
|
||||
int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPos.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip;
|
||||
int32 w = width() - 2 * x, availw = width() - 2 * xact;
|
||||
int32 w = width();
|
||||
int32 from = (r.top() >= 0) ? qFloor(r.top() / st::sessionHeight) : 0, count = _list->size();
|
||||
if (from < count) {
|
||||
int32 to = (r.bottom() >= 0 ? qFloor(r.bottom() / st::sessionHeight) : 0) + 1;
|
||||
@@ -52,7 +52,7 @@ void SessionsInner::paintEvent(QPaintEvent *e) {
|
||||
|
||||
p.setFont(st::sessionActiveFont->f);
|
||||
p.setPen(st::sessionActiveColor->p);
|
||||
p.drawTextRight(xact, st::sessionPadding.top(), availw, auth.active, auth.activeWidth);
|
||||
p.drawTextRight(xact, st::sessionPadding.top(), w, auth.active, auth.activeWidth);
|
||||
|
||||
p.setFont(st::sessionInfoFont->f);
|
||||
p.setPen(st::black->p);
|
||||
@@ -132,7 +132,7 @@ void SessionsInner::listUpdated() {
|
||||
j = _terminateButtons.insert(_list->at(i).hash, new IconedButton(this, st::sessionTerminate));
|
||||
connect(j.value(), SIGNAL(clicked()), this, SLOT(onTerminate()));
|
||||
}
|
||||
j.value()->moveToRight(st::sessionTerminateSkip, i * st::sessionHeight + st::sessionTerminateTop, width() - 2 * st::sessionTerminateSkip);
|
||||
j.value()->moveToRight(st::sessionTerminateSkip, i * st::sessionHeight + st::sessionTerminateTop, width());
|
||||
}
|
||||
for (TerminateButtons::iterator i = _terminateButtons.begin(); i != _terminateButtons.cend();) {
|
||||
if (i.value()->y() >= 0) {
|
||||
@@ -175,7 +175,7 @@ _terminateAll(this, lang(lng_sessions_terminate_all)), _terminateBox(0), _shortP
|
||||
void SessionsBox::resizeEvent(QResizeEvent *e) {
|
||||
ScrollableBox::resizeEvent(e);
|
||||
_done.move(0, height() - _done.height());
|
||||
_terminateAll.moveToRight(st::sessionPadding.left(), st::boxTitleHeight + st::sessionHeight + st::boxTitlePos.y() + st::boxTitleFont->ascent - st::linkFont->ascent, width() - 2 * st::sessionPadding.left());
|
||||
_terminateAll.moveToRight(st::sessionPadding.left(), st::boxTitleHeight + st::sessionHeight + st::boxTitlePos.y() + st::boxTitleFont->ascent - st::linkFont->ascent, width());
|
||||
}
|
||||
|
||||
void SessionsBox::hideAll() {
|
||||
@@ -212,7 +212,7 @@ void SessionsBox::paintEvent(QPaintEvent *e) {
|
||||
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center);
|
||||
} else {
|
||||
int32 x = st::sessionPadding.left();
|
||||
int32 w = width() - x - st::sessionPadding.right();
|
||||
int32 w = width();
|
||||
|
||||
p.setFont(st::sessionNameFont->f);
|
||||
p.setPen(st::black->p);
|
||||
|
||||
293
Telegram/SourceFiles/boxes/stickersetbox.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "lang.h"
|
||||
|
||||
#include "stickersetbox.h"
|
||||
#include "mainwidget.h"
|
||||
#include "window.h"
|
||||
#include "settingswidget.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
|
||||
#include "localstorage.h"
|
||||
|
||||
StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) :
|
||||
_loaded(false), _setId(0), _setAccess(0), _bottom(0),
|
||||
_input(set), _installRequest(0) {
|
||||
switch (set.type()) {
|
||||
case mtpc_inputStickerSetID: _setId = set.c_inputStickerSetID().vid.v; _setAccess = set.c_inputStickerSetID().vaccess_hash.v; break;
|
||||
case mtpc_inputStickerSetShortName: _setShortName = qs(set.c_inputStickerSetShortName().vshort_name); break;
|
||||
}
|
||||
MTP::send(MTPmessages_GetStickerSet(_input), rpcDone(&StickerSetInner::gotSet), rpcFail(&StickerSetInner::failedSet));
|
||||
cSetLastStickersUpdate(0);
|
||||
App::main()->updateStickers();
|
||||
}
|
||||
|
||||
void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
|
||||
_pack.clear();
|
||||
if (set.type() == mtpc_messages_stickerSet) {
|
||||
const MTPDmessages_stickerSet &d(set.c_messages_stickerSet());
|
||||
const QVector<MTPDocument> &v(d.vdocuments.c_vector().v);
|
||||
_pack.reserve(v.size());
|
||||
for (int32 i = 0, l = v.size(); i < l; ++i) {
|
||||
DocumentData *doc = App::feedDocument(v.at(i));
|
||||
if (!doc || !doc->sticker) continue;
|
||||
|
||||
_pack.push_back(doc);
|
||||
}
|
||||
if (d.vset.type() == mtpc_stickerSet) {
|
||||
const MTPDstickerSet &s(d.vset.c_stickerSet());
|
||||
_setTitle = qs(s.vtitle);
|
||||
_title = st::boxTitleFont->m.elidedText(_setTitle, Qt::ElideRight, width() - st::btnStickersClose.width - st::boxTitlePos.x());
|
||||
_setShortName = qs(s.vshort_name);
|
||||
_setId = s.vid.v;
|
||||
_setAccess = s.vaccess_hash.v;
|
||||
}
|
||||
}
|
||||
|
||||
if (_pack.isEmpty() || _setShortName.isEmpty()) {
|
||||
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_not_found), true), true);
|
||||
} else {
|
||||
int32 rows = _pack.size() / StickerPanPerRow + ((_pack.size() % StickerPanPerRow) ? 1 : 0);
|
||||
resize(st::stickersPadding + StickerPanPerRow * st::stickersSize.width(), rows * st::stickersSize.height() + st::stickersAddOrShare);
|
||||
}
|
||||
_loaded = true;
|
||||
|
||||
emit updateButtons();
|
||||
}
|
||||
|
||||
bool StickerSetInner::failedSet(const RPCError &error) {
|
||||
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
|
||||
|
||||
_loaded = true;
|
||||
|
||||
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_not_found), true), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StickerSetInner::installDone(const MTPBool &result) {
|
||||
StickerSets &sets(cRefStickerSets());
|
||||
|
||||
sets.insert(_setId, StickerSet(_setId, _setAccess, _setTitle, _setShortName)).value().stickers = _pack;
|
||||
int32 index = cStickerSetsOrder().indexOf(_setId);
|
||||
if (index > 0) {
|
||||
cRefStickerSetsOrder().removeAt(index);
|
||||
cRefStickerSetsOrder().push_front(_setId);
|
||||
} else if (index < 0) {
|
||||
cRefStickerSetsOrder().push_front(_setId);
|
||||
}
|
||||
|
||||
StickerSets::iterator custom = sets.find(CustomStickerSetId);
|
||||
if (custom != sets.cend()) {
|
||||
for (int32 i = 0, l = _pack.size(); i < l; ++i) {
|
||||
custom->stickers.removeOne(_pack.at(i));
|
||||
}
|
||||
if (custom->stickers.isEmpty()) {
|
||||
sets.erase(custom);
|
||||
}
|
||||
}
|
||||
cSetStickersHash(QByteArray());
|
||||
Local::writeStickers();
|
||||
emit installed(_setId);
|
||||
App::wnd()->hideLayer();
|
||||
}
|
||||
|
||||
bool StickerSetInner::installFailed(const RPCError &error) {
|
||||
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
|
||||
|
||||
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_not_found), true), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StickerSetInner::paintEvent(QPaintEvent *e) {
|
||||
QRect r(e->rect());
|
||||
Painter p(this);
|
||||
|
||||
if (_pack.isEmpty()) return;
|
||||
|
||||
int32 rows = _pack.size() / StickerPanPerRow + ((_pack.size() % StickerPanPerRow) ? 1 : 0);
|
||||
int32 from = qFloor(e->rect().top() / st::stickersSize.height()), to = qFloor(e->rect().bottom() / st::stickersSize.height()) + 1;
|
||||
|
||||
for (int32 i = from; i < to; ++i) {
|
||||
for (int32 j = 0; j < StickerPanPerRow; ++j) {
|
||||
int32 index = i * StickerPanPerRow + j;
|
||||
if (index >= _pack.size()) break;
|
||||
|
||||
DocumentData *doc = _pack.at(index);
|
||||
QPoint pos(st::stickerPanPadding + j * st::stickersSize.width(), i * st::stickersSize.height());
|
||||
|
||||
bool goodThumb = !doc->thumb->isNull() && ((doc->thumb->width() >= 128) || (doc->thumb->height() >= 128));
|
||||
if (goodThumb) {
|
||||
doc->thumb->load();
|
||||
} else {
|
||||
bool already = !doc->already().isEmpty(), hasdata = !doc->data.isEmpty();
|
||||
if (!doc->loader && doc->status != FileFailed && !already && !hasdata) {
|
||||
doc->save(QString());
|
||||
}
|
||||
if (doc->sticker->img->isNull() && (already || hasdata)) {
|
||||
if (already) {
|
||||
doc->sticker->img = ImagePtr(doc->already());
|
||||
} else {
|
||||
doc->sticker->img = ImagePtr(doc->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float64 coef = qMin((st::stickersSize.width() - st::msgRadius * 2) / float64(doc->dimensions.width()), (st::stickersSize.height() - st::msgRadius * 2) / float64(doc->dimensions.height()));
|
||||
if (coef > 1) coef = 1;
|
||||
int32 w = qRound(coef * doc->dimensions.width()), h = qRound(coef * doc->dimensions.height());
|
||||
if (w < 1) w = 1;
|
||||
if (h < 1) h = 1;
|
||||
QPoint ppos = pos + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
|
||||
if (goodThumb) {
|
||||
p.drawPixmapLeft(ppos, width(), doc->thumb->pix(w, h));
|
||||
} else if (!doc->sticker->img->isNull()) {
|
||||
p.drawPixmapLeft(ppos, width(), doc->sticker->img->pix(w, h));
|
||||
}
|
||||
}
|
||||
}
|
||||
p.fillRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare, st::emojiPanHeaderBg->b);
|
||||
}
|
||||
|
||||
void StickerSetInner::setScrollBottom(int32 bottom) {
|
||||
if (bottom == _bottom) return;
|
||||
|
||||
QRegion upd = QRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare);
|
||||
_bottom = bottom;
|
||||
upd += QRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare);
|
||||
repaint(upd);
|
||||
}
|
||||
|
||||
bool StickerSetInner::loaded() const {
|
||||
return _loaded && !_pack.isEmpty();
|
||||
}
|
||||
|
||||
int32 StickerSetInner::notInstalled() const {
|
||||
return (_loaded && (cStickerSets().constFind(_setId) == cStickerSets().cend())) ? _pack.size() : 0;
|
||||
}
|
||||
|
||||
QString StickerSetInner::title() const {
|
||||
return _loaded ? (_pack.isEmpty() ? lang(lng_attach_failed) : _title) : lang(lng_contacts_loading);
|
||||
}
|
||||
|
||||
QString StickerSetInner::shortName() const {
|
||||
return _setShortName;
|
||||
}
|
||||
|
||||
void StickerSetInner::install() {
|
||||
if (_installRequest) return;
|
||||
_installRequest = MTP::send(MTPmessages_InstallStickerSet(_input), rpcDone(&StickerSetInner::installDone), rpcFail(&StickerSetInner::installFailed));
|
||||
}
|
||||
|
||||
StickerSetInner::~StickerSetInner() {
|
||||
}
|
||||
|
||||
StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st::stickersScroll), _inner(set),
|
||||
_close(this, st::btnStickersClose),
|
||||
_addStickers(this, lng_stickers_add_pack(lt_count, 0), st::btnStickersAdd),
|
||||
_shareStickers(this, lang(lng_stickers_share_pack), st::btnStickersAdd) {
|
||||
resize(st::stickersWidth, height());
|
||||
setMaxHeight(st::stickersMaxHeight);
|
||||
connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated()));
|
||||
|
||||
init(&_inner, 0, st::boxFont->height + st::newGroupNamePadding.top() + st::newGroupNamePadding.bottom());
|
||||
|
||||
connect(&_close, SIGNAL(clicked()), this, SLOT(onClose()));
|
||||
connect(&_addStickers, SIGNAL(clicked()), this, SLOT(onAddStickers()));
|
||||
connect(&_shareStickers, SIGNAL(clicked()), this, SLOT(onShareStickers()));
|
||||
|
||||
connect(&_inner, SIGNAL(updateButtons()), this, SLOT(onUpdateButtons()));
|
||||
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
|
||||
|
||||
connect(&_inner, SIGNAL(installed(uint64)), this, SIGNAL(installed(uint64)));
|
||||
|
||||
onStickersUpdated();
|
||||
|
||||
onScroll();
|
||||
|
||||
prepare();
|
||||
}
|
||||
|
||||
void StickerSetBox::onStickersUpdated() {
|
||||
showAll();
|
||||
}
|
||||
|
||||
void StickerSetBox::onAddStickers() {
|
||||
_inner.install();
|
||||
}
|
||||
|
||||
void StickerSetBox::onShareStickers() {
|
||||
QString url = qsl("https://telegram.me/addstickers/") + _inner.shortName();
|
||||
DEBUG_LOG(("Setting text to clipboard from stickerset box: %1").arg(url));
|
||||
QApplication::clipboard()->setText(url);
|
||||
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_copied), true), true);
|
||||
}
|
||||
|
||||
void StickerSetBox::onUpdateButtons() {
|
||||
if (!_close.isHidden()) showAll();
|
||||
}
|
||||
|
||||
void StickerSetBox::onScroll() {
|
||||
_inner.setScrollBottom(_scroll.scrollTop() + _scroll.height());
|
||||
}
|
||||
|
||||
void StickerSetBox::hideAll() {
|
||||
ScrollableBox::hideAll();
|
||||
_close.hide();
|
||||
_addStickers.hide();
|
||||
_shareStickers.hide();
|
||||
}
|
||||
|
||||
void StickerSetBox::showAll() {
|
||||
ScrollableBox::showAll();
|
||||
_close.show();
|
||||
int32 cnt = _inner.notInstalled();
|
||||
if (_inner.loaded()) {
|
||||
if (_inner.notInstalled()) {
|
||||
_addStickers.setText(lng_stickers_add_pack(lt_count, cnt));
|
||||
_addStickers.show();
|
||||
_addStickers.raise();
|
||||
_shareStickers.hide();
|
||||
} else {
|
||||
_shareStickers.show();
|
||||
_shareStickers.raise();
|
||||
_addStickers.hide();
|
||||
}
|
||||
} else {
|
||||
_addStickers.hide();
|
||||
_shareStickers.hide();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void StickerSetBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
if (paint(p)) return;
|
||||
|
||||
paintTitle(p, _inner.title(), false);
|
||||
}
|
||||
|
||||
void StickerSetBox::resizeEvent(QResizeEvent *e) {
|
||||
ScrollableBox::resizeEvent(e);
|
||||
_inner.resize(width(), _inner.height());
|
||||
_close.moveToRight(0, 0, width());
|
||||
_addStickers.move((width() - _addStickers.width()) / 2, height() - (st::stickersAddOrShare + _addStickers.height()) / 2);
|
||||
_shareStickers.move((width() - _shareStickers.width()) / 2, height() - (st::stickersAddOrShare + _shareStickers.height()) / 2);
|
||||
}
|
||||
100
Telegram/SourceFiles/boxes/stickersetbox.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "abstractbox.h"
|
||||
|
||||
class StickerSetInner : public QWidget, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
StickerSetInner(const MTPInputStickerSet &set);
|
||||
|
||||
void init();
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
bool loaded() const;
|
||||
int32 notInstalled() const;
|
||||
QString title() const;
|
||||
QString shortName() const;
|
||||
|
||||
void setScrollBottom(int32 bottom);
|
||||
void install();
|
||||
|
||||
~StickerSetInner();
|
||||
|
||||
signals:
|
||||
|
||||
void updateButtons();
|
||||
void installed(uint64 id);
|
||||
|
||||
private:
|
||||
|
||||
void gotSet(const MTPmessages_StickerSet &set);
|
||||
bool failedSet(const RPCError &error);
|
||||
|
||||
void installDone(const MTPBool &result);
|
||||
bool installFailed(const RPCError &error);
|
||||
|
||||
StickerPack _pack;
|
||||
bool _loaded;
|
||||
uint64 _setId, _setAccess;
|
||||
QString _title, _setTitle, _setShortName;
|
||||
|
||||
int32 _bottom;
|
||||
MTPInputStickerSet _input;
|
||||
|
||||
mtpRequestId _installRequest;
|
||||
};
|
||||
|
||||
class StickerSetBox : public ScrollableBox, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
StickerSetBox(const MTPInputStickerSet &set);
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
|
||||
public slots:
|
||||
|
||||
void onStickersUpdated();
|
||||
void onAddStickers();
|
||||
void onShareStickers();
|
||||
void onUpdateButtons();
|
||||
|
||||
void onScroll();
|
||||
|
||||
signals:
|
||||
|
||||
void installed(uint64 id);
|
||||
|
||||
protected:
|
||||
|
||||
void hideAll();
|
||||
void showAll();
|
||||
|
||||
private:
|
||||
|
||||
StickerSetInner _inner;
|
||||
IconedButton _close;
|
||||
FlatButton _addStickers, _shareStickers;
|
||||
};
|
||||
@@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
static const int32 AppVersion = 8004;
|
||||
static const wchar_t *AppVersionStr = L"0.8.4";
|
||||
static const bool DevChannel = false;
|
||||
static const int32 AppVersion = 8023;
|
||||
static const wchar_t *AppVersionStr = L"0.8.23";
|
||||
static const bool DevChannel = true;
|
||||
|
||||
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
|
||||
static const wchar_t *AppName = L"Telegram Desktop";
|
||||
@@ -40,6 +40,8 @@ enum {
|
||||
MTPContainerLives = 600, // container lives 10 minutes in haveSent map
|
||||
MTPMinReceiveDelay = 4000, // 4 seconds
|
||||
MTPMaxReceiveDelay = 64000, // 64 seconds
|
||||
MTPMinConnectDelay = 1000, // tcp connect should take less then 1 second
|
||||
MTPMaxConnectDelay = 8000, // tcp connect should take 8 seconds max
|
||||
MTPConnectionOldTimeout = 192000, // 192 seconds
|
||||
MTPTcpConnectionWaitTimeout = 3000, // 3 seconds waiting for tcp, until we accept http
|
||||
MTPMillerRabinIterCount = 30, // 30 Miller-Rabin iterations for dh_prime primality check
|
||||
@@ -86,12 +88,16 @@ enum {
|
||||
AudioCheckPositionDelta = 4800, // update position called each 4800 samples
|
||||
AudioFadeTimeout = 10, // 10ms
|
||||
AudioFadeDuration = 500,
|
||||
AudioVoiceMsgSkip = 400, // 200ms
|
||||
AudioVoiceMsgFade = 300, // 300ms
|
||||
AudioPreloadSamples = 5 * 48000, // preload next part if less than 5 seconds remains
|
||||
AudioVoiceMsgFrequency = 48000, // 48 kHz
|
||||
AudioVoiceMsgMaxLength = 100 * 60, // 100 minutes
|
||||
AudioVoiceMsgUpdateView = 100, // 100ms
|
||||
AudioVoiceMsgChannels = 2, // stereo
|
||||
AudioVoiceMsgBufferSize = 1024 * 1024, // 1 Mb buffers
|
||||
AudioVoiceMsgInMemory = 1024 * 1024, // 1 Mb audio is hold in memory and auto loaded
|
||||
AudioSuspendTimeout = 3000, // suspend in 3 secs after playing is over
|
||||
AudioPauseDeviceTimeout = 3000, // pause in 3 secs after playing is over
|
||||
|
||||
StickerInMemory = 256 * 1024, // 128 Kb stickers hold in memory, auto loaded and displayed inline
|
||||
StickerMaxSize = 2048, // 2048x2048 is a max image size for sticker
|
||||
@@ -101,9 +107,10 @@ enum {
|
||||
ZoomToScreenLevel = 1024, // just constant
|
||||
|
||||
PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request
|
||||
EmojiPadPerRow = 7,
|
||||
EmojiPadRowsPerPage = 6,
|
||||
StickerPadPerRow = 3,
|
||||
EmojiPanPerRow = 7,
|
||||
EmojiPanRowsPerPage = 6,
|
||||
StickerPanPerRow = 5,
|
||||
StickerPanRowsPerPage = 4,
|
||||
StickersUpdateTimeout = 3600000, // update not more than once in an hour
|
||||
|
||||
SearchPeopleLimit = 5,
|
||||
@@ -255,6 +262,8 @@ static const char *DefaultCountry = "US";
|
||||
static const char *DefaultLanguage = "en";
|
||||
|
||||
enum {
|
||||
DefaultChatBackground = 21,
|
||||
|
||||
DialogsFirstLoad = 20, // first dialogs part size requested
|
||||
DialogsPerPage = 40, // next dialogs part size
|
||||
|
||||
@@ -279,8 +288,8 @@ enum {
|
||||
UploadRequestInterval = 500, // one part each half second, if not uploaded faster
|
||||
|
||||
MaxPhotosInMemory = 50, // try to clear some memory after 50 photos are created
|
||||
NoUpdatesTimeout = 180 * 1000, // if nothing is received in 3 min we getDifference
|
||||
NoUpdatesAfterSleepTimeout = 60 * 1000, // if nothing is received in 1 min when was a sleepmode we getDifference
|
||||
NoUpdatesTimeout = 60 * 1000, // if nothing is received in 1 min we ping
|
||||
NoUpdatesAfterSleepTimeout = 60 * 1000, // if nothing is received in 1 min when was a sleepmode we ping
|
||||
WaitForSkippedTimeout = 1000, // 1s wait for skipped seq or pts in updates
|
||||
|
||||
MemoryForImageCache = 64 * 1024 * 1024, // after 64mb of unpacked images we try to clear some memory
|
||||
|
||||
@@ -67,11 +67,11 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||
QRect r(e->rect());
|
||||
bool trivial = (rect() == r);
|
||||
|
||||
QPainter p(this);
|
||||
Painter p(this);
|
||||
if (!trivial) {
|
||||
p.setClipRect(r);
|
||||
}
|
||||
|
||||
|
||||
if (_state == DefaultState) {
|
||||
int32 otherStart = dialogs.list.count * st::dlgHeight;
|
||||
PeerData *active = App::main()->activePeer(), *selected = sel ? sel->history->peer : 0;
|
||||
@@ -1122,7 +1122,7 @@ void DialogsListWidget::saveRecentHashtags(const QString &text) {
|
||||
}
|
||||
}
|
||||
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
|
||||
Local::readRecentStickers();
|
||||
Local::readRecentHashtags();
|
||||
recent = cRecentSearchHashtags();
|
||||
}
|
||||
found = true;
|
||||
|
||||
@@ -25,25 +25,32 @@ class Dropdown : public TWidget, public Animated {
|
||||
|
||||
public:
|
||||
|
||||
Dropdown(QWidget *parent);
|
||||
Dropdown(QWidget *parent, const style::dropdown &st = st::dropdownDef);
|
||||
|
||||
IconedButton *addButton(IconedButton *button);
|
||||
void resetButtons();
|
||||
void updateButtons();
|
||||
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
void enterEvent(QEvent *e);
|
||||
void leaveEvent(QEvent *e);
|
||||
void keyPressEvent(QKeyEvent *e);
|
||||
void otherEnter();
|
||||
void otherLeave();
|
||||
|
||||
void fastHide();
|
||||
void ignoreShow(bool ignore = true);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *e);
|
||||
|
||||
signals:
|
||||
|
||||
void hiding();
|
||||
|
||||
public slots:
|
||||
|
||||
void hideStart();
|
||||
@@ -52,13 +59,21 @@ public slots:
|
||||
void showStart();
|
||||
void onWndActiveChanged();
|
||||
|
||||
void buttonStateChanged(int oldState, ButtonStateChangeSource source);
|
||||
|
||||
private:
|
||||
|
||||
void adjustButtons();
|
||||
|
||||
bool _ignore;
|
||||
|
||||
typedef QVector<IconedButton*> Buttons;
|
||||
Buttons _buttons;
|
||||
|
||||
int32 _selected;
|
||||
|
||||
const style::dropdown &_st;
|
||||
|
||||
int32 _width, _height;
|
||||
bool _hiding;
|
||||
|
||||
@@ -117,7 +132,70 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class EmojiPanInner : public QWidget, public Animated {
|
||||
static const int EmojiColorsCount = 5;
|
||||
|
||||
class EmojiColorPicker : public TWidget, public Animated {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
EmojiColorPicker(QWidget *parent);
|
||||
|
||||
void showEmoji(uint32 code);
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void enterEvent(QEvent *e);
|
||||
void leaveEvent(QEvent *e);
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
void showStart();
|
||||
|
||||
void clearSelection(bool fast = false);
|
||||
|
||||
public slots:
|
||||
|
||||
void hideStart(bool fast = false);
|
||||
|
||||
signals:
|
||||
|
||||
void emojiSelected(EmojiPtr emoji);
|
||||
void hidden();
|
||||
|
||||
private:
|
||||
|
||||
void drawVariant(Painter &p, int variant);
|
||||
|
||||
void updateSelected();
|
||||
|
||||
bool _ignoreShow;
|
||||
|
||||
EmojiPtr _variants[EmojiColorsCount + 1];
|
||||
|
||||
typedef QMap<int32, uint64> EmojiAnimations; // index - showing, -index - hiding
|
||||
EmojiAnimations _emojiAnimations;
|
||||
|
||||
float64 _hovers[EmojiColorsCount + 1];
|
||||
|
||||
int32 _selected, _pressedSel;
|
||||
QPoint _lastMousePos;
|
||||
|
||||
bool _hiding;
|
||||
QPixmap _cache;
|
||||
|
||||
anim::fvalue a_opacity;
|
||||
|
||||
QTimer _hideTimer;
|
||||
|
||||
BoxShadow _shadow;
|
||||
|
||||
};
|
||||
|
||||
static const int32 SwitcherSelected = (INT_MAX / 2);
|
||||
|
||||
class EmojiPanInner : public TWidget, public Animated {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -130,39 +208,147 @@ public:
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
void leaveEvent(QEvent *e);
|
||||
void leaveToChildEvent(QEvent *e);
|
||||
void enterFromChildEvent(QEvent *e);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
void hideFinish();
|
||||
|
||||
void showEmojiPack(DBIEmojiTab packIndex);
|
||||
|
||||
void clearSelection(bool fast = false);
|
||||
|
||||
DBIEmojiTab currentTab(int yOffset) const;
|
||||
|
||||
void refreshRecent();
|
||||
|
||||
void setScrollTop(int top);
|
||||
|
||||
public slots:
|
||||
|
||||
void updateSelected();
|
||||
void onSaveConfig();
|
||||
|
||||
void onShowPicker();
|
||||
void onPickerHidden();
|
||||
void onColorSelected(EmojiPtr emoji);
|
||||
|
||||
signals:
|
||||
|
||||
void emojiSelected(EmojiPtr emoji);
|
||||
void stickerSelected(DocumentData *sticker);
|
||||
void selected(EmojiPtr emoji);
|
||||
|
||||
void switchToStickers();
|
||||
|
||||
void scrollToY(int y);
|
||||
void disableScroll(bool dis);
|
||||
|
||||
private:
|
||||
|
||||
typedef QMap<int32, uint64> EmojiAnimations; // index - showing, -index - hiding
|
||||
EmojiAnimations _emojiAnimations;
|
||||
int32 countHeight();
|
||||
void selectEmoji(EmojiPtr emoji);
|
||||
|
||||
StickerPack _stickers;
|
||||
QVector<bool> _isUserGen;
|
||||
QVector<EmojiPtr> _emojis;
|
||||
QVector<float64> _hovers;
|
||||
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
|
||||
Animations _animations;
|
||||
|
||||
DBIEmojiTab _tab;
|
||||
int32 _selected, _xSelected, _pressedSel, _xPressedSel;
|
||||
int32 _top, _counts[emojiTabCount];
|
||||
|
||||
QVector<EmojiPtr> _emojis[emojiTabCount];
|
||||
QVector<float64> _hovers[emojiTabCount];
|
||||
|
||||
int32 _esize;
|
||||
|
||||
int32 _selected, _pressedSel, _pickerSel;
|
||||
QPoint _lastMousePos;
|
||||
|
||||
QTimer _saveConfigTimer;
|
||||
|
||||
EmojiColorPicker _picker;
|
||||
QTimer _showPickerTimer;
|
||||
|
||||
float64 _switcherHover;
|
||||
int32 _stickersWidth;
|
||||
};
|
||||
|
||||
struct StickerIcon {
|
||||
StickerIcon() : setId(RecentStickerSetId), sticker(0), pixw(0), pixh(0) {
|
||||
}
|
||||
StickerIcon(uint64 setId, DocumentData *sticker, int32 pixw, int32 pixh) : setId(setId), sticker(sticker), pixw(pixw), pixh(pixh) {
|
||||
}
|
||||
uint64 setId;
|
||||
DocumentData *sticker;
|
||||
int32 pixw, pixh;
|
||||
};
|
||||
|
||||
class StickerPanInner : public TWidget, public Animated {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
StickerPanInner(QWidget *parent = 0);
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
void leaveEvent(QEvent *e);
|
||||
void leaveToChildEvent(QEvent *e);
|
||||
void enterFromChildEvent(QEvent *e);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
|
||||
void showStickerSet(uint64 setId);
|
||||
|
||||
void clearSelection(bool fast = false);
|
||||
|
||||
void refreshStickers();
|
||||
void refreshRecent(bool resize = true);
|
||||
|
||||
void fillIcons(QVector<StickerIcon> &icons);
|
||||
|
||||
void setScrollTop(int top);
|
||||
void preloadImages();
|
||||
|
||||
uint64 currentSet(int yOffset) const;
|
||||
|
||||
public slots:
|
||||
|
||||
void updateSelected();
|
||||
|
||||
signals:
|
||||
|
||||
void selected(DocumentData *sticker);
|
||||
void removing(uint64 setId);
|
||||
|
||||
void refreshIcons();
|
||||
|
||||
void switchToEmoji();
|
||||
|
||||
void scrollToY(int y);
|
||||
void disableScroll(bool dis);
|
||||
|
||||
private:
|
||||
|
||||
void appendSet(uint64 setId);
|
||||
|
||||
int32 countHeight();
|
||||
void selectEmoji(EmojiPtr emoji);
|
||||
|
||||
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
|
||||
Animations _animations;
|
||||
|
||||
int32 _top;
|
||||
|
||||
QList<QString> _titles;
|
||||
QList<uint64> _setIds;
|
||||
QList<StickerPack> _sets;
|
||||
QList<QVector<float64> > _hovers;
|
||||
|
||||
int32 _selected, _pressedSel;
|
||||
QPoint _lastMousePos;
|
||||
|
||||
float64 _switcherHover;
|
||||
int32 _emojiWidth;
|
||||
};
|
||||
|
||||
class EmojiPan : public TWidget, public Animated {
|
||||
@@ -179,6 +365,12 @@ public:
|
||||
void otherEnter();
|
||||
void otherLeave();
|
||||
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
|
||||
bool event(QEvent *e);
|
||||
|
||||
void fastHide();
|
||||
bool hiding() const {
|
||||
return _hiding || _hideTimer.isActive();
|
||||
@@ -186,10 +378,15 @@ public:
|
||||
|
||||
bool animStep(float64 ms);
|
||||
|
||||
bool iconAnim(float64 ms);
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *e);
|
||||
void stickersInstalled(uint64 setId);
|
||||
|
||||
public slots:
|
||||
|
||||
void refreshStickers();
|
||||
|
||||
void hideStart();
|
||||
void hideFinish();
|
||||
|
||||
@@ -197,6 +394,14 @@ public slots:
|
||||
void onWndActiveChanged();
|
||||
|
||||
void onTabChange();
|
||||
void onScroll();
|
||||
void onSwitch();
|
||||
|
||||
void onRemoveSet(uint64 setId);
|
||||
void onRemoveSetSure();
|
||||
void onDelayedHide();
|
||||
|
||||
void onRefreshIcons();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -206,9 +411,20 @@ signals:
|
||||
|
||||
private:
|
||||
|
||||
bool _horizontal;
|
||||
|
||||
void leaveToChildEvent(QEvent *e);
|
||||
|
||||
void updateSelected();
|
||||
void updateIcons();
|
||||
|
||||
void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab);
|
||||
|
||||
void showAll();
|
||||
void hideAll();
|
||||
|
||||
bool _noTabUpdate;
|
||||
|
||||
int32 _width, _height;
|
||||
bool _hiding;
|
||||
QPixmap _cache;
|
||||
@@ -219,11 +435,32 @@ private:
|
||||
|
||||
BoxShadow _shadow;
|
||||
|
||||
FlatRadiobutton _recent, _people, _nature, _objects, _places, _symbols, _stickers;
|
||||
FlatRadiobutton _recent, _people, _nature, _food, _celebration, _activity, _travel, _objects;
|
||||
QVector<StickerIcon> _icons;
|
||||
QVector<float64> _iconHovers;
|
||||
int32 _iconOver, _iconSel, _iconDown;
|
||||
bool _iconsDragging;
|
||||
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
|
||||
Animations _iconAnimations;
|
||||
Animation _iconAnim;
|
||||
QPoint _iconsMousePos, _iconsMouseDown;
|
||||
int32 _iconsLeft, _iconsTop;
|
||||
int32 _iconsStartX, _iconsMax;
|
||||
anim::ivalue _iconsX;
|
||||
uint64 _iconsStartAnim;
|
||||
|
||||
int32 _emojiPack;
|
||||
ScrollArea _scroll;
|
||||
EmojiPanInner _inner;
|
||||
bool _stickersShown;
|
||||
QPixmap _fromCache, _toCache;
|
||||
anim::ivalue a_fromCoord, a_toCoord;
|
||||
anim::fvalue a_fromAlpha, a_toAlpha;
|
||||
uint64 _moveStart;
|
||||
|
||||
ScrollArea e_scroll;
|
||||
EmojiPanInner e_inner;
|
||||
ScrollArea s_scroll;
|
||||
StickerPanInner s_inner;
|
||||
|
||||
uint64 _removingSetId;
|
||||
|
||||
};
|
||||
|
||||
@@ -336,97 +573,3 @@ private:
|
||||
BoxShadow _shadow;
|
||||
|
||||
};
|
||||
|
||||
//class StickerPanInner : public QWidget, public Animated {
|
||||
// Q_OBJECT
|
||||
//
|
||||
//public:
|
||||
//
|
||||
// StickerPanInner(QWidget *parent = 0);
|
||||
//
|
||||
// void paintEvent(QPaintEvent *e);
|
||||
//
|
||||
// void mousePressEvent(QMouseEvent *e);
|
||||
// void mouseReleaseEvent(QMouseEvent *e);
|
||||
// void mouseMoveEvent(QMouseEvent *e);
|
||||
// void leaveEvent(QEvent *e);
|
||||
//
|
||||
// bool animStep(float64 ms);
|
||||
//
|
||||
// void showStickerPack(EmojiPtr emoji);
|
||||
// bool hasContent() const;
|
||||
//
|
||||
//public slots:
|
||||
//
|
||||
// void updateSelected();
|
||||
//
|
||||
//signals:
|
||||
//
|
||||
// void stickerSelected(DocumentData *sticker);
|
||||
//
|
||||
//private:
|
||||
//
|
||||
// typedef QMap<int32, uint64> StickerAnimations; // index - showing, -index - hiding
|
||||
// StickerAnimations _stickerAnimations;
|
||||
//
|
||||
// StickerPack _stickers;
|
||||
// QVector<float64> _hovers;
|
||||
//
|
||||
// EmojiPtr _emoji;
|
||||
// int32 _selected, _pressedSel;
|
||||
// QPoint _lastMousePos;
|
||||
//
|
||||
//};
|
||||
//
|
||||
//class StickerPan : public TWidget, public Animated {
|
||||
// Q_OBJECT
|
||||
//
|
||||
//public:
|
||||
//
|
||||
// StickerPan(QWidget *parent);
|
||||
//
|
||||
// void setStickerPack(EmojiPtr emoji, bool show);
|
||||
// void paintEvent(QPaintEvent *e);
|
||||
//
|
||||
// void enterEvent(QEvent *e);
|
||||
// void leaveEvent(QEvent *e);
|
||||
// void otherEnter();
|
||||
// void otherLeave();
|
||||
//
|
||||
// void fastHide();
|
||||
//
|
||||
// bool animStep(float64 ms);
|
||||
//
|
||||
// bool eventFilter(QObject *obj, QEvent *e);
|
||||
//
|
||||
//public slots:
|
||||
//
|
||||
// void hideStart();
|
||||
// void hideFinish();
|
||||
//
|
||||
// void showStart();
|
||||
//
|
||||
//signals:
|
||||
//
|
||||
// void stickerSelected(DocumentData *sticker);
|
||||
//
|
||||
//private:
|
||||
//
|
||||
// void showAll();
|
||||
// void hideAll();
|
||||
//
|
||||
// int32 _width, _height;
|
||||
// bool _hiding;
|
||||
// QPixmap _cache;
|
||||
//
|
||||
// anim::fvalue a_opacity;
|
||||
//
|
||||
// QTimer _hideTimer;
|
||||
//
|
||||
// BoxShadow _shadow;
|
||||
//
|
||||
// EmojiPtr _emoji;
|
||||
// ScrollArea _scroll;
|
||||
// StickerPanInner _inner;
|
||||
//
|
||||
//};
|
||||
|
||||
@@ -38,8 +38,12 @@ void FileUploader::uploadMedia(MsgId msgId, const ReadyLocalMedia &media) {
|
||||
}
|
||||
document->status = FileUploading;
|
||||
if (!media.file.isEmpty()) {
|
||||
document->location = FileLocation(mtpc_storage_filePartial, media.file);
|
||||
document->location = FileLocation(StorageFilePartial, media.file);
|
||||
}
|
||||
} else if (media.type == ToPrepareAudio) {
|
||||
AudioData *audio = App::feedAudio(media.audio);
|
||||
audio->status = FileUploading;
|
||||
audio->data = media.data;
|
||||
}
|
||||
queue.insert(msgId, File(media));
|
||||
sendNext();
|
||||
@@ -56,6 +60,12 @@ void FileUploader::currentFailed() {
|
||||
doc->status = FileFailed;
|
||||
}
|
||||
emit documentFailed(j.key());
|
||||
} else if (j->media.type == ToPrepareAudio) {
|
||||
AudioData *audio = App::audio(j->media.id);
|
||||
if (audio->status == FileUploading) {
|
||||
audio->status = FileFailed;
|
||||
}
|
||||
emit audioFailed(j.key());
|
||||
}
|
||||
queue.erase(j);
|
||||
}
|
||||
@@ -112,7 +122,7 @@ void FileUploader::sendNext() {
|
||||
emit photoReady(uploading, MTP_inputFile(MTP_long(i->media.id), MTP_int(i->partsCount), MTP_string(i->media.filename), MTP_string(i->media.jpeg_md5)));
|
||||
} else if (i->media.type == ToPrepareDocument) {
|
||||
QByteArray docMd5(32, Qt::Uninitialized);
|
||||
hashMd5Hex(i->docHash.result(), docMd5.data());
|
||||
hashMd5Hex(i->md5Hash.result(), docMd5.data());
|
||||
|
||||
MTPInputFile doc = (i->docSize > UseBigFilesFrom) ? MTP_inputFileBig(MTP_long(i->media.id), MTP_int(i->docPartsCount), MTP_string(i->media.filename)) : MTP_inputFile(MTP_long(i->media.id), MTP_int(i->docPartsCount), MTP_string(i->media.filename), MTP_string(docMd5));
|
||||
if (i->partsCount) {
|
||||
@@ -120,6 +130,12 @@ void FileUploader::sendNext() {
|
||||
} else {
|
||||
emit documentReady(uploading, doc);
|
||||
}
|
||||
} else if (i->media.type == ToPrepareAudio) {
|
||||
QByteArray audioMd5(32, Qt::Uninitialized);
|
||||
hashMd5Hex(i->md5Hash.result(), audioMd5.data());
|
||||
|
||||
MTPInputFile audio = (i->docSize > UseBigFilesFrom) ? MTP_inputFileBig(MTP_long(i->media.id), MTP_int(i->docPartsCount), MTP_string(i->media.filename)) : MTP_inputFile(MTP_long(i->media.id), MTP_int(i->docPartsCount), MTP_string(i->media.filename), MTP_string(audioMd5));
|
||||
emit audioReady(uploading, audio);
|
||||
}
|
||||
queue.remove(uploading);
|
||||
uploading = 0;
|
||||
@@ -139,12 +155,12 @@ void FileUploader::sendNext() {
|
||||
}
|
||||
toSend = i->docFile->read(i->docPartSize);
|
||||
if (i->docSize <= UseBigFilesFrom) {
|
||||
i->docHash.feed(toSend.constData(), toSend.size());
|
||||
i->md5Hash.feed(toSend.constData(), toSend.size());
|
||||
}
|
||||
} else {
|
||||
toSend = i->media.data.mid(i->docSentParts * i->docPartSize, i->docPartSize);
|
||||
if (i->media.type == ToPrepareDocument && i->docSentParts <= UseBigFilesFrom) {
|
||||
i->docHash.feed(toSend.constData(), toSend.size());
|
||||
if ((i->media.type == ToPrepareDocument || i->media.type == ToPrepareAudio) && i->docSentParts <= UseBigFilesFrom) {
|
||||
i->md5Hash.feed(toSend.constData(), toSend.size());
|
||||
}
|
||||
}
|
||||
if (toSend.size() > i->docPartSize || (toSend.size() < i->docPartSize && i->docSentParts + 1 != i->docPartsCount)) {
|
||||
@@ -249,6 +265,14 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
|
||||
}
|
||||
}
|
||||
emit documentProgress(k.key());
|
||||
} else if (k->media.type == ToPrepareAudio) {
|
||||
AudioData *audio = App::audio(k->media.id);
|
||||
if (audio->status == FileUploading) {
|
||||
audio->uploadOffset = (k->docSentParts - docRequestsSent.size()) * k->docPartSize;
|
||||
if (audio->uploadOffset > audio->size) {
|
||||
audio->uploadOffset = audio->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,19 +45,22 @@ signals:
|
||||
void photoReady(MsgId msgId, const MTPInputFile &file);
|
||||
void documentReady(MsgId msgId, const MTPInputFile &file);
|
||||
void thumbDocumentReady(MsgId msgId, const MTPInputFile &file, const MTPInputFile &thumb);
|
||||
void audioReady(MsgId msgId, const MTPInputFile &file);
|
||||
|
||||
void photoProgress(MsgId msgId);
|
||||
void documentProgress(MsgId msgId);
|
||||
void audioProgress(MsgId msgId);
|
||||
|
||||
void photoFailed(MsgId msgId);
|
||||
void documentFailed(MsgId msgId);
|
||||
void audioFailed(MsgId msgId);
|
||||
|
||||
private:
|
||||
|
||||
struct File {
|
||||
File(const ReadyLocalMedia &media) : media(media), docSentParts(0) {
|
||||
partsCount = media.parts.size();
|
||||
if (media.type == ToPrepareDocument) {
|
||||
if (media.type == ToPrepareDocument || media.type == ToPrepareAudio) {
|
||||
docSize = media.file.isEmpty() ? media.data.size() : media.filesize;
|
||||
if (docSize >= 1024 * 1024 || !setPartSize(DocumentUploadPartSize0)) {
|
||||
if (docSize > 32 * 1024 * 1024 || !setPartSize(DocumentUploadPartSize1)) {
|
||||
@@ -83,12 +86,13 @@ private:
|
||||
ReadyLocalMedia media;
|
||||
int32 partsCount;
|
||||
|
||||
HashMd5 md5Hash;
|
||||
|
||||
QSharedPointer<QFile> docFile;
|
||||
int32 docSentParts;
|
||||
int32 docSize;
|
||||
int32 docPartSize;
|
||||
int32 docPartsCount;
|
||||
HashMd5 docHash;
|
||||
};
|
||||
typedef QMap<MsgId, File> Queue;
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "animation.h"
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "window.h"
|
||||
|
||||
namespace {
|
||||
AnimationManager *manager = 0;
|
||||
@@ -78,6 +80,11 @@ namespace anim {
|
||||
manager->start(obj);
|
||||
}
|
||||
|
||||
void step(Animated *obj) {
|
||||
if (!manager) return;
|
||||
manager->step(obj);
|
||||
}
|
||||
|
||||
void stop(Animated *obj) {
|
||||
if (!manager) return;
|
||||
manager->stop(obj);
|
||||
@@ -94,3 +101,135 @@ namespace anim {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool AnimatedGif::animStep(float64 ms) {
|
||||
int32 f = frame;
|
||||
while (f < images.size() && ms > delays[f]) {
|
||||
++f;
|
||||
if (f == images.size() && images.size() < framesCount) {
|
||||
if (reader->read(&img)) {
|
||||
int64 d = reader->nextImageDelay(), delay = delays[f - 1];
|
||||
if (!d) d = 1;
|
||||
delay += d;
|
||||
if (img.size() != QSize(w, h)) img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
images.push_back(img);
|
||||
frames.push_back(QPixmap());
|
||||
delays.push_back(delay);
|
||||
for (int32 i = 0; i < images.size(); ++i) {
|
||||
if (!images[i].isNull() || !frames[i].isNull()) {
|
||||
images[i] = QImage();
|
||||
frames[i] = QPixmap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
framesCount = images.size();
|
||||
}
|
||||
}
|
||||
if (f == images.size()) {
|
||||
if (!duration) {
|
||||
duration = delays.isEmpty() ? 1 : delays.back();
|
||||
}
|
||||
|
||||
f = 0;
|
||||
for (int32 i = 0, s = delays.size() - 1; i <= s; ++i) {
|
||||
delays[i] += duration;
|
||||
}
|
||||
if (images[f].isNull()) {
|
||||
QString fname = reader->fileName();
|
||||
delete reader;
|
||||
reader = new QImageReader(fname);
|
||||
}
|
||||
}
|
||||
if (images[f].isNull() && reader->read(&img)) {
|
||||
if (img.size() != QSize(w, h)) img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
images[f] = img;
|
||||
frames[f] = QPixmap();
|
||||
}
|
||||
}
|
||||
if (frame != f) {
|
||||
frame = f;
|
||||
if (msg && App::main()) {
|
||||
App::main()->msgUpdated(msg->history()->peer->id, msg);
|
||||
} else {
|
||||
emit updated();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimatedGif::start(HistoryItem *row, const QString &file) {
|
||||
stop();
|
||||
|
||||
reader = new QImageReader(file);
|
||||
if (!reader->canRead() || !reader->supportsAnimation()) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
QSize s = reader->size();
|
||||
w = s.width();
|
||||
h = s.height();
|
||||
framesCount = reader->imageCount();
|
||||
if (!w || !h || !framesCount) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
frames.reserve(framesCount);
|
||||
images.reserve(framesCount);
|
||||
delays.reserve(framesCount);
|
||||
|
||||
int32 sizeLeft = MediaViewImageSizeLimit, delay = 0;
|
||||
for (bool read = reader->read(&img); read; read = reader->read(&img)) {
|
||||
sizeLeft -= w * h * 4;
|
||||
if (img.size() != s) img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
images.push_back(img);
|
||||
frames.push_back(QPixmap());
|
||||
int32 d = reader->nextImageDelay();
|
||||
if (!d) d = 1;
|
||||
delay += d;
|
||||
delays.push_back(delay);
|
||||
if (sizeLeft < 0) break;
|
||||
}
|
||||
|
||||
msg = row;
|
||||
|
||||
anim::start(this);
|
||||
if (msg) {
|
||||
msg->initDimensions();
|
||||
if (App::main()) App::main()->itemResized(msg, true);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimatedGif::stop(bool onItemRemoved) {
|
||||
if (isNull()) return;
|
||||
|
||||
delete reader;
|
||||
reader = 0;
|
||||
HistoryItem *row = msg;
|
||||
msg = 0;
|
||||
frames.clear();
|
||||
images.clear();
|
||||
delays.clear();
|
||||
w = h = frame = framesCount = duration = 0;
|
||||
|
||||
anim::stop(this);
|
||||
if (row && !onItemRemoved) {
|
||||
row->initDimensions();
|
||||
if (App::main()) App::main()->itemResized(row, true);
|
||||
}
|
||||
}
|
||||
|
||||
const QPixmap &AnimatedGif::current(int32 width, int32 height, bool rounded) {
|
||||
if (!width) width = w;
|
||||
if (!height) height = h;
|
||||
if ((frames[frame].isNull() || frames[frame].width() != width || frames[frame].height() != height) && !images[frame].isNull()) {
|
||||
QImage img = images[frame];
|
||||
if (img.width() != width || img.height() != height) img = img.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
if (rounded) imageRound(img);
|
||||
frames[frame] = QPixmap::fromImage(img, Qt::ColorOnly);
|
||||
frames[frame].setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
return frames[frame];
|
||||
}
|
||||
|
||||
@@ -185,6 +185,7 @@ namespace anim {
|
||||
};
|
||||
|
||||
void start(Animated *obj);
|
||||
void step(Animated *obj);
|
||||
void stop(Animated *obj);
|
||||
|
||||
void startManager();
|
||||
@@ -222,6 +223,63 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class AnimationFunc {
|
||||
public:
|
||||
virtual bool animStep(float64 ms) = 0;
|
||||
virtual ~AnimationFunc() {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class AnimationFuncOwned : public AnimationFunc {
|
||||
public:
|
||||
typedef bool (Type::*Method)(float64);
|
||||
|
||||
AnimationFuncOwned(Type *obj, Method method) : _obj(obj), _method(method) {
|
||||
}
|
||||
|
||||
bool animStep(float64 ms) {
|
||||
return (_obj->*_method)(ms);
|
||||
}
|
||||
|
||||
private:
|
||||
Type *_obj;
|
||||
Method _method;
|
||||
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
AnimationFunc *animFunc(Type *obj, typename AnimationFuncOwned<Type>::Method method) {
|
||||
return new AnimationFuncOwned<Type>(obj, method);
|
||||
}
|
||||
|
||||
class Animation : public Animated {
|
||||
public:
|
||||
|
||||
Animation(AnimationFunc *func) : _func(func) {
|
||||
}
|
||||
|
||||
void start() {
|
||||
anim::start(this);
|
||||
}
|
||||
void stop() {
|
||||
anim::stop(this);
|
||||
}
|
||||
|
||||
//Animation
|
||||
bool animStep(float64 ms) {
|
||||
return _func->animStep(ms);
|
||||
}
|
||||
|
||||
~Animation() {
|
||||
delete _func;
|
||||
}
|
||||
|
||||
private:
|
||||
AnimationFunc *_func;
|
||||
|
||||
};
|
||||
|
||||
class AnimationManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -248,6 +306,23 @@ public:
|
||||
obj->animInProcess = true;
|
||||
}
|
||||
|
||||
void step(Animated *obj) {
|
||||
if (iterating) return;
|
||||
|
||||
float64 ms = float64(getms());
|
||||
AnimObjs::iterator i = objs.find(obj);
|
||||
if (i != objs.cend()) {
|
||||
Animated *obj = *i;
|
||||
if (!obj->animStep(ms - obj->animStarted)) {
|
||||
objs.erase(i);
|
||||
if (!objs.size()) {
|
||||
timer.stop();
|
||||
}
|
||||
obj->animInProcess = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stop(Animated *obj) {
|
||||
if (iterating) {
|
||||
toStop.insert(obj);
|
||||
@@ -307,3 +382,46 @@ private:
|
||||
bool iterating;
|
||||
|
||||
};
|
||||
|
||||
class HistoryItem;
|
||||
class AnimatedGif : public QObject, public Animated {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AnimatedGif() : msg(0), reader(0), w(0), h(0), frame(0), framesCount(0), duration(0) {
|
||||
}
|
||||
|
||||
bool animStep(float64 ms);
|
||||
|
||||
void start(HistoryItem *row, const QString &file);
|
||||
void stop(bool onItemRemoved = false);
|
||||
|
||||
bool isNull() const {
|
||||
return !reader;
|
||||
}
|
||||
|
||||
~AnimatedGif() {
|
||||
stop(true);
|
||||
}
|
||||
|
||||
const QPixmap ¤t(int32 width = 0, int32 height = 0, bool rounded = false);
|
||||
|
||||
signals:
|
||||
|
||||
void updated();
|
||||
|
||||
public:
|
||||
|
||||
HistoryItem *msg;
|
||||
QImage img;
|
||||
QImageReader *reader;
|
||||
int32 w, h, frame;
|
||||
|
||||
private:
|
||||
|
||||
QVector<QPixmap> frames;
|
||||
QVector<QImage> images;
|
||||
QVector<int64> delays;
|
||||
int32 framesCount, duration;
|
||||
};
|
||||
|
||||
@@ -19,12 +19,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include "boxshadow.h"
|
||||
|
||||
BoxShadow::BoxShadow(const style::rect &topLeft) : _size(topLeft.width() / cIntRetinaFactor()) {
|
||||
QImage cornersImage(_size * 2, _size * 2, QImage::Format_ARGB32_Premultiplied);
|
||||
BoxShadow::BoxShadow(const style::sprite &topLeft) : _size(topLeft.pxWidth()), _pixsize(_size * cIntRetinaFactor()) {
|
||||
if (!_size) return;
|
||||
|
||||
QImage cornersImage(_pixsize * 2, _pixsize * 2, QImage::Format_ARGB32_Premultiplied);
|
||||
cornersImage.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
QPainter p(&cornersImage);
|
||||
p.drawPixmap(QPoint(0, 0), App::sprite(), topLeft);
|
||||
p.drawPixmap(QPoint(rtl() ? _size : 0, 0), App::sprite(), topLeft);
|
||||
}
|
||||
if (rtl()) cornersImage = cornersImage.mirrored(true, false);
|
||||
uchar *bits = cornersImage.bits();
|
||||
if (bits) {
|
||||
for (
|
||||
@@ -38,37 +42,65 @@ BoxShadow::BoxShadow(const style::rect &topLeft) : _size(topLeft.width() / cIntR
|
||||
{
|
||||
QPainter p(&cornersImage);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.drawImage(0, _size, cornersImage.mirrored(), 0, _size, _size, _size);
|
||||
QImage m = cornersImage.mirrored();
|
||||
m.setDevicePixelRatio(cRetinaFactor());
|
||||
p.drawImage(0, _size, m, 0, _pixsize, _pixsize, _pixsize);
|
||||
}
|
||||
{
|
||||
QPainter p(&cornersImage);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.drawImage(_size, 0, cornersImage.mirrored(true, false), _size, 0, _size, _size * 2);
|
||||
QImage m = cornersImage.mirrored(true, false);
|
||||
m.setDevicePixelRatio(cRetinaFactor());
|
||||
p.drawImage(_size, 0, m, _pixsize, 0, _pixsize, _pixsize * 2);
|
||||
}
|
||||
_corners = QPixmap::fromImage(cornersImage, Qt::ColorOnly);
|
||||
_colors.reserve(_size);
|
||||
_corners.setDevicePixelRatio(cRetinaFactor());
|
||||
_colors.reserve(_pixsize);
|
||||
uchar prev = 0;
|
||||
for (int32 i = 0; i < _size; ++i) {
|
||||
uchar a = (cornersImage.pixel(QPoint(i, _size - 1)) >> 24);
|
||||
for (int32 i = 0; i < _pixsize; ++i) {
|
||||
uchar a = (cornersImage.pixel(QPoint(i, _pixsize - 1)) >> 24);
|
||||
if (a < prev) break;
|
||||
|
||||
_colors.push_back(style::color(0, 0, 0, a));
|
||||
prev = a;
|
||||
}
|
||||
}
|
||||
|
||||
void BoxShadow::paint(QPainter &p, const QRect &box, const QPoint &shift, int32 flags) {
|
||||
int32 count = _colors.size(), minus = _size - count + 1;
|
||||
bool left = (flags & Left), top = (flags & Top), right = (flags & Right), bottom = (flags & Bottom);
|
||||
if (left && top) p.drawPixmap(box.left() - _size + minus + shift.x(), box.top() - _size + minus + shift.y(), _corners, 0, 0, _size, _size);
|
||||
if (right && top) p.drawPixmap(box.right() - minus + 1 + shift.x(), box.top() - _size + minus + shift.y(), _corners, _size, 0, _size, _size);
|
||||
if (right && bottom) p.drawPixmap(box.right() - minus + 1 + shift.x(), box.bottom() - minus + 1 + shift.y(), _corners, _size, _size, _size, _size);
|
||||
if (left && bottom) p.drawPixmap(box.left() - _size + minus + shift.x(), box.bottom() - minus + 1 + shift.y(), _corners, 0, _size, _size, _size);
|
||||
for (int32 i = 1; i <= count; ++i) {
|
||||
p.setPen(_colors[i - 1]->p);
|
||||
if (top) p.fillRect(box.left() + (left ? minus : 0) + shift.x(), box.top() - count + i + shift.y(), box.width() - (right ? minus : 0) - (left ? minus : 0), cIntRetinaFactor(), _colors[i - 1]->b);
|
||||
if (right) p.fillRect(box.right() + count - i + shift.x(), box.top() + (top ? minus : 0) + shift.y(), cIntRetinaFactor(), box.height() - (bottom ? minus : 0) - (top ? minus : 0), _colors[i - 1]->b);
|
||||
if (bottom) p.fillRect(box.left() + (left ? minus : 0) + shift.x(), box.bottom() + count - i + shift.y(), box.width() - (right ? minus : 0) - (left ? minus : 0), cIntRetinaFactor(), _colors[i - 1]->b);
|
||||
if (left) p.fillRect(box.left() - count + i + shift.x(), box.top() + (top ? minus : 0) + shift.y(), cIntRetinaFactor(), box.height() - (bottom ? minus : 0) - (top ? minus : 0), _colors[i - 1]->b);
|
||||
if (cRetina()) {
|
||||
_left = QPixmap::fromImage(cornersImage.copy(0, _pixsize - 1, _colors.size(), 1), Qt::ColorOnly);
|
||||
_left.setDevicePixelRatio(cRetinaFactor());
|
||||
_top = QPixmap::fromImage(cornersImage.copy(_pixsize - 1, 0, 1, _colors.size()), Qt::ColorOnly);
|
||||
_top.setDevicePixelRatio(cRetinaFactor());
|
||||
_right = QPixmap::fromImage(cornersImage.copy(_pixsize * 2 - _colors.size(), _pixsize, _colors.size(), 1), Qt::ColorOnly);
|
||||
_right.setDevicePixelRatio(cRetinaFactor());
|
||||
_bottom = QPixmap::fromImage(cornersImage.copy(_pixsize, _pixsize * 2 - _colors.size(), 1, _colors.size()), Qt::ColorOnly);
|
||||
_bottom.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
}
|
||||
|
||||
void BoxShadow::paint(QPainter &p, const QRect &box, int32 shifty, int32 flags) {
|
||||
if (!_size) return;
|
||||
|
||||
int32 rshifty = shifty * cIntRetinaFactor();
|
||||
int32 count = _colors.size(), countsize = count / cIntRetinaFactor(), minus = _size - countsize + shifty;
|
||||
bool left = (flags & Left), top = (flags & Top), right = (flags & Right), bottom = (flags & Bottom);
|
||||
if (left && top) p.drawPixmap(box.left() - _size + minus, box.top() - _size + minus + shifty, _corners, 0, 0, _pixsize, _pixsize);
|
||||
if (right && top) p.drawPixmap(box.left() + box.width() - minus, box.top() - _size + minus + shifty, _corners, _pixsize, 0, _pixsize, _pixsize);
|
||||
if (right && bottom) p.drawPixmap(box.left() + box.width() - minus, box.top() + box.height() - minus + shifty, _corners, _pixsize, _pixsize, _pixsize, _pixsize);
|
||||
if (left && bottom) p.drawPixmap(box.left() - _size + minus, box.top() + box.height() - minus + shifty, _corners, 0, _pixsize, _pixsize, _pixsize);
|
||||
if (cRetina()) {
|
||||
bool wasSmooth = p.renderHints().testFlag(QPainter::SmoothPixmapTransform);
|
||||
if (wasSmooth) p.setRenderHint(QPainter::SmoothPixmapTransform, false);
|
||||
if (left) p.drawPixmap(box.left() - countsize + shifty, box.top() + (top ? minus : 0) + shifty, countsize - shifty, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _left, 0, 0, count - rshifty, 1);
|
||||
if (top) p.drawPixmap(box.left() + (left ? minus : 0), box.top() - countsize + 2 * shifty, box.width() - (right ? minus : 0) - (left ? minus : 0), countsize - 2 * shifty, _top, 0, 0, 1, count - 2 * rshifty);
|
||||
if (right) p.drawPixmap(box.left() + box.width(), box.top() + (top ? minus : 0) + shifty, countsize - shifty, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _right, rshifty, 0, count - rshifty, 1);
|
||||
if (bottom) p.drawPixmap(box.left() + (left ? minus : 0), box.top() + box.height(), box.width() - (right ? minus : 0) - (left ? minus : 0), countsize, _bottom, 0, 0, 1, count);
|
||||
if (wasSmooth) p.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
} else {
|
||||
p.setPen(Qt::NoPen);
|
||||
for (int32 i = 0; i < count; ++i) {
|
||||
if (left && i + shifty < count) p.fillRect(box.left() - count + i + shifty, box.top() + (top ? minus : 0) + shifty, 1, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _colors[i]->b);
|
||||
if (top && i + 2 * shifty < count) p.fillRect(box.left() + (left ? minus : 0), box.top() - count + i + 2 * shifty, box.width() - (right ? minus : 0) - (left ? minus : 0), 1, _colors[i]->b);
|
||||
if (right && i + shifty < count) p.fillRect(box.left() + box.width() + count - i - shifty - 1, box.top() + (top ? minus : 0) + shifty, 1, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _colors[i]->b);
|
||||
if (bottom) p.fillRect(box.left() + (left ? minus : 0), box.top() + box.height() + count - i - 1, box.width() - (right ? minus : 0) - (left ? minus : 0), 1, _colors[i]->b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,14 +27,14 @@ public:
|
||||
Bottom = 8
|
||||
};
|
||||
|
||||
BoxShadow(const style::rect &topLeft);
|
||||
BoxShadow(const style::sprite &topLeft);
|
||||
|
||||
void paint(QPainter &p, const QRect &box, const QPoint &shift = QPoint(0, 1), int32 flags = Left | Top | Right | Bottom);
|
||||
void paint(QPainter &p, const QRect &box, int32 shifty, int32 flags = Left | Top | Right | Bottom);
|
||||
|
||||
private:
|
||||
|
||||
int32 _size;
|
||||
QPixmap _corners;
|
||||
int32 _size, _pixsize;
|
||||
QPixmap _corners, _left, _top, _right, _bottom;
|
||||
QVector<style::color> _colors;
|
||||
|
||||
};
|
||||
|
||||
@@ -77,6 +77,7 @@ void Button::mouseReleaseEvent(QMouseEvent *e) {
|
||||
}
|
||||
|
||||
void Button::setOver(bool over, ButtonStateChangeSource source) {
|
||||
// LOG(("Set over: %1, by: %2 AT %3").arg(logBool(over)).arg(source).arg(dynamic_cast<IconedButton*>(this) ? dynamic_cast<IconedButton*>(this)->getText() : qsl("Unknown")));
|
||||
if (over && !(_state & StateOver)) {
|
||||
int oldState = _state;
|
||||
_state |= StateOver;
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
#include "lang.h"
|
||||
|
||||
ContextMenu::ContextMenu(QWidget *parent, const style::iconedButton &st) : TWidget(0),
|
||||
_hiding(false), _buttonStyle(st), _shadow(st::dropdownShadow), _selected(-1), a_opacity(0), _deleteOnHide(false) {
|
||||
ContextMenu::ContextMenu(QWidget *parent, const style::dropdown &st, const style::iconedButton &btnst) : TWidget(0),
|
||||
_width(st.width), _hiding(false), _st(st), _btnst(btnst), _shadow(_st.shadow), _selected(-1), a_opacity(0), _deleteOnHide(false) {
|
||||
resetActions();
|
||||
|
||||
setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool | Qt::NoDropShadowWindowHint | Qt::WindowStaysOnTopHint);
|
||||
@@ -43,13 +43,13 @@ QAction *ContextMenu::addAction(const QString &text, const QObject *receiver, co
|
||||
connect(a, SIGNAL(changed()), this, SLOT(actionChanged()));
|
||||
|
||||
IconedButton *b = 0;
|
||||
_buttons.push_back(b = new IconedButton(this, _buttonStyle, a->text()));
|
||||
_buttons.push_back(b = new IconedButton(this, _btnst, a->text()));
|
||||
connect(b, SIGNAL(clicked()), this, SLOT(hideStart()));
|
||||
connect(b, SIGNAL(clicked()), a, SIGNAL(triggered()));
|
||||
connect(b, SIGNAL(stateChanged(int,ButtonStateChangeSource)), this, SLOT(buttonStateChanged(int,ButtonStateChangeSource)));
|
||||
|
||||
_width = qMax(_width, int(st::dropdownPadding.left() + st::dropdownPadding.right() + b->width()));
|
||||
for (int32 i = 0, l = _buttons.size(); i < l; ++i) _buttons[i]->resize(_width - int(st::dropdownPadding.left() + st::dropdownPadding.right()), _buttons[i]->height());
|
||||
_width = qMax(_width, int(_st.padding.left() + _st.padding.right() + b->width()));
|
||||
for (int32 i = 0, l = _buttons.size(); i < l; ++i) _buttons[i]->resize(_width - int(_st.padding.left() + _st.padding.right()), _buttons[i]->height());
|
||||
_height += b->height();
|
||||
|
||||
resize(_width, _height);
|
||||
@@ -64,8 +64,8 @@ ContextMenu::Actions &ContextMenu::actions() {
|
||||
void ContextMenu::actionChanged() {
|
||||
for (int32 i = 0, l = _actions.size(); i < l; ++i) {
|
||||
_buttons[i]->setText(_actions[i]->text());
|
||||
_width = qMax(_width, int(st::dropdownPadding.left() + st::dropdownPadding.right() + _buttons[i]->width()));
|
||||
_buttons[i]->resize(_width - int(st::dropdownPadding.left() + st::dropdownPadding.right()), _buttons[i]->height());
|
||||
_width = qMax(_width, int(_st.padding.left() + _st.padding.right() + _buttons[i]->width()));
|
||||
_buttons[i]->resize(_width - int(_st.padding.left() + _st.padding.right()), _buttons[i]->height());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@ void ContextMenu::buttonStateChanged(int oldState, ButtonStateChangeSource sourc
|
||||
}
|
||||
|
||||
void ContextMenu::resetActions() {
|
||||
_width = st::dropdownPadding.left() + st::dropdownPadding.right();
|
||||
_height = st::dropdownPadding.top() + st::dropdownPadding.bottom();
|
||||
_width = qMax(_st.padding.left() + _st.padding.right(), int(_st.width));
|
||||
_height = _st.padding.top() + _st.padding.bottom();
|
||||
resize(_width, _height);
|
||||
|
||||
clearActions();
|
||||
@@ -122,9 +122,9 @@ void ContextMenu::clearActions() {
|
||||
}
|
||||
|
||||
void ContextMenu::resizeEvent(QResizeEvent *e) {
|
||||
int32 top = st::dropdownPadding.top();
|
||||
int32 top = _st.padding.top();
|
||||
for (Buttons::const_iterator i = _buttons.cbegin(), e = _buttons.cend(); i != e; ++i) {
|
||||
(*i)->move(st::dropdownPadding.left(), top);
|
||||
(*i)->move(_st.padding.left(), top);
|
||||
top += (*i)->height();
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,7 @@ void ContextMenu::resizeEvent(QResizeEvent *e) {
|
||||
void ContextMenu::paintEvent(QPaintEvent *e) {
|
||||
QPainter p(this);
|
||||
|
||||
p.setClipRect(e->rect());
|
||||
QPainter::CompositionMode m = p.compositionMode();
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.fillRect(e->rect(), st::transparent->b);
|
||||
@@ -141,9 +142,9 @@ void ContextMenu::paintEvent(QPaintEvent *e) {
|
||||
p.setOpacity(a_opacity.current());
|
||||
}
|
||||
|
||||
QRect r(st::dropdownPadding.left(), st::dropdownPadding.top(), _width - st::dropdownPadding.left() - st::dropdownPadding.right(), _height - st::dropdownPadding.top() - st::dropdownPadding.bottom());
|
||||
QRect r(_st.padding.left(), _st.padding.top(), _width - _st.padding.left() - _st.padding.right(), _height - _st.padding.top() - _st.padding.bottom());
|
||||
// draw shadow
|
||||
_shadow.paint(p, r);
|
||||
_shadow.paint(p, r, _st.shadowShift);
|
||||
}
|
||||
|
||||
void ContextMenu::keyPressEvent(QKeyEvent *e) {
|
||||
@@ -245,13 +246,19 @@ void ContextMenu::deleteOnHide() {
|
||||
}
|
||||
|
||||
void ContextMenu::popup(const QPoint &p) {
|
||||
QPoint w = p - QPoint(st::dropdownPadding.left(), st::dropdownPadding.top());
|
||||
QPoint w = p - QPoint(_st.padding.left(), _st.padding.top());
|
||||
QRect r = App::app() ? App::app()->desktop()->screenGeometry(p) : QDesktopWidget().screenGeometry(p);
|
||||
if (w.x() + width() - st::dropdownPadding.right() > r.x() + r.width()) {
|
||||
w.setX(r.x() + r.width() - width() + st::dropdownPadding.right());
|
||||
if (rtl()) {
|
||||
if (w.x() - width() + 2 * _st.padding.left() < r.x() - _st.padding.left()) {
|
||||
w.setX(r.x() - _st.padding.left());
|
||||
} else {
|
||||
w.setX(w.x() - width() + 2 * _st.padding.left());
|
||||
}
|
||||
} else if (w.x() + width() - _st.padding.right() > r.x() + r.width()) {
|
||||
w.setX(r.x() + r.width() - width() + _st.padding.right());
|
||||
}
|
||||
if (w.y() + height() - st::dropdownPadding.bottom() > r.y() + r.height()) {
|
||||
w.setY(p.y() - height() + st::dropdownPadding.bottom());
|
||||
if (w.y() + height() - _st.padding.bottom() > r.y() + r.height()) {
|
||||
w.setY(p.y() - height() + _st.padding.bottom());
|
||||
}
|
||||
if (w.y() < r.y()) {
|
||||
w.setY(r.y());
|
||||
|
||||
@@ -25,7 +25,7 @@ class ContextMenu : public TWidget, public Animated {
|
||||
|
||||
public:
|
||||
|
||||
ContextMenu(QWidget *parent, const style::iconedButton &st = st::btnContext);
|
||||
ContextMenu(QWidget *parent, const style::dropdown &st = st::dropdownDef, const style::iconedButton &btnst = st::btnContext);
|
||||
QAction *addAction(const QString &text, const QObject *receiver, const char* member);
|
||||
void resetActions();
|
||||
|
||||
@@ -72,7 +72,8 @@ private:
|
||||
int32 _width, _height;
|
||||
bool _hiding;
|
||||
|
||||
const style::iconedButton &_buttonStyle;
|
||||
const style::dropdown &_st;
|
||||
const style::iconedButton &_btnst;
|
||||
|
||||
BoxShadow _shadow;
|
||||
int32 _selected;
|
||||
|
||||
@@ -485,7 +485,7 @@ void CountrySelect::paintEvent(QPaintEvent *e) {
|
||||
p.setOpacity(1);
|
||||
|
||||
QRect inner(_innerLeft, _innerTop, _innerWidth, _innerHeight);
|
||||
_shadow.paint(p, inner);
|
||||
_shadow.paint(p, inner, st::boxShadowShift);
|
||||
if (trivial || e->rect().intersects(inner)) {
|
||||
// fill bg
|
||||
p.fillRect(inner, st::white->b);
|
||||
|
||||
@@ -19,10 +19,116 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include "gui/text.h"
|
||||
|
||||
void initEmoji();
|
||||
EmojiPtr getEmoji(uint32 code);
|
||||
void emojiInit();
|
||||
EmojiPtr emojiGet(uint32 code);
|
||||
EmojiPtr emojiGet(uint32 code, uint32 code2);
|
||||
EmojiPtr emojiGet(EmojiPtr emoji, uint32 color);
|
||||
EmojiPtr emojiGet(const QChar *from, const QChar *end);
|
||||
QString emojiGetSequence(int index);
|
||||
|
||||
void findEmoji(const QChar *ch, const QChar *e, const QChar *&newEmojiEnd, uint32 &emojiCode);
|
||||
inline QString emojiString(EmojiPtr emoji) {
|
||||
if ((emoji->code & 0xFFFF0000U) == 0xFFFF0000U) { // sequence
|
||||
return emojiGetSequence(emoji->code & 0xFFFFU);
|
||||
}
|
||||
|
||||
QString result;
|
||||
result.reserve(emoji->len + (emoji->postfix ? 1 : 0));
|
||||
if (!(emoji->code >> 16)) {
|
||||
result.append(QChar(emoji->code & 0xFFFF));
|
||||
} else {
|
||||
result.append(QChar((emoji->code >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->code & 0xFFFF));
|
||||
if (emoji->code2) {
|
||||
result.append(QChar((emoji->code2 >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->code2 & 0xFFFF));
|
||||
}
|
||||
}
|
||||
if (emoji->color && ((emoji->color & 0xFFFF0000U) != 0xFFFF0000U)) {
|
||||
result.append(QChar((emoji->color >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->color & 0xFFFF));
|
||||
}
|
||||
if (emoji->postfix) result.append(QChar(emoji->postfix));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint64 emojiKey(EmojiPtr emoji) {
|
||||
uint64 key = emoji->code;
|
||||
if (emoji->code2) {
|
||||
key = (key << 32) | uint64(emoji->code2);
|
||||
} else if (emoji->color && ((emoji->color & 0xFFFF0000U) != 0xFFFF0000U)) {
|
||||
key = (key << 32) | uint64(emoji->color);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
inline EmojiPtr emojiFromKey(uint64 key) {
|
||||
uint32 code = uint32(key >> 32), code2 = uint32(key & 0xFFFFFFFFLLU);
|
||||
if (!code && code2) {
|
||||
code = code2;
|
||||
code2 = 0;
|
||||
}
|
||||
EmojiPtr emoji = emojiGet(code);
|
||||
if (emoji == TwoSymbolEmoji) {
|
||||
return emojiGet(code, code2);
|
||||
} else if (emoji && emoji->color && code2) {
|
||||
return emojiGet(emoji, code2);
|
||||
}
|
||||
return emoji;
|
||||
}
|
||||
|
||||
inline EmojiPtr emojiFromUrl(const QString &url) {
|
||||
return emojiFromKey(url.midRef(10).toULongLong(0, 16)); // skip emoji://e.
|
||||
}
|
||||
|
||||
inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) {
|
||||
EmojiPtr emoji = 0;
|
||||
if (ch + 1 < e && ((ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) || (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3))) {
|
||||
uint32 code = (ch->unicode() << 16) | (ch + 1)->unicode();
|
||||
emoji = emojiGet(code);
|
||||
if (emoji) {
|
||||
if (emoji == TwoSymbolEmoji) { // check two symbol
|
||||
if (ch + 3 >= e) {
|
||||
emoji = 0;
|
||||
} else {
|
||||
uint32 code2 = ((uint32((ch + 2)->unicode()) << 16) | uint32((ch + 3)->unicode()));
|
||||
emoji = emojiGet(code, code2);
|
||||
}
|
||||
} else {
|
||||
if (ch + 2 < e && (ch + 2)->unicode() == 0x200D) { // check sequence
|
||||
EmojiPtr seq = emojiGet(ch, e);
|
||||
if (seq) {
|
||||
emoji = seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ch < e) {
|
||||
emoji = emojiGet(ch->unicode());
|
||||
Q_ASSERT(emoji != TwoSymbolEmoji);
|
||||
}
|
||||
|
||||
if (emoji) {
|
||||
len = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
|
||||
if (emoji->color && (ch + len + 1 < e && (ch + len)->isHighSurrogate() && (ch + len + 1)->isLowSurrogate())) { // color
|
||||
uint32 color = ((uint32((ch + len)->unicode()) << 16) | uint32((ch + len + 1)->unicode()));
|
||||
EmojiPtr col = emojiGet(emoji, color);
|
||||
if (col && col != emoji) {
|
||||
len += col->len - emoji->len;
|
||||
emoji = col;
|
||||
if (ch + len < e && (ch + len)->unicode() == 0xFE0F) {
|
||||
++len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return emoji;
|
||||
}
|
||||
|
||||
extern int EmojiSizes[5], EIndex, ESize;
|
||||
extern const char *EmojiNames[5], *EName;
|
||||
|
||||
void emojiFind(const QChar *ch, const QChar *e, const QChar *&newEmojiEnd, uint32 &emojiCode);
|
||||
|
||||
inline bool emojiEdge(const QChar *ch) {
|
||||
return true;
|
||||
@@ -48,13 +154,14 @@ inline QString replaceEmojis(const QString &text) {
|
||||
uint32 emojiCode = 0;
|
||||
const QChar *newEmojiEnd = 0;
|
||||
if (canFindEmoji) {
|
||||
findEmoji(ch, e, newEmojiEnd, emojiCode);
|
||||
emojiFind(ch, e, newEmojiEnd, emojiCode);
|
||||
}
|
||||
|
||||
while (currentLink < lnkCount && ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len) {
|
||||
++currentLink;
|
||||
}
|
||||
if (emojiCode &&
|
||||
EmojiPtr emoji = emojiCode ? emojiGet(emojiCode) : 0;
|
||||
if (emoji && emoji != TwoSymbolEmoji &&
|
||||
(ch == emojiStart || !ch->isLetterOrNumber() || !(ch - 1)->isLetterOrNumber()) &&
|
||||
(newEmojiEnd == e || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) &&
|
||||
(currentLink >= lnkCount || (ch < lnkRanges[currentLink].from && newEmojiEnd <= lnkRanges[currentLink].from) || (ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len && newEmojiEnd > lnkRanges[currentLink].from + lnkRanges[currentLink].len))
|
||||
@@ -64,10 +171,18 @@ inline QString replaceEmojis(const QString &text) {
|
||||
if (ch > emojiEnd + (consumePrevious ? 1 : 0)) {
|
||||
result.append(emojiEnd, ch - emojiEnd - (consumePrevious ? 1 : 0));
|
||||
}
|
||||
if (emojiCode > 65535) {
|
||||
result.append(QChar((emojiCode >> 16) & 0xFFFF));
|
||||
if (emoji->color) {
|
||||
EmojiColorVariants::const_iterator it = cEmojiVariants().constFind(emoji->code);
|
||||
if (it != cEmojiVariants().cend()) {
|
||||
EmojiPtr replace = emojiFromKey(it.value());
|
||||
if (replace) {
|
||||
if (replace != TwoSymbolEmoji && replace->code == emoji->code && replace->code2 == emoji->code2) {
|
||||
emoji = replace;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result.append(QChar(emojiCode & 0xFFFF));
|
||||
result.append(emojiString(emoji));
|
||||
|
||||
ch = emojiEnd = newEmojiEnd;
|
||||
canFindEmoji = true;
|
||||
@@ -91,4 +206,5 @@ inline QString replaceEmojis(const QString &text) {
|
||||
return result;
|
||||
}
|
||||
|
||||
int emojiPackCount(DBIEmojiTab tab);
|
||||
EmojiPack emojiPack(DBIEmojiTab tab);
|
||||
|
||||
@@ -172,16 +172,26 @@ void IconedButton::setText(const QString &text) {
|
||||
}
|
||||
}
|
||||
|
||||
QString IconedButton::getText() const {
|
||||
return _text;
|
||||
}
|
||||
|
||||
bool IconedButton::animStep(float64 ms) {
|
||||
float64 dt = ms / _st.duration;
|
||||
bool res = true;
|
||||
if (dt >= 1) {
|
||||
if (_st.duration <= 1) {
|
||||
a_opacity.finish();
|
||||
a_bg.finish();
|
||||
res = false;
|
||||
} else {
|
||||
a_opacity.update(dt, anim::linear);
|
||||
a_bg.update(dt, anim::linear);
|
||||
float64 dt = ms / _st.duration;
|
||||
if (dt >= 1) {
|
||||
a_opacity.finish();
|
||||
a_bg.finish();
|
||||
res = false;
|
||||
} else {
|
||||
a_opacity.update(dt, anim::linear);
|
||||
a_bg.update(dt, anim::linear);
|
||||
}
|
||||
}
|
||||
update();
|
||||
return res;
|
||||
|
||||
@@ -100,6 +100,7 @@ public:
|
||||
void setOpacity(float64 o);
|
||||
|
||||
void setText(const QString &text);
|
||||
QString getText() const;
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
@@ -174,8 +174,7 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
|
||||
|
||||
if (!text.isEmpty()) {
|
||||
QTextCharFormat format = fragment.charFormat();
|
||||
QString imageName = static_cast<const QTextImageFormat*>(&format)->name();
|
||||
return getEmoji(imageName.mid(8).toUInt(0, 16));
|
||||
return emojiFromUrl(static_cast<const QTextImageFormat*>(&format)->name());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -288,7 +287,7 @@ void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment
|
||||
}
|
||||
if (f.isImageFormat() && !t.isEmpty() && t.at(0).unicode() == QChar::ObjectReplacementCharacter) {
|
||||
QString imageName = static_cast<QTextImageFormat*>(&f)->name();
|
||||
if (imageName.midRef(0, 8) == qsl("emoji://")) {
|
||||
if (imageName.startsWith(QLatin1String("emoji://e."))) {
|
||||
fragment = fr;
|
||||
text = t;
|
||||
return;
|
||||
@@ -372,11 +371,9 @@ QString FlatTextarea::getText(int32 start, int32 end) const {
|
||||
case QChar::ObjectReplacementCharacter:
|
||||
if (emojiText.isEmpty() && f.isImageFormat()) {
|
||||
QString imageName = static_cast<QTextImageFormat*>(&f)->name();
|
||||
if (imageName.midRef(0, 8) == qsl("emoji://")) {
|
||||
uint32 index = imageName.mid(8).toUInt(0, 16);
|
||||
const EmojiData *emoji = getEmoji(index);
|
||||
if (emoji) {
|
||||
emojiText = textEmojiString(emoji);
|
||||
if (imageName.startsWith(QLatin1String("emoji://e."))) {
|
||||
if (EmojiPtr emoji = emojiFromUrl(imageName)) {
|
||||
emojiText = emojiString(emoji);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,7 +517,7 @@ void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) {
|
||||
c.removeSelectedText();
|
||||
|
||||
QPixmap img(App::emojiSingle(emoji, _st.font->height));
|
||||
QString url = qsl("emoji://") + QString::number(emoji->code, 16);
|
||||
QString url = qsl("emoji://e.") + QString::number(emojiKey(emoji), 16);
|
||||
document()->addResource(QTextDocument::ImageResource, QUrl(url), QVariant(img));
|
||||
QTextImageFormat imageFormat;
|
||||
imageFormat.setWidth(img.width() / cIntRetinaFactor());
|
||||
@@ -546,33 +543,20 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
|
||||
QTextFragment fragment(iter.fragment());
|
||||
if (!fragment.isValid()) continue;
|
||||
|
||||
int32 p = fragment.position(), e = p + fragment.length();
|
||||
if (p >= end || e <= start) {
|
||||
int32 fp = fragment.position(), fe = fp + fragment.length();
|
||||
if (fp >= end || fe <= start) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString t(fragment.text());
|
||||
for (const QChar *ch = t.constData(), *e = ch + t.size(); ch != e; ++ch) {
|
||||
if (ch + 1 < e && (ch->isHighSurrogate() || (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3))) {
|
||||
emoji = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
|
||||
if (emoji) {
|
||||
if (emoji->len == 4 && (ch + 3 >= e || ((uint32((ch + 2)->unicode()) << 16) | uint32((ch + 3)->unicode())) != emoji->code2)) {
|
||||
emoji = 0;
|
||||
} else {
|
||||
emojiPosition = p + (ch - t.constData());
|
||||
emojiLen = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
++ch;
|
||||
} else {
|
||||
emoji = getEmoji(ch->unicode());
|
||||
if (emoji) {
|
||||
emojiPosition = p + (ch - t.constData());
|
||||
emojiLen = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
const QChar *ch = t.constData(), *e = ch + t.size();
|
||||
for (; ch != e; ++ch) {
|
||||
emoji = emojiFromText(ch, e, emojiLen);
|
||||
if (emoji) {
|
||||
emojiPosition = fp + (ch - t.constData());
|
||||
break;
|
||||
}
|
||||
if (ch + 1 < e && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) ++ch;
|
||||
}
|
||||
if (emoji) break;
|
||||
}
|
||||
@@ -697,8 +681,12 @@ bool FlatTextarea::animStep(float64 ms) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const QString &FlatTextarea::getLastText() const {
|
||||
return _oldtext;
|
||||
}
|
||||
|
||||
void FlatTextarea::updatePlaceholder() {
|
||||
bool vis = !hasText();
|
||||
bool vis = getLastText().isEmpty();
|
||||
if (vis == _phVisible) return;
|
||||
|
||||
a_phLeft.start(vis ? 0 : _st.phShift);
|
||||
@@ -720,10 +708,16 @@ QMimeData *FlatTextarea::createMimeDataFromSelection() const {
|
||||
|
||||
void FlatTextarea::keyPressEvent(QKeyEvent *e) {
|
||||
bool shift = e->modifiers().testFlag(Qt::ShiftModifier);
|
||||
bool macmeta = (cPlatform() == dbipMac) && e->modifiers().testFlag(Qt::ControlModifier) && !e->modifiers().testFlag(Qt::MetaModifier) && !e->modifiers().testFlag(Qt::AltModifier);
|
||||
bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier), ctrlGood = (ctrl && cCtrlEnter()) || (!ctrl && !shift && !cCtrlEnter()) || (ctrl && shift);
|
||||
bool enter = (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return);
|
||||
|
||||
if (enter && ctrlGood) {
|
||||
if (macmeta && e->key() == Qt::Key_Backspace) {
|
||||
QTextCursor tc(textCursor()), start(tc);
|
||||
start.movePosition(QTextCursor::StartOfLine);
|
||||
tc.setPosition(start.position(), QTextCursor::KeepAnchor);
|
||||
tc.removeSelectedText();
|
||||
} else if (enter && ctrlGood) {
|
||||
emit submitted(ctrl && shift);
|
||||
} else if (e->key() == Qt::Key_Escape) {
|
||||
emit cancelled();
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
|
||||
const QString &getLastText() const;
|
||||
void updatePlaceholder();
|
||||
|
||||
QRect getTextRect() const;
|
||||
@@ -85,6 +86,22 @@ signals:
|
||||
protected:
|
||||
|
||||
void insertEmoji(EmojiPtr emoji, QTextCursor c);
|
||||
TWidget *tparent() {
|
||||
return qobject_cast<TWidget*>(parentWidget());
|
||||
}
|
||||
const TWidget *tparent() const {
|
||||
return qobject_cast<const TWidget*>(parentWidget());
|
||||
}
|
||||
void enterEvent(QEvent *e) {
|
||||
TWidget *p(tparent());
|
||||
if (p) p->leaveToChildEvent(e);
|
||||
return QTextEdit::enterEvent(e);
|
||||
}
|
||||
void leaveEvent(QEvent *e) {
|
||||
TWidget *p(tparent());
|
||||
if (p) p->enterFromChildEvent(e);
|
||||
return QTextEdit::leaveEvent(e);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "gui/images.h"
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "localstorage.h"
|
||||
|
||||
namespace {
|
||||
typedef QMap<QString, LocalImage*> LocalImages;
|
||||
@@ -33,6 +34,11 @@ namespace {
|
||||
StorageImages storageImages;
|
||||
|
||||
int64 globalAquiredSize = 0;
|
||||
|
||||
static const uint64 BlurredCacheSkip = 0x1000000000000000LLU;
|
||||
static const uint64 ColoredCacheSkip = 0x2000000000000000LLU;
|
||||
static const uint64 BlurredColoredCacheSkip = 0x3000000000000000LLU;
|
||||
static const uint64 RoundedCacheSkip = 0x4000000000000000LLU;
|
||||
}
|
||||
|
||||
bool Image::isNull() const {
|
||||
@@ -43,7 +49,7 @@ ImagePtr::ImagePtr() : Parent(blank()) {
|
||||
}
|
||||
|
||||
ImagePtr::ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def) :
|
||||
Parent((location.type() == mtpc_fileLocation) ? (Image*)(getImage(width, height, location.c_fileLocation().vdc_id.v, location.c_fileLocation().vvolume_id.v, location.c_fileLocation().vlocal_id.v, location.c_fileLocation().vsecret.v)) : def.v()) {
|
||||
Parent((location.type() == mtpc_fileLocation) ? (Image*)(getImage(StorageImageLocation(width, height, location.c_fileLocation()))) : def.v()) {
|
||||
}
|
||||
|
||||
const QPixmap &Image::pix(int32 w, int32 h) const {
|
||||
@@ -69,6 +75,29 @@ const QPixmap &Image::pix(int32 w, int32 h) const {
|
||||
return i.value();
|
||||
}
|
||||
|
||||
const QPixmap &Image::pixRounded(int32 w, int32 h) const {
|
||||
restore();
|
||||
checkload();
|
||||
|
||||
if (w <= 0 || !width() || !height()) {
|
||||
w = width();
|
||||
} else if (cRetina()) {
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = RoundedCacheSkip | (uint64(w) << 32) | uint64(h);
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend()) {
|
||||
QPixmap p(pixNoCache(w, h, true, false, true));
|
||||
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
|
||||
i = _sizesCache.insert(k, p);
|
||||
if (!p.isNull()) {
|
||||
globalAquiredSize += int64(p.width()) * p.height() * 4;
|
||||
}
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
|
||||
restore();
|
||||
checkload();
|
||||
@@ -79,10 +108,10 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0x1000000000000000LL | (uint64(w) << 32) | uint64(h);
|
||||
uint64 k = BlurredCacheSkip | (uint64(w) << 32) | uint64(h);
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend()) {
|
||||
QPixmap p(pixBlurredNoCache(w, h));
|
||||
QPixmap p(pixNoCache(w, h, true, true));
|
||||
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
|
||||
i = _sizesCache.insert(k, p);
|
||||
if (!p.isNull()) {
|
||||
@@ -102,7 +131,7 @@ const QPixmap &Image::pixColored(const style::color &add, int32 w, int32 h) cons
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0x2000000000000000LL | (uint64(w) << 32) | uint64(h);
|
||||
uint64 k = ColoredCacheSkip | (uint64(w) << 32) | uint64(h);
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend()) {
|
||||
QPixmap p(pixColoredNoCache(add, w, h, true));
|
||||
@@ -125,7 +154,7 @@ const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0x3000000000000000LL | (uint64(w) << 32) | uint64(h);
|
||||
uint64 k = BlurredColoredCacheSkip | (uint64(w) << 32) | uint64(h);
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend()) {
|
||||
QPixmap p(pixBlurredColoredNoCache(add, w, h));
|
||||
@@ -138,7 +167,7 @@ const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32
|
||||
return i.value();
|
||||
}
|
||||
|
||||
const QPixmap &Image::pixSingle(int32 w, int32 h) const {
|
||||
const QPixmap &Image::pixSingle(int32 w, int32 h, int32 outerw, int32 outerh) const {
|
||||
restore();
|
||||
checkload();
|
||||
|
||||
@@ -154,7 +183,7 @@ const QPixmap &Image::pixSingle(int32 w, int32 h) const {
|
||||
if (i != _sizesCache.cend()) {
|
||||
globalAquiredSize -= int64(i->width()) * i->height() * 4;
|
||||
}
|
||||
QPixmap p(pixNoCache(w, h, true));
|
||||
QPixmap p(pixNoCache(w, h, true, false, true, outerw, outerh));
|
||||
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
|
||||
i = _sizesCache.insert(k, p);
|
||||
if (!p.isNull()) {
|
||||
@@ -164,7 +193,7 @@ const QPixmap &Image::pixSingle(int32 w, int32 h) const {
|
||||
return i.value();
|
||||
}
|
||||
|
||||
const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const {
|
||||
const QPixmap &Image::pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh) const {
|
||||
restore();
|
||||
checkload();
|
||||
|
||||
@@ -174,13 +203,13 @@ const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const {
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0x1000000000000000LL | 0LL;
|
||||
uint64 k = BlurredCacheSkip | 0LL;
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend() || i->width() != w || (h && i->height() != h)) {
|
||||
if (i != _sizesCache.cend()) {
|
||||
globalAquiredSize -= int64(i->width()) * i->height() * 4;
|
||||
}
|
||||
QPixmap p(pixBlurredNoCache(w, h));
|
||||
QPixmap p(pixNoCache(w, h, true, true, true, outerw, outerh));
|
||||
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
|
||||
i = _sizesCache.insert(k, p);
|
||||
if (!p.isNull()) {
|
||||
@@ -308,6 +337,39 @@ yi += stride;
|
||||
return img;
|
||||
}
|
||||
|
||||
void imageRound(QImage &img) {
|
||||
img.setDevicePixelRatio(cRetinaFactor());
|
||||
img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
|
||||
QImage **masks = App::cornersMask();
|
||||
int32 w = masks[0]->width(), h = masks[0]->height();
|
||||
int32 tw = img.width(), th = img.height();
|
||||
|
||||
uchar *bits = img.bits();
|
||||
const uchar *c0 = masks[0]->constBits(), *c1 = masks[1]->constBits(), *c2 = masks[2]->constBits(), *c3 = masks[3]->constBits();
|
||||
|
||||
int32 s0 = 0, s1 = (tw - w) * 4, s2 = (th - h) * tw * 4, s3 = ((th - h + 1) * tw - w) * 4;
|
||||
for (int32 i = 0; i < w; ++i) {
|
||||
for (int32 j = 0; j < h; ++j) {
|
||||
#define update(s, c) \
|
||||
{ \
|
||||
uint64 color = _blurGetColors(bits + s + (j * tw + i) * 4); \
|
||||
color *= (c[(j * w + i) * 4 + 3] + 1); \
|
||||
color = (color >> 8); \
|
||||
bits[s + (j * tw + i) * 4] = color & 0xFF; \
|
||||
bits[s + (j * tw + i) * 4 + 1] = (color >> 16) & 0xFF; \
|
||||
bits[s + (j * tw + i) * 4 + 2] = (color >> 32) & 0xFF; \
|
||||
bits[s + (j * tw + i) * 4 + 3] = (color >> 48) & 0xFF; \
|
||||
}
|
||||
update(s0, c0);
|
||||
update(s1, c1);
|
||||
update(s2, c2);
|
||||
update(s3, c3);
|
||||
#undef update
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QImage imageColored(const style::color &add, QImage img) {
|
||||
QImage::Format fmt = img.format();
|
||||
if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) {
|
||||
@@ -329,35 +391,39 @@ QImage imageColored(const style::color &add, QImage img) {
|
||||
return img;
|
||||
}
|
||||
|
||||
QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth) const {
|
||||
restore();
|
||||
loaded();
|
||||
|
||||
const QPixmap &p(pixData());
|
||||
if (p.isNull()) {
|
||||
return blank()->pix();
|
||||
}
|
||||
if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) return p;
|
||||
if (h <= 0) {
|
||||
return QPixmap::fromImage(p.toImage().scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation), Qt::ColorOnly);
|
||||
}
|
||||
return QPixmap::fromImage(p.toImage().scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation), Qt::ColorOnly);
|
||||
}
|
||||
|
||||
QPixmap Image::pixBlurredNoCache(int32 w, int32 h) const {
|
||||
QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh) const {
|
||||
restore();
|
||||
loaded();
|
||||
|
||||
const QPixmap &p(pixData());
|
||||
if (p.isNull()) return blank()->pix();
|
||||
|
||||
QImage img = imageBlur(p.toImage());
|
||||
if (h <= 0) {
|
||||
img = img.scaledToWidth(w, Qt::SmoothTransformation);
|
||||
QImage img = p.toImage();
|
||||
if (blurred) img = imageBlur(img);
|
||||
if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) {
|
||||
} else if (h <= 0) {
|
||||
img = img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation);
|
||||
} else {
|
||||
img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
img = img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation);
|
||||
}
|
||||
|
||||
if (outerw > 0 && outerh > 0) {
|
||||
outerw *= cIntRetinaFactor();
|
||||
outerh *= cIntRetinaFactor();
|
||||
if (outerw != w || outerh != h) {
|
||||
img.setDevicePixelRatio(cRetinaFactor());
|
||||
QImage result(outerw, outerh, QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
QPainter p(&result);
|
||||
if (w < outerw || h < outerh) {
|
||||
p.fillRect(0, 0, result.width(), result.height(), st::black->b);
|
||||
}
|
||||
p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img);
|
||||
}
|
||||
img = result;
|
||||
}
|
||||
}
|
||||
if (rounded) imageRound(img);
|
||||
return QPixmap::fromImage(img, Qt::ColorOnly);
|
||||
}
|
||||
|
||||
@@ -517,11 +583,14 @@ int64 imageCacheSize() {
|
||||
return globalAquiredSize;
|
||||
}
|
||||
|
||||
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) : w(width), h(height), loader(new mtpFileLoader(dc, volume, local, secret, size)) {
|
||||
StorageImage::StorageImage(const StorageImageLocation &location, int32 size) : w(location.width), h(location.height), loader(new mtpFileLoader(location.dc, location.volume, location.local, location.secret, size)) {
|
||||
}
|
||||
|
||||
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, QByteArray &bytes) : w(width), h(height), loader(0) {
|
||||
StorageImage::StorageImage(const StorageImageLocation &location, QByteArray &bytes) : w(location.width), h(location.height), loader(0) {
|
||||
setData(bytes);
|
||||
if (location.dc) {
|
||||
Local::writeImage(storageKey(location.dc, location.volume, location.local), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes));
|
||||
}
|
||||
}
|
||||
|
||||
const QPixmap &StorageImage::pixData() const {
|
||||
@@ -608,24 +677,27 @@ bool StorageImage::loaded() const {
|
||||
return check();
|
||||
}
|
||||
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) {
|
||||
StorageKey key(storageKey(dc, volume, local));
|
||||
StorageImage *getImage(const StorageImageLocation &location, int32 size) {
|
||||
StorageKey key(storageKey(location.dc, location.volume, location.local));
|
||||
StorageImages::const_iterator i = storageImages.constFind(key);
|
||||
if (i == storageImages.cend()) {
|
||||
i = storageImages.insert(key, new StorageImage(width, height, dc, volume, local, secret, size));
|
||||
i = storageImages.insert(key, new StorageImage(location, size));
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes) {
|
||||
StorageKey key(storageKey(dc, volume, local));
|
||||
StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes) {
|
||||
StorageKey key(storageKey(location.dc, location.volume, location.local));
|
||||
StorageImages::const_iterator i = storageImages.constFind(key);
|
||||
if (i == storageImages.cend()) {
|
||||
QByteArray bytesArr(bytes);
|
||||
i = storageImages.insert(key, new StorageImage(width, height, dc, volume, local, secret, bytesArr));
|
||||
i = storageImages.insert(key, new StorageImage(location, bytesArr));
|
||||
} else if (!i.value()->loaded()) {
|
||||
QByteArray bytesArr(bytes);
|
||||
i.value()->setData(bytesArr);
|
||||
if (location.dc) {
|
||||
Local::writeImage(storageKey(location.dc, location.volume, location.local), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes));
|
||||
}
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,21 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include <QtGui/QPixmap>
|
||||
|
||||
QImage imageBlur(QImage img);
|
||||
void imageRound(QImage &img);
|
||||
|
||||
struct StorageImageLocation {
|
||||
StorageImageLocation() : width(0), height(0), dc(0), volume(0), local(0), secret(0) {
|
||||
}
|
||||
StorageImageLocation(int32 width, int32 height, int32 dc, const uint64 &volume, int32 local, const uint64 &secret) : width(width), height(height), dc(dc), volume(volume), local(local), secret(secret) {
|
||||
}
|
||||
StorageImageLocation(int32 width, int32 height, const MTPDfileLocation &location) : width(width), height(height), dc(location.vdc_id.v), volume(location.vvolume_id.v), local(location.vlocal_id.v), secret(location.vsecret.v) {
|
||||
}
|
||||
int32 width, height;
|
||||
int32 dc;
|
||||
uint64 volume;
|
||||
int32 local;
|
||||
uint64 secret;
|
||||
};
|
||||
|
||||
class Image {
|
||||
public:
|
||||
@@ -33,13 +48,13 @@ public:
|
||||
return false;
|
||||
}
|
||||
const QPixmap &pix(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixRounded(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixColored(const style::color &add, int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixBlurredColored(const style::color &add, int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixSingle(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixBlurredSingle(int32 w = 0, int32 h = 0) const;
|
||||
QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false) const;
|
||||
QPixmap pixBlurredNoCache(int32 w, int32 h = 0) const;
|
||||
const QPixmap &pixSingle(int32 w, int32 y, int32 outerw, int32 outerh) const;
|
||||
const QPixmap &pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh) const;
|
||||
QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false, bool blurred = false, bool rounded = false, int32 outerw = -1, int32 outerh = -1) const;
|
||||
QPixmap pixColoredNoCache(const style::color &add, int32 w = 0, int32 h = 0, bool smooth = false) const;
|
||||
QPixmap pixBlurredColoredNoCache(const style::color &add, int32 w, int32 h = 0) const;
|
||||
|
||||
@@ -123,25 +138,66 @@ typedef QPair<uint64, uint64> StorageKey;
|
||||
inline uint64 storageMix32To64(int32 a, int32 b) {
|
||||
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32) | uint64(*reinterpret_cast<uint32*>(&b));
|
||||
}
|
||||
inline StorageKey storageKey(int32 dc, const int64 &volume, int32 local) {
|
||||
inline StorageKey storageKey(int32 dc, const uint64 &volume, int32 local) {
|
||||
return StorageKey(storageMix32To64(dc, local), volume);
|
||||
}
|
||||
inline StorageKey storageKey(const MTPDfileLocation &location) {
|
||||
return storageKey(location.vdc_id.v, location.vvolume_id.v, location.vlocal_id.v);
|
||||
}
|
||||
enum StorageFileType {
|
||||
StorageFileUnknown = 0xaa963b05, // mtpc_storage_fileUnknown
|
||||
StorageFileJpeg = 0x7efe0e, // mtpc_storage_fileJpeg
|
||||
StorageFileGif = 0xcae1aadf, // mtpc_storage_fileGif
|
||||
StorageFilePng = 0xa4f63c0, // mtpc_storage_filePng
|
||||
StorageFilePdf = 0xae1e508d, // mtpc_storage_filePdf
|
||||
StorageFileMp3 = 0x528a0677, // mtpc_storage_fileMp3
|
||||
StorageFileMov = 0x4b09ebbc, // mtpc_storage_fileMov
|
||||
StorageFilePartial = 0x40bc6f52, // mtpc_storage_filePartial
|
||||
StorageFileMp4 = 0xb3cea0e4, // mtpc_storage_fileMp4
|
||||
StorageFileWebp = 0x1081464c, // mtpc_storage_fileWebp
|
||||
};
|
||||
inline StorageFileType mtpToStorageType(mtpTypeId type) {
|
||||
switch (type) {
|
||||
case mtpc_storage_fileJpeg: return StorageFileJpeg;
|
||||
case mtpc_storage_fileGif: return StorageFileGif;
|
||||
case mtpc_storage_filePng: return StorageFilePng;
|
||||
case mtpc_storage_filePdf: return StorageFilePdf;
|
||||
case mtpc_storage_fileMp3: return StorageFileMp3;
|
||||
case mtpc_storage_fileMov: return StorageFileMov;
|
||||
case mtpc_storage_filePartial: return StorageFilePartial;
|
||||
case mtpc_storage_fileMp4: return StorageFileMp4;
|
||||
case mtpc_storage_fileWebp: return StorageFileWebp;
|
||||
case mtpc_storage_fileUnknown:
|
||||
default: return StorageFileUnknown;
|
||||
}
|
||||
}
|
||||
inline mtpTypeId mtpFromStorageType(StorageFileType type) {
|
||||
switch (type) {
|
||||
case StorageFileGif: return mtpc_storage_fileGif;
|
||||
case StorageFilePng: return mtpc_storage_filePng;
|
||||
case StorageFilePdf: return mtpc_storage_filePdf;
|
||||
case StorageFileMp3: return mtpc_storage_fileMp3;
|
||||
case StorageFileMov: return mtpc_storage_fileMov;
|
||||
case StorageFilePartial: return mtpc_storage_filePartial;
|
||||
case StorageFileMp4: return mtpc_storage_fileMp4;
|
||||
case StorageFileWebp: return mtpc_storage_fileWebp;
|
||||
case StorageFileUnknown:
|
||||
default: return mtpc_storage_fileUnknown;
|
||||
}
|
||||
}
|
||||
struct StorageImageSaved {
|
||||
StorageImageSaved() : type(mtpc_storage_fileUnknown) {
|
||||
StorageImageSaved() : type(StorageFileUnknown) {
|
||||
}
|
||||
StorageImageSaved(mtpTypeId type, const QByteArray &data) : type(type), data(data) {
|
||||
StorageImageSaved(StorageFileType type, const QByteArray &data) : type(type), data(data) {
|
||||
}
|
||||
mtpTypeId type;
|
||||
StorageFileType type;
|
||||
QByteArray data;
|
||||
};
|
||||
class StorageImage : public Image {
|
||||
public:
|
||||
|
||||
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
|
||||
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, QByteArray &bytes);
|
||||
StorageImage(const StorageImageLocation &location, int32 size = 0);
|
||||
StorageImage(const StorageImageLocation &location, QByteArray &bytes);
|
||||
|
||||
int32 width() const;
|
||||
int32 height() const;
|
||||
@@ -188,8 +244,8 @@ private:
|
||||
mutable mtpFileLoader *loader;
|
||||
};
|
||||
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes);
|
||||
StorageImage *getImage(const StorageImageLocation &location, int32 size = 0);
|
||||
StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes);
|
||||
Image *getImage(int32 width, int32 height, const MTPFileLocation &location);
|
||||
|
||||
class ImagePtr : public ManagedPtr<Image> {
|
||||
@@ -201,9 +257,9 @@ public:
|
||||
}
|
||||
ImagePtr(const QPixmap &pixmap, QByteArray format) : Parent(getImage(pixmap, format)) {
|
||||
}
|
||||
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0) : Parent(getImage(width, height, dc, volume, local, secret, size)) {
|
||||
ImagePtr(const StorageImageLocation &location, int32 size = 0) : Parent(getImage(location, size)) {
|
||||
}
|
||||
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes) : Parent(getImage(width, height, dc, volume, local, secret, bytes)) {
|
||||
ImagePtr(const StorageImageLocation &location, const QByteArray &bytes) : Parent(getImage(location, bytes)) {
|
||||
}
|
||||
ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr());
|
||||
};
|
||||
@@ -213,16 +269,16 @@ void clearAllImages();
|
||||
int64 imageCacheSize();
|
||||
|
||||
struct FileLocation {
|
||||
FileLocation(mtpTypeId type, const QString &name, const QDateTime &modified, qint32 size) : type(type), name(name), modified(modified), size(size) {
|
||||
FileLocation(StorageFileType type, const QString &name, const QDateTime &modified, qint32 size) : type(type), name(name), modified(modified), size(size) {
|
||||
}
|
||||
FileLocation(mtpTypeId type, const QString &name) : type(type), name(name) {
|
||||
FileLocation(StorageFileType type, const QString &name) : type(type), name(name) {
|
||||
QFileInfo f(name);
|
||||
if (f.exists()) {
|
||||
qint64 s = f.size();
|
||||
if (s > INT_MAX) {
|
||||
this->name = QString();
|
||||
size = 0;
|
||||
type = mtpc_storage_fileUnknown;
|
||||
type = StorageFileUnknown;
|
||||
} else {
|
||||
modified = f.lastModified();
|
||||
size = qint32(s);
|
||||
@@ -230,7 +286,7 @@ struct FileLocation {
|
||||
} else {
|
||||
this->name = QString();
|
||||
size = 0;
|
||||
type = mtpc_storage_fileUnknown;
|
||||
type = StorageFileUnknown;
|
||||
}
|
||||
}
|
||||
FileLocation() : size(0) {
|
||||
@@ -245,7 +301,7 @@ struct FileLocation {
|
||||
|
||||
return (f.lastModified() == modified) && (qint32(s) == size);
|
||||
}
|
||||
mtpTypeId type;
|
||||
StorageFileType type;
|
||||
QString name;
|
||||
QDateTime modified;
|
||||
qint32 size;
|
||||
@@ -257,11 +313,34 @@ inline bool operator!=(const FileLocation &a, const FileLocation &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
enum LocationType {
|
||||
UnknownFileLocation = 0,
|
||||
DocumentFileLocation = 0x4e45abe9, // mtpc_inputDocumentFileLocation
|
||||
AudioFileLocation = 0x74dc404d, // mtpc_inputAudioFileLocation
|
||||
VideoFileLocation = 0x3d0364ec, // mtpc_inputVideoFileLocation
|
||||
};
|
||||
inline LocationType mtpToLocationType(mtpTypeId type) {
|
||||
switch (type) {
|
||||
case mtpc_inputDocumentFileLocation: return DocumentFileLocation;
|
||||
case mtpc_inputAudioFileLocation: return AudioFileLocation;
|
||||
case mtpc_inputVideoFileLocation: return VideoFileLocation;
|
||||
default: return UnknownFileLocation;
|
||||
}
|
||||
}
|
||||
inline mtpTypeId mtpFromLocationType(LocationType type) {
|
||||
switch (type) {
|
||||
case DocumentFileLocation: return mtpc_inputDocumentFileLocation;
|
||||
case AudioFileLocation: return mtpc_inputAudioFileLocation;
|
||||
case VideoFileLocation: return mtpc_inputVideoFileLocation;
|
||||
case UnknownFileLocation:
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
typedef QPair<uint64, uint64> MediaKey;
|
||||
inline uint64 mediaMix32To64(mtpTypeId a, int32 b) {
|
||||
inline uint64 mediaMix32To64(int32 a, int32 b) {
|
||||
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32) | uint64(*reinterpret_cast<uint32*>(&b));
|
||||
}
|
||||
inline MediaKey mediaKey(mtpTypeId type, int32 dc, const int64 &id) {
|
||||
inline MediaKey mediaKey(LocationType type, int32 dc, const uint64 &id) {
|
||||
return MediaKey(mediaMix32To64(type, dc), id);
|
||||
}
|
||||
inline StorageKey mediaKey(const MTPDfileLocation &location) {
|
||||
|
||||
@@ -54,7 +54,7 @@ ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::flatScroll *st)
|
||||
}
|
||||
|
||||
void ScrollBar::recountSize() {
|
||||
setGeometry(_vertical ? QRect(rtl() ? 0 : (_area->width() - _st->width), 0, _st->width, _area->height()) : QRect(0, _area->height() - _st->width, _area->width(), _st->width));
|
||||
setGeometry(_vertical ? QRect(rtl() ? 0 : (_area->width() - _st->width), _st->deltat, _st->width, _area->height() - _st->deltat - _st->deltab) : QRect(_st->deltat, _area->height() - _st->width, _area->width() - _st->deltat - _st->deltab, _st->width));
|
||||
}
|
||||
|
||||
void ScrollBar::updateBar(bool force) {
|
||||
@@ -65,7 +65,7 @@ void ScrollBar::updateBar(bool force) {
|
||||
_area->rangeChanged(oldMax, newMax, _vertical);
|
||||
}
|
||||
if (_vertical) {
|
||||
int sh = _area->scrollHeight(), rh = height() - 2 * _st->deltay, h = sh ? int32((rh * int64(_area->height())) / sh) : 0;
|
||||
int sh = _area->scrollHeight(), rh = height(), h = sh ? int32((rh * int64(_area->height())) / sh) : 0;
|
||||
if (h >= rh || !_area->scrollTopMax() || rh < _st->minHeight) {
|
||||
if (!isHidden()) hide();
|
||||
bool newTopSh = (_st->topsh < 0), newBottomSh = (_st->bottomsh < 0);
|
||||
@@ -78,9 +78,9 @@ void ScrollBar::updateBar(bool force) {
|
||||
int stm = _area->scrollTopMax(), y = stm ? int32(((rh - h) * int64(_area->scrollTop())) / stm) : 0;
|
||||
if (y > rh - h) y = rh - h;
|
||||
|
||||
newBar = QRect(_st->deltax, y + _st->deltay, width() - 2 * _st->deltax, h);
|
||||
newBar = QRect(_st->deltax, y, width() - 2 * _st->deltax, h);
|
||||
} else {
|
||||
int sw = _area->scrollWidth(), rw = width() - 2 * _st->deltay, w = sw ? int32((rw * int64(_area->width())) / sw) : 0;
|
||||
int sw = _area->scrollWidth(), rw = width(), w = sw ? int32((rw * int64(_area->width())) / sw) : 0;
|
||||
if (w >= rw || !_area->scrollLeftMax() || rw < _st->minHeight) {
|
||||
if (!isHidden()) hide();
|
||||
return;
|
||||
@@ -90,11 +90,11 @@ void ScrollBar::updateBar(bool force) {
|
||||
int slm = _area->scrollLeftMax(), x = slm ? int32(((rw - w) * int64(_area->scrollLeft())) / slm) : 0;
|
||||
if (x > rw - w) x = rw - w;
|
||||
|
||||
newBar = QRect(x + _st->deltay, _st->deltax, w, height() - 2 * _st->deltax);
|
||||
newBar = QRect(x, _st->deltax, w, height() - 2 * _st->deltax);
|
||||
}
|
||||
if (newBar != _bar) {
|
||||
_bar = newBar;
|
||||
update();
|
||||
update();// parentWidget()->update(geometry());
|
||||
}
|
||||
if (_vertical) {
|
||||
bool newTopSh = (_st->topsh < 0) || (_area->scrollTop() > _st->topsh), newBottomSh = (_st->bottomsh < 0) || (_area->scrollTop() < _area->scrollTopMax() - _st->bottomsh);
|
||||
@@ -119,15 +119,16 @@ void ScrollBar::paintEvent(QPaintEvent *e) {
|
||||
if (!a_bg.current().alpha() && !a_bar.current().alpha()) return;
|
||||
QPainter p(this);
|
||||
|
||||
int32 deltax = _vertical ? _st->deltax : _st->deltay, deltay = _vertical ? _st->deltay : _st->deltax;
|
||||
int32 deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0;
|
||||
int32 deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax;
|
||||
p.setPen(Qt::NoPen);
|
||||
if (_st->round) {
|
||||
p.setBrush(a_bg.current());
|
||||
p.drawRoundedRect(QRect(deltax, deltay, width() - 2 * deltax, height() - 2 * deltay), _st->round, _st->round);
|
||||
p.drawRoundedRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), _st->round, _st->round);
|
||||
p.setBrush(a_bar.current());
|
||||
p.drawRoundedRect(_bar, _st->round, _st->round);
|
||||
} else {
|
||||
p.fillRect(QRect(deltax, deltay, width() - 2 * deltax, height() - 2 * deltay), a_bg.current());
|
||||
p.fillRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), a_bg.current());
|
||||
p.fillRect(_bar, a_bar.current());
|
||||
}
|
||||
}
|
||||
@@ -143,7 +144,7 @@ bool ScrollBar::animStep(float64 ms) {
|
||||
a_bg.update(dt, anim::linear);
|
||||
a_bar.update(dt, anim::linear);
|
||||
}
|
||||
update();
|
||||
update();// parentWidget()->update(geometry());
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -176,6 +177,8 @@ void ScrollBar::leaveEvent(QEvent *e) {
|
||||
anim::start(this);
|
||||
if (_hideIn >= 0) {
|
||||
_hideTimer.start(_hideIn);
|
||||
} else if (_st->hiding) {
|
||||
hideTimeout(_st->hiding);
|
||||
}
|
||||
}
|
||||
_over = _overbar = false;
|
||||
@@ -193,7 +196,7 @@ void ScrollBar::mouseMoveEvent(QMouseEvent *e) {
|
||||
}
|
||||
if (_moving) {
|
||||
int delta = 0, barDelta = _vertical ? (_area->height() - _bar.height()) : (_area->width() - _bar.width());
|
||||
if (barDelta) {
|
||||
if (barDelta > 0) {
|
||||
QPoint d = (e->globalPos() - _dragStart);
|
||||
delta = int32((_vertical ? (d.y() * int64(_area->scrollTopMax())) : (d.x() * int64(_area->scrollLeftMax()))) / barDelta);
|
||||
}
|
||||
@@ -209,7 +212,10 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) {
|
||||
if (_overbar) {
|
||||
_startFrom = _connected->value();
|
||||
} else {
|
||||
_startFrom = _vertical ? int32((e->pos().y() * int64(_area->scrollTopMax())) / height()) : ((e->pos().x() * int64(_area->scrollLeftMax())) / width());
|
||||
int32 val = _vertical ? e->pos().y() : e->pos().x(), div = _vertical ? height() : width();
|
||||
val = (val <= _st->deltat) ? 0 : (val - _st->deltat);
|
||||
div = (div <= _st->deltat + _st->deltab) ? 1 : (div - _st->deltat - _st->deltab);
|
||||
_startFrom = _vertical ? int32((val * int64(_area->scrollTopMax())) / div) : ((val * int64(_area->scrollLeftMax())) / div);
|
||||
_connected->setValue(_startFrom);
|
||||
if (!_overbar) {
|
||||
_overbar = true;
|
||||
@@ -253,19 +259,18 @@ void ScrollBar::resizeEvent(QResizeEvent *e) {
|
||||
}
|
||||
|
||||
ScrollArea::ScrollArea(QWidget *parent, const style::flatScroll &st, bool handleTouch) : QScrollArea(parent),
|
||||
_st(st),
|
||||
_disabled(false), _st(st),
|
||||
hor(this, false, &_st), vert(this, true, &_st), topSh(this, &_st), bottomSh(this, &_st),
|
||||
_touchEnabled(handleTouch), _touchScroll(false), _touchPress(false), _touchRightButton(false),
|
||||
_touchScrollState(TouchScrollManual), _touchPrevPosValid(false), _touchWaitingAcceleration(false),
|
||||
_touchSpeedTime(0), _touchAccelerationTime(0), _touchTime(0), _widgetAcceptsTouch(false) {
|
||||
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SIGNAL(scrolled()));
|
||||
connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SIGNAL(scrolled()));
|
||||
setLayoutDirection(cLangDir());
|
||||
|
||||
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onScrolled()));
|
||||
connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onScrolled()));
|
||||
connect(&vert, SIGNAL(topShadowVisibility(bool)), &topSh, SLOT(changeVisibility(bool)));
|
||||
connect(&vert, SIGNAL(bottomShadowVisibility(bool)), &bottomSh, SLOT(changeVisibility(bool)));
|
||||
vert.updateBar(true);
|
||||
if (_st.hiding) {
|
||||
connect(this, SIGNAL(scrolled()), this, SLOT(onScrolled()));
|
||||
}
|
||||
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
@@ -292,19 +297,31 @@ void ScrollArea::touchDeaccelerate(int32 elapsed) {
|
||||
}
|
||||
|
||||
void ScrollArea::onScrolled() {
|
||||
bool em = false;
|
||||
int32 horValue = horizontalScrollBar()->value(), vertValue = verticalScrollBar()->value();
|
||||
if (_horValue != horValue) {
|
||||
_horValue = horValue;
|
||||
if (_st.hiding) {
|
||||
hor.hideTimeout(_st.hiding);
|
||||
if (_disabled) {
|
||||
horizontalScrollBar()->setValue(_horValue);
|
||||
} else {
|
||||
_horValue = horValue;
|
||||
if (_st.hiding) {
|
||||
hor.hideTimeout(_st.hiding);
|
||||
}
|
||||
em = true;
|
||||
}
|
||||
}
|
||||
if (_vertValue != vertValue) {
|
||||
_vertValue = vertValue;
|
||||
if (_st.hiding) {
|
||||
vert.hideTimeout(_st.hiding);
|
||||
if (_disabled) {
|
||||
verticalScrollBar()->setValue(_vertValue);
|
||||
} else {
|
||||
_vertValue = vertValue;
|
||||
if (_st.hiding) {
|
||||
vert.hideTimeout(_st.hiding);
|
||||
}
|
||||
em = true;
|
||||
}
|
||||
}
|
||||
if (em) emit scrolled();
|
||||
}
|
||||
|
||||
int ScrollArea::scrollWidth() const {
|
||||
@@ -528,6 +545,21 @@ void ScrollArea::touchScrollUpdated(const QPoint &screenPos) {
|
||||
touchUpdateSpeed();
|
||||
}
|
||||
|
||||
void ScrollArea::disableScroll(bool dis) {
|
||||
_disabled = dis;
|
||||
if (_disabled) {
|
||||
hor.hideTimeout(0);
|
||||
vert.hideTimeout(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollArea::scrollContentsBy(int dx, int dy) {
|
||||
if (_disabled) {
|
||||
return;
|
||||
}
|
||||
QScrollArea::scrollContentsBy(dx, dy);
|
||||
}
|
||||
|
||||
bool ScrollArea::touchScroll(const QPoint &delta) {
|
||||
int32 scTop = scrollTop(), scMax = scrollTopMax(), scNew = snap(scTop - delta.y(), 0, scMax);
|
||||
if (scNew == scTop) return false;
|
||||
@@ -559,10 +591,14 @@ void ScrollArea::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
|
||||
void ScrollArea::enterEvent(QEvent *e) {
|
||||
if (_disabled) return;
|
||||
if (_st.hiding) {
|
||||
hor.hideTimeout(_st.hiding);
|
||||
vert.hideTimeout(_st.hiding);
|
||||
}
|
||||
TWidget *p(tparent());
|
||||
if (p) p->leaveToChildEvent(e);
|
||||
return QScrollArea::enterEvent(e);
|
||||
}
|
||||
|
||||
void ScrollArea::leaveEvent(QEvent *e) {
|
||||
@@ -570,6 +606,9 @@ void ScrollArea::leaveEvent(QEvent *e) {
|
||||
hor.hideTimeout(0);
|
||||
vert.hideTimeout(0);
|
||||
}
|
||||
TWidget *p(tparent());
|
||||
if (p) p->enterFromChildEvent(e);
|
||||
return QScrollArea::leaveEvent(e);
|
||||
}
|
||||
|
||||
void ScrollArea::scrollToY(int toTop, int toBottom) {
|
||||
|
||||
@@ -134,6 +134,7 @@ public:
|
||||
public slots:
|
||||
|
||||
void scrollToY(int toTop, int toBottom = -1);
|
||||
void disableScroll(bool dis);
|
||||
void onScrolled();
|
||||
|
||||
void onTouchTimer();
|
||||
@@ -146,6 +147,16 @@ signals:
|
||||
void scrollFinished();
|
||||
void geometryChanged();
|
||||
|
||||
protected:
|
||||
|
||||
void scrollContentsBy(int dx, int dy);
|
||||
TWidget *tparent() {
|
||||
return qobject_cast<TWidget*>(parentWidget());
|
||||
}
|
||||
const TWidget *tparent() const {
|
||||
return qobject_cast<const TWidget*>(parentWidget());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool touchScroll(const QPoint &delta);
|
||||
@@ -156,6 +167,8 @@ private:
|
||||
void touchUpdateSpeed();
|
||||
void touchDeaccelerate(int32 elapsed);
|
||||
|
||||
bool _disabled;
|
||||
|
||||
style::flatScroll _st;
|
||||
ScrollBar hor, vert;
|
||||
ScrollShadow topSh, bottomSh;
|
||||
|
||||
@@ -26,6 +26,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include <QtGui/QCursor>
|
||||
#include <QtGui/QFont>
|
||||
|
||||
inline QRect rtlrect(int x, int y, int w, int h, int outerw) {
|
||||
return rtl() ? QRect(outerw - x - w, y, w, h) : QRect(x, y, w, h);
|
||||
}
|
||||
inline QRect centerrect(const QRect &inRect, const QRect &rect) {
|
||||
return QRect(inRect.x() + (inRect.width() - rect.width()) / 2, inRect.y() + (inRect.height() - rect.height()) / 2, rect.width(), rect.height());
|
||||
}
|
||||
|
||||
namespace style {
|
||||
|
||||
class FontData;
|
||||
@@ -197,14 +204,17 @@ inline bool operator!=(const Font &a, const Font &b) {
|
||||
typedef QMap<uint32, ColorData*> ColorDatas;
|
||||
extern ColorDatas _colorsMap;
|
||||
|
||||
extern int _spriteWidth;
|
||||
|
||||
typedef float64 number;
|
||||
typedef QString string;
|
||||
typedef QRect rect;
|
||||
class sprite : public rect {
|
||||
|
||||
class sprite : public rect {
|
||||
public:
|
||||
sprite() {
|
||||
}
|
||||
sprite(int left, int top, int width, int height) : rect(left, top, width, height) {
|
||||
sprite(int left, int top, int width, int height) : rect(rtl() ? (_spriteWidth - left - width) : left, top, width, height) {
|
||||
}
|
||||
inline int pxWidth() const {
|
||||
return rect::width() / cIntRetinaFactor();
|
||||
@@ -260,3 +270,7 @@ inline bool operator!=(const Font &a, const Font &b) {
|
||||
void stopManager();
|
||||
|
||||
};
|
||||
|
||||
inline QRect centersprite(const QRect &inRect, const style::sprite &sprite) {
|
||||
return centerrect(inRect, QRect(QPoint(0, 0), sprite.pxSize()));
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace {
|
||||
|
||||
const QRegularExpression _reDomain(QString::fromUtf8("(?<![A-Za-z\\$0-9А-Яа-яёЁ\\-\\_%=\\.])(?:([a-zA-Z]+)://)?((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){1,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"));
|
||||
const QRegularExpression _reExplicitDomain(QString::fromUtf8("(?<![A-Za-z\\$0-9А-Яа-яёЁ\\-\\_%=\\.])(?:([a-zA-Z]+)://)((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){0,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"));
|
||||
const QRegularExpression _reDomain(QString::fromUtf8("(?<![\\w\\$\\-\\_%=\\.])(?:([a-zA-Z]+)://)?((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){1,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"), QRegularExpression::UseUnicodePropertiesOption);
|
||||
const QRegularExpression _reExplicitDomain(QString::fromUtf8("(?<![\\w\\$\\-\\_%=\\.])(?:([a-zA-Z]+)://)((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){0,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"), QRegularExpression::UseUnicodePropertiesOption);
|
||||
const QRegularExpression _reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$"));
|
||||
const QRegularExpression _reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
|
||||
const QRegularExpression _reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[\\w]{2,64}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
|
||||
@@ -239,26 +239,6 @@ const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink)
|
||||
return (result < end && *result == TextCommand) ? (result + 1) : from;
|
||||
}
|
||||
|
||||
QString textEmojiString(EmojiPtr emoji) {
|
||||
QString result;
|
||||
result.reserve(emoji->len + (emoji->postfix ? 1 : 0));
|
||||
switch (emoji->len) {
|
||||
case 1: result.append(QChar(emoji->code & 0xFFFF)); break;
|
||||
case 2:
|
||||
result.append(QChar((emoji->code >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->code & 0xFFFF));
|
||||
break;
|
||||
case 4:
|
||||
result.append(QChar((emoji->code >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->code & 0xFFFF));
|
||||
result.append(QChar((emoji->code2 >> 16) & 0xFFFF));
|
||||
result.append(QChar(emoji->code2 & 0xFFFF));
|
||||
break;
|
||||
}
|
||||
if (emoji->postfix) result.append(QChar(emoji->postfix));
|
||||
return result;
|
||||
}
|
||||
|
||||
class TextParser {
|
||||
public:
|
||||
|
||||
@@ -515,24 +495,19 @@ public:
|
||||
}
|
||||
|
||||
void parseEmojiFromCurrent() {
|
||||
const EmojiData *e = getEmoji(chInt);
|
||||
int len = 0, skipped = (chInt > 0xFFFFU) ? 1 : 0;
|
||||
EmojiPtr e = emojiFromText(ptr - skipped, end, len);
|
||||
if (!e) return;
|
||||
|
||||
if (e->len > 2) {
|
||||
if (ptr + 2 >= end || e->code2 != ((uint32((ptr + 1)->unicode()) << 16) | uint32((ptr + 2)->unicode()))) {
|
||||
return;
|
||||
} else {
|
||||
_t->_text.push_back(*++ptr);
|
||||
_t->_text.push_back(*++ptr);
|
||||
}
|
||||
}
|
||||
int emojiLen = e->len;
|
||||
if (ptr + 1 < end && (ptr + 1)->unicode() == 0xFE0F) {
|
||||
for (int l = len - skipped - 1; l > 0; --l) {
|
||||
_t->_text.push_back(*++ptr);
|
||||
++emojiLen;
|
||||
}
|
||||
if (e->postfix && _t->_text.at(_t->_text.size() - 1).unicode() != e->postfix) {
|
||||
_t->_text.push_back(e->postfix);
|
||||
++len;
|
||||
}
|
||||
|
||||
createBlock(-emojiLen);
|
||||
createBlock(-len);
|
||||
emoji = e;
|
||||
}
|
||||
|
||||
@@ -759,9 +734,15 @@ namespace {
|
||||
void TextLink::onClick(Qt::MouseButton button) const {
|
||||
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
|
||||
QString url = TextLink::encoded();
|
||||
QRegularExpressionMatch telegramMe = QRegularExpression(qsl("^https?://telegram\\.me/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
|
||||
if (telegramMe.hasMatch()) {
|
||||
App::openUserByName(telegramMe.captured(1));
|
||||
QRegularExpressionMatch telegramMeUser = QRegularExpression(qsl("^https?://telegram\\.me/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
|
||||
QRegularExpressionMatch telegramMeGroup = QRegularExpression(qsl("^https?://telegram\\.me/joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
|
||||
QRegularExpressionMatch telegramMeStickers = QRegularExpression(qsl("^https?://telegram\\.me/addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
|
||||
if (telegramMeUser.hasMatch()) {
|
||||
App::openUserByName(telegramMeUser.captured(1));
|
||||
} else if (telegramMeGroup.hasMatch()) {
|
||||
App::joinGroupByHash(telegramMeGroup.captured(1));
|
||||
} else if (telegramMeStickers.hasMatch()) {
|
||||
App::stickersBox(telegramMeStickers.captured(1));
|
||||
} else if (QRegularExpression(qsl("^tg://[a-zA-Z0-9]+"), QRegularExpression::CaseInsensitiveOption).match(url).hasMatch()) {
|
||||
App::openLocalUrl(url);
|
||||
} else {
|
||||
@@ -887,7 +868,7 @@ public:
|
||||
_align = align;
|
||||
|
||||
_parDirection = _t->_startDir;
|
||||
if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = langDir();
|
||||
if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = cLangDir();
|
||||
if ((*_t->_blocks.cbegin())->type() != TextBlockNewline) {
|
||||
initNextParagraph(_t->_blocks.cbegin());
|
||||
}
|
||||
@@ -926,7 +907,7 @@ public:
|
||||
}
|
||||
|
||||
_parDirection = static_cast<NewlineBlock*>(b)->nextDirection();
|
||||
if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = langDir();
|
||||
if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = cLangDir();
|
||||
initNextParagraph(i + 1);
|
||||
|
||||
longWordLine = true;
|
||||
@@ -1148,12 +1129,12 @@ public:
|
||||
|
||||
if ((selectFromStart && _parDirection == Qt::LeftToRight) || (selectTillEnd && _parDirection == Qt::RightToLeft)) {
|
||||
if (x > _x) {
|
||||
_p->fillRect(QRectF(_x.toReal(), _y + _yDelta, (x - _x).toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF(_x.toReal(), _y + _yDelta, (x - _x).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
}
|
||||
if ((selectTillEnd && _parDirection == Qt::LeftToRight) || (selectFromStart && _parDirection == Qt::RightToLeft)) {
|
||||
if (x < _x + _wLeft) {
|
||||
_p->fillRect(QRectF((x + _w - _wLeft).toReal(), _y + _yDelta, (_x + _wLeft - x).toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF((x + _w - _wLeft).toReal(), _y + _yDelta, (_x + _wLeft - x).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1311,19 +1292,19 @@ public:
|
||||
const QChar *chFrom = _str + currentBlock->from(), *chTo = chFrom + ((nextBlock ? nextBlock->from() : _t->_text.size()) - currentBlock->from());
|
||||
if (_localFrom + si.position >= _selectedFrom) { // could be without space
|
||||
if (chTo == chFrom || (chTo - 1)->unicode() != QChar::Space || _selectedTo >= (chTo - _str)) {
|
||||
_p->fillRect(QRectF(x.toReal(), _y + _yDelta, si.width.toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF(x.toReal(), _y + _yDelta, si.width.toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
} else { // or with space
|
||||
_p->fillRect(QRectF(glyphX.toReal(), _y + _yDelta, currentBlock->f_width().toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF(glyphX.toReal(), _y + _yDelta, currentBlock->f_width().toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
} else if (chTo > chFrom && (chTo - 1)->unicode() == QChar::Space && (chTo - 1 - _str) >= _selectedFrom) {
|
||||
if (rtl) { // rtl space only
|
||||
_p->fillRect(QRectF(x.toReal(), _y + _yDelta, (glyphX - x).toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF(x.toReal(), _y + _yDelta, (glyphX - x).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
} else { // ltr space only
|
||||
_p->fillRect(QRectF((x + currentBlock->f_width()).toReal(), _y + _yDelta, (si.width - currentBlock->f_width()).toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF((x + currentBlock->f_width()).toReal(), _y + _yDelta, (si.width - currentBlock->f_width()).toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
}
|
||||
}
|
||||
_p->drawPixmap(QPoint((glyphX + int(st::emojiPadding)).toInt(), _y + _yDelta + emojiY), App::emojis(), QRect(static_cast<EmojiBlock*>(currentBlock)->emoji->x, static_cast<EmojiBlock*>(currentBlock)->emoji->y, st::emojiImgSize, st::emojiImgSize));
|
||||
emojiDraw(*_p, static_cast<EmojiBlock*>(currentBlock)->emoji, (glyphX + int(st::emojiPadding)).toInt(), _y + _yDelta + emojiY);
|
||||
// } else if (_p && currentBlock->type() == TextBlockSkip) { // debug
|
||||
// _p->fillRect(QRect(x.toInt(), _y, currentBlock->width(), static_cast<SkipBlock*>(currentBlock)->height()), QColor(0, 0, 0, 32));
|
||||
}
|
||||
@@ -1445,7 +1426,7 @@ public:
|
||||
}
|
||||
}
|
||||
if (rtl) selX = x + itemWidth - (selX - x) - selWidth;
|
||||
_p->fillRect(QRectF(selX.toReal(), _y + _yDelta, selWidth.toReal(), _fontHeight), _textStyle->selectBG->b);
|
||||
_p->fillRect(QRectF(selX.toReal(), _y + _yDelta, selWidth.toReal(), _fontHeight), _textStyle->selectBg->b);
|
||||
}
|
||||
|
||||
_p->drawTextItem(QPointF(x.toReal(), textY), gf);
|
||||
@@ -2262,6 +2243,21 @@ _startDir(other._startDir)
|
||||
}
|
||||
}
|
||||
|
||||
Text &Text::operator=(const Text &other) {
|
||||
_minResizeWidth = other._minResizeWidth;
|
||||
_maxWidth = other._maxWidth;
|
||||
_minHeight = other._minHeight;
|
||||
_text = other._text;
|
||||
_font = other._font;
|
||||
_blocks = TextBlocks(other._blocks.size());
|
||||
_links = other._links;
|
||||
_startDir = other._startDir;
|
||||
for (int32 i = 0, l = _blocks.size(); i < l; ++i) {
|
||||
_blocks[i] = other._blocks.at(i)->clone();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Text::setText(style::font font, const QString &text, const TextParseOptions &options) {
|
||||
if (!_textStyle) _initDefault();
|
||||
_font = font;
|
||||
@@ -2613,7 +2609,7 @@ QString Text::original(uint16 selectedFrom, uint16 selectedTo, bool expandLinks)
|
||||
result += r;
|
||||
} else {
|
||||
QUrl u(url);
|
||||
if (r.size() > 3 && _text.midRef(lnkFrom, r.size() - 3) == (u.isValid() ? u.toDisplayString() : url).midRef(0, r.size() - 3)) { // same link
|
||||
if (r.size() <= 3 || _text.midRef(lnkFrom, r.size() - 3) == (u.isValid() ? u.toDisplayString() : url).midRef(0, r.size() - 3)) { // same link
|
||||
result += url;
|
||||
} else {
|
||||
result.append(r).append(qsl(" ( ")).append(url).append(qsl(" )"));
|
||||
@@ -4047,29 +4043,19 @@ bool textSplit(QString &sendingText, QString &leftText, int32 limit) {
|
||||
}
|
||||
}
|
||||
}
|
||||
EmojiPtr e = 0;
|
||||
if (ch->isHighSurrogate()) {
|
||||
if (ch + 1 < end && (ch + 1)->isLowSurrogate()) {
|
||||
e = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
|
||||
if (!e) {
|
||||
++ch;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ch + 1 < end) {
|
||||
if (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3) {
|
||||
e = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
|
||||
} else if ((ch + 1)->unicode() == 0xFE0F) {
|
||||
e = getEmoji(ch->unicode());
|
||||
}
|
||||
}
|
||||
}
|
||||
int elen = 0;
|
||||
EmojiPtr e = emojiFromText(ch, end, elen);
|
||||
if (e) {
|
||||
ch += (e->len - 1);
|
||||
if (ch + 1 < end && (ch + 1)->unicode() == 0xFE0F) {
|
||||
++ch;
|
||||
++s;
|
||||
for (int i = 0; i < elen; ++i, ++ch, ++s) {
|
||||
if (ch->isHighSurrogate() && i + 1 < elen && (ch + 1)->isLowSurrogate()) {
|
||||
++ch;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
--ch;
|
||||
--s;
|
||||
} else if (ch->isHighSurrogate() && ch + 1 < end && (ch + 1)->isLowSurrogate()) {
|
||||
++ch;
|
||||
}
|
||||
if (s >= limit) {
|
||||
sendingText = leftText.mid(0, good - start);
|
||||
@@ -4090,7 +4076,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
initLinkSets();
|
||||
int32 len = text.size(), nextCmd = rich ? 0 : len;
|
||||
const QChar *start = text.unicode(), *end = start + text.size();
|
||||
for (int32 offset = 0, matchOffset = offset; offset < len;) {
|
||||
for (int32 offset = 0, matchOffset = offset, mentionSkip = 0; offset < len;) {
|
||||
if (nextCmd <= offset) {
|
||||
for (nextCmd = offset; nextCmd < len; ++nextCmd) {
|
||||
if (*(start + nextCmd) == TextCommand) {
|
||||
@@ -4101,8 +4087,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
QRegularExpressionMatch mDomain = _reDomain.match(text, matchOffset);
|
||||
QRegularExpressionMatch mExplicitDomain = _reExplicitDomain.match(text, matchOffset);
|
||||
QRegularExpressionMatch mHashtag = withHashtags ? _reHashtag.match(text, matchOffset) : QRegularExpressionMatch();
|
||||
QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, matchOffset) : QRegularExpressionMatch();
|
||||
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch() && !mMention.hasMatch()) break;
|
||||
QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch();
|
||||
|
||||
LinkRange link;
|
||||
int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
|
||||
@@ -4121,7 +4106,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
--hashtagEnd;
|
||||
}
|
||||
}
|
||||
if (mMention.hasMatch()) {
|
||||
while (mMention.hasMatch()) {
|
||||
if (!mMention.capturedRef(1).isEmpty()) {
|
||||
++mentionOffset;
|
||||
}
|
||||
@@ -4129,10 +4114,21 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
--mentionEnd;
|
||||
}
|
||||
if (!(start + mentionOffset + 1)->isLetter() || !(start + mentionEnd - 1)->isLetterOrNumber()) {
|
||||
mentionOffset = mentionEnd = INT_MAX;
|
||||
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break;
|
||||
mentionSkip = mentionEnd;
|
||||
mMention = _reMention.match(text, qMax(mentionSkip, matchOffset));
|
||||
if (mMention.hasMatch()) {
|
||||
mentionOffset = mMention.capturedStart();
|
||||
mentionEnd = mMention.capturedEnd();
|
||||
} else {
|
||||
mentionOffset = INT_MAX;
|
||||
mentionEnd = INT_MAX;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mMention.hasMatch() && !mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break;
|
||||
|
||||
if (explicitDomainOffset < domainOffset) {
|
||||
domainOffset = explicitDomainOffset;
|
||||
domainEnd = explicitDomainEnd;
|
||||
@@ -4237,3 +4233,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
|
||||
|
||||
return lnkRanges;
|
||||
}
|
||||
|
||||
void emojiDraw(QPainter &p, EmojiPtr e, int x, int y) {
|
||||
p.drawPixmap(QPoint(x, y), App::emojis(), QRect(e->x * ESize, e->y * ESize, ESize, ESize));
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich = false);
|
||||
|
||||
#include "gui/emoji_config.h"
|
||||
|
||||
void emojiDraw(QPainter &p, EmojiPtr e, int x, int y);
|
||||
|
||||
#include "../../../QtStatic/qtbase/src/gui/text/qfontengine_p.h"
|
||||
|
||||
enum TextBlockType {
|
||||
@@ -424,6 +426,7 @@ public:
|
||||
Text(int32 minResizeWidth = QFIXED_MAX);
|
||||
Text(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions, int32 minResizeWidth = QFIXED_MAX, bool richText = false);
|
||||
Text(const Text &other);
|
||||
Text &operator=(const Text &other);
|
||||
|
||||
int32 countHeight(int32 width) const;
|
||||
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions);
|
||||
@@ -432,6 +435,10 @@ public:
|
||||
void setLink(uint16 lnkIndex, const TextLinkPtr &lnk);
|
||||
bool hasLinks() const;
|
||||
|
||||
bool hasSkipBlock() const {
|
||||
return _blocks.isEmpty() ? false : _blocks.back()->type() == TextBlockSkip;
|
||||
}
|
||||
|
||||
int32 maxWidth() const {
|
||||
return _maxWidth.ceil().toInt();
|
||||
}
|
||||
@@ -532,8 +539,6 @@ QString textcmdStartColor(const style::color &color);
|
||||
QString textcmdStopColor();
|
||||
const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink = true);
|
||||
|
||||
QString textEmojiString(EmojiPtr emoji);
|
||||
|
||||
inline bool chIsSpace(QChar ch, bool rich = false) {
|
||||
return ch.isSpace() || (ch < 32 && !(rich && ch == TextCommand)) || (ch == QChar::ParagraphSeparator) || (ch == QChar::LineSeparator) || (ch == QChar::ObjectReplacementCharacter) || (ch == QChar::SoftHyphen) || (ch == QChar::CarriageReturn) || (ch == QChar::Tabulation);
|
||||
}
|
||||
@@ -544,10 +549,11 @@ inline bool chIsTrimmed(QChar ch, bool rich = false) {
|
||||
return (!rich || ch != TextCommand) && (chIsSpace(ch) || chIsBad(ch));
|
||||
}
|
||||
inline bool chIsDiac(QChar ch) { // diac and variation selectors
|
||||
return (ch >= 768 && ch < 880) || (ch >= 7616 && ch < 7680) || (ch >= 8400 && ch < 8448) || (ch >= 65056 && ch < 65072);
|
||||
QChar::Category c = ch.category();
|
||||
return (c == QChar::Mark_NonSpacing);
|
||||
}
|
||||
inline int32 chMaxDiacAfterSymbol() {
|
||||
return 4;
|
||||
return 2;
|
||||
}
|
||||
inline bool chIsNewline(QChar ch) {
|
||||
return (ch == QChar::LineFeed || ch == 156);
|
||||
|
||||
@@ -20,9 +20,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "application.h"
|
||||
|
||||
namespace {
|
||||
Qt::LayoutDirection _dir = Qt::LeftToRight;
|
||||
bool _rtl = false;
|
||||
|
||||
void _sendResizeEvents(QWidget *target) {
|
||||
QResizeEvent e(target->size(), QSize());
|
||||
QApplication::sendEvent(target, &e);
|
||||
@@ -37,19 +34,6 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
void rtl(bool is) {
|
||||
_rtl = is;
|
||||
_dir = _rtl ? Qt::RightToLeft : Qt::LeftToRight;
|
||||
}
|
||||
|
||||
bool rtl() {
|
||||
return _rtl;
|
||||
}
|
||||
|
||||
Qt::LayoutDirection langDir() { // current lang dependent
|
||||
return _dir;
|
||||
}
|
||||
|
||||
QPixmap myGrab(QWidget *target, const QRect &rect) {
|
||||
if (!cRetina()) return target->grab(rect);
|
||||
|
||||
|
||||
@@ -17,38 +17,107 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
void rtl(bool is);
|
||||
bool rtl();
|
||||
Qt::LayoutDirection langDir();
|
||||
|
||||
class Widget : public QWidget {
|
||||
public:
|
||||
|
||||
Widget(QWidget *parent = 0) : QWidget(parent) {
|
||||
}
|
||||
void moveToLeft(int x, int y, int w) {
|
||||
move(rtl() ? (x + w - width()) : x, y);
|
||||
void moveToLeft(int x, int y, int outerw) {
|
||||
move(rtl() ? (outerw - x - width()) : x, y);
|
||||
}
|
||||
void moveToRight(int x, int y, int w) {
|
||||
move(rtl() ? x : (x + w - width()), y);
|
||||
void moveToRight(int x, int y, int outerw) {
|
||||
move(rtl() ? x : (outerw - x - width()), y);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
namespace App {
|
||||
const QPixmap &sprite();
|
||||
}
|
||||
|
||||
class Painter : public QPainter {
|
||||
public:
|
||||
explicit Painter(QPaintDevice *device) : QPainter(device) {
|
||||
}
|
||||
|
||||
void drawTextLeft(int x, int y, int w, const QString &text, int textWidth = -1) {
|
||||
void drawTextLeft(int x, int y, int outerw, const QString &text, int textWidth = -1) {
|
||||
QFontMetrics m(fontMetrics());
|
||||
if (rtl() && textWidth < 0) textWidth = m.width(text);
|
||||
drawText(x + (rtl() ? (w - textWidth) : 0), y + m.ascent(), text);
|
||||
drawText(rtl() ? (outerw - x - textWidth) : x, y + m.ascent(), text);
|
||||
}
|
||||
void drawTextRight(int x, int y, int w, const QString &text, int textWidth = -1) {
|
||||
void drawTextRight(int x, int y, int outerw, const QString &text, int textWidth = -1) {
|
||||
QFontMetrics m(fontMetrics());
|
||||
if (!rtl() && textWidth < 0) textWidth = m.width(text);
|
||||
drawText(x + (rtl() ? 0 : (w - textWidth)), y + m.ascent(), text);
|
||||
drawText(rtl() ? x : (outerw - x - textWidth), y + m.ascent(), text);
|
||||
}
|
||||
void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
drawPixmap(QPoint(rtl() ? (outerw - x - (from.width() / pix.devicePixelRatio())) : x, y), pix, from);
|
||||
}
|
||||
void drawPixmapLeft(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
return drawPixmapLeft(p.x(), p.y(), outerw, pix, from);
|
||||
}
|
||||
void drawPixmapLeft(int x, int y, int w, int h, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
drawPixmap(QRect(rtl() ? (outerw - x - w) : x, y, w, h), pix, from);
|
||||
}
|
||||
void drawPixmapLeft(const QRect &r, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
return drawPixmapLeft(r.x(), r.y(), r.width(), r.height(), outerw, pix, from);
|
||||
}
|
||||
void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix) {
|
||||
drawPixmap(QPoint(rtl() ? (outerw - x - (pix.width() / pix.devicePixelRatio())) : x, y), pix);
|
||||
}
|
||||
void drawPixmapLeft(const QPoint &p, int outerw, const QPixmap &pix) {
|
||||
return drawPixmapLeft(p.x(), p.y(), outerw, pix);
|
||||
}
|
||||
void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
drawPixmap(QPoint(rtl() ? x : (outerw - x - (from.width() / pix.devicePixelRatio())), y), pix, from);
|
||||
}
|
||||
void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
return drawPixmapRight(p.x(), p.y(), outerw, pix, from);
|
||||
}
|
||||
void drawPixmapRight(int x, int y, int w, int h, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
drawPixmap(QRect(rtl() ? x : (outerw - x - w), y, w, h), pix, from);
|
||||
}
|
||||
void drawPixmapRight(const QRect &r, int outerw, const QPixmap &pix, const QRect &from) {
|
||||
return drawPixmapRight(r.x(), r.y(), r.width(), r.height(), outerw, pix, from);
|
||||
}
|
||||
void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix) {
|
||||
drawPixmap(QPoint(rtl() ? x : (outerw - x - (pix.width() / pix.devicePixelRatio())), y), pix);
|
||||
}
|
||||
void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix) {
|
||||
return drawPixmapRight(p.x(), p.y(), outerw, pix);
|
||||
}
|
||||
void drawSprite(int x, int y, const style::sprite &sprite) {
|
||||
return drawPixmap(QPoint(x, y), App::sprite(), sprite);
|
||||
}
|
||||
void drawSprite(const QPoint &p, const style::sprite &sprite) {
|
||||
return drawPixmap(p, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteLeft(int x, int y, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapLeft(x, y, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteLeft(const QPoint &p, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapLeft(p, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteLeft(int x, int y, int w, int h, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapLeft(x, y, w, h, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteLeft(const QRect &r, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapLeft(r, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteRight(int x, int y, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapRight(x, y, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteRight(const QPoint &p, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapRight(p, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteRight(int x, int y, int w, int h, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapRight(x, y, w, h, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteRight(const QRect &r, int outerw, const style::sprite &sprite) {
|
||||
return drawPixmapRight(r, outerw, App::sprite(), sprite);
|
||||
}
|
||||
void drawSpriteCenter(const QRect &in, const style::sprite &sprite) {
|
||||
return drawPixmap(QPoint(in.x() + (in.width() - sprite.pxWidth()) / 2, in.y() + (in.height() - sprite.pxHeight()) / 2), App::sprite(), sprite);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -80,10 +149,12 @@ protected:
|
||||
void enterEvent(QEvent *e) {
|
||||
TWidget *p(tparent());
|
||||
if (p) p->leaveToChildEvent(e);
|
||||
return Widget::enterEvent(e);
|
||||
}
|
||||
void leaveEvent(QEvent *e) {
|
||||
TWidget *p(tparent());
|
||||
if (p) p->enterFromChildEvent(e);
|
||||
return Widget::leaveEvent(e);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -34,7 +34,6 @@ extern TextParseOptions _textNameOptions, _textDlgOptions;
|
||||
|
||||
#include "structs.h"
|
||||
|
||||
|
||||
struct History;
|
||||
struct Histories : public QHash<PeerId, History*>, public Animated {
|
||||
typedef QHash<PeerId, History*> Parent;
|
||||
@@ -218,6 +217,9 @@ struct History : public QList<HistoryBlock*> {
|
||||
HistoryItem *currentNotification() {
|
||||
return notifies.isEmpty() ? 0 : notifies.front();
|
||||
}
|
||||
bool hasNotification() const {
|
||||
return !notifies.isEmpty();
|
||||
}
|
||||
void skipNotification() {
|
||||
if (!notifies.isEmpty()) {
|
||||
notifies.pop_front();
|
||||
@@ -620,6 +622,8 @@ private:
|
||||
ItemAnimations &itemAnimations();
|
||||
|
||||
class HistoryReply; // dynamic_cast optimize
|
||||
class HistoryMessage; // dynamic_cast optimize
|
||||
class HistoryForwarded; // dynamic_cast optimize
|
||||
|
||||
class HistoryMedia;
|
||||
class HistoryItem : public HistoryElem {
|
||||
@@ -655,6 +659,9 @@ public:
|
||||
bool detached() const {
|
||||
return !_block;
|
||||
}
|
||||
void attach(HistoryBlock *block) {
|
||||
_block = block;
|
||||
}
|
||||
bool out() const {
|
||||
return _flags & MTPDmessage_flag_out;
|
||||
}
|
||||
@@ -665,6 +672,12 @@ public:
|
||||
bool notifyByFrom() const {
|
||||
return _flags & MTPDmessage_flag_notify_by_from;
|
||||
}
|
||||
bool isMediaUnread() const {
|
||||
return _flags & MTPDmessage_flag_media_unread;
|
||||
}
|
||||
void markMediaRead() {
|
||||
_flags &= ~MTPDmessage_flag_media_unread;
|
||||
}
|
||||
virtual bool needCheck() const {
|
||||
return true;
|
||||
}
|
||||
@@ -722,13 +735,25 @@ public:
|
||||
virtual QString time() const {
|
||||
return QString();
|
||||
}
|
||||
virtual int32 timeWidth() const {
|
||||
virtual int32 timeWidth(bool forText) const {
|
||||
return 0;
|
||||
}
|
||||
virtual bool animating() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize
|
||||
return 0;
|
||||
}
|
||||
virtual const HistoryMessage *toHistoryMessage() const { // dynamic_cast optimize
|
||||
return 0;
|
||||
}
|
||||
virtual HistoryForwarded *toHistoryForwarded() { // dynamic_cast optimize
|
||||
return 0;
|
||||
}
|
||||
virtual const HistoryForwarded *toHistoryForwarded() const { // dynamic_cast optimize
|
||||
return 0;
|
||||
}
|
||||
virtual HistoryReply *toHistoryReply() { // dynamic_cast optimize
|
||||
return 0;
|
||||
}
|
||||
@@ -779,6 +804,9 @@ public:
|
||||
virtual const QString inDialogsText() const = 0;
|
||||
virtual const QString inHistoryText() const = 0;
|
||||
virtual bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0;
|
||||
virtual bool isDisplayed() const {
|
||||
return true;
|
||||
}
|
||||
virtual int32 countHeight(const HistoryItem *parent, int32 width = -1) const {
|
||||
return height();
|
||||
}
|
||||
@@ -802,6 +830,10 @@ public:
|
||||
virtual void updateFrom(const MTPMessageMedia &media) {
|
||||
}
|
||||
|
||||
virtual bool isImageLink() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool updateStickerEmoji() {
|
||||
return false;
|
||||
}
|
||||
@@ -830,7 +862,7 @@ protected:
|
||||
class HistoryPhoto : public HistoryMedia {
|
||||
public:
|
||||
|
||||
HistoryPhoto(const MTPDphoto &photo);
|
||||
HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent);
|
||||
HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0);
|
||||
|
||||
void init();
|
||||
@@ -843,6 +875,7 @@ public:
|
||||
}
|
||||
const QString inDialogsText() const;
|
||||
const QString inHistoryText() const;
|
||||
const Text &captionForClone() const;
|
||||
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
HistoryMedia *clone() const;
|
||||
@@ -870,19 +903,22 @@ public:
|
||||
private:
|
||||
int16 pixw, pixh;
|
||||
PhotoData *data;
|
||||
Text _caption;
|
||||
TextLinkPtr openl;
|
||||
|
||||
};
|
||||
|
||||
QString formatSizeText(qint64 size);
|
||||
QString formatDurationText(qint64 duration);
|
||||
|
||||
class HistoryVideo : public HistoryMedia {
|
||||
public:
|
||||
|
||||
HistoryVideo(const MTPDvideo &video);
|
||||
HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent);
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeVideo;
|
||||
}
|
||||
@@ -907,6 +943,8 @@ private:
|
||||
VideoData *data;
|
||||
TextLinkPtr _openl, _savel, _cancell;
|
||||
|
||||
Text _caption;
|
||||
|
||||
QString _size;
|
||||
int32 _thumbw, _thumbx, _thumby;
|
||||
|
||||
@@ -933,9 +971,15 @@ public:
|
||||
}
|
||||
HistoryMedia *clone() const;
|
||||
|
||||
AudioData *audio() {
|
||||
return data;
|
||||
}
|
||||
|
||||
void regItem(HistoryItem *item);
|
||||
void unregItem(HistoryItem *item);
|
||||
|
||||
void updateFrom(const MTPMessageMedia &media);
|
||||
|
||||
private:
|
||||
AudioData *data;
|
||||
TextLinkPtr _openl, _savel, _cancell;
|
||||
@@ -1064,6 +1108,9 @@ public:
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
bool isDisplayed() const {
|
||||
return !data->pendingTill;
|
||||
}
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeWebPage;
|
||||
@@ -1151,7 +1198,7 @@ private:
|
||||
class HistoryImageLink : public HistoryMedia {
|
||||
public:
|
||||
|
||||
HistoryImageLink(const QString &url);
|
||||
HistoryImageLink(const QString &url, const QString &title = QString(), const QString &description = QString());
|
||||
int32 fullWidth() const;
|
||||
int32 fullHeight() const;
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
@@ -1167,8 +1214,13 @@ public:
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
HistoryMedia *clone() const;
|
||||
|
||||
bool isImageLink() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
ImageLinkData *data;
|
||||
Text _title, _description;
|
||||
TextLinkPtr link;
|
||||
|
||||
};
|
||||
@@ -1181,6 +1233,7 @@ public:
|
||||
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, const QString &msg, HistoryMedia *media);
|
||||
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, DocumentData *doc);
|
||||
|
||||
void initTime();
|
||||
void initMedia(const MTPMessageMedia &media, QString ¤tText);
|
||||
void initMediaFromText(QString ¤tText);
|
||||
void initMediaFromDocument(DocumentData *doc);
|
||||
@@ -1199,7 +1252,10 @@ public:
|
||||
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
bool hasPoint(int32 x, int32 y) const;
|
||||
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
|
||||
virtual void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const;
|
||||
|
||||
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
|
||||
uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
|
||||
return _text.adjustSelection(from, to, type);
|
||||
@@ -1210,7 +1266,9 @@ public:
|
||||
QString notificationText() const;
|
||||
|
||||
void updateMedia(const MTPMessageMedia &media) {
|
||||
if (_media) _media->updateFrom(media);
|
||||
if (_media) {
|
||||
_media->updateFrom(media);
|
||||
}
|
||||
}
|
||||
void updateStickerEmoji();
|
||||
|
||||
@@ -1222,8 +1280,8 @@ public:
|
||||
QString time() const {
|
||||
return _time;
|
||||
}
|
||||
int32 timeWidth() const {
|
||||
return _timeWidth;
|
||||
int32 timeWidth(bool forText) const {
|
||||
return _timeWidth + (forText ? (st::msgDateSpace + (out() ? st::msgDateCheckSpace + st::msgCheckRect.pxWidth() : 0) - st::msgDateDelta.x()) : 0);
|
||||
}
|
||||
virtual bool animating() const {
|
||||
return _media ? _media->animating() : false;
|
||||
@@ -1236,6 +1294,13 @@ public:
|
||||
return from();
|
||||
}
|
||||
|
||||
HistoryMessage *toHistoryMessage() { // dynamic_cast optimize
|
||||
return this;
|
||||
}
|
||||
const HistoryMessage *toHistoryMessage() const { // dynamic_cast optimize
|
||||
return this;
|
||||
}
|
||||
|
||||
~HistoryMessage();
|
||||
|
||||
protected:
|
||||
@@ -1260,10 +1325,13 @@ public:
|
||||
void fwdNameUpdated() const;
|
||||
|
||||
void draw(QPainter &p, uint32 selection) const;
|
||||
void drawForwardedFrom(QPainter &p, int32 x, int32 y, int32 w, bool selected) const;
|
||||
void drawMessageText(QPainter &p, const QRect &trect, uint32 selection) const;
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
bool hasPoint(int32 x, int32 y) const;
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
|
||||
void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const;
|
||||
void getForwardedState(TextLinkPtr &lnk, bool &inText, int32 x, int32 w) const;
|
||||
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
|
||||
|
||||
QDateTime dateForwarded() const {
|
||||
@@ -1274,6 +1342,13 @@ public:
|
||||
}
|
||||
QString selectedText(uint32 selection) const;
|
||||
|
||||
HistoryForwarded *toHistoryForwarded() {
|
||||
return this;
|
||||
}
|
||||
const HistoryForwarded *toHistoryForwarded() const {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
QDateTime fwdDate;
|
||||
@@ -1308,6 +1383,7 @@ public:
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
bool hasPoint(int32 x, int32 y) const;
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
|
||||
void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const;
|
||||
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
|
||||
|
||||
UserData *replyTo() const {
|
||||
|
||||
@@ -250,7 +250,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class HistoryWidget : public QWidget, public RPCSender, public Animated {
|
||||
class HistoryWidget : public TWidget, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -273,6 +273,8 @@ public:
|
||||
void leaveEvent(QEvent *e);
|
||||
void dropEvent(QDropEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
void leaveToChildEvent(QEvent *e);
|
||||
void contextMenuEvent(QContextMenuEvent *e);
|
||||
|
||||
void updateTopBarSelection();
|
||||
@@ -296,8 +298,8 @@ public:
|
||||
QRect historyRect() const;
|
||||
|
||||
void updateTyping(bool typing = true);
|
||||
// void updateStickerPan();
|
||||
void updateRecentStickers();
|
||||
void stickersInstalled(uint64 setId);
|
||||
void typingDone(const MTPBool &result, mtpRequestId req);
|
||||
|
||||
void destroyData();
|
||||
@@ -316,7 +318,6 @@ public:
|
||||
void updateOnlineDisplay(int32 x, int32 w);
|
||||
void updateOnlineDisplayTimer();
|
||||
|
||||
// mtpRequestId onForward(const PeerId &peer, SelectedItemSet toForward);
|
||||
void onShareContact(const PeerId &peer, UserData *contact);
|
||||
void onSendPaths(const PeerId &peer);
|
||||
|
||||
@@ -329,7 +330,7 @@ public:
|
||||
int32 lastScrollTop() const;
|
||||
|
||||
void animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false);
|
||||
bool animStep(float64 ms);
|
||||
bool showStep(float64 ms);
|
||||
void animStop();
|
||||
|
||||
QPoint clampMousePosition(QPoint point);
|
||||
@@ -366,6 +367,10 @@ public:
|
||||
void updatePreview();
|
||||
void previewCancel();
|
||||
|
||||
bool recordStep(float64 ms);
|
||||
bool recordingStep(float64 ms);
|
||||
void stopRecording(bool send);
|
||||
|
||||
~HistoryWidget();
|
||||
|
||||
signals:
|
||||
@@ -379,6 +384,8 @@ public slots:
|
||||
void onReplyToMessage();
|
||||
void onReplyForwardPreviewCancel();
|
||||
|
||||
void onStickerPackInfo();
|
||||
|
||||
void onPreviewParse();
|
||||
void onPreviewCheck();
|
||||
void onPreviewTimeout();
|
||||
@@ -391,10 +398,13 @@ public slots:
|
||||
void onPhotoUploaded(MsgId msgId, const MTPInputFile &file);
|
||||
void onDocumentUploaded(MsgId msgId, const MTPInputFile &file);
|
||||
void onThumbDocumentUploaded(MsgId msgId, const MTPInputFile &file, const MTPInputFile &thumb);
|
||||
void onAudioUploaded(MsgId msgId, const MTPInputFile &file);
|
||||
|
||||
void onDocumentProgress(MsgId msgId);
|
||||
void onAudioProgress(MsgId msgId);
|
||||
|
||||
void onDocumentFailed(MsgId msgId);
|
||||
void onAudioFailed(MsgId msgId);
|
||||
|
||||
void onListScroll();
|
||||
void onHistoryToEnd();
|
||||
@@ -440,6 +450,10 @@ public slots:
|
||||
|
||||
void updateStickers();
|
||||
|
||||
void onRecordError();
|
||||
void onRecordDone(QByteArray result, qint32 samples);
|
||||
void onRecordUpdate(qint16 level, qint32 samples);
|
||||
|
||||
private:
|
||||
|
||||
MsgId _replyToId;
|
||||
@@ -473,7 +487,6 @@ private:
|
||||
void stickersGot(const MTPmessages_AllStickers &stickers);
|
||||
bool stickersFailed(const RPCError &error);
|
||||
|
||||
uint64 _lastStickersUpdate;
|
||||
mtpRequestId _stickersUpdateRequest;
|
||||
|
||||
void writeDraft(MsgId *replyTo = 0, const QString *text = 0, const MessageCursor *cursor = 0, bool *previewCancelled = 0);
|
||||
@@ -507,10 +520,16 @@ private:
|
||||
FlatButton _send;
|
||||
IconedButton _attachDocument, _attachPhoto, _attachEmoji;
|
||||
MessageField _field;
|
||||
Animation _recordAnim, _recordingAnim;
|
||||
bool _recording, _inRecord, _inField;
|
||||
anim::ivalue a_recordingLevel;
|
||||
int32 _recordingSamples;
|
||||
anim::fvalue a_recordOver, a_recordDown;
|
||||
anim::cvalue a_recordCancel;
|
||||
int32 _recordCancelWidth;
|
||||
|
||||
Dropdown _attachType;
|
||||
EmojiPan _emojiPan;
|
||||
// StickerPan _stickerPan;
|
||||
DragState _attachDrag;
|
||||
DragArea _attachDragDocument, _attachDragPhoto;
|
||||
|
||||
@@ -530,6 +549,7 @@ private:
|
||||
|
||||
bool hiderOffered;
|
||||
|
||||
Animation _showAnim;
|
||||
QPixmap _animCache, _bgAnimCache, _animTopBarCache, _bgAnimTopBarCache;
|
||||
anim::ivalue a_coord, a_bgCoord;
|
||||
anim::fvalue a_alpha, a_bgAlpha;
|
||||
|
||||
@@ -153,6 +153,7 @@ void IntroCode::activate() {
|
||||
callTimer.start(1000);
|
||||
error = "";
|
||||
errorAlpha = anim::fvalue(0);
|
||||
sentCode = QString();
|
||||
show();
|
||||
code.setDisabled(false);
|
||||
code.setFocus();
|
||||
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} hat {user} entfernt";
|
||||
"lng_action_user_left" = "{from} hat die Gruppe verlassen";
|
||||
"lng_action_user_joined" = "{from} ist der Gruppe beigetreten";
|
||||
"lng_action_user_joined_by_link" = "{from} ist der Gruppe über den Einladungslink beigetreten";
|
||||
"lng_action_user_registered" = "{from} benutzt nun auch Telegram";
|
||||
"lng_action_removed_photo" = "{from} hat das Gruppenbild entfernt";
|
||||
"lng_action_changed_photo" = "{from} hat das Gruppenbild geändert";
|
||||
"lng_action_changed_title" = "{from} hat den Gruppennamen zu «{title}» geändert";
|
||||
"lng_action_created_chat" = "{from} hat die Gruppe «{title}» erstellt";
|
||||
|
||||
"lng_group_invite_bad_link" = "Der Einladungslink ist \nnicht mehr gültig.";
|
||||
"lng_group_invite_want_join" = "Möchtest du der Gruppe «{title}» beitreten?";
|
||||
"lng_group_invite_join" = "Beitreten";
|
||||
|
||||
"lng_group_invite_link" = "Einladungslink";
|
||||
"lng_group_invite_create" = "Neuer Link";
|
||||
"lng_group_invite_about" = "Jeder, der Telegram installiert hat,\nkann anhand dieses Links in deine Gruppe.";
|
||||
"lng_group_invite_create_new" = "Link widerrufen";
|
||||
"lng_group_invite_about_new" = "Dein vorheriger Link wird ungültig,\nwenn du einen neuen erstellst.";
|
||||
"lng_group_invite_copied" = "Der Einladungslink für die Gruppe\nwurde in die Zwischenablage kopiert.";
|
||||
"lng_group_invite_no_room" = "Leider kannst du dieser Gruppe nicht mehr\nbeitreten, da sie bereits voll ist.";
|
||||
|
||||
"lng_forwarded_from" = "Weitergeleitet von";
|
||||
"lng_in_reply_to" = "Antwort auf";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Videodatei";
|
||||
"lng_media_audio" = "Sprachnachricht";
|
||||
|
||||
"lng_emoji_category0" = "Häufig genutzt";
|
||||
"lng_emoji_category1" = "Personen";
|
||||
"lng_emoji_category2" = "Natur";
|
||||
"lng_emoji_category3" = "Essen & Trinken";
|
||||
"lng_emoji_category4" = "Feiern";
|
||||
"lng_emoji_category5" = "Aktivität";
|
||||
"lng_emoji_category6" = "Reisen & Orte";
|
||||
"lng_emoji_category7" = "Objekte & Symbole";
|
||||
|
||||
"lng_switch_stickers" = "Sticker";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Eigene Sticker";
|
||||
"lng_stickers_remove_pack" = "«{sticker_pack}» entfernen?";
|
||||
"lng_stickers_add_pack" = "{count:_not_used_|# Sticker|# Sticker} hinzufügen";
|
||||
"lng_stickers_share_pack" = "Sticker teilen";
|
||||
"lng_stickers_not_found" = "Sticker-Paket nicht gefunden.";
|
||||
"lng_stickers_copied" = "Sticker-Paket Link in die Zwischenablage kopiert.";
|
||||
"lng_stickers_default_set" = "Große Denker";
|
||||
|
||||
"lng_in_dlg_photo" = "Bild";
|
||||
"lng_in_dlg_video" = "Video";
|
||||
"lng_in_dlg_contact" = "Kontakt";
|
||||
@@ -417,6 +450,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "Senden";
|
||||
"lng_message_ph" = "Schreibe deine Nachricht..";
|
||||
"lng_record_cancel" = "Zum Abbrechen rausbewegen";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "Wähle einen Chat aus, um zu schreiben";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
@@ -458,6 +492,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Video speichern unter..";
|
||||
"lng_context_open_audio" = "Sprachnachricht öffnen";
|
||||
"lng_context_save_audio" = "Sprachnachricht speichern unter..";
|
||||
"lng_context_pack_info" = "Sticker-Paket";
|
||||
"lng_context_open_file" = "Datei öffnen";
|
||||
"lng_context_save_file" = "Datei speichern als..";
|
||||
"lng_context_forward_file" = "Datei weiterleiten";
|
||||
@@ -527,12 +562,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:Keine Nachrichten|# Nachricht|# Nachrichten}";
|
||||
"lng_search_global_results" = "Globale Suchergebnisse";
|
||||
|
||||
"lng_mediaview_save" = "Download";
|
||||
"lng_media_save_progress" = "{ready} von {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Speichern unter…";
|
||||
"lng_mediaview_copy" = "Kopieren";
|
||||
"lng_mediaview_forward" = "Weiterleiten";
|
||||
"lng_mediaview_delete" = "Löschen";
|
||||
"lng_mediaview_photos_all" = "Alle Fotos anzeigen";
|
||||
"lng_mediaview_files_all" = "Alle Dateien anzeigen";
|
||||
"lng_mediaview_single_photo" = "Bild";
|
||||
"lng_mediaview_group_photo" = "Bild";
|
||||
"lng_mediaview_profile_photo" = "Profilbild";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} von {count}";
|
||||
"lng_mediaview_n_of_count" = "Bild {n} von {count}";
|
||||
"lng_mediaview_doc_image" = "Datei";
|
||||
"lng_mediaview_today" = "heute um {time}";
|
||||
@@ -545,7 +585,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop wurde aktualisiert auf Version {version}\n\n{changes}\n\nGesamter Versionsverlauf:\n{link}";
|
||||
"lng_new_version_minor" = "— Fehlerbehebungen und Softwareoptimierungen";
|
||||
"lng_new_version_text" = "— Linkvorschau: Erhalte eine Zusammenfassung für Tweets, YouTube Videos, Instagram Fotos und sonstigen Inhalten.\n— Zweistufige Bestätigung: Lege ein zusätzliches Kennwort fest, das du für die Anmeldung bei deinem Telegram-Konto benötigst.\n— Sitzungslisten: Betrachte alle deine aktiven Telegram-Sitzungen (auf dem Desktop, Tablet und mobilen Geräten) und beende sie.";
|
||||
"lng_new_version_text" = "— Verschicke Sprachnachrichten\n— Sticker verschwinden nicht mehr (Fehlerbehebung)";
|
||||
|
||||
"lng_menu_insert_unicode" = "Unicode-Steuerzeichen einfügen";
|
||||
|
||||
|
||||
@@ -161,19 +161,19 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_settings_crop_profile" = "Elige el cuadrado para tu foto de perfil";
|
||||
"lng_settings_uploading_photo" = "Cargando foto...";
|
||||
|
||||
"lng_username_title" = "Cambiar apodo";
|
||||
"lng_username_about" = "Puedes elegir un apodo en Telegram. \nSi lo haces, otras personas te podrán \nencontrar por ese apodo y contactarte \nsin saber tu número de teléfono.\n\nPuedes usar a-z, 0-9 y guiones bajos.\nLa longitud mínima es de 5 caracteres.";
|
||||
"lng_username_invalid" = "Este apodo es inválido.";
|
||||
"lng_username_occupied" = "Este apodo ya está ocupado.";
|
||||
"lng_username_too_short" = "Este apodo es muy corto.";
|
||||
"lng_username_bad_symbols" = "Este apodo tiene símbolos equivocados.";
|
||||
"lng_username_available" = "Este apodo está disponible.";
|
||||
"lng_username_title" = "Cambiar alias";
|
||||
"lng_username_about" = "Puedes elegir un alias en Telegram. \nSi lo haces, otras personas te podrán \nencontrar por ese alias y contactarte \nsin saber tu número de teléfono.\n\nPuedes usar a-z, 0-9 y guiones bajos.\nLa longitud mínima es de 5 caracteres.";
|
||||
"lng_username_invalid" = "Este alias es inválido.";
|
||||
"lng_username_occupied" = "Este alias ya está ocupado.";
|
||||
"lng_username_too_short" = "Este alias es muy corto.";
|
||||
"lng_username_bad_symbols" = "Este alias tiene símbolos equivocados.";
|
||||
"lng_username_available" = "Este alias está disponible.";
|
||||
"lng_username_not_found" = "No se encontró el usuario @{user}.";
|
||||
|
||||
"lng_settings_section_contact_info" = "Información";
|
||||
"lng_settings_phone_number" = "Número de teléfono:";
|
||||
"lng_settings_username" = "Apodo:";
|
||||
"lng_settings_choose_username" = "Elige un apodo";
|
||||
"lng_settings_username" = "Alias:";
|
||||
"lng_settings_choose_username" = "Elige un alias";
|
||||
|
||||
"lng_settings_section_notify" = "Notificaciones";
|
||||
"lng_settings_desktop_notify" = "Notificaciones de escritorio";
|
||||
@@ -263,7 +263,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_passcode_enter_old" = "Pon tu código actual";
|
||||
"lng_passcode_enter_first" = "Pon un código de acceso";
|
||||
"lng_passcode_enter_new" = "Pon tu nuevo código";
|
||||
"lng_passcode_confirm_new" = "Pon, otra vez, el código";
|
||||
"lng_passcode_confirm_new" = "Repite el nuevo código";
|
||||
"lng_passcode_about" = "Cuando configuras un código, aparece un candado en el menú. Pulsa sobre él para bloquear la aplicación.\n\nNota: si olvidas el código, tendrás que reiniciar la sesión en Telegram Desktop.";
|
||||
"lng_passcode_differ" = "Los códigos de acceso son diferentes";
|
||||
"lng_passcode_wrong" = "Código de acceso equivocado";
|
||||
@@ -281,7 +281,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_cloud_password_enter_old" = "Pon la vieja contraseña";
|
||||
"lng_cloud_password_enter_first" = "Pon una contraseña";
|
||||
"lng_cloud_password_enter_new" = "Pon la nueva contraseña";
|
||||
"lng_cloud_password_confirm_new" = "Pon, otra vez, la contraseña";
|
||||
"lng_cloud_password_confirm_new" = "Repite la nueva contraseña";
|
||||
"lng_cloud_password_hint" = "Pon una pista para la contraseña";
|
||||
"lng_cloud_password_bad" = "La contraseña y la pista no pueden ser iguales.";
|
||||
"lng_cloud_password_email" = "Pon un e-mail de recuperación";
|
||||
@@ -307,7 +307,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_connection_tcp_proxy_rb" = "TCP con socks5-proxy personalizado";
|
||||
"lng_connection_host_ph" = "Nombre del host";
|
||||
"lng_connection_port_ph" = "Puerto";
|
||||
"lng_connection_user_ph" = "Apodo";
|
||||
"lng_connection_user_ph" = "Alias";
|
||||
"lng_connection_password_ph" = "Contraseña";
|
||||
"lng_connection_save" = "Guardar";
|
||||
"lng_settings_show_sessions" = "Mostrar todas las sesiones";
|
||||
@@ -350,7 +350,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_kick" = "Expulsar";
|
||||
"lng_profile_sure_kick" = "¿Quieres expulsar a {user} del grupo?";
|
||||
"lng_profile_loading" = "Cargando...";
|
||||
"lng_profile_shared_media" = "Multimedia compartida";
|
||||
"lng_profile_shared_media" = "Todos los archivos";
|
||||
"lng_profile_no_media" = "No hay multimedia en esta conversación.";
|
||||
"lng_profile_photos" = "{count:_not_used_|# foto|# fotos} »";
|
||||
"lng_profile_photos_header" = "Todas las fotos";
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} expulsó a {user}";
|
||||
"lng_action_user_left" = "{from} dejó el grupo";
|
||||
"lng_action_user_joined" = "{from} se unió al grupo";
|
||||
"lng_action_user_joined_by_link" = "{from} se unió al grupo con un enlace de invitación";
|
||||
"lng_action_user_registered" = "{from} se acaba de unir a Telegram";
|
||||
"lng_action_removed_photo" = "{from} quitó la foto del grupo";
|
||||
"lng_action_changed_photo" = "{from} cambió la foto del grupo";
|
||||
"lng_action_changed_title" = "{from} cambió el nombre del grupo a «{title}»";
|
||||
"lng_action_created_chat" = "{from} creó el grupo «{title}»";
|
||||
|
||||
"lng_group_invite_bad_link" = "El enlace de invitación está \nroto o ha expirado.";
|
||||
"lng_group_invite_want_join" = "¿Quieres unirte al grupo «{title}»?";
|
||||
"lng_group_invite_join" = "Unirme";
|
||||
|
||||
"lng_group_invite_link" = "Invitación";
|
||||
"lng_group_invite_create" = "Crear un enlace de invitación";
|
||||
"lng_group_invite_about" = "Los usuarios de Telegram podrán unirse\na tu grupo a través de este enlace.";
|
||||
"lng_group_invite_create_new" = "Anular enlace";
|
||||
"lng_group_invite_about_new" = "El enlace previo será desactivado\ny generaremos uno nuevo para ti.";
|
||||
"lng_group_invite_copied" = "Enlace de invitación copiado al portapapeles.";
|
||||
"lng_group_invite_no_room" = "No puedes unirte a este grupo porque\nhay muchos miembros en él.";
|
||||
|
||||
"lng_forwarded_from" = "Reenviado desde";
|
||||
"lng_in_reply_to" = "Respondiendo a";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Vídeo";
|
||||
"lng_media_audio" = "Mensaje de voz";
|
||||
|
||||
"lng_emoji_category0" = "Usados frecuentemente";
|
||||
"lng_emoji_category1" = "Gente";
|
||||
"lng_emoji_category2" = "Naturaleza";
|
||||
"lng_emoji_category3" = "Comida y bebidas";
|
||||
"lng_emoji_category4" = "Celebración";
|
||||
"lng_emoji_category5" = "Actividad";
|
||||
"lng_emoji_category6" = "Viajes y lugares";
|
||||
"lng_emoji_category7" = "Objetos y símbolos";
|
||||
|
||||
"lng_switch_stickers" = "Stickers";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Stickers personalizados";
|
||||
"lng_stickers_remove_pack" = "¿Quitar «{sticker_pack}»?";
|
||||
"lng_stickers_add_pack" = "Añadir {count:_not_used_|# sticker|# stickers}";
|
||||
"lng_stickers_share_pack" = "Compartir stickers";
|
||||
"lng_stickers_not_found" = "Pack de stickers no encontrado.";
|
||||
"lng_stickers_copied" = "Enlace del pack de stickers copiado al portapapeles.";
|
||||
"lng_stickers_default_set" = "Grandes personajes";
|
||||
|
||||
"lng_in_dlg_photo" = "Foto";
|
||||
"lng_in_dlg_video" = "Vídeo";
|
||||
"lng_in_dlg_contact" = "Contacto";
|
||||
@@ -417,6 +450,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "Enviar";
|
||||
"lng_message_ph" = "Escribir un mensaje...";
|
||||
"lng_record_cancel" = "Suelta fuera de aquí para cancelar";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "Por favor, elige un chat para comenzar a conversar";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
@@ -444,7 +478,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_open_hashtag" = "Buscar por hashtag";
|
||||
"lng_context_copy_hashtag" = "Copiar hashtag";
|
||||
"lng_context_open_mention" = "Abrir perfil";
|
||||
"lng_context_copy_mention" = "Copiar apodo";
|
||||
"lng_context_copy_mention" = "Copiar alias";
|
||||
"lng_context_open_image" = "Abrir imagen";
|
||||
"lng_context_save_image" = "Guardar como...";
|
||||
"lng_context_forward_image" = "Reenviar imagen";
|
||||
@@ -458,6 +492,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Guardar como...";
|
||||
"lng_context_open_audio" = "Abrir audio";
|
||||
"lng_context_save_audio" = "Guardar como...";
|
||||
"lng_context_pack_info" = "Información del pack";
|
||||
"lng_context_open_file" = "Abrir archivo";
|
||||
"lng_context_save_file" = "Guardar como...";
|
||||
"lng_context_forward_file" = "Reenviar archivo";
|
||||
@@ -527,12 +562,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:No se encontraron mensajes|Found # mensaje|Found # mensajes}";
|
||||
"lng_search_global_results" = "Resultados de la búsqueda global";
|
||||
|
||||
"lng_mediaview_save" = "Descargar";
|
||||
"lng_media_save_progress" = "{ready} de {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Guardar como...";
|
||||
"lng_mediaview_copy" = "Copiar";
|
||||
"lng_mediaview_forward" = "Reenviar";
|
||||
"lng_mediaview_delete" = "Eliminar";
|
||||
"lng_mediaview_photos_all" = "Ver todas las fotos";
|
||||
"lng_mediaview_files_all" = "Ver todos los archivos";
|
||||
"lng_mediaview_single_photo" = "Foto";
|
||||
"lng_mediaview_group_photo" = "Foto del grupo";
|
||||
"lng_mediaview_profile_photo" = "Foto de perfil";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} de {count}";
|
||||
"lng_mediaview_n_of_count" = "Foto {n} de {count}";
|
||||
"lng_mediaview_doc_image" = "Archivo";
|
||||
"lng_mediaview_today" = "hoy a las {time}";
|
||||
@@ -545,7 +585,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop fue actualizada a la versión {version}\n\n{changes}\n\nEl historial completo está disponible aquí:\n{link}";
|
||||
"lng_new_version_minor" = "— Corrección de errores y otras mejoras menores";
|
||||
"lng_new_version_text" = "— Vista previa de enlaces de Twitter, YouTube, Instagram y otros\n— Verificación en dos pasos\n— Ve todas tus sesiones de Telegram y cierra sesiones específicas";
|
||||
"lng_new_version_text" = "— Añadido el envío de mensajes de voz\n— Arreglada la desaparición de stickers";
|
||||
|
||||
"lng_menu_insert_unicode" = "Insertar caracteres de control Unicode";
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_open_from_tray" = "Apri Telegram";
|
||||
"lng_minimize_to_tray" = "Minimizza";
|
||||
"lng_quit_from_tray" = "Chiudi Telegram";
|
||||
"lng_tray_icon_text" = "Telegram è ancora aperto qui,\npuoi cambiare questo nelle impostazioni.\n\nSe l'icona scompare dalla barra tray,\npuoi riportarla indietro dalle icone nascoste.";
|
||||
"lng_tray_icon_text" = "Telegram è ancora aperto qui,\npuoi cambiare questo nelle impostazioni.\n\nSe l'icona scompare dall'area di notifica,\npuoi riportarla indietro dalle icone nascoste.";
|
||||
|
||||
"lng_month1" = "Gennaio";
|
||||
"lng_month2" = "Febbraio";
|
||||
@@ -196,10 +196,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_settings_update_ready" = "La nuova versione è pronta";
|
||||
"lng_settings_update_now" = "Riavvia ora";
|
||||
"lng_settings_update_fail" = "Ricerca aggiornamenti fallita :(";
|
||||
"lng_settings_workmode_tray" = "Mostra icona nella barra tray";
|
||||
"lng_settings_workmode_tray" = "Mostra icona nell'area di notifica";
|
||||
"lng_settings_workmode_window" = "Mostra icona nella barra applicazioni";
|
||||
"lng_settings_auto_start" = "Avvia Telegram all'avvio del sistema";
|
||||
"lng_settings_start_min" = "Avvia minimizzato";
|
||||
"lng_settings_start_min" = "Avvia ridotto a icona";
|
||||
"lng_settings_add_sendto" = "Inserisci Telegram nel menu «Invia a»";
|
||||
"lng_settings_scale_label" = "Ridimensiona interfaccia";
|
||||
"lng_settings_scale_auto" = "Auto ({cur})";
|
||||
@@ -240,7 +240,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_settings_section_cache" = "Archivio locale";
|
||||
"lng_settings_no_data_cached" = "Non ci sono dati nella cache!";
|
||||
"lng_settings_images_cached" = "{count:_not_used_|# immagine|# immagini}, {size}";
|
||||
"lng_settings_audios_cached" = "{count:_not_used_|# messaggio vocale|# messaggi vocali}, {size}";
|
||||
"lng_settings_audios_cached" = "{count:_not_used_|# nota vocale|# note vocali}, {size}";
|
||||
"lng_local_storage_clear" = "Elimina tutto";
|
||||
"lng_local_storage_clearing" = "Eliminando..";
|
||||
"lng_local_storage_cleared" = "Eliminato!";
|
||||
@@ -287,7 +287,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_cloud_password_email" = "Inserisci email di recupero";
|
||||
"lng_cloud_password_bad_email" = "E-mail non valida, riprova con un'altra.";
|
||||
"lng_cloud_password_about" = "La password verrà richiesta quando ti connetti da un nuovo dispositivo in aggiunta al codice.";
|
||||
"lng_cloud_password_about_recover" = "Attenzione! Sei sicuro di non voler \naggiungere un'e-mail di recupero?\n\nSe dimentichi la tua password, perderai\nl'accesso al tuo account Telegram.";
|
||||
"lng_cloud_password_about_recover" = "Attenzione! Sei sicuro di non voler\naggiungere un'e-mail di recupero?\n\nSe dimentichi la tua password, perderai\nl'accesso al tuo account Telegram.";
|
||||
"lng_cloud_password_almost" = "Abbiamo inviato un link di conferma\nall'e-mail che ci hai fornito.\n\nLa verifica in due passaggi sarà attivata\nnon appena aprirai quel link.";
|
||||
"lng_cloud_password_was_set" = "Verifica in due passaggi abilitata.";
|
||||
"lng_cloud_password_updated" = "La tua password è stata aggiornata.";
|
||||
@@ -347,7 +347,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_set_group_photo" = "Imposta foto";
|
||||
"lng_profile_add_participant" = "Aggiungi membro";
|
||||
"lng_profile_delete_and_exit" = "Esci";
|
||||
"lng_profile_kick" = "Espelli";
|
||||
"lng_profile_kick" = "Rimuovi";
|
||||
"lng_profile_sure_kick" = "Espellere {user} dal gruppo?";
|
||||
"lng_profile_loading" = "Caricamento..";
|
||||
"lng_profile_shared_media" = "Media condivisi";
|
||||
@@ -358,8 +358,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_videos_header" = "Panoramica video";
|
||||
"lng_profile_files" = "{count:_not_used_|# file|# file} »";
|
||||
"lng_profile_files_header" = "Panoramica file";
|
||||
"lng_profile_audios" = "{count:_not_used_|# messaggio vocale|# messaggi vocali} »";
|
||||
"lng_profile_audios_header" = "Panoramica messaggi vocali";
|
||||
"lng_profile_audios" = "{count:_not_used_|# nota vocale|# note vocali} »";
|
||||
"lng_profile_audios_header" = "Panoramica note vocali";
|
||||
"lng_profile_show_all_types" = "Mostra tutti i tipi";
|
||||
"lng_profile_copy_phone" = "Copia numero di telefono";
|
||||
|
||||
@@ -379,15 +379,28 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_message_empty" = "(vuoto)";
|
||||
|
||||
"lng_action_add_user" = "{from} ha aggiunto {user}";
|
||||
"lng_action_kick_user" = "{from} ha espulso {user}";
|
||||
"lng_action_kick_user" = "{from} ha rimosso {user}";
|
||||
"lng_action_user_left" = "{from} ha lasciato il gruppo";
|
||||
"lng_action_user_joined" = "{from} si è unito al gruppo";
|
||||
"lng_action_user_joined_by_link" = "{from} si è unito al gruppo tramite link di invito";
|
||||
"lng_action_user_registered" = "{from} ha iniziato a usare Telegram";
|
||||
"lng_action_removed_photo" = "{from} ha rimosso la foto del gruppo";
|
||||
"lng_action_changed_photo" = "{from} ha cambiato la foto del gruppo";
|
||||
"lng_action_changed_title" = "{from} ha cambiato il nome del gruppo in «{title}»";
|
||||
"lng_action_created_chat" = "{from} ha creato il gruppo «{title}»";
|
||||
|
||||
"lng_group_invite_bad_link" = "Questo link di invito non funziona\no è scaduto.";
|
||||
"lng_group_invite_want_join" = "Vuoi unirti al gruppo «{title}»?";
|
||||
"lng_group_invite_join" = "Unisciti";
|
||||
|
||||
"lng_group_invite_link" = "Link di invito";
|
||||
"lng_group_invite_create" = "Crea un link di invito";
|
||||
"lng_group_invite_about" = "Gli utenti di Telegram saranno in grado di \naggiungersi al tuo gruppo aprendo il link.";
|
||||
"lng_group_invite_create_new" = "Revoca link";
|
||||
"lng_group_invite_about_new" = "Il link precedente smetterà di funzionare\ne ne sarà creato uno nuovo per te.";
|
||||
"lng_group_invite_copied" = "Link di invito copiato negli appunti.";
|
||||
"lng_group_invite_no_room" = "Impossibile unirsi a questo gruppo\nperché ci sono già troppi membri.";
|
||||
|
||||
"lng_forwarded_from" = "Inoltrato da";
|
||||
"lng_in_reply_to" = "In risposta a";
|
||||
|
||||
@@ -399,13 +412,33 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_type_photos" = "Foto";
|
||||
"lng_media_type_videos" = "Video";
|
||||
"lng_media_type_files" = "File";
|
||||
"lng_media_type_audios" = "Messaggi vocali";
|
||||
"lng_media_type_audios" = "Note vocali";
|
||||
|
||||
"lng_media_open_with" = "Apri con";
|
||||
"lng_media_download" = "Download";
|
||||
"lng_media_cancel" = "Annulla";
|
||||
"lng_media_video" = "Video";
|
||||
"lng_media_audio" = "Messaggio vocale";
|
||||
"lng_media_audio" = "Nota vocale";
|
||||
|
||||
"lng_emoji_category0" = "Utilizzate di frequente";
|
||||
"lng_emoji_category1" = "Persone";
|
||||
"lng_emoji_category2" = "Natura";
|
||||
"lng_emoji_category3" = "Cibo e bevande";
|
||||
"lng_emoji_category4" = "Festeggiamenti";
|
||||
"lng_emoji_category5" = "Attività";
|
||||
"lng_emoji_category6" = "Viaggi e luoghi";
|
||||
"lng_emoji_category7" = "Oggetti e simboli";
|
||||
|
||||
"lng_switch_stickers" = "Sticker";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Sticker personalizzati";
|
||||
"lng_stickers_remove_pack" = "Rimuovere «{sticker_pack}»?";
|
||||
"lng_stickers_add_pack" = "Aggiungi {count:_not_used_|# sticker|# sticker}";
|
||||
"lng_stickers_share_pack" = "Condividi sticker";
|
||||
"lng_stickers_not_found" = "Pacchetto di sticker non trovato.";
|
||||
"lng_stickers_copied" = "Link degli sticker copiato negli appunti.";
|
||||
"lng_stickers_default_set" = "Grandi menti";
|
||||
|
||||
"lng_in_dlg_photo" = "Foto";
|
||||
"lng_in_dlg_video" = "Video";
|
||||
@@ -417,6 +450,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "Invia";
|
||||
"lng_message_ph" = "Scrivi un messaggio..";
|
||||
"lng_record_cancel" = "Rilascia fuori per annullare";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "Seleziona una chat per iniziare a messaggiare";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
@@ -458,6 +492,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Salva video come..";
|
||||
"lng_context_open_audio" = "Apri audio";
|
||||
"lng_context_save_audio" = "Salva audio come..";
|
||||
"lng_context_pack_info" = "Mostra sticker";
|
||||
"lng_context_open_file" = "Apri file";
|
||||
"lng_context_save_file" = "Salva file come..";
|
||||
"lng_context_forward_file" = "Inoltra file";
|
||||
@@ -527,12 +562,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:Nessun messaggio trovato|# messaggio trovato|# messaggi trovati}";
|
||||
"lng_search_global_results" = "Risultati ricerca globale";
|
||||
|
||||
"lng_mediaview_save" = "Download";
|
||||
"lng_media_save_progress" = "{ready} di {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Salva come..";
|
||||
"lng_mediaview_copy" = "Copia";
|
||||
"lng_mediaview_forward" = "Inoltra";
|
||||
"lng_mediaview_delete" = "Elimina";
|
||||
"lng_mediaview_photos_all" = "Visualizza tutte le foto";
|
||||
"lng_mediaview_files_all" = "Visualizza tutti i file";
|
||||
"lng_mediaview_single_photo" = "Foto singola";
|
||||
"lng_mediaview_group_photo" = "Foto gruppo";
|
||||
"lng_mediaview_profile_photo" = "Foto profilo";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} di {count}";
|
||||
"lng_mediaview_n_of_count" = "Foto {n} di {count}";
|
||||
"lng_mediaview_doc_image" = "File";
|
||||
"lng_mediaview_today" = "oggi alle {time}";
|
||||
@@ -545,7 +585,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop si è aggiornato alla versione {version}\n\n{changes}\n\nLa cronologia degli update è disponibile qui:\n{link}";
|
||||
"lng_new_version_minor" = "— Bug fix e altri miglioramenti minori";
|
||||
"lng_new_version_text" = "— Anteprima dei link per Twitter, Youtube, Instagram e altri contenuti.\n— Verifica in due passaggi.\n— Visualizza le sessioni attive di Telegram e termina sessioni specifiche.";
|
||||
"lng_new_version_text" = "— Aggiunto invio di note vocali\n— Risolta la scomparsa degli sticker";
|
||||
|
||||
"lng_menu_insert_unicode" = "Inserisci carattere di controllo Unicode";
|
||||
|
||||
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} 님께서 {user} 님을 추방하셨습니다.";
|
||||
"lng_action_user_left" = "{from} 님이 그룹을 나가셨습니다.";
|
||||
"lng_action_user_joined" = "{from} 님이 그룹에 들어오셨습니다.";
|
||||
"lng_action_user_joined_by_link" = "초대링크를 타고 {from} 님이 그룹에 참여하였습니다.";
|
||||
"lng_action_user_registered" = "{from} 님이 텔레그램에 가입하셨습니다.";
|
||||
"lng_action_removed_photo" = "{from} 님이 그룹사진을 삭제하셨습니다.";
|
||||
"lng_action_changed_photo" = "{from} 님이 그룹사진을 변경하셨습니다.";
|
||||
"lng_action_changed_title" = "{from} 님이 그룹이름을 «{title}» 로 바꾸셨습니다.";
|
||||
"lng_action_created_chat" = "{from} 님이 그룹 «{title}» 을 생성하셨습니다.";
|
||||
|
||||
"lng_group_invite_bad_link" = "초대링크가 깨졌거나,\n폐기되었습니다.";
|
||||
"lng_group_invite_want_join" = "«{title}» 그룹방에 참여하시겠습니까?";
|
||||
"lng_group_invite_join" = "참여";
|
||||
|
||||
"lng_group_invite_link" = "초대링크";
|
||||
"lng_group_invite_create" = "초대링크 생성";
|
||||
"lng_group_invite_about" = "이 링크를 통하여,\n그룹방에 초대가 가능합니다.";
|
||||
"lng_group_invite_create_new" = "초대링크 폐기";
|
||||
"lng_group_invite_about_new" = "기존 링크는 비활성화 될 예정이며,\n새로운 링크가 재생성이 될 예정입니다.";
|
||||
"lng_group_invite_copied" = "클립보드에 초대링크가 복사 되었습니다.";
|
||||
"lng_group_invite_no_room" = "그룹방 인원 최대치가 도달하여\n더이상 참여가 안됩니다.";
|
||||
|
||||
"lng_forwarded_from" = "전달받음";
|
||||
"lng_in_reply_to" = "다음 유저에게 답장 :";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "비디오 파일";
|
||||
"lng_media_audio" = "음성 메시지";
|
||||
|
||||
"lng_emoji_category0" = "자주 사용";
|
||||
"lng_emoji_category1" = "사람";
|
||||
"lng_emoji_category2" = "자연";
|
||||
"lng_emoji_category3" = "음식점";
|
||||
"lng_emoji_category4" = "기념일";
|
||||
"lng_emoji_category5" = "활동";
|
||||
"lng_emoji_category6" = "여행 및 장소";
|
||||
"lng_emoji_category7" = "오브제 & 상징";
|
||||
|
||||
"lng_switch_stickers" = "스티커";
|
||||
"lng_switch_emoji" = "이모티콘";
|
||||
|
||||
"lng_custom_stickers" = "커스텀 스티커";
|
||||
"lng_stickers_remove_pack" = "«{sticker_pack}»을 제거하시겠습니까?";
|
||||
"lng_stickers_add_pack" = "{count:_not_used_|# 스티커|# 스티커} 추가";
|
||||
"lng_stickers_share_pack" = "스티커 공유";
|
||||
"lng_stickers_not_found" = "스티커 팩을 찾을 수 없습니다.";
|
||||
"lng_stickers_copied" = "클립보드에 스티커 팩 링크가 복사 되었습니다.";
|
||||
"lng_stickers_default_set" = "Great Minds";
|
||||
|
||||
"lng_in_dlg_photo" = "사진";
|
||||
"lng_in_dlg_video" = "비디오";
|
||||
"lng_in_dlg_contact" = "연락처";
|
||||
@@ -417,6 +450,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "보내기";
|
||||
"lng_message_ph" = "메시지 쓰기";
|
||||
"lng_record_cancel" = "이 영역에서 마우스 클릭을 해제하시면 취소가 됩니다.";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "대화하실 방을 선택해주세요.";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
@@ -458,6 +492,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "비디오 이름을 다른이름으로 저장.";
|
||||
"lng_context_open_audio" = "음성메시지 열기";
|
||||
"lng_context_save_audio" = "음성메시지를 다른 이름으로 저장..";
|
||||
"lng_context_pack_info" = "팩 정보";
|
||||
"lng_context_open_file" = "파일 열기";
|
||||
"lng_context_save_file" = "파일을 다른 이름으로 저장..";
|
||||
"lng_context_forward_file" = "파일 전달";
|
||||
@@ -527,12 +562,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:메시지를 찾을 수 없습니다.|# 개의 메시지를 찾았습니다.|# 개의 메시지를 찾았습니다.}";
|
||||
"lng_search_global_results" = "아이디 검색 결과";
|
||||
|
||||
"lng_mediaview_save" = "다운로드";
|
||||
"lng_media_save_progress" = "{ready} / {total} {mb}";
|
||||
"lng_mediaview_save_as" = "다른 이름으로 저장하기";
|
||||
"lng_mediaview_copy" = "복사하기";
|
||||
"lng_mediaview_forward" = "전달";
|
||||
"lng_mediaview_delete" = "삭제";
|
||||
"lng_mediaview_photos_all" = "모든 사진 보기";
|
||||
"lng_mediaview_files_all" = "모든 파일 보기";
|
||||
"lng_mediaview_single_photo" = "단일 사진";
|
||||
"lng_mediaview_group_photo" = "그룹 사진";
|
||||
"lng_mediaview_profile_photo" = "프로필 사진";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} 중 {count}";
|
||||
"lng_mediaview_n_of_count" = "사진 {n} 중 {count}";
|
||||
"lng_mediaview_doc_image" = "파일";
|
||||
"lng_mediaview_today" = "오늘 {time}";
|
||||
@@ -545,7 +585,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "텔레그램 데스크탑은 {version} 버전으로 업데이트 되었습니다.\n\n{changes}\n\n전체 버전 히스토리는 아래에서 확인 가능합니다:\n{link}";
|
||||
"lng_new_version_minor" = "— 버그 수정 및 일부 기능 향상";
|
||||
"lng_new_version_text" = "— 트윗, 유투브 비디오, 인스터그램 사진등의 프리뷰 기능\n— 2단계 인증\n— 활성화된 모든 텔레그램 세션 확인 및 특정 세션 종료 기능";
|
||||
"lng_new_version_text" = "— 음성 메시지 전송\n— 스티커가 사라지는 문제 해결";
|
||||
|
||||
"lng_menu_insert_unicode" = "유니코드 문자를 입력하세요.";
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_passcode_enter_first" = "Toegangscode invoeren";
|
||||
"lng_passcode_enter_new" = "Nieuwe toegangscode invoeren";
|
||||
"lng_passcode_confirm_new" = "Toegangscode opnieuw invoeren";
|
||||
"lng_passcode_about" = "Als je een toegangscode instelt verschijnt er een slotje op de chatspagina, klikerop om de app te vergrendelen\n\nLet op: als je de toegangscode vergeet, zul je opnieuw in moeten loggen.";
|
||||
"lng_passcode_about" = "Als je een toegangscode instelt verschijnt er een slotje op de chatspagina, klik erop om de app te vergrendelen\n\nLet op: als je de toegangscode vergeet, zul je opnieuw in moeten loggen.";
|
||||
"lng_passcode_differ" = "Toegangscodes zijn verschillend";
|
||||
"lng_passcode_wrong" = "Onjuiste toegangscode";
|
||||
"lng_passcode_is_same" = "Toegangscode is niet gewijzigd";
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} heeft {user} verwijderd";
|
||||
"lng_action_user_left" = "{from} heeft de groep verlaten";
|
||||
"lng_action_user_joined" = "{from} neemt deel aan de groep";
|
||||
"lng_action_user_joined_by_link" = "{from} neemt deel aan de groep via uitnodigingslink";
|
||||
"lng_action_user_registered" = "{from} heeft nu Telegram";
|
||||
"lng_action_removed_photo" = "{from} heeft de groepsafbeelding verwijderd";
|
||||
"lng_action_changed_photo" = "{from} heeft de groepsafbeelding gewijzigd";
|
||||
"lng_action_changed_title" = "{from} heeft de groepsnaam gewijzigd naar «{title}»";
|
||||
"lng_action_created_chat" = "{from} heeft de groep «{title}» gemaakt";
|
||||
|
||||
"lng_group_invite_bad_link" = "Deze uitnodigingslink is \ndefect of verlopen.";
|
||||
"lng_group_invite_want_join" = "Wil je deelnemen aan de groep «{title}»?";
|
||||
"lng_group_invite_join" = "Deelnemen";
|
||||
|
||||
"lng_group_invite_link" = "Uitnodigingslink";
|
||||
"lng_group_invite_create" = "Uitnodigingslink maken";
|
||||
"lng_group_invite_about" = "Gebruikers kunnen aan je groep \ndeelnemen met deze link.";
|
||||
"lng_group_invite_create_new" = "Intrekken";
|
||||
"lng_group_invite_about_new" = "Je uitnodigingslink zal inactief worden\neen nieuwe link zal worden gegenereerd.";
|
||||
"lng_group_invite_copied" = "Link gekopieerd naar klembord.";
|
||||
"lng_group_invite_no_room" = "Sorry, deze groep is al vol.";
|
||||
|
||||
"lng_forwarded_from" = "Doorgestuurd van";
|
||||
"lng_in_reply_to" = "Antwoord op";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Video";
|
||||
"lng_media_audio" = "Spraakbericht";
|
||||
|
||||
"lng_emoji_category0" = "Veelgebruikt";
|
||||
"lng_emoji_category1" = "Mensen";
|
||||
"lng_emoji_category2" = "Natuur";
|
||||
"lng_emoji_category3" = "Eten & drinken";
|
||||
"lng_emoji_category4" = "Feestelijkheid";
|
||||
"lng_emoji_category5" = "Activiteit";
|
||||
"lng_emoji_category6" = "Reizen & plekken";
|
||||
"lng_emoji_category7" = "Objecten & symbolen";
|
||||
|
||||
"lng_switch_stickers" = "Stickers";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Aangepaste stickers";
|
||||
"lng_stickers_remove_pack" = " «{sticker_pack}» verwijderen?";
|
||||
"lng_stickers_add_pack" = "{count:_not_used_|# Sticker|# Stickers} toevoegen";
|
||||
"lng_stickers_share_pack" = "Stickers delen";
|
||||
"lng_stickers_not_found" = "Stickerbundel niet gevonden.";
|
||||
"lng_stickers_copied" = "Stickerbundel-link gekopieerd naar klembord";
|
||||
"lng_stickers_default_set" = "Grote geesten";
|
||||
|
||||
"lng_in_dlg_photo" = "Foto";
|
||||
"lng_in_dlg_video" = "Video";
|
||||
"lng_in_dlg_contact" = "Contact";
|
||||
@@ -417,6 +450,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "Stuur";
|
||||
"lng_message_ph" = "Bericht schrijven";
|
||||
"lng_record_cancel" = "Annuleren: uit het vak loslaten";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "Kies een chat om te beginnen";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
@@ -458,6 +492,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Video opslaan als";
|
||||
"lng_context_open_audio" = "Geluidsbestand openen";
|
||||
"lng_context_save_audio" = "Geluidsbestand opslaan als";
|
||||
"lng_context_pack_info" = "Bundelinformatie";
|
||||
"lng_context_open_file" = "Bestand openen";
|
||||
"lng_context_save_file" = "Bestand opslaan als";
|
||||
"lng_context_forward_file" = "Bestand doorsturen";
|
||||
@@ -527,12 +562,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:geen berichten gevonden|# berichten gevonden|# berichten gevonden}";
|
||||
"lng_search_global_results" = "Wereldwijde zoekresultaten";
|
||||
|
||||
"lng_mediaview_save" = "Downloaden";
|
||||
"lng_media_save_progress" = "{ready} van {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Opslaan als";
|
||||
"lng_mediaview_copy" = "Kopiëren";
|
||||
"lng_mediaview_forward" = "Doorsturen";
|
||||
"lng_mediaview_delete" = "Verwijder";
|
||||
"lng_mediaview_photos_all" = "Alle foto's weergeven";
|
||||
"lng_mediaview_files_all" = "Alle bestanden weergeven";
|
||||
"lng_mediaview_single_photo" = "Foto";
|
||||
"lng_mediaview_group_photo" = "Groepsafbeelding";
|
||||
"lng_mediaview_profile_photo" = "Profielfoto";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} van {count}";
|
||||
"lng_mediaview_n_of_count" = "Foto {n} van {count}";
|
||||
"lng_mediaview_doc_image" = "Bestand";
|
||||
"lng_mediaview_today" = "vandaag om {time}";
|
||||
@@ -545,7 +585,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram is bijgewerkt naar versie {version}\n\n{changes} \n\nVolledige versiegeschiedenis is hier te vinden:\n{link}";
|
||||
"lng_new_version_minor" = "— Probleemoplossing en andere kleine verbeteringen";
|
||||
"lng_new_version_text" = "— Link-preview voor Twitter, YouTube, Instagram en diverse andere links\n— Twee-staps-verificatie\n— Bekijk al je Telegram-sessies, beëindig specifieke sessies.";
|
||||
"lng_new_version_text" = "— Je kunt nu spraakberichten versturen\n— Verdwijnende stickers opgelost";
|
||||
|
||||
"lng_menu_insert_unicode" = "Unicode-besturingsteken invoegen";
|
||||
|
||||
|
||||
@@ -371,10 +371,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_failed_add_participant" = "Não foi possível adicionar o usuário. Tente novamente mais tarde.";
|
||||
|
||||
"lng_sure_delete_contact" = "Você tem certeza que deseja deletar {contact} da sua lista de contatos?";
|
||||
"lng_sure_delete_history" = "Você tem certeza que deseja deletar todo o seu histórico de mensagens com {contact}?\n\nEssa ação não pode ser desfeita.";
|
||||
"lng_sure_delete_contact" = "Você tem certeza que deseja remover {contact} da sua lista de contatos?";
|
||||
"lng_sure_delete_history" = "Você tem certeza que deseja apagar todo o seu histórico de mensagens com {contact}?\n\nEssa ação não pode ser desfeita.";
|
||||
|
||||
"lng_sure_delete_and_exit" = "Você tem certeza que deseja deletar todo o seu histórico de mensagens e deixar «{group}»?\n\nEssa ação não pode ser desfeita.";
|
||||
"lng_sure_delete_and_exit" = "Você tem certeza que deseja apagar todo o seu histórico de mensagens e deixar «{group}»?\n\nEssa ação não pode ser desfeita.";
|
||||
|
||||
"lng_message_empty" = "(vazio)";
|
||||
|
||||
@@ -382,12 +382,25 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_action_kick_user" = "{from} removeu {user}";
|
||||
"lng_action_user_left" = "{from} deixou o grupo";
|
||||
"lng_action_user_joined" = "{from} entrou no grupo";
|
||||
"lng_action_user_joined_by_link" = "{from} entrou no grupo via link de convite";
|
||||
"lng_action_user_registered" = "{from} entrou para o Telegram";
|
||||
"lng_action_removed_photo" = "{from} removeu a foto do grupo";
|
||||
"lng_action_changed_photo" = "{from} alterou a foto do grupo";
|
||||
"lng_action_changed_title" = "{from} alterou o título do grupo para «{title}»";
|
||||
"lng_action_created_chat" = "{from} criou o grupo «{title}»";
|
||||
|
||||
"lng_group_invite_bad_link" = "Link de convite quebrado\nou expirado.";
|
||||
"lng_group_invite_want_join" = "Você deseja entrar no grupo «{title}»?";
|
||||
"lng_group_invite_join" = "Entrar";
|
||||
|
||||
"lng_group_invite_link" = "Link de convite";
|
||||
"lng_group_invite_create" = "Criar um link de convite";
|
||||
"lng_group_invite_about" = "Usuários do Telegram poderão entrar\nem seu grupo clicando no link.";
|
||||
"lng_group_invite_create_new" = "Desativar link";
|
||||
"lng_group_invite_about_new" = "Seu link de convite será desativado\ne iremos gerar um novo link para você.";
|
||||
"lng_group_invite_copied" = "Link copiado para área de transferência.";
|
||||
"lng_group_invite_no_room" = "Não foi possível entrar no grupo porque\nele já possui muitos membros.";
|
||||
|
||||
"lng_forwarded_from" = "Encaminhado de";
|
||||
"lng_in_reply_to" = "Em resposta a";
|
||||
|
||||
@@ -407,6 +420,26 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_media_video" = "Arquivo de vídeo";
|
||||
"lng_media_audio" = "Mensagem de voz";
|
||||
|
||||
"lng_emoji_category0" = "Frequentemente usado";
|
||||
"lng_emoji_category1" = "Pessoas";
|
||||
"lng_emoji_category2" = "Natureza";
|
||||
"lng_emoji_category3" = "Comidas e Bebidas";
|
||||
"lng_emoji_category4" = "Celebração";
|
||||
"lng_emoji_category5" = "Atividade";
|
||||
"lng_emoji_category6" = "Viagens e Lugares";
|
||||
"lng_emoji_category7" = "Objetos e Símbolos";
|
||||
|
||||
"lng_switch_stickers" = "Stickers";
|
||||
"lng_switch_emoji" = "Emoji";
|
||||
|
||||
"lng_custom_stickers" = "Stickers customizados";
|
||||
"lng_stickers_remove_pack" = "Remover «{sticker_pack}»?";
|
||||
"lng_stickers_add_pack" = "Adicionar {count:_not_used_|# Sticker|# Stickers}";
|
||||
"lng_stickers_share_pack" = "Compartilhar Stickers";
|
||||
"lng_stickers_not_found" = "Pacote de sticker não encontrado.";
|
||||
"lng_stickers_copied" = "Link copiado para a àrea de transferência.";
|
||||
"lng_stickers_default_set" = "Grandes Mentes";
|
||||
|
||||
"lng_in_dlg_photo" = "Foto";
|
||||
"lng_in_dlg_video" = "Vídeo";
|
||||
"lng_in_dlg_contact" = "Contato";
|
||||
@@ -417,6 +450,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_send_button" = "Enviar";
|
||||
"lng_message_ph" = "Escrever a mensagem..";
|
||||
"lng_record_cancel" = "Solte fora daqui para cancelar";
|
||||
"lng_empty_history" = "";
|
||||
"lng_willbe_history" = "Selecione um chat para começar a conversar";
|
||||
"lng_message_with_from" = "[c]{from}:[/c] {message}";
|
||||
@@ -458,6 +492,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_context_save_video" = "Salvar Vídeo Como..";
|
||||
"lng_context_open_audio" = "Abrir Áudio";
|
||||
"lng_context_save_audio" = "Salvar Áudio Como..";
|
||||
"lng_context_pack_info" = "Informação do pacote";
|
||||
"lng_context_open_file" = "Abrir Arquivo";
|
||||
"lng_context_save_file" = "Salvar Arquivo Como..";
|
||||
"lng_context_forward_file" = "Encaminhar Arquivo";
|
||||
@@ -527,12 +562,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
"lng_search_found_results" = "{count:Nenhuma mensagem encontrada|Encontrada # mensagem|Encontradas # mensagens}";
|
||||
"lng_search_global_results" = "Resultados da busca global";
|
||||
|
||||
"lng_mediaview_save" = "Baixar";
|
||||
"lng_media_save_progress" = "{ready} de {total} {mb}";
|
||||
"lng_mediaview_save_as" = "Salvar Como..";
|
||||
"lng_mediaview_copy" = "Copiar";
|
||||
"lng_mediaview_forward" = "Encaminhar";
|
||||
"lng_mediaview_delete" = "Apagar";
|
||||
"lng_mediaview_photos_all" = "Visualizar Fotos";
|
||||
"lng_mediaview_files_all" = "Visualizar Arquivos";
|
||||
"lng_mediaview_single_photo" = "Foto Única";
|
||||
"lng_mediaview_group_photo" = "Grupo de Fotos";
|
||||
"lng_mediaview_profile_photo" = "Foto de Perfil";
|
||||
"lng_mediaview_file_n_of_count" = "{file} {n} de {count}";
|
||||
"lng_mediaview_n_of_count" = "Foto {n} de {count}";
|
||||
"lng_mediaview_doc_image" = "Arquivo";
|
||||
"lng_mediaview_today" = "hoje às {time}";
|
||||
@@ -545,7 +585,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop foi atualizado para a versão {version}\n\n{changes}\n\nHistórico completo de mudanças disponível aqui:\n{link}";
|
||||
"lng_new_version_minor" = "— Resolução de bugs e outras menores melhorias";
|
||||
"lng_new_version_text" = "— Pré-visualização de links para o Twitter, YouTube, Instagram e alguns outros links\n— Verificação em duas etapas\n— Visualizar todas as suas sessões do Telegram, encerrar sessões específicas.";
|
||||
"lng_new_version_text" = "— Adicionado o envio de mensagens de voz\n— Bug dos stickers desaparecendo consertado";
|
||||
|
||||
"lng_menu_insert_unicode" = "Inserir caractere de controle Unicode";
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "gui/filedialog.h"
|
||||
|
||||
BackgroundWidget::BackgroundWidget(QWidget *parent, LayeredWidget *w) : QWidget(parent), w(w), _hidden(0),
|
||||
aBackground(0), aBackgroundFunc(anim::easeOutCirc), hiding(false), shadow(st::boxShadow) {
|
||||
aBackground(0), aBackgroundFunc(anim::easeOutCirc), hiding(false), shadow(st::boxShadow) {
|
||||
w->setParent(this);
|
||||
setGeometry(0, 0, App::wnd()->width(), App::wnd()->height());
|
||||
aBackground.start(1);
|
||||
@@ -54,7 +54,7 @@ void BackgroundWidget::paintEvent(QPaintEvent *e) {
|
||||
p.fillRect(rect(), st::layerBG->b);
|
||||
|
||||
p.setOpacity(aBackground.current());
|
||||
shadow.paint(p, w->boxRect());
|
||||
shadow.paint(p, w->boxRect(), st::boxShadowShift);
|
||||
}
|
||||
|
||||
void BackgroundWidget::keyPressEvent(QKeyEvent *e) {
|
||||
|
||||
@@ -37,6 +37,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
QByteArray data;
|
||||
PeerId peer;
|
||||
uint64 id, thumbId = 0;
|
||||
int32 duration = 0;
|
||||
QString thumbExt = "jpg";
|
||||
ToPrepareMediaType type;
|
||||
bool animated = false;
|
||||
@@ -53,6 +54,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
peer = list.front().peer;
|
||||
id = list.front().id;
|
||||
type = list.front().type;
|
||||
duration = list.front().duration;
|
||||
ctrlShiftEnter = list.front().ctrlShiftEnter;
|
||||
replyTo = list.front().replyTo;
|
||||
}
|
||||
@@ -85,29 +87,34 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
filename = info.fileName();
|
||||
filesize = info.size();
|
||||
} else if (!data.isEmpty()) {
|
||||
img = App::readImage(data, 0, true, &animated);
|
||||
if (type == ToPrepareAuto) {
|
||||
if (!img.isNull() && data.size() < MaxUploadPhotoSize) {
|
||||
type = ToPreparePhoto;
|
||||
} else if (data.size() < MaxUploadDocumentSize) {
|
||||
type = ToPrepareDocument;
|
||||
} else {
|
||||
img = QImage();
|
||||
if (type != ToPrepareAudio) {
|
||||
img = App::readImage(data, 0, true, &animated);
|
||||
if (type == ToPrepareAuto) {
|
||||
if (!img.isNull() && data.size() < MaxUploadPhotoSize) {
|
||||
type = ToPreparePhoto;
|
||||
} else if (data.size() < MaxUploadDocumentSize) {
|
||||
type = ToPrepareDocument;
|
||||
} else {
|
||||
img = QImage();
|
||||
}
|
||||
}
|
||||
}
|
||||
MimeType mimeType = mimeTypeForData(data);
|
||||
if (type == ToPrepareDocument) {
|
||||
if (type == ToPrepareDocument || type == ToPrepareAudio) {
|
||||
mime = mimeType.name();
|
||||
}
|
||||
if (mime == "image/jpeg") {
|
||||
filename = filedialogDefaultName(qsl("image"), qsl(".jpg"), QString(), true);
|
||||
} else if (type == ToPrepareAudio) {
|
||||
filename = filedialogDefaultName(qsl("audio"), qsl(".ogg"), QString(), true);
|
||||
mime = "audio/ogg";
|
||||
} else {
|
||||
QString ext;
|
||||
QStringList patterns = mimeType.globPatterns();
|
||||
if (!patterns.isEmpty()) {
|
||||
ext = patterns.front().replace('*', QString());
|
||||
}
|
||||
filename = filedialogDefaultName(qsl("doc"), ext, QString(), true);
|
||||
filename = filedialogDefaultName((type == ToPrepareAudio) ? qsl("audio") : qsl("doc"), ext, QString(), true);
|
||||
}
|
||||
filesize = data.size();
|
||||
}
|
||||
@@ -136,7 +143,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
}
|
||||
}
|
||||
|
||||
if ((img.isNull() && (type != ToPrepareDocument || !filesize)) || type == ToPrepareAuto || (img.isNull() && file.isEmpty() && data.isEmpty())) { // if could not decide what type
|
||||
if ((img.isNull() && ((type != ToPrepareDocument && type != ToPrepareAudio) || !filesize)) || type == ToPrepareAuto || (img.isNull() && file.isEmpty() && data.isEmpty())) { // if could not decide what type
|
||||
{
|
||||
QMutexLocker lock(loader->toPrepareMutex());
|
||||
ToPrepareMedias &list(loader->toPrepareMedias());
|
||||
@@ -155,6 +162,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
MTPPhotoSize thumb(MTP_photoSizeEmpty(MTP_string("")));
|
||||
MTPPhoto photo(MTP_photoEmpty(MTP_long(0)));
|
||||
MTPDocument document(MTP_documentEmpty(MTP_long(0)));
|
||||
MTPAudio audio(MTP_audioEmpty(MTP_long(0)));
|
||||
|
||||
QByteArray jpeg;
|
||||
if (type == ToPreparePhoto) {
|
||||
@@ -178,7 +186,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
}
|
||||
if (!filesize) filesize = jpeg.size();
|
||||
|
||||
photo = MTP_photo(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_string(""), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
|
||||
photo = MTP_photo(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes));
|
||||
|
||||
thumbId = id;
|
||||
} else if ((type == ToPrepareVideo || type == ToPrepareDocument) && !img.isNull()) {
|
||||
@@ -188,7 +196,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
if (animated) {
|
||||
attributes.push_back(MTP_documentAttributeAnimated());
|
||||
} else if (mime == stickerMime && w > 0 && h > 0 && w <= StickerMaxSize && h <= StickerMaxSize && filesize < StickerInMemory) {
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string("")));
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(""), MTP_inputStickerSetEmpty()));
|
||||
thumbFormat = "webp";
|
||||
thumbExt = qsl("webp");
|
||||
}
|
||||
@@ -210,11 +218,13 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
|
||||
if (type == ToPrepareDocument) {
|
||||
document = MTP_document(MTP_long(id), MTP_long(0), MTP_int(unixtime()), MTP_string(mime), MTP_int(filesize), thumb, MTP_int(MTP::maindc()), MTP_vector<MTPDocumentAttribute>(attributes));
|
||||
} else if (type == ToPrepareAudio) {
|
||||
audio = MTP_audio(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_int(duration), MTP_string(mime), MTP_int(filesize), MTP_int(MTP::maindc()));
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker lock(loader->readyMutex());
|
||||
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, photoThumbs, document, jpeg, ctrlShiftEnter, replyTo));
|
||||
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, thumbId, thumbExt, peer, photo, audio, photoThumbs, document, jpeg, ctrlShiftEnter, replyTo));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -267,6 +277,22 @@ PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, MsgI
|
||||
return result;
|
||||
}
|
||||
|
||||
AudioId LocalImageLoader::append(const QByteArray &audio, int32 duration, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t) {
|
||||
AudioId result = 0;
|
||||
{
|
||||
QMutexLocker lock(toPrepareMutex());
|
||||
toPrepare.push_back(ToPrepareMedia(audio, duration, peer, t, false, replyTo));
|
||||
result = toPrepare.back().id;
|
||||
}
|
||||
if (!thread) {
|
||||
thread = new QThread();
|
||||
priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread);
|
||||
thread->start();
|
||||
}
|
||||
emit needToPrepare();
|
||||
return result;
|
||||
}
|
||||
|
||||
PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter) {
|
||||
PhotoId result = 0;
|
||||
{
|
||||
|
||||
@@ -20,16 +20,19 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
enum ToPrepareMediaType {
|
||||
ToPrepareAuto,
|
||||
ToPreparePhoto,
|
||||
ToPrepareAudio,
|
||||
ToPrepareVideo,
|
||||
ToPrepareDocument,
|
||||
};
|
||||
|
||||
struct ToPrepareMedia {
|
||||
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
}
|
||||
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
}
|
||||
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), duration(0), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
}
|
||||
ToPrepareMedia(const QByteArray &data, int32 duration, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter, MsgId replyTo) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), duration(duration), ctrlShiftEnter(ctrlShiftEnter), replyTo(replyTo) {
|
||||
}
|
||||
PhotoId id;
|
||||
QString file;
|
||||
@@ -37,6 +40,7 @@ struct ToPrepareMedia {
|
||||
QByteArray data;
|
||||
PeerId peer;
|
||||
ToPrepareMediaType type;
|
||||
int32 duration;
|
||||
bool ctrlShiftEnter;
|
||||
MsgId replyTo;
|
||||
};
|
||||
@@ -44,8 +48,8 @@ typedef QList<ToPrepareMedia> ToPrepareMedias;
|
||||
|
||||
typedef QMap<int32, QByteArray> LocalFileParts;
|
||||
struct ReadyLocalMedia {
|
||||
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter, MsgId replyTo) :
|
||||
replyTo(replyTo), type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) {
|
||||
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const MTPAudio &audio, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter, MsgId replyTo) :
|
||||
replyTo(replyTo), type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), audio(audio), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) {
|
||||
if (!jpeg.isEmpty()) {
|
||||
int32 size = jpeg.size();
|
||||
for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) {
|
||||
@@ -66,6 +70,7 @@ struct ReadyLocalMedia {
|
||||
|
||||
MTPPhoto photo;
|
||||
MTPDocument document;
|
||||
MTPAudio audio;
|
||||
PreparedPhotoThumbs photoThumbs;
|
||||
LocalFileParts parts;
|
||||
QByteArray jpeg_md5;
|
||||
@@ -107,6 +112,7 @@ public:
|
||||
LocalImageLoader(QObject *parent);
|
||||
void append(const QStringList &files, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
|
||||
PhotoId append(const QByteArray &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
|
||||
AudioId append(const QByteArray &audio, int32 duration, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
|
||||
PhotoId append(const QImage &img, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t, bool ctrlShiftEnter = false);
|
||||
PhotoId append(const QString &file, const PeerId &peer, MsgId replyTo, ToPrepareMediaType t);
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||
#include "lang.h"
|
||||
|
||||
namespace {
|
||||
enum StickerSetType {
|
||||
StickerSetTypeEmpty = 0,
|
||||
StickerSetTypeID = 1,
|
||||
StickerSetTypeShortName = 2,
|
||||
};
|
||||
|
||||
typedef quint64 FileKey;
|
||||
|
||||
@@ -140,6 +145,10 @@ namespace {
|
||||
return sizeof(quint32) + str.size() * sizeof(ushort);
|
||||
}
|
||||
|
||||
uint32 _bytearraySize(const QByteArray &arr) {
|
||||
return sizeof(quint32) + arr.size();
|
||||
}
|
||||
|
||||
QByteArray _settingsSalt, _passKeySalt, _passKeyEncrypted;
|
||||
|
||||
mtpAuthKey _oldKey, _settingsKey, _passKey, _localKey;
|
||||
@@ -484,17 +493,18 @@ namespace {
|
||||
FileKey _dataNameKey = 0;
|
||||
|
||||
enum { // Local Storage Keys
|
||||
lskUserMap = 0,
|
||||
lskDraft, // data: PeerId peer
|
||||
lskDraftPosition, // data: PeerId peer
|
||||
lskImages, // data: StorageKey location
|
||||
lskLocations, // no data
|
||||
lskStickers, // data: StorageKey location
|
||||
lskAudios, // data: StorageKey location
|
||||
lskRecentStickers, // no data
|
||||
lskBackground, // no data
|
||||
lskUserSettings, // no data
|
||||
lskRecentHashtags, // no data
|
||||
lskUserMap = 0x00,
|
||||
lskDraft = 0x01, // data: PeerId peer
|
||||
lskDraftPosition = 0x02, // data: PeerId peer
|
||||
lskImages = 0x03, // data: StorageKey location
|
||||
lskLocations = 0x04, // no data
|
||||
lskStickerImages = 0x05, // data: StorageKey location
|
||||
lskAudios = 0x06, // data: StorageKey location
|
||||
lskRecentStickersOld = 0x07, // no data
|
||||
lskBackground = 0x08, // no data
|
||||
lskUserSettings = 0x09, // no data
|
||||
lskRecentHashtags = 0x0a, // no data
|
||||
lskStickers = 0x0b, // no data
|
||||
};
|
||||
|
||||
typedef QMap<PeerId, FileKey> DraftsMap;
|
||||
@@ -509,7 +519,7 @@ namespace {
|
||||
FileLocationPairs _fileLocationPairs;
|
||||
FileKey _locationsKey = 0;
|
||||
|
||||
FileKey _recentStickersKey = 0;
|
||||
FileKey _recentStickersKeyOld = 0, _stickersKey = 0;
|
||||
|
||||
FileKey _backgroundKey = 0;
|
||||
bool _backgroundWasRead = false;
|
||||
@@ -520,7 +530,7 @@ namespace {
|
||||
|
||||
typedef QPair<FileKey, qint32> FileDesc; // file, size
|
||||
typedef QMap<StorageKey, FileDesc> StorageMap;
|
||||
StorageMap _imagesMap, _stickersMap, _audiosMap;
|
||||
StorageMap _imagesMap, _stickerImagesMap, _audiosMap;
|
||||
int32 _storageImagesSize = 0, _storageStickersSize = 0, _storageAudiosSize = 0;
|
||||
|
||||
bool _mapChanged = false;
|
||||
@@ -585,7 +595,7 @@ namespace {
|
||||
locations.stream >> first >> second >> type >> loc.name >> loc.modified >> loc.size;
|
||||
|
||||
MediaKey key(first, second);
|
||||
loc.type = type;
|
||||
loc.type = StorageFileType(type);
|
||||
|
||||
if (loc.check()) {
|
||||
_fileLocations.insert(key, loc);
|
||||
@@ -831,6 +841,9 @@ namespace {
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
cSetTileBackground(v == 1);
|
||||
if (version < 8005 && !_backgroundKey) {
|
||||
cSetTileBackground(false);
|
||||
}
|
||||
} break;
|
||||
|
||||
case dbiAutoLock: {
|
||||
@@ -902,24 +915,70 @@ namespace {
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
switch (v) {
|
||||
case dbietRecent: cSetEmojiTab(dbietRecent); break;
|
||||
case dbietPeople: cSetEmojiTab(dbietPeople); break;
|
||||
case dbietNature: cSetEmojiTab(dbietNature); break;
|
||||
case dbietObjects: cSetEmojiTab(dbietObjects); break;
|
||||
case dbietPlaces: cSetEmojiTab(dbietPlaces); break;
|
||||
case dbietSymbols: cSetEmojiTab(dbietSymbols); break;
|
||||
case dbietStickers: cSetEmojiTab(dbietStickers); break;
|
||||
case dbietRecent : cSetEmojiTab(dbietRecent ); break;
|
||||
case dbietPeople : cSetEmojiTab(dbietPeople ); break;
|
||||
case dbietNature : cSetEmojiTab(dbietNature ); break;
|
||||
case dbietFood : cSetEmojiTab(dbietFood ); break;
|
||||
case dbietCelebration: cSetEmojiTab(dbietCelebration); break;
|
||||
case dbietActivity : cSetEmojiTab(dbietActivity ); break;
|
||||
case dbietTravel : cSetEmojiTab(dbietTravel ); break;
|
||||
case dbietObjects : cSetEmojiTab(dbietObjects ); break;
|
||||
case dbietStickers : cSetEmojiTab(dbietStickers ); break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case dbiRecentEmojisOld: {
|
||||
RecentEmojisPreloadOld v;
|
||||
stream >> v;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
if (!v.isEmpty()) {
|
||||
RecentEmojisPreload p;
|
||||
p.reserve(v.size());
|
||||
for (int i = 0; i < v.size(); ++i) {
|
||||
uint64 e(v.at(i).first);
|
||||
switch (e) {
|
||||
case 0xD83CDDEFLLU: e = 0xD83CDDEFD83CDDF5LLU; break;
|
||||
case 0xD83CDDF0LLU: e = 0xD83CDDF0D83CDDF7LLU; break;
|
||||
case 0xD83CDDE9LLU: e = 0xD83CDDE9D83CDDEALLU; break;
|
||||
case 0xD83CDDE8LLU: e = 0xD83CDDE8D83CDDF3LLU; break;
|
||||
case 0xD83CDDFALLU: e = 0xD83CDDFAD83CDDF8LLU; break;
|
||||
case 0xD83CDDEBLLU: e = 0xD83CDDEBD83CDDF7LLU; break;
|
||||
case 0xD83CDDEALLU: e = 0xD83CDDEAD83CDDF8LLU; break;
|
||||
case 0xD83CDDEELLU: e = 0xD83CDDEED83CDDF9LLU; break;
|
||||
case 0xD83CDDF7LLU: e = 0xD83CDDF7D83CDDFALLU; break;
|
||||
case 0xD83CDDECLLU: e = 0xD83CDDECD83CDDE7LLU; break;
|
||||
}
|
||||
p.push_back(qMakePair(e, v.at(i).second));
|
||||
}
|
||||
cSetRecentEmojisPreload(p);
|
||||
}
|
||||
} break;
|
||||
|
||||
case dbiRecentEmojis: {
|
||||
RecentEmojiPreload v;
|
||||
RecentEmojisPreload v;
|
||||
stream >> v;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
cSetRecentEmojisPreload(v);
|
||||
} break;
|
||||
|
||||
case dbiRecentStickers: {
|
||||
RecentStickerPreload v;
|
||||
stream >> v;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
cSetRecentStickersPreload(v);
|
||||
} break;
|
||||
|
||||
case dbiEmojiVariants: {
|
||||
EmojiColorVariants v;
|
||||
stream >> v;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
cSetEmojiVariants(v);
|
||||
} break;
|
||||
|
||||
case dbiDialogLastPath: {
|
||||
QString path;
|
||||
stream >> path;
|
||||
@@ -1036,10 +1095,17 @@ namespace {
|
||||
LOG(("App Info: reading old user config.."));
|
||||
qint32 version = 0;
|
||||
|
||||
mtpDcOptions dcOpts(cDcOptions());
|
||||
mtpDcOptions dcOpts;
|
||||
{
|
||||
QReadLocker lock(MTP::dcOptionsMutex());
|
||||
dcOpts = cDcOptions();
|
||||
}
|
||||
_dcOpts = &dcOpts;
|
||||
_readOldUserSettingsFields(&file, version);
|
||||
cSetDcOptions(dcOpts);
|
||||
{
|
||||
QWriteLocker lock(MTP::dcOptionsMutex());
|
||||
cSetDcOptions(dcOpts);
|
||||
}
|
||||
|
||||
file.close();
|
||||
result = true;
|
||||
@@ -1116,10 +1182,17 @@ namespace {
|
||||
LOG(("App Info: reading old keys.."));
|
||||
qint32 version = 0;
|
||||
|
||||
mtpDcOptions dcOpts(cDcOptions());
|
||||
mtpDcOptions dcOpts;
|
||||
{
|
||||
QReadLocker lock(MTP::dcOptionsMutex());
|
||||
dcOpts = cDcOptions();
|
||||
}
|
||||
_dcOpts = &dcOpts;
|
||||
_readOldMtpDataFields(&file, version);
|
||||
cSetDcOptions(dcOpts);
|
||||
{
|
||||
QWriteLocker lock(MTP::dcOptionsMutex());
|
||||
cSetDcOptions(dcOpts);
|
||||
}
|
||||
|
||||
file.close();
|
||||
result = true;
|
||||
@@ -1137,7 +1210,9 @@ namespace {
|
||||
|
||||
uint32 size = 11 * (sizeof(quint32) + sizeof(qint32));
|
||||
size += sizeof(quint32) + _stringSize(cAskDownloadPath() ? QString() : cDownloadPath());
|
||||
size += sizeof(quint32) + sizeof(qint32) + cGetRecentEmojis().size() * (sizeof(uint32) + sizeof(ushort));
|
||||
size += sizeof(quint32) + sizeof(qint32) + (cRecentEmojisPreload().isEmpty() ? cGetRecentEmojis().size() : cRecentEmojisPreload().size()) * (sizeof(uint64) + sizeof(ushort));
|
||||
size += sizeof(quint32) + sizeof(qint32) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64));
|
||||
size += sizeof(quint32) + sizeof(qint32) + (cRecentStickersPreload().isEmpty() ? cGetRecentStickers().size() : cRecentStickersPreload().size()) * (sizeof(uint64) + sizeof(ushort));
|
||||
size += sizeof(quint32) + _stringSize(cDialogLastPath());
|
||||
|
||||
EncryptedDescriptor data(size);
|
||||
@@ -1155,12 +1230,27 @@ namespace {
|
||||
data.stream << quint32(dbiEmojiTab) << qint32(cEmojiTab());
|
||||
data.stream << quint32(dbiDialogLastPath) << cDialogLastPath();
|
||||
|
||||
RecentEmojiPreload v;
|
||||
v.reserve(cGetRecentEmojis().size());
|
||||
for (RecentEmojiPack::const_iterator i = cGetRecentEmojis().cbegin(), e = cGetRecentEmojis().cend(); i != e; ++i) {
|
||||
v.push_back(qMakePair(i->first->code, i->second));
|
||||
{
|
||||
RecentEmojisPreload v(cRecentEmojisPreload());
|
||||
if (v.isEmpty()) {
|
||||
v.reserve(cGetRecentEmojis().size());
|
||||
for (RecentEmojiPack::const_iterator i = cGetRecentEmojis().cbegin(), e = cGetRecentEmojis().cend(); i != e; ++i) {
|
||||
v.push_back(qMakePair(emojiKey(i->first), i->second));
|
||||
}
|
||||
}
|
||||
data.stream << quint32(dbiRecentEmojis) << v;
|
||||
}
|
||||
data.stream << quint32(dbiEmojiVariants) << cEmojiVariants();
|
||||
{
|
||||
RecentStickerPreload v(cRecentStickersPreload());
|
||||
if (v.isEmpty()) {
|
||||
v.reserve(cGetRecentStickers().size());
|
||||
for (RecentStickerPack::const_iterator i = cGetRecentStickers().cbegin(), e = cGetRecentStickers().cend(); i != e; ++i) {
|
||||
v.push_back(qMakePair(i->first->id, i->second));
|
||||
}
|
||||
}
|
||||
data.stream << quint32(dbiRecentStickers) << v;
|
||||
}
|
||||
data.stream << quint32(dbiRecentEmojis) << v;
|
||||
|
||||
FileWriteDescriptor file(_userSettingsKey);
|
||||
file.writeEncrypted(data);
|
||||
@@ -1282,9 +1372,9 @@ namespace {
|
||||
|
||||
DraftsMap draftsMap, draftsPositionsMap;
|
||||
DraftsNotReadMap draftsNotReadMap;
|
||||
StorageMap imagesMap, stickersMap, audiosMap;
|
||||
StorageMap imagesMap, stickerImagesMap, audiosMap;
|
||||
qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0;
|
||||
quint64 locationsKey = 0, recentStickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0;
|
||||
quint64 locationsKey = 0, recentStickersKeyOld = 0, stickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0;
|
||||
while (!map.stream.atEnd()) {
|
||||
quint32 keyType;
|
||||
map.stream >> keyType;
|
||||
@@ -1322,7 +1412,7 @@ namespace {
|
||||
storageImagesSize += size;
|
||||
}
|
||||
} break;
|
||||
case lskStickers: {
|
||||
case lskStickerImages: {
|
||||
quint32 count = 0;
|
||||
map.stream >> count;
|
||||
for (quint32 i = 0; i < count; ++i) {
|
||||
@@ -1330,7 +1420,7 @@ namespace {
|
||||
quint64 first, second;
|
||||
qint32 size;
|
||||
map.stream >> key >> first >> second >> size;
|
||||
stickersMap.insert(StorageKey(first, second), FileDesc(key, size));
|
||||
stickerImagesMap.insert(StorageKey(first, second), FileDesc(key, size));
|
||||
storageStickersSize += size;
|
||||
}
|
||||
} break;
|
||||
@@ -1349,8 +1439,8 @@ namespace {
|
||||
case lskLocations: {
|
||||
map.stream >> locationsKey;
|
||||
} break;
|
||||
case lskRecentStickers: {
|
||||
map.stream >> recentStickersKey;
|
||||
case lskRecentStickersOld: {
|
||||
map.stream >> recentStickersKeyOld;
|
||||
} break;
|
||||
case lskBackground: {
|
||||
map.stream >> backgroundKey;
|
||||
@@ -1361,6 +1451,9 @@ namespace {
|
||||
case lskRecentHashtags: {
|
||||
map.stream >> recentHashtagsKey;
|
||||
} break;
|
||||
case lskStickers: {
|
||||
map.stream >> stickersKey;
|
||||
} break;
|
||||
default:
|
||||
LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType));
|
||||
return Local::ReadMapFailed;
|
||||
@@ -1376,13 +1469,14 @@ namespace {
|
||||
|
||||
_imagesMap = imagesMap;
|
||||
_storageImagesSize = storageImagesSize;
|
||||
_stickersMap = stickersMap;
|
||||
_stickerImagesMap = stickerImagesMap;
|
||||
_storageStickersSize = storageStickersSize;
|
||||
_audiosMap = audiosMap;
|
||||
_storageAudiosSize = storageAudiosSize;
|
||||
|
||||
_locationsKey = locationsKey;
|
||||
_recentStickersKey = recentStickersKey;
|
||||
_recentStickersKeyOld = recentStickersKeyOld;
|
||||
_stickersKey = stickersKey;
|
||||
_backgroundKey = backgroundKey;
|
||||
_userSettingsKey = userSettingsKey;
|
||||
_recentHashtagsKey = recentHashtagsKey;
|
||||
@@ -1442,10 +1536,11 @@ namespace {
|
||||
if (!_draftsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2;
|
||||
if (!_draftsPositionsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsPositionsMap.size() * sizeof(quint64) * 2;
|
||||
if (!_imagesMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _imagesMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
||||
if (!_stickersMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickersMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
||||
if (!_stickerImagesMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickerImagesMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
||||
if (!_audiosMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _audiosMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
||||
if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_recentStickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_recentStickersKeyOld) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_stickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_recentHashtagsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
@@ -1468,9 +1563,9 @@ namespace {
|
||||
mapData.stream << quint64(i.value().first) << quint64(i.key().first) << quint64(i.key().second) << qint32(i.value().second);
|
||||
}
|
||||
}
|
||||
if (!_stickersMap.isEmpty()) {
|
||||
mapData.stream << quint32(lskStickers) << quint32(_stickersMap.size());
|
||||
for (StorageMap::const_iterator i = _stickersMap.cbegin(), e = _stickersMap.cend(); i != e; ++i) {
|
||||
if (!_stickerImagesMap.isEmpty()) {
|
||||
mapData.stream << quint32(lskStickerImages) << quint32(_stickerImagesMap.size());
|
||||
for (StorageMap::const_iterator i = _stickerImagesMap.cbegin(), e = _stickerImagesMap.cend(); i != e; ++i) {
|
||||
mapData.stream << quint64(i.value().first) << quint64(i.key().first) << quint64(i.key().second) << qint32(i.value().second);
|
||||
}
|
||||
}
|
||||
@@ -1483,8 +1578,11 @@ namespace {
|
||||
if (_locationsKey) {
|
||||
mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
|
||||
}
|
||||
if (_recentStickersKey) {
|
||||
mapData.stream << quint32(lskRecentStickers) << quint64(_recentStickersKey);
|
||||
if (_recentStickersKeyOld) {
|
||||
mapData.stream << quint32(lskRecentStickersOld) << quint64(_recentStickersKeyOld);
|
||||
}
|
||||
if (_stickersKey) {
|
||||
mapData.stream << quint32(lskStickers) << quint64(_stickersKey);
|
||||
}
|
||||
if (_backgroundKey) {
|
||||
mapData.stream << quint32(lskBackground) << quint64(_backgroundKey);
|
||||
@@ -1604,7 +1702,11 @@ namespace Local {
|
||||
LOG(("App Error: could not decrypt settings from settings file, maybe bad passcode.."));
|
||||
return writeSettings();
|
||||
}
|
||||
mtpDcOptions dcOpts(cDcOptions());
|
||||
mtpDcOptions dcOpts;
|
||||
{
|
||||
QReadLocker lock(MTP::dcOptionsMutex());
|
||||
dcOpts = cDcOptions();
|
||||
}
|
||||
_dcOpts = &dcOpts;
|
||||
LOG(("App Info: reading encrypted settings.."));
|
||||
while (!settings.stream.atEnd()) {
|
||||
@@ -1625,7 +1727,10 @@ namespace Local {
|
||||
DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port));
|
||||
}
|
||||
}
|
||||
cSetDcOptions(dcOpts);
|
||||
{
|
||||
QWriteLocker lock(MTP::dcOptionsMutex());
|
||||
cSetDcOptions(dcOpts);
|
||||
}
|
||||
|
||||
_settingsSalt = salt;
|
||||
}
|
||||
@@ -1646,13 +1751,19 @@ namespace Local {
|
||||
}
|
||||
settings.writeData(_settingsSalt);
|
||||
|
||||
mtpDcOptions dcOpts(cDcOptions());
|
||||
mtpDcOptions dcOpts;
|
||||
{
|
||||
QReadLocker lock(MTP::dcOptionsMutex());
|
||||
dcOpts = cDcOptions();
|
||||
}
|
||||
if (dcOpts.isEmpty()) {
|
||||
const BuiltInDc *bdcs = builtInDcs();
|
||||
for (int i = 0, l = builtInDcsCount(); i < l; ++i) {
|
||||
dcOpts.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, "", bdcs[i].ip, bdcs[i].port));
|
||||
DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port));
|
||||
}
|
||||
|
||||
QWriteLocker lock(MTP::dcOptionsMutex());
|
||||
cSetDcOptions(dcOpts);
|
||||
}
|
||||
|
||||
@@ -1715,9 +1826,9 @@ namespace Local {
|
||||
_draftsPositionsMap.clear();
|
||||
_imagesMap.clear();
|
||||
_draftsNotReadMap.clear();
|
||||
_stickersMap.clear();
|
||||
_stickerImagesMap.clear();
|
||||
_audiosMap.clear();
|
||||
_locationsKey = _recentStickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = 0;
|
||||
_locationsKey = _recentStickersKeyOld = _stickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = 0;
|
||||
_mapChanged = true;
|
||||
_writeMap(WriteMapNow);
|
||||
|
||||
@@ -1925,13 +2036,13 @@ namespace Local {
|
||||
if (_imagesMap.constFind(location) != _imagesMap.cend()) return;
|
||||
|
||||
QByteArray fmt = image->savedFormat();
|
||||
mtpTypeId format = 0;
|
||||
StorageFileType format = StorageFileUnknown;
|
||||
if (fmt == "JPG") {
|
||||
format = mtpc_storage_fileJpeg;
|
||||
format = StorageFileJpeg;
|
||||
} else if (fmt == "PNG") {
|
||||
format = mtpc_storage_filePng;
|
||||
format = StorageFilePng;
|
||||
} else if (fmt == "GIF") {
|
||||
format = mtpc_storage_fileGif;
|
||||
format = StorageFileGif;
|
||||
}
|
||||
if (format) {
|
||||
image->forget();
|
||||
@@ -1981,7 +2092,7 @@ namespace Local {
|
||||
quint32 imageType;
|
||||
draft.stream >> locFirst >> locSecond >> imageType >> imageData;
|
||||
|
||||
return (locFirst == location.first && locSecond == location.second) ? StorageImageSaved(imageType, imageData) : StorageImageSaved();
|
||||
return (locFirst == location.first && locSecond == location.second) ? StorageImageSaved(StorageFileType(imageType), imageData) : StorageImageSaved();
|
||||
}
|
||||
|
||||
int32 hasImages() {
|
||||
@@ -1992,13 +2103,13 @@ namespace Local {
|
||||
return _storageImagesSize;
|
||||
}
|
||||
|
||||
void writeSticker(const StorageKey &location, const QByteArray &sticker, bool overwrite) {
|
||||
void writeStickerImage(const StorageKey &location, const QByteArray &sticker, bool overwrite) {
|
||||
if (!_working()) return;
|
||||
|
||||
qint32 size = _storageStickerSize(sticker.size());
|
||||
StorageMap::const_iterator i = _stickersMap.constFind(location);
|
||||
if (i == _stickersMap.cend()) {
|
||||
i = _stickersMap.insert(location, FileDesc(genKey(UserPath), size));
|
||||
StorageMap::const_iterator i = _stickerImagesMap.constFind(location);
|
||||
if (i == _stickerImagesMap.cend()) {
|
||||
i = _stickerImagesMap.insert(location, FileDesc(genKey(UserPath), size));
|
||||
_storageStickersSize += size;
|
||||
_mapChanged = true;
|
||||
_writeMap();
|
||||
@@ -2012,20 +2123,20 @@ namespace Local {
|
||||
if (i.value().second != size) {
|
||||
_storageStickersSize += size;
|
||||
_storageStickersSize -= i.value().second;
|
||||
_stickersMap[location].second = size;
|
||||
_stickerImagesMap[location].second = size;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray readSticker(const StorageKey &location) {
|
||||
StorageMap::iterator j = _stickersMap.find(location);
|
||||
if (j == _stickersMap.cend()) {
|
||||
QByteArray readStickerImage(const StorageKey &location) {
|
||||
StorageMap::iterator j = _stickerImagesMap.find(location);
|
||||
if (j == _stickerImagesMap.cend()) {
|
||||
return QByteArray();
|
||||
}
|
||||
FileReadDescriptor draft;
|
||||
if (!readEncryptedFile(draft, j.value().first, UserPath)) {
|
||||
clearKey(j.value().first, UserPath);
|
||||
_storageStickersSize -= j.value().second;
|
||||
_stickersMap.erase(j);
|
||||
_stickerImagesMap.erase(j);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
@@ -2037,7 +2148,7 @@ namespace Local {
|
||||
}
|
||||
|
||||
int32 hasStickers() {
|
||||
return _stickersMap.size();
|
||||
return _stickerImagesMap.size();
|
||||
}
|
||||
|
||||
qint64 storageStickersSize() {
|
||||
@@ -2096,56 +2207,100 @@ namespace Local {
|
||||
return _storageAudiosSize;
|
||||
}
|
||||
|
||||
void writeRecentStickers() {
|
||||
void _writeStickerSet(QDataStream &stream, uint64 setId) {
|
||||
StickerSets::const_iterator it = cStickerSets().constFind(setId);
|
||||
if (it == cStickerSets().cend() || it->stickers.isEmpty()) return;
|
||||
|
||||
stream << quint64(it->id) << quint64(it->access) << it->title << it->shortName << quint32(it->stickers.size());
|
||||
for (StickerPack::const_iterator j = it->stickers.cbegin(), e = it->stickers.cend(); j != e; ++j) {
|
||||
DocumentData *doc = *j;
|
||||
stream << quint64(doc->id) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << doc->sticker->alt;
|
||||
switch (doc->sticker->set.type()) {
|
||||
case mtpc_inputStickerSetID: {
|
||||
stream << qint32(StickerSetTypeID);
|
||||
} break;
|
||||
case mtpc_inputStickerSetShortName: {
|
||||
stream << qint32(StickerSetTypeShortName);
|
||||
} break;
|
||||
case mtpc_inputStickerSetEmpty:
|
||||
default: {
|
||||
stream << qint32(StickerSetTypeEmpty);
|
||||
} break;
|
||||
}
|
||||
const StorageImageLocation &loc(doc->sticker->loc);
|
||||
stream << qint32(loc.width) << qint32(loc.height) << qint32(loc.dc) << quint64(loc.volume) << qint32(loc.local) << quint64(loc.secret);
|
||||
}
|
||||
}
|
||||
|
||||
void writeStickers() {
|
||||
if (!_working()) return;
|
||||
|
||||
const RecentStickerPack &recent(cRecentStickers());
|
||||
if (recent.isEmpty()) {
|
||||
if (_recentStickersKey) {
|
||||
clearKey(_recentStickersKey);
|
||||
_recentStickersKey = 0;
|
||||
|
||||
const StickerSets &sets(cStickerSets());
|
||||
if (sets.isEmpty()) {
|
||||
if (_stickersKey) {
|
||||
clearKey(_stickersKey);
|
||||
_stickersKey = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
_writeMap();
|
||||
} else {
|
||||
if (!_recentStickersKey) {
|
||||
_recentStickersKey = genKey();
|
||||
if (!_stickersKey) {
|
||||
_stickersKey = genKey();
|
||||
_mapChanged = true;
|
||||
_writeMap(WriteMapFast);
|
||||
}
|
||||
quint32 size = 0;
|
||||
for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
|
||||
DocumentData *doc = i->first;
|
||||
if (doc->status == FileFailed) continue;
|
||||
quint32 size = sizeof(quint32) + _bytearraySize(cStickersHash());
|
||||
for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) {
|
||||
if (i->stickers.isEmpty()) continue;
|
||||
|
||||
// id + value + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt
|
||||
size += sizeof(quint64) + sizeof(qint16) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->alt);
|
||||
// id + access + title + shortName + stickersCount
|
||||
size += sizeof(quint64) * 2 + _stringSize(i->title) + _stringSize(i->shortName) + sizeof(quint32);
|
||||
for (StickerPack::const_iterator j = i->stickers.cbegin(), e = i->stickers.cend(); j != e; ++j) {
|
||||
DocumentData *doc = *j;
|
||||
|
||||
// id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt + type-of-set
|
||||
size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->sticker->alt) + sizeof(qint32);
|
||||
|
||||
// thumb-width + thumb-height + thumb-dc + thumb-volume + thumb-local + thumb-secret
|
||||
size += sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint64) + sizeof(qint32) + sizeof(quint64);
|
||||
}
|
||||
}
|
||||
EncryptedDescriptor data(size);
|
||||
for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
|
||||
DocumentData *doc = i->first;
|
||||
if (doc->status == FileFailed) continue;
|
||||
|
||||
data.stream << quint64(doc->id) << qint16(i->second) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << doc->alt;
|
||||
data.stream << quint32(cStickerSets().size()) << cStickersHash();
|
||||
_writeStickerSet(data.stream, DefaultStickerSetId);
|
||||
_writeStickerSet(data.stream, CustomStickerSetId);
|
||||
for (StickerSetsOrder::const_iterator i = cStickerSetsOrder().cbegin(), e = cStickerSetsOrder().cend(); i != e; ++i) {
|
||||
_writeStickerSet(data.stream, *i);
|
||||
}
|
||||
FileWriteDescriptor file(_recentStickersKey);
|
||||
FileWriteDescriptor file(_stickersKey);
|
||||
file.writeEncrypted(data);
|
||||
}
|
||||
}
|
||||
|
||||
void readRecentStickers() {
|
||||
if (!_recentStickersKey) return;
|
||||
void importOldRecentStickers() {
|
||||
if (!_recentStickersKeyOld) return;
|
||||
|
||||
FileReadDescriptor stickers;
|
||||
if (!readEncryptedFile(stickers, _recentStickersKey)) {
|
||||
clearKey(_recentStickersKey);
|
||||
_recentStickersKey = 0;
|
||||
if (!readEncryptedFile(stickers, _recentStickersKeyOld)) {
|
||||
clearKey(_recentStickersKeyOld);
|
||||
_recentStickersKeyOld = 0;
|
||||
_writeMap();
|
||||
return;
|
||||
}
|
||||
|
||||
StickerSets &sets(cRefStickerSets());
|
||||
sets.clear();
|
||||
cSetStickerSetsOrder(StickerSetsOrder());
|
||||
|
||||
RecentStickerPack &recent(cRefRecentStickers());
|
||||
recent.clear();
|
||||
|
||||
cSetStickersHash(QByteArray());
|
||||
|
||||
StickerSet &def(sets.insert(DefaultStickerSetId, StickerSet(DefaultStickerSetId, 0, lang(lng_stickers_default_set), QString())).value());
|
||||
StickerSet &custom(sets.insert(CustomStickerSetId, StickerSet(CustomStickerSetId, 0, lang(lng_custom_stickers), QString())).value());
|
||||
|
||||
QMap<uint64, bool> read;
|
||||
RecentStickerPack recent;
|
||||
while (!stickers.stream.atEnd()) {
|
||||
quint64 id, access;
|
||||
QString name, mime, alt;
|
||||
@@ -2155,7 +2310,7 @@ namespace Local {
|
||||
if (stickers.version >= 7021) {
|
||||
stickers.stream >> alt;
|
||||
}
|
||||
if (read.contains(id)) continue;
|
||||
if (!value || read.contains(id)) continue;
|
||||
read.insert(id, true);
|
||||
|
||||
QVector<MTPDocumentAttribute> attributes;
|
||||
@@ -2163,23 +2318,133 @@ namespace Local {
|
||||
if (type == AnimatedDocument) {
|
||||
attributes.push_back(MTP_documentAttributeAnimated());
|
||||
} else if (type == StickerDocument) {
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt)));
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty()));
|
||||
}
|
||||
if (width > 0 && height > 0) {
|
||||
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
|
||||
}
|
||||
|
||||
recent.push_back(qMakePair(App::document(id, 0, access, date, attributes, mime, ImagePtr(), dc, size), value));
|
||||
DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, ImagePtr(), dc, size, StorageImageLocation());
|
||||
if (!doc->sticker) continue;
|
||||
|
||||
if (value > 0) {
|
||||
def.stickers.push_back(doc);
|
||||
} else {
|
||||
custom.stickers.push_back(doc);
|
||||
}
|
||||
if (recent.size() < StickerPanPerRow * StickerPanRowsPerPage && qAbs(value) > 1) recent.push_back(qMakePair(doc, qAbs(value)));
|
||||
}
|
||||
if (def.stickers.isEmpty()) sets.remove(DefaultStickerSetId);
|
||||
if (custom.stickers.isEmpty()) sets.remove(CustomStickerSetId);
|
||||
|
||||
writeStickers();
|
||||
writeUserSettings();
|
||||
|
||||
clearKey(_recentStickersKeyOld);
|
||||
_recentStickersKeyOld = 0;
|
||||
_writeMap();
|
||||
}
|
||||
|
||||
void readStickers() {
|
||||
if (!_stickersKey) {
|
||||
return importOldRecentStickers();
|
||||
}
|
||||
|
||||
cSetRecentStickers(recent);
|
||||
FileReadDescriptor stickers;
|
||||
if (!readEncryptedFile(stickers, _stickersKey)) {
|
||||
clearKey(_stickersKey);
|
||||
_stickersKey = 0;
|
||||
_writeMap();
|
||||
return;
|
||||
}
|
||||
|
||||
StickerSets &sets(cRefStickerSets());
|
||||
sets.clear();
|
||||
|
||||
StickerSetsOrder &order(cRefStickerSetsOrder());
|
||||
order.clear();
|
||||
|
||||
quint32 cnt;
|
||||
QByteArray hash;
|
||||
stickers.stream >> cnt >> hash;
|
||||
if (stickers.version < 8019) {
|
||||
hash.clear(); // bad data in old caches
|
||||
cnt += 2; // try to read at least something
|
||||
}
|
||||
for (uint32 i = 0; i < cnt; ++i) {
|
||||
quint64 setId = 0, setAccess = 0;
|
||||
QString setTitle, setShortName;
|
||||
quint32 scnt = 0;
|
||||
stickers.stream >> setId >> setAccess >> setTitle >> setShortName >> scnt;
|
||||
|
||||
if (setId == DefaultStickerSetId) {
|
||||
setTitle = lang(lng_stickers_default_set);
|
||||
} else if (setId == CustomStickerSetId) {
|
||||
setTitle = lang(lng_custom_stickers);
|
||||
} else if (setId) {
|
||||
order.push_back(setId);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
StickerSet &set(sets.insert(setId, StickerSet(setId, setAccess, setTitle, setShortName)).value());
|
||||
set.stickers.reserve(scnt);
|
||||
|
||||
QMap<uint64, bool> read;
|
||||
for (uint32 j = 0; j < scnt; ++j) {
|
||||
quint64 id, access;
|
||||
QString name, mime, alt;
|
||||
qint32 date, dc, size, width, height, type, typeOfSet;
|
||||
stickers.stream >> id >> access >> date >> name >> mime >> dc >> size >> width >> height >> type >> alt >> typeOfSet;
|
||||
|
||||
qint32 thumbWidth, thumbHeight, thumbDc, thumbLocal;
|
||||
quint64 thumbVolume, thumbSecret;
|
||||
stickers.stream >> thumbWidth >> thumbHeight >> thumbDc >> thumbVolume >> thumbLocal >> thumbSecret;
|
||||
|
||||
if (read.contains(id)) continue;
|
||||
read.insert(id, true);
|
||||
|
||||
if (setId == DefaultStickerSetId || setId == CustomStickerSetId) {
|
||||
typeOfSet = StickerSetTypeEmpty;
|
||||
}
|
||||
|
||||
QVector<MTPDocumentAttribute> attributes;
|
||||
if (!name.isEmpty()) attributes.push_back(MTP_documentAttributeFilename(MTP_string(name)));
|
||||
if (type == AnimatedDocument) {
|
||||
attributes.push_back(MTP_documentAttributeAnimated());
|
||||
} else if (type == StickerDocument) {
|
||||
switch (typeOfSet) {
|
||||
case StickerSetTypeID: {
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetID(MTP_long(setId), MTP_long(setAccess))));
|
||||
} break;
|
||||
case StickerSetTypeShortName: {
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetShortName(MTP_string(setShortName))));
|
||||
} break;
|
||||
case StickerSetTypeEmpty:
|
||||
default: {
|
||||
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty()));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
if (width > 0 && height > 0) {
|
||||
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
|
||||
}
|
||||
|
||||
StorageImageLocation thumb(thumbWidth, thumbHeight, thumbDc, thumbVolume, thumbLocal, thumbSecret);
|
||||
DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.dc ? ImagePtr(thumb) : ImagePtr(), dc, size, thumb);
|
||||
if (!doc->sticker) continue;
|
||||
|
||||
set.stickers.push_back(doc);
|
||||
}
|
||||
}
|
||||
|
||||
cSetStickersHash(hash);
|
||||
}
|
||||
|
||||
void writeBackground(int32 id, const QImage &img) {
|
||||
if (!_working()) return;
|
||||
|
||||
QByteArray png;
|
||||
{
|
||||
if (!img.isNull()) {
|
||||
QBuffer buf(&png);
|
||||
if (!img.save(&buf, "BMP")) return;
|
||||
}
|
||||
@@ -2188,9 +2453,10 @@ namespace Local {
|
||||
_mapChanged = true;
|
||||
_writeMap(WriteMapFast);
|
||||
}
|
||||
quint32 size = sizeof(qint32) + sizeof(quint32) + sizeof(quint32) + png.size();
|
||||
quint32 size = sizeof(qint32) + sizeof(quint32) + (png.isEmpty() ? 0 : (sizeof(quint32) + png.size()));
|
||||
EncryptedDescriptor data(size);
|
||||
data.stream << qint32(id) << png;
|
||||
data.stream << qint32(id);
|
||||
if (!png.isEmpty()) data.stream << png;
|
||||
|
||||
FileWriteDescriptor file(_backgroundKey);
|
||||
file.writeEncrypted(data);
|
||||
@@ -2210,7 +2476,17 @@ namespace Local {
|
||||
|
||||
QByteArray pngData;
|
||||
qint32 id;
|
||||
bg.stream >> id >> pngData;
|
||||
bg.stream >> id;
|
||||
if (!id || id == DefaultChatBackground) {
|
||||
if (bg.version < 8005) {
|
||||
if (!id) cSetTileBackground(!DefaultChatBackground);
|
||||
App::initBackground(DefaultChatBackground, QImage(), true);
|
||||
} else {
|
||||
App::initBackground(id, QImage(), true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bg.stream >> pngData;
|
||||
|
||||
QImage img;
|
||||
QBuffer buf(&pngData);
|
||||
@@ -2330,8 +2606,8 @@ namespace Local {
|
||||
_storageImagesSize = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (!_stickersMap.isEmpty()) {
|
||||
_stickersMap.clear();
|
||||
if (!_stickerImagesMap.isEmpty()) {
|
||||
_stickerImagesMap.clear();
|
||||
_storageStickersSize = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
@@ -2352,8 +2628,12 @@ namespace Local {
|
||||
_locationsKey = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (_recentStickersKey) {
|
||||
_recentStickersKey = 0;
|
||||
if (_recentStickersKeyOld) {
|
||||
_recentStickersKeyOld = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (_stickersKey) {
|
||||
_stickersKey = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (_recentHashtagsKey) {
|
||||
@@ -2380,9 +2660,9 @@ namespace Local {
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (data->stickers.isEmpty()) {
|
||||
data->stickers = _stickersMap;
|
||||
data->stickers = _stickerImagesMap;
|
||||
} else {
|
||||
for (StorageMap::const_iterator i = _stickersMap.cbegin(), e = _stickersMap.cend(); i != e; ++i) {
|
||||
for (StorageMap::const_iterator i = _stickerImagesMap.cbegin(), e = _stickerImagesMap.cend(); i != e; ++i) {
|
||||
StorageKey k = i.key();
|
||||
while (data->stickers.constFind(k) != data->stickers.cend()) {
|
||||
++k.second;
|
||||
@@ -2390,8 +2670,8 @@ namespace Local {
|
||||
data->stickers.insert(k, i.value());
|
||||
}
|
||||
}
|
||||
if (!_stickersMap.isEmpty()) {
|
||||
_stickersMap.clear();
|
||||
if (!_stickerImagesMap.isEmpty()) {
|
||||
_stickerImagesMap.clear();
|
||||
_storageStickersSize = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
|
||||
@@ -122,8 +122,8 @@ namespace Local {
|
||||
int32 hasImages();
|
||||
qint64 storageImagesSize();
|
||||
|
||||
void writeSticker(const StorageKey &location, const QByteArray &data, bool overwrite = true);
|
||||
QByteArray readSticker(const StorageKey &location);
|
||||
void writeStickerImage(const StorageKey &location, const QByteArray &data, bool overwrite = true);
|
||||
QByteArray readStickerImage(const StorageKey &location);
|
||||
int32 hasStickers();
|
||||
qint64 storageStickersSize();
|
||||
|
||||
@@ -132,8 +132,8 @@ namespace Local {
|
||||
int32 hasAudios();
|
||||
qint64 storageAudiosSize();
|
||||
|
||||
void writeRecentStickers();
|
||||
void readRecentStickers();
|
||||
void writeStickers();
|
||||
void readStickers();
|
||||
|
||||
void writeBackground(int32 id, const QImage &img);
|
||||
bool readBackground();
|
||||
|
||||