Compare commits

...

74 Commits

Author SHA1 Message Date
John Preston
7afda6dfc3 changelog added in 0.8.24 2015-06-08 15:25:23 +03:00
John Preston
3a35b6c5fd version 0.8.24 with new sticker packs buttons done 2015-06-08 15:24:21 +03:00
John Preston
c449e45def Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-06-08 15:19:42 +03:00
John Preston
6c704e3595 langs done 2015-06-08 15:19:25 +03:00
John Preston
318794692f trying to fix undefined behaviour in notifyShowNext() 2015-06-06 20:08:44 +03:00
John Preston
62a0878198 linear anim for sticker packs icons 2015-06-05 20:06:37 +03:00
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
94 changed files with 7282 additions and 6786 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

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

@@ -1,10 +1,10 @@
@echo OFF
set "AppVersion=8014"
set "AppVersionStrSmall=0.8.14"
set "AppVersionStr=0.8.14"
set "AppVersionStrFull=0.8.14.0"
set "DevChannel=1"
set "AppVersion=8024"
set "AppVersionStrSmall=0.8.24"
set "AppVersionStr=0.8.24"
set "AppVersionStrFull=0.8.24.0"
set "DevChannel=0"
if %DevChannel% neq 0 goto preparedev

View File

@@ -438,6 +438,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"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";
@@ -449,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}";
@@ -583,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" = "— Added support for new emoji\n— Improved emoji and stickers panel";
"lng_new_version_text" = "— Improved sticker panel\n— Bug fixes and minor stuff";
"lng_menu_insert_unicode" = "Insert Unicode control character";

View File

@@ -54,7 +54,7 @@ wndBG: #FFF;
wndShadow: sprite(209px, 46px, 19px, 19px);
wndShadowShift: 1px;
layerAlpha: 0.3;
layerAlpha: 0.5;
layerBG: #000;
titleBG: #6389a8;
@@ -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;
@@ -774,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);
@@ -788,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;
@@ -818,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);
@@ -851,6 +851,7 @@ msgImgDblCheckRect: sprite(300px, 65px, 20px, 20px);
msgDateImgDelta: 4px;
msgDateImgColor: #fff;
msgDateImgBg: #00000054;
msgDateImgSelectBg: #1c4a7187;
msgDateImgPadding: point(8px, 2px);
msgDateImgCheckSpace: 4px;
@@ -862,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;
@@ -985,12 +986,21 @@ btnAttachPhoto: iconedButton(btnAttachDocument) {
btnAttachEmoji: iconedButton(btnAttachDocument) {
overBgColor: white;
icon: sprite(363px, 344px, 21px, 22px);
iconPos: point(6px, 13px);
iconPos: point(6px, 12px);
downIcon: sprite(363px, 344px, 21px, 22px);
downIconPos: point(6px, 13px);
downIconPos: point(6px, 12px);
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;
@@ -1177,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;
@@ -1376,6 +1385,7 @@ dropdownDef: dropdown {
padding: margins(10px, 10px, 10px, 10px);
shadow: sprite(241px, 46px, 6px, 6px);
shadowShift: 1px;
duration: 150;
width: 0px;
@@ -1474,7 +1484,7 @@ newScroll: flatScroll(scrollDef) {
stickersMaxHeight: 340px;
stickersAddOrShare: 70px;
btnStickersAdd: flatButton(btnDefNext, btnDefBig) {
width: 180px;
width: 200px;
height: 42px;
textTop: 9px;
@@ -1519,12 +1529,12 @@ 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(105px, 196px, 21px, 22px);
emojiActivityOver: sprite(308px, 264px, 21px, 22px);
emojiActivityActive: sprite(308px, 286px, 21px, 22px);
emojiTravel: sprite(126px, 196px, 21px, 22px);
emojiTravelOver: sprite(321px, 344px, 21px, 22px);
emojiTravelActive: sprite(321px, 366px, 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);
@@ -1618,7 +1628,6 @@ emojiPanSize: size(39px, 35px);
emojiPanFullSize: size(300px, 321px);
emojiPanDuration: 200;
emojiPanHover: #f0f4f7;
emojiPanRound: 2px;
emojiPanHeader: 42px;
emojiPanHeaderFont: font(fsize semibold);
@@ -1638,10 +1647,16 @@ emojiSwitchEmoji: sprite(310px, 328px, 8px, 12px);
emojiSwitchColor: #42a8db;
stickerPanSize: size(55px, 55px);
stickerPanRound: 3px;
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;
mvBgColor: #222;
mvBgOpacity: 0.92;
@@ -1718,9 +1733,13 @@ 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;
@@ -1730,11 +1749,16 @@ mvDocLink: linkButton(btnDefLink) {
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;
@@ -1794,7 +1818,7 @@ radialBgOpacity: 0.4;
radialDownload: sprite(346px, 0px, 50px, 50px);
radialDownloadOpacity: 0.8;
radialCancel: sprite(378px, 50px, 18px, 18px);
radialCancelOpacity: 0.7;
radialCancelOpacity: 1.0;
overviewLoader: size(34px, 14px);
overviewLoaderPoint: size(4px, 4px);

View File

@@ -20,7 +20,7 @@ textStyle {
lnkOverFlags: font;
lnkColor: color;
lnkDownColor: color;
selectBG: color;
selectBg: color;
selectOverlay: color;
lineHeight: number;
}
@@ -254,6 +254,7 @@ dropdown {
padding: margins;
shadow: sprite;
shadowShift: number;
duration: number;
width: number;

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,13 +139,22 @@ int main(int argc, const char * argv[]) {
}
if (update) {
writeLog([@"Starting update files iteration, path: " stringByAppendingString: [workDir stringByAppendingString:@"tupdates/ready"]]);
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) {
@@ -163,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

@@ -75,6 +75,9 @@ namespace {
QPixmap *sprite = 0, *emojis = 0, *emojisLarge = 0;
QPixmap *corners[RoundCornersCount][4] = { { 0 } };
QImage *cornersMask[4] = { 0 };
typedef QMap<uint64, QPixmap> EmojisMap;
EmojisMap mainEmojisMap;
QMap<int32, EmojisMap> otherEmojisMap;
@@ -86,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;
@@ -892,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);
}
@@ -934,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;
@@ -1521,6 +1541,7 @@ namespace App {
cSetStickersHash(QByteArray());
cSetEmojiStickers(EmojiStickersMap());
cSetStickerSets(StickerSets());
cSetStickerSetsOrder(StickerSetsOrder());
cSetLastStickersUpdate(0);
::videoItems.clear();
::audioItems.clear();
@@ -1572,6 +1593,34 @@ 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();
@@ -1593,6 +1642,32 @@ namespace App {
::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) {
@@ -1610,6 +1685,12 @@ namespace App {
::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();
@@ -1922,6 +2003,41 @@ namespace App {
}
}
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;
@@ -2064,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));
@@ -2077,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();
@@ -93,6 +118,7 @@ namespace App {
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);
@@ -205,14 +231,26 @@ namespace App {
void stickersBox(const QString &name);
void openLocalUrl(const QString &url);
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);
}
void initBackground(int32 id = DefaultChatBackground, const QImage &p = QImage(), bool nowrite = false);
style::color msgServiceBG();
style::color historyScrollBarColor();
style::color historyScrollBgColor();
style::color historyScrollBarOverColor();
style::color historyScrollBgOverColor();
style::color introPointHoverColor();
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;
@@ -180,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;
@@ -213,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();
}
}
@@ -487,7 +466,7 @@ void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId)
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);
@@ -540,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);
@@ -648,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();
@@ -658,13 +636,13 @@ 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() < 8014) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Added support for sticker packs\n\xe2\x80\x94 New emoji and sticker panel").replace('@', qsl("@") + QChar(0x200D));
} else if (!DevChannel && Local::oldMapVersion() < 8013) {
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() < 8024) {
versionFeatures = lang(lng_new_version_text).trimmed();
}
if (!versionFeatures.isEmpty()) {

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.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 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

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

@@ -83,7 +83,25 @@ bool StickerSetInner::failedSet(const RPCError &error) {
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);
@@ -132,7 +150,7 @@ void StickerSetInner::paintEvent(QPaintEvent *e) {
}
}
float64 coef = qMin((st::stickersSize.width() - st::stickerPanRound * 2) / float64(doc->dimensions.width()), (st::stickersSize.height() - st::stickerPanRound * 2) / float64(doc->dimensions.height()));
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;

View File

@@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
static const int32 AppVersion = 8014;
static const wchar_t *AppVersionStr = L"0.8.14";
static const bool DevChannel = true;
static const int32 AppVersion = 8024;
static const wchar_t *AppVersionStr = L"0.8.24";
static const bool DevChannel = false;
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

View File

