Compare commits

...

128 Commits

Author SHA1 Message Date
John Preston
cb1c8c4aea version 0.8.23.dev with sticker packs buttons prepared 2015-06-05 18:59:58 +03:00
John Preston
1ee5c14b85 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-06-05 18:55:58 +03:00
John Preston
01410e5d92 fixed crash in new sticker icons 2015-06-05 18:55:46 +03:00
John Preston
0e5d26a469 sticker packs buttons improved for os x 2015-06-05 18:37:11 +03:00
John Preston
95e5b7be0b stickers pan with packs buttons 2015-06-05 18:02:20 +03:00
John Preston
6d8f277904 version 0.8.22.dev prepared - new OpenAL version, new Auto-Update (no temp -> ready rename), some fixes 2015-06-04 16:04:34 +03:00
John Preston
b6325ec9d4 0.8.24.dev test (not production!) version ready, updaters improved (no tdata copying while updating) 2015-06-04 14:59:30 +03:00
John Preston
3373a2f382 skipping tdata folder when updating in linux, log file name fixed for Updater in os x 2015-06-04 14:51:52 +03:00
John Preston
9fcd878f1d Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-06-04 13:55:29 +03:00
John Preston
1c50c35abe 0.8.23.dev test (not production!) version ready, autoupdater cleaning old ready dir 2015-06-04 13:54:56 +03:00
John Preston
6a0ee57054 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-06-04 13:52:29 +03:00
John Preston
00adfa6f3d improved logging of Updater in os x 2015-06-04 13:52:16 +03:00
John Preston
6058c8862e improved Updater in Linux (DebugLogs for Updater in working dir) 2015-06-04 13:50:53 +03:00
John Preston
e5f2f68188 fixed recording display, 0.8.22.dev test version ready (not production!) 2015-06-03 21:35:26 +03:00
John Preston
c6ee2772e2 new updater fixed for os x, 0.8.22.dev test version (not production) 2015-06-03 21:29:57 +03:00
John Preston
16caff1ca4 test dev version 0.8.22 (not production!) prepared, fixed display of group from which I left 2015-06-03 21:20:57 +03:00
John Preston
ac2ae16f47 MSVC instruction improved for OpenAL, merging autoupdate code for all OSs (not tested yet!) 2015-06-03 21:13:01 +03:00
John Preston
c40758f30d openal updated in Windows 2015-06-03 15:18:46 +03:00
John Preston
16aafe28d5 added minimum system version to Info.plist, added suspended flag to OpenAL handle 2015-06-03 14:57:14 +03:00
John Preston
db96605332 changelog added in 0.8.21 stable 2015-06-02 17:37:15 +03:00
John Preston
31954f5266 stable version 0.8.21 2015-06-02 17:31:25 +03:00
John Preston
66718de562 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-06-02 17:01:13 +03:00
John Preston
e2d02f4e4b fixed gif animations on retina display 2015-06-02 17:00:30 +03:00
John Preston
e3d4bf192f 0.8.20.dev version linux local8bit -> utf8 2015-06-02 15:29:02 +03:00
John Preston
e7b94f3d3a version 0.8.20.dev prepared (some fixes) 2015-06-02 14:22:00 +03:00
John Preston
30be7af3e3 langs updated 2015-06-02 14:17:14 +03:00
John Preston
9069b4b269 langs updated for 0.8.19.dev 2015-06-01 23:40:09 +03:00
John Preston
040c80ae0b Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-06-01 23:29:28 +03:00
John Preston
a87c9b15d2 FixMake done for linux 32bit 2015-06-01 23:29:21 +03:00
John Preston
635cae4f94 version 0.8.19 with fixed stickerpacks and voice messages changelog 2015-06-01 23:24:09 +03:00
John Preston
67e6d12384 fixed build instructions 2015-06-01 15:56:55 +03:00
John Preston
9ccdc0e94b fixed build instructions 2015-06-01 15:52:44 +03:00
John Preston
762c0aa579 fixed build instructions 2015-06-01 15:49:53 +03:00
John Preston
5dc78932d7 instructions for building ffmpeg added 2015-06-01 15:44:10 +03:00
John Preston
e9c5f18142 fixed circle, final 0.8.18.dev-2 2015-06-01 14:21:41 +03:00
John Preston
175023b2dc Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-06-01 14:15:30 +03:00
John Preston
0a7c42c59a linux audio capture improved 2015-06-01 14:15:07 +03:00
John Preston
42122fdea0 added some stickers logging , 0.8.18 dev version ready 2015-06-01 13:58:46 +03:00
John Preston
4bfe65d8ab ffmpeg added to os x build 2015-06-01 12:42:56 +03:00
John Preston
1b06fe1220 ffmpeg audio play / capture done in os x 2015-05-30 19:30:47 +03:00
John Preston
1b11a7feae voice message recording / sending done 2015-05-29 21:52:43 +03:00
John Preston
6b60b51775 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-26 11:19:52 +03:00
John Preston
981162e6b9 ffmpeg support for audio added 2015-05-26 11:19:47 +03:00
John Preston
c5dd99b1f1 added libmpg123 and libfaad to linux version 2015-05-24 21:58:42 +03:00
John Preston
53c536d76d libmpg123, libfaad and libmp4ff added to os x build 2015-05-24 21:39:07 +03:00
John Preston
e3ab8821b9 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-24 20:59:04 +03:00
John Preston
6befea6a13 implemented .mp3 playing through libmpg123 and .m4a playing through libfaad2 in voice messages 2015-05-24 20:58:39 +03:00
John Preston
f24e3c6192 fixed audio mark as read after download 2015-05-23 18:57:16 +03:00
John Preston
63e593b3a8 fixed retina boxshadow 2015-05-22 14:43:47 +03:00
John Preston
7ca4ec1bed box shadows and bg changed, retina shadow draw fixed 2015-05-22 14:14:52 +03:00
John Preston
8ed1961886 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop 2015-05-21 18:09:07 +03:00
John Preston
ad44e45695 version 0.8.17 prepare fixed 2015-05-21 18:08:53 +03:00
John Preston
962ec1e454 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop 2015-05-21 16:52:23 +03:00
John Preston
bcc718b5a9 QTKit.framework removed from build 2015-05-21 16:52:15 +03:00
John Preston
ac50119dd9 version 0.8.17 crash fix 2015-05-21 16:46:49 +03:00
John Preston
e953e11b7f rounding gifs in messages history 2015-05-21 15:08:05 +03:00
John Preston
18361ce144 sticker packs order now is saved, new packs added to the top, fixed emoji categories icons 2015-05-21 13:44:26 +03:00
John Preston
cfdacb09ac improved langs support, v 0.8.16 commit 2015-05-21 01:02:55 +03:00
John Preston
5bb83afc7a version 0.8.16 stable - fixed select of a sticker reply 2015-05-21 00:33:39 +03:00
John Preston
f35853c42e version 0.8.15.dev ready 2015-05-20 22:54:15 +03:00
John Preston
44492b9e2d fixed round corners for forward bubble and for retina 2015-05-20 22:50:05 +03:00
John Preston
a46bb46e54 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-20 22:29:08 +03:00
John Preston
84c190c293 langs updated 2015-05-20 22:28:52 +03:00
John Preston
9da4a21f94 caption display in video, caption display in mediaview, removed Loading / Failed webpage display, rounded corners everywhere 2015-05-20 22:28:24 +03:00
John Preston
2a3a351445 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-20 00:20:14 +03:00
John Preston
928cd0a8cb Great Minds went to langpack 2015-05-20 00:19:11 +03:00
John Preston
509d2189c1 added stickersetbox to .pro file 2015-05-19 19:02:41 +03:00
John Preston
725fa87188 removing new added stickers from custom 2015-05-19 18:58:22 +03:00
John Preston
147eaab59a prepared version 0.8.14.dev 2015-05-19 18:48:00 +03:00
John Preston
890ec34202 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-19 18:46:51 +03:00
John Preston
92858dc7d3 sticker packs done 2015-05-19 18:46:45 +03:00
John Preston
4440b42848 openssl multithread locks support added 2015-05-19 18:42:32 +03:00
John Preston
136fd5c8e1 added lock for dcOptions, emojibox large emoji display, session management improved, new emoji dropdown started 2015-05-14 19:50:04 +03:00
John Preston
d92356ce28 improved emoji text replaces 2015-05-12 18:01:49 +03:00
John Preston
47f673aa69 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-12 17:25:56 +03:00
John Preston
64d4a3e8a8 removed QT_STYLE_OVERRIDE env variable read 2015-05-12 15:03:10 +03:00
John Preston
87e72fc7aa langs updated 2015-05-12 14:53:50 +03:00
John Preston
6d44a3ec95 emoji sprites improved 2015-05-11 16:33:02 +03:00
John Preston
eb47eabba4 langs updated 2015-05-11 16:31:37 +03:00
John Preston
cfb0de69f0 next layer, stickerdata moved from documentdata 2015-05-11 15:44:27 +03:00
John Preston
2d46cc4c11 preparing version 0.8.13 with new emoji pan 2015-05-11 13:18:57 +03:00
John Preston
9761c5bb56 fixed 0.8.12.dev version 2015-05-08 19:01:11 +03:00
John Preston
7d6bf487a7 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-08 17:44:40 +03:00
John Preston
6fda783fc1 version 0.8.12.dev with new emojis support 2015-05-08 17:44:07 +03:00
John Preston
aeaa039542 cmd+backspace support added 2015-05-08 17:36:05 +03:00
John Preston
62e85b1cf0 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-08 16:08:36 +03:00
John Preston
080a08fa76 some optimizations in shortchatmessage 2015-05-08 16:06:53 +03:00
John Preston
be1eb1c693 Merge branch 'emojis' into dev 2015-05-08 16:02:33 +03:00
John Preston
daf5d4acdb removed temp code 2015-05-08 16:02:24 +03:00
John Preston
f4781b9117 recent emojis more frequent update 2015-05-08 15:58:34 +03:00
John Preston
0f778431f5 new emoji support done 2015-05-08 15:45:14 +03:00
John Preston
2121ce1210 version 0.8.11 stable is prepared 2015-05-01 12:14:43 +03:00
John Preston
c54aadcac3 version 0.8.10.dev with fixes in webpage layout, history load and history msgs reg 2015-05-01 02:05:19 +03:00
John Preston
3fc74166de langs updated 2015-05-01 02:03:22 +03:00
John Preston
749b13adec dev version 0.8.9 fixed telegram.me joinchat 2015-04-30 18:11:21 +03:00
John Preston
c33ddf49ff langs updated 2015-04-30 18:09:06 +03:00
John Preston
bc1e2dcb54 langs updated 2015-04-30 16:58:21 +03:00
John Preston
9a193ed88f new emojis rendered 2015-04-30 16:56:33 +03:00
John Preston
fb32c5bcd1 version 0.8.8.dev - audio listened status, photo caption display, map places name-address display, gray muted badges, invite links support, some fixes 2015-04-30 16:53:36 +03:00
John Preston
67b46d9aac Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into emojis 2015-04-25 16:55:12 +03:00
John Preston
aeb2ec68ef added some debug info in autoupdate, version 0.8.7 stable prepared 2015-04-24 19:02:45 +03:00
John Preston
e1c304c2e5 langs updated, qt multimedia dependency removed from xcode project 2015-04-24 18:59:02 +03:00
John Preston
c7de9d4668 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-23 22:58:41 +03:00
John Preston
45aa6dff75 langs updated 2015-04-23 22:58:35 +03:00
John Preston
8b0562b946 lang updated 2015-04-23 22:58:02 +03:00
John Preston
373f1a0ff0 added old chat bg to git and to telegram_linux.qrc 2015-04-23 19:03:31 +03:00
John Preston
058f6bd8de Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-23 19:02:11 +03:00
John Preston
a707f7b9e7 0.8.6 prepared - forwarded info display in media (except photos and stickers), grouped forwarded notification, old bg returned, critical mtproto fix 2015-04-23 19:01:43 +03:00
John Preston
61d1574023 added forwarded and from names in files / audios / videos / contacts display, grouped notification for many forwarded messages, fix of getDifference() for new_session_created 2015-04-23 18:50:11 +03:00
John Preston
fe59898e5c changed chat bg in linux resource file 2015-04-22 15:05:29 +03:00
John Preston
0df1952a04 changelog added 2015-04-22 14:33:52 +03:00
John Preston
1d8ec7c7d6 dev-version 0.8.5 prepared - new photoviewer, new default chat background 2015-04-22 14:21:45 +03:00
John Preston
b770ea4f8d optimized new mediaview, os x new mediaview done 2015-04-19 22:01:45 +03:00
John Preston
b31f2d952c regen emojis started 2015-04-19 14:16:48 +03:00
John Preston
370c47d95b new photoviewer adapted for documents 2015-04-19 13:29:19 +03:00
John Preston
9b3767e77c Merge branch 'master' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-16 18:00:43 +03:00
John Preston
196b643e7d Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-16 18:00:16 +03:00
John Preston
c9626c140c sending ping_delay_disconnect instead of getDifference by no-updates timeouts 2015-04-16 17:59:42 +03:00
John Preston
a356a4dc06 dont cache default background image, cmd+m minimizes window in os x 2015-04-16 14:14:25 +03:00
John Preston
34d5c3777b Merge branch 'master' of https://github.com/telegramdesktop/tdesktop 2015-04-15 11:07:27 +02:00
John Preston
ac74a08d53 fixed crash in photo update from 2015-04-15 09:23:39 +02:00
John Preston
47b0f901c8 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop 2015-04-13 10:59:32 +01:00
John Preston
18103aae75 xcode project updated 2015-04-13 10:59:28 +01:00
John Preston
cc9ae13297 fixed vimeo links preview 2015-04-13 10:58:30 +01:00
John Preston
334b3ac706 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-13 10:41:01 +01:00
John Preston
a9063eb87b Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-04-13 09:58:34 +01:00
John Preston
175e0f71ce fixed reply to text color 2015-04-13 09:58:13 +01:00
John Preston
782c254ea0 started photoviewer redesign 2015-04-11 11:04:10 +01:00
162 changed files with 20760 additions and 15749 deletions