@@ -120,7 +120,7 @@ void Dropdown::paintEvent(QPaintEvent *e) {
// draw shadow
QRect r(_st.padding.left(), _st.padding.top(), _width - _st.padding.left() - _st.padding.right(), _height - _st.padding.top() - _st.padding.bottom());
_shadow.paint(p, r);
_shadow.paint(p, r, _st.shadowShift);
if (!_buttons.isEmpty() && _st.border > 0) { // paint separators
p.setPen(_st.borderColor->p);
@@ -308,7 +308,7 @@ bool Dropdown::eventFilter(QObject *obj, QEvent *e) {
}
DragArea::DragArea(QWidget *parent) : TWidget(parent),
_hiding(false), _in(false), a_opacity(0), a_color(st::dragColor->c), _shadow(st::boxShadow) {
_hiding(false), _in(false), a_opacity(0), a_color(st::dragColor->c), _shadow(st::boxShadow) {
setMouseTracking(true);
setAcceptDrops(true);
}
@@ -354,7 +354,7 @@ void DragArea::paintEvent(QPaintEvent *e) {
QRect r(st::dragPadding.left(), st::dragPadding.top(), width() - st::dragPadding.left() - st::dragPadding.right(), height() - st::dragPadding.top() - st::dragPadding.bottom());
// draw shadow
_shadow.paint(p, r);
_shadow.paint(p, r, st::boxShadowShift);
p.fillRect(r, st::white->b);
@@ -487,7 +487,7 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) {
int32 w = st::dropdownDef.shadow.pxWidth(), h = st::dropdownDef.shadow.pxHeight();
QRect r = QRect(w, h, width() - 2 * w, height() - 2 * h);
_shadow.paint(p, r);
_shadow.paint(p, r, st::dropdownDef.shadowShift);
if (_cache.isNull()) {
p.fillRect(r, st::white->b);
@@ -675,11 +675,9 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) {
QPoint w(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + variant * st::emojiPanSize.width() + (variant ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding);
if (hover > 0) {
p.setOpacity(hover);
p.setBrush(st::emojiPanHover->b);
p.setPen(Qt::NoPen);
QPoint tl(w);
if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width());
p.drawRoundedRect(QRect(tl, st::emojiPanSize), st::emojiPanRound, st::emojiPanRound);
App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners);
p.setOpacity(1);
}
int esize = EmojiSizes[EIndex + 1];
@@ -786,11 +784,9 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) {
QPoint w(st::emojiPanPadding + j * st::emojiPanSize.width(), y + i * st::emojiPanSize.height());
if (hover > 0) {
p.setOpacity(hover);
p.setBrush(st::emojiPanHover->b);
p.setPen(Qt::NoPen);
QPoint tl(w);
if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width());
p.drawRoundedRect(QRect(tl, st::emojiPanSize), st::emojiPanRound, st::emojiPanRound);
App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners);
p.setOpacity(1);
}
p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (_esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (_esize / cIntRetinaFactor())) / 2, width(), App::emojisLarge(), QRect(_emojis[c][index]->x * _esize, _emojis[c][index]->y * _esize, _esize, _esize));
@@ -928,9 +924,9 @@ void EmojiPanInner::onShowPicker() {
int32 size = (c == tab) ? (sel - (sel % EmojiPanPerRow)) : _counts[c], rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0);
y += st::emojiPanHeader + (rows * st::emojiPanSize.height());
}
y -= _picker.height() - st::emojiPanRound;
y -= _picker.height() - st::msgRadius;
if (y < _top) {
y += _picker.height() - st::emojiPanRound + st::emojiPanSize.height() - st::emojiPanRound;
y += _picker.height() - st::msgRadius + st::emojiPanSize.height() - st::msgRadius;
}
int xmax = width() - _picker.width();
float64 coef = float64(sel % EmojiPanPerRow) / float64(EmojiPanPerRow - 1);
@@ -1158,10 +1154,12 @@ void StickerPanInner::setScrollTop(int top) {
}
int StickerPanInner::countHeight() {
int result = 0;
int result = 0, minLastH = st::emojiPanFullSize.height() - st::rbEmoji.height - st::stickerPanPadding;
for (int i = 0; i < _sets.size(); ++i) {
int cnt = _sets.at(i).size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0);
result += st::emojiPanHeader + rows * st::stickerPanSize.height();
int h = st::emojiPanHeader + rows * st::stickerPanSize.height();
if (i == _sets.size() - 1 && h < minLastH) h = minLastH;
result += h;
}
return result + st::stickerPanPadding;
}
@@ -1181,7 +1179,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
tilly = y + st::emojiPanHeader + (rows * st::stickerPanSize.height());
if (r.top() >= tilly) continue;
bool special = (_setIds[c] == DefaultStickerSetId || _setIds[c] == CustomStickerSetId || _setIds[c] == RecentStickerSetId);
bool special = (_setIds[c] == DefaultStickerSetId || _setIds[c] == RecentStickerSetId);
y += st::emojiPanHeader;
QString title = _titles[c];
@@ -1211,11 +1209,9 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
QPoint pos(st::stickerPanPadding + j * st::stickerPanSize.width(), y + i * st::stickerPanSize.height());
if (hover > 0) {
p.setOpacity(hover);
p.setBrush(st::emojiPanHover->b);
p.setPen(Qt::NoPen);
QPoint tl(pos);
if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width());
p.drawRoundedRect(QRect(tl, st::stickerPanSize), st::stickerPanRound, st::stickerPanRound);
App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners);
p.setOpacity(1);
}
@@ -1236,7 +1232,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
}
}
float64 coef = qMin((st::stickerPanSize.width() - st::stickerPanRound * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::stickerPanRound * 2) / float64(sticker->dimensions.height()));
float64 coef = qMin((st::stickerPanSize.width() - st::msgRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::msgRadius * 2) / float64(sticker->dimensions.height()));
if (coef > 1) coef = 1;
int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height());
if (w < 1) w = 1;
@@ -1248,7 +1244,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
p.drawPixmapLeft(ppos, width(), sticker->sticker->img->pix(w, h));
}
if (hover > 0 && _setIds[c] == CustomStickerSetId) {
if (hover > 0 && _setIds[c] == RecentStickerSetId && _custom.at(index)) {
float64 xHover = _hovers[c][_sets[c].size() + index];
QPoint xPos = pos + QPoint(st::stickerPanSize.width() - st::stickerPanDelete.pxWidth(), 0);
@@ -1302,16 +1298,16 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
}
int tab = (_selected / emojiTabShift), sel = _selected % emojiTabShift;
if (_setIds[tab] == CustomStickerSetId && sel >= _sets[tab].size() && sel < _sets[tab].size() * 2) {
if (_setIds[tab] == RecentStickerSetId && sel >= _sets[tab].size() && sel < _sets[tab].size() * 2 && _custom.at(sel - _sets[tab].size())) {
clearSelection(true);
int32 refresh = 0;
bool refresh = false;
DocumentData *sticker = _sets[tab].at(sel - _sets[tab].size());
RecentStickerPack &recent(cGetRecentStickers());
for (int32 i = 0, l = recent.size(); i < l; ++i) {
if (recent.at(i).first == sticker) {
recent.removeAt(i);
Local::writeUserSettings();
refresh = 1;
refresh = true;
break;
}
}
@@ -1325,17 +1321,13 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
sets.erase(it);
}
Local::writeStickers();
refresh = 2;
refresh = true;
break;
}
}
}
if (refresh) {
if (refresh > 1) {
refreshStickers();
} else {
refreshRecent();
}
refreshRecent();
updateSelected();
update();
}
@@ -1392,18 +1384,16 @@ void StickerPanInner::refreshStickers() {
refreshRecent(false);
StickerSets::const_iterator it;
it = sets.constFind(CustomStickerSetId); if (it != sets.cend()) appendSet(it);
it = sets.constFind(DefaultStickerSetId); if (it != sets.cend()) appendSet(it);
for (it = sets.cbegin(); it != sets.cend(); ++it) {
if (it->id != CustomStickerSetId && it->id != DefaultStickerSetId) appendSet(it);
appendSet(DefaultStickerSetId);
for (StickerSetsOrder::const_iterator i = cStickerSetsOrder().cbegin(), e = cStickerSetsOrder().cend(); i != e; ++i) {
appendSet(*i);
}
int32 h = countHeight();
if (h != height()) resize(width(), h);
emit refreshIcons();
updateSelected();
}
@@ -1437,8 +1427,23 @@ void StickerPanInner::preloadImages() {
}
}
void StickerPanInner::appendSet(StickerSets::const_iterator it) {
if (it->stickers.isEmpty()) return;
uint64 StickerPanInner::currentSet(int yOffset) const {
int y, ytill = 0;
for (int i = 0, l = _sets.size(); i < l; ++i) {
int cnt = _sets.at(i).size();
y = ytill;
ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height();
if (yOffset < ytill) {
return _setIds.at(i);
}
}
return _setIds.isEmpty() ? RecentStickerSetId : _setIds.back();
}
void StickerPanInner::appendSet(uint64 setId) {
const StickerSets &sets(cStickerSets());
StickerSets::const_iterator it = sets.constFind(setId);
if (it == sets.cend() || it->stickers.isEmpty()) return;
StickerPack pack;
pack.reserve(it->stickers.size());
@@ -1447,14 +1452,16 @@ void StickerPanInner::appendSet(StickerSets::const_iterator it) {
}
_setIds.push_back(it->id);
_sets.push_back(pack);
_hovers.push_back(QVector<float64>(it->stickers.size() + (it->id == CustomStickerSetId ? it->stickers.size() : 1), 0));
_hovers.push_back(QVector<float64>(it->stickers.size() + 1, 0));
int32 availw = width() - st::emojiPanHeaderLeft - st::emojiSwitchSkip - _emojiWidth - (st::emojiSwitchSkip - st::emojiSwitchImgSkip);
_titles.push_back(st::emojiPanHeaderFont->m.elidedText(it->title, Qt::ElideRight, availw));
}
void StickerPanInner::refreshRecent(bool performResize) {
_custom.clear();
clearSelection(true);
if (cGetRecentStickers().isEmpty()) {
StickerSets::const_iterator customIt = cStickerSets().constFind(CustomStickerSetId);
if (cGetRecentStickers().isEmpty() && (customIt == cStickerSets().cend() || customIt->stickers.isEmpty())) {
if (!_setIds.isEmpty() && _setIds.at(0) == RecentStickerSetId) {
_setIds.pop_front();
_sets.pop_front();
@@ -1463,18 +1470,33 @@ void StickerPanInner::refreshRecent(bool performResize) {
}
} else {
StickerPack recent;
recent.reserve(cGetRecentStickers().size());
int32 customCnt = (customIt == cStickerSets().cend() ? 0 : customIt->stickers.size());
QMap<DocumentData*, bool> recentOnly;
recent.reserve(cGetRecentStickers().size() + customCnt);
_custom.reserve(cGetRecentStickers().size() + customCnt);
for (int32 i = 0, l = cGetRecentStickers().size(); i < l; ++i) {
recent.push_back(cGetRecentStickers().at(i).first);
DocumentData *s = cGetRecentStickers().at(i).first;
recent.push_back(s);
recentOnly.insert(s, true);
_custom.push_back(false);
}
for (int32 i = 0; i < customCnt; ++i) {
DocumentData *s = customIt->stickers.at(i);
if (recentOnly.contains(s)) {
_custom[recent.indexOf(s)] = true;
} else {
recent.push_back(s);
_custom.push_back(true);
}
}
if (_setIds.isEmpty() || _setIds.at(0) != RecentStickerSetId) {
_setIds.push_front(RecentStickerSetId);
_hovers.push_back(QVector<float64>(recent.size() + 1, 0));
_hovers.push_back(QVector<float64>(recent.size() * 2, 0));
_sets.push_back(recent);
_titles.push_back(lang(lng_emoji_category0));
} else {
_sets[0] = recent;
_hovers[0].resize(recent.size() + 1);
_hovers[0].resize(recent.size() * 2);
}
}
@@ -1486,6 +1508,31 @@ void StickerPanInner::refreshRecent(bool performResize) {
}
}
void StickerPanInner::fillIcons(QVector<StickerIcon> &icons) {
icons.clear();
if (_setIds.isEmpty()) return;
icons.reserve(_sets.size());
int32 i = 0;
if (_setIds.at(0) == RecentStickerSetId) ++i;
if (i > 0) icons.push_back(StickerIcon());
for (int32 l = _sets.size(); i < l; ++i) {
DocumentData *s = _sets.at(i).at(0);
int32 availw = st::rbEmoji.width - 2 * st::stickerIconPadding, availh = st::rbEmoji.height - 2 * st::stickerIconPadding;
int32 thumbw = s->thumb->width(), thumbh = s->thumb->height(), pixw = 1, pixh = 1;
if (availw * thumbh > availh * thumbw) {
pixh = availh;
pixw = (pixh * thumbw) / thumbh;
} else {
pixw = availw;
pixh = thumbw ? ((pixw * thumbh) / thumbw) : 1;
}
if (pixw < 1) pixw = 1;
if (pixh < 1) pixh = 1;
icons.push_back(StickerIcon(_setIds.at(i), s, pixw, pixh));
}
}
void StickerPanInner::updateSelected() {
if (_pressedSel >= 0) return;
@@ -1502,7 +1549,7 @@ void StickerPanInner::updateSelected() {
int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::stickerPanPadding;
for (int c = 0, l = _setIds.size(); c < l; ++c) {
int cnt = _sets[c].size();
bool special = _setIds[c] == DefaultStickerSetId || _setIds[c] == CustomStickerSetId || _setIds[c] == RecentStickerSetId;
bool special = _setIds[c] == DefaultStickerSetId || _setIds[c] == RecentStickerSetId;
y = ytill;
ytill = y + st::emojiPanHeader + ((cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0)) * st::stickerPanSize.height();
if (p.y() >= y && p.y() < ytill) {
@@ -1515,7 +1562,7 @@ void StickerPanInner::updateSelected() {
if (selIndex >= _sets[c].size()) {
selIndex = -1;
} else {
if (_setIds[c] == CustomStickerSetId) {
if (_setIds[c] == RecentStickerSetId && _custom[selIndex]) {
int32 inx = sx - (selIndex % StickerPanPerRow) * st::stickerPanSize.width(), iny = p.y() - y - ((selIndex / StickerPanPerRow) * st::stickerPanSize.height());
if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.pxWidth() && iny < st::stickerPanDelete.pxHeight()) {
selIndex += _sets[c].size();
@@ -1532,11 +1579,11 @@ void StickerPanInner::updateSelected() {
bool startanim = false;
int oldSel = _selected, oldSelTab = oldSel / emojiTabShift, xOldSel = -1, newSel = selIndex, newSelTab = newSel / emojiTabShift, xNewSel = -1;
if (oldSel >= 0 && oldSelTab < _setIds.size() && _setIds[oldSelTab] == CustomStickerSetId && oldSel >= oldSelTab * emojiTabShift + _sets[oldSelTab].size()) {
if (oldSel >= 0 && oldSelTab < _setIds.size() && _setIds[oldSelTab] == RecentStickerSetId && oldSel >= oldSelTab * emojiTabShift + _sets[oldSelTab].size()) {
xOldSel = oldSel;
oldSel -= _sets[oldSelTab].size();
}
if (newSel >= 0 && newSelTab < _setIds.size() && _setIds[newSelTab] == CustomStickerSetId && newSel >= newSelTab * emojiTabShift + _sets[newSelTab].size()) {
if (newSel >= 0 && newSelTab < _setIds.size() && _setIds[newSelTab] == RecentStickerSetId && newSel >= newSelTab * emojiTabShift + _sets[newSelTab].size()) {
xNewSel = newSel;
newSel -= _sets[newSelTab].size();
}
@@ -1612,7 +1659,7 @@ void StickerPanInner::showStickerSet(uint64 setId) {
}
EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent),
_noTabUpdate(false), _hiding(false), a_opacity(0), _shadow(st::dropdownDef.shadow),
_horizontal(false), _noTabUpdate(false), _hiding(false), a_opacity(0), _shadow(st::dropdownDef.shadow),
_recent(this , qsl("emoji_group"), dbietRecent , QString(), true , st::rbEmojiRecent),
_people(this , qsl("emoji_group"), dbietPeople , QString(), false, st::rbEmojiPeople),
_nature(this , qsl("emoji_group"), dbietNature , QString(), false, st::rbEmojiNature),
@@ -1621,6 +1668,9 @@ _celebration(this, qsl("emoji_group"), dbietCelebration, QString(), false, st::r
_activity(this , qsl("emoji_group"), dbietActivity , QString(), false, st::rbEmojiActivity),
_travel(this , qsl("emoji_group"), dbietTravel , QString(), false, st::rbEmojiTravel),
_objects(this , qsl("emoji_group"), dbietObjects , QString(), false, st::rbEmojiObjects),
_iconOver(-1), _iconSel(0), _iconDown(-1), _iconsDragging(false),
_iconAnim(animFunc(this, &EmojiPan::iconAnim)),
_iconsLeft(0), _iconsTop(0), _iconsStartX(0), _iconsMax(0), _iconsX(0, 0), _iconsStartAnim(0),
_stickersShown(false), _moveStart(0),
e_scroll(this, st::emojiScroll), e_inner(), s_scroll(this, st::emojiScroll), s_inner(), _removingSetId(0) {
setFocusPolicy(Qt::NoFocus);
@@ -1633,8 +1683,8 @@ e_scroll(this, st::emojiScroll), e_inner(), s_scroll(this, st::emojiScroll), s_i
_height = st::dropdownDef.padding.top() + st::emojiPanFullSize.height() + st::dropdownDef.padding.bottom();
resize(_width, _height);
e_scroll.resize(st::emojiPanFullSize.width(), st::emojiPanFullSize.height() - _recent.height());
s_scroll.resize(st::emojiPanFullSize.width(), st::emojiPanFullSize.height());
e_scroll.resize(st::emojiPanFullSize.width(), st::emojiPanFullSize.height() - st::rbEmoji.height);
s_scroll.resize(st::emojiPanFullSize.width(), st::emojiPanFullSize.height() - st::rbEmoji.height);
e_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top());
e_scroll.setWidget(&e_inner);
@@ -1646,8 +1696,8 @@ e_scroll(this, st::emojiScroll), e_inner(), s_scroll(this, st::emojiScroll), s_i
s_inner.setAttribute(Qt::WA_OpaquePaintEvent);
s_scroll.setAutoFillBackground(true);
int32 left = st::dropdownDef.padding.left() + (st::emojiPanFullSize.width() - 8 * _recent.width()) / 2;
int32 top = st::dropdownDef.padding.top() + st::emojiPanFullSize.height() - _recent.height();
int32 left = _iconsLeft = st::dropdownDef.padding.left() + (st::emojiPanFullSize.width() - 8 * st::rbEmoji.width) / 2;
int32 top = _iconsTop = st::dropdownDef.padding.top() + st::emojiPanFullSize.height() - st::rbEmoji.height;
prepareTab(left, top, _width, _recent);
prepareTab(left, top, _width, _people);
prepareTab(left, top, _width, _nature);
@@ -1675,10 +1725,14 @@ e_scroll(this, st::emojiScroll), e_inner(), s_scroll(this, st::emojiScroll), s_i
connect(&s_inner, SIGNAL(switchToEmoji()), this, SLOT(onSwitch()));
connect(&s_inner, SIGNAL(removing(uint64)), this, SLOT(onRemoveSet(uint64)));
connect(&s_inner, SIGNAL(refreshIcons()), this, SLOT(onRefreshIcons()));
if (cPlatform() == dbipMac) {
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
}
setMouseTracking(true);
// setAttribute(Qt::WA_AcceptTouchEvents);
}
void EmojiPan::prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab) {
@@ -1695,7 +1749,7 @@ void EmojiPan::onWndActiveChanged() {
}
void EmojiPan::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
float64 o = 1;
if (!_cache.isNull()) {
@@ -1704,21 +1758,70 @@ void EmojiPan::paintEvent(QPaintEvent *e) {
QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom());
_shadow.paint(p, r);
_shadow.paint(p, r, st::dropdownDef.shadowShift);
if (_toCache.isNull()) {
if (_cache.isNull()) {
if (!_stickersShown) {
p.fillRect(r.left(), _recent.y(), (rtl() ? _objects.x() : _recent.x() - r.left()), _recent.height(), st::emojiPanCategories->b);
if (_stickersShown) {
p.fillRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height, st::emojiPanCategories->b);
if (!_icons.isEmpty()) {
int32 x = _iconsLeft, i = 0;
if (!_icons.at(i).sticker) {
if (_iconSel == i) {
p.fillRect(rtl() ? (width() - x - st::rbEmoji.width) : x, _iconsTop, st::rbEmoji.width, st::rbEmoji.height, st::stickerIconSel->b);
} else if (_iconHovers.at(i) > 0) {
p.setOpacity(_iconHovers.at(i));
p.fillRect(rtl() ? (width() - x - st::rbEmoji.width) : x, _iconsTop, st::rbEmoji.width, st::rbEmoji.height, st::stickerIconHover->b);
p.setOpacity(1);
}
p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::stickerIconRecent);
x += st::rbEmoji.width;
++i;
}
QRect clip(x, _iconsTop, _iconsLeft + 8 * st::rbEmoji.width - x, st::rbEmoji.height);
if (rtl()) clip.moveLeft(width() - x - clip.width());
p.setClipRect(clip);
i += _iconsX.current() / int(st::rbEmoji.width);
x -= _iconsX.current() % int(st::rbEmoji.width);
for (int32 l = qMin(_icons.size(), i + 8 + (_icons.at(0).sticker ? 1 : 0)); i < l; ++i) {
const StickerIcon &s(_icons.at(i));
s.sticker->thumb->load();
QPixmap pix(s.sticker->thumb->pix(s.pixw, s.pixh));
if (_iconSel == i) {
p.fillRect(rtl() ? (width() - x - st::rbEmoji.width) : x, _iconsTop, st::rbEmoji.width, st::rbEmoji.height, st::stickerIconSel->b);
} else if (_iconHovers.at(i) > 0) {
p.setOpacity(_iconHovers.at(i));
p.fillRect(rtl() ? (width() - x - st::rbEmoji.width) : x, _iconsTop, st::rbEmoji.width, st::rbEmoji.height, st::stickerIconHover->b);
p.setOpacity(1);
}
p.drawPixmapLeft(x + (st::rbEmoji.width - s.pixw) / 2, _iconsTop + (st::rbEmoji.height - s.pixh) / 2, width(), pix);
x += st::rbEmoji.width;
}
float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.pxWidth(), 0., 1.);
if (o_left > 0) {
p.setOpacity(o_left);
p.drawSpriteLeft(QRect(_iconsLeft + (_icons.at(0).sticker ? 0 : st::rbEmoji.width), _iconsTop, st::stickerIconLeft.pxWidth(), st::rbEmoji.height), width(), st::stickerIconLeft);
}
float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.pxWidth(), 0., 1.);
if (o_right > 0) {
p.setOpacity(o_right);
p.drawSpriteRight(QRect(width() - _iconsLeft - 8 * st::rbEmoji.width, _iconsTop, st::stickerIconRight.pxWidth(), st::rbEmoji.height), width(), st::stickerIconRight);
}
}
} else {
p.fillRect(r.left(), _recent.y(), (rtl() ? _objects.x() : _recent.x() - r.left()), st::rbEmoji.height, st::emojiPanCategories->b);
int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width());
p.fillRect(x, _recent.y(), r.left() + r.width() - x, _recent.height(), st::emojiPanCategories->b);
p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::emojiPanCategories->b);
}
} else {
p.fillRect(r, st::white->b);
p.drawPixmap(r.left(), r.top(), _cache);
}
} else {
p.fillRect(r, st::white->b);
p.fillRect(QRect(r.left(), r.top(), r.width(), r.height() - st::rbEmoji.height), st::white->b);
p.fillRect(QRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height), st::emojiPanCategories->b);
p.setOpacity(o * a_fromAlpha.current());
QRect fromDst = QRect(r.left() + a_fromCoord.current(), r.top(), _fromCache.width() / cIntRetinaFactor(), _fromCache.height() / cIntRetinaFactor());
QRect fromSrc = QRect(0, 0, _fromCache.width(), _fromCache.height());
@@ -1775,6 +1878,88 @@ void EmojiPan::otherLeave() {
}
}
void EmojiPan::mousePressEvent(QMouseEvent *e) {
if (!_stickersShown || _icons.isEmpty()) return;
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
updateSelected();
_iconDown = _iconOver;
_iconsMouseDown = _iconsMousePos;
_iconsStartX = _iconsX.current();
}
void EmojiPan::mouseMoveEvent(QMouseEvent *e) {
if (!_stickersShown || _icons.isEmpty()) return;
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
updateSelected();
if (!_iconsDragging && _iconDown >= (_icons.at(0).sticker ? 0 : 1)) {
if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) {
_iconsDragging = true;
}
}
if (_iconsDragging) {
int32 newX = snap(_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), 0, _iconsMax);
if (newX != _iconsX.current()) {
_iconsX = anim::ivalue(newX, newX);
_iconsStartAnim = 0;
if (_iconAnimations.isEmpty()) _iconAnim.stop();
updateIcons();
}
}
}
void EmojiPan::mouseReleaseEvent(QMouseEvent *e) {
if (!_stickersShown || _icons.isEmpty()) return;
int32 wasDown = _iconDown;
_iconDown = -1;
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
if (_iconsDragging) {
int32 newX = snap(_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), 0, _iconsMax);
if (newX != _iconsX.current()) {
_iconsX = anim::ivalue(newX, newX);
_iconsStartAnim = 0;
if (_iconAnimations.isEmpty()) _iconAnim.stop();
updateIcons();
}
_iconsDragging = false;
updateSelected();
} else {
updateSelected();
if (wasDown == _iconOver && _iconOver >= 0) {
s_inner.showStickerSet(_icons.at(_iconOver).setId);
}
}
}
bool EmojiPan::event(QEvent *e) {
if (e->type() == QEvent::TouchBegin) {
int a = 0;
} else if (e->type() == QEvent::Wheel && _iconOver >= ((_icons.isEmpty() || _icons.at(0).sticker) ? 0 : 1) && _iconDown < 0) {
QWheelEvent *ev = static_cast<QWheelEvent*>(e);
bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal);
bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical);
if (hor) _horizontal = true;
int32 newX = _iconsX.current();
if (/*_horizontal && */hor) {
newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax);
} else if (/*!_horizontal && */ver) {
newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax);
}
if (newX != _iconsX.current()) {
_iconsX = anim::ivalue(newX, newX);
_iconsStartAnim = 0;
if (_iconAnimations.isEmpty()) _iconAnim.stop();
updateSelected();
updateIcons();
}
}
return TWidget::event(e);
}
void EmojiPan::fastHide() {
if (animating()) {
anim::stop(this);
@@ -1792,6 +1977,116 @@ void EmojiPan::refreshStickers() {
}
}
void EmojiPan::onRefreshIcons() {
_iconOver = -1;
_iconHovers.clear();
_iconAnimations.clear();
s_inner.fillIcons(_icons);
_iconsX = anim::ivalue(0, 0);
_iconsStartAnim = 0;
_iconAnim.stop();
if (_icons.isEmpty()) {
_iconsMax = 0;
} else {
_iconHovers = QVector<float64>(_icons.size(), 0);
_iconsMax = qMax(int((_icons.size() - 8) * st::rbEmoji.width), 0);
}
updateSelected();
updateIcons();
}
void EmojiPan::leaveToChildEvent(QEvent *e) {
if (!_stickersShown) return;
_iconsMousePos = QCursor::pos();
updateSelected();
}
void EmojiPan::updateSelected() {
if (_icons.isEmpty() || _iconDown >= 0) return;
QPoint p(mapFromGlobal(_iconsMousePos));
int32 x = p.x(), y = p.y(), newOver = -1;
if (rtl()) x = width() - x;
x -= _iconsLeft;
if (y >= _iconsTop && y < _iconsTop + st::rbEmoji.height && x >= 0 && x < 8 * st::rbEmoji.width && x < _icons.size() * st::rbEmoji.width) {
if (!_icons.at(0).sticker) {
if (x < st::rbEmoji.width) {
newOver = 0;
} else {
x -= st::rbEmoji.width;
}
}
if (newOver < 0) {
x += _iconsX.current();
newOver = qFloor(x / st::rbEmoji.width) + (_icons.at(0).sticker ? 0 : 1);
}
}
if (newOver != _iconOver) {
if (newOver < 0) {
setCursor(style::cur_default);
} else if (_iconOver < 0) {
setCursor(style::cur_pointer);
}
bool startanim = false;
if (_iconOver >= 0) {
_iconAnimations.remove(_iconOver + 1);
if (_iconAnimations.find(-_iconOver - 1) == _iconAnimations.end()) {
if (_iconAnimations.isEmpty() && !_iconsStartAnim) startanim = true;
_iconAnimations.insert(-_iconOver - 1, getms());
}
}
_iconOver = newOver;
if (_iconOver >= 0) {
_iconAnimations.remove(-_iconOver - 1);
if (_iconAnimations.find(_iconOver + 1) == _iconAnimations.end()) {
if (_iconAnimations.isEmpty() && !_iconsStartAnim) startanim = true;
_iconAnimations.insert(_iconOver + 1, getms());
}
}
if (startanim) _iconAnim.start();
}
}
void EmojiPan::updateIcons() {
QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom());
update(r.left(), _iconsTop, r.width(), st::rbEmoji.height);
}
bool EmojiPan::iconAnim(float64 ms) {
if (!_stickersShown) return false;
uint64 now = getms();
for (Animations::iterator i = _iconAnimations.begin(); i != _iconAnimations.end();) {
int index = qAbs(i.key()) - 1;
float64 dt = float64(now - i.value()) / st::emojiPanDuration;
if (index >= _iconHovers.size()) {
i = _iconAnimations.erase(i);
} else if (dt >= 1) {
_iconHovers[index] = (i.key() > 0) ? 1 : 0;
i = _iconAnimations.erase(i);
} else {
_iconHovers[index] = (i.key() > 0) ? dt : (1 - dt);
++i;
}
}
if (_iconsStartAnim) {
float64 dt = (now - _iconsStartAnim) / st::stickerIconMove;
if (dt >= 1) {
_iconsStartAnim = 0;
_iconsX.finish();
} else {
_iconsX.update(dt, anim::linear);
}
updateSelected();
}
updateIcons();
return !_iconAnimations.isEmpty() || _iconsStartAnim;
}
bool EmojiPan::animStep(float64 ms) {
bool res1 = false;
if (_moveStart) {
@@ -1823,8 +2118,8 @@ bool EmojiPan::animStep(float64 ms) {
res1 = false;
hideFinish();
} else {
if (_toCache.isNull()) showAll();
_cache = QPixmap();
if (_toCache.isNull()) showAll();
}
} else {
a_opacity.update(dt, anim::linear);
@@ -1854,9 +2149,22 @@ void EmojiPan::hideFinish() {
e_inner.hideFinish();
_cache = _toCache = _fromCache = QPixmap();
_moveStart = 0;
_horizontal = false;
e_scroll.scrollToY(0);
if (!_recent.checked()) {
_noTabUpdate = true;
_recent.setChecked(true);
_noTabUpdate = false;
}
s_scroll.scrollToY(0);
_iconOver = _iconDown = -1;
_iconSel = 0;
_iconsX = anim::ivalue(0, 0);
_iconsStartAnim = 0;
_iconAnim.stop();
_iconHovers = _icons.isEmpty() ? QVector<float64>() : QVector<float64>(_icons.size(), 0);
_iconAnimations.clear();
}
void EmojiPan::showStart() {
@@ -1978,25 +2286,47 @@ void EmojiPan::onTabChange() {
void EmojiPan::onScroll() {
int top = e_scroll.scrollTop();
DBIEmojiTab tab = e_inner.currentTab(top);
FlatRadiobutton *check = 0;
switch (tab) {
case dbietRecent : check = &_recent ; break;
case dbietPeople : check = &_people ; break;
case dbietNature : check = &_nature ; break;
case dbietFood : check = &_food ; break;
case dbietCelebration: check = &_celebration; break;
case dbietActivity : check = &_activity ; break;
case dbietTravel : check = &_travel ; break;
case dbietObjects : check = &_objects ; break;
}
if (check && !check->checked()) {
_noTabUpdate = true;
check->setChecked(true);
_noTabUpdate = false;
if (!_stickersShown) {
DBIEmojiTab tab = e_inner.currentTab(top);
FlatRadiobutton *check = 0;
switch (tab) {
case dbietRecent : check = &_recent ; break;
case dbietPeople : check = &_people ; break;
case dbietNature : check = &_nature ; break;
case dbietFood : check = &_food ; break;
case dbietCelebration: check = &_celebration; break;
case dbietActivity : check = &_activity ; break;
case dbietTravel : check = &_travel ; break;
case dbietObjects : check = &_objects ; break;
}
if (check && !check->checked()) {
_noTabUpdate = true;
check->setChecked(true);
_noTabUpdate = false;
}
}
e_inner.setScrollTop(top);
s_inner.setScrollTop(s_scroll.scrollTop());
top = s_scroll.scrollTop();
if (_stickersShown) {
uint64 setId = s_inner.currentSet(top);
int32 newSel = 0;
for (int32 i = 0, l = _icons.size(); i < l; ++i) {
if (_icons.at(i).setId == setId) {
newSel = i;
break;
}
}
if (newSel != _iconSel) {
_iconSel = newSel;
_iconsX.start(snap((2 * newSel - 7 - ((_icons.isEmpty() || _icons.at(0).sticker) ? 0 : 1)) * int(st::rbEmoji.width) / 2, 0, _iconsMax));
_iconsStartAnim = getms();
_iconAnim.start();
updateSelected();
updateIcons();
}
}
s_inner.setScrollTop(top);
}
void EmojiPan::onSwitch() {
@@ -2004,6 +2334,11 @@ void EmojiPan::onSwitch() {
_fromCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding));
_stickersShown = !_stickersShown;
_iconOver = -1;
_iconHovers = _icons.isEmpty() ? QVector<float64>() : QVector<float64>(_icons.size(), 0);
_iconAnimations.clear();
_iconAnim.stop();
_cache = QPixmap();
showAll();
_toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding));
@@ -2012,9 +2347,9 @@ void EmojiPan::onSwitch() {
hideAll();
_moveStart = getms();
a_toCoord = _stickersShown ? anim::ivalue(st::emojiPanFullSize.width(), 0) : anim::ivalue(-st::emojiPanFullSize.width(), 0);
a_toCoord = (_stickersShown != rtl()) ? anim::ivalue(st::emojiPanFullSize.width(), 0) : anim::ivalue(-st::emojiPanFullSize.width(), 0);
a_toAlpha = anim::fvalue(0, 1);
a_fromCoord = _stickersShown ? anim::ivalue(0, -st::emojiPanFullSize.width()) : anim::ivalue(0, st::emojiPanFullSize.width());
a_fromCoord = (_stickersShown != rtl()) ? anim::ivalue(0, -st::emojiPanFullSize.width()) : anim::ivalue(0, st::emojiPanFullSize.width());
a_fromAlpha = anim::fvalue(1, 0);
if (!animating()) anim::start(this);
@@ -2023,7 +2358,7 @@ void EmojiPan::onSwitch() {
void EmojiPan::onRemoveSet(uint64 setId) {
StickerSets::const_iterator it = cStickerSets().constFind(setId);
if (it != cStickerSets().cend() && setId != CustomStickerSetId && setId != DefaultStickerSetId && setId != RecentStickerSetId) {
if (it != cStickerSets().cend() && setId != DefaultStickerSetId && setId != RecentStickerSetId) {
_removingSetId = it->id;
ConfirmBox *box = new ConfirmBox(lng_stickers_remove_pack(lt_sticker_pack, it->title));
connect(box, SIGNAL(confirmed()), this, SLOT(onRemoveSetSure()));
@@ -2035,7 +2370,7 @@ void EmojiPan::onRemoveSet(uint64 setId) {
void EmojiPan::onRemoveSetSure() {
App::wnd()->hideLayer();
StickerSets::iterator it = cRefStickerSets().find(_removingSetId);
if (it != cRefStickerSets().cend() && _removingSetId != CustomStickerSetId && _removingSetId != DefaultStickerSetId && _removingSetId != RecentStickerSetId) {
if (it != cRefStickerSets().cend() && _removingSetId != DefaultStickerSetId && _removingSetId != RecentStickerSetId) {
if (it->id && it->access) {
MTP::send(MTPmessages_UninstallStickerSet(MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access))));
} else if (!it->shortName.isEmpty()) {
@@ -2052,6 +2387,7 @@ void EmojiPan::onRemoveSetSure() {
}
}
cRefStickerSets().erase(it);
cRefStickerSetsOrder().removeOne(_removingSetId);
cSetStickersHash(QByteArray());
refreshStickers();
Local::writeStickers();
@@ -2110,7 +2446,7 @@ void MentionsInner::paintEvent(QPaintEvent *e) {
}
}
user->photo->load();
p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pix(st::mentionPhotoSize));
p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize));
user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
p.setFont(st::mentionFont->f);

View File

@@ -269,6 +269,16 @@ private:
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
@@ -294,9 +304,13 @@ public:
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();
@@ -306,6 +320,8 @@ signals:
void selected(DocumentData *sticker);
void removing(uint64 setId);
void refreshIcons();
void switchToEmoji();
void scrollToY(int y);
@@ -313,7 +329,7 @@ signals:
private:
void appendSet(StickerSets::const_iterator it);
void appendSet(uint64 setId);
int32 countHeight();
void selectEmoji(EmojiPtr emoji);
@@ -327,6 +343,7 @@ private:
QList<uint64> _setIds;
QList<StickerPack> _sets;
QList<QVector<float64> > _hovers;
QList<bool> _custom;
int32 _selected, _pressedSel;
QPoint _lastMousePos;
@@ -349,6 +366,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();
@@ -356,6 +379,8 @@ public:
bool animStep(float64 ms);
bool iconAnim(float64 ms);
bool eventFilter(QObject *obj, QEvent *e);
void stickersInstalled(uint64 setId);
@@ -377,6 +402,8 @@ public slots:
void onRemoveSetSure();
void onDelayedHide();
void onRefreshIcons();
signals:
void emojiSelected(EmojiPtr emoji);
@@ -385,6 +412,13 @@ signals:
private:
bool _horizontal;
void leaveToChildEvent(QEvent *e);
void updateSelected();
void updateIcons();
void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab);
void showAll();
@@ -403,6 +437,18 @@ private:
BoxShadow _shadow;
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;
bool _stickersShown;
QPixmap _fromCache, _toCache;

View File

@@ -40,6 +40,10 @@ void FileUploader::uploadMedia(MsgId msgId, const ReadyLocalMedia &media) {
if (!media.file.isEmpty()) {
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

@@ -104,26 +104,29 @@ namespace anim {
bool AnimatedGif::animStep(float64 ms) {
int32 f = frame;
while (f < frames.size() && ms > delays[f]) {
while (f < images.size() && ms > delays[f]) {
++f;
if (f == frames.size() && frames.size() < framesCount) {
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;
frames.push_back(QPixmap::fromImage(img.size() == QSize(w, h) ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly));
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 < frames.size(); ++i) {
if (!frames[i].isNull()) {
for (int32 i = 0; i < images.size(); ++i) {
if (!images[i].isNull() || !frames[i].isNull()) {
images[i] = QImage();
frames[i] = QPixmap();
break;
}
}
} else {
framesCount = frames.size();
framesCount = images.size();
}
}
if (f == frames.size()) {
if (f == images.size()) {
if (!duration) {
duration = delays.isEmpty() ? 1 : delays.back();
}
@@ -132,14 +135,16 @@ bool AnimatedGif::animStep(float64 ms) {
for (int32 i = 0, s = delays.size() - 1; i <= s; ++i) {
delays[i] += duration;
}
if (frames[f].isNull()) {
if (images[f].isNull()) {
QString fname = reader->fileName();
delete reader;
reader = new QImageReader(fname);
}
}
if (frames[f].isNull() && reader->read(&img)) {
frames[f] = QPixmap::fromImage(img.size() == QSize(w, h) ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly);
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) {
@@ -172,12 +177,15 @@ void AnimatedGif::start(HistoryItem *row, const QString &file) {
}
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;
frames.push_back(QPixmap::fromImage(img.size() == s ? img : img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly));
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;
@@ -202,6 +210,7 @@ void AnimatedGif::stop(bool onItemRemoved) {
HistoryItem *row = msg;
msg = 0;
frames.clear();
images.clear();
delays.clear();
w = h = frame = framesCount = duration = 0;
@@ -211,3 +220,16 @@ void AnimatedGif::stop(bool onItemRemoved) {
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

@@ -223,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
@@ -348,6 +405,8 @@ public:
stop(true);
}
const QPixmap &current(int32 width = 0, int32 height = 0, bool rounded = false);
signals:
void updated();
@@ -357,7 +416,12 @@ public:
HistoryItem *msg;
QImage img;
QImageReader *reader;
int32 w, h, frame;
private:
QVector<QPixmap> frames;
QVector<QImage> images;
QVector<int64> delays;
int32 w, h, frame, framesCount, duration;
int32 framesCount, duration;
};

View File

@@ -19,10 +19,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "boxshadow.h"
BoxShadow::BoxShadow(const style::rect &topLeft) : _size(topLeft.width() / cIntRetinaFactor()) {
BoxShadow::BoxShadow(const style::sprite &topLeft) : _size(topLeft.pxWidth()), _pixsize(_size * cIntRetinaFactor()) {
if (!_size) return;
QImage cornersImage(_size * 2, _size * 2, QImage::Format_ARGB32_Premultiplied);
QImage cornersImage(_pixsize * 2, _pixsize * 2, QImage::Format_ARGB32_Premultiplied);
cornersImage.setDevicePixelRatio(cRetinaFactor());
{
QPainter p(&cornersImage);
p.drawPixmap(QPoint(rtl() ? _size : 0, 0), App::sprite(), topLeft);
@@ -41,39 +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) {
if (!_size) return;
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

@@ -144,7 +144,7 @@ void ContextMenu::paintEvent(QPaintEvent *e) {
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) {

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

View File

@@ -81,9 +81,6 @@ inline EmojiPtr emojiFromUrl(const QString &url) {
}
inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) {
QString tmp(ch, e - ch);
QByteArray tmp2 = tmp.toUtf8();
const char *tmp3 = tmp2.constData();
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();

View File

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

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

@@ -34,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 {
@@ -70,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();
@@ -80,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()) {
@@ -103,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));
@@ -126,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));
@@ -139,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();
@@ -155,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()) {
@@ -165,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();
@@ -175,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()) {
@@ -309,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) {
@@ -330,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);
}

View File

@@ -20,6 +20,7 @@ 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) {
@@ -47,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;

View File

@@ -596,6 +596,9 @@ void ScrollArea::enterEvent(QEvent *e) {
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) {
@@ -603,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

@@ -150,6 +150,12 @@ signals:
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:

View File

@@ -1129,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);
}
}
@@ -1292,15 +1292,15 @@ 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);
}
}
}
@@ -1426,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);
@@ -2243,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;

View File

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

View File

@@ -56,6 +56,12 @@ public:
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);
}
@@ -68,6 +74,12 @@ public:
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);
}
@@ -86,12 +98,24 @@ public:
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);
}

View File

@@ -1597,7 +1597,7 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) {
_minh += st::msgPadding.top() + st::msgNameFont->height;
}
if (!_caption.isEmpty()) {
_minh += _caption.minHeight();
_minh += st::webPagePhotoSkip + _caption.minHeight();
}
_minh += st::mediaPadding.bottom();
}
@@ -1666,6 +1666,10 @@ const QString HistoryPhoto::inHistoryText() const {
return qsl("[ ") + lang(lng_in_dlg_photo) + qsl(" ]");
}
const Text &HistoryPhoto::captionForClone() const {
return _caption;
}
bool HistoryPhoto::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
if (width < 0) width = w;
return (x >= 0 && y >= 0 && x < width && y < _height);
@@ -1765,15 +1769,20 @@ void HistoryPhoto::updateFrom(const MTPMessageMedia &media) {
void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const {
const HistoryReply *reply = toHistoryReply(parent);
const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
bool out = parent->out();
if (width < 0) width = w;
int skipx = 0, skipy = 0, height = _height;
if (reply || !_caption.isEmpty()) {
skipx = st::mediaPadding.left();
style::color bg(selected ? (parent->out() ? st::msgOutSelectBG : st::msgInSelectBG) : (parent->out() ? st::msgOutBG : st::msgInBG));
p.fillRect(QRect(0, 0, width, _height), bg->b);
style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg));
style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners));
App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
int replyFrom = 0, fwdFrom = 0;
if (!parent->out() && parent->history()->peer->chat) {
if (!out && parent->history()->peer->chat) {
replyFrom = st::msgPadding.top() + st::msgNameFont->height;
fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
skipy += replyFrom;
@@ -1797,20 +1806,18 @@ void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, i
if (!_caption.isEmpty()) {
height -= st::webPagePhotoSkip + _caption.countHeight(width);
}
} else {
App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInSelectShadow : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
}
data->full->load(false, false);
bool out = parent->out();
bool full = data->full->loaded();
QPixmap pix;
if (full) {
pix = data->full->pixSingle(pixw, pixh);
pix = data->full->pixSingle(pixw, pixh, width, height);
} else {
pix = data->thumb->pixBlurredSingle(pixw, pixh);
pix = data->thumb->pixBlurredSingle(pixw, pixh, width, height);
}
if (pixw < width || pixh < height) {
p.fillRect(QRect(skipx, skipy, width, height), st::black->b);
}
p.drawPixmap(QPoint(skipx + (width - pixw) / 2, skipy + (height - pixh) / 2), pix);
p.drawPixmap(skipx, skipy, pix);
if (!full) {
uint64 dt = itemAnimations().animate(parent, getms());
int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta);
@@ -1833,10 +1840,8 @@ void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, i
}
if (selected) {
p.fillRect(skipx, skipy, width, height, textstyleCurrent()->selectOverlay->b);
App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
style::color shadow(selected ? st::msgInSelectShadow : st::msgInShadow);
p.fillRect(0, _height, width + (skipx ? (st::mediaPadding.left() + st::mediaPadding.right()) : 0), st::msgShadow, shadow->b);
// date
QString time(parent->time());
@@ -1850,10 +1855,8 @@ void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, i
int32 dateW = skipx + width - dateX - st::msgDateImgDelta;
int32 dateH = skipy + height - dateY - st::msgDateImgDelta;
p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b);
if (selected) {
p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b);
}
App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgSelectBg : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
p.setFont(st::msgDateFont->f);
p.setPen(st::msgDateImgColor->p);
p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time);
@@ -1945,6 +1948,10 @@ HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, Histo
, _dldDone(0)
, _uplDone(0)
{
if (!caption.isEmpty()) {
_caption.setText(st::msgFont, caption + textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions);
}
_size = formatDurationAndSizeText(data->duration, data->size);
if (!_openWithWidth) {
@@ -1988,7 +1995,11 @@ void HistoryVideo::initDimensions(const HistoryItem *parent) {
}
_minh += st::msgServiceNameFont->height;
}
_height = _minh;
if (_caption.isEmpty()) {
_height = _minh;
} else {
_minh += st::webPagePhotoSkip + _caption.minHeight();
}
}
void HistoryVideo::regItem(HistoryItem *item) {
@@ -2042,7 +2053,11 @@ void HistoryVideo::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, co
}
if (!out) { // draw Download / Save As button
int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2;
int32 h = _height;
if (!_caption.isEmpty()) {
h -= st::webPagePhotoSkip + _caption.countHeight(width - _buttonWidth - st::mediaSaveDelta - st::mediaPadding.left() - st::mediaPadding.right());
}
int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (h - skipy - btnh) / 2;
if (x >= btnx && y >= btny && x < btnx + btnw && y < btny + btnh) {
lnk = data->loader ? _cancell : _savel;
return;
@@ -2067,10 +2082,14 @@ void HistoryVideo::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, co
}
}
if (x >= 0 && y >= skipy && x < width && y < _height && !data->loader && data->access) {
int32 tw = width - st::mediaPadding.left() - st::mediaPadding.right();
if (x >= st::mediaPadding.left() && y >= skipy + st::mediaPadding.top() && x < st::mediaPadding.left() + tw && y < skipy + st::mediaPadding.top() + st::mediaThumbSize && !data->loader && data->access) {
lnk = _openl;
return;
}
if (!_caption.isEmpty() && x >= st::mediaPadding.left() && x < st::mediaPadding.left() + tw && y >= skipy + st::mediaPadding.top() + st::mediaThumbSize + st::webPagePhotoSkip) {
return _caption.getState(lnk, inText, x - st::mediaPadding.left(), y - skipy - st::mediaPadding.top() - st::mediaThumbSize - st::webPagePhotoSkip, tw);
}
}
HistoryMedia *HistoryVideo::clone() const {
@@ -2110,11 +2129,16 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i
pressed = hovered && ((data->loader ? _cancell : _savel) == textlnkDown());
if (hovered && !pressed && textlnkDown()) hovered = false;
int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2;
p.fillRect(QRect(btnx, btny, btnw, btnh), (selected ? st::msgInSelectBG : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor))->b);
int32 h = _height;
if (!_caption.isEmpty()) {
h -= st::webPagePhotoSkip + _caption.countHeight(width - _buttonWidth - st::mediaSaveDelta - st::mediaPadding.left() - st::mediaPadding.right());
}
style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
p.fillRect(btnx, btny + btnh, btnw, st::msgShadow, shadow->b);
int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (h - skipy - btnh) / 2;
style::color bg(selected ? st::msgInSelectBg : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor));
style::color sh(selected ? st::msgInSelectShadow : st::msgInShadow);
RoundCorners cors(selected ? MessageInSelectedCorners : (hovered ? ButtonHoverCorners : MessageInCorners));
App::roundRect(p, btnx, btny, btnw, btnh, bg, cors, &sh);
p.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p);
p.setFont(st::mediaSaveButton.font->f);
@@ -2124,11 +2148,10 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i
width -= btnw + st::mediaSaveDelta;
}
style::color bg(selected ? (out ? st::msgOutSelectBG : st::msgInSelectBG) : (out ? st::msgOutBG : st::msgInBG));
p.fillRect(QRect(0, 0, width, _height), bg->b);
style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
p.fillRect(0, _height, width, st::msgShadow, shadow->b);
style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg));
style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners));
App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
if (!parent->out() && parent->history()->peer->chat) {
p.setFont(st::msgNameFont->f);
@@ -2142,13 +2165,12 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i
}
if (_thumbw) {
int32 rf(cIntRetinaFactor());
p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pix(_thumbw), QRect(_thumbx * rf, _thumby * rf, st::mediaThumbSize * rf, st::mediaThumbSize * rf));
p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize));
} else {
p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), (out ? st::mediaDocOutImg : st::mediaDocInImg));
}
if (selected) {
p.fillRect(st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, (out ? st::msgOutSelectOverlay : st::msgInSelectOverlay)->b);
App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
@@ -2198,6 +2220,11 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i
}
p.setFont(st::msgDateFont->f);
if (!_caption.isEmpty()) {
p.setPen(st::black->p);
_caption.draw(p, st::mediaPadding.left(), skipy + st::mediaPadding.top() + st::mediaThumbSize + st::webPagePhotoSkip, width - st::mediaPadding.left() - st::mediaPadding.right());
}
style::color date(selected ? (out ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (out ? st::msgOutDateColor : st::msgInDateColor));
p.setPen(date->p);
@@ -2218,6 +2245,32 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i
}
}
int32 HistoryVideo::resize(int32 width, bool dontRecountText, const HistoryItem *parent) {
w = qMin(width, _maxw);
if (_caption.isEmpty()) return _height;
_height = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
if (!parent->out() && parent->history()->peer->chat) {
_height += st::msgPadding.top() + st::msgNameFont->height;
}
if (const HistoryReply *reply = toHistoryReply(parent)) {
_height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
} else if (const HistoryForwarded *fwd = toHistoryForwarded(parent)) {
if (parent->out() || !parent->history()->peer->chat) {
_height += st::msgPadding.top();
}
_height += st::msgServiceNameFont->height;
}
if (!_caption.isEmpty()) {
int32 textw = w - st::mediaPadding.left() - st::mediaPadding.right();
if (!parent->out()) { // substract Download / Save As button
textw -= st::mediaSaveDelta + _buttonWidth;
}
_height += st::webPagePhotoSkip + _caption.countHeight(textw);
}
return _height;
}
ImagePtr HistoryVideo::replyPreview() {
if (data->replyPreview->isNull() && !data->thumb->isNull()) {
if (data->thumb->loaded()) {
@@ -2298,8 +2351,7 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i
width = _maxw;
}
bool mp3 = (data->mime == QLatin1String("audio/mp3"));
if (!data->loader && !mp3 && data->status != FileFailed && !already && !hasdata && data->size < AudioVoiceMsgInMemory) {
if (!data->loader && data->status != FileFailed && !already && !hasdata && data->size < AudioVoiceMsgInMemory) {
data->save(QString());
}
@@ -2309,11 +2361,11 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i
if (hovered && !pressed && textlnkDown()) hovered = false;
int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2;
p.fillRect(QRect(btnx, btny, btnw, btnh), (selected ? st::msgInSelectBG : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor))->b);
style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
p.fillRect(btnx, btny + btnh, btnw, st::msgShadow, shadow->b);
style::color bg(selected ? st::msgInSelectBg : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor));
style::color sh(selected ? st::msgInSelectShadow : st::msgInShadow);
RoundCorners cors(selected ? MessageInSelectedCorners : (hovered ? ButtonHoverCorners : MessageInCorners));
App::roundRect(p, btnx, btny, btnw, btnh, bg, cors, &sh);
p.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p);
p.setFont(st::mediaSaveButton.font->f);
QString btnText(lang(data->loader ? lng_media_cancel : (already ? lng_media_open_with : lng_media_download)));
@@ -2322,11 +2374,10 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i
width -= btnw + st::mediaSaveDelta;
}
style::color bg(selected ? (out ? st::msgOutSelectBG : st::msgInSelectBG) : (out ? st::msgOutBG : st::msgInBG));
p.fillRect(QRect(0, 0, width, _height), bg->b);
style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
p.fillRect(0, _height, width, st::msgShadow, shadow->b);
style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg));
style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners));
App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
if (!parent->out() && parent->history()->peer->chat) {
p.setFont(st::msgNameFont->f);
@@ -2340,21 +2391,50 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i
}
AudioData *playing = 0;
VoiceMessageState playingState = VoiceMessageStopped;
AudioPlayerState playingState = AudioPlayerStopped;
int64 playingPosition = 0, playingDuration = 0;
if (!mp3 && audioVoice()) {
audioVoice()->currentState(&playing, &playingState, &playingPosition, &playingDuration);
int32 playingFrequency = 0;
if (audioPlayer()) {
audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
}
QRect img;
if (!mp3 && (already || hasdata)) {
bool showPause = (playing == data) && (playingState == VoiceMessagePlaying || playingState == VoiceMessageResuming || playingState == VoiceMessageStarting);
QString statusText;
if (data->status == FileFailed) {
statusText = lang(lng_attach_failed);
img = out ? st::mediaAudioOutImg : st::mediaAudioInImg;
} else if (data->status == FileUploading) {
if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
_uplDone = data->uploadOffset;
_uplTextCache = formatDownloadText(_uplDone, data->size);
}
statusText = _uplTextCache;
img = out ? st::mediaAudioOutImg : st::mediaAudioInImg;
} else if (already || hasdata) {
bool showPause = false;
if (playing == data && playingState != AudioPlayerStopped && playingState != AudioPlayerStoppedAtStart) {
statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
} else {
statusText = formatDurationText(data->duration);
}
img = out ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg);
} else {
if (data->loader) {
if (_dldTextCache.isEmpty() || _dldDone != data->loader->currentOffset()) {
_dldDone = data->loader->currentOffset();
_dldTextCache = formatDownloadText(_dldDone, data->size);
}
statusText = _dldTextCache;
} else {
statusText = _size;
}
img = out ? st::mediaAudioOutImg : st::mediaAudioInImg;
}
p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), img);
if (selected) {
p.fillRect(st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay->b);
App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
@@ -2366,37 +2446,8 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i
p.setPen(st::black->c);
p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, lang(lng_media_audio));
QString statusText;
style::color status(selected ? (out ? st::mediaOutSelectColor : st::mediaInSelectColor) : (out ? st::mediaOutColor : st::mediaInColor));
p.setPen(status->p);
if (!mp3 && (already || hasdata)) {
if (playing == data && playingState != VoiceMessageStopped) {
statusText = formatDurationText(playingPosition / AudioVoiceMsgFrequency) + qsl(" / ") + formatDurationText(playingDuration / AudioVoiceMsgFrequency);
} else {
statusText = formatDurationText(data->duration);
}
} else {
if (data->loader) {
if (_dldTextCache.isEmpty() || _dldDone != data->loader->currentOffset()) {
_dldDone = data->loader->currentOffset();
_dldTextCache = formatDownloadText(_dldDone, data->size);
}
statusText = _dldTextCache;
} else {
if (data->status == FileFailed) {
statusText = lang(lng_attach_failed);
} else if (data->status == FileUploading) {
if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
_uplDone = data->uploadOffset;
_uplTextCache = formatDownloadText(_uplDone, data->size);
}
statusText = _uplTextCache;
} else {
statusText = _size;
}
}
}
int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->height;
p.drawText(tleft, texty + st::mediaFont->ascent, statusText);
if (parent->isMediaUnread()) {
@@ -2439,6 +2490,15 @@ void HistoryAudio::unregItem(HistoryItem *item) {
App::unregAudioItem(data, item);
}
void HistoryAudio::updateFrom(const MTPMessageMedia &media) {
if (media.type() == mtpc_messageMediaAudio) {
App::feedAudio(media.c_messageMediaAudio().vaudio, data);
if (!data->data.isEmpty()) {
Local::writeAudio(mediaKey(mtpToLocationType(mtpc_inputAudioFileLocation), data->dc, data->id), data->data);
}
}
}
const QString HistoryAudio::inDialogsText() const {
return lang(lng_in_dlg_audio);
}
@@ -2557,8 +2617,8 @@ HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia()
void HistoryDocument::initDimensions(const HistoryItem *parent) {
if (parent == animated.msg) {
_maxw = animated.w;
_minh = animated.h;
_maxw = animated.w / cIntRetinaFactor();
_minh = animated.h / cIntRetinaFactor();
} else {
_maxw = st::mediaMaxWidth;
int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
@@ -2591,21 +2651,18 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
bool out = parent->out(), hovered, pressed;
if (parent == animated.msg) {
if (width >= animated.w) {
p.drawPixmap(0, 0, animated.frames[animated.frame]);
if (selected) {
p.fillRect(0, 0, animated.w, animated.h, textstyleCurrent()->selectOverlay->b);
}
} else {
bool s = p.renderHints().testFlag(QPainter::SmoothPixmapTransform);
if (!s) p.setRenderHint(QPainter::SmoothPixmapTransform);
int32 h = (width == w) ? _height : (width * animated.h / animated.w);
if (h < 1) h = 1;
p.drawPixmap(QRect(0, 0, width, h), animated.frames[animated.frame]);
if (!s) p.setRenderHint(QPainter::SmoothPixmapTransform, false);
if (selected) {
p.fillRect(0, 0, width, h, textstyleCurrent()->selectOverlay->b);
}
int32 pw = animated.w / cIntRetinaFactor(), ph = animated.h / cIntRetinaFactor();
if (width < pw) {
pw = width;
ph = (pw == w) ? _height : (pw * animated.h / animated.w);
if (ph < 1) ph = 1;
}
App::roundShadow(p, 0, 0, pw, ph, selected ? st::msgInSelectShadow : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
p.drawPixmap(0, 0, animated.current(pw * cIntRetinaFactor(), ph * cIntRetinaFactor(), true));
if (selected) {
App::roundRect(p, 0, 0, pw, ph, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
return;
}
@@ -2639,10 +2696,10 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
if (hovered && !pressed && textlnkDown()) hovered = false;
int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2;
p.fillRect(QRect(btnx, btny, btnw, btnh), (selected ? st::msgInSelectBG : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor))->b);
style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
p.fillRect(btnx, btny + btnh, btnw, st::msgShadow, shadow->b);
style::color bg(selected ? st::msgInSelectBg : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor));
style::color sh(selected ? st::msgInSelectShadow : st::msgInShadow);
RoundCorners cors(selected ? MessageInSelectedCorners : (hovered ? ButtonHoverCorners : MessageInCorners));
App::roundRect(p, btnx, btny, btnw, btnh, bg, cors, &sh);
p.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p);
p.setFont(st::mediaSaveButton.font->f);
@@ -2652,11 +2709,10 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
width -= btnw + st::mediaSaveDelta;
}
style::color bg(selected ? (out ? st::msgOutSelectBG : st::msgInSelectBG) : (out ? st::msgOutBG : st::msgInBG));
p.fillRect(QRect(0, 0, width, _height), bg->b);
style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
p.fillRect(0, _height, width, st::msgShadow, shadow->b);
style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg));
style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners));
App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
if (!parent->out() && parent->history()->peer->chat) {
p.setFont(st::msgNameFont->f);
@@ -2669,13 +2725,12 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
}
if (_thumbw) {
int32 rf(cIntRetinaFactor());
p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pix(_thumbw), QRect(_thumbx * rf, _thumby * rf, st::mediaThumbSize * rf, st::mediaThumbSize * rf));
p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize));
} else {
p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), (out ? st::mediaDocOutImg : st::mediaDocInImg));
}
if (selected) {
p.fillRect(st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay->b);
App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
@@ -2696,24 +2751,22 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
style::color status(selected ? (out ? st::mediaOutSelectColor : st::mediaInSelectColor) : (out ? st::mediaOutColor : st::mediaInColor));
p.setPen(status->p);
if (data->loader) {
if (data->status == FileFailed) {
statusText = lang(lng_attach_failed);
} else if (data->status == FileUploading) {
if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
_uplDone = data->uploadOffset;
_uplTextCache = formatDownloadText(_uplDone, data->size);
}
statusText = _uplTextCache;
} else if (data->loader) {
if (_dldTextCache.isEmpty() || _dldDone != data->loader->currentOffset()) {
_dldDone = data->loader->currentOffset();
_dldTextCache = formatDownloadText(_dldDone, data->size);
}
statusText = _dldTextCache;
} else {
if (data->status == FileFailed) {
statusText = lang(lng_attach_failed);
} else if (data->status == FileUploading) {
if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
_uplDone = data->uploadOffset;
_uplTextCache = formatDownloadText(_uplDone, data->size);
}
statusText = _uplTextCache;
} else {
statusText = _size;
}
statusText = _size;
}
p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->descent, statusText);
@@ -2759,9 +2812,9 @@ int32 HistoryDocument::resize(int32 width, bool dontRecountText, const HistoryIt
if (w > st::maxMediaSize) {
w = st::maxMediaSize;
}
_height = animated.h;
if (animated.w > w) {
_height = (w * _height / animated.w);
_height = animated.h / cIntRetinaFactor();
if (animated.w / cIntRetinaFactor() > w) {
_height = (w * _height / (animated.w / cIntRetinaFactor()));
if (_height <= 0) _height = 1;
}
} else {
@@ -2924,7 +2977,7 @@ void HistorySticker::initDimensions(const HistoryItem *parent) {
_maxw = qMax(pixw, int16(st::minPhotoSize));
_minh = qMax(pixh, int16(st::minPhotoSize));
if (const HistoryReply *reply = toHistoryReply(parent)) {
_maxw += reply->replyToWidth();
_maxw += st::msgReplyPadding.left() + reply->replyToWidth();
}
_height = _minh;
w = qMin(lastw, _maxw);
@@ -2938,7 +2991,7 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected,
int32 usew = _maxw, usex = 0;
const HistoryReply *reply = toHistoryReply(parent);
if (reply) {
usew -= reply->replyToWidth();
usew -= st::msgReplyPadding.left() + reply->replyToWidth();
if (parent->out()) {
usex = width - usew;
}
@@ -2980,10 +3033,8 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected,
int32 dateW = usex + usew - dateX - st::msgDateImgDelta;
int32 dateH = _height - dateY - st::msgDateImgDelta;
p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b);
if (selected) {
p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b);
}
App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgSelectBg : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
p.setFont(st::msgDateFont->f);
p.setPen(st::msgDateImgColor->p);
p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time);
@@ -3003,17 +3054,10 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected,
}
if (reply) {
int32 rw = width - usew, rh = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
int32 rx = parent->out() ? 0 : usew, ry = _height - rh;
int32 rw = width - usew - st::msgReplyPadding.left(), rh = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
int32 rx = parent->out() ? 0 : (usew + st::msgReplyPadding.left()), ry = _height - rh;
p.setPen(Qt::NoPen);
QRect r(rx, ry, rw, rh);
p.setBrush(App::msgServiceBG()->b);
p.drawRoundedRect(r, st::msgServiceRadius, st::msgServiceRadius);
if (selected) {
p.setBrush(textstyleCurrent()->selectOverlay->b);
p.drawRoundedRect(r, st::msgServiceRadius, st::msgServiceRadius);
}
App::roundRect(p, rx, ry, rw, rh, selected ? App::msgServiceSelectBg() : App::msgServiceBg(), selected ? ServiceSelectedCorners : ServiceCorners);
reply->drawReplyTo(p, rx + st::msgReplyPadding.left(), ry, rw - st::msgReplyPadding.left() - st::msgReplyPadding.right(), selected, true);
}
@@ -3036,6 +3080,9 @@ void HistorySticker::unregItem(HistoryItem *item) {
void HistorySticker::updateFrom(const MTPMessageMedia &media) {
if (media.type() == mtpc_messageMediaDocument) {
App::feedDocument(media.c_messageMediaDocument().vdocument, data);
if (!data->data.isEmpty()) {
Local::writeStickerImage(mediaKey(mtpToLocationType(mtpc_inputDocumentFileLocation), data->dc, data->id), data->data);
}
if (App::main()) App::main()->incrementSticker(data);
}
}
@@ -3217,11 +3264,10 @@ void HistoryContact::draw(QPainter &p, const HistoryItem *parent, bool selected,
width = _maxw;
}
style::color bg(selected ? (out ? st::msgOutSelectBG : st::msgInSelectBG) : (out ? st::msgOutBG : st::msgInBG));
p.fillRect(QRect(0, 0, width, _height), bg->b);
style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
p.fillRect(0, _height, width, st::msgShadow, shadow->b);
style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg));
style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners));
App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
if (!parent->out() && parent->history()->peer->chat) {
p.setFont(st::msgNameFont->f);
@@ -3234,7 +3280,10 @@ void HistoryContact::draw(QPainter &p, const HistoryItem *parent, bool selected,
fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
}
p.drawPixmap(st::mediaPadding.left(), skipy + st::mediaPadding.top(), (contact ? contact->photo : userDefPhoto(1))->pix(st::mediaThumbSize));
p.drawPixmap(st::mediaPadding.left(), skipy + st::mediaPadding.top(), (contact ? contact->photo : userDefPhoto(1))->pixRounded(st::mediaThumbSize));
if (selected) {
App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
int32 twidth = width - tleft - st::mediaPadding.right();
@@ -3307,9 +3356,10 @@ HistoryWebPage::HistoryWebPage(WebPageData *data) : HistoryMedia()
void HistoryWebPage::initDimensions(const HistoryItem *parent) {
if (data->pendingTill) {
_maxw = st::webPageLeft + st::linkFont->m.width(lang((data->pendingTill < 0) ? lng_attach_failed : lng_profile_loading));
_minh = st::replyHeight;
_height = _minh;
_maxw = _minh = _height = 0;
//_maxw = st::webPageLeft + st::linkFont->m.width(lang((data->pendingTill < 0) ? lng_attach_failed : lng_profile_loading));
//_minh = st::replyHeight;
//_height = _minh;
return;
}
if (!_openl && !data->url.isEmpty()) _openl = TextLinkPtr(new TextLink(data->url));
@@ -3404,29 +3454,29 @@ void HistoryWebPage::initDimensions(const HistoryItem *parent) {
void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const {
if (width < 0) width = w;
if (width < 1) return;
if (width < 1 || data->pendingTill) return;
int32 bottomSkip = 0;
if (!data->pendingTill) {
//if (!data->pendingTill) {
if (data->photo) {
bottomSkip += st::webPagePhotoSkip;
if (_asArticle || (st::webPageLeft + qMax(_pixw, int16(st::minPhotoSize)) + parent->timeWidth(true) > width)) {
bottomSkip += (st::msgDateFont->height - st::msgDateDelta.y());
}
}
}
//}
style::color bar = (selected ? (parent->out() ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (parent->out() ? st::msgOutReplyBarColor : st::msgInReplyBarColor));
style::color semibold = (selected ? (parent->out() ? st::msgOutServiceSelColor : st::msgInServiceSelColor) : (parent->out() ? st::msgOutServiceColor : st::msgInServiceColor));
style::color regular = (selected ? (parent->out() ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (parent->out() ? st::msgOutDateColor : st::msgInDateColor));
p.fillRect(0, 0, st::webPageBar, _height - bottomSkip, bar->b);
if (data->pendingTill) {
p.setFont(st::linkFont->f);
p.setPen(regular->p);
p.drawText(st::webPageLeft, (_minh - st::linkFont->height) / 2 + st::linkFont->ascent, lang(data->pendingTill < 0 ? lng_attach_failed : lng_profile_loading));
return;
}
//if (data->pendingTill) {
// p.setFont(st::linkFont->f);
// p.setPen(regular->p);
// p.drawText(st::webPageLeft, (_minh - st::linkFont->height) / 2 + st::linkFont->ascent, lang(data->pendingTill < 0 ? lng_attach_failed : lng_profile_loading));
// return;
//}
p.save();
p.translate(st::webPageLeft, 0);
@@ -3440,19 +3490,13 @@ void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected,
bool full = data->photo->medium->loaded();
QPixmap pix;
if (full) {
pix = data->photo->medium->pixSingle(_pixw, _pixh);
pix = data->photo->medium->pixSingle(_pixw, _pixh, pixwidth, pixheight);
} else {
pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh);
pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh, pixwidth, pixheight);
}
if (_pixw < pixwidth || _pixh < pixheight) {
p.fillRect(QRect(width - pixwidth, 0, pixwidth, pixheight), st::black->b);
}
if (_pixw > pixwidth) {
p.drawPixmap(QRect(width - pixwidth, (pixheight - _pixh) / 2, pixwidth, _pixh), pix, QRect(cIntRetinaFactor() * (_pixw - pixwidth) / 2, 0, cIntRetinaFactor() * pixwidth, cIntRetinaFactor() * _pixh));
} else if (_pixh > pixheight) {
p.drawPixmap(QRect(width - pixwidth + (pixwidth - _pixw) / 2, 0, _pixw, pixheight), pix, QRect(0, cIntRetinaFactor() * (_pixh - pixheight) / 2, cIntRetinaFactor() * _pixw, cIntRetinaFactor() * pixheight));
} else {
p.drawPixmap(QPoint(width - pixwidth + (pixwidth - _pixw) / 2, (pixheight - _pixh) / 2), pix);
p.drawPixmap(width - pixwidth, 0, pix);
if (selected) {
App::roundRect(p, width - pixwidth, 0, pixwidth, pixheight, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
}
int32 articleLines = 5;
@@ -3511,14 +3555,11 @@ void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected,
bool full = data->photo->full->loaded();
QPixmap pix;
if (full) {
pix = data->photo->full->pixSingle(_pixw, _pixh);
pix = data->photo->full->pixSingle(_pixw, _pixh, pixwidth, pixheight);
} else {
pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh);
pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh, pixwidth, pixheight);
}
if (_pixw < pixwidth || _pixh < pixheight) {
p.fillRect(QRect(0, 0, pixwidth, pixheight), st::black->b);
}
p.drawPixmap(QPoint((pixwidth - _pixw) / 2, (pixheight - _pixh) / 2), pix);
p.drawPixmap(0, 0, pix);
if (!full) {
uint64 dt = itemAnimations().animate(parent, getms());
int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta);
@@ -3541,7 +3582,7 @@ void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected,
}
if (selected) {
p.fillRect(0, 0, pixwidth, pixheight, textstyleCurrent()->selectOverlay->b);
App::roundRect(p, 0, 0, pixwidth, pixheight, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
if (data->type == WebPageVideo) {
@@ -3556,10 +3597,8 @@ void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected,
int32 dateW = pixwidth - dateX - st::msgDateImgDelta;
int32 dateH = pixheight - dateY - st::msgDateImgDelta;
p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b);
if (selected) {
p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b);
}
App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgSelectBg : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
p.setFont(st::msgDateFont->f);
p.setPen(st::msgDateImgColor->p);
p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, _duration);
@@ -4214,13 +4253,18 @@ void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selecte
int skipx = 0, skipy = 0, height = _height;
const HistoryReply *reply = toHistoryReply(parent);
const HistoryForwarded *fwd = toHistoryForwarded(parent);
bool out = parent->out();
if (reply || !_title.isEmpty() || !_description.isEmpty()) {
skipx = st::mediaPadding.left();
style::color bg(selected ? (parent->out() ? st::msgOutSelectBG : st::msgInSelectBG) : (parent->out() ? st::msgOutBG : st::msgInBG));
p.fillRect(QRect(0, 0, width, _height), bg->b);
style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg));
style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow));
RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners));
App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
int replyFrom = 0, fwdFrom = 0;
if (!parent->out() && parent->history()->peer->chat) {
if (!out && parent->history()->peer->chat) {
replyFrom = st::msgPadding.top() + st::msgNameFont->height;
fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
skipy += replyFrom;
@@ -4255,27 +4299,27 @@ void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selecte
skipy += st::webPagePhotoSkip;
}
height -= skipy + st::mediaPadding.bottom();
} else {
App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInSelectShadow : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
}
data->load();
bool out = parent->out();
QPixmap toDraw;
if (data && !data->thumb->isNull()) {
int32 w = data->thumb->width(), h = data->thumb->height();
QPixmap pix;
if (width * h == height * w || (w == convertScale(fullWidth()) && h == convertScale(fullHeight()))) {
p.drawPixmap(QPoint(skipx, skipy), data->thumb->pixSingle(width, height));
pix = data->thumb->pixSingle(width, height, width, height);
} else if (width * h > height * w) {
int32 nw = height * w / h;
pix = data->thumb->pixSingle(nw, height, width, height);
} else {
p.fillRect(QRect(skipx, skipy, width, height), st::black->b);
if (width * h > height * w) {
int32 nw = height * w / h;
p.drawPixmap(QPoint(skipx + (width - nw) / 2, skipy), data->thumb->pixSingle(nw, height));
} else {
int32 nh = width * h / w;
p.drawPixmap(QPoint(skipx, skipy + (height - nh) / 2), data->thumb->pixSingle(width, nh));
}
int32 nh = width * h / w;
pix = data->thumb->pixSingle(width, nh, width, height);
}
p.drawPixmap(QPoint(skipx, skipy), pix);
} else {
p.fillRect(QRect(skipx, skipy, width, height), st::black->b);
App::roundRect(p, skipx, skipy, width, height, st::black, BlackCorners);
}
if (data) {
switch (data->type) {
@@ -4298,10 +4342,8 @@ void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selecte
}
}
if (selected) {
p.fillRect(skipx, skipy, width, height, textstyleCurrent()->selectOverlay->b);
App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
style::color shadow(selected ? st::msgInSelectShadow : st::msgInShadow);
p.fillRect(0, _height, width + (skipx ? (st::mediaPadding.left() + st::mediaPadding.right()) : 0), st::msgShadow, shadow->b);
// date
QString time(parent->time());
@@ -4314,10 +4356,8 @@ void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selecte
int32 dateW = skipx + width - dateX - st::msgDateImgDelta;
int32 dateH = skipy + height - dateY - st::msgDateImgDelta;
p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b);
if (selected) {
p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b);
}
App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgSelectBg : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
p.setFont(st::msgDateFont->f);
p.setPen(st::msgDateImgColor->p);
p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time);
@@ -4616,7 +4656,7 @@ void HistoryMessage::initMediaFromDocument(DocumentData *doc) {
void HistoryMessage::initDimensions(const QString &text) {
if (!_media || !text.isEmpty()) { // !justMedia()
if (_media) {
if (_media && _media->isDisplayed()) {
_text.setText(st::msgFont, text, _historyTextOptions);
} else {
_text.setText(st::msgFont, text + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions);
@@ -4635,9 +4675,26 @@ void HistoryMessage::initDimensions(const HistoryItem *parent) {
_maxw += st::msgPadding.left() + st::msgPadding.right();
if (_media) {
_media->initDimensions(this);
int32 maxw = _media->maxWidth() + st::msgPadding.left() + st::msgPadding.right();
if (maxw > _maxw) _maxw = maxw;
_minh += st::msgPadding.bottom() + _media->minHeight();
if (_media->isDisplayed() && _text.hasSkipBlock()) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was, _historyTextOptions); // without date skip
_textWidth = 0;
_textHeight = 0;
}
} else if (!_media->isDisplayed() && !_text.hasSkipBlock()) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions); // without date skip
_textWidth = 0;
_textHeight = 0;
}
}
if (_media->isDisplayed()) {
int32 maxw = _media->maxWidth() + st::msgPadding.left() + st::msgPadding.right();
if (maxw > _maxw) _maxw = maxw;
_minh += st::msgPadding.bottom() + _media->minHeight();
}
}
}
fromNameUpdated();
@@ -4674,21 +4731,28 @@ HistoryMedia *HistoryMessage::getMedia(bool inOverview) const {
void HistoryMessage::setMedia(const MTPmessageMedia &media) {
if ((!_media || _media->isImageLink()) && media.type() == mtpc_messageMediaEmpty) return;
bool wasMedia = false;
bool mediaWasDisplayed = false;
if (_media) {
wasMedia = true;
mediaWasDisplayed = _media->isDisplayed();
delete _media;
_media = 0;
}
QString t;
initMedia(media, t);
if (_media && !wasMedia) {
if (_media && _media->isDisplayed() && !mediaWasDisplayed) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was, _historyTextOptions); // without date skip
_textWidth = 0;
_textHeight = 0;
}
} else if (mediaWasDisplayed && (!_media || !_media->isDisplayed())) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions); // without date skip
_textWidth = 0;
_textHeight = 0;
}
}
initDimensions(0);
if (App::main()) App::main()->itemResized(this);
@@ -4728,7 +4792,7 @@ void HistoryMessage::draw(QPainter &p, uint32 selection) const {
}
if (!out() && _history->peer->chat) {
p.drawPixmap(left, _height - st::msgMargin.bottom() - st::msgPhotoSize, _from->photo->pix(st::msgPhotoSize));
p.drawPixmap(left, _height - st::msgMargin.bottom() - st::msgPhotoSize, _from->photo->pixRounded(st::msgPhotoSize));
// width -= st::msgPhotoSkip;
left += st::msgPhotoSkip;
}
@@ -4746,11 +4810,10 @@ void HistoryMessage::draw(QPainter &p, uint32 selection) const {
} else {
QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
style::color bg(selected ? (out() ? st::msgOutSelectBG : st::msgInSelectBG) : (out() ? st::msgOutBG : st::msgInBG));
p.fillRect(r, bg->b);
style::color shadow(selected ? (out() ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out() ? st::msgOutShadow : st::msgInShadow));
p.fillRect(left, _height - st::msgMargin.bottom(), width, st::msgShadow, shadow->b);
style::color bg(selected ? (out() ? st::msgOutSelectBg : st::msgInSelectBg) : (out() ? st::msgOutBg : st::msgInBg));
style::color sh(selected ? (out() ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out() ? st::msgOutShadow : st::msgInShadow));
RoundCorners cors(selected ? (out() ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out() ? MessageOutCorners : MessageInCorners));
App::roundRect(p, r, bg, cors, &sh);
if (!out() && _history->peer->chat) {
p.setFont(st::msgNameFont->f);
@@ -4822,7 +4885,7 @@ int32 HistoryMessage::resize(int32 width, bool dontRecountText, const HistoryIte
if (_media) _media->resize(_maxw - st::msgPadding.left() - st::msgPadding.right(), dontRecountText, this);
} else {
_height = _textHeight;
if (_media) _height += st::msgPadding.bottom() + _media->resize(nwidth, dontRecountText, this);
if (_media && _media->isDisplayed()) _height += st::msgPadding.bottom() + _media->resize(nwidth, dontRecountText, this);
}
if (!out() && _history->peer->chat) {
_height += st::msgNameFont->height;
@@ -4906,7 +4969,7 @@ void HistoryMessage::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y)
void HistoryMessage::getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const {
QRect trect(r.marginsAdded(-st::msgPadding));
TextLinkPtr medialnk;
if (_media) {
if (_media && _media->isDisplayed()) {
if (y >= trect.bottom() - _media->height() && y < trect.bottom()) {
_media->getState(lnk, inText, x - trect.left(), y + _media->height() - trect.bottom(), this);
return;
@@ -4943,7 +5006,7 @@ void HistoryMessage::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x,
r.setTop(r.top() + st::msgNameFont->height);
}
QRect trect(r.marginsAdded(-st::msgPadding));
if (_media) {
if (_media && _media->isDisplayed()) {
trect.setBottom(trect.bottom() - _media->height() - st::msgPadding.bottom());
}
_text.getSymbol(symbol, after, upon, x - trect.x(), y - trect.y(), trect.width());
@@ -5323,14 +5386,9 @@ void HistoryReply::drawReplyTo(QPainter &p, int32 x, int32 y, int32 w, bool sele
ImagePtr replyPreview = replyToMsg->getMedia()->replyPreview();
if (!replyPreview->isNull()) {
QRect to(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
if (replyPreview->width() == replyPreview->height()) {
p.drawPixmap(to.x(), to.y(), replyPreview->pix());
} else {
QRect from = (replyPreview->width() > replyPreview->height()) ? QRect((replyPreview->width() - replyPreview->height()) / 2, 0, replyPreview->height(), replyPreview->height()) : QRect(0, (replyPreview->height() - replyPreview->width()) / 2, replyPreview->width(), replyPreview->width());
p.drawPixmap(to, replyPreview->pix(), from);
}
p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height()));
if (selected) {
p.fillRect(to, textstyleCurrent()->selectOverlay->b);
App::roundRect(p, to, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
}
}
@@ -5653,16 +5711,8 @@ void HistoryServiceMsg::draw(QPainter &p, uint32 selection) const {
left += (width - _maxw) / 2;
width = _maxw;
}
// QRect r(0, st::msgServiceMargin.top(), _history->width, height);
QRect r(left, st::msgServiceMargin.top(), width, height);
p.setBrush(App::msgServiceBG()->b);
p.setPen(Qt::NoPen);
// p.fillRect(r, App::msgServiceBG()->b);
p.drawRoundedRect(r, st::msgServiceRadius, st::msgServiceRadius);
if (selection == FullItemSel) {
p.setBrush(st::msgServiceSelectBG->b);
p.drawRoundedRect(r, st::msgServiceRadius, st::msgServiceRadius);
}
App::roundRect(p, left, st::msgServiceMargin.top(), width, height, App::msgServiceBg(), (selection == FullItemSel) ? ServiceSelectedCorners : ServiceCorners);
p.setBrush(Qt::NoBrush);
p.setPen(st::msgServiceColor->p);
p.setFont(st::msgServiceFont->f);

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;
@@ -805,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();
}
@@ -873,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;
@@ -906,6 +909,7 @@ private:
};
QString formatSizeText(qint64 size);
QString formatDurationText(qint64 duration);
class HistoryVideo : public HistoryMedia {
public:
@@ -914,6 +918,7 @@ public:
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;
}
@@ -966,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;
@@ -1097,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;
@@ -1252,7 +1266,9 @@ public:
QString notificationText() const;
void updateMedia(const MTPMessageMedia &media) {
if (_media) _media->updateFrom(media);
if (_media) {
_media->updateFrom(media);
}
}
void updateStickerEmoji();

View File

@@ -28,6 +28,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "passcodewidget.h"
#include "window.h"
#include "fileuploader.h"
#include "audio.h"
#include "localstorage.h"
@@ -523,6 +524,7 @@ void HistoryList::dragActionFinish(const QPoint &screenPos, Qt::MouseButton butt
App::pressedItem(0);
}
if (needClick) {
DEBUG_LOG(("Clicked link: %1 (%2) %3").arg(needClick->text()).arg(needClick->readable()).arg(needClick->encoded()));
needClick->onClick(button);
dragActionCancel();
return;
@@ -701,7 +703,7 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
_menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage()));
}
if (item && !isUponSelected && !_contextMenuLnk) {
if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(msg->getMedia())) {
if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(msg ? msg->getMedia() : 0)) {
DocumentData *doc = sticker->document();
if (doc && doc->sticker && doc->sticker->set.type() != mtpc_inputStickerSetEmpty) {
if (!_menu) _menu = new ContextMenu(this);
@@ -1316,35 +1318,35 @@ void MessageField::focusInEvent(QFocusEvent *e) {
}
HistoryHider::HistoryHider(MainWidget *parent, bool forwardSelected) : QWidget(parent)
, _sharedContact(0)
, _forwardSelected(forwardSelected)
, _sendPath(false)
, forwardButton(this, lang(lng_forward), st::btnSelectDone)
, cancelButton(this, lang(lng_cancel), st::btnSelectCancel)
, offered(0)
, aOpacity(0, 1)
, aOpacityFunc(anim::easeOutCirc)
, hiding(false)
, _forwardRequest(0)
, toTextWidth(0)
, shadow(st::boxShadow)
, _sharedContact(0)
, _forwardSelected(forwardSelected)
, _sendPath(false)
, forwardButton(this, lang(lng_forward), st::btnSelectDone)
, cancelButton(this, lang(lng_cancel), st::btnSelectCancel)
, offered(0)
, aOpacity(0, 1)
, aOpacityFunc(anim::easeOutCirc)
, hiding(false)
, _forwardRequest(0)
, toTextWidth(0)
, shadow(st::boxShadow)
{
init();
}
HistoryHider::HistoryHider(MainWidget *parent, UserData *sharedContact) : QWidget(parent)
, _sharedContact(sharedContact)
, _forwardSelected(false)
, _sendPath(false)
, forwardButton(this, lang(lng_forward_send), st::btnSelectDone)
, cancelButton(this, lang(lng_cancel), st::btnSelectCancel)
, offered(0)
, aOpacity(0, 1)
, aOpacityFunc(anim::easeOutCirc)
, hiding(false)
, _forwardRequest(0)
, toTextWidth(0)
, shadow(st::boxShadow)
, _sharedContact(sharedContact)
, _forwardSelected(false)
, _sendPath(false)
, forwardButton(this, lang(lng_forward_send), st::btnSelectDone)
, cancelButton(this, lang(lng_cancel), st::btnSelectCancel)
, offered(0)
, aOpacity(0, 1)
, aOpacityFunc(anim::easeOutCirc)
, hiding(false)
, _forwardRequest(0)
, toTextWidth(0)
, shadow(st::boxShadow)
{
init();
}
@@ -1410,7 +1412,7 @@ void HistoryHider::paintEvent(QPaintEvent *e) {
if (cacheForAnim.isNull() || !offered) {
p.setFont(st::forwardFont->f);
if (offered) {
shadow.paint(p, box);
shadow.paint(p, box, st::boxShadowShift);
// fill bg
p.fillRect(box, st::boxBG->b);
@@ -1424,10 +1426,8 @@ void HistoryHider::paintEvent(QPaintEvent *e) {
p.setPen(st::black->p);
toText.drawElided(p, box.left() + (box.width() - toTextWidth) / 2, box.top() + st::boxPadding.top(), toTextWidth + 1);
} else {
p.setBrush(st::forwardBG->b);
p.setPen(Qt::NoPen);
int32 w = st::forwardMargins.left() + _chooseWidth + st::forwardMargins.right(), h = st::forwardMargins.top() + st::forwardFont->height + st::forwardMargins.bottom();
p.drawRoundedRect((width() - w) / 2, (height() - h) / 2, w, h, st::forwardRadius, st::forwardRadius);
App::roundRect(p, (width() - w) / 2, (height() - h) / 2, w, h, st::forwardBg, ForwardCorners);
p.setPen(st::white->p);
p.drawText(box, lang(lng_forward_choose), QTextOption(style::al_center));
@@ -1570,7 +1570,7 @@ HistoryHider::~HistoryHider() {
parent()->noHider(this);
}
HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
, _replyToId(0)
, _replyTo(0)
, _replyToNameVersion(0)
@@ -1599,9 +1599,14 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
, _attachPhoto(this, st::btnAttachPhoto)
, _attachEmoji(this, st::btnAttachEmoji)
, _field(this, st::taMsgField, lang(lng_message_ph))
, _recordAnim(animFunc(this, &HistoryWidget::recordStep))
, _recordingAnim(animFunc(this, &HistoryWidget::recordingStep))
, _recording(false), _inRecord(false), _inField(false)
, a_recordingLevel(0, 0), _recordingSamples(0)
, a_recordOver(0, 0), a_recordDown(0, 0), a_recordCancel(st::recordCancel->c, st::recordCancel->c)
, _recordCancelWidth(st::recordFont->m.width(lang(lng_record_cancel)))
, _attachType(this)
, _emojiPan(this)
//, _stickerPan(this)
, _attachDrag(DragStateNone)
, _attachDragDocument(this)
, _attachDragPhoto(this)
@@ -1612,6 +1617,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
, confirmWithText(false)
, titlePeerTextWidth(0)
, hiderOffered(false)
, _showAnim(animFunc(this, &HistoryWidget::showStep))
, _scrollDelta(0)
, _typingRequest(0)
, _saveDraftStart(0)
@@ -1640,9 +1646,13 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
connect(&_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr)));
connect(&_emojiPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*)));
connect(&_emojiPan, SIGNAL(updateStickers()), this, SLOT(updateStickers()));
// connect(&_stickerPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*)));
connect(&_typingStopTimer, SIGNAL(timeout()), this, SLOT(cancelTyping()));
connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreviewTimeout()));
if (audioCapture()) {
connect(audioCapture(), SIGNAL(onError()), this, SLOT(onRecordError()));
connect(audioCapture(), SIGNAL(onUpdate(qint16,qint32)), this, SLOT(onRecordUpdate(qint16,qint32)));
connect(audioCapture(), SIGNAL(onDone(QByteArray,qint32)), this, SLOT(onRecordDone(QByteArray,qint32)));
}
_scrollTimer.setSingleShot(false);
@@ -1680,15 +1690,11 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
_attachDocument.installEventFilter(&_attachType);
_attachPhoto.installEventFilter(&_attachType);
_attachEmoji.installEventFilter(&_emojiPan);
// _attachEmoji.installEventFilter(&_stickerPan);
// _emojiPan.installEventFilter(&_stickerPan);
// _stickerPan.installEventFilter(&_emojiPan);
connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
_attachType.hide();
_emojiPan.hide();
// _stickerPan.hide();
_attachDragDocument.hide();
_attachDragPhoto.hide();
@@ -1704,18 +1710,29 @@ void HistoryWidget::start() {
void HistoryWidget::onTextChange() {
updateTyping();
// updateStickerPan();
if (cHasAudioCapture()) {
if (_field.getLastText().isEmpty() && !App::main()->hasForwardingItems()) {
_previewCancelled = false;
_send.hide();
setMouseTracking(true);
mouseMoveEvent(0);
} else if (!_field.isHidden() && _send.isHidden()) {
_send.show();
setMouseTracking(false);
_recordAnim.stop();
_inRecord = _inField = false;
a_recordOver = a_recordDown = anim::fvalue(0, 0);
a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c);
}
}
if (!hist || _synthedTextUpdate) return;
_saveDraftText = true;
onDraftSave(true);
if (!_field.hasText()) _previewCancelled = false;
}
void HistoryWidget::onDraftSaveDelayed() {
// updateStickerPan();
if (!hist || _synthedTextUpdate) return;
if (!_field.textCursor().anchor() && !_field.textCursor().position() && !_field.verticalScrollBar()->value()) {
if (!Local::hasDraftPositions(hist->peer->id)) return;
@@ -1742,7 +1759,7 @@ void HistoryWidget::writeDraft(MsgId *replyTo, const QString *text, const Messag
_saveDraftStart = 0;
_saveDraftTimer.stop();
if (_saveDraftText) {
if (save) Local::writeDraft(hist->peer->id, Local::MessageDraft(replyTo ? (*replyTo) : _replyToId, text ? (*text) : _field.getText(), previewCancelled ? (*previewCancelled) : _previewCancelled));
if (save) Local::writeDraft(hist->peer->id, Local::MessageDraft(replyTo ? (*replyTo) : _replyToId, text ? (*text) : _field.getLastText(), previewCancelled ? (*previewCancelled) : _previewCancelled));
_saveDraftText = false;
}
if (save) Local::writeDraftPositions(hist->peer->id, cursor ? (*cursor) : MessageCursor(_field));
@@ -1767,12 +1784,6 @@ void HistoryWidget::updateTyping(bool typing) {
}
}
//void HistoryWidget::updateStickerPan() {
// EmojiPtr e = _field.getSingleEmoji();
// if (e) updateStickers();
// _stickerPan.setStickerPack(e, !_emojiPan.isHidden() && !_emojiPan.hiding());
//}
void HistoryWidget::updateRecentStickers() {
_emojiPan.refreshStickers();
}
@@ -1801,7 +1812,7 @@ void HistoryWidget::activate() {
}
}
if (_list) {
if (_selCount) {
if (_selCount || _recording) {
_list->setFocus();
} else {
_field.setFocus();
@@ -1809,6 +1820,31 @@ void HistoryWidget::activate() {
}
}
void HistoryWidget::onRecordError() {
stopRecording(false);
}
void HistoryWidget::onRecordDone(QByteArray result, qint32 samples) {
App::wnd()->activateWindow();
int32 duration = samples / AudioVoiceMsgFrequency;
imageLoader.append(result, duration, histPeer->id, _replyToId, ToPrepareAudio);
cancelReply();
}
void HistoryWidget::onRecordUpdate(qint16 level, qint32 samples) {
if (!_recording) {
return;
}
a_recordingLevel.start(level);
_recordingAnim.start();
_recordingSamples = samples;
if (samples < 0 || samples >= AudioVoiceMsgFrequency * AudioVoiceMsgMaxLength) {
stopRecording(samples > 0 && _inField);
}
update(0, _scroll.y() + _scroll.height(), width(), height() - _scroll.y() - _scroll.height());
}
void HistoryWidget::updateStickers() {
if (cLastStickersUpdate() && getms(true) < cLastStickersUpdate() + StickersUpdateTimeout) return;
if (_stickersUpdateRequest) return;
@@ -1831,15 +1867,19 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
QByteArray wasHash = cStickersHash();
cSetStickersHash(qba(d.vhash));
StickerSetsOrder &setsOrder(cRefStickerSetsOrder());
setsOrder.clear();
StickerSets &sets(cRefStickerSets());
StickerSets::iterator def = sets.find(DefaultStickerSetId);
if (def == sets.cend()) {
def = sets.insert(DefaultStickerSetId, StickerSet(DefaultStickerSetId, 0, qsl("Great Minds"), QString()));
def = sets.insert(DefaultStickerSetId, StickerSet(DefaultStickerSetId, 0, lang(lng_stickers_default_set), QString()));
}
for (int32 i = 0; i < d_sets.size(); ++i) {
if (d_sets.at(i).type() == mtpc_stickerSet) {
const MTPDstickerSet &set(d_sets.at(i).c_stickerSet());
StickerSets::iterator i = sets.find(set.vid.v);
setsOrder.push_back(set.vid.v);
if (i == sets.cend()) {
i = sets.insert(set.vid.v, StickerSet(set.vid.v, set.vaccess_hash.v, qs(set.vtitle), qs(set.vshort_name)));
} else {
@@ -1933,6 +1973,7 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
++i;
}
}
setsOrder.removeOne(it->id);
it = sets.erase(it);
removed = true;
} else {
@@ -1954,6 +1995,7 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
}
}
if (it->stickers.isEmpty()) {
setsOrder.removeOne(it->id);
it = sets.erase(it);
} else {
++it;
@@ -2006,13 +2048,14 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
}
}
// updateStickerPan();
if (App::main()) emit App::main()->stickersUpdated();
}
bool HistoryWidget::stickersFailed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
LOG(("App Fail: Failed to get stickers!"));
cSetLastStickersUpdate(getms(true));
_stickersUpdateRequest = 0;
return true;
@@ -2114,7 +2157,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
}
}
if (hist) {
hist->draft = _field.getText();
hist->draft = _field.getLastText();
hist->draftCursor.fillFrom(_field);
hist->draftToId = _replyToId;
hist->draftPreviewCancelled = _previewCancelled;
@@ -2252,7 +2295,7 @@ void HistoryWidget::checkUnreadLoaded(bool checkOnlyShow) {
if (!hist) return;
if (hist->readyForWork()) {
if (checkOnlyShow && !_scroll.isHidden()) return;
if (!animating()) {
if (!_showAnim.animating()) {
if (_scroll.isHidden()) {
_scroll.show();
if (!_field.isHidden()) update();
@@ -2262,7 +2305,7 @@ void HistoryWidget::checkUnreadLoaded(bool checkOnlyShow) {
return;
}
updateListSize(0, true);
if (!animating()) updateControlsVisibility();
if (!_showAnim.animating()) updateControlsVisibility();
if (hist->readyForWork()) {
if (!_scroll.isHidden() && !_list->isHidden()) {
onListScroll();
@@ -2273,7 +2316,7 @@ void HistoryWidget::checkUnreadLoaded(bool checkOnlyShow) {
}
void HistoryWidget::updateControlsVisibility() {
if (!hist || animating()) {
if (!hist || _showAnim.animating()) {
_scroll.hide();
_send.hide();
_toHistoryEnd.hide();
@@ -2285,7 +2328,6 @@ void HistoryWidget::updateControlsVisibility() {
_attachEmoji.hide();
_attachType.hide();
_emojiPan.hide();
// _stickerPan.hide();
return;
}
@@ -2296,20 +2338,41 @@ void HistoryWidget::updateControlsVisibility() {
_toHistoryEnd.show();
}
if (!histPeer->chat || !histPeer->asChat()->forbidden) {
_send.show();
if (cDefaultAttach() == dbidaPhoto) {
_attachPhoto.show();
if (cHasAudioCapture() && _field.getLastText().isEmpty() && !App::main()->hasForwardingItems()) {
_send.hide();
setMouseTracking(true);
mouseMoveEvent(0);
} else {
_attachDocument.show();
_send.show();
setMouseTracking(false);
_recordAnim.stop();
_inRecord = _inField = false;
a_recordOver = anim::fvalue(0, 0);
}
_attachEmoji.show();
if (_field.isHidden()) {
if (_recording) {
_field.hide();
_attachEmoji.hide();
_attachDocument.hide();
_attachPhoto.hide();
} else {
_field.show();
_attachEmoji.show();
if (cDefaultAttach() == dbidaPhoto) {
_attachDocument.hide();
_attachPhoto.show();
} else {
_attachDocument.show();
_attachPhoto.hide();
}
}
if ((_replyToId || App::main()->hasForwardingItems() || (_previewData && _previewData->pendingTill >= 0)) && _replyForwardPreviewCancel.isHidden()) {
_replyForwardPreviewCancel.show();
resizeEvent(0);
update();
if ((_replyToId || App::main()->hasForwardingItems() || (_previewData && _previewData->pendingTill >= 0))) {
if (_replyForwardPreviewCancel.isHidden()) {
_replyForwardPreviewCancel.show();
resizeEvent(0);
update();
}
} else {
_replyForwardPreviewCancel.hide();
}
} else {
_attachMention.hide();
@@ -2319,7 +2382,6 @@ void HistoryWidget::updateControlsVisibility() {
_attachEmoji.hide();
_attachType.hide();
_emojiPan.hide();
// _stickerPan.hide();
if (!_field.isHidden()) {
_field.hide();
resizeEvent(0);
@@ -2341,7 +2403,6 @@ void HistoryWidget::updateControlsVisibility() {
_attachEmoji.hide();
_attachType.hide();
_emojiPan.hide();
// _stickerPan.hide();
_toHistoryEnd.hide();
_replyForwardPreviewCancel.hide();
if (!_field.isHidden()) {
@@ -2616,7 +2677,7 @@ void HistoryWidget::loadMessagesAround() {
void HistoryWidget::onListScroll() {
App::checkImageCacheSize();
if (histPreloading || !hist || ((_list->isHidden() || _scroll.isHidden() || !App::wnd()->windowHandle()->isVisible()) && hist->readyForWork())) {
if (histPreloading || !hist || ((_list->isHidden() || _scroll.isHidden() || _showAnim.animating() || !App::wnd()->windowHandle()->isVisible()) && hist->readyForWork())) {
checkUnreadLoaded(true);
return;
}
@@ -2672,7 +2733,7 @@ void HistoryWidget::onHistoryToEnd() {
void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
if (!hist) return;
QString text = prepareMessage(_field.getText());
QString text = prepareMessage(_field.getLastText());
if (!text.isEmpty()) {
App::main()->readServerHistory(hist, false);
hist->loadAround(0);
@@ -2688,7 +2749,6 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
if (!_attachMention.isHidden()) _attachMention.hideStart();
if (!_attachType.isHidden()) _attachType.hideStart();
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
// if (!_stickerPan.isHidden()) _stickerPan.hideStart();
} else if (App::main()->hasForwardingItems()) {
App::main()->readServerHistory(hist, false);
hist->loadAround(0);
@@ -2780,16 +2840,16 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo
a_alpha = anim::fvalue(0, 1);
a_bgCoord = back ? anim::ivalue(0, st::introSlideShift) : anim::ivalue(0, -st::introSlideShift);
a_bgAlpha = anim::fvalue(1, 0);
anim::start(this);
_showAnim.start();
App::main()->topBar()->update();
}
bool HistoryWidget::animStep(float64 ms) {
bool HistoryWidget::showStep(float64 ms) {
float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration;
float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0;
bool res = true;
if (dt2 >= 1) {
anim::stop(this);
_showAnim.stop();
res = false;
a_bgCoord.finish();
a_bgAlpha.finish();
@@ -2798,7 +2858,6 @@ bool HistoryWidget::animStep(float64 ms) {
_bgAnimCache = _animCache = _animTopBarCache = _bgAnimTopBarCache = QPixmap();
App::main()->topBar()->stopAnim();
App::main()->topBar()->enableShadow();
updateControlsVisibility();
if (hist && hist->readyForWork()) {
_scroll.show();
if (hist->lastScrollTop == History::ScrollMax) {
@@ -2806,6 +2865,10 @@ bool HistoryWidget::animStep(float64 ms) {
}
onListScroll();
}
if (hist && !_histInited) {
checkUnreadLoaded();
}
updateControlsVisibility();
App::wnd()->setInnerFocus();
} else {
a_bgCoord.update(dt1, st::introHideFunc);
@@ -2819,8 +2882,38 @@ bool HistoryWidget::animStep(float64 ms) {
}
void HistoryWidget::animStop() {
if (!animating()) return;
anim::stop(this);
if (!_showAnim.animating()) return;
_showAnim.stop();
}
bool HistoryWidget::recordStep(float64 ms) {
float64 dt = ms / st::btnSend.duration;
bool res = true;
if (dt >= 1 || !_send.isHidden()) {
res = false;
a_recordOver.finish();
a_recordDown.finish();
a_recordCancel.finish();
} else {
a_recordOver.update(dt, anim::linear);
a_recordDown.update(dt, anim::linear);
a_recordCancel.update(dt, anim::linear);
}
update(_recording ? QRect(0, _scroll.y() + _scroll.height(), width(), height() - _scroll.y() - _scroll.height()) : _send.geometry());
return res;
}
bool HistoryWidget::recordingStep(float64 ms) {
float64 dt = ms / AudioVoiceMsgUpdateView;
bool res = true;
if (dt >= 1) {
res = false;
a_recordingLevel.finish();
} else {
a_recordingLevel.update(dt, anim::linear);
}
update(_attachDocument.geometry());
return res;
}
void HistoryWidget::onPhotoSelect() {
@@ -2904,6 +2997,33 @@ void HistoryWidget::leaveEvent(QEvent *e) {
_attachDrag = DragStateNone;
updateDragAreas();
}
if (hasMouseTracking()) mouseMoveEvent(0);
}
void HistoryWidget::mouseMoveEvent(QMouseEvent *e) {
QPoint pos(e ? e->pos() : mapFromGlobal(QCursor::pos()));
bool inRecord = _send.geometry().contains(pos);
bool inField = pos.y() >= (_scroll.y() + _scroll.height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width();
bool startAnim = false;
if (inRecord != _inRecord) {
_inRecord = inRecord;
a_recordOver.start(_inRecord ? 1 : 0);
a_recordDown.restart();
a_recordCancel.restart();
startAnim = true;
}
if (inField != _inField && _recording) {
_inField = inField;
a_recordOver.restart();
a_recordDown.start(_inField ? 1 : 0);
a_recordCancel.start(_inField ? st::recordCancel->c : st::recordCancelActive->c);
startAnim = true;
}
if (startAnim) _recordAnim.start();
}
void HistoryWidget::leaveToChildEvent(QEvent *e) { // e -- from enterEvent() of child TWidget
if (hasMouseTracking()) mouseMoveEvent(0);
}
void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) {
@@ -2915,6 +3035,28 @@ void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) {
_attachDrag = DragStateNone;
updateDragAreas();
}
if (_recording && cHasAudioCapture()) {
stopRecording(_inField);
}
}
void HistoryWidget::stopRecording(bool send) {
audioCapture()->stop(send);
a_recordingLevel = anim::ivalue(0, 0);
_recordingAnim.stop();
_recording = false;
_recordingSamples = 0;
updateControlsVisibility();
activate();
update(0, _scroll.y() + _scroll.height(), width(), height() - _scroll.y() - _scroll.height());
a_recordDown.start(0);
a_recordOver.restart();
a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c);
_recordAnim.start();
}
DragState HistoryWidget::getDragState(const QMimeData *d) {
@@ -3048,7 +3190,7 @@ void HistoryWidget::selectMessage() {
}
void HistoryWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth) {
if (animating()) {
if (_showAnim.animating()) {
p.setOpacity(a_bgAlpha.current());
p.drawPixmap(a_bgCoord.current(), 0, _bgAnimTopBarCache);
p.setOpacity(a_alpha.current());
@@ -3083,7 +3225,7 @@ void HistoryWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth)
}
void HistoryWidget::topBarShadowParams(int32 &x, float64 &o) {
if (animating() && a_coord.current() >= 0) {
if (_showAnim.animating() && a_coord.current() >= 0) {
x = a_coord.current();
o = a_alpha.current();
}
@@ -3305,10 +3447,13 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), this, SLOT(onPhotoUploaded(MsgId, const MTPInputFile &)), Qt::UniqueConnection);
connect(App::uploader(), SIGNAL(documentReady(MsgId, const MTPInputFile &)), this, SLOT(onDocumentUploaded(MsgId, const MTPInputFile &)), Qt::UniqueConnection);
connect(App::uploader(), SIGNAL(thumbDocumentReady(MsgId, const MTPInputFile &, const MTPInputFile &)), this, SLOT(onThumbDocumentUploaded(MsgId, const MTPInputFile &, const MTPInputFile &)), Qt::UniqueConnection);
connect(App::uploader(), SIGNAL(audioReady(MsgId, const MTPInputFile &)), this, SLOT(onAudioUploaded(MsgId, const MTPInputFile &)), Qt::UniqueConnection);
// connect(App::uploader(), SIGNAL(photoProgress(MsgId)), this, SLOT(onPhotoProgress(MsgId)), Qt::UniqueConnection);
connect(App::uploader(), SIGNAL(documentProgress(MsgId)), this, SLOT(onDocumentProgress(MsgId)), Qt::UniqueConnection);
connect(App::uploader(), SIGNAL(audioProgress(MsgId)), this, SLOT(onAudioProgress(MsgId)), Qt::UniqueConnection);
// connect(App::uploader(), SIGNAL(photoFailed(MsgId)), this, SLOT(onPhotoFailed(MsgId)), Qt::UniqueConnection);
connect(App::uploader(), SIGNAL(documentFailed(MsgId)), this, SLOT(onDocumentFailed(MsgId)), Qt::UniqueConnection);
connect(App::uploader(), SIGNAL(audioFailed(MsgId)), this, SLOT(onAudioFailed(MsgId)), Qt::UniqueConnection);
App::uploader()->uploadMedia(newId, img);
@@ -3323,6 +3468,11 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
int32 flags = (h->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out); // unread, out
if (img.replyTo) flags |= MTPDmessage::flag_reply_to_msg_id;
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTPint(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaDocument(img.document)));
} else if (img.type == ToPrepareAudio) {
h->loadAround(0);
int32 flags = (h->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out | MTPDmessage_flag_media_unread); // unread, out
if (img.replyTo) flags |= MTPDmessage::flag_reply_to_msg_id;
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTPint(), MTPint(), MTP_int(img.replyTo), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaAudio(img.audio)));
}
if (hist && histPeer && img.peer == histPeer->id) {
@@ -3424,6 +3574,30 @@ void HistoryWidget::onThumbDocumentUploaded(MsgId newId, const MTPInputFile &fil
}
}
void HistoryWidget::onAudioUploaded(MsgId newId, const MTPInputFile &file) {
if (!MTP::authedId()) return;
HistoryMessage *item = dynamic_cast<HistoryMessage*>(App::histItemById(newId));
if (item) {
AudioData *audio = 0;
if (HistoryAudio *media = dynamic_cast<HistoryAudio*>(item->getMedia())) {
audio = media->audio();
}
if (audio) {
//App::main()->readServerHistory(item->history(), false);
uint64 randomId = MTP::nonce<uint64>();
App::historyRegRandom(randomId, newId);
History *hist = item->history();
MsgId replyTo = item->toHistoryReply() ? item->toHistoryReply()->replyToId() : 0;
int32 sendFlags = 0;
if (replyTo) {
sendFlags |= MTPmessages_SendMedia::flag_reply_to_msg_id;
}
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_int(sendFlags), item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedAudio(file, MTP_int(audio->duration), MTP_string(audio->mime)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
}
}
}
void HistoryWidget::onDocumentProgress(MsgId newId) {
if (!MTP::authedId()) return;
HistoryItem *item = App::histItemById(newId);
@@ -3432,6 +3606,14 @@ void HistoryWidget::onDocumentProgress(MsgId newId) {
}
}
void HistoryWidget::onAudioProgress(MsgId newId) {
if (!MTP::authedId()) return;
HistoryItem *item = App::histItemById(newId);
if (item) {
msgUpdated(item->history()->peer->id, item);
}
}
void HistoryWidget::onDocumentFailed(MsgId newId) {
if (!MTP::authedId()) return;
HistoryItem *item = App::histItemById(newId);
@@ -3440,6 +3622,14 @@ void HistoryWidget::onDocumentFailed(MsgId newId) {
}
}
void HistoryWidget::onAudioFailed(MsgId newId) {
if (!MTP::authedId()) return;
HistoryItem *item = App::histItemById(newId);
if (item) {
msgUpdated(item->history()->peer->id, item);
}
}
void HistoryWidget::peerMessagesUpdated(PeerId peer) {
if (histPeer && _list && peer == histPeer->id) {
updateListSize();
@@ -3473,7 +3663,6 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
_attachType.move(0, _attachDocument.y() - _attachType.height());
_emojiPan.move(width() - _emojiPan.width(), _attachEmoji.y() - _emojiPan.height());
// _stickerPan.move(width() - _emojiPan.width() - _stickerPan.width() + st::dropdownPadding.left(), _attachEmoji.y() - _stickerPan.height());
switch (_attachDrag) {
case DragStateFiles:
@@ -3525,7 +3714,7 @@ MsgId HistoryWidget::replyToId() const {
void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, HistoryItem *resizedItem, bool scrollToIt) {
if (!hist || (!_histInited && !initial)) return;
if (!isVisible()) {
if (!isVisible() || _showAnim.animating()) {
if (initial) _histInited = false;
if (resizedItem) _list->recountHeight(true);
return; // scrollTopMax etc are not working after recountHeight()
@@ -3623,6 +3812,18 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
_replyForwardPressed = QRect(0, _field.y() - st::replyHeight, st::replySkip, st::replyHeight).contains(e->pos());
if (_replyForwardPressed && !_replyForwardPreviewCancel.isHidden()) {
update(0, _field.y() - st::sendPadding - st::replyHeight, width(), st::replyHeight);
} else if (_inRecord && cHasAudioCapture()) {
audioCapture()->start();
_recording = _inField = true;
updateControlsVisibility();
activate();
update(0, _scroll.y() + _scroll.height(), width(), height() - _scroll.y() - _scroll.height());
a_recordDown.start(1);
a_recordOver.restart();
_recordAnim.start();
}
}
@@ -3715,9 +3916,7 @@ void HistoryWidget::onStickerSend(DocumentData *sticker) {
if (!_attachMention.isHidden()) _attachMention.hideStart();
if (!_attachType.isHidden()) _attachType.hideStart();
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
// if (!_stickerPan.isHidden()) _stickerPan.hideStart();
// _field.removeSingleEmoji();
_field.setFocus();
}
@@ -3772,7 +3971,7 @@ void HistoryWidget::cancelReply() {
}
void HistoryWidget::cancelForwarding() {
if (!_previewData || _previewData->pendingTill < 0) _replyForwardPreviewCancel.hide();
updateControlsVisibility();
resizeEvent(0);
update();
}
@@ -3924,7 +4123,7 @@ void HistoryWidget::onPeerLoaded(PeerData *data) {
void HistoryWidget::peerUpdated(PeerData *data) {
if (data && data == histPeer) {
updateListSize();
if (!animating()) updateControlsVisibility();
if (!_showAnim.animating()) updateControlsVisibility();
if (data->chat && data->asChat()->count > 0 && data->asChat()->participants.isEmpty()) {
App::api()->requestFullPeer(data);
}
@@ -4028,7 +4227,7 @@ void HistoryWidget::updateTopBarSelection() {
updateControlsVisibility();
updateListSize();
if (!App::wnd()->layerShown() && !App::passcoded()) {
if (_selCount) {
if (_selCount || _recording) {
_list->setFocus();
} else {
_field.setFocus();
@@ -4054,10 +4253,11 @@ void HistoryWidget::updateReplyTo(bool force) {
void HistoryWidget::updateForwarding(bool force) {
if (App::main()->hasForwardingItems()) {
_replyForwardPreviewCancel.show();
updateControlsVisibility();
} else {
resizeEvent(0);
update();
}
resizeEvent(0);
update();
}
void HistoryWidget::updateReplyToName() {
@@ -4096,12 +4296,7 @@ void HistoryWidget::drawFieldBackground(QPainter &p) {
ImagePtr replyPreview = _replyTo->getMedia()->replyPreview();
if (!replyPreview->isNull()) {
QRect to(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
if (replyPreview->width() == replyPreview->height()) {
p.drawPixmap(to.x(), to.y(), replyPreview->pix());
} else {
QRect from = (replyPreview->width() > replyPreview->height()) ? QRect((replyPreview->width() - replyPreview->height()) / 2, 0, replyPreview->height(), replyPreview->height()) : QRect(0, (replyPreview->height() - replyPreview->width()) / 2, replyPreview->width(), replyPreview->width());
p.drawPixmap(to, replyPreview->pix(), from);
}
p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height()));
}
replyLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x();
}
@@ -4159,12 +4354,12 @@ void HistoryWidget::drawFieldBackground(QPainter &p) {
}
void HistoryWidget::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
QRect r(e->rect());
if (r != rect()) {
p.setClipRect(r);
}
if (animating()) {
if (_showAnim.animating()) {
p.setOpacity(a_bgAlpha.current());
p.drawPixmap(a_bgCoord.current(), 0, _bgAnimCache);
p.setOpacity(a_alpha.current());
@@ -4204,8 +4399,40 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
if (_list) {
if (!_scroll.isHidden()) {
if (!_field.isHidden()) {
if (!_field.isHidden() || _recording) {
drawFieldBackground(p);
if (_send.isHidden()) {
if (a_recordDown.current() < 1) {
p.setOpacity(st::btnAttachEmoji.opacity * (1 - a_recordOver.current()) + st::btnAttachEmoji.overOpacity * a_recordOver.current());
p.drawSprite(_send.x() + (_send.width() - st::btnRecordAudio.pxWidth()) / 2, _send.y() + (_send.height() - st::btnRecordAudio.pxHeight()) / 2, st::btnRecordAudio);
}
if (a_recordDown.current() > 0) {
p.setOpacity(a_recordDown.current());
p.drawSprite(_send.x() + (_send.width() - st::btnRecordAudioActive.pxWidth()) / 2, _send.y() + (_send.height() - st::btnRecordAudioActive.pxHeight()) / 2, st::btnRecordAudioActive);
}
p.setOpacity(1);
if (_recording) {
p.setPen(Qt::NoPen);
p.setBrush(st::recordSignalColor->b);
p.setRenderHint(QPainter::HighQualityAntialiasing);
float64 delta = qMin(float64(a_recordingLevel.current()) * 3 * M_PI / 0x7fff, 1.);
int32 d = 2 * qRound(st::recordSignalMin + (delta * (st::recordSignalMax - st::recordSignalMin)));
p.drawEllipse(_attachPhoto.x() + (_attachEmoji.width() - d) / 2, _attachPhoto.y() + (_attachPhoto.height() - d) / 2, d, d);
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
QString duration = formatDurationText(_recordingSamples / AudioVoiceMsgFrequency);
p.setFont(st::recordFont->f);
p.setPen(st::black->p);
p.drawText(_attachPhoto.x() + _attachEmoji.width(), _attachPhoto.y() + st::recordTextTop + st::recordFont->ascent, duration);
int32 left = _attachPhoto.x() + _attachEmoji.width() + st::recordFont->m.width(duration) + ((_send.width() - st::btnRecordAudio.pxWidth()) / 2);
int32 right = width() - _send.width();
p.setPen(a_recordCancel.current());
p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachPhoto.y() + st::recordTextTop + st::recordFont->ascent, lang(lng_record_cancel));
}
}
}
} else {
QPoint dogPos((width() - st::msgDogImg.pxWidth()) / 2, ((height() - _field.height() - 2 * st::sendPadding - st::msgDogImg.pxHeight()) * 4) / 9);
@@ -4230,9 +4457,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
style::font font(st::msgServiceFont);
int32 w = font->m.width(lang(lng_willbe_history)) + st::msgPadding.left() + st::msgPadding.right(), h = font->height + st::msgServicePadding.top() + st::msgServicePadding.bottom() + 2;
QRect tr((width() - w) / 2, (height() - _field.height() - 2 * st::sendPadding - h) / 2, w, h);
p.setPen(Qt::NoPen);
p.setBrush(App::msgServiceBG()->b);
p.drawRoundedRect(tr, st::msgServiceRadius, st::msgServiceRadius);
App::roundRect(p, tr, App::msgServiceBg(), ServiceCorners);
p.setPen(st::msgServiceColor->p);
p.setFont(font->f);

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,7 +298,6 @@ public:
QRect historyRect() const;
void updateTyping(bool typing = true);
// void updateStickerPan();
void updateRecentStickers();
void stickersInstalled(uint64 setId);
void typingDone(const MTPBool &result, mtpRequestId req);
@@ -317,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);
@@ -330,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);
@@ -367,6 +367,10 @@ public:
void updatePreview();
void previewCancel();
bool recordStep(float64 ms);
bool recordingStep(float64 ms);
void stopRecording(bool send);
~HistoryWidget();
signals:
@@ -394,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();
@@ -443,6 +450,10 @@ public slots:
void updateStickers();
void onRecordError();
void onRecordDone(QByteArray result, qint32 samples);
void onRecordUpdate(qint16 level, qint32 samples);
private:
MsgId _replyToId;
@@ -509,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;
@@ -532,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

@@ -428,7 +428,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_emoji_category5" = "Aktivität";
"lng_emoji_category6" = "Reisen & Orte";
"lng_emoji_category7" = "Objekte & Symbole";
"lng_emoji_category8" = "Sticker";
"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";
@@ -440,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}";
@@ -481,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";
@@ -573,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" = "— Neue Emoji hinzugefügt\n— Emoji- und Sticker-Panel wurden verbessert";
"lng_new_version_text" = "— Sticker Panel wurde verbessert\n— Fehlerbehebungen und sonstige Kleinigkeiten";
"lng_menu_insert_unicode" = "Unicode-Steuerzeichen einfügen";

View File

@@ -396,7 +396,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"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 de invitación";
"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.";
@@ -428,7 +428,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_emoji_category5" = "Actividad";
"lng_emoji_category6" = "Viajes y lugares";
"lng_emoji_category7" = "Objetos y símbolos";
"lng_emoji_category8" = "Stickers";
"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";
@@ -440,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}";
@@ -481,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";
@@ -573,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" = "— Añadido el soporte para nuevos emojis\n— Pestañas de stickers y emojis mejoradas ";
"lng_new_version_text" = "— Pestaña de stickers mejorada\n— Corrección de errores y elementos menores";
"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})";
@@ -428,7 +428,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_emoji_category5" = "Attività";
"lng_emoji_category6" = "Viaggi e luoghi";
"lng_emoji_category7" = "Oggetti e simboli";
"lng_emoji_category8" = "Sticker";
"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";
@@ -440,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}";
@@ -481,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";
@@ -573,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" = "— Aggiunto il supporto per nuove emoji\n— Pannello emoji e sticker migliorato";
"lng_new_version_text" = "— Pannello sticker migliorato\n— Risoluzione bug e miglioramenti minori";
"lng_menu_insert_unicode" = "Inserisci carattere di controllo Unicode";

View File

@@ -428,7 +428,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_emoji_category5" = "활동";
"lng_emoji_category6" = "여행 및 장소";
"lng_emoji_category7" = "오브제 & 상징";
"lng_emoji_category8" = "스티커";
"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" = "비디오";
@@ -440,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}";
@@ -481,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" = "파일 전달";
@@ -573,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— 이모티콘 및 스티커 패널 향상";
"lng_new_version_text" = "— 스티커 패널 향상\n— 버그 픽스 및 세부 수정등";
"lng_menu_insert_unicode" = "유니코드 문자를 입력하세요.";

View File

@@ -396,7 +396,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"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" = "Uitnodigingslink intrekken";
"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.";
@@ -425,10 +425,20 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_emoji_category2" = "Natuur";
"lng_emoji_category3" = "Eten & drinken";
"lng_emoji_category4" = "Feestelijkheid";
"lng_emoji_category5" = "Actviteit";
"lng_emoji_category5" = "Activiteit";
"lng_emoji_category6" = "Reizen & plekken";
"lng_emoji_category7" = "Objecten & symbolen";
"lng_emoji_category8" = "Stickers";
"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";
@@ -440,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}";
@@ -481,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";
@@ -573,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" = "— Ondersteuning voor nieuwe emoji toegevoegd\n— Verbeterd emoji- en stickerspaneel";
"lng_new_version_text" = "— Verbeterd stickerpaneel\n— Probleemoplossing en kleine wijzigingen";
"lng_menu_insert_unicode" = "Unicode-besturingsteken invoegen";

View File

@@ -428,7 +428,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_emoji_category5" = "Atividade";
"lng_emoji_category6" = "Viagens e Lugares";
"lng_emoji_category7" = "Objetos e Símbolos";
"lng_emoji_category8" = "Stickers";
"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";
@@ -440,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}";
@@ -481,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";
@@ -573,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" = "— Adicionado suporte para novos emojis\n— Painel de emojis e stickers melhorado";
"lng_new_version_text" = "— Painel de sticker melhorado\n— Resolução de bugs e pequenas melhorias";
"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) {
@@ -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

@@ -2207,6 +2207,31 @@ namespace Local {
return _storageAudiosSize;
}
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;
@@ -2241,29 +2266,11 @@ namespace Local {
}
}
EncryptedDescriptor data(size);
data.stream << quint32(sets.size()) << cStickersHash();
for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) {
if (i->stickers.isEmpty()) continue;
data.stream << quint64(i->id) << quint64(i->access) << i->title << i->shortName << quint32(i->stickers.size());
for (StickerPack::const_iterator j = i->stickers.cbegin(), e = i->stickers.cend(); j != e; ++j) {
DocumentData *doc = *j;
data.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: {
data.stream << qint32(StickerSetTypeID);
} break;
case mtpc_inputStickerSetShortName: {
data.stream << qint32(StickerSetTypeShortName);
} break;
case mtpc_inputStickerSetEmpty:
default: {
data.stream << qint32(StickerSetTypeEmpty);
} break;
}
const StorageImageLocation &loc(doc->sticker->loc);
data.stream << qint32(loc.width) << qint32(loc.height) << qint32(loc.dc) << quint64(loc.volume) << qint32(loc.local) << quint64(loc.secret);
}
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(_stickersKey);
file.writeEncrypted(data);
@@ -2280,15 +2287,17 @@ namespace Local {
_writeMap();
return;
}
StickerSets &sets(cRefStickerSets());
sets.clear();
cSetStickerSetsOrder(StickerSetsOrder());
RecentStickerPack &recent(cRefRecentStickers());
recent.clear();
cSetStickersHash(QByteArray());
StickerSet &def(sets.insert(DefaultStickerSetId, StickerSet(DefaultStickerSetId, 0, qsl("Great Minds"), QString())).value());
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;
@@ -2352,25 +2361,36 @@ namespace Local {
StickerSets &sets(cRefStickerSets());
sets.clear();
StickerSetsOrder &order(cRefStickerSetsOrder());
order.clear();
quint32 cnt;
QByteArray hash;
stickers.stream >> cnt >> hash;
for (int32 i = 0; i < cnt; ++i) {
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 = qsl("Great Minds");
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 (int32 j = 0; j < scnt; ++j) {
for (uint32 j = 0; j < scnt; ++j) {
quint64 id, access;
QString name, mime, alt;
qint32 date, dc, size, width, height, type, typeOfSet;

View File

@@ -66,6 +66,7 @@ void debugLogWrite(const char *file, int32 line, const QString &v) {
{
QMutexLocker lock(&debugLogMutex);
if (!cDebug() || !debugLogStream) return;
logsInitDebug(); // maybe need to reopen new file
@@ -87,6 +88,7 @@ void tcpLogWrite(const QString &v) {
{
QMutexLocker lock(&debugLogMutex);
if (!cDebug() || !tcpLogStream) return;
logsInitDebug(); // maybe need to reopen new file
@@ -100,6 +102,7 @@ void mtpLogWrite(int32 dc, const QString &v) {
{
QMutexLocker lock(&debugLogMutex);
if (!cDebug() || !mtpLogStream) return;
logsInitDebug(); // maybe need to reopen new file
@@ -109,7 +112,7 @@ void mtpLogWrite(int32 dc, const QString &v) {
}
void logWrite(const QString &v) {
if (!mainLog.isOpen()) return;
if (!mainLogStream) return;
time_t t = time(NULL);
struct tm tm;
@@ -117,6 +120,8 @@ void logWrite(const QString &v) {
{
QMutexLocker lock(&mainLogMutex);
if (!mainLogStream) return;
QString msg(QString("[%1.%2.%3 %4:%5:%6] %7\n").arg(tm.tm_year + 1900).arg(tm.tm_mon + 1, 2, 10, zero).arg(tm.tm_mday, 2, 10, zero).arg(tm.tm_hour, 2, 10, zero).arg(tm.tm_min, 2, 10, zero).arg(tm.tm_sec, 2, 10, zero).arg(v));
(*mainLogStream) << msg;
mainLogStream->flush();
@@ -227,6 +232,8 @@ void logsInit() {
logsInitDebug();
cSetDebug(true);
}
QDir().setCurrent(cWorkingDir());
}
void logsInitDebug() {

View File

@@ -373,9 +373,9 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
connect(&history, SIGNAL(peerShown(PeerData*)), this, SLOT(onPeerShown(PeerData*)));
connect(&updateNotifySettingTimer, SIGNAL(timeout()), this, SLOT(onUpdateNotifySettings()));
connect(this, SIGNAL(showPeerAsync(quint64,qint32,bool,bool)), this, SLOT(showPeer(quint64,qint32,bool,bool)), Qt::QueuedConnection);
if (audioVoice()) {
connect(audioVoice(), SIGNAL(updated(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
connect(audioVoice(), SIGNAL(stopped(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
if (audioPlayer()) {
connect(audioPlayer(), SIGNAL(updated(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
connect(audioPlayer(), SIGNAL(stopped(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
}
connect(&_updateMutedTimer, SIGNAL(timeout()), this, SLOT(onUpdateMuted()));
@@ -1013,6 +1013,7 @@ void MainWidget::stopAnimActive() {
}
void MainWidget::searchMessages(const QString &query) {
App::wnd()->hideMediaview();
dialogs.searchMessages(query);
if (!cWideMode()) onShowDialogs();
}
@@ -1356,18 +1357,18 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
if (audio->loader) {
if (audio->loader->done()) {
audio->finish();
bool mp3 = (audio->mime == QLatin1String("audio/mp3"));
QString already = audio->already();
bool play = !mp3 && audio->openOnSave > 0 && audioVoice();
bool play = audio->openOnSave > 0 && audioPlayer();
if ((!already.isEmpty() && audio->openOnSave) || (!audio->data.isEmpty() && play)) {
if (play) {
AudioData *playing = 0;
VoiceMessageState state = VoiceMessageStopped;
audioVoice()->currentState(&playing, &state);
if (playing == audio && state != VoiceMessageStopped) {
audioVoice()->pauseresume();
AudioPlayerState state = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &state);
if (playing == audio && state != AudioPlayerStopped) {
audioPlayer()->pauseresume();
} else {
audioVoice()->play(audio);
audioPlayer()->play(audio);
if (App::main()) App::main()->audioMarkRead(audio);
}
} else {
QPoint pos(QCursor::pos());
@@ -1376,6 +1377,7 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
} else {
psOpenFile(already, audio->openOnSave < 0);
}
if (App::main()) App::main()->audioMarkRead(audio);
}
}
}
@@ -1390,6 +1392,32 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
}
void MainWidget::audioPlayProgress(AudioData *audio) {
AudioData *playing = 0;
AudioPlayerState state = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &state);
if (playing == audio && state == AudioPlayerStoppedAtStart) {
audioPlayer()->clearStoppedAtStart(audio);
QString already = audio->already(true);
if (already.isEmpty() && !audio->data.isEmpty()) {
bool mp3 = (audio->mime == QLatin1String("audio/mp3"));
QString filename = saveFileName(lang(lng_save_audio), mp3 ? qsl("MP3 Audio (*.mp3);;All files (*.*)") : qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), mp3 ? qsl(".mp3") : qsl(".ogg"), false);
if (!filename.isEmpty()) {
QFile f(filename);
if (f.open(QIODevice::WriteOnly)) {
if (f.write(audio->data) == audio->data.size()) {
f.close();
already = filename;
audio->location = FileLocation(mtpToStorageType(mtpc_storage_filePartial), filename);
Local::writeFileLocation(mediaKey(mtpToLocationType(mtpc_inputAudioFileLocation), audio->dc, audio->id), FileLocation(mtpToStorageType(mtpc_storage_filePartial), filename));
}
}
}
}
if (!already.isEmpty()) {
psOpenFile(already);
}
}
const AudioItems &items(App::audioItems());
AudioItems::const_iterator i = items.constFind(audio);
if (i != items.cend()) {
@@ -1427,8 +1455,10 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) {
HistoryItem *item = App::histItemById(document->openOnSaveMsgId);
if (reader.supportsAnimation() && reader.imageCount() > 1 && item) {
startGif(item, already);
} else {
} else if (item) {
App::wnd()->showDocument(document, item);
} else {
psOpenFile(already);
}
} else {
psOpenFile(already);
@@ -2507,6 +2537,8 @@ void MainWidget::openLocalUrl(const QString &url) {
}
void MainWidget::openUserByName(const QString &username, bool toProfile) {
App::wnd()->hideMediaview();
UserData *user = App::userByName(username);
if (user) {
if (toProfile) {
@@ -2520,10 +2552,12 @@ void MainWidget::openUserByName(const QString &username, bool toProfile) {
}
void MainWidget::joinGroupByHash(const QString &hash) {
App::wnd()->hideMediaview();
MTP::send(MTPmessages_CheckChatInvite(MTP_string(hash)), rpcDone(&MainWidget::inviteCheckDone, hash), rpcFail(&MainWidget::inviteCheckFail));
}
void MainWidget::stickersBox(const MTPInputStickerSet &set) {
App::wnd()->hideMediaview();
StickerSetBox *box = new StickerSetBox(set);
connect(box, SIGNAL(installed(uint64)), this, SLOT(onStickersInstalled(uint64)));
App::wnd()->showLayer(box);

View File

@@ -41,6 +41,14 @@ namespace {
MediaView *_view;
};
TextParseOptions _captionTextOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
}
MediaView::MediaView() : TWidget(App::wnd()),
@@ -175,7 +183,6 @@ void MediaView::documentUpdated(DocumentData *doc) {
}
void MediaView::onGifUpdated() {
_currentGif.frames[_currentGif.frame].setDevicePixelRatio(cRetinaFactor());
update(_x, _y, _w, _h);
}
@@ -282,6 +289,14 @@ void MediaView::updateControls() {
} else {
_leftNavVisible = _rightNavVisible = false;
}
if (!_caption.isEmpty()) {
int32 skipw = qMax(_dateNav.left() + _dateNav.width(), _headerNav.left() + _headerNav.width());
int32 maxw = qMin(qMax(width() - 2 * skipw - st::mvCaptionPadding.left() - st::mvCaptionPadding.right() - 2 * st::mvCaptionMargin.width(), int(st::msgMinWidth)), _caption.maxWidth());
int32 maxh = qMin(_caption.countHeight(maxw), int(height() / 4 - st::mvCaptionPadding.top() - st::mvCaptionPadding.bottom() - 2 * st::mvCaptionMargin.height()));
_captionRect = QRect((width() - maxw) / 2, height() - maxh - st::mvCaptionPadding.bottom() - st::mvCaptionMargin.height(), maxw, maxh);
} else {
_captionRect = QRect();
}
updateOver(mapFromGlobal(QCursor::pos()));
update();
}
@@ -336,7 +351,7 @@ bool MediaView::animStep(float64 msp) {
} else {
a_cOpacity.update(dt, anim::linear);
}
QRegion toUpdate = QRegion() + (_over == OverLeftNav ? _leftNav : _leftNavIcon) + (_over == OverRightNav ? _rightNav : _rightNavIcon) + (_over == OverClose ? _closeNav : _closeNavIcon) + _saveNavIcon + _moreNavIcon + _headerNav + _nameNav + _dateNav;
QRegion toUpdate = QRegion() + (_over == OverLeftNav ? _leftNav : _leftNavIcon) + (_over == OverRightNav ? _rightNav : _rightNavIcon) + (_over == OverClose ? _closeNav : _closeNavIcon) + _saveNavIcon + _moreNavIcon + _headerNav + _nameNav + _dateNav + _captionRect.marginsAdded(st::mvCaptionPadding);
update(toUpdate);
if (dt < 1) result = true;
}
@@ -641,7 +656,7 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) {
findCurrent();
}
displayPhoto(photo);
displayPhoto(photo, context);
preloadData(0);
activateControls();
}
@@ -679,7 +694,7 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) {
loadBack();
}
}
displayPhoto(photo);
displayPhoto(photo, 0);
preloadData(0);
activateControls();
}
@@ -722,11 +737,18 @@ void MediaView::showDocument(DocumentData *doc, HistoryItem *context) {
activateControls();
}
void MediaView::displayPhoto(PhotoData *photo) {
void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) {
_photo = photo;
_doc = 0;
_zoom = 0;
_caption = Text();
if (HistoryMessage *itemMsg = item ? item->toHistoryMessage() : 0) {
if (HistoryPhoto *photoMsg = dynamic_cast<HistoryPhoto*>(itemMsg->getMedia())) {
_caption.setText(st::mvCaptionFont, photoMsg->captionForClone().original(0, 0xFFFF), _captionTextOptions);
}
}
_zoomToScreen = 0;
MTP::clearLoaderPriorities();
_full = -1;
@@ -774,6 +796,7 @@ void MediaView::displayPhoto(PhotoData *photo) {
void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) {
_doc = doc;
_caption = Text();
QString already = _doc->already(true);
if (_doc->sticker && !_doc->sticker->img->isNull() && _doc->sticker->img->loaded()) {
_currentGif.stop();
@@ -801,6 +824,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) {
if (_current.isNull() && _currentGif.isNull()) {
if (_doc->thumb->isNull()) {
style::sprite thumbs[] = { st::mvDocBlue, st::mvDocGreen, st::mvDocRed, st::mvDocYellow };
style::color colors[] = { st::mvDocBlueColor, st::mvDocGreenColor, st::mvDocRedColor, st::mvDocYellowColor };
QString name = _doc->name.toLower(), mime = _doc->mime.toLower();
if (name.endsWith(QLatin1String(".doc")) ||
name.endsWith(QLatin1String(".txt")) ||
@@ -808,17 +832,20 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) {
mime.startsWith(QLatin1String("text/"))
) {
_docIcon = thumbs[0];
_docIconColor = colors[0];
} else if (
name.endsWith(QLatin1String(".xls")) ||
name.endsWith(QLatin1String(".csv"))
) {
_docIcon = thumbs[1];
_docIconColor = colors[1];
} else if (
name.endsWith(QLatin1String(".pdf")) ||
name.endsWith(QLatin1String(".ppt")) ||
name.endsWith(QLatin1String(".key"))
) {
_docIcon = thumbs[2];
_docIconColor = colors[2];
} else if (
name.endsWith(QLatin1String(".zip")) ||
name.endsWith(QLatin1String(".rar")) ||
@@ -828,10 +855,12 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) {
name.endsWith(QLatin1String(".avi"))
) {
_docIcon = thumbs[3];
_docIconColor = colors[3];
} else {
int ext = name.lastIndexOf('.');
QChar ch = (ext >= 0 && ext + 1 < name.size()) ? name.at(ext + 1) : (name.isEmpty() ? (mime.isEmpty() ? '0' : mime.at(0)) : name.at(0));
_docIcon = thumbs[ch.unicode() % 4];
_docIconColor = colors[ch.unicode() % 4];
}
} else {
_doc->thumb->load();
@@ -881,9 +910,8 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) {
_w = _current.width() / cIntRetinaFactor();
_h = _current.height() / cIntRetinaFactor();
} else {
_currentGif.frames[_currentGif.frame].setDevicePixelRatio(cRetinaFactor());
_w = _currentGif.frames[_currentGif.frame].width() / cIntRetinaFactor();
_h = _currentGif.frames[_currentGif.frame].height() / cIntRetinaFactor();
_w = _currentGif.w / cIntRetinaFactor();
_h = _currentGif.h / cIntRetinaFactor();
}
if (isHidden()) {
moveToScreen();
@@ -966,19 +994,19 @@ void MediaView::paintEvent(QPaintEvent *e) {
_full = 1;
} else if (_full < 0 && _photo->medium->loaded()) {
int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999);
_current = _photo->medium->pixBlurredNoCache(w, h);
_current = _photo->medium->pixNoCache(w, h, true, true);
if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor());
_full = 0;
} else if (_current.isNull() && _photo->thumb->loaded()) {
int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999);
_current = _photo->thumb->pixBlurredNoCache(w, h);
_current = _photo->thumb->pixNoCache(w, h, true, true);
if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor());
}
}
p.setOpacity(1);
if (_photo || !_current.isNull() || !_currentGif.isNull()) {
QRect imgRect(_x, _y, _w, _h);
const QPixmap *toDraw = _currentGif.isNull() ? &_current : &_currentGif.frames[_currentGif.frame];
const QPixmap *toDraw = _currentGif.isNull() ? &_current : &_currentGif.current(_currentGif.w, _currentGif.h, false);
if (imgRect.intersects(r)) {
if (toDraw->hasAlpha() && (!_doc || !_doc->sticker || _doc->sticker->img->isNull())) {
p.fillRect(imgRect, _transparentBrush);
@@ -1028,9 +1056,7 @@ void MediaView::paintEvent(QPaintEvent *e) {
_saveMsgOpacity.update(qMin(progress, 1.), anim::linear);
if (_saveMsgOpacity.current() > 0) {
p.setOpacity(_saveMsgOpacity.current());
p.setBrush(st::medviewSaveMsg->b);
p.setPen(Qt::NoPen);
p.drawRoundedRect(_saveMsg, st::medviewSaveMsgRadius, st::medviewSaveMsgRadius);
App::roundRect(p, _saveMsg, st::medviewSaveMsg, MediaviewSaveCorners);
p.drawPixmap(_saveMsg.topLeft() + st::medviewSaveMsgCheckPos, App::sprite(), st::medviewSaveMsgCheck);
p.setPen(st::white->p);
@@ -1054,11 +1080,13 @@ void MediaView::paintEvent(QPaintEvent *e) {
if (_docIconRect.intersects(r)) {
icon = true;
if (_doc->thumb->isNull()) {
p.drawPixmap(_docIconRect.topLeft(), App::sprite(), _docIcon);
if (!_doc->already().isEmpty() && (!_docRadialStart || _docRadialOpacity < 1)) {
p.drawPixmap(_docIconRect.topLeft(), App::sprite(), _docIcon);
p.setPen(st::mvDocExtColor->p);
p.setFont(st::mvDocExtFont->f);
p.drawText(_docIconRect.x() + (_docIconRect.width() - _docExtWidth) / 2, _docIconRect.y() + st::mvDocExtTop + st::mvDocExtFont->ascent, _docExt);
} else {
p.fillRect(_docIconRect, _docIconColor->b);
}
} else {
int32 rf(cIntRetinaFactor());
@@ -1216,6 +1244,23 @@ void MediaView::paintEvent(QPaintEvent *e) {
p.drawLine(_dateNav.left(), _dateNav.top() + st::mvFont->ascent + 1, _dateNav.right(), _dateNav.top() + st::mvFont->ascent + 1);
}
}
// caption
if (!_caption.isEmpty()) {
QRect outer(_captionRect.marginsAdded(st::mvCaptionPadding));
if (outer.intersects(r)) {
p.setOpacity(co);
p.setBrush(st::mvCaptionBg->b);
p.setPen(Qt::NoPen);
p.drawRoundedRect(outer, st::mvCaptionRadius, st::mvCaptionRadius);
if (_captionRect.intersects(r)) {
textstyleSet(&st::medviewSaveAsTextStyle);
p.setPen(st::white->p);
_caption.drawElided(p, _captionRect.x(), _captionRect.y(), _captionRect.width(), _captionRect.height() / st::mvCaptionFont->height);
textstyleRestore();
}
}
}
}
// static uint64 t = getms();
@@ -1276,7 +1321,7 @@ void MediaView::keyPressEvent(QKeyEvent *e) {
newZoom = 0;
}
_x = -_width / 2;
_y = -(((_currentGif.isNull() ? _current.height() : _currentGif.frames[_currentGif.frame].height()) / cIntRetinaFactor()) / 2);
_y = -(((_currentGif.isNull() ? _current.height() : _currentGif.h) / cIntRetinaFactor()) / 2);
float64 z = (_zoom == ZoomToScreenLevel) ? _zoomToScreen : _zoom;
if (z >= 0) {
_x = qRound(_x * (z + 1));
@@ -1296,8 +1341,8 @@ void MediaView::keyPressEvent(QKeyEvent *e) {
}
if (_zoom != newZoom) {
float64 nx, ny, z = (_zoom == ZoomToScreenLevel) ? _zoomToScreen : _zoom;
_w = (_currentGif.isNull() ? _current.width() : _currentGif.frames[_currentGif.frame].width()) / cIntRetinaFactor();
_h = (_currentGif.isNull() ? _current.height() : _currentGif.frames[_currentGif.frame].height()) / cIntRetinaFactor();
_w = (_currentGif.isNull() ? _current.width() : _currentGif.w) / cIntRetinaFactor();
_h = (_currentGif.isNull() ? _current.height() : _currentGif.h) / cIntRetinaFactor();
if (z >= 0) {
nx = (_x - width() / 2.) / (z + 1);
ny = (_y - height() / 2.) / (z + 1);
@@ -1334,7 +1379,7 @@ void MediaView::moveToNext(int32 delta) {
if (HistoryItem *item = App::histItemById(_history->_overview[_overview][_index])) {
_msgid = item->id;
switch (item->getMedia()->type()) {
case MediaTypePhoto: displayPhoto(static_cast<HistoryPhoto*>(item->getMedia())->photo()); preloadData(delta); break;
case MediaTypePhoto: displayPhoto(static_cast<HistoryPhoto*>(item->getMedia())->photo(), item); preloadData(delta); break;
case MediaTypeDocument: displayDocument(static_cast<HistoryDocument*>(item->getMedia())->document(), item); preloadData(delta); break;
case MediaTypeSticker: displayDocument(static_cast<HistorySticker*>(item->getMedia())->document(), item); preloadData(delta); break;
}
@@ -1346,7 +1391,7 @@ void MediaView::moveToNext(int32 delta) {
} else if (_user) {
if (newIndex >= 0 && newIndex < _user->photos.size()) {
_index = newIndex;
displayPhoto(_user->photos[_index]);
displayPhoto(_user->photos[_index], 0);
preloadData(delta);
}
if (delta > 0 && _index > _user->photos.size() - MediaOverviewStartPerPage) {
@@ -1405,7 +1450,10 @@ void MediaView::preloadData(int32 delta) {
void MediaView::mousePressEvent(QMouseEvent *e) {
updateOver(e->pos());
if (_menu || !_receiveMouse) return;
textlnkDown(textlnkOver());
if (textlnkDown() != textlnkOver()) {
textlnkDown(textlnkOver());
}
if (e->button() == Qt::LeftButton) {
_down = OverNone;
@@ -1540,9 +1588,12 @@ bool MediaView::updateOverState(OverState newState) {
void MediaView::updateOver(QPoint pos) {
TextLinkPtr lnk;
bool inText;
if (_saveMsgStarted) {
if (_saveMsgStarted && _saveMsg.contains(pos)) {
_saveMsgText.getState(lnk, inText, pos.x() - _saveMsg.x() - st::medviewSaveMsgPadding.left(), pos.y() - _saveMsg.y() - st::medviewSaveMsgPadding.top(), _saveMsg.width() - st::medviewSaveMsgPadding.left() - st::medviewSaveMsgPadding.right());
}
} else if (_captionRect.contains(pos)) {
_caption.getState(lnk, inText, pos.x() - _captionRect.x(), pos.y() - _captionRect.y(), _captionRect.width());
}
// retina
if (pos.x() == width()) {
@@ -1555,7 +1606,7 @@ void MediaView::updateOver(QPoint pos) {
if (lnk != textlnkOver()) {
textlnkOver(lnk);
setCursor((textlnkOver() || textlnkDown()) ? style::cur_pointer : style::cur_default);
updateImage();
update(QRegion(_saveMsg) + _captionRect);
}
if (_pressed || _dragging) return;

View File

@@ -99,7 +99,7 @@ public slots:
private:
void displayPhoto(PhotoData *photo);
void displayPhoto(PhotoData *photo, HistoryItem *item);
void displayDocument(DocumentData *doc, HistoryItem *item);
void findCurrent();
void loadBack();
@@ -124,6 +124,9 @@ private:
QString _dateText;
QString _headerText;
Text _caption;
QRect _captionRect;
uint64 _animStarted;
int32 _width, _x, _y, _w, _h, _xStart, _yStart;
@@ -137,6 +140,7 @@ private:
int32 _full; // -1 - thumb, 0 - medium, 1 - full
style::sprite _docIcon;
style::color _docIconColor;
QString _docName, _docSize, _docExt;
int32 _docNameWidth, _docSizeWidth, _docExtWidth;
QRect _docRect, _docIconRect;

View File

@@ -645,7 +645,7 @@ namespace MTP {
dcMask %= _mtp_internal::dcShift;
for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) {
if (((*i)->getDcWithShift() % _mtp_internal::dcShift) == dcMask) {
if (((*i)->getDcWithShift() % int(_mtp_internal::dcShift)) == dcMask) {
(*i)->restart();
}
}

View File

@@ -1093,6 +1093,7 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne
, retryTimeout(1)
, oldConnection(true)
, receiveDelay(MTPMinReceiveDelay)
, connectDelay(MTPMinConnectDelay)
, firstSentAt(-1)
, _pingId(0)
, _pingIdToSend(0)
@@ -1106,6 +1107,7 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne
, authKeyStrings(0) {
oldConnectionTimer.moveToThread(thread);
cantConnectTimer.moveToThread(thread);
connCheckTimer.moveToThread(thread);
_pingSender.moveToThread(thread);
retryTimer.moveToThread(thread);
@@ -1130,6 +1132,7 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne
connect(&retryTimer, SIGNAL(timeout()), this, SLOT(retryByTimer()));
connect(&connCheckTimer, SIGNAL(timeout()), this, SLOT(onBadConnection()));
connect(&cantConnectTimer, SIGNAL(timeout()), this, SLOT(onCantConnect()));
connect(&oldConnectionTimer, SIGNAL(timeout()), this, SLOT(onOldConnection()));
connect(&_pingSender, SIGNAL(timeout()), this, SLOT(onPingSender()));
connect(sessionData->owner(), SIGNAL(authKeyCreated()), this, SLOT(updateAuthKey()), Qt::QueuedConnection);
@@ -1732,6 +1735,8 @@ void MTProtoConnectionPrivate::restartNow() {
void MTProtoConnectionPrivate::socketStart(bool afterConfig) {
if (!conn) createConn();
retryTimer.stop();
cantConnectTimer.stop();
if (conn->isConnected()) {
onConnected();
@@ -1768,6 +1773,7 @@ void MTProtoConnectionPrivate::socketStart(bool afterConfig) {
connect(conn, SIGNAL(connected()), this, SLOT(onConnected()));
connect(conn, SIGNAL(disconnected()), this, SLOT(restart()));
cantConnectTimer.start(connectDelay);
conn->connectToServer(ip.c_str(), port);
}
@@ -1778,6 +1784,7 @@ void MTProtoConnectionPrivate::restart(bool maybeBadKey) {
DEBUG_LOG(("MTP Info: restarting MTProtoConnection, maybe bad key = %1").arg(logBool(maybeBadKey)));
connCheckTimer.stop();
cantConnectTimer.stop();
mtpAuthKeyPtr key(sessionData->getKey());
if (key) {
@@ -1884,6 +1891,17 @@ void MTProtoConnectionPrivate::onBadConnection() {
QTimer::singleShot(0, this, SLOT(socketStart()));
}
void MTProtoConnectionPrivate::onCantConnect() {
DEBUG_LOG(("MTP Info: can't connect in %1ms").arg(connectDelay));
if (connectDelay < MTPMaxConnectDelay) connectDelay *= 2;
doDisconnect();
restarted = true;
DEBUG_LOG(("MTP Info: immediate restart!"));
QTimer::singleShot(0, this, SLOT(socketStart()));
}
void MTProtoConnectionPrivate::doDisconnect() {
if (conn) {
disconnect(conn, SIGNAL(disconnected()), 0, 0);
@@ -2819,6 +2837,9 @@ void MTProtoConnectionPrivate::resendMany(QVector<quint64> msgIds, quint64 msCan
}
void MTProtoConnectionPrivate::onConnected() {
connectDelay = MTPMinConnectDelay;
cantConnectTimer.stop();
QReadLocker lockFinished(&sessionDataMutex);
if (!sessionData) return;
@@ -3308,6 +3329,8 @@ void MTProtoConnectionPrivate::clearAuthKeyData() {
}
void MTProtoConnectionPrivate::onError(bool mayBeBadKey) {
cantConnectTimer.stop();
MTP_LOG(dc, ("Restarting after error, maybe bad key: %1..").arg(logBool(mayBeBadKey)));
return restart(mayBeBadKey);
}

View File

@@ -344,6 +344,7 @@ public slots:
void onPingSender();
void onPingSendForce();
void onBadConnection();
void onCantConnect();
void onOldConnection();
void onSentSome(uint64 size);
void onReceivedSome();
@@ -405,8 +406,8 @@ private:
SingleTimer oldConnectionTimer;
bool oldConnection;
SingleTimer connCheckTimer;
uint32 receiveDelay;
SingleTimer connCheckTimer, cantConnectTimer;
uint32 receiveDelay, connectDelay;
int64 firstSentAt;
QVector<MTPlong> ackRequestData, resendRequestData;

View File

@@ -562,6 +562,7 @@ QPixmap OverviewInner::genPix(PhotoData *photo, int32 size) {
} else {
img = img.copy(0, (img.height() - img.width()) / 2, img.width(), img.width()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
}
// imageRound(img);
img.setDevicePixelRatio(cRetinaFactor());
photo->forget();
return QPixmap::fromImage(img, Qt::ColorOnly);
@@ -688,7 +689,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) {
bool out = item->out();
int32 mw = media->maxWidth(), left = (out ? st::msgMargin.right() : st::msgMargin.left()) + (out && mw < w ? (w - mw) : 0);
if (!out && _hist->peer->chat) {
p.drawPixmap(left, media->countHeight(item, w) - st::msgPhotoSize, item->from()->photo->pix(st::msgPhotoSize));
p.drawPixmap(left, media->countHeight(item, w) - st::msgPhotoSize, item->from()->photo->pixRounded(st::msgPhotoSize));
left += st::msgPhotoSkip;
}
@@ -720,9 +721,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) {
width = strwidth;
QRect r(left, st::msgServiceMargin.top(), width, height);
p.setBrush(App::msgServiceBG()->b);
p.setPen(Qt::NoPen);
p.drawRoundedRect(r, st::msgServiceRadius, st::msgServiceRadius);
App::roundRect(p, r, App::msgServiceBg(), ServiceCorners);
p.setBrush(Qt::NoBrush);
p.setPen(st::msgServiceColor->p);

View File

@@ -43,6 +43,29 @@ extern "C" {
#include <unity/unity/unity.h>
namespace {
QString escapeShell(const QString &str) {
QString result;
const QChar *b = str.constData(), *e = str.constEnd();
for (const QChar *ch = b; ch != e; ++ch) {
if (*ch == ' ' || *ch == '"' || *ch == '\'' || *ch == '\\') {
if (result.isEmpty()) {
result.reserve(str.size() * 2);
}
if (ch > b) {
result.append(b, ch - b);
}
result.append('\\');
b = ch;
}
}
if (result.isEmpty()) return str;
if (e > b) {
result.append(b, e - b);
}
return result;
}
bool frameless = true;
bool finished = true;
bool noQtTrayIcon = false;
@@ -923,183 +946,6 @@ PsApplication::~PsApplication() {
_psEventFilter = 0;
}
PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update) : reply(0), already(0), full(0) {
updateUrl = qs(update.vurl);
moveToThread(thread);
manager.moveToThread(thread);
App::setProxySettings(manager);
connect(thread, SIGNAL(started()), this, SLOT(start()));
initOutput();
}
PsUpdateDownloader::PsUpdateDownloader(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 PsUpdateDownloader::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 PsUpdateDownloader::start() {
sendRequest();
}
void PsUpdateDownloader::sendRequest() {
QNetworkRequest req(updateUrl);
QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";// + QByteArray::number(already + cUpdateChunk() - 1);
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 PsUpdateDownloader::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 PsUpdateDownloader::ready() {
QMutexLocker lock(&mutex);
return already;
}
int32 PsUpdateDownloader::size() {
QMutexLocker lock(&mutex);
return full;
}
void PsUpdateDownloader::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 PsUpdateDownloader::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();
}
bool _removeDirectory(const QString &path) { // from http://stackoverflow.com/questions/2256945/removing-a-non-empty-directory-programmatically-in-c-or-c
QByteArray pathRaw = QFile::encodeName(path);
DIR *d = opendir(pathRaw.constData());
@@ -1131,236 +977,8 @@ bool _removeDirectory(const QString &path) { // from http://stackoverflow.com/qu
return !rmdir(pathRaw.constData());
}
void PsUpdateDownloader::deleteDir(const QString &dir) {
_removeDirectory(dir);
}
void PsUpdateDownloader::fatalFail() {
clearAll();
emit App::app()->updateFailed();
}
void PsUpdateDownloader::clearAll() {
deleteDir(cWorkingDir() + qsl("tupdates"));
}
#ifdef Q_OS_WIN
typedef DWORD VerInt;
typedef WCHAR VerChar;
#else
typedef int VerInt;
typedef wchar_t VerChar;
#endif
void PsUpdateDownloader::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"), readyDirPath = cWorkingDir() + qsl("tupdates/ready");
deleteDir(tempDirPath);
deleteDir(readyDirPath);
QDir tempDir(tempDirPath), readyDir(readyDirPath);
if (tempDir.exists() || readyDir.exists()) {
LOG(("Update Error: cant clear tupdates/temp or tupdates/ready 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();
}
if (!tempDir.rename(tempDir.absolutePath(), readyDir.absolutePath())) {
LOG(("Update Error: cant rename temp dir '%1' to ready dir '%2'").arg(tempDir.absolutePath()).arg(readyDir.absolutePath()));
return fatalFail();
}
deleteDir(tempDirPath);
outputFile.remove();
emit App::app()->updateReady();
}
PsUpdateDownloader::~PsUpdateDownloader() {
delete reply;
reply = 0;
void psDeleteDir(const QString &dir) {
_removeDirectory(dir);
}
namespace {
@@ -1474,129 +1092,6 @@ int psFixPrevious() {
return 0;
}
#ifdef Q_OS_LINUX
bool moveFile(const char *from, const char *to) {
FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb");
if (!ffrom) {
if (fto) fclose(fto);
return false;
}
if (!fto) {
fclose(ffrom);
return false;
}
static const int BufSize = 65536;
char buf[BufSize];
while (size_t size = fread(buf, 1, BufSize, ffrom)) {
fwrite(buf, 1, size, fto);
}
struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c
//let's say this wont fail since you already worked OK on that fp
if (fstat(fileno(ffrom), &fst) != 0) {
fclose(ffrom);
fclose(fto);
return false;
}
//update to the same uid/gid
if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
fclose(ffrom);
fclose(fto);
return false;
}
//update the permissions
if (fchmod(fileno(fto), fst.st_mode) != 0) {
fclose(ffrom);
fclose(fto);
return false;
}
fclose(ffrom);
fclose(fto);
if (unlink(from)) {
return false;
}
return true;
}
#endif
bool psCheckReadyUpdate() {
QString readyPath = cWorkingDir() + qsl("tupdates/ready");
if (!QDir(readyPath).exists()) {
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));
PsUpdateDownloader::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));
PsUpdateDownloader::clearAll();
return false;
}
fVersion.close();
if (versionNum <= AppVersion) {
LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
PsUpdateDownloader::clearAll();
return false;
}
}
#ifdef Q_OS_WIN
QString curUpdater = (cExeDir() + "Updater.exe");
QFileInfo updater(cWorkingDir() + "tupdates/ready/Updater.exe");
#elif defined Q_OS_MAC
QString curUpdater = (cExeDir() + "Telegram.app/Contents/Frameworks/Updater");
QFileInfo updater(cWorkingDir() + "tupdates/ready/Telegram.app/Contents/Frameworks/Updater");
#elif defined Q_OS_LINUX
QString curUpdater = (cExeDir() + "Updater");
QFileInfo updater(cWorkingDir() + "tupdates/ready/Updater");
#endif
if (!updater.exists()) {
QFileInfo current(curUpdater);
if (!current.exists()) {
PsUpdateDownloader::clearAll();
return false;
}
if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
PsUpdateDownloader::clearAll();
return false;
}
}
#ifdef Q_OS_WIN
if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) {
PsUpdateDownloader::clearAll();
return false;
}
if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
PsUpdateDownloader::clearAll();
return false;
}
#elif defined Q_OS_MAC
QFileInfo to(curUpdater);
QDir().mkpath(to.absolutePath());
if (!objc_moveFile(updater.absoluteFilePath(), curUpdater)) {
PsUpdateDownloader::clearAll();
return false;
}
#elif defined Q_OS_LINUX
if (!moveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) {
PsUpdateDownloader::clearAll();
return false;
}
#endif
return true;
}
void psPostprocessFile(const QString &name) {
}
@@ -1606,7 +1101,7 @@ void psOpenFile(const QString &name, bool openWith) {
void psShowInFolder(const QString &name) {
App::wnd()->layerHidden();
system(("nautilus \"" + QFileInfo(name).absoluteDir().absolutePath() + "\"").toLocal8Bit().constData());
system((qsl("nautilus ") + escapeShell(QFileInfo(name).absoluteDir().absolutePath())).toUtf8().constData());
}
void psStart() {
@@ -1617,12 +1112,12 @@ void psFinish() {
namespace {
bool _psRunCommand(const QString &command) {
int result = system(command.toLocal8Bit().constData());
int result = system(command.toUtf8().constData());
if (result) {
DEBUG_LOG(("App Error: command failed, code: %1, command: %2").arg(result).arg(command.toLocal8Bit().constData()));
DEBUG_LOG(("App Error: command failed, code: %1, command (in utf8): %2").arg(result).arg(command));
return false;
}
DEBUG_LOG(("App Info: command succeeded, command: %1").arg(command.toLocal8Bit().constData()));
DEBUG_LOG(("App Info: command succeeded, command (in utf8): %1").arg(command));
return true;
}
}
@@ -1655,19 +1150,19 @@ void psRegisterCustomScheme() {
s << "Version=1.0\n";
s << "Name=Telegram Desktop\n";
s << "Comment=Official desktop version of Telegram messaging app\n";
s << "Exec=" << cExeDir().toLocal8Bit().constData() << cExeName().toLocal8Bit().constData() << " -- %u\n";
s << "Icon=" << icon.toLocal8Bit().constData() << "\n";
s << "Exec=" << escapeShell(cExeDir() + cExeName()) << " -- %u\n";
s << "Icon=" << icon << "\n";
s << "Terminal=false\n";
s << "Type=Application\n";
s << "Categories=Network;\n";
s << "MimeType=application/x-xdg-protocol-tg;x-scheme-handler/tg;\n";
f.close();
if (_psRunCommand(qsl("desktop-file-install --dir=%1.local/share/applications --delete-original \"%2\"").arg(home).arg(file))) {
if (_psRunCommand(qsl("desktop-file-install --dir=%1 --delete-original %2").arg(escapeShell(home + qsl(".local/share/applications"))).arg(escapeShell(file)))) {
DEBUG_LOG(("App Info: removing old .desktop file"));
QFile(qsl("%1.local/share/applications/telegram.desktop").arg(home)).remove();
_psRunCommand(qsl("update-desktop-database %1.local/share/applications").arg(home));
_psRunCommand(qsl("update-desktop-database %1").arg(escapeShell(home + qsl(".local/share/applications"))));
_psRunCommand(qsl("xdg-mime default telegramdesktop.desktop x-scheme-handler/tg"));
}
} else {
@@ -1676,7 +1171,7 @@ void psRegisterCustomScheme() {
}
DEBUG_LOG(("App Info: registerting for Gnome"));
if (_psRunCommand(qsl("gconftool-2 -t string -s /desktop/gnome/url-handlers/tg/command \"%1 -- %s\"").arg(cExeDir() + cExeName()))) {
if (_psRunCommand(qsl("gconftool-2 -t string -s /desktop/gnome/url-handlers/tg/command %1").arg(escapeShell(qsl("%1 -- %s").arg(escapeShell(cExeDir() + cExeName())))))) {
_psRunCommand(qsl("gconftool-2 -t bool -s /desktop/gnome/url-handlers/tg/needs_terminal false"));
_psRunCommand(qsl("gconftool-2 -t bool -s /desktop/gnome/url-handlers/tg/enabled true"));
}
@@ -1697,7 +1192,7 @@ void psRegisterCustomScheme() {
QTextStream s(&f);
s.setCodec("UTF-8");
s << "[Protocol]\n";
s << "exec=" << cExeDir().toLocal8Bit().constData() << cExeName().toLocal8Bit().constData() << " -- %u\n";
s << "exec=" << escapeShell(cExeDir() + cExeName()) << " -- %u\n";
s << "protocol=tg\n";
s << "input=none\n";
s << "output=none\n";
@@ -1741,7 +1236,7 @@ bool _execUpdater(bool update = true) {
args[argIndex++] = p_datafile;
}
}
QByteArray pathf = cWorkingDir().toLocal8Bit();
QByteArray pathf = cWorkingDir().toUtf8();
if (pathf.size() < MaxLen) {
memcpy(p_pathbuf, pathf.constData(), pathf.size());
args[argIndex++] = p_path;
@@ -1758,8 +1253,7 @@ bool _execUpdater(bool update = true) {
void psExecUpdater() {
if (!_execUpdater()) {
QString readyPath = cWorkingDir() + qsl("tupdates/ready");
PsUpdateDownloader::deleteDir(readyPath);
psDeleteDir(cWorkingDir() + qsl("tupdates/temp"));
}
}
@@ -1779,3 +1273,49 @@ void psSendToMenu(bool send, bool silent) {
void psUpdateOverlayed(QWidget *widget) {
}
bool linuxMoveFile(const char *from, const char *to) {
FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb");
if (!ffrom) {
if (fto) fclose(fto);
return false;
}
if (!fto) {
fclose(ffrom);
return false;
}
static const int BufSize = 65536;
char buf[BufSize];
while (size_t size = fread(buf, 1, BufSize, ffrom)) {
fwrite(buf, 1, size, fto);
}
struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c
//let's say this wont fail since you already worked OK on that fp
if (fstat(fileno(ffrom), &fst) != 0) {
fclose(ffrom);
fclose(fto);
return false;
}
//update to the same uid/gid
if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
fclose(ffrom);
fclose(fto);
return false;
}
//update the permissions
if (fchmod(fileno(fto), fst.st_mode) != 0) {
fclose(ffrom);
fclose(fto);
return false;
}
fclose(ffrom);
fclose(fto);
if (unlink(from)) {
return false;
}
return true;
}

View File

@@ -116,55 +116,9 @@ public:
void psInstallEventFilter();
~PsApplication();
signals:
void updateChecking();
void updateLatest();
void updateDownloading(qint64 ready, qint64 total);
void updateReady();
void updateFailed();
};
class PsUpdateDownloader : public QObject {
Q_OBJECT
public:
PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update);
PsUpdateDownloader(QThread *thread, const QString &url);
void unpackUpdate();
int32 ready();
int32 size();
static void deleteDir(const QString &dir);
static void clearAll();
~PsUpdateDownloader();
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;
};
void psDeleteDir(const QString &dir);
void psUserActionDone();
bool psIdleSupported();
@@ -194,7 +148,6 @@ void psBringToBack(QWidget *w);
int psCleanup();
int psFixPrevious();
bool psCheckReadyUpdate();
void psExecUpdater();
void psExecTelegram();
@@ -212,3 +165,5 @@ void psUpdateOverlayed(QWidget *widget);
inline QString psConvertFileUrl(const QString &url) {
return url;
}
bool linuxMoveFile(const char *from, const char *to);

View File

@@ -422,7 +422,7 @@ void PsMainWindow::psMacUpdateMenu() {
canPaste = !App::app()->clipboard()->text().isEmpty();
} else if (FlatTextarea *edit = qobject_cast<FlatTextarea*>(focused)) {
canCut = canCopy = canDelete = edit->textCursor().hasSelection();
canSelectAll = edit->hasText();
canSelectAll = !edit->getLastText().isEmpty();
canUndo = edit->isUndoAvailable();
canRedo = edit->isRedoAvailable();
canPaste = !App::app()->clipboard()->text().isEmpty();
@@ -522,415 +522,10 @@ PsApplication::~PsApplication() {
_psEventFilter = 0;
}
PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update) : reply(0), already(0), full(0) {
updateUrl = qs(update.vurl);
moveToThread(thread);
manager.moveToThread(thread);
App::setProxySettings(manager);
connect(thread, SIGNAL(started()), this, SLOT(start()));
initOutput();
}
PsUpdateDownloader::PsUpdateDownloader(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 PsUpdateDownloader::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 PsUpdateDownloader::start() {
sendRequest();
}
void PsUpdateDownloader::sendRequest() {
QNetworkRequest req(updateUrl);
QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";// + QByteArray::number(already + cUpdateChunk() - 1);
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 PsUpdateDownloader::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 PsUpdateDownloader::ready() {
QMutexLocker lock(&mutex);
return already;
}
int32 PsUpdateDownloader::size() {
QMutexLocker lock(&mutex);
return full;
}
void PsUpdateDownloader::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 PsUpdateDownloader::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 PsUpdateDownloader::deleteDir(const QString &dir) {
void psDeleteDir(const QString &dir) {
objc_deleteDir(dir);
}
void PsUpdateDownloader::fatalFail() {
clearAll();
emit App::app()->updateFailed();
}
void PsUpdateDownloader::clearAll() {
deleteDir(cWorkingDir() + qsl("tupdates"));
}
#ifdef Q_OS_WIN
typedef DWORD VerInt;
typedef WCHAR VerChar;
#else
typedef int VerInt;
typedef wchar_t VerChar;
#endif
void PsUpdateDownloader::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"), readyDirPath = cWorkingDir() + qsl("tupdates/ready");
deleteDir(tempDirPath);
deleteDir(readyDirPath);
QDir tempDir(tempDirPath), readyDir(readyDirPath);
if (tempDir.exists() || readyDir.exists()) {
LOG(("Update Error: cant clear tupdates/temp or tupdates/ready 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 (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();
}
if (!tempDir.rename(tempDir.absolutePath(), readyDir.absolutePath())) {
LOG(("Update Error: cant rename temp dir '%1' to ready dir '%2'").arg(tempDir.absolutePath()).arg(readyDir.absolutePath()));
return fatalFail();
}
deleteDir(tempDirPath);
outputFile.remove();
emit App::app()->updateReady();
}
PsUpdateDownloader::~PsUpdateDownloader() {
delete reply;
reply = 0;
}
namespace {
uint64 _lastUserAction = 0;
}
@@ -1029,73 +624,6 @@ int psFixPrevious() {
return 0;
}
bool psCheckReadyUpdate() {
QString readyPath = cWorkingDir() + qsl("tupdates/ready");
if (!QDir(readyPath).exists()) {
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));
PsUpdateDownloader::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));
PsUpdateDownloader::clearAll();
return false;
}
fVersion.close();
if (versionNum <= AppVersion) {
LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
PsUpdateDownloader::clearAll();
return false;
}
}
#ifdef Q_OS_WIN
QString curUpdater = (cExeDir() + qsl("Updater.exe"));
QFileInfo updater(cWorkingDir() + qsl("tupdates/ready/Updater.exe"));
#elif defined Q_OS_MAC
QString curUpdater = (cExeDir() + cExeName() + qsl("/Contents/Frameworks/Updater"));
QFileInfo updater(cWorkingDir() + qsl("tupdates/ready/Telegram.app/Contents/Frameworks/Updater"));
#endif
if (!updater.exists()) {
QFileInfo current(curUpdater);
if (!current.exists()) {
PsUpdateDownloader::clearAll();
return false;
}
if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
PsUpdateDownloader::clearAll();
return false;
}
}
#ifdef Q_OS_WIN
if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) {
PsUpdateDownloader::clearAll();
return false;
}
if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
PsUpdateDownloader::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)) {
PsUpdateDownloader::clearAll();
return false;
}
#endif
return true;
}
bool psShowOpenWithMenu(int x, int y, const QString &file) {
return objc_showOpenWithMenu(x, y, file);
}
@@ -1125,8 +653,7 @@ void psRegisterCustomScheme() {
void psExecUpdater() {
if (!objc_execUpdater()) {
QString readyPath = cWorkingDir() + qsl("tupdates/ready");
PsUpdateDownloader::deleteDir(readyPath);
psDeleteDir(cWorkingDir() + qsl("tupdates/temp"));
}
}

View File

@@ -144,55 +144,9 @@ public:
void psInstallEventFilter();
~PsApplication();
signals:
void updateChecking();
void updateLatest();
void updateDownloading(qint64 ready, qint64 total);
void updateReady();
void updateFailed();
};
class PsUpdateDownloader : public QObject {
Q_OBJECT
public:
PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update);
PsUpdateDownloader(QThread *thread, const QString &url);
void unpackUpdate();
int32 ready();
int32 size();
static void deleteDir(const QString &dir);
static void clearAll();
~PsUpdateDownloader();
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;
};
void psDeleteDir(const QString &dir);
void psUserActionDone();
bool psIdleSupported();
@@ -222,7 +176,6 @@ void psBringToBack(QWidget *w);
int psCleanup();
int psFixPrevious();
bool psCheckReadyUpdate();
void psExecUpdater();
void psExecTelegram();

View File

@@ -1061,10 +1061,6 @@ void PsMainWindow::psInitSize() {
setMinimumHeight(st::wndMinHeight);
TWindowPos pos(cWindowPos());
if (cDebug()) { // temp while design
pos.w = st::wndDefWidth;
pos.h = st::wndDefHeight;
}
QRect avail(App::app() ? App::app()->desktop()->availableGeometry() : QDesktopWidget().availableGeometry());
bool maximized = false;
QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight);
@@ -1389,184 +1385,7 @@ PsApplication::~PsApplication() {
_psEventFilter = 0;
}
PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update) : already(0), reply(0), full(0) {
updateUrl = qs(update.vurl);
moveToThread(thread);
manager.moveToThread(thread);
App::setProxySettings(manager);
connect(thread, SIGNAL(started()), this, SLOT(start()));
initOutput();
}
PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const QString &url) : already(0), reply(0), full(0) {
updateUrl = url;
moveToThread(thread);
manager.moveToThread(thread);
App::setProxySettings(manager);
connect(thread, SIGNAL(started()), this, SLOT(start()));
initOutput();
}
void PsUpdateDownloader::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 PsUpdateDownloader::start() {
sendRequest();
}
void PsUpdateDownloader::sendRequest() {
QNetworkRequest req(updateUrl);
QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";// + QByteArray::number(already + cUpdateChunk() - 1);
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 PsUpdateDownloader::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 PsUpdateDownloader::ready() {
QMutexLocker lock(&mutex);
return already;
}
int32 PsUpdateDownloader::size() {
QMutexLocker lock(&mutex);
return full;
}
void PsUpdateDownloader::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 PsUpdateDownloader::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 PsUpdateDownloader::deleteDir(const QString &dir) {
void psDeleteDir(const QString &dir) {
std::wstring wDir = QDir::toNativeSeparators(dir).toStdWString();
WCHAR path[4096];
memcpy(path, wDir.c_str(), (wDir.size() + 1) * sizeof(WCHAR));
@@ -1586,185 +1405,6 @@ void PsUpdateDownloader::deleteDir(const QString &dir) {
int res = SHFileOperation(&file_op);
}
void PsUpdateDownloader::fatalFail() {
clearAll();
emit App::app()->updateFailed();
}
void PsUpdateDownloader::clearAll() {
deleteDir(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 PsUpdateDownloader::unpackUpdate() {
QByteArray packed;
if (!outputFile.open(QIODevice::ReadOnly)) {
LOG(("Update Error: cant read updates file!"));
return fatalFail();
}
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
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"), readyDirPath = cWorkingDir() + qsl("tupdates/ready");
deleteDir(tempDirPath);
deleteDir(readyDirPath);
{
QDir tempDir(tempDirPath), readyDir(readyDirPath);
if (tempDir.exists() || readyDir.exists()) {
LOG(("Update Error: cant clear tupdates/temp or tupdates/ready dir!"));
return fatalFail();
}
tempDirPath = tempDir.absolutePath();
readyDirPath = readyDir.absolutePath();
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();
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();
}
QDir().mkdir(tempDirPath);
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 (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 (int32 i = 0; i < filesCount; ++i) {
QString relativeName;
quint32 fileSize;
QByteArray fileInnerData;
stream >> relativeName >> fileSize >> fileInnerData;
if (stream.status() != QDataStream::Ok) {
LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status()));
return fatalFail();
}
if (fileSize != 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 (!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();
}
// create tdata/version file
QDir().mkdir(tempDirPath + qsl("/tdata"));
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();
DWORD versionNum = DWORD(version), versionLen = DWORD(versionString.size() * sizeof(WCHAR));
WCHAR 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("/tdata/version")));
return fatalFail();
}
fVersion.write((const char*)&versionNum, sizeof(DWORD));
fVersion.write((const char*)&versionLen, sizeof(DWORD));
fVersion.write((const char*)&versionStr[0], versionLen);
fVersion.close();
}
std::wstring tempDirNative = QDir::toNativeSeparators(tempDirPath).toStdWString(), readyDirNative = QDir::toNativeSeparators(readyDirPath).toStdWString();
if (!MoveFile(tempDirNative.c_str(), readyDirNative.c_str())) {
LOG(("Update Error: cant rename temp dir '%1' to ready dir '%2'. %3").arg(QString::fromStdWString(tempDirNative)).arg(QString::fromStdWString(readyDirNative)).arg(winapiErrorWrap()));
return fatalFail();
}
deleteDir(tempDirPath);
outputFile.remove();
emit App::app()->updateReady();
}
PsUpdateDownloader::~PsUpdateDownloader() {
delete reply;
reply = 0;
}
namespace {
BOOL CALLBACK _ActivateProcess(HWND hWnd, LPARAM lParam) {
uint64 &processId(*(uint64*)lParam);
@@ -2138,65 +1778,6 @@ int psFixPrevious() {
return 0;
}
bool psCheckReadyUpdate() {
QString readyPath = cWorkingDir() + qsl("tupdates/ready");
if (!QDir(readyPath).exists()) {
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));
PsUpdateDownloader::clearAll();
return false;
}
DWORD versionNum;
if (fVersion.read((char*)&versionNum, sizeof(DWORD)) != sizeof(DWORD)) {
LOG(("Update Error: cant read version from file '%1'").arg(versionPath));
PsUpdateDownloader::clearAll();
return false;
}
fVersion.close();
if (versionNum <= AppVersion) {
LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
PsUpdateDownloader::clearAll();
return false;
}
}
QString curUpdater = (cExeDir() + qsl("Updater.exe"));
QFileInfo updater(cWorkingDir() + qsl("tupdates/ready/Updater.exe"));
if (!updater.exists()) {
QFileInfo current(curUpdater);
if (!current.exists()) {
PsUpdateDownloader::clearAll();
return false;
}
if (CopyFile(current.absoluteFilePath().toStdWString().c_str(), updater.absoluteFilePath().toStdWString().c_str(), TRUE) == FALSE) {
PsUpdateDownloader::clearAll();
return false;
}
}
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 {
PsUpdateDownloader::clearAll();
return false;
}
}
if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
PsUpdateDownloader::clearAll();
return false;
}
return true;
}
void psPostprocessFile(const QString &name) {
std::wstring zoneFile = QDir::toNativeSeparators(name).toStdWString() + L":Zone.Identifier";
HANDLE f = CreateFile(zoneFile.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
@@ -2478,7 +2059,7 @@ void psExecUpdater() {
if (cStartInTray()) targs += qsl(" -startintray");
if (cWriteProtected()) targs += qsl(" -writeprotected \"") + cExeDir() + '"';
QString updaterPath = cWriteProtected() ? (cWorkingDir() + qsl("tupdates/ready/Updater.exe")) : (cExeDir() + qsl("Updater.exe"));
QString updaterPath = cWriteProtected() ? (cWorkingDir() + qsl("tupdates/temp/Updater.exe")) : (cExeDir() + qsl("Updater.exe"));
QString updater(QDir::toNativeSeparators(updaterPath)), wdir(QDir::toNativeSeparators(cWorkingDir()));
@@ -2486,8 +2067,7 @@ void psExecUpdater() {
HINSTANCE r = ShellExecute(0, cWriteProtected() ? L"runas" : 0, updater.toStdWString().c_str(), targs.toStdWString().c_str(), wdir.isEmpty() ? 0 : wdir.toStdWString().c_str(), SW_SHOWNORMAL);
if (long(r) < 32) {
DEBUG_LOG(("Application Error: failed to execute %1, working directory: '%2', result: %3").arg(updater).arg(wdir).arg(long(r)));
QString readyPath = cWorkingDir() + qsl("tupdates/ready");
PsUpdateDownloader::deleteDir(readyPath);
psDeleteDir(cWorkingDir() + qsl("tupdates/temp"));
}
}

View File

@@ -117,55 +117,9 @@ public:
void psInstallEventFilter();
~PsApplication();
signals:
void updateChecking();
void updateLatest();
void updateDownloading(qint64 ready, qint64 total);
void updateReady();
void updateFailed();
};
class PsUpdateDownloader : public QObject {
Q_OBJECT
public:
PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update);
PsUpdateDownloader(QThread *thread, const QString &url);
void unpackUpdate();
int32 ready();
int32 size();
static void deleteDir(const QString &dir);
static void clearAll();
~PsUpdateDownloader();
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;
};
void psDeleteDir(const QString &dir);
void psUserActionDone();
bool psIdleSupported();
@@ -196,7 +150,6 @@ void psBringToBack(QWidget *w);
int psCleanup();
int psFixPrevious();
bool psCheckReadyUpdate();
void psExecUpdater();
void psExecTelegram();

View File

@@ -84,6 +84,9 @@ QString gTimeFormat = qsl("hh:mm");
int32 gAutoLock = 3600;
bool gHasPasscode = false;
bool gHasAudioPlayer = true;
bool gHasAudioCapture = true;
DBIEmojiTab gEmojiTab = dbietRecent;
RecentEmojiPack gRecentEmojis;
RecentEmojisPreload gRecentEmojisPreload;
@@ -96,6 +99,7 @@ EmojiStickersMap gEmojiStickers;
RecentStickerPreload gRecentStickersPreload;
RecentStickerPack gRecentStickers;
StickerSets gStickerSets;
StickerSetsOrder gStickerSetsOrder;
uint64 gLastStickersUpdate = 0;

View File

@@ -134,6 +134,9 @@ DeclareSetting(QString, TimeFormat);
DeclareSetting(int32, AutoLock);
DeclareSetting(bool, HasPasscode);
DeclareSetting(bool, HasAudioPlayer);
DeclareSetting(bool, HasAudioCapture);
inline void cChangeTimeFormat(const QString &newFormat) {
if (!newFormat.isEmpty()) cSetTimeFormat(newFormat);
}
@@ -208,6 +211,8 @@ struct StickerSet {
};
typedef QMap<uint64, StickerSet> StickerSets;
DeclareRefSetting(StickerSets, StickerSets);
typedef QList<uint64> StickerSetsOrder;
DeclareRefSetting(StickerSetsOrder, StickerSetsOrder);
typedef QList<QPair<QString, ushort> > RecentHashtagPack;
DeclareSetting(RecentHashtagPack, RecentWriteHashtags);

View File

@@ -37,6 +37,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "langloaderplain.h"
#include "gui/filedialog.h"
#include "autoupdater.h"
#include "localstorage.h"
Slider::Slider(QWidget *parent, const style::slider &st, int32 count, int32 sel) : QWidget(parent),
@@ -1172,7 +1174,7 @@ void SettingsInner::onCheckNow() {
}
void SettingsInner::onRestartNow() {
psCheckReadyUpdate();
checkReadyUpdate();
if (_updatingState == UpdatingReady) {
cSetRestartingUpdate(true);
} else {

View File

@@ -303,7 +303,7 @@ void VideoOpenLink::onClick(Qt::MouseButton button) const {
QString filename = saveFileName(lang(lng_save_video), qsl("MOV Video (*.mov);;All files (*.*)"), qsl("video"), qsl(".mov"), false);
if (!filename.isEmpty()) {
data->openOnSave = 1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : (App::contextItem() ? App::contextItem()->id : 0);
data->save(filename);
}
}
@@ -369,19 +369,17 @@ void AudioOpenLink::onClick(Qt::MouseButton button) const {
AudioData *data = audio();
if ((!data->user && !data->date) || button != Qt::LeftButton) return;
bool mp3 = (data->mime == QLatin1String("audio/mp3"));
QString already = data->already(true);
bool play = !mp3 && audioVoice();
bool play = audioPlayer();
if (!already.isEmpty() || (!data->data.isEmpty() && play)) {
if (play) {
AudioData *playing = 0;
VoiceMessageState playingState = VoiceMessageStopped;
audioVoice()->currentState(&playing, &playingState);
if (playing == data && playingState != VoiceMessageStopped) {
audioVoice()->pauseresume();
AudioPlayerState playingState = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &playingState);
if (playing == data && playingState != AudioPlayerStopped) {
audioPlayer()->pauseresume();
} else {
audioVoice()->play(data);
audioPlayer()->play(data);
if (App::main()) App::main()->audioMarkRead(data);
}
} else {
@@ -393,10 +391,11 @@ void AudioOpenLink::onClick(Qt::MouseButton button) const {
if (data->status != FileReady) return;
bool mp3 = (data->mime == QLatin1String("audio/mp3"));
QString filename = saveFileName(lang(lng_save_audio), mp3 ? qsl("MP3 Audio (*.mp3);;All files (*.*)") : qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), mp3 ? qsl(".mp3") : qsl(".ogg"), false);
if (!filename.isEmpty()) {
data->openOnSave = 1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : (App::contextItem() ? App::contextItem()->id : 0);
data->save(filename);
}
}
@@ -421,7 +420,7 @@ void AudioSaveLink::doSave(AudioData *data, bool forceSavingAs) {
data->cancel();
} else if (!already.isEmpty()) {
data->openOnSave = -1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : (App::contextItem() ? App::contextItem()->id : 0);
}
data->save(filename);
}
@@ -470,8 +469,10 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const {
if (reader.canRead()) {
if (reader.supportsAnimation() && reader.imageCount() > 1 && App::hoveredLinkItem()) {
startGif(App::hoveredLinkItem(), already);
} else if (App::hoveredLinkItem() || App::contextItem()) {
App::wnd()->showDocument(data, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem());
} else {
App::wnd()->showDocument(data, App::hoveredLinkItem());
psOpenFile(already);
}
} else {
psOpenFile(already);
@@ -501,7 +502,7 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const {
QString filename = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, false);
if (!filename.isEmpty()) {
data->openOnSave = 1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : (App::contextItem() ? App::contextItem()->id : 0);
data->save(filename);
}
}
@@ -538,7 +539,7 @@ void DocumentSaveLink::doSave(DocumentData *data, bool forceSavingAs) {
data->cancel();
} else if (!already.isEmpty()) {
data->openOnSave = -1;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0;
data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : (App::contextItem() ? App::contextItem()->id : 0);
}
data->save(filename);
}

View File

@@ -23,6 +23,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "passcodewidget.h"
#include "window.h"
#include "application.h"
#include "autoupdater.h"
SysBtn::SysBtn(QWidget *parent, const style::sysButton &st, const QString &text) : Button(parent),
_st(st), a_color(_st.color->c), _overLevel(0), _text(text) {
@@ -142,7 +143,7 @@ UpdateBtn::UpdateBtn(QWidget *parent, Window *window, const QString &text) : Sys
}
void UpdateBtn::onClick() {
psCheckReadyUpdate();
checkReadyUpdate();
if (App::app()->updatingState() == Application::UpdatingReady) {
cSetRestartingUpdate(true);
} else {

View File

@@ -57,7 +57,7 @@ void ConnectingWidget::set(const QString &text, const QString &reconnect) {
void ConnectingWidget::paintEvent(QPaintEvent *e) {
QPainter p(this);
_shadow.paint(p, QRect(0, st::boxShadow.pxHeight(), width() - st::boxShadow.pxWidth(), height() - st::boxShadow.pxHeight()), QPoint(0, 0), BoxShadow::Top | BoxShadow::Right);
_shadow.paint(p, QRect(0, st::boxShadow.pxHeight(), width() - st::boxShadow.pxWidth(), height() - st::boxShadow.pxHeight()), 0, BoxShadow::Top | BoxShadow::Right);
p.fillRect(0, st::boxShadow.pxHeight(), width() - st::boxShadow.pxWidth(), height() - st::boxShadow.pxHeight(), st::connectingBG->b);
p.setFont(st::linkFont->f);
p.setPen(st::connectingColor->p);
@@ -810,9 +810,7 @@ void Window::hideLayer(bool fast) {
layerBG = 0;
}
}
if (_mediaView && !_mediaView->isHidden()) {
_mediaView->hide();
}
hideMediaview();
}
bool Window::hideInnerLayer() {
@@ -843,10 +841,14 @@ void Window::layerHidden() {
layerBG->deleteLater();
}
layerBG = 0;
if (_mediaView && !_mediaView->isHidden()) _mediaView->hide();
hideMediaview();
setInnerFocus();
}
void Window::hideMediaview() {
if (_mediaView && !_mediaView->isHidden()) _mediaView->hide();
}
void Window::setInnerFocus() {
if (_passcode) {
_passcode->setInnerFocus();
@@ -1413,7 +1415,8 @@ void Window::notifyShowNext(NotifyWindow *remove) {
while (count > 0) {
uint64 next = 0;
HistoryItem *notifyItem = 0;
NotifyWaiters::iterator notifyWaiter;
History *notifyHistory = 0;
NotifyWaiters::iterator notifyWaiter = notifyWaiters.end();
for (NotifyWaiters::iterator i = notifyWaiters.begin(); i != notifyWaiters.end(); ++i) {
History *history = i.key();
if (history->currentNotification() && history->currentNotification()->id != i.value().msg) {
@@ -1421,6 +1424,7 @@ void Window::notifyShowNext(NotifyWindow *remove) {
if (j == notifyWhenMaps.end()) {
history->clearNotifications();
i = notifyWaiters.erase(i);
if (notifyHistory) notifyWaiter = notifyWaiters.find(notifyHistory);
continue;
}
do {
@@ -1436,12 +1440,14 @@ void Window::notifyShowNext(NotifyWindow *remove) {
if (!history->currentNotification()) {
notifyWhenMaps.remove(history);
i = notifyWaiters.erase(i);
if (notifyHistory) notifyWaiter = notifyWaiters.find(notifyHistory);
continue;
}
uint64 when = i.value().when;
if (!notifyItem || next > when) {
next = when;
notifyItem = history->currentNotification();
notifyHistory = history;
notifyWaiter = i;
}
}
@@ -1508,8 +1514,8 @@ void Window::notifyShowNext(NotifyWindow *remove) {
}
if (!history->hasNotification()) {
notifyWaiters.erase(notifyWaiter);
if (j != notifyWhenMaps.end()) notifyWhenMaps.erase(j);
if (notifyWaiter != notifyWaiters.cend()) notifyWaiters.erase(notifyWaiter);
if (j != notifyWhenMaps.cend()) notifyWhenMaps.erase(j);
continue;
}
}
@@ -1691,7 +1697,7 @@ QImage Window::iconWithCounter(int size, int count, style::color bg, bool smallI
void Window::sendPaths() {
if (App::passcoded()) return;
if (_mediaView && !_mediaView->isHidden()) _mediaView->hide();
hideMediaview();
if (settings) {
hideSettings();
} else {

View File

@@ -231,6 +231,7 @@ public:
void changingMsgId(HistoryItem *row, MsgId newId);
bool isActive(bool cached = true) const;
void hideMediaview();
public slots:

View File

@@ -11,7 +11,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.8.14</string>
<string>0.8.24</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@@ -83,6 +83,7 @@ SOURCES += \
./SourceFiles/app.cpp \
./SourceFiles/application.cpp \
./SourceFiles/audio.cpp \
./SourceFiles/autoupdater.cpp \
./SourceFiles/dialogswidget.cpp \
./SourceFiles/dropdown.cpp \
./SourceFiles/fileuploader.cpp \
@@ -151,6 +152,7 @@ SOURCES += \
./SourceFiles/boxes/photocropbox.cpp \
./SourceFiles/boxes/photosendbox.cpp \
./SourceFiles/boxes/sessionsbox.cpp \
./SourceFiles/boxes/stickersetbox.cpp \
./SourceFiles/boxes/usernamebox.cpp \
./SourceFiles/intro/intro.cpp \
./SourceFiles/intro/introcode.cpp \
@@ -165,6 +167,7 @@ HEADERS += \
./SourceFiles/app.h \
./SourceFiles/application.h \
./SourceFiles/audio.h \
./SourceFiles/autoupdater.h \
./SourceFiles/config.h \
./SourceFiles/countries.h \
./SourceFiles/dialogswidget.h \
@@ -241,6 +244,7 @@ HEADERS += \
./SourceFiles/boxes/photocropbox.h \
./SourceFiles/boxes/photosendbox.h \
./SourceFiles/boxes/sessionsbox.h \
./SourceFiles/boxes/stickersetbox.h \
./SourceFiles/boxes/usernamebox.h \
./SourceFiles/intro/intro.h \
./SourceFiles/intro/introcode.h \
@@ -298,7 +302,7 @@ INCLUDEPATH += "/usr/include/atk-1.0"
INCLUDEPATH += "/usr/include/dee-1.0"
INCLUDEPATH += "/usr/include/libdbusmenu-glib-0.4"
LIBS += -lcrypto -lssl -lz -ldl -llzma -lexif -lopus -lopusfile -logg -lopenal
LIBS += -lcrypto -lssl -lz -ldl -llzma -lexif -lopenal -lavformat -lavcodec -lswresample -lavutil -lopus
LIBS += ./../../../Libraries/QtStatic/qtbase/plugins/platforminputcontexts/libcomposeplatforminputcontextplugin.a
RESOURCES += \

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -891,6 +891,18 @@
<ClCompile Include="SourceFiles\boxes\stickersetbox.cpp">
<Filter>boxes</Filter>
</ClCompile>
<ClCompile Include="SourceFiles\autoupdater.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_autoupdater.cpp">
<Filter>Generated Files\Deploy</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_autoupdater.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_autoupdater.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SourceFiles\stdafx.h">
@@ -1183,6 +1195,9 @@
<CustomBuild Include="SourceFiles\boxes\stickersetbox.h">
<Filter>boxes</Filter>
</CustomBuild>
<CustomBuild Include="SourceFiles\autoupdater.h">
<Filter>Source Files</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Image Include="SourceFiles\art\icon256.ico" />

View File

@@ -40,6 +40,8 @@
06EABCC49D2EEE4076322BE7 /* moc_mtp.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 924D4939FD169BB4B8AEB1C9 /* moc_mtp.cpp */; settings = {ATTRIBUTES = (); }; };
07080BCF1A43588C00741A51 /* lang_auto.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07080BCD1A43588C00741A51 /* lang_auto.cpp */; };
07080BD21A436A5000741A51 /* lang.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07080BD01A436A5000741A51 /* lang.cpp */; };
0710C9FE1B0B9376001B4272 /* stickersetbox.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0710C9FC1B0B9376001B4272 /* stickersetbox.cpp */; };
0710CA051B0B9404001B4272 /* moc_stickersetbox.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0710CA041B0B9404001B4272 /* moc_stickersetbox.cpp */; };
0732E4A9199E262300D50FE7 /* overviewwidget.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0732E4A7199E262300D50FE7 /* overviewwidget.cpp */; };
0732E4AC199E268A00D50FE7 /* moc_overviewwidget.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 0732E4AB199E268A00D50FE7 /* moc_overviewwidget.cpp */; };
074756191A1372C600CA07F7 /* moc_types.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 074756181A1372C600CA07F7 /* moc_types.cpp */; };
@@ -66,6 +68,8 @@
07BE85121A20961F008ACB9F /* moc_localstorage.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07BE85111A20961F008ACB9F /* moc_localstorage.cpp */; };
07C4753B1967DF1C00CAAFE9 /* switcher.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07C475391967DF1C00CAAFE9 /* switcher.cpp */; };
07C4753F1967E37300CAAFE9 /* moc_switcher.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07C4753E1967E37300CAAFE9 /* moc_switcher.cpp */; };
07C7596F1B1F7E0000662169 /* autoupdater.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07C7596D1B1F7E0000662169 /* autoupdater.cpp */; };
07C759721B1F7E2800662169 /* moc_autoupdater.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07C759711B1F7E2800662169 /* moc_autoupdater.cpp */; };
07CAACD81AEA64F00058E508 /* AudioUnit.framework in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 07CAACD71AEA64F00058E508 /* AudioUnit.framework */; };
07D7034B19B8755A00C4EED2 /* audio.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07D7034919B8755A00C4EED2 /* audio.cpp */; };
07D703BB19B88FB900C4EED2 /* moc_audio.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07D703BA19B88FB900C4EED2 /* moc_audio.cpp */; };
@@ -213,7 +217,6 @@
EBA5E17368D2BBC6014E92B9 /* qcorewlanbearer in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = EE03BC5CA4628A6D6BEB0122 /* qcorewlanbearer */; };
EBE29731916DB43BF49FE7A4 /* aboutbox.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = C194EDD00F76216057D48A5C /* aboutbox.cpp */; settings = {ATTRIBUTES = (); }; };
ED2557A57C6782721DC494AF /* moc_connectionbox.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = FEC58F9D8A0963E5A9D4BE6F /* moc_connectionbox.cpp */; settings = {ATTRIBUTES = (); }; };
EDFF7E777D3A3730A9BB3FC2 /* QTKit.framework in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 2AC1C71FA3CD6FD909ED0276 /* QTKit.framework */; };
F26454630C80841CBDCFE1CA /* Foundation.framework in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = FCC237CA5AD60B9BA4447615 /* Foundation.framework */; };
F278C423357CA99797EA30AB /* photosendbox.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = D1C9C77F1318F5A55C9BF289 /* photosendbox.cpp */; settings = {ATTRIBUTES = (); }; };
F2A75ACAC9DF6A3F4E5711E7 /* AppKit.framework in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 723F90793B2C195E2CCB2233 /* AppKit.framework */; };
@@ -265,6 +268,9 @@
07080BD01A436A5000741A51 /* lang.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = lang.cpp; path = SourceFiles/lang.cpp; sourceTree = SOURCE_ROOT; };
07080BD11A436A5000741A51 /* lang.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lang.h; path = SourceFiles/lang.h; sourceTree = SOURCE_ROOT; };
07084684195445A600B5AE3A /* Updater.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = Updater.xcodeproj; sourceTree = SOURCE_ROOT; };
0710C9FC1B0B9376001B4272 /* stickersetbox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = stickersetbox.cpp; path = SourceFiles/boxes/stickersetbox.cpp; sourceTree = SOURCE_ROOT; };
0710C9FD1B0B9376001B4272 /* stickersetbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stickersetbox.h; path = SourceFiles/boxes/stickersetbox.h; sourceTree = SOURCE_ROOT; };
0710CA041B0B9404001B4272 /* moc_stickersetbox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_stickersetbox.cpp; path = GeneratedFiles/Debug/moc_stickersetbox.cpp; sourceTree = SOURCE_ROOT; };
072E117A1A56EB9400A87ACC /* lang_pt_BR.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = lang_pt_BR.strings; path = SourceFiles/langs/lang_pt_BR.strings; sourceTree = SOURCE_ROOT; };
0732E4A7199E262300D50FE7 /* overviewwidget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = overviewwidget.cpp; path = SourceFiles/overviewwidget.cpp; sourceTree = SOURCE_ROOT; };
0732E4A8199E262300D50FE7 /* overviewwidget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = overviewwidget.h; path = SourceFiles/overviewwidget.h; sourceTree = SOURCE_ROOT; };
@@ -310,6 +316,9 @@
07C475391967DF1C00CAAFE9 /* switcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = switcher.cpp; path = SourceFiles/gui/switcher.cpp; sourceTree = SOURCE_ROOT; };
07C4753A1967DF1C00CAAFE9 /* switcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = switcher.h; path = SourceFiles/gui/switcher.h; sourceTree = SOURCE_ROOT; };
07C4753E1967E37300CAAFE9 /* moc_switcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_switcher.cpp; path = GeneratedFiles/Debug/moc_switcher.cpp; sourceTree = SOURCE_ROOT; };
07C7596D1B1F7E0000662169 /* autoupdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = autoupdater.cpp; path = SourceFiles/autoupdater.cpp; sourceTree = SOURCE_ROOT; };
07C7596E1B1F7E0000662169 /* autoupdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = autoupdater.h; path = SourceFiles/autoupdater.h; sourceTree = SOURCE_ROOT; };
07C759711B1F7E2800662169 /* moc_autoupdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_autoupdater.cpp; path = GeneratedFiles/Debug/moc_autoupdater.cpp; sourceTree = SOURCE_ROOT; };
07CAACD71AEA64F00058E508 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
07D7034919B8755A00C4EED2 /* audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = audio.cpp; path = SourceFiles/audio.cpp; sourceTree = SOURCE_ROOT; };
07D7034A19B8755A00C4EED2 /* audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = audio.h; path = SourceFiles/audio.h; sourceTree = SOURCE_ROOT; };
@@ -390,7 +399,6 @@
27E7471A4EC90E84353AA16F /* mtpCoreTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = mtpCoreTypes.h; path = SourceFiles/mtproto/mtpCoreTypes.h; sourceTree = "<absolute>"; };
28BD0D10214709D95B161E24 /* /usr/local/Qt-5.4.0/mkspecs/modules/qt_lib_multimediawidgets.pri */ = {isa = PBXFileReference; lastKnownFileType = text; path = "/usr/local/Qt-5.4.0/mkspecs/modules/qt_lib_multimediawidgets.pri"; sourceTree = "<absolute>"; };
293C8DEEE270847AC20E70F9 /* /usr/local/Qt-5.4.0/mkspecs/modules/qt_lib_network.pri */ = {isa = PBXFileReference; lastKnownFileType = text; path = "/usr/local/Qt-5.4.0/mkspecs/modules/qt_lib_network.pri"; sourceTree = "<absolute>"; };
2AC1C71FA3CD6FD909ED0276 /* QTKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QTKit.framework; path = /System/Library/Frameworks/QTKit.framework; sourceTree = "<absolute>"; };
2BB2A1BB8DB0993F78F4E3C7 /* title.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = title.cpp; path = SourceFiles/title.cpp; sourceTree = "<absolute>"; };
2C540BAEABD7F9B5FA11008E /* moc_mtpDC.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = moc_mtpDC.cpp; path = GeneratedFiles/Debug/moc_mtpDC.cpp; sourceTree = "<absolute>"; };
2C99425D7670941EAF07B453 /* moc_historywidget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = moc_historywidget.cpp; path = GeneratedFiles/Debug/moc_historywidget.cpp; sourceTree = "<absolute>"; };
@@ -710,7 +718,6 @@
CCA737EE379CDB10CC9A0F23 /* AVFoundation.framework in Link Binary With Libraries */,
B78304F135DEF1F7A68393A6 /* CoreMedia.framework in Link Binary With Libraries */,
F2A75ACAC9DF6A3F4E5711E7 /* AppKit.framework in Link Binary With Libraries */,
EDFF7E777D3A3730A9BB3FC2 /* QTKit.framework in Link Binary With Libraries */,
EBA5E17368D2BBC6014E92B9 /* qcorewlanbearer in Link Binary With Libraries */,
D0EECF370C58DDCACBC71BAD /* CoreWLAN.framework in Link Binary With Libraries */,
8883FF366F2623E89D90A9E6 /* qgenericbearer in Link Binary With Libraries */,
@@ -897,6 +904,7 @@
06E379415713F34B83F99C35 /* app.cpp */,
C20F9DD8C7B031B8E20D5653 /* application.cpp */,
07D7034919B8755A00C4EED2 /* audio.cpp */,
07C7596D1B1F7E0000662169 /* autoupdater.cpp */,
E466873F01ABA1E55E914489 /* dialogswidget.cpp */,
710C982FC773400941B3AFBC /* dropdown.cpp */,
9B36BB8C5B8CA7B07F3F35F0 /* fileuploader.cpp */,
@@ -932,6 +940,7 @@
C19DF71B273A4843553518F2 /* app.h */,
09FD01F2BD652EB838A296D8 /* application.h */,
07D7034A19B8755A00C4EED2 /* audio.h */,
07C7596E1B1F7E0000662169 /* autoupdater.h */,
24F7D3E789E91B10E422C116 /* config.h */,
206B4F5CBD5354BCE19FF32F /* countries.h */,
55B4A93DD455EED91C899A8E /* dialogswidget.h */,
@@ -1099,6 +1108,8 @@
801973D3334D0FCA849CF485 /* Debug */ = {
isa = PBXGroup;
children = (
07C759711B1F7E2800662169 /* moc_autoupdater.cpp */,
0710CA041B0B9404001B4272 /* moc_stickersetbox.cpp */,
0755AEDA1AD12A80004D738A /* moc_abstractbox.cpp */,
0755AEDB1AD12A80004D738A /* moc_intropwdcheck.cpp */,
0755AEDC1AD12A80004D738A /* moc_sessionsbox.cpp */,
@@ -1183,6 +1194,7 @@
E908A6C86F93FA27DF70866C /* photocropbox.cpp */,
D1C9C77F1318F5A55C9BF289 /* photosendbox.cpp */,
07DB674B1AD07C9200A51329 /* sessionsbox.cpp */,
0710C9FC1B0B9376001B4272 /* stickersetbox.cpp */,
07D8509719F8320900623D75 /* usernamebox.cpp */,
143405635D04698F421A12EA /* aboutbox.h */,
07DB674A1AD07C9200A51329 /* abstractbox.h */,
@@ -1199,6 +1211,7 @@
14437BFDCD58FF1742EF1B35 /* photocropbox.h */,
0BDE09020E45EFA57DCB2E25 /* photosendbox.h */,
07DB674C1AD07C9200A51329 /* sessionsbox.h */,
0710C9FD1B0B9376001B4272 /* stickersetbox.h */,
07D8509819F8320900623D75 /* usernamebox.h */,
);
name = boxes;
@@ -1230,7 +1243,6 @@
186D09F4CB713AD4B8BDD260 /* AudioUnit.framework */,
EDD43CF4FA85D97A1140E973 /* qavfmediaplayer */,
1C21DCD421D7B7E0462F1121 /* qqt7engine */,
2AC1C71FA3CD6FD909ED0276 /* QTKit.framework */,
833B45FEF5DC4AD0E8ADA64A /* Qt5MultimediaWidgets */,
AA5379CB06E908AC80BE7B82 /* Qt5OpenGL */,
A490341D0650372A5757B367 /* qtmedia_audioengine */,
@@ -1561,6 +1573,7 @@
07C4753B1967DF1C00CAAFE9 /* switcher.cpp in Compile Sources */,
A0A6B97F7DBEC81004EC9461 /* confirmbox.cpp in Compile Sources */,
4FEA8F51B7BC7CAC71347A1A /* connectionbox.cpp in Compile Sources */,
07C7596F1B1F7E0000662169 /* autoupdater.cpp in Compile Sources */,
078A2FCA1A811C5900CCC7A0 /* moc_backgroundbox.cpp in Compile Sources */,
298BFAB73BF182297584F96F /* contactsbox.cpp in Compile Sources */,
BA41D511A9BBCA09365DF88C /* downloadpathbox.cpp in Compile Sources */,
@@ -1588,6 +1601,7 @@
074756191A1372C600CA07F7 /* moc_types.cpp in Compile Sources */,
98E4F55DB5D8E64AB9F08C83 /* moc_localimageloader.cpp in Compile Sources */,
A24E4B5B683764E07683ECEC /* moc_mainwidget.cpp in Compile Sources */,
0710CA051B0B9404001B4272 /* moc_stickersetbox.cpp in Compile Sources */,
07DE92A71AA4925B00A18F6F /* autolockbox.cpp in Compile Sources */,
07D8509919F8320900623D75 /* usernamebox.cpp in Compile Sources */,
A469EC9C4C367E0B773A9BB7 /* moc_settingswidget.cpp in Compile Sources */,
@@ -1616,6 +1630,7 @@
1BD711B4C358EA7D727BF358 /* moc_flatcheckbox.cpp in Compile Sources */,
565F748438E6CE0148C54AFE /* moc_flatinput.cpp in Compile Sources */,
8B71D1C7BB9DCEE6511219C2 /* moc_flatlabel.cpp in Compile Sources */,
0710C9FE1B0B9376001B4272 /* stickersetbox.cpp in Compile Sources */,
0764D55D1ABAD71B00FBFEED /* moc_apiwrap.cpp in Compile Sources */,
07DE92AD1AA4928B00A18F6F /* moc_passcodebox.cpp in Compile Sources */,
FCC949FEA178F9F5D7478027 /* moc_flattextarea.cpp in Compile Sources */,
@@ -1631,6 +1646,7 @@
074968D01A44D14C00394F46 /* languagebox.cpp in Compile Sources */,
07BE85121A20961F008ACB9F /* moc_localstorage.cpp in Compile Sources */,
07AF95F41AFD03B90060B057 /* qrc_telegram_emojis.cpp in Compile Sources */,
07C759721B1F7E2800662169 /* moc_autoupdater.cpp in Compile Sources */,
07DB674E1AD07C9200A51329 /* sessionsbox.cpp in Compile Sources */,
49C3C1BF153F7FC078A25CE4 /* moc_downloadpathbox.cpp in Compile Sources */,
9D294F23E02CFDF22C288382 /* moc_emojibox.cpp in Compile Sources */,
@@ -1687,7 +1703,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.8.14;
CURRENT_PROJECT_VERSION = 0.8.24;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -1705,7 +1721,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = 0.8.14;
CURRENT_PROJECT_VERSION = 0.8.24;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_OPTIMIZATION_LEVEL = fast;
GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h;
@@ -1731,10 +1747,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.8.14;
CURRENT_PROJECT_VERSION = 0.8.24;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DYLIB_COMPATIBILITY_VERSION = 0.8;
DYLIB_CURRENT_VERSION = 0.8.14;
DYLIB_CURRENT_VERSION = 0.8.24;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1758,9 +1774,7 @@
./SourceFiles,
./GeneratedFiles,
/usr/local/include,
"./../../Libraries/libogg-1.3.2/include",
./../../Libraries/opus/include,
./../../Libraries/opusfile/include,
"./../../Libraries/openal-soft/include",
"./../../Libraries/libexif-0.6.20",
"/usr/local/Qt-5.4.0/include",
@@ -1846,11 +1860,14 @@
"-lz",
"-lm",
/usr/local/lib/libopenal.a,
/usr/local/lib/libopusfile.a,
/usr/local/lib/libopus.a,
/usr/local/lib/libogg.a,
/usr/local/lib/liblzma.a,
/usr/local/lib/libexif.a,
/usr/local/lib/libavcodec.a,
/usr/local/lib/libavformat.a,
/usr/local/lib/libswresample.a,
/usr/local/lib/libavutil.a,
/usr/local/lib/libiconv.a,
"../../Libraries/openssl-xcode/libcrypto.a",
);
PRODUCT_NAME = Telegram;
@@ -1873,10 +1890,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.8.14;
CURRENT_PROJECT_VERSION = 0.8.24;
DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = 0.8;
DYLIB_CURRENT_VERSION = 0.8.14;
DYLIB_CURRENT_VERSION = 0.8.24;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1900,9 +1917,7 @@
./SourceFiles,
./GeneratedFiles,
/usr/local/include,
"./../../Libraries/libogg-1.3.2/include",
./../../Libraries/opus/include,
./../../Libraries/opusfile/include,
"./../../Libraries/openal-soft/include",
"./../../Libraries/libexif-0.6.20",
"/usr/local/Qt-5.4.0/include",
@@ -1987,11 +2002,14 @@
"-lz",
"-lm",
/usr/local/lib/libopenal.a,
/usr/local/lib/libopusfile.a,
/usr/local/lib/libopus.a,
/usr/local/lib/libogg.a,
/usr/local/lib/liblzma.a,
/usr/local/lib/libexif.a,
/usr/local/lib/libavcodec.a,
/usr/local/lib/libavformat.a,
/usr/local/lib/libswresample.a,
/usr/local/lib/libavutil.a,
/usr/local/lib/libiconv.a,
"../../Libraries/openssl-xcode/libcrypto.a",
);
PRODUCT_NAME = Telegram;

View File

@@ -31,7 +31,7 @@ mocables: compiler_moc_header_make_all compiler_moc_source_make_all
check: first
compilers: GeneratedFiles/qrc_telegram.cpp GeneratedFiles/qrc_telegram_emojis.cpp GeneratedFiles/qrc_telegram_mac.cpp GeneratedFiles/Debug/moc_apiwrap.cpp GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp GeneratedFiles/Debug/moc_dialogswidget.cpp GeneratedFiles/Debug/moc_dropdown.cpp\
compilers: GeneratedFiles/qrc_telegram.cpp GeneratedFiles/qrc_telegram_emojis.cpp GeneratedFiles/qrc_telegram_mac.cpp GeneratedFiles/Debug/moc_apiwrap.cpp GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp GeneratedFiles/Debug/moc_autoupdater.cpp GeneratedFiles/Debug/moc_dialogswidget.cpp GeneratedFiles/Debug/moc_dropdown.cpp\
GeneratedFiles/Debug/moc_fileuploader.cpp GeneratedFiles/Debug/moc_history.cpp GeneratedFiles/Debug/moc_historywidget.cpp GeneratedFiles/Debug/moc_layerwidget.cpp\
GeneratedFiles/Debug/moc_mediaview.cpp GeneratedFiles/Debug/moc_overviewwidget.cpp GeneratedFiles/Debug/moc_profilewidget.cpp\
GeneratedFiles/Debug/moc_passcodewidget.cpp\
@@ -50,7 +50,7 @@ compilers: GeneratedFiles/qrc_telegram.cpp GeneratedFiles/qrc_telegram_emojis.cp
GeneratedFiles/Debug/moc_confirmbox.cpp GeneratedFiles/Debug/moc_connectionbox.cpp GeneratedFiles/Debug/moc_contactsbox.cpp\
GeneratedFiles/Debug/moc_downloadpathbox.cpp GeneratedFiles/Debug/moc_emojibox.cpp GeneratedFiles/Debug/moc_languagebox.cpp\
GeneratedFiles/Debug/moc_passcodebox.cpp\
GeneratedFiles/Debug/moc_photocropbox.cpp GeneratedFiles/Debug/moc_photosendbox.cpp GeneratedFiles/Debug/moc_sessionsbox.cpp GeneratedFiles/Debug/moc_usernamebox.cpp GeneratedFiles/Debug/moc_intro.cpp\
GeneratedFiles/Debug/moc_photocropbox.cpp GeneratedFiles/Debug/moc_photosendbox.cpp GeneratedFiles/Debug/moc_sessionsbox.cpp GeneratedFiles/Debug/moc_stickersetbox.cpp GeneratedFiles/Debug/moc_usernamebox.cpp GeneratedFiles/Debug/moc_intro.cpp\
GeneratedFiles/Debug/moc_introcode.cpp GeneratedFiles/Debug/moc_introphone.cpp GeneratedFiles/Debug/moc_intropwdcheck.cpp GeneratedFiles/Debug/moc_introsignup.cpp\
GeneratedFiles/Debug/moc_pspecific_mac.cpp
compiler_objective_c_make_all:
@@ -99,9 +99,9 @@ GeneratedFiles/qrc_telegram_mac.cpp: SourceFiles/telegram_mac.qrc \
SourceFiles/art/osxtray.png
/usr/local/Qt-5.4.0/bin/rcc -name telegram_mac SourceFiles/telegram_mac.qrc -o GeneratedFiles/qrc_telegram_mac.cpp
compiler_moc_header_make_all: GeneratedFiles/Debug/moc_apiwrap.cpp GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp GeneratedFiles/Debug/moc_dialogswidget.cpp GeneratedFiles/Debug/moc_dropdown.cpp GeneratedFiles/Debug/moc_fileuploader.cpp GeneratedFiles/Debug/moc_history.cpp GeneratedFiles/Debug/moc_historywidget.cpp GeneratedFiles/Debug/moc_layerwidget.cpp GeneratedFiles/Debug/moc_mediaview.cpp GeneratedFiles/Debug/moc_overviewwidget.cpp GeneratedFiles/Debug/moc_profilewidget.cpp GeneratedFiles/Debug/moc_passcodewidget.cpp GeneratedFiles/Debug/moc_localimageloader.cpp GeneratedFiles/Debug/moc_localstorage.cpp GeneratedFiles/Debug/moc_mainwidget.cpp GeneratedFiles/Debug/moc_settingswidget.cpp GeneratedFiles/Debug/moc_sysbuttons.cpp GeneratedFiles/Debug/moc_title.cpp GeneratedFiles/Debug/moc_types.cpp GeneratedFiles/Debug/moc_window.cpp GeneratedFiles/Debug/moc_mtp.cpp GeneratedFiles/Debug/moc_mtpConnection.cpp GeneratedFiles/Debug/moc_mtpDC.cpp GeneratedFiles/Debug/moc_mtpFileLoader.cpp GeneratedFiles/Debug/moc_mtpSession.cpp GeneratedFiles/Debug/moc_animation.cpp GeneratedFiles/Debug/moc_button.cpp GeneratedFiles/Debug/moc_contextmenu.cpp GeneratedFiles/Debug/moc_countrycodeinput.cpp GeneratedFiles/Debug/moc_countryinput.cpp GeneratedFiles/Debug/moc_flatbutton.cpp GeneratedFiles/Debug/moc_flatcheckbox.cpp GeneratedFiles/Debug/moc_flatinput.cpp GeneratedFiles/Debug/moc_flatlabel.cpp GeneratedFiles/Debug/moc_flattextarea.cpp GeneratedFiles/Debug/moc_switcher.cpp GeneratedFiles/Debug/moc_phoneinput.cpp GeneratedFiles/Debug/moc_scrollarea.cpp GeneratedFiles/Debug/moc_twidget.cpp GeneratedFiles/Debug/moc_aboutbox.cpp GeneratedFiles/Debug/moc_abstractbox.cpp GeneratedFiles/Debug/moc_addcontactbox.cpp GeneratedFiles/Debug/moc_autolockbox.cpp GeneratedFiles/Debug/moc_backgroundbox.cpp GeneratedFiles/Debug/moc_confirmbox.cpp GeneratedFiles/Debug/moc_connectionbox.cpp GeneratedFiles/Debug/moc_contactsbox.cpp GeneratedFiles/Debug/moc_downloadpathbox.cpp GeneratedFiles/Debug/moc_emojibox.cpp GeneratedFiles/Debug/moc_languagebox.cpp GeneratedFiles/Debug/moc_passcodebox.cpp GeneratedFiles/Debug/moc_photocropbox.cpp GeneratedFiles/Debug/moc_photosendbox.cpp GeneratedFiles/Debug/moc_sessionsbox.cpp GeneratedFiles/Debug/moc_usernamebox.cpp GeneratedFiles/Debug/moc_intro.cpp GeneratedFiles/Debug/moc_introcode.cpp GeneratedFiles/Debug/moc_introphone.cpp GeneratedFiles/Debug/moc_intropwdcheck.cpp GeneratedFiles/Debug/moc_introsignup.cpp GeneratedFiles/Debug/moc_pspecific_mac.cpp
compiler_moc_header_make_all: GeneratedFiles/Debug/moc_apiwrap.cpp GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp GeneratedFiles/Debug/moc_autoupdater.cpp GeneratedFiles/Debug/moc_dialogswidget.cpp GeneratedFiles/Debug/moc_dropdown.cpp GeneratedFiles/Debug/moc_fileuploader.cpp GeneratedFiles/Debug/moc_history.cpp GeneratedFiles/Debug/moc_historywidget.cpp GeneratedFiles/Debug/moc_layerwidget.cpp GeneratedFiles/Debug/moc_mediaview.cpp GeneratedFiles/Debug/moc_overviewwidget.cpp GeneratedFiles/Debug/moc_profilewidget.cpp GeneratedFiles/Debug/moc_passcodewidget.cpp GeneratedFiles/Debug/moc_localimageloader.cpp GeneratedFiles/Debug/moc_localstorage.cpp GeneratedFiles/Debug/moc_mainwidget.cpp GeneratedFiles/Debug/moc_settingswidget.cpp GeneratedFiles/Debug/moc_sysbuttons.cpp GeneratedFiles/Debug/moc_title.cpp GeneratedFiles/Debug/moc_types.cpp GeneratedFiles/Debug/moc_window.cpp GeneratedFiles/Debug/moc_mtp.cpp GeneratedFiles/Debug/moc_mtpConnection.cpp GeneratedFiles/Debug/moc_mtpDC.cpp GeneratedFiles/Debug/moc_mtpFileLoader.cpp GeneratedFiles/Debug/moc_mtpSession.cpp GeneratedFiles/Debug/moc_animation.cpp GeneratedFiles/Debug/moc_button.cpp GeneratedFiles/Debug/moc_contextmenu.cpp GeneratedFiles/Debug/moc_countrycodeinput.cpp GeneratedFiles/Debug/moc_countryinput.cpp GeneratedFiles/Debug/moc_flatbutton.cpp GeneratedFiles/Debug/moc_flatcheckbox.cpp GeneratedFiles/Debug/moc_flatinput.cpp GeneratedFiles/Debug/moc_flatlabel.cpp GeneratedFiles/Debug/moc_flattextarea.cpp GeneratedFiles/Debug/moc_switcher.cpp GeneratedFiles/Debug/moc_phoneinput.cpp GeneratedFiles/Debug/moc_scrollarea.cpp GeneratedFiles/Debug/moc_twidget.cpp GeneratedFiles/Debug/moc_aboutbox.cpp GeneratedFiles/Debug/moc_abstractbox.cpp GeneratedFiles/Debug/moc_addcontactbox.cpp GeneratedFiles/Debug/moc_autolockbox.cpp GeneratedFiles/Debug/moc_backgroundbox.cpp GeneratedFiles/Debug/moc_confirmbox.cpp GeneratedFiles/Debug/moc_connectionbox.cpp GeneratedFiles/Debug/moc_contactsbox.cpp GeneratedFiles/Debug/moc_downloadpathbox.cpp GeneratedFiles/Debug/moc_emojibox.cpp GeneratedFiles/Debug/moc_languagebox.cpp GeneratedFiles/Debug/moc_passcodebox.cpp GeneratedFiles/Debug/moc_photocropbox.cpp GeneratedFiles/Debug/moc_photosendbox.cpp GeneratedFiles/Debug/moc_sessionsbox.cpp GeneratedFiles/Debug/moc_stickersetbox.cpp GeneratedFiles/Debug/moc_usernamebox.cpp GeneratedFiles/Debug/moc_intro.cpp GeneratedFiles/Debug/moc_introcode.cpp GeneratedFiles/Debug/moc_introphone.cpp GeneratedFiles/Debug/moc_intropwdcheck.cpp GeneratedFiles/Debug/moc_introsignup.cpp GeneratedFiles/Debug/moc_pspecific_mac.cpp
compiler_moc_header_clean:
-$(DEL_FILE) GeneratedFiles/Debug/moc_apiwrap.cpp GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp GeneratedFiles/Debug/moc_dialogswidget.cpp GeneratedFiles/Debug/moc_dropdown.cpp GeneratedFiles/Debug/moc_fileuploader.cpp GeneratedFiles/Debug/moc_history.cpp GeneratedFiles/Debug/moc_historywidget.cpp GeneratedFiles/Debug/moc_layerwidget.cpp GeneratedFiles/Debug/moc_mediaview.cpp GeneratedFiles/Debug/moc_overviewwidget.cpp GeneratedFiles/Debug/moc_profilewidget.cpp GeneratedFiles/Debug/moc_passcodewidget.cpp GeneratedFiles/Debug/moc_localimageloader.cpp GeneratedFiles/Debug/moc_localstorage.cpp GeneratedFiles/Debug/moc_mainwidget.cpp GeneratedFiles/Debug/moc_settingswidget.cpp GeneratedFiles/Debug/moc_sysbuttons.cpp GeneratedFiles/Debug/moc_title.cpp GeneratedFiles/Debug/moc_types.cpp GeneratedFiles/Debug/moc_window.cpp GeneratedFiles/Debug/moc_mtp.cpp GeneratedFiles/Debug/moc_mtpConnection.cpp GeneratedFiles/Debug/moc_mtpDC.cpp GeneratedFiles/Debug/moc_mtpFileLoader.cpp GeneratedFiles/Debug/moc_mtpSession.cpp GeneratedFiles/Debug/moc_animation.cpp GeneratedFiles/Debug/moc_button.cpp GeneratedFiles/Debug/moc_contextmenu.cpp GeneratedFiles/Debug/moc_countrycodeinput.cpp GeneratedFiles/Debug/moc_countryinput.cpp GeneratedFiles/Debug/moc_flatbutton.cpp GeneratedFiles/Debug/moc_flatcheckbox.cpp GeneratedFiles/Debug/moc_flatinput.cpp GeneratedFiles/Debug/moc_flatlabel.cpp GeneratedFiles/Debug/moc_flattextarea.cpp GeneratedFiles/Debug/moc_switcher.cpp GeneratedFiles/Debug/moc_phoneinput.cpp GeneratedFiles/Debug/moc_scrollarea.cpp GeneratedFiles/Debug/moc_twidget.cpp GeneratedFiles/Debug/moc_aboutbox.cpp GeneratedFiles/Debug/moc_abstractbox.cpp GeneratedFiles/Debug/moc_addcontactbox.cpp GeneratedFiles/Debug/moc_autolockbox.cpp GeneratedFiles/Debug/moc_backgroundbox.cpp GeneratedFiles/Debug/moc_confirmbox.cpp GeneratedFiles/Debug/moc_connectionbox.cpp GeneratedFiles/Debug/moc_contactsbox.cpp GeneratedFiles/Debug/moc_downloadpathbox.cpp GeneratedFiles/Debug/moc_emojibox.cpp GeneratedFiles/Debug/moc_languagebox.cpp GeneratedFiles/Debug/moc_passcodebox.cpp GeneratedFiles/Debug/moc_photocropbox.cpp GeneratedFiles/Debug/moc_photosendbox.cpp GeneratedFiles/Debug/moc_sessionsbox.cpp GeneratedFiles/Debug/moc_usernamedbox.cpp GeneratedFiles/Debug/moc_intro.cpp GeneratedFiles/Debug/moc_introcode.cpp GeneratedFiles/Debug/moc_introphone.cpp GeneratedFiles/Debug/moc_intropwdcheck.cpp GeneratedFiles/Debug/moc_introsignup.cpp GeneratedFiles/Debug/moc_pspecific_mac.cpp
-$(DEL_FILE) GeneratedFiles/Debug/moc_apiwrap.cpp GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp GeneratedFiles/Debug/moc_autoupdater.cpp GeneratedFiles/Debug/moc_dialogswidget.cpp GeneratedFiles/Debug/moc_dropdown.cpp GeneratedFiles/Debug/moc_fileuploader.cpp GeneratedFiles/Debug/moc_history.cpp GeneratedFiles/Debug/moc_historywidget.cpp GeneratedFiles/Debug/moc_layerwidget.cpp GeneratedFiles/Debug/moc_mediaview.cpp GeneratedFiles/Debug/moc_overviewwidget.cpp GeneratedFiles/Debug/moc_profilewidget.cpp GeneratedFiles/Debug/moc_passcodewidget.cpp GeneratedFiles/Debug/moc_localimageloader.cpp GeneratedFiles/Debug/moc_localstorage.cpp GeneratedFiles/Debug/moc_mainwidget.cpp GeneratedFiles/Debug/moc_settingswidget.cpp GeneratedFiles/Debug/moc_sysbuttons.cpp GeneratedFiles/Debug/moc_title.cpp GeneratedFiles/Debug/moc_types.cpp GeneratedFiles/Debug/moc_window.cpp GeneratedFiles/Debug/moc_mtp.cpp GeneratedFiles/Debug/moc_mtpConnection.cpp GeneratedFiles/Debug/moc_mtpDC.cpp GeneratedFiles/Debug/moc_mtpFileLoader.cpp GeneratedFiles/Debug/moc_mtpSession.cpp GeneratedFiles/Debug/moc_animation.cpp GeneratedFiles/Debug/moc_button.cpp GeneratedFiles/Debug/moc_contextmenu.cpp GeneratedFiles/Debug/moc_countrycodeinput.cpp GeneratedFiles/Debug/moc_countryinput.cpp GeneratedFiles/Debug/moc_flatbutton.cpp GeneratedFiles/Debug/moc_flatcheckbox.cpp GeneratedFiles/Debug/moc_flatinput.cpp GeneratedFiles/Debug/moc_flatlabel.cpp GeneratedFiles/Debug/moc_flattextarea.cpp GeneratedFiles/Debug/moc_switcher.cpp GeneratedFiles/Debug/moc_phoneinput.cpp GeneratedFiles/Debug/moc_scrollarea.cpp GeneratedFiles/Debug/moc_twidget.cpp GeneratedFiles/Debug/moc_aboutbox.cpp GeneratedFiles/Debug/moc_abstractbox.cpp GeneratedFiles/Debug/moc_addcontactbox.cpp GeneratedFiles/Debug/moc_autolockbox.cpp GeneratedFiles/Debug/moc_backgroundbox.cpp GeneratedFiles/Debug/moc_confirmbox.cpp GeneratedFiles/Debug/moc_connectionbox.cpp GeneratedFiles/Debug/moc_contactsbox.cpp GeneratedFiles/Debug/moc_downloadpathbox.cpp GeneratedFiles/Debug/moc_emojibox.cpp GeneratedFiles/Debug/moc_languagebox.cpp GeneratedFiles/Debug/moc_passcodebox.cpp GeneratedFiles/Debug/moc_photocropbox.cpp GeneratedFiles/Debug/moc_photosendbox.cpp GeneratedFiles/Debug/moc_sessionsbox.cpp GeneratedFiles/Debug/moc_stickersetbox.cpp GeneratedFiles/Debug/moc_usernamedbox.cpp GeneratedFiles/Debug/moc_intro.cpp GeneratedFiles/Debug/moc_introcode.cpp GeneratedFiles/Debug/moc_introphone.cpp GeneratedFiles/Debug/moc_intropwdcheck.cpp GeneratedFiles/Debug/moc_introsignup.cpp GeneratedFiles/Debug/moc_pspecific_mac.cpp
GeneratedFiles/Debug/moc_apiwrap.cpp: SourceFiles/types.h \
SourceFiles/logs.h \
SourceFiles/apiwrap.h
@@ -131,12 +131,21 @@ GeneratedFiles/Debug/moc_application.cpp: ../../Libraries/QtStatic/qtbase/includ
/usr/local/Qt-5.4.0/bin/moc $(DEFINES) -D__APPLE__ -D__GNUC__=4 -I/usr/local/Qt-5.4.0/mkspecs/macx-clang -I. -I/usr/local/Qt-5.4.0/include/QtGui/5.4.0/QtGui -I/usr/local/Qt-5.4.0/include/QtCore/5.4.0/QtCore -I/usr/local/Qt-5.4.0/include -I./SourceFiles -I./GeneratedFiles -I../../Libraries/lzma/C -I../../Libraries/libexif-0.6.20 -I/usr/local/Qt-5.4.0/include -I/usr/local/Qt-5.4.0/include/QtMultimedia -I/usr/local/Qt-5.4.0/include/QtWidgets -I/usr/local/Qt-5.4.0/include/QtNetwork -I/usr/local/Qt-5.4.0/include/QtGui -I/usr/local/Qt-5.4.0/include/QtCore -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/backward -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.1/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include SourceFiles/application.h -o GeneratedFiles/Debug/moc_application.cpp
GeneratedFiles/Debug/moc_audio.cpp: SourceFiles/types.h \
SourceFiles/audio.h \
../../Libraries/QtStatic/qtbase/include/QtCore/QReadWriteLock \
SourceFiles/logs.h \
../../Libraries/QtStatic/qtbase/include/QtCore/QTimer \
../../Libraries/QtStatic/qtbase/include/QtGui/QColor
/usr/local/Qt-5.4.0/bin/moc $(DEFINES) -D__APPLE__ -D__GNUC__=4 -I/usr/local/Qt-5.4.0/mkspecs/macx-clang -I. -I/usr/local/Qt-5.4.0/include/QtGui/5.4.0/QtGui -I/usr/local/Qt-5.4.0/include/QtCore/5.4.0/QtCore -I/usr/local/Qt-5.4.0/include -I./SourceFiles -I./GeneratedFiles -I../../Libraries/lzma/C -I../../Libraries/libexif-0.6.20 -I/usr/local/Qt-5.4.0/include -I/usr/local/Qt-5.4.0/include/QtMultimedia -I/usr/local/Qt-5.4.0/include/QtWidgets -I/usr/local/Qt-5.4.0/include/QtNetwork -I/usr/local/Qt-5.4.0/include/QtGui -I/usr/local/Qt-5.4.0/include/QtCore -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/backward -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.1/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include SourceFiles/audio.h -o GeneratedFiles/Debug/moc_audio.cpp
GeneratedFiles/Debug/moc_autoupdater.cpp: SourceFiles/types.h \
SourceFiles/autoupdater.h \
../../Libraries/QtStatic/qtbase/include/QtCore/QReadWriteLock \
SourceFiles/logs.h \
../../Libraries/QtStatic/qtbase/include/QtCore/QTimer \
../../Libraries/QtStatic/qtbase/include/QtGui/QColor
/usr/local/Qt-5.4.0/bin/moc $(DEFINES) -D__APPLE__ -D__GNUC__=4 -I/usr/local/Qt-5.4.0/mkspecs/macx-clang -I. -I/usr/local/Qt-5.4.0/include/QtGui/5.4.0/QtGui -I/usr/local/Qt-5.4.0/include/QtCore/5.4.0/QtCore -I/usr/local/Qt-5.4.0/include -I./SourceFiles -I./GeneratedFiles -I../../Libraries/lzma/C -I../../Libraries/libexif-0.6.20 -I/usr/local/Qt-5.4.0/include -I/usr/local/Qt-5.4.0/include/QtMultimedia -I/usr/local/Qt-5.4.0/include/QtWidgets -I/usr/local/Qt-5.4.0/include/QtNetwork -I/usr/local/Qt-5.4.0/include/QtGui -I/usr/local/Qt-5.4.0/include/QtCore -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/backward -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.1/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include SourceFiles/autoupdater.h -o GeneratedFiles/Debug/moc_autoupdater.cpp
GeneratedFiles/Debug/moc_dialogswidget.cpp: SourceFiles/dialogswidget.h
/usr/local/Qt-5.4.0/bin/moc $(DEFINES) -D__APPLE__ -D__GNUC__=4 -I/usr/local/Qt-5.4.0/mkspecs/macx-clang -I. -I/usr/local/Qt-5.4.0/include/QtGui/5.4.0/QtGui -I/usr/local/Qt-5.4.0/include/QtCore/5.4.0/QtCore -I/usr/local/Qt-5.4.0/include -I./SourceFiles -I./GeneratedFiles -I../../Libraries/lzma/C -I../../Libraries/libexif-0.6.20 -I/usr/local/Qt-5.4.0/include -I/usr/local/Qt-5.4.0/include/QtMultimedia -I/usr/local/Qt-5.4.0/include/QtWidgets -I/usr/local/Qt-5.4.0/include/QtNetwork -I/usr/local/Qt-5.4.0/include/QtGui -I/usr/local/Qt-5.4.0/include/QtCore -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/backward -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.1/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include SourceFiles/dialogswidget.h -o GeneratedFiles/Debug/moc_dialogswidget.cpp
@@ -569,6 +578,12 @@ GeneratedFiles/Debug/moc_sessionsbox.cpp: SourceFiles/boxes/abstractbox.h \
SourceFiles/boxes/sessionsbox.h
/usr/local/Qt-5.4.0/bin/moc $(DEFINES) -D__APPLE__ -D__GNUC__=4 -I/usr/local/Qt-5.4.0/mkspecs/macx-clang -I. -I/usr/local/Qt-5.4.0/include/QtGui/5.4.0/QtGui -I/usr/local/Qt-5.4.0/include/QtCore/5.4.0/QtCore -I/usr/local/Qt-5.4.0/include -I./SourceFiles -I./GeneratedFiles -I../../Libraries/lzma/C -I../../Libraries/libexif-0.6.20 -I/usr/local/Qt-5.4.0/include -I/usr/local/Qt-5.4.0/include/QtMultimedia -I/usr/local/Qt-5.4.0/include/QtWidgets -I/usr/local/Qt-5.4.0/include/QtNetwork -I/usr/local/Qt-5.4.0/include/QtGui -I/usr/local/Qt-5.4.0/include/QtCore -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/backward -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.1/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include SourceFiles/boxes/sessionsbox.h -o GeneratedFiles/Debug/moc_sessionsbox.cpp
GeneratedFiles/Debug/moc_stickersetbox.cpp: SourceFiles/boxes/abstractbox.h \
SourceFiles/gui/boxshadow.h \
SourceFiles/localimageloader.h \
SourceFiles/boxes/stickersetbox.h
/usr/local/Qt-5.4.0/bin/moc $(DEFINES) -D__APPLE__ -D__GNUC__=4 -I/usr/local/Qt-5.4.0/mkspecs/macx-clang -I. -I/usr/local/Qt-5.4.0/include/QtGui/5.4.0/QtGui -I/usr/local/Qt-5.4.0/include/QtCore/5.4.0/QtCore -I/usr/local/Qt-5.4.0/include -I./SourceFiles -I./GeneratedFiles -I../../Libraries/lzma/C -I../../Libraries/libexif-0.6.20 -I/usr/local/Qt-5.4.0/include -I/usr/local/Qt-5.4.0/include/QtMultimedia -I/usr/local/Qt-5.4.0/include/QtWidgets -I/usr/local/Qt-5.4.0/include/QtNetwork -I/usr/local/Qt-5.4.0/include/QtGui -I/usr/local/Qt-5.4.0/include/QtCore -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/backward -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.1/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include SourceFiles/boxes/stickersetbox.h -o GeneratedFiles/Debug/moc_stickersetbox.cpp
GeneratedFiles/Debug/moc_usernamebox.cpp: SourceFiles/boxes/abstractbox.h \
SourceFiles/gui/boxshadow.h \
SourceFiles/boxes/usernamebox.h

Binary file not shown.

View File

@@ -1,2 +1,2 @@
echo 8014 0.8.14 1
echo 8024 0.8.24 0
# AppVersion AppVersionStr DevChannel

View File

@@ -0,0 +1,35 @@
diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/mmdevapi.c
index cfd12d8..8a6f9fb 100644
--- a/Alc/backends/mmdevapi.c
+++ b/Alc/backends/mmdevapi.c
@@ -1719,7 +1719,7 @@ static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
{
- if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+ if(type == ALCbackend_Playback/* || type == ALCbackend_Capture*/)
return ALC_TRUE;
return ALC_FALSE;
}
diff --git a/Alc/backends/winmm.c b/Alc/backends/winmm.c
index 03805ab..77212c2 100644
--- a/Alc/backends/winmm.c
+++ b/Alc/backends/winmm.c
@@ -220,7 +220,7 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- while(GetMessage(&msg, NULL, 0, 0))
+ if (!self->killNow) while (GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WOM_DONE)
continue;
@@ -505,7 +505,7 @@ static int ALCwinmmCapture_captureProc(void *arg)
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
- while(GetMessage(&msg, NULL, 0, 0))
+ if (!self->killNow) while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WIM_DATA)
continue;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,812 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <windows.h>
#include <mmsystem.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "backends/base.h"
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
TYPEDEF_VECTOR(al_string, vector_al_string)
static vector_al_string PlaybackDevices;
static vector_al_string CaptureDevices;
static void clear_devlist(vector_al_string *list)
{
VECTOR_FOR_EACH(al_string, *list, al_string_deinit);
VECTOR_RESIZE(*list, 0);
}
static void ProbePlaybackDevices(void)
{
al_string *iter, *end;
ALuint numdevs;
ALuint i;
clear_devlist(&PlaybackDevices);
numdevs = waveOutGetNumDevs();
VECTOR_RESERVE(PlaybackDevices, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEOUTCAPSW WaveCaps;
al_string dname;
AL_STRING_INIT(dname);
if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
{
ALuint count = 0;
do {
al_string_copy_wcstr(&dname, WaveCaps.szPname);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
al_string_append_cstr(&dname, str);
}
count++;
iter = VECTOR_ITER_BEGIN(PlaybackDevices);
end = VECTOR_ITER_END(PlaybackDevices);
for(;iter != end;iter++)
{
if(al_string_cmp(*iter, dname) == 0)
break;
}
} while(iter != end);
TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(PlaybackDevices, dname);
}
}
static void ProbeCaptureDevices(void)
{
al_string *iter, *end;
ALuint numdevs;
ALuint i;
clear_devlist(&CaptureDevices);
numdevs = waveInGetNumDevs();
VECTOR_RESERVE(CaptureDevices, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEINCAPSW WaveCaps;
al_string dname;
AL_STRING_INIT(dname);
if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
{
ALuint count = 0;
do {
al_string_copy_wcstr(&dname, WaveCaps.szPname);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
al_string_append_cstr(&dname, str);
}
count++;
iter = VECTOR_ITER_BEGIN(CaptureDevices);
end = VECTOR_ITER_END(CaptureDevices);
for(;iter != end;iter++)
{
if(al_string_cmp(*iter, dname) == 0)
break;
}
} while(iter != end);
TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(CaptureDevices, dname);
}
}
typedef struct ALCwinmmPlayback {
DERIVE_FROM_TYPE(ALCbackend);
RefCount WaveBuffersCommitted;
WAVEHDR WaveBuffer[4];
HWAVEOUT OutHdl;
WAVEFORMATEX Format;
volatile ALboolean killNow;
althrd_t thread;
} ALCwinmmPlayback;
static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device);
static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self);
static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
static int ALCwinmmPlayback_mixerProc(void *arg);
static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name);
static void ALCwinmmPlayback_close(ALCwinmmPlayback *self);
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self);
static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCwinmmPlayback);
static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCwinmmPlayback, ALCbackend, self);
InitRef(&self->WaveBuffersCommitted, 0);
self->OutHdl = NULL;
self->killNow = AL_TRUE;
}
static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self)
{
if(self->OutHdl)
waveOutClose(self->OutHdl);
self->OutHdl = 0;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
/* ALCwinmmPlayback_waveOutProc
*
* Posts a message to 'ALCwinmmPlayback_mixerProc' everytime a WaveOut Buffer
* is completed and returns to the application (for more data)
*/
static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
{
ALCwinmmPlayback *self = (ALCwinmmPlayback*)instance;
if(msg != WOM_DONE)
return;
DecrementRef(&self->WaveBuffersCommitted);
PostThreadMessage(self->thread, msg, 0, param1);
}
FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
{
ALCwinmmPlayback *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
WAVEHDR *WaveHdr;
MSG msg;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
if (!self->killNow) while (GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WOM_DONE)
continue;
if(self->killNow)
{
if(ReadRef(&self->WaveBuffersCommitted) == 0)
break;
continue;
}
WaveHdr = ((WAVEHDR*)msg.lParam);
aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength /
self->Format.nBlockAlign);
// Send buffer back to play more data
waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR));
IncrementRef(&self->WaveBuffersCommitted);
}
return 0;
}
static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *deviceName)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const al_string *iter;
UINT DeviceID;
MMRESULT res;
if(VECTOR_SIZE(PlaybackDevices) == 0)
ProbePlaybackDevices();
// Find the Device ID matching the deviceName if valid
#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && \
(!deviceName || al_string_cmp_cstr(*(iter), deviceName) == 0))
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
if(iter == VECTOR_ITER_END(PlaybackDevices))
return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME
DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices));
retry_open:
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
if(device->FmtType == DevFmtFloat)
{
self->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
self->Format.wBitsPerSample = 32;
}
else
{
self->Format.wFormatTag = WAVE_FORMAT_PCM;
if(device->FmtType == DevFmtUByte || device->FmtType == DevFmtByte)
self->Format.wBitsPerSample = 8;
else
self->Format.wBitsPerSample = 16;
}
self->Format.nChannels = ((device->FmtChans == DevFmtMono) ? 1 : 2);
self->Format.nBlockAlign = self->Format.wBitsPerSample *
self->Format.nChannels / 8;
self->Format.nSamplesPerSec = device->Frequency;
self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
self->Format.nBlockAlign;
self->Format.cbSize = 0;
if((res=waveOutOpen(&self->OutHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmPlayback_waveOutProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
{
if(device->FmtType == DevFmtFloat)
{
device->FmtType = DevFmtShort;
goto retry_open;
}
ERR("waveOutOpen failed: %u\n", res);
goto failure;
}
al_string_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
return ALC_NO_ERROR;
failure:
if(self->OutHdl)
waveOutClose(self->OutHdl);
self->OutHdl = NULL;
return ALC_INVALID_VALUE;
}
static void ALCwinmmPlayback_close(ALCwinmmPlayback* UNUSED(self))
{ }
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
self->Format.nSamplesPerSec /
device->Frequency);
device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
device->NumUpdates = 4;
device->Frequency = self->Format.nSamplesPerSec;
if(self->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
{
if(self->Format.wBitsPerSample == 32)
device->FmtType = DevFmtFloat;
else
{
ERR("Unhandled IEEE float sample depth: %d\n", self->Format.wBitsPerSample);
return ALC_FALSE;
}
}
else if(self->Format.wFormatTag == WAVE_FORMAT_PCM)
{
if(self->Format.wBitsPerSample == 16)
device->FmtType = DevFmtShort;
else if(self->Format.wBitsPerSample == 8)
device->FmtType = DevFmtUByte;
else
{
ERR("Unhandled PCM sample depth: %d\n", self->Format.wBitsPerSample);
return ALC_FALSE;
}
}
else
{
ERR("Unhandled format tag: 0x%04x\n", self->Format.wFormatTag);
return ALC_FALSE;
}
if(self->Format.nChannels == 2)
device->FmtChans = DevFmtStereo;
else if(self->Format.nChannels == 1)
device->FmtChans = DevFmtMono;
else
{
ERR("Unhandled channel count: %d\n", self->Format.nChannels);
return ALC_FALSE;
}
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALbyte *BufferData;
ALint BufferSize;
ALuint i;
self->killNow = AL_FALSE;
if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success)
return ALC_FALSE;
InitRef(&self->WaveBuffersCommitted, 0);
// Create 4 Buffers
BufferSize = device->UpdateSize*device->NumUpdates / 4;
BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
BufferData = calloc(4, BufferSize);
for(i = 0;i < 4;i++)
{
memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
self->WaveBuffer[i].dwBufferLength = BufferSize;
self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
(self->WaveBuffer[i-1].lpData +
self->WaveBuffer[i-1].dwBufferLength));
waveOutPrepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
waveOutWrite(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
IncrementRef(&self->WaveBuffersCommitted);
}
return ALC_TRUE;
}
static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
{
void *buffer = NULL;
int i;
if(self->killNow)
return;
// Set flag to stop processing headers
self->killNow = AL_TRUE;
althrd_join(self->thread, &i);
// Release the wave buffers
for(i = 0;i < 4;i++)
{
waveOutUnprepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
if(i == 0) buffer = self->WaveBuffer[i].lpData;
self->WaveBuffer[i].lpData = NULL;
}
free(buffer);
}
typedef struct ALCwinmmCapture {
DERIVE_FROM_TYPE(ALCbackend);
RefCount WaveBuffersCommitted;
WAVEHDR WaveBuffer[4];
HWAVEIN InHdl;
RingBuffer *Ring;
WAVEFORMATEX Format;
volatile ALboolean killNow;
althrd_t thread;
} ALCwinmmCapture;
static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device);
static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self);
static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
static int ALCwinmmCapture_captureProc(void *arg);
static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name);
static void ALCwinmmCapture_close(ALCwinmmCapture *self);
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
DEFINE_ALCBACKEND_VTABLE(ALCwinmmCapture);
static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCwinmmCapture, ALCbackend, self);
InitRef(&self->WaveBuffersCommitted, 0);
self->InHdl = NULL;
self->killNow = AL_TRUE;
}
static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self)
{
if(self->InHdl)
waveInClose(self->InHdl);
self->InHdl = 0;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
/* ALCwinmmCapture_waveInProc
*
* Posts a message to 'ALCwinmmCapture_captureProc' everytime a WaveIn Buffer
* is completed and returns to the application (with more data).
*/
static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
{
ALCwinmmCapture *self = (ALCwinmmCapture*)instance;
if(msg != WIM_DATA)
return;
DecrementRef(&self->WaveBuffersCommitted);
PostThreadMessage(self->thread, msg, 0, param1);
}
static int ALCwinmmCapture_captureProc(void *arg)
{
ALCwinmmCapture *self = arg;
WAVEHDR *WaveHdr;
MSG msg;
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
if (!self->killNow) while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WIM_DATA)
continue;
/* Don't wait for other buffers to finish before quitting. We're
* closing so we don't need them. */
if(self->killNow)
break;
WaveHdr = ((WAVEHDR*)msg.lParam);
WriteRingBuffer(self->Ring, (ALubyte*)WaveHdr->lpData,
WaveHdr->dwBytesRecorded/self->Format.nBlockAlign);
// Send buffer back to capture more data
waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
IncrementRef(&self->WaveBuffersCommitted);
}
return 0;
}
static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const al_string *iter;
ALbyte *BufferData = NULL;
DWORD CapturedDataSize;
ALint BufferSize;
UINT DeviceID;
MMRESULT res;
ALuint i;
if(VECTOR_SIZE(CaptureDevices) == 0)
ProbeCaptureDevices();
// Find the Device ID matching the deviceName if valid
#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && (!name || al_string_cmp_cstr(*iter, name) == 0))
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
if(iter == VECTOR_ITER_END(CaptureDevices))
return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME
DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices));
switch(device->FmtChans)
{
case DevFmtMono:
case DevFmtStereo:
break;
case DevFmtQuad:
case DevFmtX51:
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
case DevFmtBFormat3D:
return ALC_INVALID_ENUM;
}
switch(device->FmtType)
{
case DevFmtUByte:
case DevFmtShort:
case DevFmtInt:
case DevFmtFloat:
break;
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
return ALC_INVALID_ENUM;
}
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
self->Format.nBlockAlign = self->Format.wBitsPerSample *
self->Format.nChannels / 8;
self->Format.nSamplesPerSec = device->Frequency;
self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
self->Format.nBlockAlign;
self->Format.cbSize = 0;
if((res=waveInOpen(&self->InHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmCapture_waveInProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
{
ERR("waveInOpen failed: %u\n", res);
goto failure;
}
// Allocate circular memory buffer for the captured audio
CapturedDataSize = device->UpdateSize*device->NumUpdates;
// Make sure circular buffer is at least 100ms in size
if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
CapturedDataSize = self->Format.nSamplesPerSec / 10;
self->Ring = CreateRingBuffer(self->Format.nBlockAlign, CapturedDataSize);
if(!self->Ring) goto failure;
InitRef(&self->WaveBuffersCommitted, 0);
// Create 4 Buffers of 50ms each
BufferSize = self->Format.nAvgBytesPerSec / 20;
BufferSize -= (BufferSize % self->Format.nBlockAlign);
BufferData = calloc(4, BufferSize);
if(!BufferData) goto failure;
for(i = 0;i < 4;i++)
{
memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
self->WaveBuffer[i].dwBufferLength = BufferSize;
self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
(self->WaveBuffer[i-1].lpData +
self->WaveBuffer[i-1].dwBufferLength));
self->WaveBuffer[i].dwFlags = 0;
self->WaveBuffer[i].dwLoops = 0;
waveInPrepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
waveInAddBuffer(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
IncrementRef(&self->WaveBuffersCommitted);
}
self->killNow = AL_FALSE;
if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
goto failure;
al_string_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
return ALC_NO_ERROR;
failure:
if(BufferData)
{
for(i = 0;i < 4;i++)
waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
free(BufferData);
}
if(self->Ring)
DestroyRingBuffer(self->Ring);
self->Ring = NULL;
if(self->InHdl)
waveInClose(self->InHdl);
self->InHdl = NULL;
return ALC_INVALID_VALUE;
}
static void ALCwinmmCapture_close(ALCwinmmCapture *self)
{
void *buffer = NULL;
int i;
/* Tell the processing thread to quit and wait for it to do so. */
self->killNow = AL_TRUE;
PostThreadMessage(self->thread, WM_QUIT, 0, 0);
althrd_join(self->thread, &i);
/* Make sure capture is stopped and all pending buffers are flushed. */
waveInReset(self->InHdl);
// Release the wave buffers
for(i = 0;i < 4;i++)
{
waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
if(i == 0) buffer = self->WaveBuffer[i].lpData;
self->WaveBuffer[i].lpData = NULL;
}
free(buffer);
DestroyRingBuffer(self->Ring);
self->Ring = NULL;
// Close the Wave device
waveInClose(self->InHdl);
self->InHdl = NULL;
}
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
{
waveInStart(self->InHdl);
return ALC_TRUE;
}
static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
{
waveInStop(self->InHdl);
}
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
{
ReadRingBuffer(self->Ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
{
return RingBufferSize(self->Ring);
}
static inline void AppendAllDevicesList2(const al_string *name)
{
if(!al_string_empty(*name))
AppendAllDevicesList(al_string_get_cstr(*name));
}
static inline void AppendCaptureDeviceList2(const al_string *name)
{
if(!al_string_empty(*name))
AppendCaptureDeviceList(al_string_get_cstr(*name));
}
typedef struct ALCwinmmBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCwinmmBackendFactory;
#define ALCWINMMBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwinmmBackendFactory, ALCbackendFactory) } }
static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory* UNUSED(self))
{
VECTOR_INIT(PlaybackDevices);
VECTOR_INIT(CaptureDevices);
return ALC_TRUE;
}
static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory* UNUSED(self))
{
clear_devlist(&PlaybackDevices);
VECTOR_DEINIT(PlaybackDevices);
clear_devlist(&CaptureDevices);
VECTOR_DEINIT(CaptureDevices);
}
static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
ProbePlaybackDevices();
VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
break;
case CAPTURE_DEVICE_PROBE:
ProbeCaptureDevices();
VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
break;
}
}
static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCwinmmPlayback *backend;
backend = ALCwinmmPlayback_New(sizeof(*backend));
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCwinmmPlayback_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
ALCwinmmCapture *backend;
backend = ALCwinmmCapture_New(sizeof(*backend));
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCwinmmCapture_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void)
{
static ALCwinmmBackendFactory factory = ALCWINMMBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}

View File

@@ -1,4 +1,4 @@
##Build instructions for Xcode 5.1.1
##Build instructions for Xcode 6.3.1
###Prepare folder
@@ -87,21 +87,9 @@ In Terminal go to **/Users/user/TBuild/Libraries/openal-soft/build** and there r
make
sudo make install
####libogg 1.3.2
####Opus codec
Get sources from http://xiph.org/downloads/ in [ZIP](http://downloads.xiph.org/releases/ogg/libogg-1.3.2.zip) and extract to **/Users/user/TBuild/Libraries**
#####Building library
In Terminal go to **/Users/user/TBuild/Libraries/libogg-1.3.2** and there run
./configure
make
sudo make install
####Opus codec, opusfile
Download sources [opus-1.1.tar.gz](http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz) and [opusfile-0.6.tar.gz](http://downloads.xiph.org/releases/opus/opusfile-0.6.tar.gz) from http://www.opus-codec.org/downloads/, extract to **/Users/user/TBuild/Libraries** and rename to have **/Users/user/TBuild/Libraries/opus/configure** and **/Users/user/TBuild/Libraries/opusfile/configure**
Download sources [opus-1.1.tar.gz](http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz) from http://www.opus-codec.org/downloads/, extract to **/Users/user/TBuild/Libraries** and rename to have **/Users/user/TBuild/Libraries/opus/configure**
#####Building libraries
@@ -119,9 +107,32 @@ then go to **/Users/user/TBuild/Libraries/opus** and there run
make
sudo make install
then go to **/Users/user/TBuild/Libraries/opusfile** and there run
####FFmpeg
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 **/Users/user/TBuild/Libraries** to have **/Users/user/TBuild/Libraries/ffmpeg-2.6.3**
#####Building libraries
Download [libiconv-1.14](http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz) from http://www.gnu.org/software/libiconv/#downloading, extract it to **/Users/user/TBuild/Libraries**
In Termianl go to **/Users/user/TBuild/Libraries/libiconv-1.14** and run
./configure --enable-static
make
sudo make install
Then in Terminal go to **/Users/user/TBuild/Libraries/ffmpeg-2.6.3** and run
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install automake fdk-aac git lame libass libtool libvorbis libvpx opus sdl shtool texi2html theora wget x264 xvid yasm
CFLAGS=`freetype-config --cflags`
LDFLAGS=`freetype-config --libs`
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/X11/lib/pkgconfig
./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 --extra-cflags="-mmacosx-version-min=10.7" --extra-cxxflags="-mmacosx-version-min=10.7" --extra-ldflags="-mmacosx-version-min=10.7"
./configure
make
sudo make install