72
MSVC.md
View File

@@ -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

View File

@@ -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 ..

View File

@@ -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

View File

@@ -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

View File

@@ -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'

View File

@@ -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'

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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";

View File

@@ -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;

View File

@@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -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)) {

View File

@@ -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)) {

View File

@@ -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]);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

View File

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1003 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 956 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 218 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -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;
};

View 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;
}

View 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();

View File

@@ -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())));
}
}

View File

@@ -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;

View File

@@ -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));

View File

@@ -38,6 +38,7 @@ private:
void fillBlocks();
int32 _esize;
BottomButton _done;
int32 _blockHeight;

View File

@@ -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));

View File

@@ -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);

View 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);
}

View 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;
};

View File

@@ -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

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -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;
//
//};

View File

@@ -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;
}
}
}
}
}

View File

@@ -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;

View File

@@ -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];
}

View File

@@ -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 &current(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;
};

View File

@@ -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);
}
}
}

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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());

View File

@@ -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;

View File

@@ -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);

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -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;

View File

@@ -100,6 +100,7 @@ public:
void setOpacity(float64 o);
void setText(const QString &text);
QString getText() const;
public slots:

View File

@@ -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();

View File

@@ -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:

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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()));
}

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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:

File diff suppressed because it is too large Load Diff

View File

@@ -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 &currentText);
void initMediaFromText(QString &currentText);
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 {

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -153,6 +153,7 @@ void IntroCode::activate() {
callTimer.start(1000);
error = "";
errorAlpha = anim::fvalue(0);
sentCode = QString();
show();
code.setDisabled(false);
code.setFocus();

View File

@@ -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";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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" = "유니코드 문자를 입력하세요.";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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) {

View File

@@ -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;
{

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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();

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