Compare commits

...

82 Commits

Author SHA1 Message Date
John Preston
520d82b0ff beta version 9019002 2016-02-07 22:08:18 +03:00
John Preston
47ead03925 moved Interfaces to types.h, tooltip hiding fixed 2016-02-07 21:58:42 +03:00
John Preston
0ede06700a Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2016-02-07 18:38:57 +03:00
John Preston
0b4ddb045e max history width limited, custom tooltips replace QToolTip, keys with modifiers are not passed to MentionsDropdown 2016-02-07 18:38:49 +03:00
John Preston
980b50f8cf Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2016-02-07 13:51:47 +03:00
John Preston
dd0c79ee56 qt patch updated, os x tray icon fixed 2016-02-07 13:10:35 +03:00
John Preston
db9f47c6f2 fixed version in crash reporting for betas, fixed global app event filter install 2016-02-05 22:56:42 +03:00
John Preston
c240295a74 retina settings fixed for os x tray icon 2016-02-05 22:53:01 +03:00
John Preston
2b8ef0e9b2 fixed linux build for 9019001 beta 2016-02-05 18:58:51 +03:00
John Preston
759ede3c57 fixed linux launch with static linking 2016-02-05 17:32:19 +03:00
John Preston
6e8fc37ca9 using libs for google breakpad on windows 2016-02-05 16:31:24 +03:00
John Preston
3e30a11af8 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2016-02-05 13:22:03 +03:00
John Preston
cec7ebc24f fixed build script, 0.9.19.1 version in .rc 2016-02-05 13:21:57 +03:00
John Preston
f3a4b54601 fixed bugs in crash dump finding and in beta version 2016-02-05 01:16:25 +03:00
John Preston
91e1330b59 improved breakpad appstore patch 2016-02-04 20:45:53 +03:00
John Preston
952f24e4e1 beta version 9019001 with new crash tracking 2016-02-04 17:49:08 +03:00
John Preston
d9b09e78ad Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2016-02-04 15:44:58 +03:00
John Preston
fd0a119885 langs updated, build scripts updated 2016-02-04 15:44:39 +03:00
John Preston
837432a4f3 fixed connection on app state change 2016-02-03 17:01:34 +03:00
John Preston
ab00a0310a added crashing in settings 2016-02-02 19:21:07 +03:00
John Preston
b536c075ed Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2016-02-02 19:12:52 +03:00
John Preston
9d2b83c4d5 fixed release build for win 2016-02-02 19:12:45 +03:00
John Preston
02961ecc99 added mini_chromium patch for 10.6 and 10.8 os x google crashpad build 2016-02-02 19:00:46 +03:00
John Preston
f7f041f11d instructions updated for zlib 2016-02-02 15:50:12 +03:00
John Preston
3e1362628f conflicts fixed 2016-02-02 14:58:28 +03:00
John Preston
5940ae6411 google crashpad added to os x version 2016-02-02 14:48:46 +03:00
John Preston
44c12064a6 fixed crash reports for windows 2016-02-01 15:09:23 +03:00
John Preston
9f7b92eccd improved minidump choosing when sending crash report 2016-02-01 14:50:07 +03:00
John Preston
fa2767cc65 always writing tdata/working stacktrace when breakpad catches a crash 2016-02-01 14:09:11 +03:00
John Preston
b1f267e4dc added breakpad for linux 2016-02-01 13:39:51 +03:00
John Preston
daa0adfff9 added breakpad support for os x 2016-02-01 13:12:37 +03:00
John Preston
56fa8a0ee2 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2016-01-31 21:02:00 +03:00
John Preston
a0d171bb49 moved minizip to ThirdParty, added google breakpad to ThirdParty for windows (mac, linux broken) 2016-01-31 21:01:43 +03:00
John Preston
dd26de7dd2 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2016-01-31 21:01:05 +03:00
John Preston
fb4ee55ffa removed unused headers in mac 2016-01-31 21:01:01 +03:00
John Preston
17a319fdb3 improved crash reports for linux 2016-01-31 19:13:51 +03:00
John Preston
4fc0b439ae Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2016-01-31 14:32:45 +03:00
John Preston
74248b0284 improved os x crash reports, possible linux (and windows) broken 2016-01-31 14:32:29 +03:00
John Preston
b0681bc582 crash reports done in linux 2016-01-30 22:21:18 +03:00
John Preston
d28483fad4 windows and os x crash reports sent and shown 2016-01-30 21:38:33 +03:00
John Preston
12716a8c40 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2016-01-30 21:24:50 +03:00
John Preston
52c29bac5d started crash reporting for linux 2016-01-30 21:24:42 +03:00
John Preston
340246367d crash report and show done for os x 2016-01-30 21:24:18 +03:00
John Preston
8eef239b45 crash catch + report added for Windows version 2016-01-30 19:31:10 +03:00
John Preston
62c28cb58b fixed possible crash in History::addOlderSlice 2016-01-26 14:01:15 +03:00
John Preston
07c81db79a removed interfaces pointer by default from BasicInterface 2016-01-26 13:24:15 +03:00
John Preston
a677f784f5 up to 64 realtime combinated interfaces 2016-01-25 18:18:07 +03:00
John Preston
c8d7d23ee6 windows backtrace logging done, showing error when cant start telegram or previous launch was not finished properly 2016-01-25 13:22:58 +03:00
John Preston
0f4405dbaf backtrace output for win platform added, testing with abort() call in ~AppClass() 2016-01-21 14:58:58 +08:00
John Preston
d672353ff9 fixed single instance checking 2016-01-17 13:03:57 +08:00
John Preston
5f84567bbb initial logging improved, debug logs disabled for -many instance param 2016-01-17 13:01:14 +08:00
John Preston
d2f3fbe3f7 mentions regexp start with one letter instead of three 2016-01-17 07:28:54 +08:00
John Preston
58777dbc21 moved almost all Application to AppClass (autoupdate and singleinstance left), logs rewritten 2016-01-11 23:43:29 +08:00
John Preston
f3e560541b merged with master 2016-01-11 13:26:07 +08:00
John Preston
f6eae71397 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2016-01-11 13:22:52 +08:00
John Preston
a6861c3e94 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop 2016-01-11 13:20:16 +08:00
John Preston
fc02f96ef1 via @bot fixed in forwarded, stickers requested in stickersbox, sticker not autoselected in dropdown, forwarded messages to chats fixed from_id and via_bot_id 2016-01-11 12:45:07 +08:00
John Preston
71f588a4fe started signal handlers, shadow fixed in sticker-by-emoji, via @bot resize fixed 2016-01-10 14:05:23 +08:00
John Preston
810c60fd8c fixed build for old Qt version 2016-01-09 21:11:18 +08:00
John Preston
5531f49c3e moved instance id to Global namespace, 0.9.19 dev version 2016-01-09 21:00:50 +08:00
John Preston
325e45eafd Merge branch 'master' of https://github.com/telegramdesktop/tdesktop 2016-01-09 20:52:02 +08:00
John Preston
b13579c7d9 dev version 0.9.19 sending stickers by emoji 2016-01-09 20:51:42 +08:00
John Preston
a72a31e722 stickers by alt suggestions in mentions dropdown 2016-01-09 19:24:16 +08:00
John Preston
f66c54ee6b not loaded thumbs are now white, always initDimensions() when media is updated 2016-01-09 15:11:23 +08:00
telegramdesktop
f979067dda Merge pull request #1432 from telegramdesktop/auchri-patch-1
Add squash instructions to CONTRIBUTING.md
2016-01-09 12:26:43 +08:00
telegramdesktop
e1d932aaeb Merge pull request #1431 from L0L022/master
Use linux icon theme
2016-01-09 12:24:23 +08:00
John Preston
a39810a9c5 text render fixed when having text blocks that contain only spaces 2016-01-08 11:25:50 +08:00
John Preston
3078544892 new media unsupported message and via @inlinebot are added to langs, links don't break on / now, cancel upload langs fixed 2016-01-07 17:49:54 +08:00
John Preston
709c18e6fc fixed crash when deleting replyTo msg via inline bot 2016-01-06 18:21:07 +08:00
John Preston
931c249afe fixed frame duration count, 0.9.18 version 2016-01-05 15:42:36 +08:00
John Preston
47dee02de4 fixed warning, 0.9.18 2016-01-05 15:23:44 +08:00
John Preston
afb40b8289 ffmpeg threadsafety critical bug fixed, display gif time and status / views added, 0.9.18 2016-01-05 14:59:57 +08:00
John Preston
f2824f79f6 improved forwarded media display, 0.9.18 2016-01-05 14:11:18 +08:00
John Preston
2c7fb82708 fixed broken typing statuses, 0.9.18 2016-01-05 12:52:40 +08:00
John Preston
f4d159b2f0 version 0.9.18: crashfix + fix of transparent gifs sending 2016-01-05 12:28:06 +08:00
John Preston
b82adc7610 fixed crash, 0.9.17 version 2016-01-04 22:00:37 +08:00
Christoph Auer
61b7da069e Add instructions to squash commits to CONTRIBUTING.md 2015-12-24 21:47:50 +01:00
Escales Loïc
a6e82cf954 Use linux icon theme
Signed-off-by: Loïc Escales L0L022@openmailbox.org (github: L0L022)
2015-12-24 15:17:19 +01:00
telegramdesktop
b9bed419aa Merge pull request #1394 from heejune/dev
Fix a build error when TDESKTOP_DISABLE_AUTOUPDATE preprocessor defined.
2015-12-23 00:21:37 +03:00
Heejune Kim
4ee52afc4d Fix a build error when TDESKTOP_DISABLE_AUTOUPDATE preprocessor defined
Signed-off-by: Heejune Kim <heejune@gmail.com> (github: heejune)
2015-12-18 10:48:29 +09:00
telegramdesktop
76ca0acfd0 Merge pull request #1388 from heejune/dev
Resolving a bug #1247 (Sending files to channels)
2015-12-13 14:56:21 +03:00
Heejune Kim
42d9537ad1 Resolving a bug #1247 (Sending files to channels)
Signed-off-by: Heejune Kim <heejune@gmail.com> (github: heejune)
2015-12-13 20:40:57 +09:00
119 changed files with 10739 additions and 2949 deletions

View File

@@ -11,6 +11,7 @@ This document describes how you can contribute to Telegram Desktop. Please read
* [Pull upstream changes into your fork regularly](#pull-upstream-changes-into-your-fork-regularly)
* [How to get your pull request accepted](#how-to-get-your-pull-request-accepted)
* [Keep your pull requests limited to a single issue](#keep-your-pull-requests-limited-to-a-single-issue)
* [Squash your commits to a single commit](#squash-your-commits-to-a-single-commit)
* [Don't mix code changes with whitespace cleanup](#dont-mix-code-changes-with-whitespace-cleanup)
* [Keep your code simple!](#keep-your-code-simple)
* [Test your changes!](#test-your-changes)
@@ -107,6 +108,21 @@ Pull requests should be as small/atomic as possible. Large, wide-sweeping change
* If you are making spelling corrections in the docs, don't modify other files.
* If you are adding new functions don't '*cleanup*' unrelated functions. That cleanup belongs in another pull request.
#### Squash your commits to a single commit
To keep the history of the project clean, you should make one commit per pull request.
If you already have multiple commits, you can add the commits together (squash them) with the following commands in Git Bash:
1. Open `Git Bash` (or `Git Shell`)
2. Enter following command to squash the recent {N} commits: `git reset --soft HEAD~{N} && git commit` (replace `{N}` with the number of commits you want to squash)
3. Press <kbd>i</kbd> to get into Insert-mode
4. Enter the commit message of the new commit (and add the [signature](#sign-your-work) at the and)
5. After adding the message, press <kbd>ESC</kbd> to get out of the Insert-mode
6. Write `:wq` and press <kbd>Enter</kbd> to save the new message or write `:q!` to discard your changes
7. Enter `git push --force` to push the new commit to the remote repository
For example, if you want to squash the last 5 commits, use `git reset --soft HEAD~5 && git commit`
### Don't mix code changes with whitespace cleanup
If you change two lines of code and correct 200 lines of whitespace issues in a file the diff on that pull request is functionally unreadable and will be **rejected**. Whitespace cleanups need to be in their own pull request.

View File

@@ -32,6 +32,20 @@ Install dev libraries
sudo apt-get install libexif-dev liblzma-dev libz-dev libssl-dev libappindicator-dev libunity-dev
####zlib 1.2.8
http://www.zlib.net/ > Download [**zlib source code, version 1.2.8, zipfile format**](http://zlib.net/zlib128.zip)
Extract to **/home/user/TBuild/Libraries**
#####Building library
In Terminal go to **/home/user/TBuild/Libraries/zlib-1.2.8** and run:
./configure
make
sudo make install
Install audio libraries
####Opus codec 1.1

View File

@@ -20,6 +20,8 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
* libexif 0.6.20 ([LGPL](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html))
* LZMA SDK 9.20 ([public domain](http://www.7-zip.org/sdk.html))
* liblzma ([public domain](http://tukaani.org/xz/))
* Google Breakpad ([License](https://chromium.googlesource.com/breakpad/breakpad/+/master/LICENSE))
* Google Crashpad ([Apache License 2.0](https://chromium.googlesource.com/crashpad/crashpad/+/master/LICENSE))
* OpenAL Soft ([LGPL](http://kcat.strangesoft.net/openal.html))
* Opus codec ([BSD license](http://www.opus-codec.org/license/))
* FFmpeg ([LGPL](https://www.ffmpeg.org/legal.html))
@@ -46,21 +48,6 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
Compiles given files to single update file, compresses it with lzma and signs with a private key. It is not built in **Debug** and **Release** configurations of Telegram solution, because private key is inaccessible.
* ### Prepare
Prepares a release for deployment, puts all current files to deploy/{version} folder.
**Windows**:
* tsetup{version}.exe installer
* Telegram.exe
* Telegram.pdb (debug info for crash minidumps view)
* tupdate{updversion} binary lzma update archive
**Mac**:
* tsetup{version}.dmg
* Telegram.app
* tmacupd{updversion} binary lzma update archive
* ### MetaEmoji
Creates four sprites and text2emoji replace code

View File

@@ -29,6 +29,8 @@ set "HomePath=..\..\Telegram"
set "ReleasePath=..\Win32\Deploy"
set "DeployPath=%ReleasePath%\deploy\%AppVersionStrMajor%\%AppVersionStrFull%"
set "SignPath=..\..\TelegramPrivate\Sign.bat"
set "BinaryName=Telegram"
set "DropboxSymbolsPath=Z:\Dropbox\Telegram\symbols"
if %BetaVersion% neq 0 (
if exist %DeployPath%\ (
@@ -71,9 +73,13 @@ echo .
echo Version %AppVersionStrFull% build successfull. Preparing..
echo .
echo Dumping debug symbols..
call ..\..\Libraries\breakpad\src\tools\windows\binaries\dump_syms.exe %ReleasePath%\%BinaryName%.pdb > %ReleasePath%\%BinaryName%.sym
echo Done!
set "PATH=%PATH%;C:\Program Files\7-Zip;C:\Program Files (x86)\Inno Setup 5"
call %SignPath% %ReleasePath%\Telegram.exe
call %SignPath% %ReleasePath%\%BinaryName%.exe
if %errorlevel% neq 0 goto error
call %SignPath% %ReleasePath%\Updater.exe
@@ -90,7 +96,7 @@ if %BetaVersion% equ 0 (
)
cd %ReleasePath%
call Packer.exe -version %VersionForPacker% -path Telegram.exe -path Updater.exe %DevParam%
call Packer.exe -version %VersionForPacker% -path %BinaryName%.exe -path Updater.exe %DevParam%
cd %HomePath%
if %errorlevel% neq 0 goto error
@@ -109,6 +115,21 @@ if %BetaVersion% neq 0 (
set "PortableFile=tbeta%BetaVersion%_%BetaSignature%.zip"
)
for /f ^"usebackq^ eol^=^
^ delims^=^" %%a in (%ReleasePath%\%BinaryName%.sym) do (
set "SymbolsHashLine=%%a"
goto symbolslinedone
)
:symbolslinedone
FOR /F "tokens=1,2,3,4* delims= " %%i in ("%SymbolsHashLine%") do set "SymbolsHash=%%l"
echo Copying %BinaryName%.sym to %DropboxSymbolsPath%\%BinaryName%\%SymbolsHash%
if not exist %DropboxSymbolsPath%\%BinaryName% mkdir %DropboxSymbolsPath%\%BinaryName%
if not exist %DropboxSymbolsPath%\%BinaryName%\%SymbolsHash% mkdir %DropboxSymbolsPath%\%BinaryName%\%SymbolsHash%
xcopy %ReleasePath%\%BinaryName%.sym %DropboxSymbolsPath%\%BinaryName%\%SymbolsHash%\
echo Done!
if not exist %ReleasePath%\deploy mkdir %ReleasePath%\deploy
if not exist %ReleasePath%\deploy\%AppVersionStrMajor% mkdir %ReleasePath%\deploy\%AppVersionStrMajor%
mkdir %DeployPath%
@@ -144,7 +165,7 @@ if not exist %DeployPath%\%PortableFile% goto error
if %BetaVersion% equ 0 (
if not exist %DeployPath%\%SetupFile% goto error
)
if not exist %DeployPath%\Telegram.pdb goto error
if not exist %DeployPath%\%BinaryName%.pdb goto error
if not exist %DeployPath%\Updater.exe goto error
if not exist %DeployPath%\Updater.pdb goto error
if not exist %FinalReleasePath%\%AppVersionStrMajor% mkdir %FinalReleasePath%\%AppVersionStrMajor%
@@ -157,7 +178,7 @@ if %BetaVersion% equ 0 (
) else (
xcopy %DeployPath%\%BetaKeyFile% %FinalDeployPath%\ /Y
)
xcopy %DeployPath%\Telegram.pdb %FinalDeployPath%\
xcopy %DeployPath%\%BinaryName%.pdb %FinalDeployPath%\
xcopy %DeployPath%\Updater.exe %FinalDeployPath%\
xcopy %DeployPath%\Updater.pdb %FinalDeployPath%\

View File

@@ -37,6 +37,7 @@ if [ "$BuildTarget" == "linux" ]; then
WorkPath="./../Linux"
FixScript="$HomePath/FixMake.sh"
ReleasePath="./../Linux/Release"
BinaryName="Telegram"
elif [ "$BuildTarget" == "linux32" ]; then
echo "Building version $AppVersionStrFull for Linux 32bit.."
UpdateFile="tlinux32upd$AppVersion"
@@ -44,6 +45,7 @@ elif [ "$BuildTarget" == "linux32" ]; then
WorkPath="./../Linux"
FixScript="$HomePath/FixMake32.sh"
ReleasePath="./../Linux/Release"
BinaryName="Telegram"
elif [ "$BuildTarget" == "mac" ]; then
echo "Building version $AppVersionStrFull for OS X 10.8+.."
UpdateFile="tmacupd$AppVersion"
@@ -104,6 +106,9 @@ fi
#fi
if [ "$BuildTarget" == "linux" ] || [ "$BuildTarget" == "linux32" ]; then
DropboxSymbolsPath="/media/psf/Home/Dropbox/Telegram/symbols"
mkdir -p "$WorkPath/ReleaseIntermediateUpdater"
cd "$WorkPath/ReleaseIntermediateUpdater"
/usr/local/Qt-5.5.1/bin/qmake "$HomePath/Updater.pro"
@@ -118,8 +123,8 @@ if [ "$BuildTarget" == "linux" ] || [ "$BuildTarget" == "linux32" ]; then
make
echo "Telegram build complete!"
cd "$HomePath"
if [ ! -f "$ReleasePath/Telegram" ]; then
echo "Telegram not found!"
if [ ! -f "$ReleasePath/$BinaryName" ]; then
echo "$BinaryName not found!"
exit 1
fi
@@ -128,8 +133,16 @@ if [ "$BuildTarget" == "linux" ] || [ "$BuildTarget" == "linux32" ]; then
exit 1
fi
echo "Dumping debug symbols.."
"./../../Libraries/breakpad/src/tools/linux/dump_syms/dump_syms" "$ReleasePath/$BinaryName" > "$ReleasePath/$BinaryName.sym"
echo "Done!"
echo "Stripping the executable.."
strip -s "$ReleasePath/$BinaryName"
echo "Done!"
echo "Preparing version $AppVersionStrFull, executing Packer.."
cd "$ReleasePath" && "./Packer" -path Telegram -path Updater -version $VersionForPacker $DevParam && cd "$HomePath"
cd "$ReleasePath" && "./Packer" -path "$BinaryName" -path Updater -version $VersionForPacker $DevParam && cd "$HomePath"
echo "Packer done!"
if [ "$BetaVersion" != "0" ]; then
@@ -146,6 +159,12 @@ if [ "$BuildTarget" == "linux" ] || [ "$BuildTarget" == "linux32" ]; then
SetupFile="tbeta${BetaVersion}_${BetaSignature}.tar.xz"
fi
SymbolsHash=`head -n 1 "$ReleasePath/$BinaryName.sym" | awk -F " " 'END {print $4}'`
echo "Copying $BinaryName.sym to $DropboxSymbolsPath/$BinaryName/$SymbolsHash"
mkdir -p "$DropboxSymbolsPath/$BinaryName/$SymbolsHash"
cp "$ReleasePath/$BinaryName.sym" "$DropboxSymbolsPath/$BinaryName/$SymbolsHash/"
echo "Done!"
if [ ! -d "$ReleasePath/deploy" ]; then
mkdir "$ReleasePath/deploy"
fi
@@ -154,10 +173,10 @@ if [ "$BuildTarget" == "linux" ] || [ "$BuildTarget" == "linux32" ]; then
mkdir "$ReleasePath/deploy/$AppVersionStrMajor"
fi
echo "Copying Telegram, Updater and $UpdateFile to deploy/$AppVersionStrMajor/$AppVersionStrFull..";
echo "Copying $BinaryName, Updater and $UpdateFile to deploy/$AppVersionStrMajor/$AppVersionStrFull..";
mkdir "$DeployPath"
mkdir "$DeployPath/Telegram"
mv "$ReleasePath/Telegram" "$DeployPath/Telegram/"
mkdir "$DeployPath/$BinaryName"
mv "$ReleasePath/$BinaryName" "$DeployPath/Telegram/"
mv "$ReleasePath/Updater" "$DeployPath/Telegram/"
mv "$ReleasePath/$UpdateFile" "$DeployPath/"
if [ "$BetaVersion" != "0" ]; then
@@ -168,6 +187,8 @@ fi
if [ "$BuildTarget" == "mac" ] || [ "$BuildTarget" == "mac32" ] || [ "$BuildTarget" == "macstore" ]; then
DropboxSymbolsPath="./../../../Dropbox/Telegram/symbols"
touch "./SourceFiles/telegram.qrc"
xcodebuild -project Telegram.xcodeproj -alltargets -configuration Release build
@@ -181,6 +202,28 @@ if [ "$BuildTarget" == "mac" ] || [ "$BuildTarget" == "mac32" ] || [ "$BuildTarg
exit 1
fi
if [ "$BuildTarget" == "mac" ] || [ "$BuildTarget" == "mac32" ]; then
echo "Removing Updater debug symbols.."
rm -rf "$ReleasePath/$BinaryName.app/Contents/Frameworks/Updater.dSYM"
echo "Done!"
fi
echo "Dumping debug symbols.."
"./../../Libraries/breakpad/src/tools/mac/dump_syms/build/Release/dump_syms" "$ReleasePath/$BinaryName.app.dSYM" > "$ReleasePath/$BinaryName.sym" 2>/dev/null
echo "Done!"
echo "Stripping the executable.."
strip "$ReleasePath/$BinaryName.app/Contents/MacOS/$BinaryName"
echo "Done!"
echo "Signing the application.."
if [ "$BuildTarget" == "mac" ] || [ "$BuildTarget" == "mac32" ]; then
codesign --force --deep --sign "Developer ID Application: John Preston" "$ReleasePath/$BinaryName.app"
elif [ "$BuildTarget" == "macstore" ]; then
codesign --force --deep --sign "3rd Party Mac Developer Application: TELEGRAM MESSENGER LLP (6N38VWS5BX)" "$ReleasePath/$BinaryName.app" --entitlements "Telegram/Telegram Desktop.entitlements"
fi
echo "Done!"
AppUUID=`dwarfdump -u "$ReleasePath/$BinaryName.app/Contents/MacOS/$BinaryName" | awk -F " " '{print $2}'`
DsymUUID=`dwarfdump -u "$ReleasePath/$BinaryName.app.dSYM" | awk -F " " '{print $2}'`
if [ "$AppUUID" != "$DsymUUID" ]; then
@@ -215,6 +258,12 @@ if [ "$BuildTarget" == "mac" ] || [ "$BuildTarget" == "mac32" ] || [ "$BuildTarg
fi
fi
SymbolsHash=`head -n 1 "$ReleasePath/$BinaryName.sym" | awk -F " " 'END {print $4}'`
echo "Copying $BinaryName.sym to $DropboxSymbolsPath/$BinaryName/$SymbolsHash"
mkdir -p "$DropboxSymbolsPath/$BinaryName/$SymbolsHash"
cp "$ReleasePath/$BinaryName.sym" "$DropboxSymbolsPath/$BinaryName/$SymbolsHash/"
echo "Done!"
if [ "$BuildTarget" == "mac" ] || [ "$BuildTarget" == "mac32" ]; then
if [ "$BetaVersion" == "0" ]; then
cd "$ReleasePath"
@@ -294,6 +343,7 @@ if [ "$BuildTarget" == "mac" ] || [ "$BuildTarget" == "mac32" ] || [ "$BuildTarg
rm "$ReleasePath/$BinaryName.app/Contents/MacOS/$BinaryName"
rm -rf "$ReleasePath/$BinaryName.app/Contents/_CodeSignature"
mkdir -p "$DropboxDeployPath"
cp -v "$DeployPath/$BinaryName.app" "$DropboxDeployPath/"
cp -rv "$DeployPath/$BinaryName.app.dSYM" "$DropboxDeployPath/"
fi

View File

@@ -165,13 +165,7 @@ fi
fi
fi
if [ ! -d "$DropboxPath" ]; then
mkdir "$DropboxPath"
fi
if [ ! -d "$DropboxDeployPath" ]; then
mkdir "$DropboxDeployPath"
fi
mkdir -p "$DropboxDeployPath"
fi
#fi

View File

@@ -11,10 +11,8 @@ Replace () {
}
Replace '\-llzma' '\/usr\/lib\/x86_64\-linux\-gnu\/liblzma\.a'
Replace '\-lz' '\/usr\/lib\/x86_64\-linux\-gnu\/libz\.a'
Replace '\-lssl' '\/usr\/lib\/x86_64\-linux\-gnu\/libssl\.a'
Replace '\-lcrypto' '\/usr\/lib\/x86_64\-linux\-gnu\/libcrypto\.a'
Replace '\-lexif' '\/usr\/lib\/x86_64\-linux\-gnu\/libexif\.a'
Replace '\-lgobject\-2\.0' '\/usr\/lib\/x86_64\-linux\-gnu\/libgobject\-2\.0\.a \/usr\/lib\/x86_64\-linux\-gnu\/libffi\.a'
Replace '\-lXi' '\/usr\/lib\/x86_64\-linux\-gnu\/libXi\.a'
Replace '\-lSM' '\/usr\/lib\/x86_64\-linux\-gnu\/libSM\.a'

View File

@@ -498,7 +498,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_sure_delete_group" = "Are you sure, you want to delete this group? All members will be removed and all messages will be lost.";
"lng_message_empty" = "Empty Message";
"lng_media_unsupported" = "Media Unsupported";
"lng_message_unsupported" = "This message is not supported by your version of Telegram Desktop. Please update to the last version in Settings or install it from {link}";
"lng_action_add_user" = "{from} added {user}";
"lng_action_add_users_many" = "{from} added {users}";
@@ -600,6 +600,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_saved_gifs" = "Saved GIFs";
"lng_inline_bot_results" = "Results from {inline_bot}";
"lng_inline_bot_no_results" = "No results";
"lng_inline_bot_via" = "via {inline_bot}";
"lng_box_remove" = "Remove";
@@ -793,7 +794,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_selected_delete" = "Delete";
"lng_selected_forward" = "Forward";
"lng_selected_count" = "{count:_not_used_|# message|# messages}";
"lng_selected_cancel_sure_this" = "Do you want to cancel this upload?";
"lng_selected_cancel_sure_this" = "Cancel uploading?";
"lng_selected_upload_stop" = "Stop";
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
"lng_selected_delete_sure" = "Do you want to delete {count:_not_used_|# message|# messages}?";
"lng_box_delete" = "Delete";

View File

@@ -269,6 +269,21 @@ defaultPopupMenu: PopupMenu {
widthMin: 180px;
widthMax: 300px;
}
defaultTooltip: Tooltip {
textBg: #eef2f5;
textFg: #5d6c80;
textFont: normalFont;
textBorder: #c9d1db;
textPadding: margins(5px, 2px, 5px, 2px);
shift: point(-20px, 20px);
skip: 10px;
widthMax: 800px;
linesMax: 12;
}
almostTransparent: #ffffff0d;
boxScroll: flatScroll(solidScroll) {
width: 18px;
@@ -999,6 +1014,8 @@ historyToEndSkip: 10px;
activeFadeInDuration: 500;
activeFadeOutDuration: 3000;
historyMaxWidth: 640px;
msgRadius: 3px;
msgMaxWidth: 430px;

View File

@@ -272,6 +272,20 @@ PopupMenu {
widthMax: number;
}
Tooltip {
textBg: color;
textFg: color;
textFont: font;
textBorder: color;
textPadding: margins;
shift: point;
skip: number;
widthMax: number;
linesMax: number;
}
botKeyboardButton {
margin: number;
padding: number;

View File

@@ -333,9 +333,8 @@ void updateRegistry() {
int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdParamarg, int cmdShow) {
openLog();
#ifdef _NEED_WIN_GENERATE_DUMP
_oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter);
#endif
// CAPIHook apiHook("kernel32.dll", "SetUnhandledExceptionFilter", (PROC)RedirectedSetUnhandledExceptionFilter);
writeLog(L"Updaters started..");
@@ -465,7 +464,6 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
return 0;
}
#ifdef _NEED_WIN_GENERATE_DUMP
static const WCHAR *_programName = L"Telegram Desktop"; // folder in APPDATA, if current path is unavailable for writing
static const WCHAR *_exeName = L"Updater.exe";
@@ -507,10 +505,10 @@ HANDLE _generateDumpFileAtPath(const WCHAR *path) {
GetLocalTime(&stLocalTime);
wsprintf(szFileName, L"%s%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
szPath, szExeName, updaterVersionStr,
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
wsprintf(szFileName, L"%s%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
szPath, szExeName, updaterVersionStr,
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
GetCurrentProcessId(), GetCurrentThreadId());
return CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
}
@@ -546,7 +544,7 @@ void _generateDump(EXCEPTION_POINTERS* pExceptionPointers) {
hDumpFile = _generateDumpFileAtPath(wstrPath);
}
}
if (!hDumpFile || hDumpFile == INVALID_HANDLE_VALUE) {
return;
}
@@ -564,4 +562,10 @@ LONG CALLBACK _exceptionFilter(EXCEPTION_POINTERS* pExceptionPointers) {
return _oldWndExceptionFilter ? (*_oldWndExceptionFilter)(pExceptionPointers) : EXCEPTION_CONTINUE_SEARCH;
}
#endif
// see http://www.codeproject.com/Articles/154686/SetUnhandledExceptionFilter-and-the-C-C-Runtime-Li
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI RedirectedSetUnhandledExceptionFilter(_In_opt_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) {
// When the CRT calls SetUnhandledExceptionFilter with NULL parameter
// our handler will not get removed.
_oldWndExceptionFilter = lpTopLevelExceptionFilter;
return 0;
}

View File

@@ -32,12 +32,9 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
using std::deque;
using std::wstring;
#define _NEED_WIN_GENERATE_DUMP
#ifdef _NEED_WIN_GENERATE_DUMP
extern LPTOP_LEVEL_EXCEPTION_FILTER _oldWndExceptionFilter;
LONG CALLBACK _exceptionFilter(EXCEPTION_POINTERS* pExceptionPointers);
#endif _NEED_WIN_GENERATE_DUMP
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI RedirectedSetUnhandledExceptionFilter(_In_opt_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
static int updaterVersion = 1000;
static const WCHAR *updaterVersionStr = L"0.1.0";

View File

@@ -324,7 +324,7 @@ bool update() {
int main(int argc, char *argv[]) {
bool needupdate = true, autostart = false, debug = false, tosettings = false, startintray = false, testmode = false;
char *key = 0;
char *key = 0, *crashreport = 0;
for (int i = 1; i < argc; ++i) {
if (equal(argv[i], "-noupdate")) {
needupdate = false;
@@ -342,7 +342,9 @@ int main(int argc, char *argv[]) {
key = argv[i];
} else if (equal(argv[i], "-workpath") && ++i < argc) {
workDir = argv[i];
}
} else if (equal(argv[i], "-crashreport") && ++i < argc) {
crashreport = argv[i];
}
}
openLog();
@@ -408,17 +410,20 @@ int main(int argc, char *argv[]) {
char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key", p_startintray[] = "-startintray", p_testmode[] = "-testmode";
int argIndex = 0;
args[argIndex++] = path;
args[argIndex++] = p_noupdate;
if (autostart) args[argIndex++] = p_autostart;
if (debug) args[argIndex++] = p_debug;
if (startintray) args[argIndex++] = p_startintray;
if (testmode) args[argIndex++] = p_testmode;
if (tosettings) args[argIndex++] = p_tosettings;
if (key) {
args[argIndex++] = p_key;
args[argIndex++] = key;
}
if (crashreport) {
args[argIndex++] = crashreport;
} else {
args[argIndex++] = p_noupdate;
if (autostart) args[argIndex++] = p_autostart;
if (debug) args[argIndex++] = p_debug;
if (startintray) args[argIndex++] = p_startintray;
if (testmode) args[argIndex++] = p_testmode;
if (tosettings) args[argIndex++] = p_tosettings;
if (key) {
args[argIndex++] = p_key;
args[argIndex++] = key;
}
}
pid_t pid = fork();
switch (pid) {
case -1: writeLog("fork() failed!"); return 1;

View File

@@ -23,6 +23,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
NSString *appName = @"Telegram.app";
NSString *appDir = nil;
NSString *workDir = nil;
NSString *crashReportArg = nil;
#ifdef _DEBUG
BOOL _debug = YES;
@@ -101,6 +102,10 @@ int main(int argc, const char * argv[]) {
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
procId = [[formatter numberFromString:[NSString stringWithUTF8String:argv[i]]] intValue];
}
} else if ([@"-crashreport" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
if (++i < argc) {
crashReportArg = [NSString stringWithUTF8String:argv[i]];
}
} else if ([@"-noupdate" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
update = NO;
} else if ([@"-tosettings" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
@@ -214,15 +219,17 @@ int main(int argc, const char * argv[]) {
}
NSString *appPath = [[NSArray arrayWithObjects:appDir, appRealName, nil] componentsJoinedByString:@""];
NSMutableArray *args = [[NSMutableArray alloc] initWithObjects:@"-noupdate", nil];
if (toSettings) [args addObject:@"-tosettings"];
if (_debug) [args addObject:@"-debug"];
if (startInTray) [args addObject:@"-startintray"];
if (testMode) [args addObject:@"-testmode"];
if (autoStart) [args addObject:@"-autostart"];
if (key) {
[args addObject:@"-key"];
[args addObject:key];
NSMutableArray *args = [[NSMutableArray alloc] initWithObjects: crashReportArg ? crashReportArg : @"-noupdate", nil];
if (!crashReportArg) {
if (toSettings) [args addObject:@"-tosettings"];
if (_debug) [args addObject:@"-debug"];
if (startInTray) [args addObject:@"-startintray"];
if (testMode) [args addObject:@"-testmode"];
if (autoStart) [args addObject:@"-autostart"];
if (key) {
[args addObject:@"-key"];
[args addObject:key];
}
}
writeLog([[NSArray arrayWithObjects:@"Running application '", appPath, @"' with args '", [args componentsJoinedByString:@"' '"], @"'..", nil] componentsJoinedByString:@""]);
NSError *error = nil;

View File

@@ -430,7 +430,7 @@ void ApiWrap::requestBots(ChannelData *peer) {
void ApiWrap::gotChat(PeerData *peer, const MTPmessages_Chats &result) {
_peerRequests.remove(peer);
if (result.type() == mtpc_messages_chats) {
const QVector<MTPChat> &v(result.c_messages_chats().vchats.c_vector().v);
bool badVersion = false;
@@ -672,9 +672,9 @@ void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) {
void ApiWrap::requestStickerSets() {
for (QMap<uint64, QPair<uint64, mtpRequestId> >::iterator i = _stickerSetRequests.begin(), j = i, e = _stickerSetRequests.end(); i != e; i = j) {
++j;
if (i.value().second) continue;
++j;
int32 wait = (j == e) ? 0 : 10;
i.value().second = MTP::send(MTPmessages_GetStickerSet(MTP_inputStickerSetID(MTP_long(i.key()), MTP_long(i.value().first))), rpcDone(&ApiWrap::gotStickerSet, i.key()), rpcFail(&ApiWrap::gotStickerSetFail, i.key()), 0, wait);
}
@@ -682,10 +682,10 @@ void ApiWrap::requestStickerSets() {
void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
_stickerSetRequests.remove(setId);
if (result.type() != mtpc_messages_stickerSet) return;
const MTPDmessages_stickerSet &d(result.c_messages_stickerSet());
if (d.vset.type() != mtpc_stickerSet) return;
const MTPDstickerSet &s(d.vset.c_stickerSet());
@@ -731,12 +731,32 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result)
++i;
}
}
if (pack.isEmpty()) {
int32 removeIndex = cStickerSetsOrder().indexOf(setId);
if (removeIndex >= 0) cRefStickerSetsOrder().removeAt(removeIndex);
sets.erase(it);
} else {
it->stickers = pack;
it->emoji.clear();
const QVector<MTPStickerPack> &v(d.vpacks.c_vector().v);
for (int32 i = 0, l = v.size(); i < l; ++i) {
if (v.at(i).type() != mtpc_stickerPack) continue;
const MTPDstickerPack &pack(v.at(i).c_stickerPack());
if (EmojiPtr e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) {
const QVector<MTPlong> &stickers(pack.vdocuments.c_vector().v);
StickerPack p;
p.reserve(stickers.size());
for (int32 j = 0, c = stickers.size(); j < c; ++j) {
DocumentData *doc = App::document(stickers.at(j).v);
if (!doc || !doc->sticker()) continue;
p.push_back(doc);
}
it->emoji.insert(e, p);
}
}
}
if (writeRecent) {

View File

@@ -25,8 +25,9 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "application.h"
#include "fileuploader.h"
#include "mainwidget.h"
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
#include <libexif/exif-data.h>
#endif
#include "localstorage.h"
#include "numbers.h"
@@ -151,12 +152,12 @@ namespace App {
return result;
}
Application *app() {
return Application::app();
AppClass *app() {
return AppClass::app();
}
Window *wnd() {
return Application::wnd();
return AppClass::wnd();
}
MainWidget *main() {
@@ -902,12 +903,12 @@ namespace App {
}
if (HistoryItem *existing = App::histItemById(peerToChannel(peerId), m.vid.v)) {
existing->setText(qs(m.vmessage), m.has_entities() ? entitiesFromMTP(m.ventities.c_vector().v) : EntitiesInText());
existing->updateMedia(m.has_media() ? (&m.vmedia) : 0);
existing->setViewsCount(m.has_views() ? m.vviews.v : -1, false);
existing->initDimensions();
Notify::historyItemResized(existing);
existing->updateMedia(m.has_media() ? (&m.vmedia) : 0, true);
existing->addToOverview(AddToOverviewNew);
existing->setViewsCount(m.has_views() ? m.vviews.v : -1);
if (!existing->detached()) {
App::checkSavedGif(existing);
@@ -929,11 +930,13 @@ namespace App {
Local::writeSavedGifs();
if (App::main()) emit App::main()->savedGifsUpdated();
cSetLastSavedGifsUpdate(0);
App::main()->updateStickers();
}
}
void checkSavedGif(HistoryItem *item) {
if (!item->toHistoryForwarded() && item->out()) {
if (!item->toHistoryForwarded() && (item->out() || item->history()->peer == App::self())) {
if (HistoryMedia *media = item->getMedia()) {
if (DocumentData *doc = media->getDocument()) {
if (doc->isGifv()) {
@@ -2277,9 +2280,7 @@ namespace App {
if (wnd()) {
wnd()->quit();
}
if (app()) {
app()->quit();
}
Application::quit();
}
bool quiting() {
@@ -2610,9 +2611,14 @@ namespace App {
}
QNetworkProxy getHttpProxySettings() {
if (cConnectionType() == dbictHttpProxy) {
const ConnectionProxy &p(cConnectionProxy());
return QNetworkProxy(QNetworkProxy::HttpProxy, p.host, p.port, p.user, p.password);
const ConnectionProxy *proxy = 0;
if (Sandbox::started()) {
proxy = (cConnectionType() == dbictHttpProxy) ? (&cConnectionProxy()) : 0;
} else {
proxy = Global::PreLaunchProxy().host.isEmpty() ? 0 : (&Global::PreLaunchProxy());
}
if (proxy) {
return QNetworkProxy(QNetworkProxy::HttpProxy, proxy->host, proxy->port, proxy->user, proxy->password);
}
return QNetworkProxy(QNetworkProxy::DefaultProxy);
}

View File

@@ -22,7 +22,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "types.h"
class Application;
class AppClass;
class Window;
class MainWidget;
class SettingsWidget;
@@ -59,7 +59,7 @@ struct ReplyMarkup {
class LayeredWidget;
namespace App {
Application *app();
AppClass *app();
Window *wnd();
MainWidget *main();
SettingsWidget *settings();

File diff suppressed because it is too large Load Diff

View File

@@ -20,31 +20,52 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QNetworkReply>
#include "window.h"
#include "pspecific.h"
class MainWidget;
class FileUploader;
class Translator;
class UpdateDownloader;
class Application : public PsApplication, public RPCSender {
class UpdateChecker;
class Application : public QApplication {
Q_OBJECT
public:
Application(int &argc, char **argv);
~Application();
static Application *app();
static Window *wnd();
static QString language();
static int32 languageId();
static MainWidget *main();
// Single instance application
public slots:
void socketConnected();
void socketError(QLocalSocket::LocalSocketError e);
void socketDisconnected();
void socketWritten(qint64 bytes);
void socketReading();
void newInstanceConnected();
void closeApplication();
void readClients();
void removeClients();
private:
typedef QPair<QLocalSocket*, QByteArray> LocalClient;
typedef QList<LocalClient> LocalClients;
QString _localServerName, _localSocketReadData;
QLocalServer _localServer;
QLocalSocket _localSocket;
LocalClients _localClients;
bool _secondInstance;
void singleInstanceChecked();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
// Autoupdating
public:
void startUpdateCheck(bool forceWait);
void stopUpdate();
enum UpdatingState {
UpdatingNone,
@@ -52,11 +73,87 @@ public:
UpdatingReady,
};
UpdatingState updatingState();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
int32 updatingSize();
int32 updatingReady();
signals:
void updateChecking();
void updateLatest();
void updateProgress(qint64 ready, qint64 total);
void updateReady();
void updateFailed();
public slots:
void updateCheck();
void updateGotCurrent();
void updateFailedCurrent(QNetworkReply::NetworkError e);
void onUpdateReady();
void onUpdateFailed();
private:
SingleTimer _updateCheckTimer;
QNetworkReply *_updateReply;
QNetworkAccessManager _updateManager;
QThread *_updateThread;
UpdateChecker *_updateChecker;
#endif
};
namespace Sandboxer {
QRect availableGeometry();
QRect screenGeometry(const QPoint &p);
void setActiveWindow(QWidget *window);
bool isSavingSession();
void installEventFilter(QObject *filter);
void execExternal(const QString &cmd);
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void startUpdateCheck();
void stopUpdate();
#endif
Application::UpdatingState updatingState();
int32 updatingSize();
int32 updatingReady();
void updateChecking();
void updateLatest();
void updateProgress(qint64 ready, qint64 total);
void updateFailed();
void updateReady();
#endif
void connect(const char *signal, QObject *object, const char *method);
void startSandbox();
}
class MainWidget;
class FileUploader;
class Translator;
class AppClass : public QObject, public RPCSender {
Q_OBJECT
public:
AppClass();
~AppClass();
static AppClass *app();
static Window *wnd();
static MainWidget *main();
FileUploader *uploader();
void uploadProfilePhoto(const QImage &tosend, const PeerId &peerId);
@@ -85,14 +182,6 @@ public:
signals:
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void updateChecking();
void updateLatest();
void updateDownloading(qint64 ready, qint64 total);
void updateReady();
void updateFailed();
#endif
void peerPhotoDone(PeerId peer);
void peerPhotoFail(PeerId peer);
@@ -100,30 +189,8 @@ signals:
public slots:
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void startUpdateCheck(bool forceWait = false);
#endif
void socketConnected();
void socketError(QLocalSocket::LocalSocketError e);
void socketDisconnected();
void socketWritten(qint64 bytes);
void socketReading();
void newInstanceConnected();
void closeApplication();
void doMtpUnpause();
void readClients();
void removeClients();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void updateGotCurrent();
void updateFailedCurrent(QNetworkReply::NetworkError e);
void onUpdateReady();
void onUpdateFailed();
#endif
void photoUpdated(const FullMsgId &msgId, const MTPInputFile &file);
void onSwitchDebugMode();
@@ -139,35 +206,12 @@ private:
QMap<int32, uint64> killDownloadSessionTimes;
SingleTimer killDownloadSessionsTimer;
void startApp();
uint64 _lastActionTime;
typedef QPair<QLocalSocket*, QByteArray> ClientSocket;
typedef QVector<ClientSocket> ClientSockets;
QString serverName;
QLocalSocket socket;
QString socketRead;
QLocalServer server;
ClientSockets clients;
bool closing;
uint64 lastActionTime;
void execExternal(const QString &cmd);
Window *window;
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
mtpRequestId updateRequestId;
QNetworkAccessManager updateManager;
QNetworkReply *updateReply;
SingleTimer updateCheckTimer;
QThread *updateThread;
UpdateDownloader *updateDownloader;
#endif
Window _window;
FileUploader *_uploader;
Translator *_translator;
SingleTimer _mtpUnpauseTimer;
Translator *_translator;
};

View File

@@ -97,11 +97,6 @@ bool _checkALError() {
Q_DECLARE_METATYPE(AudioMsgId);
Q_DECLARE_METATYPE(SongMsgId);
void audioInit() {
if (!audioDevice) {
av_register_all();
avcodec_register_all();
}
if (!capture) {
capture = new AudioCapture();
cSetHasAudioCapture(capture->check());
@@ -114,7 +109,7 @@ void audioInit() {
LOG(("Audio Error: default sound device not present."));
return;
}
ALCint attributes[] = { ALC_STEREO_SOURCES, 8, 0 };
audioContext = alcCreateContext(audioDevice, attributes);
alcMakeContextCurrent(audioContext);
@@ -516,7 +511,7 @@ void AudioPlayer::play(const SongMsgId &song, int64 position) {
bool AudioPlayer::checkCurrentALError(MediaOverviewType type) {
if (_checkALError()) return true;
switch (type) {
case OverviewAudios:
setStoppedState(&_audioData[_audioCurrent], AudioPlayerStoppedAtError);
@@ -1091,7 +1086,7 @@ protected:
QFile f;
int32 dataPos;
bool openFile() {
if (data.isEmpty()) {
if (f.isOpen()) f.close();
@@ -1884,7 +1879,7 @@ void AudioCaptureInner::onInit() {
}
void AudioCaptureInner::onStart() {
// Start OpenAL Capture
const ALCchar *dName = alcGetString(0, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
DEBUG_LOG(("Audio Info: Capture device name '%1'").arg(dName));
@@ -1905,7 +1900,7 @@ void AudioCaptureInner::onStart() {
// Create encoding context
d->ioBuffer = (uchar*)av_malloc(AVBlockSize);
d->ioContext = avio_alloc_context(d->ioBuffer, AVBlockSize, 1, static_cast<void*>(d), &AudioCapturePrivate::_read_data, &AudioCapturePrivate::_write_data, &AudioCapturePrivate::_seek_data);
int res = 0;
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
@@ -2064,7 +2059,7 @@ void AudioCaptureInner::onStop(bool needResult) {
}
}
}
DEBUG_LOG(("Audio Capture: stopping (need result: %1), size: %2, samples: %3").arg(logBool(needResult)).arg(d->data.size()).arg(d->fullSamples));
DEBUG_LOG(("Audio Capture: stopping (need result: %1), size: %2, samples: %3").arg(Logs::b(needResult)).arg(d->data.size()).arg(d->fullSamples));
_captured = QByteArray();
// Finish stream
@@ -2390,7 +2385,7 @@ public:
QString title() {
return _title;
}
QString performer() {
return _performer;
}

View File

@@ -34,7 +34,7 @@ typedef int VerInt;
typedef wchar_t VerChar;
#endif
UpdateDownloader::UpdateDownloader(QThread *thread, const QString &url) : reply(0), already(0), full(0) {
UpdateChecker::UpdateChecker(QThread *thread, const QString &url) : reply(0), already(0), full(0) {
updateUrl = url;
moveToThread(thread);
manager.moveToThread(thread);
@@ -44,7 +44,7 @@ UpdateDownloader::UpdateDownloader(QThread *thread, const QString &url) : reply(
initOutput();
}
void UpdateDownloader::initOutput() {
void UpdateChecker::initOutput() {
QString fileName;
QRegularExpressionMatch m = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)")).match(updateUrl);
if (m.hasMatch()) {
@@ -99,11 +99,11 @@ void UpdateDownloader::initOutput() {
}
}
void UpdateDownloader::start() {
void UpdateChecker::start() {
sendRequest();
}
void UpdateDownloader::sendRequest() {
void UpdateChecker::sendRequest() {
QNetworkRequest req(updateUrl);
QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";
req.setRawHeader("Range", rangeHeaderValue);
@@ -115,7 +115,7 @@ void UpdateDownloader::sendRequest() {
connect(reply, SIGNAL(metaDataChanged()), this, SLOT(partMetaGot()));
}
void UpdateDownloader::partMetaGot() {
void UpdateChecker::partMetaGot() {
typedef QList<QNetworkReply::RawHeaderPair> Pairs;
Pairs pairs = reply->rawHeaderPairs();
for (Pairs::iterator i = pairs.begin(), e = pairs.end(); i != e; ++i) {
@@ -126,23 +126,24 @@ void UpdateDownloader::partMetaGot() {
QMutexLocker lock(&mutex);
full = m.captured(1).toInt();
}
emit App::app()->updateDownloading(already, full);
Sandboxer::updateProgress(already, full);
}
}
}
}
int32 UpdateDownloader::ready() {
int32 UpdateChecker::ready() {
QMutexLocker lock(&mutex);
return already;
}
int32 UpdateDownloader::size() {
int32 UpdateChecker::size() {
QMutexLocker lock(&mutex);
return full;
}
void UpdateDownloader::partFinished(qint64 got, qint64 total) {
void UpdateChecker::partFinished(qint64 got, qint64 total) {
if (!reply) return;
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
@@ -179,11 +180,11 @@ void UpdateDownloader::partFinished(qint64 got, qint64 total) {
outputFile.close();
unpackUpdate();
} else {
emit App::app()->updateDownloading(already, full);
Sandboxer::updateProgress(already, full);
}
}
void UpdateDownloader::partFailed(QNetworkReply::NetworkError e) {
void UpdateChecker::partFailed(QNetworkReply::NetworkError e) {
if (!reply) return;
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
@@ -198,15 +199,15 @@ void UpdateDownloader::partFailed(QNetworkReply::NetworkError e) {
}
}
LOG(("Update Error: failed to download part starting from %1, error %2").arg(already).arg(e));
emit App::app()->updateFailed();
Sandboxer::updateFailed();
}
void UpdateDownloader::fatalFail() {
void UpdateChecker::fatalFail() {
clearAll();
emit App::app()->updateFailed();
Sandboxer::updateFailed();
}
void UpdateDownloader::clearAll() {
void UpdateChecker::clearAll() {
psDeleteDir(cWorkingDir() + qsl("tupdates"));
}
@@ -225,7 +226,7 @@ void UpdateDownloader::clearAll() {
// return QString::fromWCharArray(errMsg);
//}
void UpdateDownloader::unpackUpdate() {
void UpdateChecker::unpackUpdate() {
QByteArray packed;
if (!outputFile.open(QIODevice::ReadOnly)) {
LOG(("Update Error: cant read updates file!"));
@@ -465,10 +466,10 @@ void UpdateDownloader::unpackUpdate() {
}
outputFile.remove();
emit App::app()->updateReady();
Sandboxer::updateReady();
}
UpdateDownloader::~UpdateDownloader() {
UpdateChecker::~UpdateChecker() {
delete reply;
reply = 0;
}
@@ -477,7 +478,7 @@ 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();
UpdateChecker::clearAll();
}
return false;
}
@@ -488,30 +489,30 @@ bool checkReadyUpdate() {
QFile fVersion(versionPath);
if (!fVersion.open(QIODevice::ReadOnly)) {
LOG(("Update Error: cant read version file '%1'").arg(versionPath));
UpdateDownloader::clearAll();
UpdateChecker::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();
UpdateChecker::clearAll();
return false;
}
if (versionNum == 0x7FFFFFFF) { // beta version
quint64 betaVersion = 0;
if (fVersion.read((char*)&betaVersion, sizeof(quint64)) != sizeof(quint64)) {
LOG(("Update Error: cant read beta version from file '%1'").arg(versionPath));
UpdateDownloader::clearAll();
UpdateChecker::clearAll();
return false;
}
if (!cBetaVersion() || betaVersion <= cBetaVersion()) {
LOG(("Update Error: cant install beta version %1 having beta version %2").arg(betaVersion).arg(cBetaVersion()));
UpdateDownloader::clearAll();
UpdateChecker::clearAll();
return false;
}
} else if (versionNum <= AppVersion) {
LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
UpdateDownloader::clearAll();
UpdateChecker::clearAll();
return false;
}
fVersion.close();
@@ -530,11 +531,11 @@ bool checkReadyUpdate() {
if (!updater.exists()) {
QFileInfo current(curUpdater);
if (!current.exists()) {
UpdateDownloader::clearAll();
UpdateChecker::clearAll();
return false;
}
if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
UpdateDownloader::clearAll();
UpdateChecker::clearAll();
return false;
}
}
@@ -545,24 +546,24 @@ bool checkReadyUpdate() {
cSetWriteProtected(true);
return true;
} else {
UpdateDownloader::clearAll();
UpdateChecker::clearAll();
return false;
}
}
if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
UpdateDownloader::clearAll();
UpdateChecker::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();
UpdateChecker::clearAll();
return false;
}
#elif defined Q_OS_LINUX
if (!linuxMoveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) {
UpdateDownloader::clearAll();
UpdateChecker::clearAll();
return false;
}
#endif
@@ -578,7 +579,7 @@ QString countBetaVersionSignature(uint64 version) { // duplicated in packer.cpp
}
QByteArray signedData = (qstr("TelegramBeta_") + QString::number(version, 16).toLower()).toUtf8();
static const int32 shaSize = 20, keySize = 128;
uchar sha1Buffer[shaSize];

View File

@@ -26,11 +26,11 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QNetworkReply>
class UpdateDownloader : public QObject {
class UpdateChecker : public QObject {
Q_OBJECT
public:
UpdateDownloader(QThread *thread, const QString &url);
UpdateChecker(QThread *thread, const QString &url);
void unpackUpdate();
@@ -39,7 +39,7 @@ public:
static void clearAll();
~UpdateDownloader();
~UpdateChecker();
public slots:
@@ -66,6 +66,11 @@ private:
bool checkReadyUpdate();
#else
class UpdateChecker : public QObject {
Q_OBJECT
};
#endif
QString countBetaVersionSignature(uint64 version);

View File

@@ -44,6 +44,8 @@ AboutBox::AboutBox() : AbstractBox(st::aboutWidth)
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
prepare();
setAcceptDrops(true);
}
void AboutBox::hideAll() {
@@ -82,7 +84,7 @@ void AboutBox::onVersion() {
}
url = url.arg(qsl("tbeta%1_%2").arg(cRealBetaVersion()).arg(countBetaVersionSignature(cRealBetaVersion())));
App::app()->clipboard()->setText(url);
Application::clipboard()->setText(url);
Ui::showLayer(new InformBox("The link to the current private beta version of Telegram Desktop was copied to the clipboard."));
} else {
@@ -105,6 +107,29 @@ void AboutBox::paintEvent(QPaintEvent *e) {
paintTitle(p, qsl("Telegram Desktop"));
}
QString _getCrashReportFile(const QMimeData *m) {
if (!m || m->urls().size() != 1) return QString();
QString file(m->urls().at(0).toLocalFile());
if (file.startsWith(qsl("/.file/id="))) file = psConvertFileUrl(file);
return file.endsWith(qstr(".telegramcrash"), Qt::CaseInsensitive) ? file : QString();
}
void AboutBox::dragEnterEvent(QDragEnterEvent *e) {
if (!_getCrashReportFile(e->mimeData()).isEmpty()) {
e->setDropAction(Qt::CopyAction);
e->accept();
}
}
void AboutBox::dropEvent(QDropEvent *e) {
if (!_getCrashReportFile(e->mimeData()).isEmpty()) {
e->acceptProposedAction();
showCrashReportWindow(_getCrashReportFile(e->mimeData()));
}
}
QString telegramFaqLink() {
QString result = qsl("https://telegram.org/faq");
if (cLang() > languageDefault && cLang() < languageCount) {
@@ -116,4 +141,4 @@ QString telegramFaqLink() {
}
}
return result;
}
}

View File

@@ -31,7 +31,10 @@ public:
void resizeEvent(QResizeEvent *e);
void keyPressEvent(QKeyEvent *e);
void paintEvent(QPaintEvent *e);
void dragEnterEvent(QDragEnterEvent *e);
void dropEvent(QDropEvent *e);
public slots:
void onVersion();

View File

@@ -750,7 +750,7 @@ void SetupChannelBox::mouseMoveEvent(QMouseEvent *e) {
void SetupChannelBox::mousePressEvent(QMouseEvent *e) {
mouseMoveEvent(e);
if (_linkOver) {
App::app()->clipboard()->setText(_channel->invitationUrl);
Application::clipboard()->setText(_channel->invitationUrl);
_goodTextLink = lang(lng_create_channel_link_copied);
a_goodOpacity = anim::fvalue(1, 0);
_a_goodFade.start();

View File

@@ -34,7 +34,7 @@ TextParseOptions _confirmBoxTextOptions = {
Qt::LayoutDirectionAuto, // dir
};
ConfirmBox::ConfirmBox(const QString &text, const QString &doneText, const style::BoxButton &doneStyle, const QString &cancelText, const style::BoxButton &cancelStyle) : AbstractBox(st::boxWidth),
ConfirmBox::ConfirmBox(const QString &text, const QString &doneText, const style::BoxButton &doneStyle, const QString &cancelText, const style::BoxButton &cancelStyle) : AbstractBox(st::boxWidth),
_informative(false),
_text(100),
_confirm(this, doneText.isEmpty() ? lang(lng_box_ok) : doneText, doneStyle),
@@ -212,7 +212,7 @@ void MaxInviteBox::mouseMoveEvent(QMouseEvent *e) {
void MaxInviteBox::mousePressEvent(QMouseEvent *e) {
mouseMoveEvent(e);
if (_linkOver) {
App::app()->clipboard()->setText(_link);
Application::clipboard()->setText(_link);
_goodTextLink = lang(lng_create_channel_link_copied);
a_goodOpacity = anim::fvalue(1, 0);
_a_good.start();

View File

@@ -43,6 +43,7 @@ _input(set), _installRequest(0) {
void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
_pack.clear();
_emoji.clear();
if (set.type() == mtpc_messages_stickerSet) {
const MTPDmessages_stickerSet &d(set.c_messages_stickerSet());
const QVector<MTPDocument> &v(d.vdocuments.c_vector().v);
@@ -50,9 +51,26 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
for (int32 i = 0, l = v.size(); i < l; ++i) {
DocumentData *doc = App::feedDocument(v.at(i));
if (!doc || !doc->sticker()) continue;
_pack.push_back(doc);
}
const QVector<MTPStickerPack> &packs(d.vpacks.c_vector().v);
for (int32 i = 0, l = packs.size(); i < l; ++i) {
if (packs.at(i).type() != mtpc_stickerPack) continue;
const MTPDstickerPack &pack(packs.at(i).c_stickerPack());
if (EmojiPtr e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) {
const QVector<MTPlong> &stickers(pack.vdocuments.c_vector().v);
StickerPack p;
p.reserve(stickers.size());
for (int32 j = 0, c = stickers.size(); j < c; ++j) {
DocumentData *doc = App::document(stickers.at(j).v);
if (!doc || !doc->sticker()) continue;
p.push_back(doc);
}
_emoji.insert(e, p);
}
}
if (d.vset.type() == mtpc_stickerSet) {
const MTPDstickerSet &s(d.vset.c_stickerSet());
_setTitle = stickerSetTitle(s);
@@ -91,7 +109,12 @@ void StickerSetInner::installDone(const MTPBool &result) {
StickerSets &sets(cRefStickerSets());
_setFlags &= ~MTPDstickerSet::flag_disabled;
sets.insert(_setId, StickerSet(_setId, _setAccess, _setTitle, _setShortName, _setCount, _setHash, _setFlags)).value().stickers = _pack;
StickerSets::iterator it = sets.find(_setId);
if (it == sets.cend()) {
it = sets.insert(_setId, StickerSet(_setId, _setAccess, _setTitle, _setShortName, _setCount, _setHash, _setFlags));
}
it.value().stickers = _pack;
it.value().emoji = _emoji;
StickerSetsOrder &order(cRefStickerSetsOrder());
int32 insertAtIndex = 0, currentIndex = order.indexOf(_setId);
@@ -507,7 +530,7 @@ void StickersInner::onUpdateSelected() {
float64 StickersInner::aboveShadowOpacity() const {
if (_above < 0) return 0;
int32 dx = 0;
int32 dy = qAbs(_above * _rowHeight + _rows.at(_above)->yadd.current() - _started * _rowHeight);
return qMin((dx + dy) * 2. / _rowHeight, 1.);
@@ -613,7 +636,7 @@ void StickersInner::rebuild() {
clear();
const StickerSetsOrder &order(cStickerSetsOrder());
_animStartTimes.reserve(order.size());
const StickerSets &sets(cStickerSets());
for (int32 i = 0, l = order.size(); i < l; ++i) {
StickerSets::const_iterator it = sets.constFind(order.at(i));
@@ -704,6 +727,7 @@ StickersBox::StickersBox() : ItemListBox(st::boxScroll)
setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight)));
connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated()));
App::main()->updateStickers();
connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_save, SIGNAL(clicked()), this, SLOT(onSave()));

View File

@@ -32,7 +32,7 @@ public:
void init();
void paintEvent(QPaintEvent *e);
bool loaded() const;
int32 notInstalled() const;
bool official() const;
@@ -60,6 +60,7 @@ private:
bool installFailed(const RPCError &error);
StickerPack _pack;
StickersByEmojiMap _emoji;
bool _loaded;
uint64 _setId, _setAccess;
QString _title, _setTitle, _setShortName;
@@ -118,7 +119,7 @@ public:
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void rebuild();
bool savingStart() {
if (_saving) return false;
@@ -201,7 +202,7 @@ public:
StickersBox();
void resizeEvent(QResizeEvent *e);
void paintEvent(QPaintEvent *e);
void closePressed();
public slots:

View File

@@ -191,7 +191,7 @@ void UsernameBox::onChanged() {
}
void UsernameBox::onLinkClick() {
App::app()->clipboard()->setText(qsl("https://telegram.me/") + getName());
Application::clipboard()->setText(qsl("https://telegram.me/") + getName());
_copiedTextLink = lang(lng_username_copied);
update();
}

View File

@@ -20,10 +20,10 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
*/
#pragma once
static const int32 AppVersion = 9016;
static const wchar_t *AppVersionStr = L"0.9.16";
static const bool DevVersion = false;
//#define BETA_VERSION (9015008ULL) // just comment this line to build public version
static const int32 AppVersion = 9019;
static const wchar_t *AppVersionStr = L"0.9.19";
static const bool DevVersion = true;
#define BETA_VERSION (9019002ULL) // just comment this line to build public version
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
static const wchar_t *AppName = L"Telegram Desktop";

View File

@@ -1912,9 +1912,7 @@ int32 StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &res
return 0;
}
if (_showingInlineItems) {
clearSelection(true);
}
clearSelection(true);
t_assert(_inlineBot != 0);
_inlineBotTitle = lng_inline_bot_results(lt_inline_bot, _inlineBot->username.isEmpty() ? _inlineBot->name : ('@' + _inlineBot->username));
@@ -1942,10 +1940,9 @@ int32 StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &res
update();
emit refreshIcons();
if (_showingInlineItems) {
_lastMousePos = QCursor::pos();
updateSelected();
}
_lastMousePos = QCursor::pos();
updateSelected();
return added;
}
@@ -2440,10 +2437,12 @@ void StickerPanInner::showStickerSet(uint64 setId) {
void StickerPanInner::updateShowingSavedGifs() {
if (cShowingSavedGifs()) {
if (!_showingInlineItems) {
clearSelection(true);
_showingSavedGifs = _showingInlineItems = true;
if (_inlineRows.isEmpty()) refreshSavedGifs();
}
} else if (!_showingInlineItems) {
clearSelection(true);
_showingSavedGifs = false;
}
}
@@ -3842,133 +3841,185 @@ void EmojiPan::recountContentMaxHeight() {
updateContentHeight();
}
MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows)
MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerPack *srows)
: _parent(parent)
, _mrows(mrows)
, _hrows(hrows)
, _brows(brows)
, _srows(srows)
, _stickersPerRow(1)
, _recentInlineBotsInRows(0)
, _sel(-1)
, _mouseSel(false)
, _overDelete(false) {
}
void MentionsInner::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
QRect r(e->rect());
if (r != rect()) p.setClipRect(r);
int32 atwidth = st::mentionFont->width('@'), hashwidth = st::mentionFont->width('#');
int32 mentionleft = 2 * st::mentionPadding.left() + st::mentionPhotoSize;
int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right();
int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width;
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1;
int32 last = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
bool hasUsername = _parent->filter().indexOf('@') > 1;
for (int32 i = from; i < to; ++i) {
if (i >= last) break;
if (!_srows->isEmpty()) {
int32 rows = rowscount(_srows->size(), _stickersPerRow);
int32 fromrow = floorclamp(r.y() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
int32 torow = ceilclamp(r.y() + r.height() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, _stickersPerRow);
int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, _stickersPerRow);
for (int32 row = fromrow; row < torow; ++row) {
for (int32 col = fromcol; col < tocol; ++col) {
int32 index = row * _stickersPerRow + col;
if (index >= _srows->size()) break;
bool selected = (i == _sel);
if (selected) {
p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b);
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
DocumentData *sticker = _srows->at(index);
if (!sticker->sticker()) continue;
QPoint pos(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height());
if (_sel == index) {
QPoint tl(pos);
if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width());
App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners);
}
bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128));
if (goodThumb) {
sticker->thumb->load();
} else {
sticker->checkSticker();
}
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;
if (h < 1) h = 1;
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
if (goodThumb) {
p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h));
} else if (!sticker->sticker()->img->isNull()) {
p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h));
}
}
}
p.setPen(st::black->p);
if (!_mrows->isEmpty()) {
UserData *user = _mrows->at(i);
QString first = (_parent->filter().size() < 2) ? QString() : ('@' + user->username.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('@' + user->username) : user->username.mid(_parent->filter().size() - 1);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth();
if (mentionwidth < unamewidth + namewidth) {
namewidth = (mentionwidth * namewidth) / (namewidth + unamewidth);
unamewidth = mentionwidth - namewidth;
if (firstwidth < unamewidth + st::mentionFont->elidew) {
if (firstwidth < unamewidth) {
first = st::mentionFont->elided(first, unamewidth);
} else if (!second.isEmpty()) {
first = st::mentionFont->elided(first + second, unamewidth);
second = QString();
} else {
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1;
int32 last = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
bool hasUsername = _parent->filter().indexOf('@') > 1;
for (int32 i = from; i < to; ++i) {
if (i >= last) break;
bool selected = (i == _sel);
if (selected) {
p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b);
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
}
p.setPen(st::black->p);
if (!_mrows->isEmpty()) {
UserData *user = _mrows->at(i);
QString first = (_parent->filter().size() < 2) ? QString() : ('@' + user->username.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('@' + user->username) : user->username.mid(_parent->filter().size() - 1);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth();
if (mentionwidth < unamewidth + namewidth) {
namewidth = (mentionwidth * namewidth) / (namewidth + unamewidth);
unamewidth = mentionwidth - namewidth;
if (firstwidth < unamewidth + st::mentionFont->elidew) {
if (firstwidth < unamewidth) {
first = st::mentionFont->elided(first, unamewidth);
} else if (!second.isEmpty()) {
first = st::mentionFont->elided(first + second, unamewidth);
second = QString();
}
} else {
second = st::mentionFont->elided(second, unamewidth - firstwidth);
}
} else {
second = st::mentionFont->elided(second, unamewidth - firstwidth);
}
}
user->photo->load();
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);
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(mentionleft + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
} else if (!_hrows->isEmpty()) {
QString hrow = _hrows->at(i);
QString first = (_parent->filter().size() < 2) ? QString() : ('#' + hrow.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('#' + hrow) : hrow.mid(_parent->filter().size() - 1);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
if (htagwidth < firstwidth + secondwidth) {
if (htagwidth < firstwidth + st::mentionFont->elidew) {
first = st::mentionFont->elided(first + second, htagwidth);
second = QString();
} else {
second = st::mentionFont->elided(second, htagwidth - firstwidth);
}
}
p.setFont(st::mentionFont->f);
if (!first.isEmpty()) {
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
}
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
} else {
UserData *user = _brows->at(i).first;
const BotCommand *command = _brows->at(i).second;
QString toHighlight = command->command;
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (hasUsername || botStatus == 0 || botStatus == 2) {
toHighlight += '@' + user->username;
}
if (true || _parent->chat() || botStatus == 0 || botStatus == 2) {
user->photo->load();
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);
int32 addleft = 0, widthleft = mentionwidth;
QString first = (_parent->filter().size() < 2) ? QString() : ('/' + toHighlight.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('/' + toHighlight) : toHighlight.mid(_parent->filter().size() - 1);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
if (widthleft < firstwidth + secondwidth) {
if (widthleft < firstwidth + st::mentionFont->elidew) {
first = st::mentionFont->elided(first + second, widthleft);
second = QString();
} else {
second = st::mentionFont->elided(second, widthleft - firstwidth);
p.setFont(st::mentionFont->f);
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(mentionleft + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
} else if (!_hrows->isEmpty()) {
QString hrow = _hrows->at(i);
QString first = (_parent->filter().size() < 2) ? QString() : ('#' + hrow.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('#' + hrow) : hrow.mid(_parent->filter().size() - 1);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
if (htagwidth < firstwidth + secondwidth) {
if (htagwidth < firstwidth + st::mentionFont->elidew) {
first = st::mentionFont->elided(first + second, htagwidth);
second = QString();
} else {
second = st::mentionFont->elided(second, htagwidth - firstwidth);
}
}
p.setFont(st::mentionFont->f);
if (!first.isEmpty()) {
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
}
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
} else {
UserData *user = _brows->at(i).first;
const BotCommand *command = _brows->at(i).second;
QString toHighlight = command->command;
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (hasUsername || botStatus == 0 || botStatus == 2) {
toHighlight += '@' + user->username;
}
if (true || _parent->chat() || botStatus == 0 || botStatus == 2) {
user->photo->load();
p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize));
}
int32 addleft = 0, widthleft = mentionwidth;
QString first = (_parent->filter().size() < 2) ? QString() : ('/' + toHighlight.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('/' + toHighlight) : toHighlight.mid(_parent->filter().size() - 1);
int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
if (widthleft < firstwidth + secondwidth) {
if (widthleft < firstwidth + st::mentionFont->elidew) {
first = st::mentionFont->elided(first + second, widthleft);
second = QString();
} else {
second = st::mentionFont->elided(second, widthleft - firstwidth);
}
}
p.setFont(st::mentionFont->f);
if (!first.isEmpty()) {
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(mentionleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
}
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(mentionleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
addleft += firstwidth + secondwidth + st::mentionPadding.left();
widthleft -= firstwidth + secondwidth + st::mentionPadding.left();
if (widthleft > st::mentionFont->elidew && !command->descriptionText().isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
command->descriptionText().drawElided(p, mentionleft + addleft, i * st::mentionHeight + st::mentionTop, widthleft, 1, style::al_right);
}
}
p.setFont(st::mentionFont->f);
if (!first.isEmpty()) {
p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
p.drawText(mentionleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
}
if (!second.isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
p.drawText(mentionleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
}
addleft += firstwidth + secondwidth + st::mentionPadding.left();
widthleft -= firstwidth + secondwidth + st::mentionPadding.left();
if (widthleft > st::mentionFont->elidew && !command->descriptionText().isEmpty()) {
p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
command->descriptionText().drawElided(p, mentionleft + addleft, i * st::mentionHeight + st::mentionTop, widthleft, 1, style::al_right);
}
}
p.fillRect(cWideMode() ? st::lineWidth : 0, _parent->innerBottom() - st::lineWidth, width() - (cWideMode() ? st::lineWidth : 0), st::lineWidth, st::shadowColor->b);
}
p.fillRect(cWideMode() ? st::lineWidth : 0, _parent->innerTop(), width() - (cWideMode() ? st::lineWidth : 0), st::lineWidth, st::shadowColor->b);
p.fillRect(cWideMode() ? st::lineWidth : 0, _parent->innerBottom() - st::lineWidth, width() - (cWideMode() ? st::lineWidth : 0), st::lineWidth, st::shadowColor->b);
}
void MentionsInner::resizeEvent(QResizeEvent *e) {
_stickersPerRow = qMax(1, int32(width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()));
}
void MentionsInner::mouseMoveEvent(QMouseEvent *e) {
@@ -3982,26 +4033,45 @@ void MentionsInner::clearSel() {
setSel((_mrows->isEmpty() && _brows->isEmpty() && _hrows->isEmpty()) ? -1 : 0);
}
bool MentionsInner::moveSel(int direction) {
bool MentionsInner::moveSel(int key) {
_mouseSel = false;
int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size());
int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? (_brows->isEmpty() ? _srows->size() : _brows->size()) : _hrows->size()) : _mrows->size());
int32 direction = (key == Qt::Key_Up) ? -1 : (key == Qt::Key_Down ? 1 : 0);
if (!_srows->isEmpty()) {
if (key == Qt::Key_Left) {
direction = -1;
} else if (key == Qt::Key_Right) {
direction = 1;
} else {
direction *= _stickersPerRow;
}
}
if (_sel >= maxSel || _sel < 0) {
if (direction < 0) {
if (direction < -1) {
setSel(((maxSel - 1) / _stickersPerRow) * _stickersPerRow, true);
} else if (direction < 0) {
setSel(maxSel - 1, true);
} else {
setSel(0, true);
}
return (_sel >= 0 && _sel < maxSel);
}
setSel((_sel + direction >= maxSel) ? -1 : (_sel + direction), true);
setSel((_sel + direction >= maxSel || _sel + direction < 0) ? -1 : (_sel + direction), true);
return true;
}
bool MentionsInner::select() {
QString sel = getSelected();
if (!sel.isEmpty()) {
emit chosen(sel);
return true;
if (!_srows->isEmpty()) {
if (_sel >= 0 && _sel < _srows->size()) {
emit selected(_srows->at(_sel));
return true;
}
} else {
QString sel = getSelected();
if (!sel.isEmpty()) {
emit chosen(sel);
return true;
}
}
return false;
}
@@ -4087,21 +4157,51 @@ void MentionsInner::leaveEvent(QEvent *e) {
}
}
void MentionsInner::updateSelectedRow() {
if (_sel >= 0) {
if (_srows->isEmpty()) {
update(0, _sel * st::mentionHeight, width(), st::mentionHeight);
} else {
int32 row = _sel / _stickersPerRow, col = _sel % _stickersPerRow;
update(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height(), st::stickerPanSize.width(), st::stickerPanSize.height());
}
}
}
void MentionsInner::setSel(int sel, bool scroll) {
if (_sel >= 0) update(0, _sel * st::mentionHeight, width(), st::mentionHeight);
updateSelectedRow();
_sel = sel;
if (_sel >= 0) update(0, _sel * st::mentionHeight, width(), st::mentionHeight);
int32 maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
if (scroll && _sel >= 0 && _sel < maxSel) emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
updateSelectedRow();
if (scroll && _sel >= 0) {
if (_srows->isEmpty()) {
emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
} else {
int32 row = _sel / _stickersPerRow;
emit mustScrollTo(st::stickerPanPadding + row * st::stickerPanSize.height(), st::stickerPanPadding + (row + 1) * st::stickerPanSize.height());
}
}
}
void MentionsInner::onUpdateSelected(bool force) {
QPoint mouse(mapFromGlobal(_mousePos));
if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
int w = width(), mouseY = mouse.y();
int32 sel = mouseY / int32(st::mentionHeight), maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
_overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= w - st::mentionHeight) : false;
int32 sel = -1, maxSel = 0;
if (!_srows->isEmpty()) {
int32 rows = rowscount(_srows->size(), _stickersPerRow);
int32 row = (mouse.y() >= st::stickerPanPadding) ? ((mouse.y() - st::stickerPanPadding) / st::stickerPanSize.height()) : -1;
int32 col = (mouse.x() >= st::stickerPanPadding) ? ((mouse.x() - st::stickerPanPadding) / st::stickerPanSize.width()) : -1;
if (row >= 0 && col >= 0) {
sel = row * _stickersPerRow + col;
}
maxSel = _srows->size();
_overDelete = false;
} else {
sel = mouse.y() / int32(st::mentionHeight);
maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
_overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= width() - st::mentionHeight) : false;
}
if (sel < 0 || sel >= maxSel) {
sel = -1;
}
@@ -4120,7 +4220,7 @@ void MentionsInner::onParentGeometryChanged() {
MentionsDropdown::MentionsDropdown(QWidget *parent) : TWidget(parent)
, _scroll(this, st::mentionScroll)
, _inner(this, &_mrows, &_hrows, &_brows)
, _inner(this, &_mrows, &_hrows, &_brows, &_srows)
, _chat(0)
, _user(0)
, _channel(0)
@@ -4131,6 +4231,7 @@ MentionsDropdown::MentionsDropdown(QWidget *parent) : TWidget(parent)
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
connect(&_inner, SIGNAL(chosen(QString)), this, SIGNAL(chosen(QString)));
connect(&_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*)));
connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int)));
connect(App::wnd(), SIGNAL(imageLoaded()), &_inner, SLOT(update()));
@@ -4151,7 +4252,7 @@ MentionsDropdown::MentionsDropdown(QWidget *parent) : TWidget(parent)
}
void MentionsDropdown::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
@@ -4159,29 +4260,43 @@ void MentionsDropdown::paintEvent(QPaintEvent *e) {
return;
}
p.fillRect(rect(), st::white->b);
p.fillRect(rect(), st::white);
}
void MentionsDropdown::showFiltered(PeerData *peer, QString query, bool start) {
if (query.isEmpty() || (peer->isUser() && query.at(0) == '@' && (!start || cRecentInlineBots().isEmpty()))) {
if (!isHidden()) {
hideStart();
}
return;
}
_chat = peer->asChat();
_user = peer->asUser();
_channel = peer->asChannel();
if (query.isEmpty()) {
rowsUpdated(MentionRows(), HashtagRows(), BotCommandRows(), _srows, false);
return;
}
_emoji = EmojiPtr();
query = query.toLower();
bool toDown = (_filter != query);
if (toDown) {
bool resetScroll = (_filter != query);
if (resetScroll) {
_filter = query;
}
_addInlineBots = start;
updateFiltered(toDown);
updateFiltered(resetScroll);
}
void MentionsDropdown::showStickers(EmojiPtr emoji) {
bool resetScroll = (_emoji != emoji);
_emoji = emoji;
if (!emoji) {
rowsUpdated(_mrows, _hrows, _brows, StickerPack(), false);
return;
}
_chat = 0;
_user = 0;
_channel = 0;
updateFiltered(resetScroll);
}
bool MentionsDropdown::clearFilteredBotCommands() {
@@ -4190,12 +4305,37 @@ bool MentionsDropdown::clearFilteredBotCommands() {
return true;
}
void MentionsDropdown::updateFiltered(bool toDown) {
void MentionsDropdown::updateFiltered(bool resetScroll) {
int32 now = unixtime(), recentInlineBots = 0;
MentionRows mrows;
HashtagRows hrows;
BotCommandRows brows;
if (_filter.at(0) == '@') {
StickerPack srows;
if (_emoji) {
QMap<uint64, uint64> setsToRequest;
StickerSets &sets(cRefStickerSets());
const StickerSetsOrder &order(cStickerSetsOrder());
for (int32 i = 0, l = order.size(); i < l; ++i) {
StickerSets::iterator it = sets.find(order.at(i));
if (it != sets.cend()) {
if (it->emoji.isEmpty()) {
setsToRequest.insert(it->id, it->access);
it->flags |= MTPDstickerSet_flag_NOT_LOADED;
} else if (!(it->flags & MTPDstickerSet::flag_disabled)) {
StickersByEmojiMap::const_iterator i = it->emoji.constFind(emojiGetNoColor(_emoji));
if (i != it->emoji.cend()) {
srows += *i;
}
}
}
}
if (!setsToRequest.isEmpty() && App::api()) {
for (QMap<uint64, uint64>::const_iterator i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) {
App::api()->scheduleStickerSetRequest(i.key(), i.value());
}
App::api()->requestStickerSets();
}
} else if (_filter.at(0) == '@') {
if (_chat) {
mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
} else if (_channel && _channel->isMegagroup()) {
@@ -4215,46 +4355,46 @@ void MentionsDropdown::updateFiltered(bool toDown) {
++recentInlineBots;
}
}
}
if (_filter.at(0) == '@' && _chat) {
QMultiMap<int32, UserData*> ordered;
mrows.reserve(mrows.size() + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
if (_chat->noParticipantInfo()) {
if (App::api()) App::api()->requestFullPeer(_chat);
} else if (!_chat->participants.isEmpty()) {
for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
UserData *user = i.key();
if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
ordered.insertMulti(App::onlineForSort(user, now), user);
if (_chat) {
QMultiMap<int32, UserData*> ordered;
mrows.reserve(mrows.size() + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
if (_chat->noParticipantInfo()) {
if (App::api()) App::api()->requestFullPeer(_chat);
} else if (!_chat->participants.isEmpty()) {
for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
UserData *user = i.key();
if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
ordered.insertMulti(App::onlineForSort(user, now), user);
}
}
}
for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
UserData *user = *i;
if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
mrows.push_back(user);
if (!ordered.isEmpty()) {
ordered.remove(App::onlineForSort(user, now), user);
}
}
if (!ordered.isEmpty()) {
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
--i;
mrows.push_back(i.value());
}
}
} else if (_filter.at(0) == '@' && _channel && _channel->isMegagroup()) {
QMultiMap<int32, UserData*> ordered;
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
if (App::api()) App::api()->requestLastParticipants(_channel);
} else {
mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size());
for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) {
for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
UserData *user = *i;
if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
mrows.push_back(user);
if (!ordered.isEmpty()) {
ordered.remove(App::onlineForSort(user, now), user);
}
}
if (!ordered.isEmpty()) {
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
--i;
mrows.push_back(i.value());
}
}
} else if (_channel && _channel->isMegagroup()) {
QMultiMap<int32, UserData*> ordered;
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
if (App::api()) App::api()->requestLastParticipants(_channel);
} else {
mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size());
for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) {
UserData *user = *i;
if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
mrows.push_back(user);
}
}
}
} else if (_filter.at(0) == '#') {
@@ -4333,28 +4473,31 @@ void MentionsDropdown::updateFiltered(bool toDown) {
}
}
}
rowsUpdated(mrows, hrows, brows, toDown);
rowsUpdated(mrows, hrows, brows, srows, resetScroll);
_inner.setRecentInlineBotsInRows(recentInlineBots);
}
void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown) {
if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty()) {
void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, const StickerPack &srows, bool resetScroll) {
if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.isEmpty()) {
if (!isHidden()) {
hideStart();
}
_mrows.clear();
_hrows.clear();
_brows.clear();
_srows.clear();
} else {
_mrows = mrows;
_hrows = hrows;
_brows = brows;
_srows = srows;
bool hidden = _hiding || isHidden();
if (hidden) {
show();
_scroll.show();
}
recount(toDown);
recount(resetScroll);
if (hidden) {
hide();
showStart();
@@ -4364,31 +4507,37 @@ void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows &
void MentionsDropdown::setBoundings(QRect boundings) {
_boundings = boundings;
resize(_boundings.width(), height());
_scroll.resize(size());
_inner.resize(width(), _inner.height());
recount();
}
void MentionsDropdown::recount(bool toDown) {
int32 h = (_mrows.isEmpty() ? (_hrows.isEmpty() ? _brows.size() : _hrows.size()) : _mrows.size()) * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst;
void MentionsDropdown::recount(bool resetScroll) {
int32 h = 0, oldst = _scroll.scrollTop(), st = oldst, maxh = 4.5 * st::mentionHeight;
if (!_srows.isEmpty()) {
int32 stickersPerRow = qMax(1, int32(_boundings.width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()));
int32 rows = rowscount(_srows.size(), stickersPerRow);
h = st::stickerPanPadding + rows * st::stickerPanSize.height();
} else if (!_mrows.isEmpty()) {
h = _mrows.size() * st::mentionHeight;
} else if (!_hrows.isEmpty()) {
h = _hrows.size() * st::mentionHeight;
} else if (!_brows.isEmpty()) {
h = _brows.size() * st::mentionHeight;
}
if (_inner.height() != h) {
// st += h - _inner.height();
_inner.resize(width(), h);
if (_inner.width() != _boundings.width() || _inner.height() != h) {
_inner.resize(_boundings.width(), h);
}
if (h > _boundings.height()) h = _boundings.height();
if (h > 4.5 * st::mentionHeight) h = 4.5 * st::mentionHeight;
if (height() != h) {
// st += _scroll.height() - h;
setGeometry(0, _boundings.height() - h, width(), h);
_scroll.resize(width(), h);
if (h > maxh) h = maxh;
if (width() != _boundings.width() || height() != h) {
setGeometry(0, _boundings.height() - h, _boundings.width(), h);
_scroll.resize(_boundings.width(), h);
} else if (y() != _boundings.height() - h) {
move(0, _boundings.height() - h);
}
if (toDown) st = 0;// _scroll.scrollTopMax();
if (resetScroll) st = 0;
if (st != oldst) _scroll.scrollToY(st);
if (toDown) _inner.clearSel();
if (resetScroll) _inner.clearSel();
}
void MentionsDropdown::fastHide() {
@@ -4488,13 +4637,12 @@ bool MentionsDropdown::eventFilter(QObject *obj, QEvent *e) {
if (isHidden()) return QWidget::eventFilter(obj, e);
if (e->type() == QEvent::KeyPress) {
QKeyEvent *ev = static_cast<QKeyEvent*>(e);
if (ev->key() == Qt::Key_Up) {
_inner.moveSel(-1);
return true;
} else if (ev->key() == Qt::Key_Down) {
return _inner.moveSel(1);
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
return _inner.select();
if (!(ev->modifiers() & (Qt::AltModifier | Qt::ControlModifier | Qt::ShiftModifier | Qt::MetaModifier))) {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down || (!_srows.isEmpty() && (ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right))) {
return _inner.moveSel(ev->key());
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
return _inner.select();
}
}
}
return QWidget::eventFilter(obj, e);

View File

@@ -734,9 +734,10 @@ class MentionsInner : public TWidget {
public:
MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows);
MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerPack *srows);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
@@ -745,7 +746,7 @@ public:
void mouseMoveEvent(QMouseEvent *e);
void clearSel();
bool moveSel(int direction);
bool moveSel(int key);
bool select();
void setRecentInlineBotsInRows(int32 bots);
@@ -755,6 +756,7 @@ public:
signals:
void chosen(QString mentionOrHashtag);
void selected(DocumentData *sticker);
void mustScrollTo(int scrollToTop, int scrollToBottom);
public slots:
@@ -764,13 +766,15 @@ public slots:
private:
void updateSelectedRow();
void setSel(int sel, bool scroll = false);
MentionsDropdown *_parent;
MentionRows *_mrows;
HashtagRows *_hrows;
BotCommandRows *_brows;
int32 _recentInlineBotsInRows;
StickerPack *_srows;
int32 _stickersPerRow, _recentInlineBotsInRows;
int32 _sel;
bool _mouseSel;
QPoint _mousePos;
@@ -791,7 +795,8 @@ public:
bool clearFilteredBotCommands();
void showFiltered(PeerData *peer, QString query, bool start);
void updateFiltered(bool toDown = false);
void showStickers(EmojiPtr emoji);
void updateFiltered(bool resetScroll = false);
void setBoundings(QRect boundings);
void step_appearance(float64 ms, bool timer);
@@ -807,6 +812,10 @@ public:
bool eventFilter(QObject *obj, QEvent *e);
QString getSelected() const;
bool stickersShown() const {
return !_srows.isEmpty();
}
bool overlaps(const QRect &globalRect) {
if (isHidden() || !testAttribute(Qt::WA_OpaquePaintEvent)) return false;
@@ -818,6 +827,7 @@ public:
signals:
void chosen(QString mentionOrHashtag);
void stickerSelected(DocumentData *sticker);
public slots:
@@ -828,14 +838,15 @@ public slots:
private:
void recount(bool toDown = false);
void recount(bool resetScroll = false);
QPixmap _cache;
MentionRows _mrows;
HashtagRows _hrows;
BotCommandRows _brows;
StickerPack _srows;
void rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown);
void rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, const StickerPack &srows, bool resetScroll);
ScrollArea _scroll;
MentionsInner _inner;
@@ -843,6 +854,7 @@ private:
ChatData *_chat;
UserData *_user;
ChannelData *_channel;
EmojiPtr _emoji;
QString _filter;
QRect _boundings;
bool _addInlineBots;

View File

@@ -24,6 +24,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "layerwidget.h"
#include "lang.h"
namespace App {
@@ -174,3 +175,157 @@ namespace Notify {
}
}
struct GlobalDataStruct {
QString LangSystemISO;
int32 LangSystem = languageDefault;
QByteArray LastCrashDump;
ConnectionProxy PreLaunchProxy;
};
GlobalDataStruct *GlobalData = 0;
namespace Global {
bool CheckBetaVersionDir() {
QFile beta(cExeDir() + qsl("TelegramBeta_data/tdata/beta"));
if (cBetaVersion()) {
cForceWorkingDir(cExeDir() + qsl("TelegramBeta_data/"));
QDir().mkpath(cWorkingDir() + qstr("tdata"));
if (*BetaPrivateKey) {
cSetBetaPrivateKey(QByteArray(BetaPrivateKey));
}
if (beta.open(QIODevice::WriteOnly)) {
QDataStream dataStream(&beta);
dataStream.setVersion(QDataStream::Qt_5_3);
dataStream << quint64(cRealBetaVersion()) << cBetaPrivateKey();
} else {
LOG(("FATAL: Could not open '%1' for writing private key!").arg(beta.fileName()));
return false;
}
} else if (beta.exists()) {
cForceWorkingDir(cExeDir() + qsl("TelegramBeta_data/"));
if (beta.open(QIODevice::ReadOnly)) {
QDataStream dataStream(&beta);
dataStream.setVersion(QDataStream::Qt_5_3);
quint64 v;
QByteArray k;
dataStream >> v >> k;
if (dataStream.status() == QDataStream::Ok) {
cSetBetaVersion(qMax(v, AppVersion * 1000ULL));
cSetBetaPrivateKey(k);
cSetRealBetaVersion(v);
} else {
LOG(("FATAL: '%1' is corrupted, reinstall private beta!").arg(beta.fileName()));
return false;
}
} else {
LOG(("FATAL: could not open '%1' for reading private key!").arg(beta.fileName()));
return false;
}
}
return true;
}
void WorkingDirReady() {
if (QFile(cWorkingDir() + qsl("tdata/withtestmode")).exists()) {
cSetTestMode(true);
}
if (!cDebug() && QFile(cWorkingDir() + qsl("tdata/withdebug")).exists()) {
cSetDebug(true);
}
if (cBetaVersion()) {
cSetDevVersion(false);
} else if (!cDevVersion() && QFile(cWorkingDir() + qsl("tdata/devversion")).exists()) {
cSetDevVersion(true);
} else if (DevVersion) {
QFile f(cWorkingDir() + qsl("tdata/devversion"));
if (!f.exists() && f.open(QIODevice::WriteOnly)) {
f.write("1");
}
}
}
void start() {
GlobalData = new GlobalDataStruct();
GlobalData->LangSystemISO = psCurrentLanguage();
if (GlobalData->LangSystemISO.isEmpty()) GlobalData->LangSystemISO = qstr("en");
QByteArray l = LangSystemISO().toLatin1();
for (int32 i = 0; i < languageCount; ++i) {
if (l.at(0) == LanguageCodes[i][0] && l.at(1) == LanguageCodes[i][1]) {
GlobalData->LangSystem = i;
break;
}
}
srand((int32)time(NULL));
}
void finish() {
delete GlobalData;
GlobalData = 0;
}
#define DefineGlobalReadOnly(Type, Name) const Type &Name() { \
t_assert_full(GlobalData != 0, "_data is null in Global::" #Name, __FILE__, __LINE__); \
return GlobalData->Name; \
}
#define DefineGlobal(Type, Name) DefineGlobalReadOnly(Type, Name) \
void Set##Name(const Type &Name) { \
t_assert_full(GlobalData != 0, "_data is null in Global::Set" #Name, __FILE__, __LINE__); \
GlobalData->Name = Name; \
} \
Type &Ref##Name() { \
t_assert_full(GlobalData != 0, "_data is null in Global::Ref" #Name, __FILE__, __LINE__); \
return GlobalData->Name; \
}
DefineGlobalReadOnly(QString, LangSystemISO);
DefineGlobalReadOnly(int32, LangSystem);
DefineGlobal(QByteArray, LastCrashDump);
DefineGlobal(ConnectionProxy, PreLaunchProxy);
}
struct SandboxDataStruct {
uint64 LaunchId = 0;
};
SandboxDataStruct *SandboxData = 0;
namespace Sandbox {
bool started() {
return SandboxData != 0;
}
void start() {
SandboxData = new SandboxDataStruct();
memset_rand(&SandboxData->LaunchId, sizeof(SandboxData->LaunchId));
}
void finish() {
delete SandboxData;
SandboxData = 0;
}
#define DefineSandboxReadOnly(Type, Name) const Type &Name() { \
t_assert_full(SandboxData != 0, "_data is null in Global::" #Name, __FILE__, __LINE__); \
return SandboxData->Name; \
}
#define DefineSandboxRef(Type, Name) DefineSandboxReadOnly(Type, Name) \
Type &Ref##Name() { \
t_assert_full(SandboxData != 0, "_data is null in Global::Ref" #Name, __FILE__, __LINE__); \
return SandboxData->Name; \
}
#define DefineSandbox(Type, Name) DefineSandboxRef(Type, Name) \
void Set##Name(const Type &Name) { \
t_assert_full(SandboxData != 0, "_data is null in Global::Set" #Name, __FILE__, __LINE__); \
SandboxData->Name = Name; \
}
DefineSandboxReadOnly(uint64, LaunchId);
};

View File

@@ -37,7 +37,7 @@ namespace App {
};
namespace Ui { // openssl doesn't allow me to use UI :(
namespace Ui {
void showStickerPreview(DocumentData *sticker);
void hideStickerPreview();
@@ -95,3 +95,39 @@ namespace Notify {
void automaticLoadSettingsChangedGif();
};
namespace Global {
bool CheckBetaVersionDir();
void WorkingDirReady();
void start();
void finish();
#define DeclareGlobalReadOnly(Type, Name) const Type &Name();
#define DeclareGlobal(Type, Name) DeclareGlobalReadOnly(Type, Name) \
void Set##Name(const Type &Name); \
Type &Ref##Name();
DeclareGlobalReadOnly(QString, LangSystemISO);
DeclareGlobalReadOnly(int32, LangSystem);
DeclareGlobal(QByteArray, LastCrashDump);
DeclareGlobal(ConnectionProxy, PreLaunchProxy);
}
namespace Sandbox {
bool started();
void start();
void finish();
#define DeclareSandboxReadOnly(Type, Name) const Type &Name();
#define DeclareSandboxRef(Type, Name) DeclareSandboxReadOnly(Type, Name) \
Type &Ref##Name();
#define DeclareSandbox(Type, Name) DeclareSandboxRef(Type, Name) \
void Set##Name(const Type &Name);
DeclareSandboxReadOnly(uint64, LaunchId);
};

View File

@@ -189,18 +189,25 @@ QPixmap _prepareFrame(const ClipFrameRequest &request, const QImage &original, b
bool needOuter = (request.outerw != request.framew) || (request.outerh != request.frameh);
if (badSize || needOuter || hasAlpha || request.rounded) {
int32 factor(request.factor);
bool fill = false;
if (cache.width() != request.outerw || cache.height() != request.outerh) {
bool newcache = (cache.width() != request.outerw || cache.height() != request.outerh);
if (newcache) {
cache = QImage(request.outerw, request.outerh, QImage::Format_ARGB32_Premultiplied);
if (request.framew < request.outerw || request.frameh < request.outerh || hasAlpha) {
fill = true;
}
cache.setDevicePixelRatio(factor);
}
{
Painter p(&cache);
if (fill) {
p.fillRect(0, 0, cache.width() / factor, cache.height() / factor, st::black);
if (newcache) {
if (request.framew < request.outerw) {
p.fillRect(0, 0, (request.outerw - request.framew) / (2 * factor), cache.height() / factor, st::black);
p.fillRect((request.outerw - request.framew) / (2 * factor) + (request.framew / factor), 0, (cache.width() / factor) - ((request.outerw - request.framew) / (2 * factor) + (request.framew / factor)), cache.height() / factor, st::black);
}
if (request.frameh < request.outerh) {
p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), 0, qMin(cache.width(), request.framew) / factor, (request.outerh - request.frameh) / (2 * factor), st::black);
p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), (request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor), qMin(cache.width(), request.framew) / factor, (cache.height() / factor) - ((request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor)), st::black);
}
}
if (hasAlpha) {
p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), qMax(0, (request.outerh - request.frameh) / (2 * factor)), qMin(cache.width(), request.framew) / factor, qMin(cache.height(), request.frameh) / factor, st::white);
}
QPoint position((request.outerw - request.framew) / (2 * factor), (request.outerh - request.frameh) / (2 * factor));
if (badSize) {
@@ -477,7 +484,7 @@ public:
, _frameDelay(0) {
}
bool readNextFrame(QImage &to, bool &hasAlpha, const QSize &size) {
bool readNextFrame() {
if (_reader) _frameDelay = _reader->nextImageDelay();
if (_framesLeft < 1 && !jumpToStart()) {
return false;
@@ -587,6 +594,11 @@ public:
}
bool readNextFrame() {
if (_frameRead) {
av_frame_unref(_frame);
_frameRead = false;
}
int res;
while (true) {
if (_avpkt.size > 0) { // previous packet not finished
@@ -638,6 +650,20 @@ public:
}
if (got_frame) {
int64 duration = av_frame_get_pkt_duration(_frame);
int64 framePts = (_frame->pkt_pts == AV_NOPTS_VALUE) ? _frame->pkt_dts : _frame->pkt_pts;
int64 frameMs = (framePts * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den;
_currentFrameDelay = _nextFrameDelay;
if (_frameMs + _currentFrameDelay < frameMs) {
_currentFrameDelay = int32(frameMs - _frameMs);
}
if (duration == AV_NOPTS_VALUE) {
_nextFrameDelay = 0;
} else {
_nextFrameDelay = (duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den;
}
_frameMs = frameMs;
_hadFrame = _frameRead = true;
return true;
}
@@ -700,20 +726,6 @@ public:
}
}
int64 duration = av_frame_get_pkt_duration(_frame);
int64 framePts = (_frame->pkt_pts == AV_NOPTS_VALUE) ? _frame->pkt_dts : _frame->pkt_pts;
int64 frameMs = (framePts * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den;
_currentFrameDelay = _nextFrameDelay;
if (_frameMs + _currentFrameDelay < frameMs) {
_currentFrameDelay = int32(frameMs - _frameMs);
}
if (duration == AV_NOPTS_VALUE) {
_nextFrameDelay = 0;
} else {
_nextFrameDelay = (duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den;
}
_frameMs = frameMs;
av_frame_unref(_frame);
return true;
}
@@ -792,6 +804,10 @@ public:
}
~FFMpegReaderImplementation() {
if (_frameRead) {
av_frame_unref(_frame);
_frameRead = false;
}
if (_ioContext) av_free(_ioContext);
if (_codecContext) avcodec_close(_codecContext);
if (_swsContext) sws_freeContext(_swsContext);

View File

@@ -80,7 +80,6 @@ void Button::mouseReleaseEvent(QMouseEvent *e) {
}
void Button::setOver(bool over, ButtonStateChangeSource source) {
// LOG(("Set over: %1, by: %2 AT %3").arg(logBool(over)).arg(source).arg(dynamic_cast<IconedButton*>(this) ? dynamic_cast<IconedButton*>(this)->getText() : qsl("Unknown")));
if (over && !(_state & StateOver)) {
int oldState = _state;
_state |= StateOver;

View File

@@ -83,7 +83,7 @@ inline EmojiPtr emojiFromUrl(const QString &url) {
return emojiFromKey(url.midRef(10).toULongLong(0, 16)); // skip emoji://e.
}
inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) {
inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int *plen = 0) {
EmojiPtr emoji = 0;
if (ch + 1 < e && ((ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) || (((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0x20E3))) {
uint32 code = (ch->unicode() << 16) | (ch + 1)->unicode();
@@ -108,15 +108,15 @@ inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) {
} else if (ch + 2 < e && ((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0xFE0F && (ch + 2)->unicode() == 0x20E3) {
uint32 code = (ch->unicode() << 16) | (ch + 2)->unicode();
emoji = emojiGet(code);
len = emoji->len + 1;
if (plen) *plen = emoji->len + 1;
return emoji;
} else if (ch < e) {
emoji = emojiGet(ch->unicode());
Q_ASSERT(emoji != TwoSymbolEmoji);
t_assert(emoji != TwoSymbolEmoji);
}
if (emoji) {
len = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
int32 len = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
if (emoji->color && (ch + len + 1 < e && (ch + len)->isHighSurrogate() && (ch + len + 1)->isLowSurrogate())) { // color
uint32 color = ((uint32((ch + len)->unicode()) << 16) | uint32((ch + len + 1)->unicode()));
EmojiPtr col = emojiGet(emoji, color);
@@ -128,8 +128,21 @@ inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) {
}
}
}
if (plen) *plen = len;
}
return emoji;
}
inline EmojiPtr emojiFromText(const QString &text, int32 *plen = 0) {
return text.isEmpty() ? EmojiPtr(0) : emojiFromText(text.constBegin(), text.constEnd(), plen);
}
inline EmojiPtr emojiGetNoColor(EmojiPtr emoji) {
if (emoji && emoji->color && (emoji->color & 0xFFFF0000U) != 0xFFFF0000U) {
EmojiPtr result = emojiGet(emoji->code);
return (result == TwoSymbolEmoji) ? emojiGet(emoji->code, emoji->code2) : result;
}
return emoji;
}
@@ -180,7 +193,7 @@ inline QString replaceEmojis(const QString &text, EntitiesInText &entities) {
if (canFindEmoji) {
emojiFind(ch, e, newEmojiEnd, emojiCode);
}
while (currentEntity < entitiesCount && ch >= emojiStart + entities[currentEntity].offset + entities[currentEntity].length) {
++currentEntity;
}

View File

@@ -311,21 +311,19 @@ void EmojiButton::paintEvent(QPaintEvent *e) {
p.drawPixmap(t, App::sprite(), i);
}
QRect inner(QPoint((width() - st::emojiCircle.width()) / 2, st::emojiCircleTop), st::emojiCircle);
int32 full = 5760;
int32 start = qRound(full * float64(ms % uint64(st::emojiCirclePeriod)) / st::emojiCirclePeriod), part = qRound(full / st::emojiCirclePart);
p.setBrush(Qt::NoBrush);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.setPen(QPen(st::emojiCircleFg->c, st::emojiCircleLine));
p.setOpacity(a_opacity.current() * _opacity);
p.drawEllipse(inner);
p.setPen(QPen(st::white->c, st::emojiCircleLine));
p.setOpacity(loading);
p.drawArc(inner, (full - start) % full, part);
p.setPen(QPen(st::emojiCircleFg, st::emojiCircleLine));
p.setBrush(Qt::NoBrush);
p.setRenderHint(QPainter::HighQualityAntialiasing);
QRect inner(QPoint((width() - st::emojiCircle.width()) / 2, st::emojiCircleTop), st::emojiCircle);
if (loading > 0) {
int32 full = 5760;
int32 start = qRound(full * float64(ms % uint64(st::emojiCirclePeriod)) / st::emojiCirclePeriod), part = qRound(loading * full / st::emojiCirclePart);
p.drawArc(inner, start, full - part);
} else {
p.drawEllipse(inner);
}
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
}

View File

@@ -66,7 +66,7 @@ FlatInput::FlatInput(QWidget *parent, const style::flatInput &st, const QString
, _notingBene(0)
, _st(st) {
resize(_st.width, _st.height);
setFont(_st.font->f);
setAlignment(_st.align);
@@ -959,7 +959,7 @@ void InputArea::processDocumentContentsChange(int position, int charsAdded) {
const QChar *ch = t.constData(), *e = ch + t.size();
for (; ch != e; ++ch, ++fp) {
int32 emojiLen = 0;
emoji = emojiFromText(ch, e, emojiLen);
emoji = emojiFromText(ch, e, &emojiLen);
if (emoji) {
if (replacePosition >= 0) {
emoji = 0; // replace tilde char format first
@@ -1331,7 +1331,7 @@ InputField::InputField(QWidget *parent, const style::InputField &st, const QStri
connect(&_inner, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool)));
connect(&_inner, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool)));
if (App::wnd()) connect(&_inner, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu()));
setCursor(style::cur_text);
if (!val.isEmpty()) {
_inner.setPlainText(val);
@@ -1412,7 +1412,7 @@ void InputField::paintEvent(QPaintEvent *e) {
if (_st.iconSprite.pxWidth()) {
p.drawSpriteLeft(_st.iconPosition, width(), _st.iconSprite);
}
bool drawPlaceholder = _placeholderVisible;
if (_a_placeholderShift.animating()) {
p.setOpacity(a_placeholderOpacity.current());
@@ -1425,11 +1425,11 @@ void InputField::paintEvent(QPaintEvent *e) {
QRect r(rect().marginsRemoved(_st.textMargins + _st.placeholderMargins));
r.moveLeft(r.left() + a_placeholderLeft.current());
if (rtl()) r.moveLeft(width() - r.left() - r.width());
p.setFont(_st.font);
p.setPen(a_placeholderFg.current());
p.drawText(r, _placeholder, _st.placeholderAlign);
p.restore();
}
TWidget::paintEvent(e);
@@ -1663,7 +1663,7 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) {
}
int32 emojiLen = 0;
emoji = emojiFromText(ch, e, emojiLen);
emoji = emojiFromText(ch, e, &emojiLen);
if (emoji) {
if (replacePosition >= 0) {
emoji = 0; // replace tilde char format first
@@ -2028,7 +2028,7 @@ MaskedInputField::MaskedInputField(QWidget *parent, const style::InputField &st,
setStyle(&_inputFieldStyle);
QLineEdit::setTextMargins(0, 0, 0, 0);
setContentsMargins(0, 0, 0, 0);
setAttribute(Qt::WA_AcceptTouchEvents);
_touchTimer.setSingleShot(true);
connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer()));

View File

@@ -738,7 +738,7 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
const QChar *ch = t.constData(), *e = ch + t.size();
for (; ch != e; ++ch, ++fp) {
int32 emojiLen = 0;
emoji = emojiFromText(ch, e, emojiLen);
emoji = emojiFromText(ch, e, &emojiLen);
if (emoji) {
if (replacePosition >= 0) {
emoji = 0; // replace tilde char format first

View File

@@ -365,7 +365,7 @@ yi += stride;
#undef update
}
delete[] rgb;
}
}
@@ -444,7 +444,7 @@ QPixmap imagePix(QImage img, int32 w, int32 h, bool smooth, bool blurred, bool r
{
QPainter p(&result);
if (w < outerw || h < outerh) {
p.fillRect(0, 0, result.width(), result.height(), st::black->b);
p.fillRect(0, 0, result.width(), result.height(), st::black);
}
p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img);
}
@@ -459,7 +459,12 @@ QPixmap imagePix(QImage img, int32 w, int32 h, bool smooth, bool blurred, bool r
QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh) const {
if (!loading()) const_cast<Image*>(this)->load();
restore();
if (_data.isNull()) return blank()->pix();
if (_data.isNull()) {
if (h <= 0 && height() > 0) {
h = qRound(width() * w / float64(height()));
}
return blank()->pixNoCache(w, h, smooth, blurred, rounded, outerw, outerh);
}
if (isNull() && outerw > 0 && outerh > 0) {
outerw *= cIntRetinaFactor();
@@ -470,7 +475,15 @@ QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool roun
{
QPainter p(&result);
p.fillRect(0, 0, result.width(), result.height(), st::black);
if (w < outerw) {
p.fillRect(0, 0, (outerw - w) / 2, result.height(), st::black);
p.fillRect(((outerw - w) / 2) + w, 0, result.width() - (((outerw - w) / 2) + w), result.height(), st::black);
}
if (h < outerh) {
p.fillRect(qMax(0, (outerw - w) / 2), 0, qMin(result.width(), w), (outerh - h) / 2, st::black);
p.fillRect(qMax(0, (outerw - w) / 2), ((outerh - h) / 2) + h, qMin(result.width(), w), result.height() - (((outerh - h) / 2) + h), st::black);
}
p.fillRect(qMax(0, (outerw - w) / 2), qMax(0, (outerh - h) / 2), qMin(result.width(), w), qMin(result.height(), h), st::white);
}
if (rounded) imageRound(result);
@@ -774,11 +787,11 @@ StorageImage::StorageImage(const StorageImageLocation &location, QByteArray &byt
}
}
int32 StorageImage::width() const {
int32 StorageImage::countWidth() const {
return _location.width();
}
int32 StorageImage::height() const {
int32 StorageImage::countHeight() const {
return _location.height();
}
@@ -904,11 +917,11 @@ StorageImage *getImage(const StorageImageLocation &location, const QByteArray &b
WebImage::WebImage(const QString &url) : _url(url), _size(0), _width(0), _height(0) {
}
int32 WebImage::width() const {
int32 WebImage::countWidth() const {
return _width;
}
int32 WebImage::height() const {
int32 WebImage::countHeight() const {
return _height;
}

View File

@@ -154,14 +154,12 @@ public:
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;
virtual int32 width() const {
restore();
return _data.width();
int32 width() const {
return qMax(countWidth(), 1);
}
virtual int32 height() const {
restore();
return _data.height();
int32 height() const {
return qMax(countHeight(), 1);
}
virtual void load(bool loadFirst = false, bool prior = true) {
@@ -203,6 +201,16 @@ protected:
}
void invalidateSizeCache() const;
virtual int32 countWidth() const {
restore();
return _data.width();
}
virtual int32 countHeight() const {
restore();
return _data.height();
}
mutable QByteArray _saved, _format;
mutable bool _forgot;
mutable QPixmap _data;
@@ -283,9 +291,6 @@ public:
StorageImage(const StorageImageLocation &location, int32 size = 0);
StorageImage(const StorageImageLocation &location, QByteArray &bytes);
int32 width() const;
int32 height() const;
virtual void setInformation(int32 size, int32 width, int32 height);
virtual FileLoader *createLoader(LoadFromCloudSetting fromCloud, bool autoLoading);
@@ -297,6 +302,9 @@ protected:
StorageImageLocation _location;
int32 _size;
virtual int32 countWidth() const;
virtual int32 countHeight() const;
};
class DelayedStorageImage : public StorageImage {
@@ -341,12 +349,14 @@ public:
WebImage(const QString &url);
int32 width() const;
int32 height() const;
virtual void setInformation(int32 size, int32 width, int32 height);
virtual FileLoader *createLoader(LoadFromCloudSetting fromCloud, bool autoLoading);
protected:
virtual int32 countWidth() const;
virtual int32 countHeight() const;
private:
QString _url;
int32 _size, _width, _height;

View File

@@ -464,7 +464,7 @@ void PopupMenu::showMenu(const QPoint &p, PopupMenu *parent, PressSource source)
_parent = parent;
QPoint w = p - QPoint(0, _padding.top());
QRect r = App::app() ? App::app()->desktop()->screenGeometry(p) : QDesktopWidget().screenGeometry(p);
QRect r = Sandboxer::screenGeometry(p);
if (rtl()) {
if (w.x() - width() < r.x() - _padding.left()) {
if (_parent && w.x() + _parent->width() - _padding.left() - _padding.right() + width() - _padding.right() <= r.x() + r.width()) {
@@ -520,3 +520,157 @@ PopupMenu::~PopupMenu() {
}
#endif
}
PopupTooltip *PopupTooltipInstance = 0;
AbstractTooltipShower::~AbstractTooltipShower() {
if (PopupTooltipInstance && PopupTooltipInstance->_shower == this) {
PopupTooltipInstance->_shower = 0;
}
}
PopupTooltip::PopupTooltip() : TWidget(0)
, _shower(0)
, _st(0) {
PopupTooltipInstance = this;
setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::ToolTip | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_NoSystemBackground, true);
_showTimer.setSingleShot(true);
connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShow()));
}
void PopupTooltip::onShow() {
if (_shower) {
QString text = _shower->tooltipText();
if (text.isEmpty()) {
Hide();
} else {
PopupTooltipInstance->popup(_shower->tooltipPos(), text, _shower->tooltipSt());
}
}
}
bool PopupTooltip::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::Leave) {
_hideByLeaveTimer.start(10);
} else if (e->type() == QEvent::Enter) {
_hideByLeaveTimer.stop();
} else if (e->type() == QEvent::MouseMove) {
if ((QCursor::pos() - _point).manhattanLength() > QApplication::startDragDistance()) {
Hide();
}
}
return TWidget::eventFilter(o, e);
}
void PopupTooltip::onHideByLeave() {
Hide();
}
PopupTooltip::~PopupTooltip() {
if (PopupTooltipInstance == this) {
PopupTooltipInstance = 0;
}
}
void PopupTooltip::popup(const QPoint &m, const QString &text, const style::Tooltip *st) {
if (!_hideByLeaveTimer.isSingleShot()) {
_hideByLeaveTimer.setSingleShot(true);
connect(&_hideByLeaveTimer, SIGNAL(timeout()), this, SLOT(onHideByLeave()));
Sandboxer::installEventFilter(this);
}
_point = m;
_st = st;
_text = Text(_st->textFont, text, _textPlainOptions, _st->widthMax, true);
int32 addw = 2 * st::lineWidth + _st->textPadding.left() + _st->textPadding.right();
int32 addh = 2 * st::lineWidth + _st->textPadding.top() + _st->textPadding.bottom();
// count tooltip size
QSize s(addw + _text.maxWidth(), addh + _text.minHeight());
if (s.width() > _st->widthMax) {
s.setWidth(addw + _text.countWidth(_st->widthMax - addw));
s.setHeight(addh + _text.countHeight(s.width() - addw));
}
int32 maxh = addh + (_st->linesMax * _st->textFont->height);
if (s.height() > maxh) {
s.setHeight(maxh);
}
// count tooltip position
QPoint p(m + _st->shift);
if (rtl()) {
p.setX(m.x() - s.width() - _st->shift.x());
}
if (s.width() < 2 * _st->shift.x()) {
p.setX(m.x() - (s.width() / 2));
}
// adjust tooltip position
QRect r(QApplication::desktop()->screenGeometry(m));
if (r.x() + r.width() - _st->skip < p.x() + s.width() && p.x() + s.width() > m.x()) {
p.setX(qMax(r.x() + r.width() - int32(_st->skip) - s.width(), m.x() - s.width()));
}
if (r.x() + _st->skip > p.x() && p.x() < m.x()) {
p.setX(qMin(m.x(), r.x() + int32(_st->skip)));
}
if (r.y() + r.height() - _st->skip < p.y() + s.height()) {
p.setY(m.y() - s.height() - _st->skip);
}
if (r.y() > p.x()) {
p.setY(qMin(m.y() + _st->shift.y(), r.y() + r.height() - s.height()));
}
setGeometry(QRect(p, s));
_hideByLeaveTimer.stop();
show();
}
void PopupTooltip::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(rect(), _st->textBg);
p.fillRect(QRect(0, 0, width(), st::lineWidth), _st->textBorder);
p.fillRect(QRect(0, height() - st::lineWidth, width(), st::lineWidth), _st->textBorder);
p.fillRect(QRect(0, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
p.fillRect(QRect(width() - st::lineWidth, st::lineWidth, st::lineWidth, height() - 2 * st::lineWidth), _st->textBorder);
int32 lines = qFloor((height() - 2 * st::lineWidth - _st->textPadding.top() - _st->textPadding.bottom()) / _st->textFont->height);
p.setPen(_st->textFg);
_text.drawElided(p, st::lineWidth + _st->textPadding.left(), st::lineWidth + _st->textPadding.top(), width() - 2 * st::lineWidth - _st->textPadding.left() - _st->textPadding.right(), lines);
}
void PopupTooltip::hideEvent(QHideEvent *e) {
if (PopupTooltipInstance == this) {
Hide();
}
}
void PopupTooltip::Show(int32 delay, const AbstractTooltipShower *shower) {
if (!PopupTooltipInstance) {
new PopupTooltip();
}
PopupTooltipInstance->_shower = shower;
if (delay >= 0) {
PopupTooltipInstance->_showTimer.start(delay);
} else {
PopupTooltipInstance->onShow();
}
}
void PopupTooltip::Hide() {
if (PopupTooltip *instance = PopupTooltipInstance) {
PopupTooltipInstance = 0;
instance->_showTimer.stop();
instance->_hideByLeaveTimer.stop();
instance->hide();
instance->deleteLater();
}
}

View File

@@ -17,6 +17,8 @@
*/
#pragma once
#include "text.h"
class PopupMenu : public TWidget {
Q_OBJECT
@@ -105,3 +107,54 @@ private:
bool _deleteOnHide, _triggering, _deleteLater;
};
class AbstractTooltipShower {
public:
virtual QString tooltipText() const = 0;
virtual QPoint tooltipPos() const = 0;
virtual const style::Tooltip *tooltipSt() const {
return &st::defaultTooltip;
}
virtual ~AbstractTooltipShower();
};
class PopupTooltip : public TWidget {
Q_OBJECT
public:
bool eventFilter(QObject *o, QEvent *e);
static void Show(int32 delay, const AbstractTooltipShower *shower);
static void Hide();
~PopupTooltip();
public slots:
void onShow();
void onHideByLeave();
protected:
void paintEvent(QPaintEvent *e);
void hideEvent(QHideEvent *e);
private:
PopupTooltip();
void popup(const QPoint &p, const QString &text, const style::Tooltip *st);
friend class AbstractTooltipShower;
const AbstractTooltipShower *_shower;
QTimer _showTimer;
Text _text;
QPoint _point;
const style::Tooltip *_st;
QTimer _hideByLeaveTimer;
};

View File

@@ -36,7 +36,7 @@ namespace {
const QRegularExpression _reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$"));
const QRegularExpression _reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
const QRegularExpression _reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[\\w]{2,64}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
const QRegularExpression _reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{3,32}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
const QRegularExpression _reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{1,32}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
const QRegularExpression _reBotCommand(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])/[A-Za-z_0-9]{1,64}(@[A-Za-z_0-9]{5,32})?([\\W]|$)"));
const QRegularExpression _rePre(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10])(````?)[\\s\\S]+?(````?)([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"), QRegularExpression::UseUnicodePropertiesOption);
const QRegularExpression _reCode(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10])(`)[^\\n]+?(`)([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"), QRegularExpression::UseUnicodePropertiesOption);
@@ -263,7 +263,7 @@ const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink)
class TextParser {
public:
static Qt::LayoutDirection stringDirection(const QString &str, int32 from, int32 to) {
const ushort *p = reinterpret_cast<const ushort*>(str.unicode()) + from;
const ushort *end = p + (to - from);
@@ -340,7 +340,7 @@ public:
void getLinkData(const QString &original, QString &result, int32 &fullDisplayed) {
if (!original.isEmpty() && original.at(0) == '/') {
result = original;
fullDisplayed = -4; // bot command
fullDisplayed = -4; // bot command
} else if (!original.isEmpty() && original.at(0) == '@') {
result = original;
fullDisplayed = -3; // mention
@@ -454,7 +454,7 @@ public:
while (waitingEntity != entitiesEnd && waitingEntity->length <= 0) ++waitingEntity;
}
}
bool readCommand() {
const QChar *afterCmd = textSkipCommand(ptr, end, links.size() < 0x7FFF);
if (afterCmd == ptr) {
@@ -598,7 +598,7 @@ public:
void parseEmojiFromCurrent() {
int len = 0;
EmojiPtr e = emojiFromText(ptr - emojiLookback, end, len);
EmojiPtr e = emojiFromText(ptr - emojiLookback, end, &len);
if (!e) return;
for (int l = len - emojiLookback - 1; l > 0; --l) {
@@ -886,6 +886,8 @@ namespace {
void TextLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
PopupTooltip::Hide();
QString url = TextLink::encoded();
QRegularExpressionMatch telegramMeUser = QRegularExpression(qsl("^https?://telegram\\.me/([a-zA-Z0-9\\.\\_]+)/?(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
QRegularExpressionMatch telegramMeGroup = QRegularExpression(qsl("^https?://telegram\\.me/joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
@@ -912,6 +914,7 @@ void TextLink::onClick(Qt::MouseButton button) const {
void EmailLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
PopupTooltip::Hide();
QUrl url(qstr("mailto:") + _email);
if (!QDesktopServices::openUrl(url)) {
psOpenFile(url.toString(QUrl::FullyEncoded), true);
@@ -975,7 +978,7 @@ public:
void initParagraphBidi() {
if (!_parLength || !_parAnalysis.isEmpty()) return;
Text::TextBlocks::const_iterator i = _parStartBlock, e = _t->_blocks.cend(), n = i + 1;
bool ignore = false;
@@ -1111,6 +1114,15 @@ public:
if (_btype == TextBlockTText) {
TextBlock *t = static_cast<TextBlock*>(b);
if (t->_words.isEmpty()) { // no words in this block, spaces only => layout this block in the same line
last_rPadding += lpadding;
_lineHeight = qMax(_lineHeight, blockHeight);
longWordLine = false;
continue;
}
QFixed f_wLeft = _wLeft; // vars for saving state of the last word start
int32 f_lineHeight = _lineHeight; // f points to the last word-start element of t->_words
for (TextBlock::TextWords::const_iterator j = t->_words.cbegin(), en = t->_words.cend(), f = j; j != en; ++j) {
@@ -1166,28 +1178,6 @@ public:
f_wLeft = _wLeft;
f_lineHeight = _lineHeight;
}
if (lpadding > 0) { // no words in this block, spaces only
int32 elidedLineHeight = qMax(_lineHeight, blockHeight);
bool elidedLine = _elideLast && (_y + elidedLineHeight >= _yToElide);
if (elidedLine) {
_lineHeight = elidedLineHeight;
}
ushort nextStart = _blockEnd(_t, i, e);
if (!drawLine(nextStart, i + 1, e)) return;
_y += _lineHeight;
_lineHeight = qMax(0, blockHeight);
_lineStart = nextStart;
_lineStartBlock = blockIndex + 1;
last_rBearing = _rb;
last_rPadding = b->rpadding();
_wLeft = _w;
if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yToElide)) {
_wLeft -= _elideRemoveFromEnd;
}
longWordLine = true;
}
continue;
}
@@ -1340,7 +1330,7 @@ public:
*_getSymbolAfter = false;
*_getSymbolUpon = ((_lnkX >= _x) && (_lineStart > 0)) ? true : false;
}
return false;
return false;
} else if (_lnkX >= x + (_w - _wLeft)) {
if (_parDirection == Qt::RightToLeft) {
*_getSymbol = _lineStart;
@@ -2428,7 +2418,7 @@ private:
style::font _f;
QFixed _x, _w, _wLeft;
int32 _y, _yDelta, _lineHeight, _fontHeight;
// elided hack support
int32 _blocksSize;
int32 _elideSavedIndex;
@@ -2584,15 +2574,6 @@ void Text::setMarkedText(style::font font, const QString &text, const EntitiesIn
_font = font;
clean();
{
// QByteArray ba = text.toUtf8(); // chars for OS X crash investigation
// const char *ch = ba.constData();
// LOG(("STR: %1").arg(text));
// LOG(("BYTES: %1").arg(mb(ba.constData(), ba.size()).str()));
// for (int32 i = 0; i < text.size(); ++i) {
// LOG(("LETTER %1: '%2' - %3").arg(i).arg(text.at(i)).arg(text.at(i).unicode()));
// }
// int32 w = _font->width(text);
// QString newText; // utf16 of the text for emoji
// newText.reserve(8 * text.size());
// for (const QChar *ch = text.constData(), *e = ch + text.size(); ch != e; ++ch) {
@@ -2735,6 +2716,111 @@ void Text::removeSkipBlock() {
}
}
int32 Text::countWidth(int32 w) const {
QFixed width = w;
if (width < _minResizeWidth) width = _minResizeWidth;
if (width >= _maxWidth) {
return _maxWidth.ceil().toInt();
}
QFixed minWidthLeft = width, widthLeft = width, last_rBearing = 0, last_rPadding = 0;
bool longWordLine = true;
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) {
ITextBlock *b = *i;
TextBlockType _btype = b->type();
int32 blockHeight = _blockHeight(b, _font);
QFixed _rb = _blockRBearing(b);
if (_btype == TextBlockTNewline) {
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
if (widthLeft < minWidthLeft) {
minWidthLeft = widthLeft;
}
widthLeft = width - (b->f_width() - last_rBearing);
longWordLine = true;
continue;
}
QFixed lpadding = b->f_lpadding();
QFixed newWidthLeft = widthLeft - lpadding - last_rBearing - (last_rPadding + b->f_width() - _rb);
if (newWidthLeft >= 0) {
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
widthLeft = newWidthLeft;
longWordLine = false;
continue;
}
if (_btype == TextBlockTText) {
TextBlock *t = static_cast<TextBlock*>(b);
if (t->_words.isEmpty()) { // no words in this block, spaces only => layout this block in the same line
last_rPadding += lpadding;
longWordLine = false;
continue;
}
QFixed f_wLeft = widthLeft;
for (TextBlock::TextWords::const_iterator j = t->_words.cbegin(), e = t->_words.cend(), f = j; j != e; ++j) {
bool wordEndsHere = (j->width >= 0);
QFixed j_width = wordEndsHere ? j->width : -j->width;
QFixed newWidthLeft = widthLeft - lpadding - last_rBearing - (last_rPadding + j_width - j->f_rbearing());
lpadding = 0;
if (newWidthLeft >= 0) {
last_rBearing = j->f_rbearing();
last_rPadding = j->rpadding;
widthLeft = newWidthLeft;
if (wordEndsHere) {
longWordLine = false;
}
if (wordEndsHere || longWordLine) {
f_wLeft = widthLeft;
f = j + 1;
}
continue;
}
if (f != j) {
j = f;
widthLeft = f_wLeft;
j_width = (j->width >= 0) ? j->width : -j->width;
}
last_rBearing = j->f_rbearing();
last_rPadding = j->rpadding;
if (widthLeft < minWidthLeft) {
minWidthLeft = widthLeft;
}
widthLeft = width - (j_width - last_rBearing);
longWordLine = true;
f = j + 1;
f_wLeft = widthLeft;
}
continue;
}
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
if (widthLeft < minWidthLeft) {
minWidthLeft = widthLeft;
}
widthLeft = width - (b->f_width() - last_rBearing);
longWordLine = true;
continue;
}
if (widthLeft < minWidthLeft) {
minWidthLeft = widthLeft;
}
return (width - minWidthLeft).ceil().toInt();
}
int32 Text::countHeight(int32 w) const {
QFixed width = w;
if (width < _minResizeWidth) width = _minResizeWidth;
@@ -2762,8 +2848,8 @@ int32 Text::countHeight(int32 w) const {
longWordLine = true;
continue;
}
widthLeft -= b->f_lpadding();
QFixed newWidthLeft = widthLeft - last_rBearing - (last_rPadding + b->f_width() - _rb);
QFixed lpadding = b->f_lpadding();
QFixed newWidthLeft = widthLeft - lpadding - last_rBearing - (last_rPadding + b->f_width() - _rb);
if (newWidthLeft >= 0) {
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
@@ -2777,13 +2863,23 @@ int32 Text::countHeight(int32 w) const {
if (_btype == TextBlockTText) {
TextBlock *t = static_cast<TextBlock*>(b);
if (t->_words.isEmpty()) { // no words in this block, spaces only => layout this block in the same line
last_rPadding += lpadding;
lineHeight = qMax(lineHeight, blockHeight);
longWordLine = false;
continue;
}
QFixed f_wLeft = widthLeft;
int32 f_lineHeight = lineHeight;
for (TextBlock::TextWords::const_iterator j = t->_words.cbegin(), e = t->_words.cend(), f = j; j != e; ++j) {
bool wordEndsHere = (j->width >= 0);
QFixed j_width = wordEndsHere ? j->width : -j->width;
QFixed newWidthLeft = widthLeft - last_rBearing - (last_rPadding + j_width - j->f_rbearing());
QFixed newWidthLeft = widthLeft - lpadding - last_rBearing - (last_rPadding + j_width - j->f_rbearing());
lpadding = 0;
if (newWidthLeft >= 0) {
last_rBearing = j->f_rbearing();
last_rPadding = j->rpadding;
@@ -2913,7 +3009,7 @@ QString Text::original(uint16 selectedFrom, uint16 selectedTo, ExpandLinksMode m
result.reserve(_text.size());
int32 lnkFrom = 0, lnkIndex = 0;
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) {
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) {
int32 blockLnkIndex = (i == e) ? 0 : (*i)->lnkIndex();
int32 blockFrom = (i == e) ? _text.size() : (*i)->from();
if (blockLnkIndex != lnkIndex) {
@@ -3156,7 +3252,8 @@ namespace {
class BlockParser {
public:
BlockParser(QTextEngine *e, TextBlock *b, QFixed minResizeWidth, int32 blockFrom) : block(b), eng(e) {
BlockParser(QTextEngine *e, TextBlock *b, QFixed minResizeWidth, int32 blockFrom, const QString &str)
: block(b), eng(e), str(str) {
parseWords(minResizeWidth, blockFrom);
}
@@ -3234,7 +3331,7 @@ public:
if (lbh.currentPosition >= eng->layoutData->string.length()
|| attributes[lbh.currentPosition].whiteSpace
|| attributes[lbh.currentPosition].lineBreak) {
|| isLineBreak(attributes, lbh.currentPosition)) {
lbh.adjustRightBearing();
block->_words.push_back(TextWord(wordStart + blockFrom, lbh.tmpData.textWidth, qMin(QFixed(), lbh.rightBearing)));
block->_width += lbh.tmpData.textWidth;
@@ -3281,10 +3378,19 @@ public:
}
}
bool isLineBreak(const QCharAttributes *attributes, int32 index) {
bool lineBreak = attributes[index].lineBreak;
if (lineBreak && block->lnkIndex() > 0 && index > 0 && str.at(index - 1) == '/') {
return false; // don't break after / in links
}
return lineBreak;
}
private:
TextBlock *block;
QTextEngine *eng;
const QString &str;
};
@@ -3318,14 +3424,15 @@ TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResi
}
}
QStackTextEngine engine(str.mid(_from, length), blockFont->f);
QString part = str.mid(_from, length);
QStackTextEngine engine(part, blockFont->f);
engine.itemize();
QTextLayout layout(&engine);
layout.beginLayout();
layout.createLine();
BlockParser parser(&engine, this, minResizeWidth, _from);
BlockParser parser(&engine, this, minResizeWidth, _from, part);
layout.endLayout();
}
@@ -3687,7 +3794,7 @@ void initLinkSets() {
namespace {
// accent char list taken from https://github.com/aristus/accent-folding
inline QChar chNoAccent(int32 code) {
inline QChar chNoAccent(int32 code) {
switch (code) {
case 7834: return QChar(97);
case 193: return QChar(97);
@@ -4411,7 +4518,7 @@ QString textAccentFold(const QString &text) {
result[i] = noAccent;
} else {
if (copying) result[i] = *ch;
++ch, ++i;
++ch, ++i;
if (copying) result[i] = *ch;
}
} else {
@@ -4494,8 +4601,7 @@ goodCanBreakEntity = canBreakEntity;\
#undef MARK_GOOD_AS_LEVEL
int elen = 0;
EmojiPtr e = emojiFromText(ch, end, elen);
if (e) {
if (EmojiPtr e = emojiFromText(ch, end, &elen)) {
for (int i = 0; i < elen; ++i, ++ch, ++s) {
if (ch->isHighSurrogate() && i + 1 < elen && (ch + 1)->isLowSurrogate()) {
++ch;

View File

@@ -549,6 +549,7 @@ public:
Text(const Text &other);
Text &operator=(const Text &other);
int32 countWidth(int32 width) const;
int32 countHeight(int32 width) const;
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions);
void setRichText(style::font font, const QString &text, TextParseOptions options = _defaultOptions, const TextCustomTagsMap &custom = TextCustomTagsMap());

View File

@@ -22,6 +22,21 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "application.h"
namespace Fonts {
bool Started = false;
void start() {
if (!Started) {
Started = true;
QFontDatabase::addApplicationFont(qsl(":/gui/art/fonts/OpenSans-Regular.ttf"));
QFontDatabase::addApplicationFont(qsl(":/gui/art/fonts/OpenSans-Bold.ttf"));
QFontDatabase::addApplicationFont(qsl(":/gui/art/fonts/OpenSans-Semibold.ttf"));
}
}
}
namespace {
void _sendResizeEvents(QWidget *target) {
QResizeEvent e(target->size(), QSize());
@@ -44,7 +59,7 @@ void myEnsureResized(QWidget *target) {
}
QPixmap myGrab(TWidget *target, QRect rect) {
myEnsureResized(target);
myEnsureResized(target);
if (rect.isNull()) rect = target->rect();
QPixmap result(rect.size() * cRetinaFactor());

View File

@@ -24,6 +24,10 @@ namespace App {
const QPixmap &sprite();
}
namespace Fonts {
void start();
}
class Painter : public QPainter {
public:
explicit Painter(QPaintDevice *device) : QPainter(device) {

View File

@@ -321,8 +321,7 @@ void History::clearLastKeyboard() {
lastKeyboardFrom = 0;
}
bool History::updateTyping(uint64 ms, uint32 dots, bool force) {
if (!ms) ms = getms(true);
bool History::updateTyping(uint64 ms, bool force) {
bool changed = force;
for (TypingUsers::iterator i = typing.begin(), e = typing.end(); i != e;) {
if (ms >= i.value()) {
@@ -369,7 +368,7 @@ bool History::updateTyping(uint64 ms, uint32 dots, bool force) {
}
}
if (!typingStr.isEmpty()) {
if (typingText.lastDots(dots % 4)) {
if (typingText.lastDots(typingDots % 4)) {
changed = true;
}
}
@@ -1245,7 +1244,7 @@ void Histories::regSendAction(History *history, UserData *user, const MTPSendMes
return;
}
uint64 ms = getms(true);
uint64 ms = getms();
switch (action.type()) {
case mtpc_sendMessageTypingAction: history->typing[user] = ms + 6000; break;
case mtpc_sendMessageRecordVideoAction: history->sendActions.insert(user, SendAction(SendActionRecordVideo, ms + 6000)); break;
@@ -1264,17 +1263,16 @@ void Histories::regSendAction(History *history, UserData *user, const MTPSendMes
TypingHistories::const_iterator i = typing.find(history);
if (i == typing.cend()) {
typing.insert(history, ms);
history->typingFrame = 0;
history->typingDots = 0;
_a_typings.start();
}
history->updateTyping(ms, history->typingFrame, true);
_a_typings.start();
history->updateTyping(ms, true);
}
void Histories::step_typings(uint64 ms, bool timer) {
for (TypingHistories::iterator i = typing.begin(), e = typing.end(); i != e;) {
uint32 typingFrame = (ms - i.value()) / 150;
i.key()->updateTyping(ms, typingFrame);
i.key()->typingDots = (ms - i.value()) / 150;
i.key()->updateTyping(ms);
if (i.key()->typing.isEmpty() && i.key()->sendActions.isEmpty()) {
i = typing.erase(i);
} else {
@@ -1320,7 +1318,11 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo
result->attach(block);
}
if (msg.type() == mtpc_message) {
result->updateMedia(msg.c_message().has_media() ? (&msg.c_message().vmedia) : 0, (block ? false : true));
result->updateMedia(msg.c_message().has_media() ? (&msg.c_message().vmedia) : 0);
result->initDimensions();
if (!block) {
Notify::historyItemResized(result);
}
if (applyServiceAction) {
App::checkSavedGif(result);
}
@@ -1392,10 +1394,13 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo
case mtpc_messageMediaUnsupported:
default: badMedia = 1; break;
}
if (false && badMedia == 1) {
// QString text(lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org")));
if (badMedia == 1) {
QString text(lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org")));
EntitiesInText entities = textParseEntities(text, _historyTextNoMonoOptions.flags);
entities.push_front(EntityInText(EntityInTextItalic, 0, text.size()));
result = new HistoryMessage(this, block, m.vid.v, m.vflags.v, m.vvia_bot_id.v, date(m.vdate), m.vfrom_id.v, text, entities, 0);
} else if (badMedia) {
result = new HistoryServiceMsg(this, block, m.vid.v, date(m.vdate), lang((badMedia == 2) ? lng_message_empty : lng_media_unsupported), m.vflags.v, 0, m.has_from_id() ? m.vfrom_id.v : 0);
result = new HistoryServiceMsg(this, block, m.vid.v, date(m.vdate), lang(lng_message_empty), m.vflags.v, 0, m.has_from_id() ? m.vfrom_id.v : 0);
} else {
if ((m.has_fwd_date() && m.vfwd_date.v > 0) || (m.has_fwd_from_id() && peerFromMTP(m.vfwd_from_id) != 0)) {
result = new HistoryForwarded(this, block, m);
@@ -1815,16 +1820,16 @@ void History::unregTyping(UserData *from) {
uint64 updateAtMs = 0;
TypingUsers::iterator i = typing.find(from);
if (i != typing.end()) {
updateAtMs = getms(true);
updateAtMs = getms();
i.value() = updateAtMs;
}
SendActionUsers::iterator j = sendActions.find(from);
if (j != sendActions.end()) {
if (!updateAtMs) updateAtMs = getms(true);
if (!updateAtMs) updateAtMs = getms();
j.value().until = updateAtMs;
}
if (updateAtMs) {
updateTyping(updateAtMs, 0, true);
updateTyping(updateAtMs, true);
}
}
@@ -1913,11 +1918,6 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice, const QVector<MTPM
const MTPMessageGroup *groupsBegin = (isChannel() && collapsed) ? collapsed->constData() : 0, *groupsIt = groupsBegin, *groupsEnd = (isChannel() && collapsed) ? (groupsBegin + collapsed->size()) : 0;
HistoryItem *oldFirst = 0, *last = 0;
if (!blocks.isEmpty()) {
t_assert(blocks.size() > 1);
oldFirst = blocks.at(1)->items.front();
}
HistoryBlock *block = new HistoryBlock(this);
block->items.reserve(slice.size() + (collapsed ? collapsed->size() : 0));
for (QVector<MTPmessage>::const_iterator i = slice.cend(), e = slice.cbegin(); i != e;) {
@@ -1942,6 +1942,10 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice, const QVector<MTPM
last = addMessageGroupAfterPrevToBlock(group, last, block);
}
if (!blocks.isEmpty()) {
t_assert(blocks.size() > 1);
oldFirst = blocks.at(1)->items.front();
}
while (oldFirst && last && oldFirst->type() == HistoryItemGroup && last->type() == HistoryItemGroup) {
static_cast<HistoryGroup*>(last)->uniteWith(static_cast<HistoryGroup*>(oldFirst));
oldFirst->destroy();
@@ -2494,6 +2498,7 @@ MsgId History::msgIdForRead() const {
int32 History::geomResize(int32 newWidth, int32 *ytransform, const HistoryItem *resizedItem) {
if (width != newWidth) resizedItem = 0; // recount all items
if (width != newWidth || resizedItem) {
width = newWidth;
int32 y = 0;
for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) {
HistoryBlock *block = *i;
@@ -2508,7 +2513,6 @@ int32 History::geomResize(int32 newWidth, int32 *ytransform, const HistoryItem *
ytransform = 0;
}
}
width = newWidth;
height = y;
}
return height;
@@ -2596,10 +2600,10 @@ void History::overviewSliceDone(int32 overviewIndex, const MTPmessages_Messages
if (peer->isChannel()) {
peer->asChannel()->ptsReceived(d.vpts.v);
} else {
LOG(("API Error: received messages.channelMessages when no channel was passed! (History::overviewSliceDone, onlyCounts %1)").arg(logBool(onlyCounts)));
LOG(("API Error: received messages.channelMessages when no channel was passed! (History::overviewSliceDone, onlyCounts %1)").arg(Logs::b(onlyCounts)));
}
if (d.has_collapsed()) { // should not be returned
LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (History::overviewSliceDone, onlyCounts %1)").arg(logBool(onlyCounts)));
LOG(("API Error: channels.getMessages and messages.getMessages should not return collapsed groups! (History::overviewSliceDone, onlyCounts %1)").arg(Logs::b(onlyCounts)));
}
App::feedUsers(d.vusers);
@@ -3429,7 +3433,7 @@ void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x
}
}
void HistoryPhoto::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) {
void HistoryPhoto::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
if (media.type() == mtpc_messageMediaPhoto) {
const MTPPhoto &photo(media.c_messageMediaPhoto().vphoto);
App::feedPhoto(photo, _data);
@@ -3627,9 +3631,7 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b
}
QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
QPixmap pix = _data->thumb->pixBlurredSingle(_thumbw, 0, width, height);
p.drawPixmap(rthumb.topLeft(), pix);
p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_thumbw, 0, width, height));
if (selected) {
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
@@ -3928,7 +3930,7 @@ void HistoryAudio::unregItem(HistoryItem *item) {
App::unregAudioItem(_data, item);
}
void HistoryAudio::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) {
void HistoryAudio::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
if (media.type() == mtpc_messageMediaAudio) {
App::feedAudio(media.c_messageMediaAudio().vaudio, _data);
if (!_data->data().isEmpty()) {
@@ -3989,6 +3991,25 @@ HistoryDocument::HistoryDocument(DocumentData *document, const QString &caption,
if (!caption.isEmpty()) {
_caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent));
}
}
HistoryDocument::HistoryDocument(const HistoryDocument &other) : HistoryFileMedia()
, _data(other._data)
, _linksavel(new DocumentSaveLink(_data))
, _linkcancell(new DocumentCancelLink(_data))
, _name(other._name)
, _namew(other._namew)
, _thumbw(other._thumbw)
, _caption(other._caption) {
setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data));
setStatusSize(other._statusSize);
}
void HistoryDocument::initDimensions(const HistoryItem *parent) {
if (_caption.hasSkipBlock()) {
_caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight());
}
if (withThumb()) {
_data->thumb->load();
@@ -4001,24 +4022,6 @@ HistoryDocument::HistoryDocument(DocumentData *document, const QString &caption,
} else {
_thumbw = 0;
}
}
HistoryDocument::HistoryDocument(const HistoryDocument &other) : HistoryFileMedia()
, _data(other._data)
, _linksavel(new DocumentSaveLink(_data))
, _linkcancell(new DocumentCancelLink(_data))
, _name(other._name)
, _namew(other._namew)
, _thumbw(other._thumbw) {
setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data));
setStatusSize(other._statusSize);
}
void HistoryDocument::initDimensions(const HistoryItem *parent) {
if (_caption.hasSkipBlock()) {
_caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight());
}
_maxw = st::msgFileMinWidth;
@@ -4097,12 +4100,8 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r
bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom();
QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width));
if (_data->thumb->loaded()) {
QPixmap thumb = loaded ? _data->thumb->pixSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize);
p.drawPixmap(rthumb.topLeft(), thumb);
} else {
App::roundRect(p, rthumb, st::black, BlackCorners);
}
QPixmap thumb = loaded ? _data->thumb->pixSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize);
p.drawPixmap(rthumb.topLeft(), thumb);
if (selected) {
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
@@ -4347,7 +4346,7 @@ void HistoryDocument::unregItem(HistoryItem *item) {
App::unregDocumentItem(_data, item);
}
void HistoryDocument::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) {
void HistoryDocument::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
if (media.type() == mtpc_messageMediaDocument) {
App::feedDocument(media.c_messageMediaDocument().vdocument, _data);
}
@@ -4379,6 +4378,7 @@ HistoryGif::HistoryGif(const HistoryGif &other) : HistoryFileMedia()
, _data(other._data)
, _thumbw(other._thumbw)
, _thumbh(other._thumbh)
, _caption(other._caption)
, _gif(0) {
setLinks(new GifOpenLink(_data), new GifOpenLink(_data), new DocumentCancelLink(_data));
@@ -4575,7 +4575,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo
if (_data->loaded() && !radial) {
icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay);
} else if (radial || _data->loading()) {
if (parent->id > 0) {
if (parent->id > 0 || _data->uploading()) {
icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel);
}
} else {
@@ -4598,18 +4598,15 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo
p.setFont(st::normalFont);
p.setPen(st::white);
p.drawTextLeft(statusX, statusY, _width, _statusText, statusW - 2 * st::msgDateImgPadding.x());
// date
if (_caption.isEmpty() && parent->getMedia() == this) {
int32 fullRight = skipx + width, fullBottom = skipy + height;
parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
}
}
}
if (!_caption.isEmpty()) {
p.setPen(st::black);
_caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw);
} else if (parent->getMedia() == this && (_data->uploading() || App::hoveredItem() == parent)) {
int32 fullRight = skipx + width, fullBottom = skipy + height;
parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
}
}
@@ -4691,7 +4688,7 @@ void HistoryGif::unregItem(HistoryItem *item) {
App::unregDocumentItem(_data, item);
}
void HistoryGif::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) {
void HistoryGif::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
if (media.type() == mtpc_messageMediaDocument) {
App::feedDocument(media.c_messageMediaDocument().vdocument, _data);
}
@@ -4748,14 +4745,11 @@ bool HistoryGif::dataLoaded() const {
HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia()
, _pixw(1)
, _pixh(1)
, _data(document) {
, _data(document)
, _emoji(_data->sticker()->alt) {
_data->thumb->load();
if (!_data->sticker()->alt.isEmpty()) {
_emoji = _data->sticker()->alt;
int32 elen = 0;
if (EmojiPtr e = emojiFromText(_emoji.constData(), _emoji.constEnd(), elen)) {
_emoji = emojiString(e);
}
if (EmojiPtr e = emojiFromText(_emoji)) {
_emoji = emojiString(e);
}
}
@@ -4886,7 +4880,7 @@ void HistorySticker::unregItem(HistoryItem *item) {
App::unregDocumentItem(_data, item);
}
void HistorySticker::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) {
void HistorySticker::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
if (media.type() == mtpc_messageMediaDocument) {
App::feedDocument(media.c_messageMediaDocument().vdocument, _data);
if (!_data->data().isEmpty()) {
@@ -5059,14 +5053,12 @@ void HistoryContact::unregItem(HistoryItem *item) {
}
}
void HistoryContact::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) {
void HistoryContact::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
if (media.type() == mtpc_messageMediaContact) {
if (_userId != media.c_messageMediaContact().vuser_id.v) {
unregItem(parent);
_userId = media.c_messageMediaContact().vuser_id.v;
regItem(parent);
parent->initDimensions();
if (allowEmitResize) Notify::historyItemResized(parent);
}
}
}
@@ -5889,7 +5881,7 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, const QRect &
}
p.drawPixmap(QPoint(skipx, skipy), pix);
} else {
App::roundRect(p, skipx, skipy, width, height, st::black, BlackCorners);
App::roundRect(p, skipx, skipy, width, height, st::white, MessageInCorners);
}
if (selected) {
App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
@@ -5984,10 +5976,10 @@ void ViaInlineBotLink::onClick(Qt::MouseButton button) const {
}
HistoryMessageVia::HistoryMessageVia(int32 userId)
: bot(App::userLoaded(peerFromUser(userId)))
, width(0)
, maxWidth(st::msgServiceNameFont->width(qsl("via @") + bot->username))
, lnk(new ViaInlineBotLink(bot)) {
: bot(App::userLoaded(peerFromUser(userId)))
, width(0)
, maxWidth(bot ? st::msgServiceNameFont->width(lng_inline_bot_via(lt_inline_bot, '@' + bot->username)) : 0)
, lnk(new ViaInlineBotLink(bot)) {
}
bool HistoryMessageVia::isNull() const {
@@ -5995,22 +5987,17 @@ bool HistoryMessageVia::isNull() const {
}
void HistoryMessageVia::resize(int32 availw) {
if (width < maxWidth && availw > width) {
if (availw < 0) {
text = QString();
width = 0;
} else {
text = lng_inline_bot_via(lt_inline_bot, '@' + bot->username);
if (availw < maxWidth) {
text = st::msgServiceNameFont->elided(qsl("via @") + bot->username, availw);
text = st::msgServiceNameFont->elided(text, availw);
width = st::msgServiceNameFont->width(text);
} else {
text = qsl("via @") + bot->username;
} else if (width < maxWidth) {
width = maxWidth;
}
} else if (availw < width) {
if (availw > 0) {
text = st::msgServiceNameFont->elided(qsl("via @") + bot->username, availw);
width = st::msgServiceNameFont->width(text);
} else {
text = QString();
width = 0;
}
}
}
@@ -6029,11 +6016,11 @@ HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPD
}
HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities, HistoryMedia *fromMedia) :
HistoryItem(history, block, msgId, flags, date, from)
HistoryItem(history, block, msgId, flags, date, (flags & MTPDmessage::flag_from_id) ? from : 0)
, _text(st::msgMinWidth)
, _textWidth(0)
, _textHeight(0)
, _via(viaBotId ? new HistoryMessageVia(viaBotId) : 0)
, _via((flags & MTPDmessage::flag_via_bot_id) ? new HistoryMessageVia(viaBotId) : 0)
, _media(0)
, _views(fromChannel() ? 1 : -1) {
initTime();
@@ -6045,11 +6032,11 @@ HistoryItem(history, block, msgId, flags, date, from)
}
HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption) :
HistoryItem(history, block, msgId, flags, date, from)
HistoryItem(history, block, msgId, flags, date, (flags & MTPDmessage::flag_from_id) ? from : 0)
, _text(st::msgMinWidth)
, _textWidth(0)
, _textHeight(0)
, _via(viaBotId ? new HistoryMessageVia(viaBotId) : 0)
, _via((flags & MTPDmessage::flag_via_bot_id) ? new HistoryMessageVia(viaBotId) : 0)
, _media(0)
, _views(fromChannel() ? 1 : -1) {
initTime();
@@ -6058,11 +6045,11 @@ HistoryItem(history, block, msgId, flags, date, from)
}
HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption) :
HistoryItem(history, block, msgId, flags, date, from)
HistoryItem(history, block, msgId, flags, date, (flags & MTPDmessage::flag_from_id) ? from : 0)
, _text(st::msgMinWidth)
, _textWidth(0)
, _textHeight(0)
, _via(viaBotId ? new HistoryMessageVia(viaBotId) : 0)
, _via((flags & MTPDmessage::flag_via_bot_id) ? new HistoryMessageVia(viaBotId) : 0)
, _media(0)
, _views(fromChannel() ? 1 : -1) {
initTime();
@@ -6201,9 +6188,17 @@ void HistoryMessage::initDimensions() {
if (maxw > _maxw) _maxw = maxw;
_minh += _media->minHeight();
}
if (!_media && !displayFromName() && via() && !toHistoryForwarded()) {
if (st::msgPadding.left() + via()->maxWidth + st::msgPadding.right() > _maxw) {
_maxw = st::msgPadding.left() + via()->maxWidth + st::msgPadding.right();
if (!_media) {
if (displayFromName()) {
int32 namew = st::msgPadding.left() + _from->nameText.maxWidth() + st::msgPadding.right();
if (via() && !toHistoryForwarded()) {
namew += st::msgServiceFont->spacew + via()->maxWidth;
}
if (namew > _maxw) _maxw = namew;
} else if (via() && !toHistoryForwarded()) {
if (st::msgPadding.left() + via()->maxWidth + st::msgPadding.right() > _maxw) {
_maxw = st::msgPadding.left() + via()->maxWidth + st::msgPadding.right();
}
}
}
} else {
@@ -6211,21 +6206,25 @@ void HistoryMessage::initDimensions() {
_maxw = _media->maxWidth();
_minh = _media->minHeight();
}
fromNameUpdated();
}
void HistoryMessage::countPositionAndSize(int32 &left, int32 &width) const {
int32 mwidth = qMin(int(st::msgMaxWidth), _maxw);
int32 mwidth = qMin(int(st::msgMaxWidth), _maxw), hwidth = _history->width;
if (_media && _media->currentWidth() < mwidth) {
mwidth = qMax(_media->currentWidth(), qMin(mwidth, plainMaxWidth()));
}
left = (!fromChannel() && out()) ? st::msgMargin.right() : st::msgMargin.left();
left = 0;
if (hwidth > st::historyMaxWidth) {
left = (hwidth - st::historyMaxWidth) / 2;
hwidth = st::historyMaxWidth;
}
left += (!fromChannel() && out()) ? st::msgMargin.right() : st::msgMargin.left();
if (displayFromPhoto()) {
left += st::msgPhotoSkip;
}
width = _history->width - st::msgMargin.left() - st::msgMargin.right();
width = hwidth - st::msgMargin.left() - st::msgMargin.right();
if (width > mwidth) {
if (!fromChannel() && out()) {
left += width - mwidth;
@@ -6234,13 +6233,12 @@ void HistoryMessage::countPositionAndSize(int32 &left, int32 &width) const {
}
}
void HistoryMessage::fromNameUpdated() const {
if (!_media && displayFromName()) {
int32 namew = st::msgPadding.left() + _from->nameText.maxWidth() + st::msgPadding.right();
void HistoryMessage::fromNameUpdated(int32 width) const {
_fromVersion = _from->nameVersion;
if (drawBubble() && displayFromName()) {
if (via() && !toHistoryForwarded()) {
namew += st::msgServiceFont->spacew + via()->maxWidth;
via()->resize(width - st::msgPadding.left() - st::msgPadding.right() - _from->nameText.maxWidth() - st::msgServiceFont->spacew);
}
if (namew > _maxw) _maxw = namew;
}
}
@@ -6294,7 +6292,7 @@ HistoryMedia *HistoryMessage::getMedia(bool inOverview) const {
return _media;
}
void HistoryMessage::setMedia(const MTPMessageMedia *media, bool allowEmitResize) {
void HistoryMessage::setMedia(const MTPMessageMedia *media) {
if ((!_media || _media->isImageLink()) && (!media || media->type() == mtpc_messageMediaEmpty)) return;
bool mediaWasDisplayed = false;
@@ -6314,8 +6312,6 @@ void HistoryMessage::setMedia(const MTPMessageMedia *media, bool allowEmitResize
_textWidth = 0;
_textHeight = 0;
}
initDimensions();
if (allowEmitResize) Notify::historyItemResized(this);
}
void HistoryMessage::setText(const QString &text, const EntitiesInText &entities) {
@@ -6420,7 +6416,7 @@ void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, int32 width
}
}
void HistoryMessage::setViewsCount(int32 count) {
void HistoryMessage::setViewsCount(int32 count, bool reinit) {
if (_views == count || (count >= 0 && _views > count)) return;
int32 was = _viewsWidth;
@@ -6435,8 +6431,10 @@ void HistoryMessage::setViewsCount(int32 count) {
_textWidth = 0;
_textHeight = 0;
}
initDimensions();
Notify::historyItemResized(this);
if (reinit) {
initDimensions();
Notify::historyItemResized(this);
}
}
}
@@ -6475,13 +6473,12 @@ void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 m
}
}
if (_from->nameVersion > _fromVersion) {
// fromNameUpdated();
_fromVersion = _from->nameVersion;
}
int32 left = 0, width = 0;
countPositionAndSize(left, width);
if (_from->nameVersion > _fromVersion) {
fromNameUpdated(width);
}
if (displayFromPhoto()) {
p.drawPixmap(left - st::msgPhotoSkip, _height - st::msgMargin.bottom() - st::msgPhotoSize, _from->photo->pixRounded(st::msgPhotoSize));
}
@@ -6549,7 +6546,7 @@ void HistoryMessage::drawMessageText(Painter &p, QRect trect, uint32 selection)
p.setFont(st::msgFont);
uint16 selectedFrom = (selection == FullSelection) ? 0 : (selection >> 16) & 0xFFFF;
uint16 selectedTo = (selection == FullSelection) ? 0 : selection & 0xFFFF;
_text.draw(p, trect.x(), trect.y(), trect.width(), Qt::AlignLeft, 0, -1, selectedFrom, selectedTo);
_text.draw(p, trect.x(), trect.y(), trect.width(), style::al_left, 0, -1, selectedFrom, selectedTo);
}
void HistoryMessage::destroy() {
@@ -6586,17 +6583,20 @@ int32 HistoryMessage::resize(int32 width) {
}
if (media) _height += _media->resize(width, this);
}
if (displayFromName()) {
if (emptyText()) {
_height += st::msgPadding.top() + st::msgNameFont->height + st::mediaHeaderSkip;
} else {
_height += st::msgNameFont->height;
}
if (via() && !toHistoryForwarded()) {
via()->resize(width - st::msgPadding.left() - st::msgPadding.right() - _from->nameText.maxWidth() - st::msgServiceFont->spacew);
}
int32 l = 0, w = 0;
countPositionAndSize(l, w);
fromNameUpdated(w);
} else if (via() && !toHistoryForwarded()) {
via()->resize(width - st::msgPadding.left() - st::msgPadding.right());
int32 l = 0, w = 0;
countPositionAndSize(l, w);
via()->resize(w - st::msgPadding.left() - st::msgPadding.right());
if (emptyText() && !displayFromName()) {
_height += st::msgPadding.top() + st::msgNameFont->height + st::mediaHeaderSkip;
} else {
@@ -6795,7 +6795,7 @@ HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const
}
HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg)
: HistoryMessage(history, block, id, newMessageFlags(history->peer) | (!history->peer->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage::flag_media_unread : 0), msg->via() ? peerToUser(msg->viaBot()->id) : 0, date, from, msg->HistoryMessage::originalText(), msg->HistoryMessage::originalEntities(), msg->getMedia())
: HistoryMessage(history, block, id, newForwardedFlags(history->peer, from, msg), msg->via() ? peerToUser(msg->viaBot()->id) : 0, date, from, msg->HistoryMessage::originalText(), msg->HistoryMessage::originalEntities(), msg->getMedia())
, fwdDate(msg->dateForwarded())
, fwdFrom(msg->fromForwarded())
, fwdFromVersion(fwdFrom->nameVersion)
@@ -6825,6 +6825,11 @@ void HistoryForwarded::initDimensions() {
void HistoryForwarded::fwdNameUpdated() const {
QString fwdName((via() && fwdFrom->isUser()) ? fwdFrom->asUser()->firstName : App::peerName(fwdFrom));
fwdFromName.setText(st::msgServiceNameFont, fwdName, _textNameOptions);
if (via()) {
int32 l = 0, w = 0;
countPositionAndSize(l, w);
via()->resize(w - st::msgPadding.left() - st::msgPadding.right() - fromWidth - fwdFromName.maxWidth() - st::msgServiceFont->spacew);
}
}
void HistoryForwarded::draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const {
@@ -6877,7 +6882,9 @@ int32 HistoryForwarded::resize(int32 width) {
_height += st::msgServiceNameFont->height;
}
if (via()) {
via()->resize(width - st::msgPadding.left() - st::msgPadding.right() - fromWidth - fwdFromName.maxWidth() - st::msgServiceFont->spacew);
int32 l = 0, w = 0;
countPositionAndSize(l, w);
via()->resize(w - st::msgPadding.left() - st::msgPadding.right() - fromWidth - fwdFromName.maxWidth() - st::msgServiceFont->spacew);
}
}
}
@@ -7022,7 +7029,7 @@ void HistoryReply::initDimensions() {
if (!_media) {
int32 replyw = st::msgPadding.left() + _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.right();
if (replyToVia()) {
replyw += st::msgServiceFont->spacew + via()->maxWidth;
replyw += st::msgServiceFont->spacew + replyToVia()->maxWidth;
}
if (replyw > _maxw) _maxw = replyw;
}
@@ -7091,10 +7098,16 @@ HistoryItem *HistoryReply::replyToMessage() const {
void HistoryReply::replyToReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
if (replyToMsg == oldItem) {
delete _replyToVia;
_replyToVia = 0;
replyToMsg = newItem;
if (!newItem) {
replyToMsgId = 0;
initDimensions();
} else if (!replyToMsg->toHistoryForwarded()) {
if (UserData *bot = replyToMsg->viaBot()) {
_replyToVia = new HistoryMessageVia(peerToUser(bot->id));
}
}
}
}

View File

@@ -264,7 +264,7 @@ public:
}
void paintDialog(Painter &p, int32 w, bool sel) const;
bool updateTyping(uint64 ms = 0, uint32 dots = 0, bool force = false);
bool updateTyping(uint64 ms, bool force = false);
void clearLastKeyboard();
typedef QList<HistoryBlock*> Blocks;
@@ -310,7 +310,7 @@ public:
SendActionUsers sendActions;
QString typingStr;
Text typingText;
uint32 typingFrame;
uint32 typingDots;
QMap<SendActionType, uint64> mySendActions;
typedef QList<MsgId> MediaOverview;
@@ -897,7 +897,7 @@ public:
virtual bool serviceMsg() const {
return false;
}
virtual void updateMedia(const MTPMessageMedia *media, bool allowEmitResize) {
virtual void updateMedia(const MTPMessageMedia *media) {
}
virtual int32 addToOverview(AddToOverviewMethod method) {
return 0;
@@ -918,7 +918,7 @@ public:
virtual void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const {
}
virtual void setViewsCount(int32 count) {
virtual void setViewsCount(int32 count, bool reinit = true) {
}
virtual void setId(MsgId newId);
virtual void setDate(const QDateTime &date) { // for date items
@@ -1161,7 +1161,7 @@ public:
virtual void unregItem(HistoryItem *item) {
}
virtual void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) {
virtual void updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
}
virtual bool isImageLink() const {
@@ -1309,7 +1309,7 @@ public:
return _data;
}
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
@@ -1323,7 +1323,7 @@ public:
return _caption.original();
}
bool needsBubble(const HistoryItem *parent) const {
return !_caption.isEmpty() || parent->toHistoryReply() || parent->viaBot();
return !_caption.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply() || parent->viaBot();
}
bool customInfoLayout() const {
return _caption.isEmpty();
@@ -1331,9 +1331,6 @@ public:
bool hideFromName() const {
return true;
}
bool hideForwardedFrom() const {
return true;
}
protected:
@@ -1392,7 +1389,7 @@ public:
ImagePtr replyPreview();
bool needsBubble(const HistoryItem *parent) const {
return !_caption.isEmpty() || parent->toHistoryReply() || parent->viaBot();
return !_caption.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply() || parent->viaBot();
}
bool customInfoLayout() const {
return _caption.isEmpty();
@@ -1400,9 +1397,6 @@ public:
bool hideFromName() const {
return true;
}
bool hideForwardedFrom() const {
return true;
}
protected:
@@ -1457,7 +1451,7 @@ public:
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
bool needsBubble(const HistoryItem *parent) const {
return true;
@@ -1525,7 +1519,7 @@ public:
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
bool hasReplyPreview() const {
return !_data->thumb->isNull();
@@ -1617,7 +1611,7 @@ public:
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
bool hasReplyPreview() const {
return !_data->thumb->isNull();
@@ -1628,7 +1622,7 @@ public:
return _caption.original();
}
bool needsBubble(const HistoryItem *parent) const {
return !_caption.isEmpty() || parent->toHistoryReply() || parent->viaBot();
return !_caption.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply() || parent->viaBot();
}
bool customInfoLayout() const {
return _caption.isEmpty();
@@ -1636,9 +1630,6 @@ public:
bool hideFromName() const {
return true;
}
bool hideForwardedFrom() const {
return true;
}
~HistoryGif();
@@ -1695,7 +1686,7 @@ public:
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
bool needsBubble(const HistoryItem *parent) const {
return false;
@@ -1754,7 +1745,7 @@ public:
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize);
void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
bool needsBubble(const HistoryItem *parent) const {
return true;
@@ -1997,7 +1988,7 @@ public:
void initMedia(const MTPMessageMedia *media, QString &currentText);
void initMediaFromDocument(DocumentData *doc, const QString &caption);
void initDimensions();
void fromNameUpdated() const;
void fromNameUpdated(int32 width) const;
virtual HistoryMessageVia *via() const {
return (_via && !_via->isNull()) ? _via : 0;
@@ -2019,14 +2010,14 @@ public:
return drawBubble();
}
bool displayFromName() const {
return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || viaBot() || !_media->hideFromName());
return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || toHistoryForwarded() || viaBot() || !_media->hideFromName());
}
bool uploading() const {
return _media && _media->uploading();
}
void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const;
void setViewsCount(int32 count);
void setViewsCount(int32 count, bool reinit = true);
void setId(MsgId newId);
void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const;
@@ -2056,11 +2047,11 @@ public:
QString notificationHeader() const;
QString notificationText() const;
void updateMedia(const MTPMessageMedia *media, bool allowEmitResize) {
void updateMedia(const MTPMessageMedia *media) {
if (media && _media && _media->type() != MediaTypeWebPage) {
_media->updateFrom(*media, this, allowEmitResize);
_media->updateFrom(*media, this);
} else {
setMedia(media, allowEmitResize);
setMedia(media);
}
}
int32 addToOverview(AddToOverviewMethod method);
@@ -2069,7 +2060,7 @@ public:
QString selectedText(uint32 selection) const;
QString inDialogsText() const;
HistoryMedia *getMedia(bool inOverview = false) const;
void setMedia(const MTPMessageMedia *media, bool allowEmitResize);
void setMedia(const MTPMessageMedia *media);
void setText(const QString &text, const EntitiesInText &entities);
QString originalText() const;
EntitiesInText originalEntities() const;
@@ -2165,7 +2156,7 @@ public:
}
QString selectedText(uint32 selection) const;
bool displayForwardedFrom() const {
return via() || !_media || !_media->isDisplayed() || !_media->hideForwardedFrom();
return via() || !_media || !_media->isDisplayed() || (fwdFrom->isChannel() || !_media->hideForwardedFrom());
}
HistoryForwarded *toHistoryForwarded() {
@@ -2244,6 +2235,13 @@ protected:
};
inline int32 newMessageFlags(PeerData *p) {
return p->isSelf() ? 0 : (((p->isChat() || (p->isUser() && !p->asUser()->botInfo)) ? MTPDmessage::flag_unread : 0) | MTPDmessage::flag_out);
}
inline int32 newForwardedFlags(PeerData *p, int32 from, HistoryMessage *msg) {
return newMessageFlags(p) | (from ? MTPDmessage::flag_from_id : 0) | (msg->via() ? MTPDmessage::flag_via_bot_id : 0) | (!p->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage::flag_media_unread : 0);
}
class HistoryServiceMsg : public HistoryItem {
public:

View File

@@ -75,8 +75,6 @@ HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, His
, _menu(0) {
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
_tooltipTimer.setSingleShot(true);
connect(&_tooltipTimer, SIGNAL(timeout()), this, SLOT(showLinkTip()));
_touchSelectTimer.setSingleShot(true);
connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect()));
@@ -458,6 +456,7 @@ void HistoryInner::touchScrollUpdated(const QPoint &screenPos) {
QPoint HistoryInner::mapMouseToItem(QPoint p, HistoryItem *item) {
int32 msgy = itemTop(item);
if (msgy < 0) return QPoint(0, 0);
p.setY(p.y() - msgy);
return p;
}
@@ -1616,7 +1615,7 @@ void HistoryInner::onUpdateSelected() {
}
}
textlnkOver(lnk);
QToolTip::hideText();
PopupTooltip::Hide();
App::hoveredLinkItem((lnk && !lnkInDesc) ? item : 0);
if (textlnkOver()) {
if (HistoryItem *item = App::hoveredLinkItem()) {
@@ -1627,11 +1626,11 @@ void HistoryInner::onUpdateSelected() {
}
}
}
if (lnk || cursorState == HistoryInDateCursorState) {
_tooltipTimer.start(1000);
}
if (_dragCursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) {
QToolTip::hideText();
PopupTooltip::Hide();
}
if (lnk || cursorState == HistoryInDateCursorState) {
PopupTooltip::Show(1000, this);
}
if (_dragAction == NoDrag) {
@@ -1868,18 +1867,20 @@ void HistoryInner::applyDragSelection(SelectedItems *toItems) const {
}
}
void HistoryInner::showLinkTip() {
QString HistoryInner::tooltipText() const {
TextLinkPtr lnk = textlnkOver();
int32 dd = QApplication::startDragDistance();
QPoint dp(mapFromGlobal(_dragPos));
QRect r(dp.x() - dd, dp.y() - dd, 2 * dd, 2 * dd);
if (lnk && !lnk->fullDisplayed()) {
QToolTip::showText(_dragPos, lnk->readable(), this, r);
return lnk->readable();
} else if (_dragCursorState == HistoryInDateCursorState && _dragAction == NoDrag) {
if (App::hoveredItem()) {
QToolTip::showText(_dragPos, App::hoveredItem()->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)), this, r);
return App::hoveredItem()->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat));
}
}
return QString();
}
QPoint HistoryInner::tooltipPos() const {
return _dragPos;
}
void HistoryInner::onParentGeometryChanged() {
@@ -2019,9 +2020,6 @@ BotKeyboard::BotKeyboard() : TWidget()
setGeometry(0, 0, _st->margin, _st->margin);
_height = _st->margin;
setMouseTracking(true);
_cmdTipTimer.setSingleShot(true);
connect(&_cmdTipTimer, SIGNAL(timeout()), this, SLOT(showCommandTip()));
}
void BotKeyboard::paintEvent(QPaintEvent *e) {
@@ -2261,20 +2259,22 @@ void BotKeyboard::clearSelection() {
}
}
void BotKeyboard::showCommandTip() {
QPoint BotKeyboard::tooltipPos() const {
return _lastMousePos;
}
QString BotKeyboard::tooltipText() const {
if (_sel >= 0) {
int row = (_sel / MatrixRowShift), col = _sel % MatrixRowShift;
if (!_btns.at(row).at(col).full) {
int32 dd = QApplication::startDragDistance();
QPoint dp(mapFromGlobal(_lastMousePos));
QRect r(dp.x() - dd, dp.y() - dd, 2 * dd, 2 * dd);
QToolTip::showText(_lastMousePos, _btns.at(row).at(col).cmd, this, r);
return _btns.at(row).at(col).cmd;
}
}
return QString();
}
void BotKeyboard::updateSelected() {
_cmdTipTimer.start(1000);
PopupTooltip::Show(1000, this);
if (_down >= 0) return;
@@ -2294,7 +2294,7 @@ void BotKeyboard::updateSelected() {
if (newSel >= 0) break;
}
if (newSel != _sel) {
QToolTip::hideText();
PopupTooltip::Hide();
if (newSel < 0) {
setCursor(style::cur_default);
} else if (_sel < 0) {
@@ -2763,6 +2763,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
_attachMention.hide();
connect(&_attachMention, SIGNAL(chosen(QString)), this, SLOT(onMentionHashtagOrBotCommandInsert(QString)));
connect(&_attachMention, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*)));
_field.installEventFilter(&_attachMention);
_field.setCtrlEnterSubmit(cCtrlEnter());
@@ -2807,7 +2808,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
}
void HistoryWidget::start() {
connect(App::main(), SIGNAL(stickersUpdated()), &_emojiPan, SLOT(refreshStickers()));
connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated()));
connect(App::main(), SIGNAL(savedGifsUpdated()), &_emojiPan, SLOT(refreshSavedGifs()));
updateRecentStickers();
@@ -2816,6 +2817,11 @@ void HistoryWidget::start() {
connect(App::api(), SIGNAL(fullPeerUpdated(PeerData*)), this, SLOT(onFullPeerUpdated(PeerData*)));
}
void HistoryWidget::onStickersUpdated() {
_emojiPan.refreshStickers();
updateStickersByEmoji();
}
void HistoryWidget::onMentionHashtagOrBotCommandInsert(QString str) {
if (str.at(0) == '/') { // bot command
App::sendBotCommand(str);
@@ -2867,8 +2873,23 @@ void HistoryWidget::updateInlineBotQuery() {
}
}
void HistoryWidget::updateStickersByEmoji() {
int32 len = 0;
if (EmojiPtr emoji = emojiFromText(_field.getLastText(), &len)) {
if (_field.getLastText().size() <= len) {
_attachMention.showStickers(emoji);
} else {
len = 0;
}
}
if (!len) {
_attachMention.showStickers(EmojiPtr(0));
}
}
void HistoryWidget::onTextChange() {
updateInlineBotQuery();
updateStickersByEmoji();
if (_peer && (!_peer->isChannel() || _peer->isMegagroup() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) {
if (!_inlineBot && (_textUpdateEventsFlags & TextUpdateEventsSendTyping)) {
@@ -3143,24 +3164,24 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
for (int32 i = 0, l = d_sets.size(); i != l; ++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);
StickerSets::iterator it = sets.find(set.vid.v);
QString title = stickerSetTitle(set);
if (i == sets.cend()) {
i = sets.insert(set.vid.v, StickerSet(set.vid.v, set.vaccess_hash.v, title, qs(set.vshort_name), set.vcount.v, set.vhash.v, set.vflags.v | MTPDstickerSet_flag_NOT_LOADED));
if (it == sets.cend()) {
it = sets.insert(set.vid.v, StickerSet(set.vid.v, set.vaccess_hash.v, title, qs(set.vshort_name), set.vcount.v, set.vhash.v, set.vflags.v | MTPDstickerSet_flag_NOT_LOADED));
} else {
i->access = set.vaccess_hash.v;
i->title = title;
i->shortName = qs(set.vshort_name);
i->flags = set.vflags.v;
if (i->count != set.vcount.v || i->hash != set.vhash.v) {
i->count = set.vcount.v;
i->hash = set.vhash.v;
i->flags |= MTPDstickerSet_flag_NOT_LOADED; // need to request this set
it->access = set.vaccess_hash.v;
it->title = title;
it->shortName = qs(set.vshort_name);
it->flags = set.vflags.v;
if (it->count != set.vcount.v || it->hash != set.vhash.v || it->emoji.isEmpty()) {
it->count = set.vcount.v;
it->hash = set.vhash.v;
it->flags |= MTPDstickerSet_flag_NOT_LOADED; // need to request this set
}
}
if (!(i->flags & MTPDstickerSet::flag_disabled) || (i->flags & MTPDstickerSet::flag_official)) {
if (!(it->flags & MTPDstickerSet::flag_disabled) || (it->flags & MTPDstickerSet::flag_official)) {
setsOrder.push_back(set.vid.v);
if (i->stickers.isEmpty() || (i->flags & MTPDstickerSet_flag_NOT_LOADED)) {
if (it->stickers.isEmpty() || (it->flags & MTPDstickerSet_flag_NOT_LOADED)) {
setsToRequest.insert(set.vid.v, set.vaccess_hash.v);
}
}
@@ -4783,10 +4804,11 @@ void HistoryWidget::onDocumentSelect() {
}
}
void HistoryWidget::dragEnterEvent(QDragEnterEvent *e) {
if (!_history) return;
if (_peer && (_peer->isChannel() && !_peer->asChannel()->canPublish())) return;
_attachDrag = getDragState(e->mimeData());
updateDragAreas();
@@ -5129,6 +5151,8 @@ void HistoryWidget::onPhotoDrop(const QMimeData *data) {
void HistoryWidget::onDocumentDrop(const QMimeData *data) {
if (!_history) return;
if (_peer && (_peer->isChannel() && !_peer->asChannel()->canPublish())) return;
QStringList files = getMediasFromMime(data);
if (files.isEmpty()) return;
@@ -5136,6 +5160,9 @@ void HistoryWidget::onDocumentDrop(const QMimeData *data) {
}
void HistoryWidget::onFilesDrop(const QMimeData *data) {
if (_peer && (_peer->isChannel() && !_peer->asChannel()->canPublish())) return;
QStringList files = getMediasFromMime(data);
if (files.isEmpty()) {
if (data->hasImage()) {
@@ -5405,10 +5432,10 @@ void HistoryWidget::onFieldFocused() {
}
void HistoryWidget::onCheckMentionDropdown() {
if (!_history || _a_show.animating() || _inlineBot) return;
if (!_history || _a_show.animating()) return;
bool start = false;
QString query = _field.getMentionHashtagBotCommandPart(start);
QString query = _inlineBot ? QString() : _field.getMentionHashtagBotCommandPart(start);
if (!query.isEmpty()) {
if (query.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtagsAndBots();
if (query.at(0) == '@' && cRecentInlineBots().isEmpty()) Local::readRecentHashtagsAndBots();
@@ -6368,11 +6395,11 @@ void HistoryWidget::onFieldTabbed() {
}
void HistoryWidget::onStickerSend(DocumentData *sticker) {
sendExistingDocument(sticker, QString(), 0);
sendExistingDocument(sticker, QString());
}
void HistoryWidget::onPhotoSend(PhotoData *photo) {
sendExistingPhoto(photo, QString(), 0);
sendExistingPhoto(photo, QString());
}
void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) {
@@ -6495,7 +6522,7 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) {
_field.setFocus();
}
void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &caption, UserData *bot) {
void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &caption) {
if (!_history || !doc || !canSendMessages(_peer)) return;
App::main()->readServerHistory(_history, false);
@@ -6519,7 +6546,7 @@ void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti
} else {
flags |= MTPDmessage::flag_from_id;
}
_history->addNewDocument(newId.msg, flags, bot ? peerToUser(bot->id) : 0, replyToId(), date(MTP_int(unixtime())), fromChannelName ? 0 : MTP::authedId(), doc, caption);
_history->addNewDocument(newId.msg, flags, 0, replyToId(), date(MTP_int(unixtime())), fromChannelName ? 0 : MTP::authedId(), doc, caption);
_history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_int(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaDocument(MTP_inputDocument(MTP_long(doc->id), MTP_long(doc->access)), MTP_string(caption)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId);
App::main()->finishForwarding(_history, _broadcast.checked());
@@ -6529,6 +6556,13 @@ void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti
App::historyRegRandom(randomId, newId);
if (_attachMention.stickersShown()) {
setFieldText(QString());
_saveDraftText = true;
_saveDraftStart = getms();
onDraftSave();
}
if (!_attachMention.isHidden()) _attachMention.hideStart();
if (!_attachType.isHidden()) _attachType.hideStart();
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
@@ -6536,7 +6570,7 @@ void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti
_field.setFocus();
}
void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption, UserData *bot) {
void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption) {
if (!_history || !photo || !canSendMessages(_peer)) return;
App::main()->readServerHistory(_history, false);
@@ -6560,7 +6594,7 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption,
} else {
flags |= MTPDmessage::flag_from_id;
}
_history->addNewPhoto(newId.msg, flags, bot ? peerToUser(bot->id) : 0, replyToId(), date(MTP_int(unixtime())), fromChannelName ? 0 : MTP::authedId(), photo, caption);
_history->addNewPhoto(newId.msg, flags, 0, replyToId(), date(MTP_int(unixtime())), fromChannelName ? 0 : MTP::authedId(), photo, caption);
_history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_int(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaPhoto(MTP_inputPhoto(MTP_long(photo->id), MTP_long(photo->access)), MTP_string(caption)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId);
App::main()->finishForwarding(_history, _broadcast.checked());
@@ -6813,7 +6847,9 @@ void HistoryWidget::updatePreview() {
void HistoryWidget::onCancel() {
if (_inlineBot && _field.getLastText().startsWith('@' + _inlineBot->username + ' ')) {
setFieldText(QString(), TextUpdateEventsSaveDraft, false);
} else {
} else if (!_attachMention.isHidden()) {
_attachMention.hideStart();
} else {
Ui::showChatsList();
emit cancelled();
}

View File

@@ -33,7 +33,7 @@ enum DragState {
};
class HistoryWidget;
class HistoryInner : public TWidget {
class HistoryInner : public TWidget, public AbstractTooltipShower {
Q_OBJECT
public:
@@ -97,6 +97,10 @@ public:
void notifyIsBotChanged();
void notifyMigrateUpdated();
// AbstractTooltipShower
virtual QString tooltipText() const;
virtual QPoint tooltipPos() const;
~HistoryInner();
public slots:
@@ -104,8 +108,6 @@ public slots:
void onUpdateSelected();
void onParentGeometryChanged();
void showLinkTip();
void openContextUrl();
void copyContextUrl();
void saveContextImage();
@@ -150,8 +152,6 @@ private:
bool _firstLoading;
QTimer _tooltipTimer;
Qt::CursorShape _cursor;
typedef QMap<HistoryItem*, uint32> SelectedItems;
SelectedItems _selected;
@@ -249,7 +249,7 @@ private:
};
class BotKeyboard : public TWidget {
class BotKeyboard : public TWidget, public AbstractTooltipShower {
Q_OBJECT
public:
@@ -277,9 +277,12 @@ public:
return _wasForMsgId;
}
// AbstractTooltipShower
virtual QString tooltipText() const;
virtual QPoint tooltipPos() const;
public slots:
void showCommandTip();
void updateSelected();
private:
@@ -290,7 +293,6 @@ private:
FullMsgId _wasForMsgId;
int32 _height, _maxOuterHeight;
bool _maximizeSize, _singleUse, _forceReply;
QTimer _cmdTipTimer;
QPoint _lastMousePos;
struct Button {
@@ -452,6 +454,7 @@ public:
void updateFieldPlaceholder();
void updateInlineBotQuery();
void updateStickersByEmoji();
void uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, const QString &source = QString(), bool withText = false);
void uploadFile(const QString &file, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, bool withText = false); // with confirmation
@@ -643,6 +646,7 @@ public slots:
void onCmdStart();
void activate();
void onStickersUpdated();
void onMentionHashtagOrBotCommandInsert(QString str);
void onTextChange();
@@ -693,8 +697,8 @@ private:
IconedButton _replyForwardPreviewCancel;
void updateReplyToName();
void sendExistingDocument(DocumentData *doc, const QString &caption, UserData *bot);
void sendExistingPhoto(PhotoData *photo, const QString &caption, UserData *bot);
void sendExistingDocument(DocumentData *doc, const QString &caption);
void sendExistingPhoto(PhotoData *photo, const QString &caption);
void drawField(Painter &p);
void drawRecordButton(Painter &p);

View File

@@ -47,7 +47,7 @@ namespace {
emit signalEmitOn->countryChanged();
}
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
if (App::app()) App::app()->startUpdateCheck();
Sandboxer::startUpdateCheck();
#endif
}
}

View File

@@ -230,7 +230,7 @@ void IntroPhone::phoneCheckDone(const MTPauth_CheckedPhone &result) {
checkRequest.start(1000);
sentRequest = MTP::send(MTPauth_SendCode(MTP_string(sentPhone), MTP_int(5), MTP_int(ApiId), MTP_string(ApiHash), MTP_string(Application::language())), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
sentRequest = MTP::send(MTPauth_SendCode(MTP_string(sentPhone), MTP_int(5), MTP_int(ApiId), MTP_string(ApiHash), MTP_string(Global::LangSystemISO())), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
} else {
showError(lang(lng_bad_phone_noreg), true);
enableAll(true);
@@ -240,7 +240,7 @@ void IntroPhone::phoneCheckDone(const MTPauth_CheckedPhone &result) {
void IntroPhone::phoneSubmitDone(const MTPauth_SentCode &result) {
stopCheck();
enableAll(false);
if (result.type() == mtpc_auth_sentCode) {
const MTPDauth_sentCode &d(result.c_auth_sentCode());
intro()->setPhone(sentPhone, d.vphone_code_hash.c_string().v.c_str(), mtpIsTrue(d.vphone_registered));
@@ -260,7 +260,7 @@ void IntroPhone::toSignUp() {
checkRequest.start(1000);
sentRequest = MTP::send(MTPauth_SendCode(MTP_string(sentPhone), MTP_int(0), MTP_int(ApiId), MTP_string(ApiHash), MTP_string(Application::language())), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
sentRequest = MTP::send(MTPauth_SendCode(MTP_string(sentPhone), MTP_int(0), MTP_int(ApiId), MTP_string(ApiHash), MTP_string(Global::LangSystemISO())), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
}
bool IntroPhone::phoneSubmitFail(const RPCError &error) {

View File

@@ -36,7 +36,7 @@ _next(this, lang(lng_start_msgs), st::btnIntroNext) {
_changeLang.hide();
if (cLang() == languageDefault) {
int32 l = App::app()->languageId();
int32 l = Global::LangSystem();
if (l != languageDefault) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[l] + qsl(".strings"), LangLoaderRequest(lng_switch_to_this));
QString text = loader.found().value(lng_switch_to_this);

View File

@@ -78,7 +78,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_weekday7_full" = "Sonntag";
"lng_month_day" = "{day}. {month}";
"lng_month_day_year" = "{day} {month}, {year}";
"lng_month_day_year" = "{day}. {month} {year}";
"lng_month_year" = "{month}, {year}";
"lng_box_ok" = "OK";
@@ -454,7 +454,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_chat_all_members_admins" = "Alle Mitglieder Sind Admins";
"lng_chat_about_all_admins" = "Gruppenmitglieder können neue Leute hinzufügen sowie den Gruppennamen und das Bild ändern.";
"lng_chat_about_admins" = "Nur Admins können neue neue Leute hinzufügen und entfernen, den Gruppennamen und das Bild ändern.";
"lng_chat_about_admins" = "Nur Admins können neue Leute hinzufügen und entfernen, den Gruppennamen und das Bild ändern.";
"lng_participant_filter" = "Suche";
"lng_participant_invite" = "Einladen";
@@ -498,7 +498,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_sure_delete_group" = "Sicher, dass du diese Gruppe löschen willst? Alle Mitglieder und Nachrichten werden entfernt.";
"lng_message_empty" = "Leere Nachricht";
"lng_media_unsupported" = "Format Nicht Unterstützt";
"lng_message_unsupported" = "Diese Nachricht wird von deiner Telegram Desktop Version nicht unterstützt. Bitte aktualisiere sie in den Einstellungen oder über {link}";
"lng_action_add_user" = "{from} hat {user} hinzugefügt";
"lng_action_add_users_many" = "{from} hat {users} hinzugefügt";
@@ -600,6 +600,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_saved_gifs" = "Gespeicherte GIFs";
"lng_inline_bot_results" = "Ergebnisse von {inline_bot}";
"lng_inline_bot_no_results" = "Keine Ergebnisse";
"lng_inline_bot_via" = "via {inline_bot}";
"lng_box_remove" = "Entfernen";
@@ -793,7 +794,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_selected_delete" = "Löschen";
"lng_selected_forward" = "Weiterleiten";
"lng_selected_count" = "{count:_not_used_|# Nachricht|# Nachrichten}";
"lng_selected_cancel_sure_this" = "Upload wirklich abbrechen?";
"lng_selected_cancel_sure_this" = "Upload abbrechen?";
"lng_selected_upload_stop" = "Abbrechen";
"lng_selected_delete_sure_this" = "Diese Nachricht wirklich löschen?";
"lng_selected_delete_sure" = "Willst du {count:_not_used_|# Nachricht|# Nachrichten} löschen?";
"lng_box_delete" = "Löschen";
@@ -877,7 +879,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_mac_menu_redo" = "Wiederholen";
"lng_mac_menu_cut" = "Ausschneiden";
"lng_mac_menu_copy" = "Kopieren";
"lng_mac_menu_paste" = "Einsetzen";
"lng_mac_menu_paste" = "Einfügen";
"lng_mac_menu_delete" = "Löschen";
"lng_mac_menu_select_all" = "Alles auswählen";
"lng_mac_menu_window" = "Fenster";

View File

@@ -498,7 +498,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_sure_delete_group" = "¿Quieres eliminar este grupo? Todos los miembros y mensajes se perderán.";
"lng_message_empty" = "Mensaje vacío";
"lng_media_unsupported" = "Multimedia no soportada";
"lng_message_unsupported" = "Este mensaje no es soportado por tu versión de Telegram Desktop. Por favor, actualiza a la última versión desde Ajustes o instálala desde {link}";
"lng_action_add_user" = "{from} añadió a {user}";
"lng_action_add_users_many" = "{from} añadió a {users}";
@@ -600,6 +600,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_saved_gifs" = "GIF guardados";
"lng_inline_bot_results" = "Resultados de {inline_bot}";
"lng_inline_bot_no_results" = "Sin resultados";
"lng_inline_bot_via" = "vía {inline_bot}";
"lng_box_remove" = "Eliminar";
@@ -793,7 +794,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_selected_delete" = "Eliminar";
"lng_selected_forward" = "Reenviar";
"lng_selected_count" = "{count:_not_used_|# mensaje|# mensajes}";
"lng_selected_cancel_sure_this" = "¿Quieres cancelar este envío?";
"lng_selected_cancel_sure_this" = "¿Cancelar envío?";
"lng_selected_upload_stop" = "Detener";
"lng_selected_delete_sure_this" = "¿Quieres eliminar este mensaje?";
"lng_selected_delete_sure" = "¿Quieres eliminar {count:_not_used_|# mensaje|# mensajes}?";
"lng_box_delete" = "Eliminar";

View File

@@ -498,7 +498,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_sure_delete_group" = "Sei sicuro di voler eliminare questo gruppo? Tutti i membri verranno rimossi e i messaggi verranno persi.";
"lng_message_empty" = "Messaggio vuoto";
"lng_media_unsupported" = "Media non supportato";
"lng_message_unsupported" = "Questo messaggio non è supportato dalla tua versione di Telegram Desktop. Per favore, aggiorna all'ultima versione dalle Impostazioni o installalo da {link}";
"lng_action_add_user" = "{from} ha aggiunto {user}";
"lng_action_add_users_many" = "{from} ha aggiunto {users}";
@@ -600,6 +600,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_saved_gifs" = "GIF salvate";
"lng_inline_bot_results" = "Risultati da {inline_bot}";
"lng_inline_bot_no_results" = "Nessun risultato";
"lng_inline_bot_via" = "via {inline_bot}";
"lng_box_remove" = "Rimuovi";
@@ -793,7 +794,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_selected_delete" = "Elimina";
"lng_selected_forward" = "Inoltra";
"lng_selected_count" = "{count:_not_used_|# messaggio|# messaggi}";
"lng_selected_cancel_sure_this" = "Vuoi annullare il caricamento?";
"lng_selected_cancel_sure_this" = "Annullare il caricamento?";
"lng_selected_upload_stop" = "Arresta ";
"lng_selected_delete_sure_this" = "Vuoi eliminare questo messaggio?";
"lng_selected_delete_sure" = "Vuoi eliminare {count:_not_used_|# messaggio|# messaggi}?";
"lng_box_delete" = "Elimina";
@@ -832,7 +834,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop si è aggiornato alla versione {version}\n\n{changes}\n\nLa cronologia degli aggiornamenti è disponibile qui:\n{link}";
"lng_new_version_minor" = "— Bug fix e altri miglioramenti minori";
"lng_new_version_text" = "Rivoluzione GIF: L'invio e il download delle GIF sono ora 10 volte più veloci, riproduci automaticamente le GIF e salva le tue GIF preferite in una pagina dedicata nel pannello sticker.\n\nPiù info sulle GIF:\n{gifs_link}\n\n@-Bot: Un nuovo modo di aggiungere contenuto dai bot in qualsiasi chat. Scrivi l'username di un bot e la tua domanda nel campo di scrittura per ricevere risultati immediati e inviarli nella chat. Prova a scrivere “@gif dog” nella tua prossima chat. Bot di esempio: @gif, @wiki, @bing, @vid, @bold.\n\nPiù info sui @-Bot:\n{bots_link}\n\nInoltre in questa versione: Nuovo design per i media, impostazioni di download automatico per foto, note vocali e GIF.";
"lng_new_version_text" = "Rivoluzione GIF: L'invio e il download delle GIF sono ora 10 volte più veloci, riproduci automaticamente le GIF e salva le tue GIF preferite in una pagina dedicata nel pannello sticker.\n\nPiù info sulle GIF:\n{gifs_link}\n\nInline Bot: Un nuovo modo di aggiungere contenuto dai bot in qualsiasi chat. Scrivi l'username di un bot e la tua domanda nel campo di scrittura per ricevere risultati immediati e inviarli nella chat. Prova a scrivere “@gif dog” nella tua prossima chat. Bot di esempio: @gif, @wiki, @bing, @vid, @bold.\n\nPiù info sugli Inline Bot:\n{bots_link}\n\nInoltre in questa versione: Nuovo design per i media, impostazioni di download automatico per foto, note vocali e GIF.";
"lng_menu_insert_unicode" = "Inserisci carattere di controllo Unicode";

View File

@@ -498,7 +498,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_sure_delete_group" = "정말로 그룹방을 삭제하시겠습니까? 모든 구성원 및 메시지가 삭제됩니다.";
"lng_message_empty" = "메시지 없음";
"lng_media_unsupported" = "지원하지 않는 미디어";
"lng_message_unsupported" = "이 메시지는 텔레그램 데스크탑에서 호환이 되지 않습니다. 설정에서 최신 버전으로 업데이트를 하던가 {link}를 통하여 설치해주세요.";
"lng_action_add_user" = "{from} 님께서 {user} 님을 초대하셨습니다.";
"lng_action_add_users_many" = "{from}님께서 {users}명을 초대하였습니다.";
@@ -600,6 +600,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_saved_gifs" = "저장된 GIF";
"lng_inline_bot_results" = "{inline_bot} 결과";
"lng_inline_bot_no_results" = "결과 없음";
"lng_inline_bot_via" = "{inline_bot} 결과";
"lng_box_remove" = "삭제";
@@ -794,6 +795,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_selected_forward" = "전달";
"lng_selected_count" = "{count:_not_used_|# 메시지|# 메시지}";
"lng_selected_cancel_sure_this" = "업로드를 취소하시겠습니까?";
"lng_selected_upload_stop" = "정지";
"lng_selected_delete_sure_this" = "메시지를 삭제하시겠습니까?";
"lng_selected_delete_sure" = "{count:_not_used_|# 메시지|# 메시지}를 삭제하시겠습니까?";
"lng_box_delete" = "삭제";

View File

@@ -498,7 +498,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_sure_delete_group" = "Groep echt verwijderen? Berichten worden gewist en alle deelnemers verwijderd.";
"lng_message_empty" = "Leeg bericht";
"lng_media_unsupported" = "Niet-ondersteunde media";
"lng_message_unsupported" = "Dit bericht wordt niet ondersteund door jouw versie van Telegram Desktop. Werk bij naar de laatste versie via de instellingen of installeer vanuit {link}";
"lng_action_add_user" = "{from} heeft {user} toegevoegd";
"lng_action_add_users_many" = "{from} heeft {users} toegevoegd";
@@ -600,6 +600,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_saved_gifs" = "Opgeslagen GIF's";
"lng_inline_bot_results" = "Resultaten van {inline_bot}";
"lng_inline_bot_no_results" = "Geen resultaten";
"lng_inline_bot_via" = "via {inline_bot}";
"lng_box_remove" = "Verwijder";
@@ -793,7 +794,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_selected_delete" = "Verwijder";
"lng_selected_forward" = "Doorsturen";
"lng_selected_count" = "{count:_not_used_|# bericht|# berichten}";
"lng_selected_cancel_sure_this" = "Wil je deze upload annuleren?";
"lng_selected_cancel_sure_this" = "Upload annuleren?";
"lng_selected_upload_stop" = "Stoppen";
"lng_selected_delete_sure_this" = "Wil je dit bericht verwijderen?";
"lng_selected_delete_sure" = "Wil je {count:_not_used_|# bericht|# berichten} verwijderen?";
"lng_box_delete" = "Verwijder";

View File

@@ -498,7 +498,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_sure_delete_group" = "Tem certeza que deseja apagar este grupo? Todos os membros serão removidos e todas as mensagens serão perdidas.";
"lng_message_empty" = "Mensagem Vazia";
"lng_media_unsupported" = "Mídia Não-Suportada";
"lng_message_unsupported" = "Essa mensagem não é suportada em sua versão do Telegram Desktop. Atualize para a última versão nas Configurações ou instale por aqui {link}";
"lng_action_add_user" = "{from} adicionou {user}";
"lng_action_add_users_many" = "{from} adicionou {users}";
@@ -600,6 +600,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_saved_gifs" = "GIFs Salvos";
"lng_inline_bot_results" = "Resultados de {inline_bot}";
"lng_inline_bot_no_results" = "Nenhum resultado";
"lng_inline_bot_via" = "via {inline_bot}";
"lng_box_remove" = "Remover";
@@ -793,7 +794,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_selected_delete" = "Apagar";
"lng_selected_forward" = "Encaminhar";
"lng_selected_count" = "{count:_not_used_|# mensagem|# mensagens}";
"lng_selected_cancel_sure_this" = "Você deseja cancelar o envio?";
"lng_selected_cancel_sure_this" = "Cancelar envio?";
"lng_selected_upload_stop" = "Parar";
"lng_selected_delete_sure_this" = "Você deseja apagar essa mensagem?";
"lng_selected_delete_sure" = "Você deseja apagar {count:_not_used_|# mensagem|# mensagens}?";
"lng_box_delete" = "Apagar";

View File

@@ -293,9 +293,9 @@ void LayoutAbstractFileItem::setStatusSize(int32 newSize, int32 fullSize, int32
}
}
LayoutOverviewDate::LayoutOverviewDate(const QDate &date, bool month)
: _date(date)
, _text(month ? langMonthFull(date) : langDayOfMonthFull(date)) {
LayoutOverviewDate::LayoutOverviewDate(const QDate &date, bool month) : LayoutItem(OverviewItemInfo::Bit())
, _date(date)
, _text(month ? langMonthFull(date) : langDayOfMonthFull(date)) {
}
void LayoutOverviewDate::initDimensions() {
@@ -311,7 +311,7 @@ void LayoutOverviewDate::paint(Painter &p, const QRect &clip, uint32 selection,
}
}
LayoutOverviewPhoto::LayoutOverviewPhoto(PhotoData *photo, HistoryItem *parent) : LayoutMediaItem(parent)
LayoutOverviewPhoto::LayoutOverviewPhoto(PhotoData *photo, HistoryItem *parent) : LayoutMediaItem(0, parent)
, _data(photo)
, _link(new PhotoLink(photo))
, _goodLoaded(false) {
@@ -385,7 +385,7 @@ void LayoutOverviewPhoto::getState(TextLinkPtr &link, HistoryCursorState &cursor
}
}
LayoutOverviewVideo::LayoutOverviewVideo(VideoData *video, HistoryItem *parent) : LayoutAbstractFileItem(parent)
LayoutOverviewVideo::LayoutOverviewVideo(VideoData *video, HistoryItem *parent) : LayoutAbstractFileItem(0, parent)
, _data(video)
, _duration(formatDurationText(_data->duration))
, _thumbLoaded(false) {
@@ -550,7 +550,7 @@ void LayoutOverviewVideo::updateStatusText() const {
}
}
LayoutOverviewAudio::LayoutOverviewAudio(AudioData *audio, HistoryItem *parent) : LayoutAbstractFileItem(parent)
LayoutOverviewAudio::LayoutOverviewAudio(AudioData *audio, HistoryItem *parent) : LayoutAbstractFileItem(OverviewItemInfo::Bit(), parent)
, _data(audio)
, _namel(new AudioOpenLink(_data)) {
setLinks(new AudioOpenLink(_data), new AudioOpenLink(_data), new AudioCancelLink(_data));
@@ -738,7 +738,7 @@ bool LayoutOverviewAudio::updateStatusText() const {
return showPause;
}
LayoutOverviewDocument::LayoutOverviewDocument(DocumentData *document, HistoryItem *parent) : LayoutAbstractFileItem(parent)
LayoutOverviewDocument::LayoutOverviewDocument(DocumentData *document, HistoryItem *parent) : LayoutAbstractFileItem(OverviewItemInfo::Bit(), parent)
, _data(document)
, _msgl(new MessageLink(parent))
, _namel(new DocumentOpenLink(_data))
@@ -1061,7 +1061,7 @@ namespace {
}
}
LayoutOverviewLink::LayoutOverviewLink(HistoryMedia *media, HistoryItem *parent) : LayoutMediaItem(parent)
LayoutOverviewLink::LayoutOverviewLink(HistoryMedia *media, HistoryItem *parent) : LayoutMediaItem(OverviewItemInfo::Bit(), parent)
, _titlew(0)
, _page(0)
, _pixw(0)
@@ -1319,8 +1319,8 @@ LayoutOverviewLink::Link::Link(const QString &url, const QString &text)
, lnk(linkFromUrl(url)) {
}
LayoutInlineItem::LayoutInlineItem(InlineResult *result, DocumentData *doc, PhotoData *photo)
: _result(result)
LayoutInlineItem::LayoutInlineItem(InlineResult *result, DocumentData *doc, PhotoData *photo) : LayoutItem(0)
, _result(result)
, _doc(doc)
, _photo(photo)
, _position(0) {

View File

@@ -101,11 +101,9 @@ public:
};
class LayoutMediaItem;
class OverviewItemInfo;
class LayoutItem {
class LayoutItem : public Interfaces {
public:
LayoutItem() : _maxw(0), _minh(0) {
LayoutItem(uint64 i_mask) : Interfaces(i_mask), _maxw(0), _minh(0) {
}
int32 maxWidth() const {
@@ -163,12 +161,6 @@ public:
virtual DocumentData *getDocument() const {
return 0;
}
virtual OverviewItemInfo *getOverviewItemInfo() {
return 0;
}
virtual const OverviewItemInfo *getOverviewItemInfo() const {
return 0;
}
MsgId msgId() const {
const HistoryItem *item = getItem();
return item ? item->id : 0;
@@ -182,7 +174,7 @@ protected:
class LayoutMediaItem : public LayoutItem {
public:
LayoutMediaItem(HistoryItem *parent) : _parent(parent) {
LayoutMediaItem(uint64 i_mask, HistoryItem *parent) : LayoutItem(i_mask), _parent(parent) {
}
virtual LayoutMediaItem *toLayoutMediaItem() {
@@ -202,7 +194,7 @@ protected:
class LayoutRadialProgressItem : public LayoutMediaItem {
public:
LayoutRadialProgressItem(HistoryItem *parent) : LayoutMediaItem(parent)
LayoutRadialProgressItem(uint64 i_mask, HistoryItem *parent) : LayoutMediaItem(i_mask, parent)
, _radial(0)
, a_iconOver(0, 0)
, _a_iconOver(animation(this, &LayoutRadialProgressItem::step_iconOver)) {
@@ -248,7 +240,7 @@ private:
class LayoutAbstractFileItem : public LayoutRadialProgressItem {
public:
LayoutAbstractFileItem(HistoryItem *parent) : LayoutRadialProgressItem(parent) {
LayoutAbstractFileItem(uint64 i_mask, HistoryItem *parent) : LayoutRadialProgressItem(i_mask, parent) {
}
protected:
@@ -276,9 +268,9 @@ public:
};
class OverviewItemInfo {
class OverviewItemInfo : public BasicInterface<OverviewItemInfo> {
public:
OverviewItemInfo() : _top(0) {
OverviewItemInfo(Interfaces *) : _top(0) {
}
int32 top() const {
return _top;
@@ -299,16 +291,7 @@ public:
virtual void initDimensions();
virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const;
virtual OverviewItemInfo *getOverviewItemInfo() {
return &_info;
}
virtual const OverviewItemInfo *getOverviewItemInfo() const {
return &_info;
}
private:
OverviewItemInfo _info;
QDate _date;
QString _text;
@@ -374,13 +357,6 @@ public:
virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const;
virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const;
virtual OverviewItemInfo *getOverviewItemInfo() {
return &_info;
}
virtual const OverviewItemInfo *getOverviewItemInfo() const {
return &_info;
}
protected:
virtual float64 dataProgress() const {
return _data->progress();
@@ -396,7 +372,6 @@ protected:
}
private:
OverviewItemInfo _info;
AudioData *_data;
TextLinkPtr _namel;
@@ -419,12 +394,6 @@ public:
virtual DocumentData *getDocument() const {
return _data;
}
virtual OverviewItemInfo *getOverviewItemInfo() {
return &_info;
}
virtual const OverviewItemInfo *getOverviewItemInfo() const {
return &_info;
}
protected:
virtual float64 dataProgress() const {
@@ -441,7 +410,6 @@ protected:
}
private:
OverviewItemInfo _info;
DocumentData *_data;
TextLinkPtr _msgl, _namel;
@@ -468,15 +436,7 @@ public:
virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const;
virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const;
virtual OverviewItemInfo *getOverviewItemInfo() {
return &_info;
}
virtual const OverviewItemInfo *getOverviewItemInfo() const {
return &_info;
}
private:
OverviewItemInfo _info;
TextLinkPtr _photol;
QString _title, _letter;

View File

@@ -135,7 +135,7 @@ TaskQueue::~TaskQueue() {
void TaskQueueWorker::onTaskAdded() {
if (_inTaskAdded) return;
_inTaskAdded = true;
bool someTasksLeft = false;
do {
TaskPtr task;
@@ -326,7 +326,7 @@ void FileLoadTask::process() {
}
}
}
if (filemime == qstr("video/mp4") || filename.endsWith(qstr(".mp4"), Qt::CaseInsensitive)) {
if (filemime == qstr("video/mp4") || filename.endsWith(qstr(".mp4"), Qt::CaseInsensitive) || animated) {
QImage cover;
MTPDocumentAttribute animatedAttribute = clipReadAnimatedAttributes(_filepath, _content, cover);
if (animatedAttribute.type() == mtpc_documentAttributeVideo) {
@@ -350,7 +350,9 @@ void FileLoadTask::process() {
thumbId = MTP::nonce<uint64>();
filemime = qstr("video/mp4");
if (filename.endsWith(qstr(".mp4"), Qt::CaseInsensitive)) {
filemime = qstr("video/mp4");
}
}
}
}
@@ -414,7 +416,7 @@ void FileLoadTask::process() {
_type = PrepareDocument;
}
}
_result->type = _type;
_result->filepath = _filepath;
_result->content = _content;

View File

@@ -412,7 +412,7 @@ namespace {
continue;
}
if (memcmp(magic, tdfMagic, tdfMagicLen)) {
DEBUG_LOG(("App Info: bad magic %1 in '%2'").arg(mb(magic, tdfMagicLen).str()).arg(name));
DEBUG_LOG(("App Info: bad magic %1 in '%2'").arg(Logs::mb(magic, tdfMagicLen).str()).arg(name));
continue;
}
@@ -863,7 +863,7 @@ namespace {
stream.readRawData((char*)key, 256);
if (!_checkStreamStatus(stream)) return false;
DEBUG_LOG(("MTP Info: key found, dc %1, key: %2").arg(dcId).arg(mb(key, 256).str()));
DEBUG_LOG(("MTP Info: key found, dc %1, key: %2").arg(dcId).arg(Logs::mb(key, 256).str()));
dcId = dcId % _mtp_internal::dcShift;
mtpAuthKeyPtr keyPtr(new mtpAuthKey());
keyPtr->setKey(key);
@@ -1983,15 +1983,7 @@ namespace _local_inner {
namespace Local {
void start() {
if (!_started) {
_started = true;
_manager = new _local_inner::Manager();
_localLoader = new TaskQueue(0, FileLoaderQueueStopTimeout);
}
}
void stop() {
void finish() {
if (_manager) {
_writeMap(WriteMapNow);
_manager->finish();
@@ -2002,8 +1994,11 @@ namespace Local {
}
}
void readSettings() {
Local::start();
void start() {
t_assert(_manager == 0);
_manager = new _local_inner::Manager();
_localLoader = new TaskQueue(0, FileLoaderQueueStopTimeout);
_basePath = cWorkingDir() + qsl("tdata/");
if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
@@ -2843,6 +2838,16 @@ namespace Local {
}
_writeStorageImageLocation(stream, doc->sticker()->loc);
}
if (AppVersion > 9018) {
stream << qint32(it->emoji.size());
for (StickersByEmojiMap::const_iterator j = it->emoji.cbegin(), e = it->emoji.cend(); j != e; ++j) {
stream << emojiString(j.key()) << qint32(j->size());
for (int32 k = 0, l = j->size(); k < l; ++k) {
stream << quint64(j->at(k)->id);
}
}
}
}
void writeStickers() {
@@ -2881,6 +2886,14 @@ namespace Local {
// loc
size += _storageImageLocationSize();
}
if (AppVersion > 9018) {
size += sizeof(qint32); // emojiCount
for (StickersByEmojiMap::const_iterator j = i->emoji.cbegin(), e = i->emoji.cend(); j != e; ++j) {
size += _stringSize(emojiString(j.key())) + sizeof(qint32) + (j->size() * sizeof(quint64));
}
}
++setsCount;
}
@@ -3073,6 +3086,29 @@ namespace Local {
set.stickers.push_back(doc);
++set.count;
}
if (stickers.version > 9018) {
qint32 emojiCount;
stickers.stream >> emojiCount;
for (int32 j = 0; j < emojiCount; ++j) {
QString emojiString;
qint32 stickersCount;
stickers.stream >> emojiString >> stickersCount;
StickerPack pack;
pack.reserve(stickersCount);
for (int32 k = 0; k < stickersCount; ++k) {
quint64 id;
stickers.stream >> id;
DocumentData *doc = App::document(id);
if (!doc || !doc->sticker()) continue;
pack.push_back(doc);
}
if (EmojiPtr e = emojiGetNoColor(emojiFromText(emojiString))) {
set.emoji.insert(e, pack);
}
}
}
}
}

View File

@@ -54,7 +54,7 @@ namespace _local_inner {
namespace Local {
void start();
void stop();
void finish();
void readSettings();
void writeSettings();

File diff suppressed because it is too large Load Diff

View File

@@ -20,76 +20,91 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
*/
#pragma once
#ifdef Q_OS_WIN
#define OUTPUT_LOG(msg) (OutputDebugString((QString msg + "\n").toStdWString().c_str()))
#endif
#if (defined _DEBUG || defined _WITH_DEBUG)
struct DebugLogMemoryBuffer {
DebugLogMemoryBuffer(const void *ptr, uint32 size) : p(ptr), s(size) {
}
QString str() const {
QString result;
const uchar *buf((const uchar*)p);
const char *hex = "0123456789ABCDEF";
result.reserve(s * 3);
for (uint32 i = 0; i < s; ++i) {
result += hex[(buf[i] >> 4)];
result += hex[buf[i] & 0x0F];
result += ' ';
}
result.chop(1);
return result;
}
const void *p;
uint32 s;
};
inline DebugLogMemoryBuffer mb(const void *ptr, uint32 size) {
return DebugLogMemoryBuffer(ptr, size);
}
void debugLogWrite(const char *file, int32 line, const QString &v);
#define DEBUG_LOG(msg) { if (cDebug()) debugLogWrite(__FILE__, __LINE__, QString msg); }
//usage DEBUG_LOG(("log: %1 %2").arg(1).arg(2))
void tcpLogWrite(const QString &v);
#define TCP_LOG(msg) { if (cDebug()) tcpLogWrite(QString msg); }
//usage TCP_LOG(("log: %1 %2").arg(1).arg(2))
void mtpLogWrite(int32 dc, const QString &v);
#define MTP_LOG(dc, msg) { if (cDebug()) mtpLogWrite(dc, QString msg); }
//usage MTP_LOG(dc, ("log: %1 %2").arg(1).arg(2))
#else
#define DEBUG_LOG(msg) (void(0))
#define TCP_LOG(msg) (void(0))
#define MTP_LOG(dc, msg) (void(0))
#endif
inline const char *logBool(bool v) {
return v ? "[TRUE]" : "[FALSE]";
}
class MTPlong;
QString logVectorLong(const QVector<MTPlong> &ids);
QString logVectorLong(const QVector<uint64> &ids);
namespace Logs {
void logWrite(const QString &v);
struct Initializer {
Initializer();
~Initializer();
};
bool started();
#define LOG(msg) (logWrite(QString msg))
bool instanceChecked();
void multipleInstances();
void writeMain(const QString &v);
void writeDebug(const char *file, int32 line, const QString &v);
void writeTcp(const QString &v);
void writeMtp(int32 dc, const QString &v);
QString full();
inline const char *b(bool v) {
return v ? "[TRUE]" : "[FALSE]";
}
struct MemoryBuffer {
MemoryBuffer(const void *ptr, uint32 size) : p(ptr), s(size) {
}
QString str() const {
QString result;
const uchar *buf((const uchar*)p);
const char *hex = "0123456789ABCDEF";
result.reserve(s * 3);
for (uint32 i = 0; i < s; ++i) {
result += hex[(buf[i] >> 4)];
result += hex[buf[i] & 0x0F];
result += ' ';
}
result.chop(1);
return result;
}
const void *p;
uint32 s;
};
inline MemoryBuffer mb(const void *ptr, uint32 size) {
return MemoryBuffer(ptr, size);
}
QString vector(const QVector<MTPlong> &ids);
QString vector(const QVector<uint64> &ids);
}
#define LOG(msg) (Logs::writeMain(QString msg))
//usage LOG(("log: %1 %2").arg(1).arg(2))
static volatile int *t_assert_nullptr = 0;
inline void t_noop() {}
inline void t_assert_fail(const char *condition, const char *file, int32 line) {
LOG(("Assertion Failed! \"%1\" %2:%3").arg(condition).arg(file).arg(line));
*t_assert_nullptr = 0;
}
#define t_assert(cond) ((!(cond)) ? t_assert_fail(#cond, __FILE__, __LINE__) : t_noop())
#define DEBUG_LOG(msg) { if (cDebug() || !Logs::started()) Logs::writeDebug(__FILE__, __LINE__, QString msg); }
//usage DEBUG_LOG(("log: %1 %2").arg(1).arg(2))
void logsInit();
void logsInitDebug();
void logsClose();
#define TCP_LOG(msg) { if (cDebug() || !Logs::started()) Logs::writeTcp(QString msg); }
//usage TCP_LOG(("log: %1 %2").arg(1).arg(2))
#define MTP_LOG(dc, msg) { if (cDebug() || !Logs::started()) Logs::writeMtp(dc, QString msg); }
//usage MTP_LOG(dc, ("log: %1 %2").arg(1).arg(2))
namespace SignalHandlers {
struct dump {
~dump();
};
const dump &operator<<(const dump &stream, const char *str);
const dump &operator<<(const dump &stream, const wchar_t *str);
const dump &operator<<(const dump &stream, int num);
const dump &operator<<(const dump &stream, unsigned int num);
const dump &operator<<(const dump &stream, unsigned long num);
const dump &operator<<(const dump &stream, unsigned long long num);
const dump &operator<<(const dump &stream, double num);
enum Status {
CantOpen,
LastCrashed,
Started
};
Status start();
Status restart(); // can be only CantOpen or Started
void finish();
}

View File

@@ -25,54 +25,22 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "localstorage.h"
int main(int argc, char *argv[]) {
#ifdef _NEED_WIN_GENERATE_DUMP
_oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter);
#endif
#ifdef _NEED_LINUX_GENERATE_DUMP
//signal(SIGSEGV, _sigsegvHandler);
#endif
InitOpenSSL _init;
int result = 0;
settingsParseArgs(argc, argv);
for (int32 i = 0; i < argc; ++i) {
if (string("-fixprevious") == argv[i]) {
return psFixPrevious();
} else if (string("-cleanup") == argv[i]) {
return psCleanup();
}
}
logsInit();
Local::readSettings();
if (Local::oldSettingsVersion() < AppVersion) {
psNewVersion();
}
if (cFromAutoStart() && !cAutoStart()) {
psAutoStart(false, true);
Local::stop();
return 0;
if (cLaunchMode() == LaunchModeFixPrevious) {
return psFixPrevious();
} else if (cLaunchMode() == LaunchModeCleanup) {
return psCleanup();
} else if (cLaunchMode() == LaunchModeShowCrash) {
return showCrashReportWindow(QFileInfo(cStartUrl()).absoluteFilePath());
}
DEBUG_LOG(("Application Info: Telegram started, test mode: %1, exe dir: %2").arg(logBool(cTestMode())).arg(cExeDir()));
if (cDebug()) {
LOG(("Application Info: Telegram started in debug mode"));
for (int32 i = 0; i < argc; ++i) {
LOG(("Argument: %1").arg(QString::fromLocal8Bit(argv[i])));
}
QStringList logs = psInitLogs();
for (int32 i = 0, l = logs.size(); i < l; ++i) {
LOG(("Init Log: %1").arg(logs.at(i)));
}
}
psClearInitLogs();
DEBUG_LOG(("Application Info: ideal thread count: %1, using %2 connections per session").arg(QThread::idealThreadCount()).arg(cConnectionsInSession()));
psStart();
int result = 0;
Logs::Initializer _logs;
{
QByteArray args[] = { "-style=0" }; // prepare fake args
PlatformSpecific::Initializer _ps;
QByteArray args[] = { "-style=0" }; // prepare fake args to disable QT_STYLE_OVERRIDE env variable
static const int a_cnt = sizeof(args) / sizeof(args[0]);
int a_argc = a_cnt + 1;
char *a_argv[a_cnt + 1] = { argv[0], args[0].data() };
@@ -82,22 +50,11 @@ int main(int argc, char *argv[]) {
result = app.exec();
}
}
psFinish();
Local::stop();
DEBUG_LOG(("Application Info: Telegram done, result: %1").arg(result));
DEBUG_LOG(("Telegram finished, result: %1").arg(result));
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
if (cRestartingUpdate()) {
if (!cBetaVersion() && DevVersion) {
LOG(("Writing 'devversion' file before launching the Updater!"));
QFile f(cWorkingDir() + qsl("tdata/devversion"));
if (!f.exists() && f.open(QIODevice::WriteOnly)) {
f.write("1");
f.close();
}
}
DEBUG_LOG(("Application Info: executing updater to install update.."));
psExecUpdater();
} else
@@ -107,6 +64,6 @@ int main(int argc, char *argv[]) {
psExecTelegram();
}
logsClose();
SignalHandlers::finish();
return result;
}

View File

@@ -907,7 +907,8 @@ void MainWidget::forwardLayer(int32 forwardSelected) {
void MainWidget::deleteLayer(int32 selectedCount) {
QString str((selectedCount < 0) ? lang(selectedCount < -1 ? lng_selected_cancel_sure_this : lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, selectedCount));
ConfirmBox *box = new ConfirmBox((selectedCount < 0) ? str : str.arg(selectedCount), lang(lng_box_delete));
QString btn(lang((selectedCount < -1) ? lng_selected_upload_stop : lng_box_delete)), cancel(lang((selectedCount < -1) ? lng_continue : lng_cancel));
ConfirmBox *box = new ConfirmBox(str, btn, st::defaultBoxButton, cancel);
if (selectedCount < 0) {
if (selectedCount < -1) {
if (HistoryItem *item = App::contextItem()) {
@@ -3458,7 +3459,7 @@ void MainWidget::start(const MTPUser &user) {
cSetOtherOnline(0);
App::feedUsers(MTP_vector<MTPUser>(1, user));
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
App::app()->startUpdateCheck();
Sandboxer::startUpdateCheck();
#endif
MTP::send(MTPupdates_GetState(), rpcDone(&MainWidget::gotState));
update();
@@ -4127,12 +4128,11 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
if (peerId) {
if (HistoryItem *item = App::histItemById(peerToChannel(peerId), d.vid.v)) {
if (!text.isEmpty()) {
item->setText(text, d.has_entities() ? entitiesFromMTP(d.ventities.c_vector().v) : EntitiesInText());
item->initDimensions();
Notify::historyItemResized(item);
}
item->updateMedia(d.has_media() ? (&d.vmedia) : 0, true);
item->setText(text, d.has_entities() ? entitiesFromMTP(d.ventities.c_vector().v) : EntitiesInText());
item->updateMedia(d.has_media() ? (&d.vmedia) : 0);
item->initDimensions();
Notify::historyItemResized(item);
item->addToOverview(AddToOverviewNew);
}
}
@@ -4594,21 +4594,41 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
if (d.vstickerset.type() == mtpc_messages_stickerSet) {
const MTPDmessages_stickerSet &set(d.vstickerset.c_messages_stickerSet());
if (set.vset.type() == mtpc_stickerSet) {
const MTPDstickerSet &s(set.vset.c_stickerSet());
StickerSets &sets(cRefStickerSets());
StickerSets::iterator it = sets.find(s.vid.v);
if (it == sets.cend()) {
it = sets.insert(s.vid.v, StickerSet(s.vid.v, s.vaccess_hash.v, stickerSetTitle(s), qs(s.vshort_name), s.vcount.v, s.vhash.v, s.vflags.v));
}
const QVector<MTPDocument> &v(set.vdocuments.c_vector().v);
StickerPack pack;
pack.reserve(v.size());
it->stickers.clear();
it->stickers.reserve(v.size());
for (int32 i = 0, l = v.size(); i < l; ++i) {
DocumentData *doc = App::feedDocument(v.at(i));
if (!doc || !doc->sticker()) continue;
pack.push_back(doc);
it->stickers.push_back(doc);
}
it->emoji.clear();
const QVector<MTPStickerPack> &packs(set.vpacks.c_vector().v);
for (int32 i = 0, l = packs.size(); i < l; ++i) {
if (packs.at(i).type() != mtpc_stickerPack) continue;
const MTPDstickerPack &pack(packs.at(i).c_stickerPack());
if (EmojiPtr e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) {
const QVector<MTPlong> &stickers(pack.vdocuments.c_vector().v);
StickerPack p;
p.reserve(stickers.size());
for (int32 j = 0, c = stickers.size(); j < c; ++j) {
DocumentData *doc = App::document(stickers.at(j).v);
if (!doc || !doc->sticker()) continue;
const MTPDstickerSet &s(set.vset.c_stickerSet());
StickerSets &sets(cRefStickerSets());
sets.insert(s.vid.v, StickerSet(s.vid.v, s.vaccess_hash.v, stickerSetTitle(s), qs(s.vshort_name), s.vcount.v, s.vhash.v, s.vflags.v)).value().stickers = pack;
p.push_back(doc);
}
it->emoji.insert(e, p);
}
}
StickerSetsOrder &order(cRefStickerSetsOrder());
int32 insertAtIndex = 0, currentIndex = order.indexOf(s.vid.v);
@@ -4621,8 +4641,8 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
StickerSets::iterator custom = sets.find(CustomStickerSetId);
if (custom != sets.cend()) {
for (int32 i = 0, l = pack.size(); i < l; ++i) {
int32 removeIndex = custom->stickers.indexOf(pack.at(i));
for (int32 i = 0, l = it->stickers.size(); i < l; ++i) {
int32 removeIndex = custom->stickers.indexOf(it->stickers.at(i));
if (removeIndex >= 0) custom->stickers.removeAt(removeIndex);
}
if (custom->stickers.isEmpty()) {

View File

@@ -179,7 +179,7 @@ void MediaView::moveToScreen() {
}
QPoint wndCenter(App::wnd()->x() + App::wnd()->width() / 2, App::wnd()->y() + App::wnd()->height() / 2);
QRect avail = App::app() ? App::app()->desktop()->screenGeometry(wndCenter) : QDesktopWidget().screenGeometry(wndCenter);
QRect avail = Sandboxer::screenGeometry(wndCenter);
if (avail != geometry()) {
setGeometry(avail);
}
@@ -586,7 +586,7 @@ void MediaView::onSaveAs() {
}
}
activateWindow();
App::app()->setActiveWindow(this);
Sandboxer::setActiveWindow(this);
setFocus();
}
@@ -1074,7 +1074,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty
show();
psShowOverAll(this);
activateWindow();
App::app()->setActiveWindow(this);
Sandboxer::setActiveWindow(this);
setFocus();
}
}
@@ -1990,7 +1990,7 @@ void MediaView::onCheckActive() {
if (App::wnd() && isVisible()) {
if (App::wnd()->isActiveWindow() && App::wnd()->hasFocus()) {
activateWindow();
App::app()->setActiveWindow(this);
Sandboxer::setActiveWindow(this);
setFocus();
}
}

View File

@@ -793,7 +793,7 @@ out.write('\n// Type classes definitions\n' + typesText);
out.write('\n// Type constructors with data\n' + dataTexts);
out.write('\n// RPC methods\n' + funcsText);
out.write('\n// Inline methods definition\n' + inlineMethods);
out.write('\n// Human-readable text serialization\n#if (defined _DEBUG || defined _WITH_DEBUG)\n\nvoid mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons, uint32 level, mtpPrime vcons);\n\n#endif\n');
out.write('\n// Human-readable text serialization\nvoid mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons, uint32 level, mtpPrime vcons);\n');
outCpp = open('mtpScheme.cpp', 'w');
outCpp.write('/*\n');
@@ -816,7 +816,6 @@ outCpp.write('Full license: https://github.com/telegramdesktop/tdesktop/blob/mas
outCpp.write('Copyright (c) 2014 John Preston, https://desktop.telegram.org\n');
outCpp.write('*/\n');
outCpp.write('#include "stdafx.h"\n#include "mtpScheme.h"\n\n');
outCpp.write('#if (defined _DEBUG || defined _WITH_DEBUG)\n\n');
outCpp.write('typedef QVector<mtpTypeId> Types;\ntypedef QVector<int32> StagesFlags;\n\n');
outCpp.write(textSerializeMethods);
outCpp.write('namespace {\n');
@@ -825,7 +824,7 @@ outCpp.write('\ttypedef QMap<mtpTypeId, mtpTextSerializer> TextSerializers;\n\tT
outCpp.write('\tvoid initTextSerializers() {\n');
outCpp.write(textSerializeInit);
outCpp.write('\t}\n}\n');
outCpp.write(textSerializeFull + '\n#endif\n');
outCpp.write(textSerializeFull + '\n');
print('Done, written {0} constructors, {1} functions.'.format(consts, funcs));

View File

@@ -52,7 +52,7 @@ public:
}
void prepareAES(const MTPint128 &msgKey, MTPint256 &aesKey, MTPint256 &aesIV, bool send = true) const {
if (!_isset) throw mtpErrorKeyNotReady(QString("prepareAES(.., %1)").arg(logBool(send)));
if (!_isset) throw mtpErrorKeyNotReady(QString("prepareAES(.., %1)").arg(Logs::b(send)));
uint32 x = send ? 0 : 8;

View File

@@ -78,14 +78,14 @@ namespace {
) {
ERR_load_crypto_strings();
LOG(("BigNum Error: BN_bin2bn failed, error: %1").arg(ERR_error_string(ERR_get_error(), 0)));
DEBUG_LOG(("BigNum Error: base %1, power %2, modul %3").arg(mb(&g_be, sizeof(uint32)).str()).arg(mb(power, 64 * sizeof(uint32)).str()).arg(mb(modul, 64 * sizeof(uint32)).str()));
DEBUG_LOG(("BigNum Error: base %1, power %2, modul %3").arg(Logs::mb(&g_be, sizeof(uint32)).str()).arg(Logs::mb(power, 64 * sizeof(uint32)).str()).arg(Logs::mb(modul, 64 * sizeof(uint32)).str()));
return false;
}
if (!BN_mod_exp(&bnResult, &bn_g, &bnPower, &bnModul, ctx)) {
ERR_load_crypto_strings();
LOG(("BigNum Error: BN_mod_exp failed, error: %1").arg(ERR_error_string(ERR_get_error(), 0)));
DEBUG_LOG(("BigNum Error: base %1, power %2, modul %3").arg(mb(&g_be, sizeof(uint32)).str()).arg(mb(power, 64 * sizeof(uint32)).str()).arg(mb(modul, 64 * sizeof(uint32)).str()));
DEBUG_LOG(("BigNum Error: base %1, power %2, modul %3").arg(Logs::mb(&g_be, sizeof(uint32)).str()).arg(Logs::mb(power, 64 * sizeof(uint32)).str()).arg(Logs::mb(modul, 64 * sizeof(uint32)).str()));
return false;
}
@@ -109,7 +109,7 @@ namespace {
if (!BN_mod_exp(&bnResult, &bn_g_a, &bnPower, &bnModul, ctx)) {
ERR_load_crypto_strings();
LOG(("BigNum Error: BN_mod_exp failed, error: %1").arg(ERR_error_string(ERR_get_error(), 0)));
DEBUG_LOG(("BigNum Error: base %1, power %2, modul %3").arg(mb(&g_be, sizeof(uint32)).str()).arg(mb(power, 64 * sizeof(uint32)).str()).arg(mb(modul, 64 * sizeof(uint32)).str()));
DEBUG_LOG(("BigNum Error: base %1, power %2, modul %3").arg(Logs::mb(&g_be, sizeof(uint32)).str()).arg(Logs::mb(power, 64 * sizeof(uint32)).str()).arg(Logs::mb(modul, 64 * sizeof(uint32)).str()));
return false;
}
@@ -123,7 +123,7 @@ namespace {
DEBUG_LOG(("BigNum Error: bad g_aResult export len (%1)").arg(resultLen));
return false;
}
BN_add_word(&bn_g_a, 1); // check g_a < dh_prime - 1
if (BN_cmp(&bn_g_a, &bnModul) >= 0) {
DEBUG_LOG(("BigNum Error: bad g_a >= dh_prime - 1"));
@@ -169,7 +169,7 @@ namespace {
) {
ERR_load_crypto_strings();
LOG(("BigNum PT Error: BN_bin2bn failed, error: %1").arg(ERR_error_string(ERR_get_error(), 0)));
DEBUG_LOG(("BigNum PT Error: prime %1").arg(mb(pData, 64 * sizeof(uint32)).str()));
DEBUG_LOG(("BigNum PT Error: prime %1").arg(Logs::mb(pData, 64 * sizeof(uint32)).str()));
return false;
}
@@ -412,12 +412,12 @@ namespace {
}
if (packet[0] != int32(size * sizeof(mtpPrime))) {
LOG(("TCP Error: bad packet header"));
TCP_LOG(("TCP Error: bad packet header, packet: %1").arg(mb(packet, size * sizeof(mtpPrime)).str()));
TCP_LOG(("TCP Error: bad packet header, packet: %1").arg(Logs::mb(packet, size * sizeof(mtpPrime)).str()));
return mtpBuffer(1, -500);
}
if (packet[size - 1] != hashCrc32(packet, (size - 1) * sizeof(mtpPrime))) {
LOG(("TCP Error: bad packet checksum"));
TCP_LOG(("TCP Error: bad packet checksum, packet: %1").arg(mb(packet, size * sizeof(mtpPrime)).str()));
TCP_LOG(("TCP Error: bad packet checksum, packet: %1").arg(Logs::mb(packet, size * sizeof(mtpPrime)).str()));
return mtpBuffer(1, -500);
}
TCP_LOG(("TCP Info: packet received, num = %1, size = %2").arg(packet[1]).arg(size * sizeof(mtpPrime)));
@@ -500,18 +500,18 @@ namespace {
uint32 len = buffer.size();
if (len < 5) {
LOG(("Fake PQ Error: bad request answer, len = %1").arg(len * sizeof(mtpPrime)));
DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(mb(answer, len * sizeof(mtpPrime)).str()));
DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str()));
throw Exception("bad pq reply");
}
if (answer[0] != 0 || answer[1] != 0 || (((uint32)answer[2]) & 0x03) != 1/* || (unixtime() - answer[3] > 300) || (answer[3] - unixtime() > 60)*/) { // didnt sync time yet
LOG(("Fake PQ Error: bad request answer start (%1 %2 %3)").arg(answer[0]).arg(answer[1]).arg(answer[2]));
DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(mb(answer, len * sizeof(mtpPrime)).str()));
DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str()));
throw Exception("bad pq reply");
}
uint32 answerLen = (uint32)answer[4];
if (answerLen != (len - 5) * sizeof(mtpPrime)) {
LOG(("Fake PQ Error: bad request answer %1 <> %2").arg(answerLen).arg((len - 5) * sizeof(mtpPrime)));
DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(mb(answer, len * sizeof(mtpPrime)).str()));
DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str()));
throw Exception("bad pq reply");
}
const mtpPrime *from(answer + 5), *end(from + len - 5);
@@ -692,7 +692,7 @@ void MTPautoConnection::sendData(mtpBuffer &buffer) {
if (buffer.size() < 3) {
LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime)));
TCP_LOG(("TCP Error: bad packet %1").arg(mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
emit error();
return;
}
@@ -979,7 +979,7 @@ void MTPtcpConnection::sendData(mtpBuffer &buffer) {
if (buffer.size() < 3) {
LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime)));
TCP_LOG(("TCP Error: bad packet %1").arg(mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
emit error();
return;
}
@@ -989,7 +989,7 @@ void MTPtcpConnection::sendData(mtpBuffer &buffer) {
buffer[0] = len;
buffer[1] = packetNum++;
buffer[size - 1] = hashCrc32(&buffer[0], len - 4);
TCP_LOG(("TCP Info: write %1 packet %2 bytes %3").arg(packetNum).arg(len).arg(mb(&buffer[0], len).str()));
TCP_LOG(("TCP Info: write %1 packet %2 bytes %3").arg(packetNum).arg(len).arg(Logs::mb(&buffer[0], len).str()));
sock.write((const char*)&buffer[0], len);
}
@@ -1068,18 +1068,18 @@ void MTPhttpConnection::sendData(mtpBuffer &buffer) {
if (buffer.size() < 3) {
LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime)));
TCP_LOG(("TCP Error: bad packet %1").arg(mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
emit error();
return;
}
int32 requestSize = (buffer.size() - 3) * sizeof(mtpPrime);
QNetworkRequest request(address);
request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize));
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(qsl("application/x-www-form-urlencoded")));
TCP_LOG(("HTTP Info: sending %1 len request %2").arg(requestSize).arg(mb(&buffer[2], requestSize).str()));
TCP_LOG(("HTTP Info: sending %1 len request %2").arg(requestSize).arg(Logs::mb(&buffer[2], requestSize).str()));
requests.insert(manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
}
@@ -1521,7 +1521,7 @@ mtpMsgId MTProtoConnectionPrivate::replaceMsgId(mtpRequest &request, mtpMsgId ne
}
mtpMsgId m = msgid();
if (m <= newId) break; // wtf
newId = m;
}
@@ -1956,7 +1956,7 @@ void MTProtoConnectionPrivate::restart(bool mayBeBadKey) {
QReadLocker lockFinished(&sessionDataMutex);
if (!sessionData) return;
DEBUG_LOG(("MTP Info: restarting MTProtoConnection, maybe bad key = %1").arg(logBool(mayBeBadKey)));
DEBUG_LOG(("MTP Info: restarting MTProtoConnection, maybe bad key = %1").arg(Logs::b(mayBeBadKey)));
_waitForReceivedTimer.stop();
_waitForConnectedTimer.stop();
@@ -2083,7 +2083,7 @@ void MTProtoConnectionPrivate::onWaitConnectedFailed() {
void MTProtoConnectionPrivate::onWaitIPv4Failed() {
_conn = _conn6;
destroyConn(&_conn4);
if (_conn) {
DEBUG_LOG(("MTP Info: can't connect through IPv4, using IPv6 connection."));
@@ -2144,14 +2144,14 @@ void MTProtoConnectionPrivate::handleReceived() {
const mtpPrime *encrypted(encryptedBuf.data());
if (len < 18) { // 2 auth_key_id, 4 msg_key, 2 salt, 2 session, 2 msg_id, 1 seq_no, 1 length, (1 data + 3 padding) min
LOG(("TCP Error: bad message received, len %1").arg(len * sizeof(mtpPrime)));
TCP_LOG(("TCP Error: bad message %1").arg(mb(encrypted, len * sizeof(mtpPrime)).str()));
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encrypted, len * sizeof(mtpPrime)).str()));
lockFinished.unlock();
return restart();
}
if (keyId != *(uint64*)encrypted) {
LOG(("TCP Error: bad auth_key_id %1 instead of %2 received").arg(keyId).arg(*(uint64*)encrypted));
TCP_LOG(("TCP Error: bad message %1").arg(mb(encrypted, len * sizeof(mtpPrime)).str()));
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encrypted, len * sizeof(mtpPrime)).str()));
lockFinished.unlock();
return restart();
@@ -2161,7 +2161,7 @@ void MTProtoConnectionPrivate::handleReceived() {
mtpPrime *data((mtpPrime*)dataBuffer.data()), *msg = data + 8;
const mtpPrime *from(msg), *end;
MTPint128 msgKey(*(MTPint128*)(encrypted + 2));
aesDecrypt(encrypted + 6, data, dataBuffer.size(), key, msgKey);
uint64 serverSalt = *(uint64*)&data[0], session = *(uint64*)&data[2], msgId = *(uint64*)&data[4];
@@ -2170,7 +2170,7 @@ void MTProtoConnectionPrivate::handleReceived() {
if (uint32(dataBuffer.size()) < msgLen + 8 * sizeof(mtpPrime) || (msgLen & 0x03)) {
LOG(("TCP Error: bad msg_len received %1, data size: %2").arg(msgLen).arg(dataBuffer.size()));
TCP_LOG(("TCP Error: bad message %1").arg(mb(encrypted, len * sizeof(mtpPrime)).str()));
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encrypted, len * sizeof(mtpPrime)).str()));
_conn->received().pop_front();
lockFinished.unlock();
@@ -2179,13 +2179,13 @@ void MTProtoConnectionPrivate::handleReceived() {
uchar sha1Buffer[20];
if (memcmp(&msgKey, hashSha1(data, msgLen + 8 * sizeof(mtpPrime), sha1Buffer) + 1, sizeof(msgKey))) {
LOG(("TCP Error: bad SHA1 hash after aesDecrypt in message"));
TCP_LOG(("TCP Error: bad message %1").arg(mb(encrypted, len * sizeof(mtpPrime)).str()));
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encrypted, len * sizeof(mtpPrime)).str()));
_conn->received().pop_front();
lockFinished.unlock();
return restart();
}
TCP_LOG(("TCP Info: decrypted message %1,%2,%3 is %4 len").arg(msgId).arg(seqNo).arg(logBool(needAck)).arg(msgLen + 8 * sizeof(mtpPrime)));
TCP_LOG(("TCP Info: decrypted message %1,%2,%3 is %4 len").arg(msgId).arg(seqNo).arg(Logs::b(needAck)).arg(msgLen + 8 * sizeof(mtpPrime)));
uint64 serverSession = sessionData->getSession();
if (session != serverSession) {
@@ -2261,7 +2261,7 @@ void MTProtoConnectionPrivate::handleReceived() {
// send acks
uint32 toAckSize = ackRequestData.size();
if (toAckSize) {
DEBUG_LOG(("MTP Info: will send %1 acks, ids: %2").arg(toAckSize).arg(logVectorLong(ackRequestData)));
DEBUG_LOG(("MTP Info: will send %1 acks, ids: %2").arg(toAckSize).arg(Logs::vector(ackRequestData)));
emit sendAnythingAsync(MTPAckSendWaiting);
}
@@ -2273,7 +2273,7 @@ void MTProtoConnectionPrivate::handleReceived() {
DEBUG_LOG(("MTP Info: emitting needToReceive() - need to parse in another thread, haveReceivedMap.size() = %1").arg(sessionData->haveReceivedMap().size()));
}
}
if (emitSignal) {
emit needToReceive();
}
@@ -2344,7 +2344,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
bool needAck = (inSeqNo.v & 0x01);
if (needAck) ackRequestData.push_back(inMsgId);
DEBUG_LOG(("Message Info: message from container, msg_id: %1, needAck: %2").arg(inMsgId.v).arg(logBool(needAck)));
DEBUG_LOG(("Message Info: message from container, msg_id: %1, needAck: %2").arg(inMsgId.v).arg(Logs::b(needAck)));
otherEnd = from + (bytes.v >> 2);
if (otherEnd > end) throw mtpErrorInsufficient();
@@ -2373,7 +2373,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
const QVector<MTPlong> &ids(msg.c_msgs_ack().vmsg_ids.c_vector().v);
uint32 idsCount = ids.size();
DEBUG_LOG(("Message Info: acks received, ids: %1").arg(logVectorLong(ids)));
DEBUG_LOG(("Message Info: acks received, ids: %1").arg(Logs::vector(ids)));
if (!idsCount) return (badTime ? 0 : 1);
if (badTime) {
@@ -2500,7 +2500,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
MTPMsgsStateReq msg(from, end);
const QVector<MTPlong> ids(msg.c_msgs_state_req().vmsg_ids.c_vector().v);
uint32 idsCount = ids.size();
DEBUG_LOG(("Message Info: msgs_state_req received, ids: %1").arg(logVectorLong(ids)));
DEBUG_LOG(("Message Info: msgs_state_req received, ids: %1").arg(Logs::vector(ids)));
if (!idsCount) return 1;
QByteArray info(idsCount, Qt::Uninitialized);
@@ -2546,11 +2546,11 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
case mtpc_msgs_state_info: {
MTPMsgsStateInfo msg(from, end);
const MTPDmsgs_state_info &data(msg.c_msgs_state_info());
uint64 reqMsgId = data.vreq_msg_id.v;
const string &states(data.vinfo.c_string().v);
DEBUG_LOG(("Message Info: msg state received, msgId %1, reqMsgId: %2, HEX states %3").arg(msgId).arg(reqMsgId).arg(mb(states.data(), states.length()).str()));
DEBUG_LOG(("Message Info: msg state received, msgId %1, reqMsgId: %2, HEX states %3").arg(msgId).arg(reqMsgId).arg(Logs::mb(states.data(), states.length()).str()));
mtpRequest requestBuffer;
{ // find this request in session-shared sent requests map
QReadLocker locker(sessionData->haveSentMutex());
@@ -2607,7 +2607,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
QVector<MTPlong> toAck;
DEBUG_LOG(("Message Info: msgs all info received, msgId %1, reqMsgIds: %2, states %3").arg(msgId).arg(logVectorLong(ids)).arg(mb(states.data(), states.length()).str()));
DEBUG_LOG(("Message Info: msgs all info received, msgId %1, reqMsgIds: %2, states %3").arg(msgId).arg(Logs::vector(ids)).arg(Logs::mb(states.data(), states.length()).str()));
handleMsgsStates(ids, states, toAck);
requestsAcked(toAck);
@@ -2669,13 +2669,13 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
resendRequestData.push_back(resMsgId);
}
} return 1;
case mtpc_msg_resend_req: {
MTPMsgResendReq msg(from, end);
const QVector<MTPlong> &ids(msg.c_msg_resend_req().vmsg_ids.c_vector().v);
uint32 idsCount = ids.size();
DEBUG_LOG(("Message Info: resend of msgs requested, ids: %1").arg(logVectorLong(ids)));
DEBUG_LOG(("Message Info: resend of msgs requested, ids: %1").arg(Logs::vector(ids)));
if (!idsCount) return (badTime ? 0 : 1);
QVector<quint64> toResend(ids.size(), Qt::Uninitialized);
@@ -2762,7 +2762,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
mtpBuffer update(from - start);
if (from > start) memcpy(update.data(), start, (from - start) * sizeof(mtpPrime));
QWriteLocker locker(sessionData->haveReceivedMutex());
mtpResponseMap &haveReceived(sessionData->haveReceivedMap());
mtpRequestId fakeRequestId = sessionData->nextFakeRequestId();
@@ -2782,7 +2782,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
MTPPong msg(from, end);
const MTPDpong &data(msg.c_pong());
DEBUG_LOG(("Message Info: pong received, msg_id: %1, ping_id: %2").arg(data.vmsg_id.v).arg(data.vping_id.v));
if (!wasSent(data.vmsg_id.v)) {
DEBUG_LOG(("Message Error: such msg_id %1 ping_id %2 was not sent recently").arg(data.vmsg_id.v).arg(data.vping_id.v));
return 0;
@@ -2817,7 +2817,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt
mtpBuffer update(end - from);
if (end > from) memcpy(update.data(), from, (end - from) * sizeof(mtpPrime));
QWriteLocker locker(sessionData->haveReceivedMutex());
mtpResponseMap &haveReceived(sessionData->haveReceivedMap());
mtpRequestId fakeRequestId = sessionData->nextFakeRequestId();
@@ -2859,14 +2859,14 @@ mtpBuffer MTProtoConnectionPrivate::ungzip(const mtpPrime *from, const mtpPrime
if (res != Z_OK && res != Z_STREAM_END) {
inflateEnd(&stream);
LOG(("RPC Error: could not unpack gziped data, code: %1").arg(res));
DEBUG_LOG(("RPC Error: bad gzip: %1").arg(mb(&packed.c_string().v[0], packedLen).str()));
DEBUG_LOG(("RPC Error: bad gzip: %1").arg(Logs::mb(&packed.c_string().v[0], packedLen).str()));
return mtpBuffer();
}
}
if (stream.avail_out & 0x03) {
uint32 badSize = result.size() * sizeof(mtpPrime) - stream.avail_out;
LOG(("RPC Error: bad length of unpacked data %1").arg(badSize));
DEBUG_LOG(("RPC Error: bad unpacked data %1").arg(mb(result.data(), badSize).str()));
DEBUG_LOG(("RPC Error: bad unpacked data %1").arg(Logs::mb(result.data(), badSize).str()));
return mtpBuffer();
}
result.resize(result.size() - (stream.avail_out >> 2));
@@ -2893,7 +2893,7 @@ bool MTProtoConnectionPrivate::requestsFixTimeSalt(const QVector<MTPlong> &ids,
void MTProtoConnectionPrivate::requestsAcked(const QVector<MTPlong> &ids, bool byResponse) {
uint32 idsCount = ids.size();
DEBUG_LOG(("Message Info: requests acked, ids %1").arg(logVectorLong(ids)));
DEBUG_LOG(("Message Info: requests acked, ids %1").arg(Logs::vector(ids)));
RPCCallbackClears clearedAcked;
QVector<MTPlong> toAckMore;
@@ -2995,7 +2995,7 @@ void MTProtoConnectionPrivate::handleMsgsStates(const QVector<MTPlong> &ids, con
DEBUG_LOG(("Message Info: void ids vector in handleMsgsStates()"));
return;
}
acked.reserve(acked.size() + idsCount);
for (uint32 i = 0, count = idsCount; i < count; ++i) {
@@ -3137,7 +3137,7 @@ void MTProtoConnectionPrivate::updateAuthKey() {
clearMessages();
keyId = newKeyId;
}
DEBUG_LOG(("AuthKey Info: MTProtoConnection update key from MTProtoSession, dc %1 result: %2").arg(dc).arg(mb(&keyId, sizeof(keyId)).str()));
DEBUG_LOG(("AuthKey Info: MTProtoConnection update key from MTProtoSession, dc %1 result: %2").arg(dc).arg(Logs::mb(&keyId, sizeof(keyId)).str()));
if (keyId) {
return authKeyCreated();
}
@@ -3186,7 +3186,7 @@ void MTProtoConnectionPrivate::pqAnswered() {
const MTPDresPQ &res_pq_data(res_pq.c_resPQ());
if (res_pq_data.vnonce != authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in res_pq)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(mb(&res_pq_data.vnonce, 16).str()).arg(mb(&authKeyData->nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&res_pq_data.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str()));
return restart();
}
@@ -3225,7 +3225,7 @@ void MTProtoConnectionPrivate::pqAnswered() {
if (!parsePQ(pq, p, q)) {
LOG(("AuthKey Error: could not factor pq!"));
DEBUG_LOG(("AuthKey Error: problematic pq: %1").arg(mb(&pq[0], pq.length()).str()));
DEBUG_LOG(("AuthKey Error: problematic pq: %1").arg(Logs::mb(&pq[0], pq.length()).str()));
return restart();
}
@@ -3247,7 +3247,7 @@ void MTProtoConnectionPrivate::pqAnswered() {
tmp.reserve(encSize);
p_q_inner.write(tmp);
LOG(("AuthKey Error: too large data for RSA encrypt, size %1").arg(encSize * sizeof(mtpPrime)));
DEBUG_LOG(("AuthKey Error: bad data for RSA encrypt %1").arg(mb(&tmp[0], tmp.size() * 4).str()));
DEBUG_LOG(("AuthKey Error: bad data for RSA encrypt %1").arg(Logs::mb(&tmp[0], tmp.size() * 4).str()));
return restart(); // can't be 255-byte string
}
@@ -3291,12 +3291,12 @@ void MTProtoConnectionPrivate::dhParamsAnswered() {
const MTPDserver_DH_params_ok &encDH(res_DH_params.c_server_DH_params_ok());
if (encDH.vnonce != authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_ok)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(mb(&encDH.vnonce, 16).str()).arg(mb(&authKeyData->nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str()));
return restart();
}
if (encDH.vserver_nonce != authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_ok)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(mb(&encDH.vserver_nonce, 16).str()).arg(mb(&authKeyData->server_nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str()));
return restart();
}
@@ -3304,7 +3304,7 @@ void MTProtoConnectionPrivate::dhParamsAnswered() {
uint32 encDHLen = encDHStr.length(), encDHBufLen = encDHLen >> 2;
if ((encDHLen & 0x03) || encDHBufLen < 6) {
LOG(("AuthKey Error: bad encrypted data length %1 (in server_DH_params_ok)!").arg(encDHLen));
DEBUG_LOG(("AuthKey Error: received encrypted data %1").arg(mb(&encDHStr[0], encDHLen).str()));
DEBUG_LOG(("AuthKey Error: received encrypted data %1").arg(Logs::mb(&encDHStr[0], encDHLen).str()));
return restart();
}
@@ -3334,18 +3334,18 @@ void MTProtoConnectionPrivate::dhParamsAnswered() {
const MTPDserver_DH_inner_data &dh_inner_data(dh_inner.c_server_DH_inner_data());
if (dh_inner_data.vnonce != authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_inner_data)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(mb(&dh_inner_data.vnonce, 16).str()).arg(mb(&authKeyData->nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&dh_inner_data.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str()));
return restart();
}
if (dh_inner_data.vserver_nonce != authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_inner_data)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(mb(&dh_inner_data.vserver_nonce, 16).str()).arg(mb(&authKeyData->server_nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&dh_inner_data.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str()));
return restart();
}
uchar sha1Buffer[20];
if (memcmp(&decBuffer[0], hashSha1(&decBuffer[5], (to - from) * sizeof(mtpPrime), sha1Buffer), 20)) {
LOG(("AuthKey Error: sha1 hash of encrypted part did not match!"));
DEBUG_LOG(("AuthKey Error: sha1 did not match, server_nonce: %1, new_nonce %2, encrypted data %3").arg(mb(&authKeyData->server_nonce, 16).str()).arg(mb(&authKeyData->new_nonce, 16).str()).arg(mb(&encDHStr[0], encDHLen).str()));
DEBUG_LOG(("AuthKey Error: sha1 did not match, server_nonce: %1, new_nonce %2, encrypted data %3").arg(Logs::mb(&authKeyData->server_nonce, 16).str()).arg(Logs::mb(&authKeyData->new_nonce, 16).str()).arg(Logs::mb(&encDHStr[0], encDHLen).str()));
return restart();
}
unixtimeSet(dh_inner_data.vserver_time.v);
@@ -3353,15 +3353,15 @@ void MTProtoConnectionPrivate::dhParamsAnswered() {
const string &dhPrime(dh_inner_data.vdh_prime.c_string().v), &g_a(dh_inner_data.vg_a.c_string().v);
if (dhPrime.length() != 256 || g_a.length() != 256) {
LOG(("AuthKey Error: bad dh_prime len (%1) or g_a len (%2)").arg(dhPrime.length()).arg(g_a.length()));
DEBUG_LOG(("AuthKey Error: dh_prime %1, g_a %2").arg(mb(&dhPrime[0], dhPrime.length()).str()).arg(mb(&g_a[0], g_a.length()).str()));
DEBUG_LOG(("AuthKey Error: dh_prime %1, g_a %2").arg(Logs::mb(&dhPrime[0], dhPrime.length()).str()).arg(Logs::mb(&g_a[0], g_a.length()).str()));
return restart();
}
// check that dhPrime and (dhPrime - 1) / 2 are really prime using openssl BIGNUM methods
_BigNumPrimeTest bnPrimeTest;
if (!bnPrimeTest.isPrimeAndGood(&dhPrime[0], MTPMillerRabinIterCount, dh_inner_data.vg.v)) {
LOG(("AuthKey Error: bad dh_prime primality!").arg(dhPrime.length()).arg(g_a.length()));
DEBUG_LOG(("AuthKey Error: dh_prime %1").arg(mb(&dhPrime[0], dhPrime.length()).str()));
DEBUG_LOG(("AuthKey Error: dh_prime %1").arg(Logs::mb(&dhPrime[0], dhPrime.length()).str()));
return restart();
}
@@ -3376,18 +3376,18 @@ void MTProtoConnectionPrivate::dhParamsAnswered() {
const MTPDserver_DH_params_fail &encDH(res_DH_params.c_server_DH_params_fail());
if (encDH.vnonce != authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_fail)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(mb(&encDH.vnonce, 16).str()).arg(mb(&authKeyData->nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str()));
return restart();
}
if (encDH.vserver_nonce != authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_fail)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(mb(&encDH.vserver_nonce, 16).str()).arg(mb(&authKeyData->server_nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str()));
return restart();
}
uchar sha1Buffer[20];
if (encDH.vnew_nonce_hash != *(MTPint128*)(hashSha1(&authKeyData->new_nonce, 32, sha1Buffer) + 1)) {
LOG(("AuthKey Error: received new_nonce_hash did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash: %1, new_nonce: %2").arg(mb(&encDH.vnew_nonce_hash, 16).str()).arg(mb(&authKeyData->new_nonce, 32).str()));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash: %1, new_nonce: %2").arg(Logs::mb(&encDH.vnew_nonce_hash, 16).str()).arg(Logs::mb(&authKeyData->new_nonce, 32).str()));
return restart();
}
LOG(("AuthKey Error: server_DH_params_fail received!"));
@@ -3430,7 +3430,7 @@ void MTProtoConnectionPrivate::dhClientParamsSend() {
MTPSet_client_DH_params req_client_DH_params;
req_client_DH_params.vnonce = authKeyData->nonce;
req_client_DH_params.vserver_nonce = authKeyData->server_nonce;
string &sdhEncString(req_client_DH_params.vencrypted_data._string().v);
uint32 client_dh_inner_size = client_dh_inner.innerLength(), encSize = (client_dh_inner_size >> 2) + 5, encFullSize = encSize;
@@ -3477,14 +3477,14 @@ void MTProtoConnectionPrivate::dhClientParamsAnswered() {
const MTPDdh_gen_ok &resDH(res_client_DH_params.c_dh_gen_ok());
if (resDH.vnonce != authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_ok)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(mb(&resDH.vnonce, 16).str()).arg(mb(&authKeyData->nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str()));
lockFinished.unlock();
return restart();
}
if (resDH.vserver_nonce != authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_ok)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(mb(&resDH.vserver_nonce, 16).str()).arg(mb(&authKeyData->server_nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str()));
lockFinished.unlock();
return restart();
@@ -3493,7 +3493,7 @@ void MTProtoConnectionPrivate::dhClientParamsAnswered() {
uchar sha1Buffer[20];
if (resDH.vnew_nonce_hash1 != *(MTPint128*)(hashSha1(authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) {
LOG(("AuthKey Error: received new_nonce_hash1 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash1: %1, new_nonce_buf: %2").arg(mb(&resDH.vnew_nonce_hash1, 16).str()).arg(mb(authKeyData->new_nonce_buf, 41).str()));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash1: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash1, 16).str()).arg(Logs::mb(authKeyData->new_nonce_buf, 41).str()));
lockFinished.unlock();
return restart();
@@ -3506,7 +3506,7 @@ void MTProtoConnectionPrivate::dhClientParamsAnswered() {
authKey->setKey(authKeyData->auth_key);
authKey->setDC(dc % _mtp_internal::dcShift);
DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2, auth key: %3").arg(authKey->keyId()).arg(serverSalt).arg(mb(authKeyData->auth_key, 256).str()));
DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2, auth key: %3").arg(authKey->keyId()).arg(serverSalt).arg(Logs::mb(authKeyData->auth_key, 256).str()));
sessionData->owner()->notifyKeyCreated(authKey); // slot will call authKeyCreated()
sessionData->clear();
@@ -3517,14 +3517,14 @@ void MTProtoConnectionPrivate::dhClientParamsAnswered() {
const MTPDdh_gen_retry &resDH(res_client_DH_params.c_dh_gen_retry());
if (resDH.vnonce != authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_retry)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(mb(&resDH.vnonce, 16).str()).arg(mb(&authKeyData->nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str()));
lockFinished.unlock();
return restart();
}
if (resDH.vserver_nonce != authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_retry)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(mb(&resDH.vserver_nonce, 16).str()).arg(mb(&authKeyData->server_nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str()));
lockFinished.unlock();
return restart();
@@ -3533,7 +3533,7 @@ void MTProtoConnectionPrivate::dhClientParamsAnswered() {
uchar sha1Buffer[20];
if (resDH.vnew_nonce_hash2 != *(MTPint128*)(hashSha1(authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) {
LOG(("AuthKey Error: received new_nonce_hash2 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash2: %1, new_nonce_buf: %2").arg(mb(&resDH.vnew_nonce_hash2, 16).str()).arg(mb(authKeyData->new_nonce_buf, 41).str()));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash2: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash2, 16).str()).arg(Logs::mb(authKeyData->new_nonce_buf, 41).str()));
lockFinished.unlock();
return restart();
@@ -3545,14 +3545,14 @@ void MTProtoConnectionPrivate::dhClientParamsAnswered() {
const MTPDdh_gen_fail &resDH(res_client_DH_params.c_dh_gen_fail());
if (resDH.vnonce != authKeyData->nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_fail)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(mb(&resDH.vnonce, 16).str()).arg(mb(&authKeyData->nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce, 16).str()).arg(Logs::mb(&authKeyData->nonce, 16).str()));
lockFinished.unlock();
return restart();
}
if (resDH.vserver_nonce != authKeyData->server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_fail)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(mb(&resDH.vserver_nonce, 16).str()).arg(mb(&authKeyData->server_nonce, 16).str()));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce, 16).str()).arg(Logs::mb(&authKeyData->server_nonce, 16).str()));
lockFinished.unlock();
return restart();
@@ -3561,7 +3561,7 @@ void MTProtoConnectionPrivate::dhClientParamsAnswered() {
uchar sha1Buffer[20];
if (resDH.vnew_nonce_hash3 != *(MTPint128*)(hashSha1(authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) {
LOG(("AuthKey Error: received new_nonce_hash3 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash3: %1, new_nonce_buf: %2").arg(mb(&resDH.vnew_nonce_hash3, 16).str()).arg(mb(authKeyData->new_nonce_buf, 41).str()));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash3: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash3, 16).str()).arg(Logs::mb(authKeyData->new_nonce_buf, 41).str()));
lockFinished.unlock();
return restart();
@@ -3622,7 +3622,7 @@ void MTProtoConnectionPrivate::onError4(bool mayBeBadKey) {
destroyConn();
_waitForConnectedTimer.stop();
MTP_LOG(dc, ("Restarting after error in IPv4 connection, maybe bad key: %1..").arg(logBool(mayBeBadKey)));
MTP_LOG(dc, ("Restarting after error in IPv4 connection, maybe bad key: %1..").arg(Logs::b(mayBeBadKey)));
return restart(mayBeBadKey);
} else {
destroyConn(&_conn4);
@@ -3636,7 +3636,7 @@ void MTProtoConnectionPrivate::onError6(bool mayBeBadKey) {
destroyConn();
_waitForConnectedTimer.stop();
MTP_LOG(dc, ("Restarting after error in IPv6 connection, maybe bad key: %1..").arg(logBool(mayBeBadKey)));
MTP_LOG(dc, ("Restarting after error in IPv6 connection, maybe bad key: %1..").arg(Logs::b(mayBeBadKey)));
return restart(mayBeBadKey);
} else {
destroyConn(&_conn6);
@@ -3692,18 +3692,18 @@ bool MTProtoConnectionPrivate::readResponseNotSecure(TResponse &response) {
uint32 len = buffer.size();
if (len < 5) {
LOG(("AuthKey Error: bad request answer, len = %1").arg(len * sizeof(mtpPrime)));
DEBUG_LOG(("AuthKey Error: answer bytes %1").arg(mb(answer, len * sizeof(mtpPrime)).str()));
DEBUG_LOG(("AuthKey Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str()));
return false;
}
if (answer[0] != 0 || answer[1] != 0 || (((uint32)answer[2]) & 0x03) != 1/* || (unixtime() - answer[3] > 300) || (answer[3] - unixtime() > 60)*/) { // didnt sync time yet
LOG(("AuthKey Error: bad request answer start (%1 %2 %3)").arg(answer[0]).arg(answer[1]).arg(answer[2]));
DEBUG_LOG(("AuthKey Error: answer bytes %1").arg(mb(answer, len * sizeof(mtpPrime)).str()));
DEBUG_LOG(("AuthKey Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str()));
return false;
}
uint32 answerLen = (uint32)answer[4];
if (answerLen != (len - 5) * sizeof(mtpPrime)) {
LOG(("AuthKey Error: bad request answer %1 <> %2").arg(answerLen).arg((len - 5) * sizeof(mtpPrime)));
DEBUG_LOG(("AuthKey Error: answer bytes %1").arg(mb(answer, len * sizeof(mtpPrime)).str()));
DEBUG_LOG(("AuthKey Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str()));
return false;
}
const mtpPrime *from(answer + 5), *end(from + len - 5);
@@ -3720,7 +3720,7 @@ bool MTProtoConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResp
uint32 messageSize = mtpRequestData::messageSize(request);
if (messageSize < 5 || fullSize < messageSize + 4) return false;
ReadLockerAttempt lock(sessionData->keyMutex());
if (!lock) {
DEBUG_LOG(("MTP Info: could not lock key for read in sendBuffer(), dc %1, restarting..").arg(dc));
@@ -3758,7 +3758,7 @@ bool MTProtoConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResp
*((MTPint128*)&result[4]) = msgKey;
aesEncrypt(request->constData(), &result[8], fullSize * sizeof(mtpPrime), key, msgKey);
DEBUG_LOG(("MTP Info: sending request, size: %1, num: %2, time: %3").arg(fullSize + 6).arg((*request)[4]).arg((*request)[5]));
_conn->setSentEncrypted();

View File

@@ -23,8 +23,6 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "lang.h"
#if defined _DEBUG || defined _WITH_DEBUG
QString mtpWrapNumber(float64 number) {
return QString::number(number);
}
@@ -63,9 +61,9 @@ void mtpTextSerializeCore(MTPStringLogger &to, const mtpPrime *&from, const mtpP
if (str.toUtf8() == strUtf8) {
to.add("\"").add(str.replace('\\', "\\\\").replace('"', "\\\"").replace('\n', "\\n")).add("\" [STRING]");
} else if (strUtf8.size() < 64) {
to.add(mb(strUtf8.constData(), strUtf8.size()).str()).add(" [").add(mtpWrapNumber(strUtf8.size())).add(" BYTES]");
to.add(Logs::mb(strUtf8.constData(), strUtf8.size()).str()).add(" [").add(mtpWrapNumber(strUtf8.size())).add(" BYTES]");
} else {
to.add(mb(strUtf8.constData(), 16).str()).add(".. [").add(mtpWrapNumber(strUtf8.size())).add(" BYTES]");
to.add(Logs::mb(strUtf8.constData(), 16).str()).add(".. [").add(mtpWrapNumber(strUtf8.size())).add(" BYTES]");
}
} break;
@@ -151,8 +149,6 @@ void mtpTextSerializeCore(MTPStringLogger &to, const mtpPrime *&from, const mtpP
}
}
#endif
QString stickerSetTitle(const MTPDstickerSet &s) {
QString title = qs(s.vtitle);
if ((s.vflags.v & MTPDstickerSet::flag_official) && !title.compare(qstr("Great Minds"), Qt::CaseInsensitive)) {

View File

@@ -162,7 +162,7 @@ typedef QMap<mtpRequestId, mtpRequest> mtpPreRequestMap;
typedef QMap<mtpMsgId, mtpRequest> mtpRequestMap;
typedef QMap<mtpMsgId, bool> mtpMsgIdsSet;
class mtpMsgIdsMap : public QMap<mtpMsgId, bool> {
public:
public:
typedef QMap<mtpMsgId, bool> ParentType;
bool insert(const mtpMsgId &k, bool v) {
@@ -595,7 +595,7 @@ inline bool operator!=(const MTPint256 &a, const MTPint256 &b) {
class MTPdouble {
public:
float64 v;
MTPdouble() {
}
MTPdouble(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_double) {
@@ -702,7 +702,7 @@ public:
from += ((l + 1) >> 2) + (((l + 1) & 0x03) ? 1 : 0);
}
if (from > end) throw mtpErrorInsufficient();
if (!data) setData(new MTPDstring());
MTPDstring &v(_string());
v.v.resize(l);
@@ -895,7 +895,6 @@ inline bool operator!=(const MTPvector<T> &a, const MTPvector<T> &b) {
}
// Human-readable text serialization
#if (defined _DEBUG || defined _WITH_DEBUG)
template <typename Type>
QString mtpWrapNumber(Type number, int32 base = 10) {
@@ -965,8 +964,6 @@ inline QString mtpTextSerialize(const mtpPrime *&from, const mtpPrime *end) {
return QString::fromUtf8(to.p, to.size);
}
#endif
#include "mtpScheme.h"
inline MTPbool MTP_bool(bool v) {

View File

@@ -22,8 +22,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "mtpScheme.h"
#if (defined _DEBUG || defined _WITH_DEBUG)
typedef QVector<mtpTypeId> Types;
typedef QVector<int32> StagesFlags;
@@ -8266,4 +8264,3 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
}
}
#endif

View File

@@ -31439,8 +31439,4 @@ inline MTPmessages_botResults MTP_messages_botResults(MTPint _flags, const MTPlo
}
// Human-readable text serialization
#if (defined _DEBUG || defined _WITH_DEBUG)
void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons, uint32 level, mtpPrime vcons);
#endif

View File

@@ -86,7 +86,7 @@ void MTProtoSession::start(int32 dcenter) {
DEBUG_LOG(("Session Info: MTProtoSession::start called on already started session"));
return;
}
msSendCall = msWait = 0;
connect(&timeouter, SIGNAL(timeout()), this, SLOT(checkRequestsByTimer()));
@@ -265,7 +265,7 @@ void MTProtoSession::checkRequestsByTimer() {
}
if (stateRequestIds.size()) {
DEBUG_LOG(("MTP Info: requesting state of msgs: %1").arg(logVectorLong(stateRequestIds)));
DEBUG_LOG(("MTP Info: requesting state of msgs: %1").arg(Logs::vector(stateRequestIds)));
{
QWriteLocker locker(data.stateRequestMutex());
for (uint32 i = 0, l = stateRequestIds.size(); i < l; ++i) {
@@ -398,7 +398,7 @@ mtpRequestId MTProtoSession::resend(quint64 msgId, quint64 msCanWait, bool force
if (sendMsgStateInfo) {
char cantResend[2] = {1, 0};
DEBUG_LOG(("Message Info: cant resend %1, request not found").arg(msgId));
return send(MTP_msgs_state_info(MTP_long(msgId), MTP_string(string(cantResend, cantResend + 1))));
}
return 0;
@@ -485,7 +485,7 @@ void MTProtoSession::layerWasInitedForDC(bool wasInited) {
}
void MTProtoSession::notifyLayerInited(bool wasInited) {
DEBUG_LOG(("MTP Info: emitting MTProtoDC::layerWasInited(%1), dcWithShift %2").arg(logBool(wasInited)).arg(dcWithShift));
DEBUG_LOG(("MTP Info: emitting MTProtoDC::layerWasInited(%1), dcWithShift %2").arg(Logs::b(wasInited)).arg(dcWithShift));
dc->setConnectionInited(wasInited);
emit dc->layerWasInited(wasInited);
}

View File

@@ -91,8 +91,6 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD
App::contextItem(0);
_linkTipTimer.setSingleShot(true);
connect(&_linkTipTimer, SIGNAL(timeout()), this, SLOT(showLinkTip()));
_touchSelectTimer.setSingleShot(true);
connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect()));
@@ -355,7 +353,7 @@ void OverviewInner::repaintItem(MsgId itemId, int32 itemIndex) {
int32 row = (_photosToAdd + shownAtIndex) / _photosInRow, col = (_photosToAdd + shownAtIndex) % _photosInRow;
update(int32(col * w), _marginTop + int32(row * vsize), qCeil(w), vsize);
} else {
int32 top = _items.at(itemIndex)->getOverviewItemInfo()->top();
int32 top = _items.at(itemIndex)->Get<OverviewItemInfo>()->top();
if (_reversed) top = _height - top;
update(_rowsLeft, _marginTop + top, _rowWidth, _items.at(itemIndex)->height());
}
@@ -729,7 +727,7 @@ QPoint OverviewInner::mapMouseToItem(QPoint p, MsgId itemId, int32 itemIndex) {
p.setX(p.x() - int32(col * w) - st::overviewPhotoSkip);
p.setY(p.y() - _marginTop - row * (_rowWidth + st::overviewPhotoSkip) - st::overviewPhotoSkip);
} else {
int32 top = _items.at(itemIndex)->getOverviewItemInfo()->top();
int32 top = _items.at(itemIndex)->Get<OverviewItemInfo>()->top();
if (_reversed) top = _height - top;
p.setY(p.y() - _marginTop - top);
}
@@ -765,7 +763,7 @@ int32 OverviewInner::itemTop(const FullMsgId &msgId) const {
int32 itemIndex = -1;
fixItemIndex(itemIndex, (msgId.channel == _channel) ? msgId.msg : ((_migrated && msgId.channel == _migrated->channelId()) ? -msgId.msg : 0));
if (itemIndex >= 0) {
int32 top = _items.at(itemIndex)->getOverviewItemInfo()->top();
int32 top = _items.at(itemIndex)->Get<OverviewItemInfo>()->top();
if (_reversed) top = _height - top;
return _marginTop + top;
}
@@ -879,10 +877,10 @@ void OverviewInner::paintEvent(QPaintEvent *e) {
int32 y = 0, w = _rowWidth;
for (int32 j = 0, l = _items.size(); j < l; ++j) {
int32 i = _reversed ? (l - j - 1) : j, nexti = _reversed ? (i - 1) : (i + 1);
int32 nextItemTop = (j + 1 == l) ? (_reversed ? 0 : _height) : _items.at(nexti)->getOverviewItemInfo()->top();
int32 nextItemTop = (j + 1 == l) ? (_reversed ? 0 : _height) : _items.at(nexti)->Get<OverviewItemInfo>()->top();
if (_reversed) nextItemTop = _height - nextItemTop;
if (_marginTop + nextItemTop > r.top()) {
OverviewItemInfo *info = _items.at(i)->getOverviewItemInfo();
OverviewItemInfo *info = _items.at(i)->Get<OverviewItemInfo>();
int32 curY = info->top();
if (_reversed) curY = _height - curY;
if (_marginTop + curY >= r.y() + r.height()) break;
@@ -944,10 +942,10 @@ void OverviewInner::onUpdateSelected() {
for (int32 j = 0, l = _items.size(); j < l; ++j) {
bool lastItem = (j + 1 == l);
int32 i = _reversed ? (l - j - 1) : j, nexti = _reversed ? (i - 1) : (i + 1);
int32 nextItemTop = lastItem ? (_reversed ? 0 : _height) : _items.at(nexti)->getOverviewItemInfo()->top();
int32 nextItemTop = lastItem ? (_reversed ? 0 : _height) : _items.at(nexti)->Get<OverviewItemInfo>()->top();
if (_reversed) nextItemTop = _height - nextItemTop;
if (_marginTop + nextItemTop > m.y() || lastItem) {
int32 top = _items.at(i)->getOverviewItemInfo()->top();
int32 top = _items.at(i)->Get<OverviewItemInfo>()->top();
if (_reversed) top = _height - top;
if (!_items.at(i)->toLayoutMediaItem()) { // day item
int32 h = _items.at(i)->height();
@@ -956,11 +954,11 @@ void OverviewInner::onUpdateSelected() {
if (i > 0 && (beforeItem || i == _items.size() - 1)) {
--i;
if (!_items.at(i)->toLayoutMediaItem()) break; // wtf
top = _items.at(i)->getOverviewItemInfo()->top();
top = _items.at(i)->Get<OverviewItemInfo>()->top();
} else if (i < _items.size() - 1 && (!beforeItem || !i)) {
++i;
if (!_items.at(i)->toLayoutMediaItem()) break; // wtf
top = _items.at(i)->getOverviewItemInfo()->top();
top = _items.at(i)->Get<OverviewItemInfo>()->top();
} else {
break; // wtf
}
@@ -1000,7 +998,7 @@ void OverviewInner::onUpdateSelected() {
}
}
textlnkOver(lnk);
QToolTip::hideText();
PopupTooltip::Hide();
App::hoveredLinkItem(lnk ? item : 0);
if (textlnkOver()) {
if (item && index >= 0) {
@@ -1015,16 +1013,16 @@ void OverviewInner::onUpdateSelected() {
lnkChanged = true;
if (oldMousedItem) repaintItem(oldMousedItem, oldMousedItemIndex);
if (item) repaintItem(item);
QToolTip::hideText();
PopupTooltip::Hide();
}
if (_cursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) {
QToolTip::hideText();
PopupTooltip::Hide();
}
if (cursorState != _cursorState) {
_cursorState = cursorState;
}
if (lnk || cursorState == HistoryInDateCursorState) {
_linkTipTimer.start(1000);
PopupTooltip::Show(1000, this);
}
fixItemIndex(_dragItemIndex, _dragItem);
@@ -1139,19 +1137,20 @@ void OverviewInner::onUpdateSelected() {
}
}
QPoint OverviewInner::tooltipPos() const {
return _dragPos;
}
void OverviewInner::showLinkTip() {
QString OverviewInner::tooltipText() const {
TextLinkPtr lnk = textlnkOver();
int32 dd = QApplication::startDragDistance();
QPoint dp(mapFromGlobal(_dragPos));
QRect r(dp.x() - dd, dp.y() - dd, 2 * dd, 2 * dd);
if (lnk && !lnk->fullDisplayed()) {
QToolTip::showText(_dragPos, lnk->readable(), this, r);
return lnk->readable();
} else if (_cursorState == HistoryInDateCursorState && _dragAction == NoDrag && _mousedItem) {
if (HistoryItem *item = App::histItemById(itemChannel(_mousedItem), itemMsgId(_mousedItem))) {
QToolTip::showText(_dragPos, item->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)), this, r);
return item->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat));
}
}
return QString();
}
void OverviewInner::updateDragSelection(MsgId dragSelFrom, int32 dragSelFromIndex, MsgId dragSelTo, int32 dragSelToIndex, bool dragSelecting) {
@@ -1385,7 +1384,7 @@ int32 OverviewInner::resizeToWidth(int32 nwidth, int32 scrollTop, int32 minHeigh
for (int32 i = 0, l = _items.size(); i < l; ++i) {
int32 h = _items.at(i)->resizeGetHeight(_rowWidth);
if (resize) {
_items.at(i)->getOverviewItemInfo()->setTop(_height + (_reversed ? h : 0));
_items.at(i)->Get<OverviewItemInfo>()->setTop(_height + (_reversed ? h : 0));
_height += h;
}
}
@@ -1746,7 +1745,7 @@ void OverviewInner::mediaOverviewUpdated() {
if (allGood) {
if (_items.size() > index && complexMsgId(_items.at(index)->getItem()) == msgid) {
if (withDates) prevDate = _items.at(index)->getItem()->date.date();
top = _items.at(index)->getOverviewItemInfo()->top();
top = _items.at(index)->Get<OverviewItemInfo>()->top();
if (!_reversed) {
top += _items.at(index)->height();
}
@@ -1756,7 +1755,7 @@ void OverviewInner::mediaOverviewUpdated() {
if (_items.size() > index + 1 && !_items.at(index)->toLayoutMediaItem() && complexMsgId(_items.at(index + 1)->getItem()) == msgid) { // day item
++index;
if (withDates) prevDate = _items.at(index)->getItem()->date.date();
top = _items.at(index)->getOverviewItemInfo()->top();
top = _items.at(index)->Get<OverviewItemInfo>()->top();
if (!_reversed) {
top += _items.at(index)->height();
}
@@ -1877,7 +1876,7 @@ void OverviewInner::repaintItem(const HistoryItem *msg) {
if (history == _migrated) msgid = -msgid;
for (int32 i = 0, l = _items.size(); i != l; ++i) {
if (complexMsgId(_items.at(i)->getItem()) == msgid) {
int32 top = _items.at(i)->getOverviewItemInfo()->top();
int32 top = _items.at(i)->Get<OverviewItemInfo>()->top();
if (_reversed) top = _height - top;
update(_rowsLeft, _marginTop + top, _rowWidth, _items.at(i)->height());
break;
@@ -1982,7 +1981,7 @@ int32 OverviewInner::setLayoutItem(int32 index, LayoutItem *item, int32 top) {
_items.push_back(item);
}
int32 h = item->resizeGetHeight(_rowWidth);
if (OverviewItemInfo *info = item->getOverviewItemInfo()) {
if (OverviewItemInfo *info = item->Get<OverviewItemInfo>()) {
info->setTop(top + (_reversed ? h : 0));
}
return h;

View File

@@ -21,7 +21,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#pragma once
class OverviewWidget;
class OverviewInner : public QWidget, public RPCSender {
class OverviewInner : public QWidget, public AbstractTooltipShower, public RPCSender {
Q_OBJECT
public:
@@ -76,12 +76,15 @@ public:
void clearSelectedItems(bool onlyTextSelection = false);
void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true);
// AbstractTooltipShower
virtual QString tooltipText() const;
virtual QPoint tooltipPos() const;
~OverviewInner();
public slots:
void onUpdateSelected();
void showLinkTip();
void openContextUrl();
void copyContextUrl();

View File

@@ -35,3 +35,12 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#ifdef Q_OS_WIN
#include "pspecific_wnd.h"
#endif
namespace PlatformSpecific {
struct Initializer {
Initializer();
~Initializer();
};
}

View File

@@ -1,17 +1,17 @@
/*
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-2015 John Preston, https://desktop.telegram.org
*/
@@ -197,8 +197,6 @@ namespace {
typedef UnityLauncherEntry* (*f_unity_launcher_entry_get_for_desktop_id)(const gchar* desktop_id);
f_unity_launcher_entry_get_for_desktop_id ps_unity_launcher_entry_get_for_desktop_id = 0;
QStringList _initLogs;
template <typename TFunction>
bool loadFunction(QLibrary &lib, const char *name, TFunction &func) {
if (!lib.isLoaded()) return false;
@@ -207,7 +205,7 @@ namespace {
if (func) {
return true;
} else {
_initLogs.push_back(QString("Init Error: Failed to load '%1' function!").arg(name));
LOG(("Error: failed to load '%1' function!").arg(name));
return false;
}
}
@@ -348,159 +346,141 @@ namespace {
return FALSE;
}
class _PsInitializer {
public:
_PsInitializer() {
static bool inited = false;
if (inited) return;
inited = true;
QString cdesktop = QString(getenv("XDG_CURRENT_DESKTOP")).toLower();
noQtTrayIcon = (cdesktop == qstr("pantheon")) || (cdesktop == qstr("gnome"));
tryAppIndicator = (cdesktop == qstr("xfce"));
noTryUnity = (cdesktop != qstr("unity"));
if (noQtTrayIcon) cSetSupportTray(false);
std::cout << "libs init..\n";
setupGtk();
setupUnity();
bool loadLibrary(QLibrary &lib, const char *name, int version) {
DEBUG_LOG(("Loading '%1' with version %2..").arg(QLatin1String(name)).arg(version));
lib.setFileNameAndVersion(QLatin1String(name), version);
if (lib.load()) {
DEBUG_LOG(("Loaded '%1' with version %2!").arg(QLatin1String(name)).arg(version));
return true;
}
bool loadLibrary(QLibrary &lib, const char *name, int version) {
std::cout << "loading " << name << " with version " << version << "..\n";
lib.setFileNameAndVersion(QLatin1String(name), version);
if (lib.load()) {
std::cout << "loaded " << name << " with version " << version << "!\n";
_initLogs.push_back(QString("Loaded '%1' version %2 library").arg(name).arg(version));
return true;
}
lib.setFileNameAndVersion(QLatin1String(name), QString());
if (lib.load()) {
std::cout << "loaded " << name << " without version!\n";
_initLogs.push_back(QString("Loaded '%1' without version library").arg(name));
return true;
}
std::cout << "could not load " << name << " without version.\n";
return false;
lib.setFileNameAndVersion(QLatin1String(name), QString());
if (lib.load()) {
DEBUG_LOG(("Loaded '%1' without version!").arg(QLatin1String(name)));
return true;
}
LOG(("Could not load '%1' with version %2 :(").arg(QLatin1String(name)).arg(version));
return false;
}
void setupGtkBase(QLibrary &lib_gtk) {
if (!loadFunction(lib_gtk, "gtk_init_check", ps_gtk_init_check)) return;
if (!loadFunction(lib_gtk, "gtk_menu_new", ps_gtk_menu_new)) return;
if (!loadFunction(lib_gtk, "gtk_menu_get_type", ps_gtk_menu_get_type)) return;
void setupGtkBase(QLibrary &lib_gtk) {
if (!loadFunction(lib_gtk, "gtk_init_check", ps_gtk_init_check)) return;
if (!loadFunction(lib_gtk, "gtk_menu_new", ps_gtk_menu_new)) return;
if (!loadFunction(lib_gtk, "gtk_menu_get_type", ps_gtk_menu_get_type)) return;
if (!loadFunction(lib_gtk, "gtk_menu_item_new_with_label", ps_gtk_menu_item_new_with_label)) return;
if (!loadFunction(lib_gtk, "gtk_menu_item_set_label", ps_gtk_menu_item_set_label)) return;
if (!loadFunction(lib_gtk, "gtk_menu_shell_append", ps_gtk_menu_shell_append)) return;
if (!loadFunction(lib_gtk, "gtk_menu_shell_get_type", ps_gtk_menu_shell_get_type)) return;
if (!loadFunction(lib_gtk, "gtk_widget_show", ps_gtk_widget_show)) return;
if (!loadFunction(lib_gtk, "gtk_widget_get_toplevel", ps_gtk_widget_get_toplevel)) return;
if (!loadFunction(lib_gtk, "gtk_widget_get_visible", ps_gtk_widget_get_visible)) return;
if (!loadFunction(lib_gtk, "gtk_widget_set_sensitive", ps_gtk_widget_set_sensitive)) return;
if (!loadFunction(lib_gtk, "gtk_menu_item_new_with_label", ps_gtk_menu_item_new_with_label)) return;
if (!loadFunction(lib_gtk, "gtk_menu_item_set_label", ps_gtk_menu_item_set_label)) return;
if (!loadFunction(lib_gtk, "gtk_menu_shell_append", ps_gtk_menu_shell_append)) return;
if (!loadFunction(lib_gtk, "gtk_menu_shell_get_type", ps_gtk_menu_shell_get_type)) return;
if (!loadFunction(lib_gtk, "gtk_widget_show", ps_gtk_widget_show)) return;
if (!loadFunction(lib_gtk, "gtk_widget_get_toplevel", ps_gtk_widget_get_toplevel)) return;
if (!loadFunction(lib_gtk, "gtk_widget_get_visible", ps_gtk_widget_get_visible)) return;
if (!loadFunction(lib_gtk, "gtk_widget_set_sensitive", ps_gtk_widget_set_sensitive)) return;
if (!loadFunction(lib_gtk, "g_type_check_instance_cast", ps_g_type_check_instance_cast)) return;
if (!loadFunction(lib_gtk, "g_signal_connect_data", ps_g_signal_connect_data)) return;
if (!loadFunction(lib_gtk, "g_type_check_instance_cast", ps_g_type_check_instance_cast)) return;
if (!loadFunction(lib_gtk, "g_signal_connect_data", ps_g_signal_connect_data)) return;
if (!loadFunction(lib_gtk, "g_object_ref_sink", ps_g_object_ref_sink)) return;
if (!loadFunction(lib_gtk, "g_object_unref", ps_g_object_unref)) return;
if (!loadFunction(lib_gtk, "g_object_ref_sink", ps_g_object_ref_sink)) return;
if (!loadFunction(lib_gtk, "g_object_unref", ps_g_object_unref)) return;
DEBUG_LOG(("Library gtk functions loaded!"));
if (ps_gtk_init_check(0, 0)) {
DEBUG_LOG(("Checked gtk with gtk_init_check!"));
useGtkBase = true;
std::cout << "loaded gtk funcs!\n";
} else {
DEBUG_LOG(("Failed to gtk_init_check(0, 0)!"));
}
}
void setupAppIndicator(QLibrary &lib_indicator) {
if (!loadFunction(lib_indicator, "app_indicator_new", ps_app_indicator_new)) return;
if (!loadFunction(lib_indicator, "app_indicator_set_status", ps_app_indicator_set_status)) return;
if (!loadFunction(lib_indicator, "app_indicator_set_menu", ps_app_indicator_set_menu)) return;
if (!loadFunction(lib_indicator, "app_indicator_set_icon_full", ps_app_indicator_set_icon_full)) return;
useAppIndicator = true;
std::cout << "loaded appindicator funcs!\n";
}
void setupAppIndicator(QLibrary &lib_indicator) {
if (!loadFunction(lib_indicator, "app_indicator_new", ps_app_indicator_new)) return;
if (!loadFunction(lib_indicator, "app_indicator_set_status", ps_app_indicator_set_status)) return;
if (!loadFunction(lib_indicator, "app_indicator_set_menu", ps_app_indicator_set_menu)) return;
if (!loadFunction(lib_indicator, "app_indicator_set_icon_full", ps_app_indicator_set_icon_full)) return;
useAppIndicator = true;
DEBUG_LOG(("Library appindicator functions loaded!"));
}
void setupGtk() {
QLibrary lib_gtk, lib_indicator;
if (!noQtTrayIcon && !tryAppIndicator) {
if (!noTryUnity) {
if (loadLibrary(lib_gtk, "gtk-3", 0)) {
void setupGtk() {
QLibrary lib_gtk, lib_indicator;
if (!noQtTrayIcon && !tryAppIndicator) {
if (!noTryUnity) {
if (loadLibrary(lib_gtk, "gtk-3", 0)) {
setupGtkBase(lib_gtk);
}
if (!useGtkBase) {
if (loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) {
setupGtkBase(lib_gtk);
}
if (!useGtkBase) {
if (loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) {
setupGtkBase(lib_gtk);
}
}
if (!useGtkBase) {
noTryUnity = true;
}
}
return;
if (!useGtkBase) {
noTryUnity = true;
}
}
return;
}
if (loadLibrary(lib_indicator, "appindicator3", 1)) {
if (loadLibrary(lib_gtk, "gtk-3", 0)) {
if (loadLibrary(lib_indicator, "appindicator3", 1)) {
if (loadLibrary(lib_gtk, "gtk-3", 0)) {
setupGtkBase(lib_gtk);
setupAppIndicator(lib_indicator);
}
}
if (!useGtkBase || !useAppIndicator) {
if (loadLibrary(lib_indicator, "appindicator", 1)) {
if (loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) {
useGtkBase = useAppIndicator = false;
setupGtkBase(lib_gtk);
setupAppIndicator(lib_indicator);
}
}
if (!useGtkBase || !useAppIndicator) {
if (loadLibrary(lib_indicator, "appindicator", 1)) {
if (loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) {
useGtkBase = useAppIndicator = false;
setupGtkBase(lib_gtk);
setupAppIndicator(lib_indicator);
}
}
}
if (tryAppIndicator) {
if (useGtkBase && useAppIndicator) {
noQtTrayIcon = true;
cSetSupportTray(false);
}
if (tryAppIndicator) {
if (useGtkBase && useAppIndicator) {
noQtTrayIcon = true;
cSetSupportTray(false);
}
return;
}
if (!useGtkBase && lib_gtk.isLoaded()) {
std::cout << "no appindicator, trying to load gtk..\n";
setupGtkBase(lib_gtk);
}
if (!useGtkBase) {
useAppIndicator = false;
_initLogs.push_back(QString("Init Error: Failed to load 'gtk-x11-2.0' library!"));
std::cout << "no appindicator :(\n";
return;
}
if (!loadFunction(lib_gtk, "gdk_init_check", ps_gdk_init_check)) return;
if (!loadFunction(lib_gtk, "gdk_pixbuf_new_from_data", ps_gdk_pixbuf_new_from_data)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_new_from_pixbuf", ps_gtk_status_icon_new_from_pixbuf)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_set_from_pixbuf", ps_gtk_status_icon_set_from_pixbuf)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_set_title", ps_gtk_status_icon_set_title)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_set_tooltip_text", ps_gtk_status_icon_set_tooltip_text)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_set_visible", ps_gtk_status_icon_set_visible)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_is_embedded", ps_gtk_status_icon_is_embedded)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_get_geometry", ps_gtk_status_icon_get_geometry)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_position_menu", ps_gtk_status_icon_position_menu)) return;
if (!loadFunction(lib_gtk, "gtk_menu_popup", ps_gtk_menu_popup)) return;
if (!loadFunction(lib_gtk, "gtk_get_current_event_time", ps_gtk_get_current_event_time)) return;
if (!loadFunction(lib_gtk, "g_idle_add", ps_g_idle_add)) return;
useStatusIcon = true;
std::cout << "status icon api loaded\n";
return;
}
void setupUnity() {
if (noTryUnity) return;
QLibrary lib_unity(qstr("unity"), 9, 0);
if (!loadLibrary(lib_unity, "unity", 9)) return;
if (!loadFunction(lib_unity, "unity_launcher_entry_get_for_desktop_id", ps_unity_launcher_entry_get_for_desktop_id)) return;
if (!loadFunction(lib_unity, "unity_launcher_entry_set_count", ps_unity_launcher_entry_set_count)) return;
if (!loadFunction(lib_unity, "unity_launcher_entry_set_count_visible", ps_unity_launcher_entry_set_count_visible)) return;
useUnityCount = true;
std::cout << "unity count api loaded\n";
if (!useGtkBase && lib_gtk.isLoaded()) {
LOG(("Could not load appindicator, trying to load gtk.."));
setupGtkBase(lib_gtk);
}
};
if (!useGtkBase) {
useAppIndicator = false;
LOG(("Could not load gtk-x11-2.0!"));
return;
}
if (!loadFunction(lib_gtk, "gdk_init_check", ps_gdk_init_check)) return;
if (!loadFunction(lib_gtk, "gdk_pixbuf_new_from_data", ps_gdk_pixbuf_new_from_data)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_new_from_pixbuf", ps_gtk_status_icon_new_from_pixbuf)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_set_from_pixbuf", ps_gtk_status_icon_set_from_pixbuf)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_set_title", ps_gtk_status_icon_set_title)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_set_tooltip_text", ps_gtk_status_icon_set_tooltip_text)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_set_visible", ps_gtk_status_icon_set_visible)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_is_embedded", ps_gtk_status_icon_is_embedded)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_get_geometry", ps_gtk_status_icon_get_geometry)) return;
if (!loadFunction(lib_gtk, "gtk_status_icon_position_menu", ps_gtk_status_icon_position_menu)) return;
if (!loadFunction(lib_gtk, "gtk_menu_popup", ps_gtk_menu_popup)) return;
if (!loadFunction(lib_gtk, "gtk_get_current_event_time", ps_gtk_get_current_event_time)) return;
if (!loadFunction(lib_gtk, "g_idle_add", ps_g_idle_add)) return;
useStatusIcon = true;
DEBUG_LOG(("Status icon api loaded!"));
}
void setupUnity() {
if (noTryUnity) return;
QLibrary lib_unity(qstr("unity"), 9, 0);
if (!loadLibrary(lib_unity, "unity", 9)) return;
if (!loadFunction(lib_unity, "unity_launcher_entry_get_for_desktop_id", ps_unity_launcher_entry_get_for_desktop_id)) return;
if (!loadFunction(lib_unity, "unity_launcher_entry_set_count", ps_unity_launcher_entry_set_count)) return;
if (!loadFunction(lib_unity, "unity_launcher_entry_set_count_visible", ps_unity_launcher_entry_set_count_visible)) return;
useUnityCount = true;
DEBUG_LOG(("Unity count api loaded!"));
}
class _PsEventFilter : public QAbstractNativeEventFilter {
public:
@@ -508,7 +488,7 @@ namespace {
}
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) {
Window *wnd = Application::wnd();
Window *wnd = App::wnd();
if (!wnd) return false;
return false;
@@ -520,7 +500,7 @@ namespace {
};
PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent),
posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/icon256.png")), iconbig256(icon256), wndIcon(QPixmap::fromImage(icon256, Qt::ColorOnly)), _psCheckStatusIconLeft(100), _psLastIndicatorUpdate(0) {
posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/icon256.png")), iconbig256(icon256), wndIcon(QIcon::fromTheme("telegram", QIcon(QPixmap::fromImage(icon256, Qt::ColorOnly)))), _psCheckStatusIconLeft(100), _psLastIndicatorUpdate(0) {
connect(&_psCheckStatusIconTimer, SIGNAL(timeout()), this, SLOT(psStatusIconCheck()));
_psCheckStatusIconTimer.setSingleShot(false);
@@ -667,7 +647,7 @@ void PsMainWindow::psUpdateCounter() {
} else if (trayIcon) {
int32 counter = App::histories().unreadFull - (cIncludeMuted() ? 0 : App::histories().unreadMuted);
bool muted = cIncludeMuted() ? (App::histories().unreadMuted >= counter) : false;
style::color bg = muted ? st::counterMuteBG : st::counterBG;
QIcon iconSmall;
iconSmall.addPixmap(QPixmap::fromImage(iconWithCounter(16, counter, bg, true), Qt::ColorOnly));
@@ -688,7 +668,7 @@ void PsMainWindow::psInitSize() {
bool maximized = false;
QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight);
if (pos.w && pos.h) {
QList<QScreen*> screens = App::app()->screens();
QList<QScreen*> screens = Application::screens();
for (QList<QScreen*>::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) {
QByteArray name = (*i)->name().toUtf8();
if (pos.moncrc == hashCrc32(name.constData(), name.size())) {
@@ -741,7 +721,7 @@ void PsMainWindow::psSavePosition(Qt::WindowState state) {
int px = curPos.x + curPos.w / 2, py = curPos.y + curPos.h / 2, d = 0;
QScreen *chosen = 0;
QList<QScreen*> screens = App::app()->screens();
QList<QScreen*> screens = Application::screens();
for (QList<QScreen*>::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) {
int dx = (*i)->geometry().x() + (*i)->geometry().width() / 2 - px; if (dx < 0) dx = -dx;
int dy = (*i)->geometry().y() + (*i)->geometry().height() / 2 - py; if (dy < 0) dy = -dy;
@@ -773,41 +753,31 @@ void PsMainWindow::psCreateTrayIcon() {
if (!noQtTrayIcon) {
cSetSupportTray(QSystemTrayIcon::isSystemTrayAvailable());
if (!noTryUnity) {
if (ps_gtk_init_check(0, 0)) {
DEBUG_LOG(("Checked gtk with gtk_init_check!"));
} else {
DEBUG_LOG(("Failed to gtk_init_check(0, 0)!"));
useUnityCount = false;
}
useUnityCount = false;
}
return;
}
if (useAppIndicator) {
DEBUG_LOG(("Trying to create AppIndicator"));
if (ps_gtk_init_check(0, 0)) {
DEBUG_LOG(("Checked gtk with gtk_init_check!"));
_trayMenu = ps_gtk_menu_new();
if (_trayMenu) {
DEBUG_LOG(("Created gtk menu for appindicator!"));
QFileInfo f(_trayIconImageFile());
if (f.exists()) {
QByteArray path = QFile::encodeName(f.absoluteFilePath());
_trayIndicator = ps_app_indicator_new("Telegram Desktop", path.constData(), APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
if (_trayIndicator) {
DEBUG_LOG(("Created appindicator!"));
} else {
DEBUG_LOG(("Failed to app_indicator_new()!"));
}
_trayMenu = ps_gtk_menu_new();
if (_trayMenu) {
DEBUG_LOG(("Created gtk menu for appindicator!"));
QFileInfo f(_trayIconImageFile());
if (f.exists()) {
QByteArray path = QFile::encodeName(f.absoluteFilePath());
_trayIndicator = ps_app_indicator_new("Telegram Desktop", path.constData(), APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
if (_trayIndicator) {
DEBUG_LOG(("Created appindicator!"));
} else {
useAppIndicator = false;
DEBUG_LOG(("Failed to create image file!"));
DEBUG_LOG(("Failed to app_indicator_new()!"));
}
} else {
DEBUG_LOG(("Failed to gtk_menu_new()!"));
useAppIndicator = false;
DEBUG_LOG(("Failed to create image file!"));
}
} else {
DEBUG_LOG(("Failed to gtk_init_check(0, 0)!"));
DEBUG_LOG(("Failed to gtk_menu_new()!"));
}
if (_trayMenu && _trayIndicator) {
ps_app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_ACTIVE);
@@ -819,7 +789,7 @@ void PsMainWindow::psCreateTrayIcon() {
}
}
if (useStatusIcon) {
if (ps_gtk_init_check(0, 0) && ps_gdk_init_check(0, 0)) {
if (ps_gdk_init_check(0, 0)) {
if (!_trayMenu) _trayMenu = ps_gtk_menu_new();
if (_trayMenu) {
loadPixbuf(_trayIconImageGen());
@@ -889,7 +859,7 @@ void PsMainWindow::psFirstShow() {
setWindowState(Qt::WindowMaximized);
}
if ((cFromAutoStart() && cStartMinimized()) || cStartInTray()) {
if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) {
setWindowState(Qt::WindowMinimized);
if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) {
hide();
@@ -972,20 +942,151 @@ void PsMainWindow::psNotifyShown(NotifyWindow *w) {
void PsMainWindow::psPlatformNotify(HistoryItem *item, int32 fwdCount) {
}
PsApplication::PsApplication(int &argc, char **argv) : QApplication(argc, argv) {
_PsInitializer _psInitializer;
Q_UNUSED(_psInitializer);
}
void PsApplication::psInstallEventFilter() {
QAbstractNativeEventFilter *psNativeEventFilter() {
delete _psEventFilter;
_psEventFilter = new _PsEventFilter();
installNativeEventFilter(_psEventFilter);
return _psEventFilter;
}
PsApplication::~PsApplication() {
delete _psEventFilter;
_psEventFilter = 0;
void psWriteDump() {
}
QString demanglestr(const QString &mangled) {
if (mangled.isEmpty()) return mangled;
QByteArray cmd = ("c++filt -n " + mangled).toUtf8();
FILE *f = popen(cmd.constData(), "r");
if (!f) return "BAD_SYMBOL_" + mangled;
QString result;
char buffer[4096] = { 0 };
while (!feof(f)) {
if (fgets(buffer, 4096, f) != NULL) {
result += buffer;
}
}
pclose(f);
return result.trimmed();
}
QStringList addr2linestr(uint64 *addresses, int count) {
QStringList result;
if (!count) return result;
result.reserve(count);
QString cmdstr = "addr2line -e " + escapeShell(cExeDir() + cExeName());
for (int i = 0; i < count; ++i) {
if (addresses[i]) {
cmdstr += qsl(" 0x%1").arg(addresses[i], 0, 16);
}
}
QByteArray cmd = cmdstr.toUtf8();
FILE *f = popen(cmd.constData(), "r");
QStringList addr2lineResult;
if (f) {
char buffer[4096] = {0};
while (!feof(f)) {
if (fgets(buffer, 4096, f) != NULL) {
addr2lineResult.push_back(QString::fromUtf8(buffer));
}
}
pclose(f);
}
for (int i = 0, j = 0; i < count; ++i) {
if (addresses[i]) {
if (j < addr2lineResult.size() && !addr2lineResult.at(j).isEmpty() && !addr2lineResult.at(j).startsWith(qstr("0x"))) {
QString res = addr2lineResult.at(j).trimmed();
if (int index = res.indexOf(qstr("/Telegram/"))) {
if (index > 0) {
res = res.mid(index + qstr("/Telegram/").size());
}
}
result.push_back(res);
} else {
result.push_back(QString());
}
++j;
} else {
result.push_back(QString());
}
}
return result;
}
QString psPrepareCrashDump(const QByteArray &crashdump, QString dumpfile) {
QString initial = QString::fromUtf8(crashdump), result;
QStringList lines = initial.split('\n');
result.reserve(initial.size());
int32 i = 0, l = lines.size();
while (i < l) {
uint64 addresses[1024] = { 0 };
for (; i < l; ++i) {
result.append(lines.at(i)).append('\n');
QString line = lines.at(i).trimmed();
if (line == qstr("Backtrace:")) {
++i;
break;
}
}
int32 start = i;
for (; i < l; ++i) {
QString line = lines.at(i).trimmed();
if (line.isEmpty()) break;
QRegularExpressionMatch m1 = QRegularExpression(qsl("^(.+)\\(([^+]+)\\+([^\\)]+)\\)\\[(.+)\\]$")).match(line);
QRegularExpressionMatch m2 = QRegularExpression(qsl("^(.+)\\[(.+)\\]$")).match(line);
QString addrstr = m1.hasMatch() ? m1.captured(4) : (m2.hasMatch() ? m2.captured(2) : QString());
if (!addrstr.isEmpty()) {
uint64 addr = addrstr.startsWith(qstr("0x")) ? addrstr.mid(2).toULongLong(0, 16) : addrstr.toULongLong();
if (addr > 1) {
addresses[i - start] = addr;
}
}
}
QStringList addr2line = addr2linestr(addresses, i - start);
for (i = start; i < l; ++i) {
QString line = lines.at(i).trimmed();
if (line.isEmpty()) break;
result.append(qsl("\n%1. ").arg(i - start));
if (line.startsWith(qstr("ERROR: "))) {
result.append(line).append('\n');
continue;
}
if (line == qstr("[0x1]")) {
result.append(qsl("(0x1 separator)\n"));
continue;
}
QRegularExpressionMatch m1 = QRegularExpression(qsl("^(.+)\\(([^+]*)\\+([^\\)]+)\\)(.+)$")).match(line);
QRegularExpressionMatch m2 = QRegularExpression(qsl("^(.+)\\[(.+)\\]$")).match(line);
if (!m1.hasMatch() && !m2.hasMatch()) {
result.append(qstr("BAD LINE: ")).append(line).append('\n');
continue;
}
if (m1.hasMatch()) {
result.append(demanglestr(m1.captured(2))).append(qsl(" + ")).append(m1.captured(3)).append(qsl(" [")).append(m1.captured(1)).append(qsl("] "));
if (!addr2line.at(i - start).isEmpty() && addr2line.at(i - start) != qsl("??:0")) {
result.append(qsl(" (")).append(addr2line.at(i - start)).append(qsl(")\n"));
} else {
result.append(m1.captured(4)).append(qsl(" (demangled)")).append('\n');
}
} else {
result.append('[').append(m2.captured(1)).append(']');
if (!addr2line.at(i - start).isEmpty() && addr2line.at(i - start) != qsl("??:0")) {
result.append(qsl(" (")).append(addr2line.at(i - start)).append(qsl(")\n"));
} else {
result.append(' ').append(m2.captured(2)).append('\n');
}
}
}
}
return result;
}
bool _removeDirectory(const QString &path) { // from http://stackoverflow.com/questions/2256945/removing-a-non-empty-directory-programmatically-in-c-or-c
@@ -1047,14 +1148,6 @@ bool psSkipDesktopNotify() {
return false;
}
QStringList psInitLogs() {
return _initLogs;
}
void psClearInitLogs() {
_initLogs = QStringList();
}
void psActivateProcess(uint64 pid) {
// objc_activateProgram();
}
@@ -1072,7 +1165,7 @@ QString psCurrentLanguage() {
namespace {
QString _psHomeDir() {
struct passwd *pw = getpwuid(getuid());
return (pw && pw->pw_dir && strlen(pw->pw_dir)) ? (QString::fromLocal8Bit(pw->pw_dir) + '/') : QString();
return (pw && pw->pw_dir && strlen(pw->pw_dir)) ? (fromUtf8Safe(pw->pw_dir) + '/') : QString();
}
}
@@ -1086,7 +1179,7 @@ QString psDownloadPath() {
}
QString psCurrentExeDirectory(int argc, char *argv[]) {
QString first = argc ? QString::fromLocal8Bit(argv[0]) : QString();
QString first = argc ? fromUtf8Safe(argv[0]) : QString();
if (!first.isEmpty()) {
QFileInfo info(first);
if (info.isSymLink()) {
@@ -1100,7 +1193,7 @@ QString psCurrentExeDirectory(int argc, char *argv[]) {
}
QString psCurrentExeName(int argc, char *argv[]) {
QString first = argc ? QString::fromLocal8Bit(argv[0]) : QString();
QString first = argc ? fromUtf8Safe(argv[0]) : QString();
if (!first.isEmpty()) {
QFileInfo info(first);
if (info.isSymLink()) {
@@ -1146,10 +1239,26 @@ void psShowInFolder(const QString &name) {
system((qsl("xdg-open ") + escapeShell(QFileInfo(name).absoluteDir().absolutePath())).toUtf8().constData());
}
void psStart() {
}
namespace PlatformSpecific {
Initializer::Initializer() {
QString cdesktop = QString(getenv("XDG_CURRENT_DESKTOP")).toLower();
noQtTrayIcon = (cdesktop == qstr("pantheon")) || (cdesktop == qstr("gnome"));
tryAppIndicator = (cdesktop == qstr("xfce"));
noTryUnity = (cdesktop != qstr("unity"));
if (noQtTrayIcon) cSetSupportTray(false);
DEBUG_LOG(("Loading libraries"));
setupGtk();
setupUnity();
}
Initializer::~Initializer() {
delete _psEventFilter;
_psEventFilter = 0;
}
void psFinish() {
}
namespace {
@@ -1172,13 +1281,15 @@ void psRegisterCustomScheme() {
DEBUG_LOG(("App Info: placing .desktop file"));
if (QDir(home + qsl(".local/")).exists()) {
QString apps = home + qsl(".local/share/applications/");
QString icons = home + qsl(".local/share/icons/");
if (!QDir(apps).exists()) QDir().mkpath(apps);
if (!QDir(icons).exists()) QDir().mkpath(icons);
QString path = cWorkingDir() + qsl("tdata/"), file = path + qsl("telegramdesktop.desktop");
QDir().mkpath(path);
QFile f(file);
if (f.open(QIODevice::WriteOnly)) {
QString icon = path + qsl("icon.png");
QString icon = icons + qsl("telegram.png");
if (!QFile(icon).exists()) {
if (QFile(qsl(":/gui/art/icon256.png")).copy(icon)) {
DEBUG_LOG(("App Info: Icon copied to 'tdata'"));
@@ -1194,7 +1305,7 @@ void psRegisterCustomScheme() {
s << "Name=Telegram Desktop\n";
s << "Comment=Official desktop version of Telegram messaging app\n";
s << "Exec=" << escapeShell(cExeDir() + cExeName()) << " -- %u\n";
s << "Icon=" << icon << "\n";
s << "Icon=telegram\n";
s << "Terminal=false\n";
s << "StartupWMClass=Telegram\n";
s << "Type=Application\n";
@@ -1258,22 +1369,22 @@ void psNewVersion() {
psRegisterCustomScheme();
}
bool _execUpdater(bool update = true) {
bool _execUpdater(bool update = true, const QString &crashreport = QString()) {
static const int MaxLen = 65536, MaxArgsCount = 128;
char path[MaxLen] = {0};
QByteArray data(QFile::encodeName(cExeDir() + "Updater"));
memcpy(path, data.constData(), data.size());
char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key", p_path[] = "-workpath", p_startintray[] = "-startintray", p_testmode[] = "-testmode";
char p_datafile[MaxLen] = {0}, p_pathbuf[MaxLen] = {0};
char *args[MaxArgsCount] = {0}, p_noupdate[] = "-noupdate", p_autostart[] = "-autostart", p_debug[] = "-debug", p_tosettings[] = "-tosettings", p_key[] = "-key", p_path[] = "-workpath", p_startintray[] = "-startintray", p_testmode[] = "-testmode", p_crashreport[] = "-crashreport";
char p_datafile[MaxLen] = {0}, p_pathbuf[MaxLen] = {0}, p_crashreportbuf[MaxLen] = {0};
int argIndex = 0;
args[argIndex++] = path;
if (!update) {
args[argIndex++] = p_noupdate;
args[argIndex++] = p_tosettings;
}
if (cFromAutoStart()) args[argIndex++] = p_autostart;
if (cLaunchMode() == LaunchModeAutoStart) args[argIndex++] = p_autostart;
if (cDebug()) args[argIndex++] = p_debug;
if (cStartInTray()) args[argIndex++] = p_startintray;
if (cTestMode()) args[argIndex++] = p_testmode;
@@ -1291,6 +1402,14 @@ bool _execUpdater(bool update = true) {
args[argIndex++] = p_path;
args[argIndex++] = p_pathbuf;
}
if (!crashreport.isEmpty()) {
QByteArray crashreportf = crashreport.toUtf8();
if (crashreportf.size() < MaxLen) {
memcpy(p_crashreportbuf, crashreportf.constData(), crashreportf.size());
args[argIndex++] = p_crashreport;
args[argIndex++] = p_crashreportbuf;
}
}
pid_t pid = fork();
switch (pid) {
@@ -1306,8 +1425,8 @@ void psExecUpdater() {
}
}
void psExecTelegram() {
_execUpdater(false);
void psExecTelegram(const QString &crashreport) {
_execUpdater(false, crashreport);
}
bool psShowOpenWithMenu(int x, int y, const QString &file) {
@@ -1368,18 +1487,3 @@ bool linuxMoveFile(const char *from, const char *to) {
return true;
}
#ifdef _NEED_LINUX_GENERATE_DUMP
void _sigsegvHandler(int sig) {
void *array[50] = {0};
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, 50);
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}
#endif

View File

@@ -1,17 +1,17 @@
/*
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-2015 John Preston, https://desktop.telegram.org
*/
@@ -113,27 +113,15 @@ private:
uint64 _psLastIndicatorUpdate;
};
#ifdef _NEED_LINUX_GENERATE_DUMP
void _sigsegvHandler(int sig);
#endif
class PsApplication : public QApplication {
Q_OBJECT
public:
PsApplication(int &argc, char **argv);
void psInstallEventFilter();
~PsApplication();
};
void psWriteDump();
QString psPrepareCrashDump(const QByteArray &crashdump, QString dumpfile);
void psDeleteDir(const QString &dir);
void psUserActionDone();
bool psIdleSupported();
uint64 psIdleTime();
bool psSkipAudioNotify();
bool psSkipDesktopNotify();
@@ -159,15 +147,15 @@ int psCleanup();
int psFixPrevious();
void psExecUpdater();
void psExecTelegram();
void psExecTelegram(const QString &arg = QString());
bool psShowOpenWithMenu(int x, int y, const QString &file);
void psPostprocessFile(const QString &name);
void psOpenFile(const QString &name, bool openWith = false);
void psShowInFolder(const QString &name);
void psStart();
void psFinish();
QAbstractNativeEventFilter *psNativeEventFilter();
void psNewVersion();

View File

@@ -1,17 +1,17 @@
/*
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-2015 John Preston, https://desktop.telegram.org
*/
@@ -26,6 +26,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "localstorage.h"
#include "passcodewidget.h"
#include <execinfo.h>
namespace {
QStringList _initLogs;
@@ -38,7 +40,7 @@ namespace {
}
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) {
Window *wnd = Application::wnd();
Window *wnd = AppClass::wnd();
if (!wnd) return false;
return wnd->psFilterNativeEvent(message);
@@ -83,7 +85,7 @@ void MacPrivate::notifyClicked(unsigned long long peer, int msgid) {
void MacPrivate::notifyReplied(unsigned long long peer, int msgid, const char *str) {
History *history = App::history(PeerId(peer));
App::main()->sendMessage(history, QString::fromUtf8(str), (msgid > 0 && !history->peer->isUser()) ? msgid : 0, false);
}
@@ -213,7 +215,7 @@ void PsMainWindow::psInitSize() {
bool maximized = false;
QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight);
if (pos.w && pos.h) {
QList<QScreen*> screens = App::app()->screens();
QList<QScreen*> screens = Application::screens();
for (QList<QScreen*>::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) {
QByteArray name = (*i)->name().toUtf8();
if (pos.moncrc == hashCrc32(name.constData(), name.size())) {
@@ -266,7 +268,7 @@ void PsMainWindow::psSavePosition(Qt::WindowState state) {
int px = curPos.x + curPos.w / 2, py = curPos.y + curPos.h / 2, d = 0;
QScreen *chosen = 0;
QList<QScreen*> screens = App::app()->screens();
QList<QScreen*> screens = Application::screens();
for (QList<QScreen*>::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) {
int dx = (*i)->geometry().x() + (*i)->geometry().width() / 2 - px; if (dx < 0) dx = -dx;
int dy = (*i)->geometry().y() + (*i)->geometry().height() / 2 - py; if (dy < 0) dy = -dy;
@@ -307,7 +309,7 @@ void PsMainWindow::psFirstShow() {
setWindowState(Qt::WindowMaximized);
}
if ((cFromAutoStart() && cStartMinimized()) || cStartInTray()) {
if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) {
setWindowState(Qt::WindowMinimized);
if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) {
hide();
@@ -422,13 +424,13 @@ void PsMainWindow::psMacUpdateMenu() {
canSelectAll = !edit->text().isEmpty();
canUndo = edit->isUndoAvailable();
canRedo = edit->isRedoAvailable();
canPaste = !App::app()->clipboard()->text().isEmpty();
canPaste = !Application::clipboard()->text().isEmpty();
} else if (FlatTextarea *edit = qobject_cast<FlatTextarea*>(focused)) {
canCut = canCopy = canDelete = edit->textCursor().hasSelection();
canSelectAll = !edit->getLastText().isEmpty();
canUndo = edit->isUndoAvailable();
canRedo = edit->isRedoAvailable();
canPaste = !App::app()->clipboard()->text().isEmpty();
canPaste = !Application::clipboard()->text().isEmpty();
} else if (HistoryInner *list = qobject_cast<HistoryInner*>(focused)) {
canCopy = list->canCopySelected();
canDelete = list->canDeleteSelected();
@@ -516,18 +518,215 @@ bool PsMainWindow::eventFilter(QObject *obj, QEvent *evt) {
return QMainWindow::eventFilter(obj, evt);
}
PsApplication::PsApplication(int &argc, char **argv) : QApplication(argc, argv) {
}
void PsApplication::psInstallEventFilter() {
QAbstractNativeEventFilter *psNativeEventFilter() {
delete _psEventFilter;
_psEventFilter = new _PsEventFilter();
installNativeEventFilter(_psEventFilter);
return _psEventFilter;
}
PsApplication::~PsApplication() {
delete _psEventFilter;
_psEventFilter = 0;
void psWriteDump() {
double v = objc_appkitVersion();
SignalHandlers::dump() << "OS-Version: " << v;
}
QString demanglestr(const QString &mangled) {
QByteArray cmd = ("c++filt -n " + mangled).toUtf8();
FILE *f = popen(cmd.constData(), "r");
if (!f) return "BAD_SYMBOL_" + mangled;
QString result;
char buffer[4096] = {0};
while (!feof(f)) {
if (fgets(buffer, 4096, f) != NULL) {
result += buffer;
}
}
pclose(f);
return result.trimmed();
}
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;
}
QStringList atosstr(uint64 *addresses, int count, uint64 base) {
QStringList result;
if (!count) return result;
result.reserve(count);
QString cmdstr = "atos -o " + escapeShell(cExeDir() + cExeName()) + qsl("/Contents/MacOS/Telegram -l 0x%1").arg(base, 0, 16);
for (int i = 0; i < count; ++i) {
if (addresses[i]) {
cmdstr += qsl(" 0x%1").arg(addresses[i], 0, 16);
}
}
QByteArray cmd = cmdstr.toUtf8();
FILE *f = popen(cmd.constData(), "r");
QStringList atosResult;
if (f) {
char buffer[4096] = {0};
while (!feof(f)) {
if (fgets(buffer, 4096, f) != NULL) {
atosResult.push_back(QString::fromUtf8(buffer));
}
}
pclose(f);
}
for (int i = 0, j = 0; i < count; ++i) {
if (addresses[i]) {
if (j < atosResult.size() && !atosResult.at(j).isEmpty() && !atosResult.at(j).startsWith(qstr("0x"))) {
result.push_back(atosResult.at(j).trimmed());
} else {
result.push_back(QString());
}
++j;
} else {
result.push_back(QString());
}
}
return result;
}
QString psPrepareCrashDump(const QByteArray &crashdump, QString dumpfile) {
QString initial = QString::fromUtf8(crashdump), result;
QStringList lines = initial.split('\n');
result.reserve(initial.size());
int32 i = 0, l = lines.size();
while (i < l) {
uint64 addresses[1024] = { 0 };
for (; i < l; ++i) {
result.append(lines.at(i)).append('\n');
QString line = lines.at(i).trimmed();
if (line == qstr("Base image addresses:")) {
++i;
break;
}
}
uint64 base = 0;
for (int32 start = i; i < l; ++i) {
QString line = lines.at(i).trimmed();
if (line.isEmpty()) break;
if (!base) {
QRegularExpressionMatch m = QRegularExpression(qsl("^\\d+ (\\d+) \\((.+)\\)")).match(line);
if (m.hasMatch()) {
if (uint64 address = m.captured(1).toULongLong()) {
if (m.captured(2).endsWith(qstr("Contents/MacOS/Telegram"))) {
base = address;
}
}
}
}
}
if (base) {
result.append(qsl("(base address read: 0x%1)\n").arg(base, 0, 16));
} else {
result.append(qsl("ERROR: base address not read!\n"));
}
for (; i < l; ++i) {
result.append(lines.at(i)).append('\n');
QString line = lines.at(i).trimmed();
if (line == qstr("Backtrace:")) {
++i;
break;
}
}
int32 start = i;
for (; i < l; ++i) {
QString line = lines.at(i).trimmed();
if (line.isEmpty()) break;
if (QRegularExpression(qsl("^\\d+")).match(line).hasMatch()) {
QStringList lst = line.split(' ', QString::SkipEmptyParts);
if (lst.size() > 2) {
uint64 addr = lst.at(2).startsWith(qstr("0x")) ? lst.at(2).mid(2).toULongLong(0, 16) : lst.at(2).toULongLong();
addresses[i - start] = addr;
}
}
}
QStringList atos = atosstr(addresses, i - start, base);
for (i = start; i < l; ++i) {
QString line = lines.at(i).trimmed();
if (line.isEmpty()) break;
if (!QRegularExpression(qsl("^\\d+")).match(line).hasMatch()) {
if (!lines.at(i).startsWith(qstr("ERROR: "))) {
result.append(qstr("BAD LINE: "));
}
result.append(line).append('\n');
continue;
}
QStringList lst = line.split(' ', QString::SkipEmptyParts);
result.append('\n').append(lst.at(0)).append(qsl(". "));
if (lst.size() < 3) {
result.append(qstr("BAD LINE: ")).append(line).append('\n');
continue;
}
if (lst.size() > 5 && lst.at(3) == qsl("0x0") && lst.at(4) == qsl("+") && lst.at(5) == qsl("1")) {
result.append(qsl("(0x1 separator)\n"));
continue;
}
if (i - start < atos.size()) {
if (!atos.at(i - start).isEmpty()) {
result.append(atos.at(i - start)).append('\n');
continue;
}
}
for (int j = 1, s = lst.size();;) {
if (lst.at(j).startsWith('_')) {
result.append(demanglestr(lst.at(j)));
if (++j < s) {
result.append(' ');
for (;;) {
result.append(lst.at(j));
if (++j < s) {
result.append(' ');
} else {
break;
}
}
}
break;
} else if (j > 2) {
result.append(lst.at(j));
}
if (++j < s) {
result.append(' ');
} else {
break;
}
}
result.append(qsl(" [demangled]")).append('\n');
}
}
return result;
}
void psDeleteDir(const QString &dir) {
@@ -592,7 +791,7 @@ QString psDownloadPath() {
}
QString psCurrentExeDirectory(int argc, char *argv[]) {
QString first = argc ? QString::fromLocal8Bit(argv[0]) : QString();
QString first = argc ? fromUtf8Safe(argv[0]) : QString();
if (!first.isEmpty()) {
QFileInfo info(first);
if (info.exists()) {
@@ -603,7 +802,7 @@ QString psCurrentExeDirectory(int argc, char *argv[]) {
}
QString psCurrentExeName(int argc, char *argv[]) {
QString first = argc ? QString::fromLocal8Bit(argv[0]) : QString();
QString first = argc ? fromUtf8Safe(argv[0]) : QString();
if (!first.isEmpty()) {
QFileInfo info(first);
if (info.exists()) {
@@ -649,12 +848,19 @@ void psShowInFolder(const QString &name) {
objc_showInFinder(name, QFileInfo(name).absolutePath());
}
void psStart() {
objc_start();
}
namespace PlatformSpecific {
Initializer::Initializer() {
objc_start();
}
Initializer::~Initializer() {
delete _psEventFilter;
_psEventFilter = 0;
objc_finish();
}
void psFinish() {
objc_finish();
}
void psNewVersion() {
@@ -667,8 +873,8 @@ void psExecUpdater() {
}
}
void psExecTelegram() {
objc_execTelegram();
void psExecTelegram(const QString &crashreport) {
objc_execTelegram(crashreport);
}
void psAutoStart(bool start, bool silent) {

View File

@@ -1,17 +1,17 @@
/*
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-2015 John Preston, https://desktop.telegram.org
*/
@@ -31,7 +31,7 @@ inline void psCheckLocalSocket(const QString &serverName) {
class MacPrivate : public PsMacWindowPrivate {
public:
void activeSpaceChanged();
void darkModeChanged();
void notifyClicked(unsigned long long peer, int msgid);
@@ -140,17 +140,8 @@ private:
};
class PsApplication : public QApplication {
Q_OBJECT
public:
PsApplication(int &argc, char **argv);
void psInstallEventFilter();
~PsApplication();
};
void psWriteDump();
QString psPrepareCrashDump(const QByteArray &crashdump, QString dumpfile);
void psDeleteDir(const QString &dir);
@@ -183,15 +174,15 @@ int psCleanup();
int psFixPrevious();
void psExecUpdater();
void psExecTelegram();
void psExecTelegram(const QString &crashreport = QString());
bool psShowOpenWithMenu(int x, int y, const QString &file);
void psPostprocessFile(const QString &name);
void psOpenFile(const QString &name, bool openWith = false);
void psShowInFolder(const QString &name);
void psStart();
void psFinish();
QAbstractNativeEventFilter *psNativeEventFilter();
void psNewVersion();

View File

@@ -69,7 +69,7 @@ void objc_openFile(const QString &file, bool openwith);
void objc_start();
void objc_finish();
bool objc_execUpdater();
void objc_execTelegram();
void objc_execTelegram(const QString &crashreport);
void objc_registerCustomScheme();
@@ -77,6 +77,8 @@ void objc_activateProgram(WId winId);
bool objc_moveFile(const QString &from, const QString &to);
void objc_deleteDir(const QString &dir);
double objc_appkitVersion();
QString objc_appDataPath();
QString objc_downloadPath();
QString objc_currentCountry();

View File

@@ -1,17 +1,17 @@
/*
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-2015 John Preston, https://desktop.telegram.org
*/
@@ -142,7 +142,7 @@ QString objcString(NSString *str) {
class PsMacWindowData {
public:
PsMacWindowData(PsMacWindowPrivate *wnd) :
wnd(wnd),
observerHelper([[ObserverHelper alloc] init:wnd]),
@@ -156,7 +156,7 @@ public:
int msgId = msgObj ? [msgObj intValue] : 0;
wnd->notifyClicked(peerLong, msgId);
}
void onNotifyReply(NSUserNotification *notification) {
NSDictionary *dict = [notification userInfo];
NSNumber *peerObj = [dict objectForKey:@"peer"], *msgObj = [dict objectForKey:@"msgid"];
@@ -164,12 +164,12 @@ public:
int msgId = msgObj ? [msgObj intValue] : 0;
wnd->notifyReplied(peerLong, msgId, [[[notification response] string] UTF8String]);
}
~PsMacWindowData() {
[observerHelper release];
[notifyHandler release];
}
PsMacWindowPrivate *wnd;
ObserverHelper *observerHelper;
NotifyHandler *notifyHandler;
@@ -208,10 +208,10 @@ public:
}
- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification {
NSNumber *instObj = [[notification userInfo] objectForKey:@"inst"];
NSNumber *instObj = [[notification userInfo] objectForKey:@"launch"];
unsigned long long instLong = instObj ? [instObj unsignedLongLongValue] : 0;
DEBUG_LOG(("Received notification with instance %1").arg(instLong));
if (instLong != cInstance()) { // other app instance notification
if (instLong != Sandbox::LaunchId()) { // other app instance notification
return;
}
if (notification.activationType == NSUserNotificationActivationTypeReplied) {
@@ -283,8 +283,8 @@ void PsMacWindowPrivate::showNotify(uint64 peer, int32 msgId, const QPixmap &pix
NSUserNotification *notification = [[NSUserNotification alloc] init];
NSImage *img = qt_mac_create_nsimage(pix);
DEBUG_LOG(("Sending notification with userinfo: peer %1, msgId %2 and instance %3").arg(peer).arg(msgId).arg(cInstance()));
[notification setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedLongLong:peer],@"peer",[NSNumber numberWithInt:msgId],@"msgid",[NSNumber numberWithUnsignedLongLong:cInstance()],@"inst",nil]];
DEBUG_LOG(("Sending notification with userinfo: peer %1, msgId %2 and instance %3").arg(peer).arg(msgId).arg(Sandbox::LaunchId()));
[notification setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedLongLong:peer],@"peer",[NSNumber numberWithInt:msgId],@"msgid",[NSNumber numberWithUnsignedLongLong:Sandbox::LaunchId()],@"launch",nil]];
[notification setTitle:QNSString(title).s()];
[notification setSubtitle:QNSString(subtitle).s()];
@@ -352,7 +352,7 @@ void PsMacWindowPrivate::clearNotifies(unsigned long long peer) {
NSArray *notifies = [center deliveredNotifications];
for (id notify in notifies) {
NSDictionary *dict = [notify userInfo];
if ([[dict objectForKey:@"peer"] unsignedLongLongValue] == peer && [[dict objectForKey:@"inst"] unsignedLongLongValue] == cInstance()) {
if ([[dict objectForKey:@"peer"] unsignedLongLongValue] == peer && [[dict objectForKey:@"launch"] unsignedLongLongValue] == Sandbox::LaunchId()) {
[center removeDeliveredNotification:notify];
}
}
@@ -384,9 +384,9 @@ bool objc_idleTime(int64 &idleTime) { // taken from https://github.com/trueinter
mach_port_t masterPort;
io_iterator_t iter;
io_registry_entry_t curObj;
IOMasterPort(MACH_PORT_NULL, &masterPort);
/* Get IOHIDSystem */
IOServiceGetMatchingServices(masterPort, IOServiceMatching("IOHIDSystem"), &iter);
if (iter == 0) {
@@ -400,11 +400,11 @@ bool objc_idleTime(int64 &idleTime) { // taken from https://github.com/trueinter
} else {
return false;
}
uint64 err = ~0L, result = err;
if (obj) {
CFTypeID type = CFGetTypeID(obj);
if (type == CFDataGetTypeID()) {
CFDataGetBytes((CFDataRef) obj, CFRangeMake(0, sizeof(result)), (UInt8*)&result);
} else if (type == CFNumberGetTypeID()) {
@@ -412,16 +412,16 @@ bool objc_idleTime(int64 &idleTime) { // taken from https://github.com/trueinter
} else {
// error
}
CFRelease(obj);
if (result != err) {
result /= 1000000; // return as ms
}
} else {
// error
}
CFRelease((CFTypeRef)properties);
IOObjectRelease(curObj);
IOObjectRelease(iter);
@@ -761,7 +761,7 @@ void objc_showInFinder(const QString &file, const QString &path) {
}
}
}
return NO;
}
@@ -790,20 +790,20 @@ void objc_openFile(const QString &f, bool openwith) {
NSArray *names =[url pathComponents];
NSString *name = [names count] ? [names lastObject] : @"";
NSArray *apps = (NSArray*)LSCopyApplicationURLsForURL(CFURLRef(url), kLSRolesAll);
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
NSRect fullRect = { { 0., 0. }, { st::macAccessory.width() * 1., st::macAccessory.height() * 1. } };
NSView *accessory = [[NSView alloc] initWithFrame:fullRect];
[accessory setAutoresizesSubviews:YES];
NSPopUpButton *selector = [[NSPopUpButton alloc] init];
[accessory addSubview:selector];
[selector addItemWithTitle:objc_lang(lng_mac_recommended_apps).s()];
[selector addItemWithTitle:objc_lang(lng_mac_all_apps).s()];
[selector sizeToFit];
NSTextField *enableLabel = [[NSTextField alloc] init];
[accessory addSubview:enableLabel];
[enableLabel setStringValue:objc_lang(lng_mac_enable_filter).s()];
@@ -848,7 +848,7 @@ void objc_openFile(const QString &f, bool openwith) {
goodFrame.origin.x = (fullRect.size.width - goodFrame.size.width) / 2.;
goodFrame.origin.y = alwaysRect.origin.y - goodFrame.size.height - st::macAppHintTop;
[goodLabel setFrame:goodFrame];
NSTextField *badLabel = [[NSTextField alloc] init];
[badLabel setStringValue:QNSString(lng_mac_not_known_app(lt_file, objcString(name))).s()];
[badLabel setFont:[goodLabel font]];
@@ -861,7 +861,7 @@ void objc_openFile(const QString &f, bool openwith) {
NSImage *badImage = [NSImage imageNamed:NSImageNameCaution];
[badIcon setImage:badImage];
[badIcon setFrame:NSMakeRect(0, 0, st::macCautionIconSize.width(), st::macCautionIconSize.height())];
NSRect badFrame = [badLabel frame], badIconFrame = [badIcon frame];
badFrame.origin.x = (fullRect.size.width - badFrame.size.width + badIconFrame.size.width) / 2.;
badIconFrame.origin.x = (fullRect.size.width - badFrame.size.width - badIconFrame.size.width) / 2.;
@@ -874,14 +874,14 @@ void objc_openFile(const QString &f, bool openwith) {
ChooseApplicationDelegate *delegate = [[ChooseApplicationDelegate alloc] init:apps withPanel:openPanel withSelector:selector withGood:goodLabel withBad:badLabel withIcon:badIcon withAccessory:accessory];
[openPanel setDelegate:delegate];
[openPanel setCanChooseDirectories:NO];
[openPanel setCanChooseFiles:YES];
[openPanel setAllowsMultipleSelection:NO];
[openPanel setResolvesAliases:YES];
[openPanel setTitle:objc_lang(lng_mac_choose_app).s()];
[openPanel setMessage:QNSString(lng_mac_choose_text(lt_file, objcString(name))).s()];
NSArray *appsPaths = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationDirectory inDomains:NSLocalDomainMask];
if ([appsPaths count]) [openPanel setDirectoryURL:[appsPaths firstObject]];
[openPanel beginWithCompletionHandler:^(NSInteger result){
@@ -950,7 +950,7 @@ void objc_registerCustomScheme() {
#endif
}
BOOL _execUpdater(BOOL update = YES) {
BOOL _execUpdater(BOOL update = YES, const QString &crashreport = QString()) {
NSString *path = @"", *args = @"";
@try {
path = [[NSBundle mainBundle] bundlePath];
@@ -964,7 +964,7 @@ BOOL _execUpdater(BOOL update = YES) {
[args addObject:[NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]]];
if (cRestartingToSettings()) [args addObject:@"-tosettings"];
if (!update) [args addObject:@"-noupdate"];
if (cFromAutoStart()) [args addObject:@"-autostart"];
if (cLaunchMode() == LaunchModeAutoStart) [args addObject:@"-autostart"];
if (cDebug()) [args addObject:@"-debug"];
if (cStartInTray()) [args addObject:@"-startintray"];
if (cTestMode()) [args addObject:@"-testmode"];
@@ -972,6 +972,10 @@ BOOL _execUpdater(BOOL update = YES) {
[args addObject:@"-key"];
[args addObject:QNSString(cDataFile()).s()];
}
if (!crashreport.isEmpty()) {
[args addObject:@"-crashreport"];
[args addObject:QNSString(crashreport).s()];
}
DEBUG_LOG(("Application Info: executing %1 %2").arg(objcString(path)).arg(objcString([args componentsJoinedByString:@" "])));
if (![NSTask launchedTaskWithLaunchPath:path arguments:args]) {
@@ -992,8 +996,8 @@ bool objc_execUpdater() {
return !!_execUpdater();
}
void objc_execTelegram() {
_execUpdater(NO);
void objc_execTelegram(const QString &crashreport) {
_execUpdater(NO, crashreport);
}
void objc_activateProgram(WId winId) {
@@ -1027,6 +1031,10 @@ void objc_deleteDir(const QString &dir) {
[[NSFileManager defaultManager] removeItemAtPath:QNSString(dir).s() error:nil];
}
double objc_appkitVersion() {
return NSAppKitVersionNumber;
}
QString objc_appDataPath() {
NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
if (url) {

File diff suppressed because it is too large Load Diff

View File

@@ -113,21 +113,9 @@ private:
void psDestroyIcons();
};
#ifdef _NEED_WIN_GENERATE_DUMP
extern LPTOP_LEVEL_EXCEPTION_FILTER _oldWndExceptionFilter;
LONG CALLBACK _exceptionFilter(EXCEPTION_POINTERS* pExceptionPointers);
#endif
class PsApplication : public QApplication {
Q_OBJECT
public:
PsApplication(int &argc, char **argv);
void psInstallEventFilter();
~PsApplication();
};
void psWriteDump();
void psWriteStackTrace();
QString psPrepareCrashDump(const QByteArray &crashdump, QString dumpfile);
void psDeleteDir(const QString &dir);
@@ -161,15 +149,15 @@ int psCleanup();
int psFixPrevious();
void psExecUpdater();
void psExecTelegram();
void psExecTelegram(const QString &arg = QString());
bool psShowOpenWithMenu(int x, int y, const QString &file);
void psPostprocessFile(const QString &name);
void psOpenFile(const QString &name, bool openWith = false);
void psShowInFolder(const QString &name);
void psStart();
void psFinish();
QAbstractNativeEventFilter *psNativeEventFilter();
void psNewVersion();

View File

@@ -26,6 +26,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
bool gRtl = false;
Qt::LayoutDirection gLangDir = gRtl ? Qt::RightToLeft : Qt::LeftToRight;
QString gArguments;
mtpDcOptions gDcOptions;
bool gDevVersion = DevVersion;
@@ -57,7 +59,7 @@ bool gAutoStart = false;
bool gSendToMenu = false;
bool gAutoUpdate = true;
TWindowPos gWindowPos;
bool gFromAutoStart = false;
LaunchMode gLaunchMode = LaunchModeNormal;
bool gSupportTray = true;
DBIWorkMode gWorkMode = dbiwmWindowAndTray;
DBIConnectionType gConnectionType = dbictAuto;
@@ -134,23 +136,20 @@ bool gRetina = false;
float64 gRetinaFactor = 1.;
int32 gIntRetinaFactor = 1;
bool gCustomNotifies = true;
uint64 gInstance = 0.;
#ifdef Q_OS_WIN
DBIPlatform gPlatform = dbipWindows;
QUrl gUpdateURL = QUrl(qsl("http://tdesktop.com/win/tupdates/current"));
#elif defined Q_OS_MAC
DBIPlatform gPlatform = dbipMac;
QUrl gUpdateURL = QUrl(qsl("http://tdesktop.com/mac/tupdates/current"));
#elif defined Q_OS_LINUX32
DBIPlatform gPlatform = dbipLinux32;
QUrl gUpdateURL = QUrl(qsl("http://tdesktop.com/linux32/tupdates/current"));
#elif defined Q_OS_LINUX64
DBIPlatform gPlatform = dbipLinux64;
QUrl gUpdateURL = QUrl(qsl("http://tdesktop.com/linux/tupdates/current"));
#elif defined Q_OS_LINUX32
DBIPlatform gPlatform = dbipLinux32;
#else
#error Unknown platform
#endif
QString gPlatformString;
QUrl gUpdateURL;
bool gIsElCapitan = false;
bool gContactsReceived = false;
@@ -182,17 +181,49 @@ bool gAutoPlayGif = true;
void settingsParseArgs(int argc, char *argv[]) {
#ifdef Q_OS_MAC
gIsElCapitan = (QSysInfo::macVersion() >= QSysInfo::MV_10_11);
if (QSysInfo::macVersion() < QSysInfo::MV_10_8) {
gUpdateURL = QUrl(qsl("http://tdesktop.com/mac32/tupdates/current"));
if (QSysInfo::macVersion() >= QSysInfo::MV_10_11) {
gIsElCapitan = true;
} else if (QSysInfo::macVersion() < QSysInfo::MV_10_8) {
gPlatform = dbipMacOld;
} else {
gCustomNotifies = false;
}
#endif
memset_rand(&gInstance, sizeof(gInstance));
switch (cPlatform()) {
case dbipWindows:
gUpdateURL = QUrl(qsl("http://tdesktop.com/win/tupdates/current"));
gPlatformString = qsl("Windows");
break;
case dbipMac:
gUpdateURL = QUrl(qsl("http://tdesktop.com/mac/tupdates/current"));
gPlatformString = qsl("MacOS");
gCustomNotifies = false;
break;
case dbipMacOld:
gUpdateURL = QUrl(qsl("http://tdesktop.com/mac32/tupdates/current"));
gPlatformString = qsl("MacOSold");
break;
case dbipLinux64:
gUpdateURL = QUrl(qsl("http://tdesktop.com/linux/tupdates/current"));
gPlatformString = qsl("Linux64bit");
break;
case dbipLinux32:
gUpdateURL = QUrl(qsl("http://tdesktop.com/linux32/tupdates/current"));
gPlatformString = qsl("Linux32bit");
break;
}
QStringList args;
for (int32 i = 0; i < argc; ++i) {
args.push_back('"' + fromUtf8Safe(argv[i]) + '"');
}
gArguments = args.join(' ');
gExeDir = psCurrentExeDirectory(argc, argv);
gExeName = psCurrentExeName(argc, argv);
if (argc == 2 && fromUtf8Safe(argv[1]).endsWith(qstr(".telegramcrash")) && QFile(fromUtf8Safe(argv[1])).exists()) {
gLaunchMode = LaunchModeShowCrash;
gStartUrl = fromUtf8Safe(argv[1]);
}
for (int32 i = 0; i < argc; ++i) {
if (string("-testmode") == argv[i]) {
gTestMode = true;
@@ -201,9 +232,16 @@ void settingsParseArgs(int argc, char *argv[]) {
} else if (string("-many") == argv[i]) {
gManyInstance = true;
} else if (string("-key") == argv[i] && i + 1 < argc) {
gKeyFile = QString::fromLocal8Bit(argv[++i]);
gKeyFile = fromUtf8Safe(argv[++i]);
} else if (string("-autostart") == argv[i]) {
gFromAutoStart = true;
gLaunchMode = LaunchModeAutoStart;
} else if (string("-fixprevious") == argv[i]) {
gLaunchMode = LaunchModeFixPrevious;
} else if (string("-cleanup") == argv[i]) {
gLaunchMode = LaunchModeCleanup;
} else if (string("-crash") == argv[i] && i + 1 < argc) {
gLaunchMode = LaunchModeShowCrash;
gStartUrl = fromUtf8Safe(argv[++i]);
} else if (string("-noupdate") == argv[i]) {
gNoStartUpdate = true;
} else if (string("-tosettings") == argv[i]) {
@@ -212,15 +250,15 @@ void settingsParseArgs(int argc, char *argv[]) {
gStartInTray = true;
} else if (string("-sendpath") == argv[i] && i + 1 < argc) {
for (++i; i < argc; ++i) {
gSendPaths.push_back(QString::fromLocal8Bit(argv[i]));
gSendPaths.push_back(fromUtf8Safe(argv[i]));
}
} else if (string("-workdir") == argv[i] && i + 1 < argc) {
QString dir = QString::fromLocal8Bit(argv[++i]);
QString dir = fromUtf8Safe(argv[++i]);
if (QDir().exists(dir)) {
gWorkingDir = dir;
}
} else if (string("--") == argv[i] && i + 1 < argc) {
gStartUrl = QString::fromLocal8Bit(argv[++i]);
gStartUrl = fromUtf8Safe(argv[++i]);
}
}
}

View File

@@ -24,10 +24,8 @@ extern bool gDebug;
inline bool cDebug() {
#if defined _DEBUG
return true;
#elif defined _WITH_DEBUG
return gDebug;
#else
return false;
return gDebug;
#endif
}
inline void cSetDebug(bool debug) {
@@ -55,6 +53,8 @@ inline bool rtl() {
return cRtl();
}
DeclareReadSetting(QString, Arguments);
struct mtpDcOption {
mtpDcOption(int id, int flags, const string &ip, int port) : id(id), flags(flags), ip(ip), port(port) {
}
@@ -82,7 +82,14 @@ DeclareSetting(bool, AutoStart);
DeclareSetting(bool, StartMinimized);
DeclareSetting(bool, StartInTray);
DeclareSetting(bool, SendToMenu);
DeclareReadSetting(bool, FromAutoStart);
enum LaunchMode {
LaunchModeNormal = 0,
LaunchModeAutoStart,
LaunchModeFixPrevious,
LaunchModeCleanup,
LaunchModeShowCrash,
};
DeclareReadSetting(LaunchMode, LaunchMode);
DeclareSetting(QString, WorkingDir);
inline void cForceWorkingDir(const QString &newDir) {
cSetWorkingDir(newDir);
@@ -209,6 +216,8 @@ DeclareRefSetting(RecentStickerPack, RecentStickers);
RecentStickerPack &cGetRecentStickers();
typedef QMap<EmojiPtr, StickerPack> StickersByEmojiMap;
static const uint64 DefaultStickerSetId = 0; // for backward compatibility
static const uint64 CustomStickerSetId = 0xFFFFFFFFFFFFFFFFULL, RecentStickerSetId = 0xFFFFFFFFFFFFFFFEULL;
static const uint64 NoneStickerSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel
@@ -219,6 +228,7 @@ struct StickerSet {
QString title, shortName;
int32 count, hash, flags;
StickerPack stickers;
StickersByEmojiMap emoji;
};
typedef QMap<uint64, StickerSet> StickerSets;
DeclareRefSetting(StickerSets, StickerSets);
@@ -306,9 +316,8 @@ DeclareSetting(float64, RetinaFactor);
DeclareSetting(int32, IntRetinaFactor);
DeclareSetting(bool, CustomNotifies);
DeclareReadSetting(uint64, Instance);
DeclareReadSetting(DBIPlatform, Platform);
DeclareReadSetting(QString, PlatformString);
DeclareReadSetting(bool, IsElCapitan);
DeclareReadSetting(QUrl, UpdateURL);

View File

@@ -221,7 +221,7 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent)
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData *)), this, SLOT(peerUpdated(PeerData *)));
connect(App::main(), SIGNAL(peerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)), this, SLOT(peerUpdated(PeerData *)));
connect(App::app(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(onReloadPassword(Qt::ApplicationState)));
Sandboxer::connect(SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(onReloadPassword(Qt::ApplicationState)));
}
// profile
@@ -269,11 +269,11 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent)
_newVersionWidth = st::linkFont->width(_newVersionText);
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
connect(App::app(), SIGNAL(updateChecking()), this, SLOT(onUpdateChecking()));
connect(App::app(), SIGNAL(updateLatest()), this, SLOT(onUpdateLatest()));
connect(App::app(), SIGNAL(updateDownloading(qint64,qint64)), this, SLOT(onUpdateDownloading(qint64,qint64)));
connect(App::app(), SIGNAL(updateReady()), this, SLOT(onUpdateReady()));
connect(App::app(), SIGNAL(updateFailed()), this, SLOT(onUpdateFailed()));
Sandboxer::connect(SIGNAL(updateChecking()), this, SLOT(onUpdateChecking()));
Sandboxer::connect(SIGNAL(updateLatest()), this, SLOT(onUpdateLatest()));
Sandboxer::connect(SIGNAL(updateProgress(qint64,qint64)), this, SLOT(onUpdateDownloading(qint64,qint64)));
Sandboxer::connect(SIGNAL(updateFailed()), this, SLOT(onUpdateFailed()));
Sandboxer::connect(SIGNAL(updateReady()), this, SLOT(onUpdateReady()));
#endif
// chat options
@@ -329,18 +329,16 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent)
updateOnlineDisplay();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
switch (App::app()->updatingState()) {
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
switch (Sandboxer::updatingState()) {
case Application::UpdatingDownload:
setUpdatingState(UpdatingDownload, true);
setDownloadProgress(App::app()->updatingReady(), App::app()->updatingSize());
setDownloadProgress(Sandboxer::updatingReady(), Sandboxer::updatingSize());
break;
case Application::UpdatingReady: setUpdatingState(UpdatingReady, true); break;
default: setUpdatingState(UpdatingNone, true); break;
}
#else
_updatingState = UpdatingNone;
#endif
#endif
updateConnectionType();
@@ -805,7 +803,9 @@ void SettingsInner::keyPressEvent(QKeyEvent *e) {
break;
} else if (str == qstr("loadlang")) {
chooseCustomLang();
} else if (qsl("debugmode").startsWith(str) || qsl("testmode").startsWith(str) || qsl("loadlang").startsWith(str)) {
} else if (str == qstr("crashplease")) {
t_assert(!"Crashed in Settings!");
} else if (qsl("debugmode").startsWith(str) || qsl("testmode").startsWith(str) || qsl("loadlang").startsWith(str) || qsl("crashplease").startsWith(str)) {
break;
}
++from;
@@ -1261,14 +1261,14 @@ void SettingsInner::onAutoUpdate() {
Local::writeSettings();
resizeEvent(0);
if (cAutoUpdate()) {
App::app()->startUpdateCheck();
Sandboxer::startUpdateCheck();
if (_updatingState == UpdatingNone) {
_checkNow.show();
} else if (_updatingState == UpdatingReady) {
_restartNow.show();
}
} else {
App::app()->stopUpdate();
Sandboxer::stopUpdate();
_restartNow.hide();
_checkNow.hide();
}
@@ -1279,12 +1279,12 @@ void SettingsInner::onCheckNow() {
if (!cAutoUpdate()) return;
cSetLastUpdateCheck(0);
App::app()->startUpdateCheck();
Sandboxer::startUpdateCheck();
}
#endif
void SettingsInner::onRestartNow() {
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
checkReadyUpdate();
if (_updatingState == UpdatingReady) {
cSetRestartingUpdate(true);
@@ -1292,10 +1292,10 @@ void SettingsInner::onRestartNow() {
cSetRestarting(true);
cSetRestartingToSettings(true);
}
#else
#else
cSetRestarting(true);
cSetRestartingToSettings(true);
#endif
#endif
App::quit();
}
@@ -1785,7 +1785,7 @@ SettingsWidget::SettingsWidget(Window *parent) : TWidget(parent)
connect(App::wnd(), SIGNAL(resized(const QSize&)), this, SLOT(onParentResize(const QSize&)));
connect(&_close, SIGNAL(clicked()), App::wnd(), SLOT(showSettings()));
setGeometry(QRect(0, st::titleHeight, Application::wnd()->width(), Application::wnd()->height() - st::titleHeight));
setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight));
showAll();
}

View File

@@ -233,6 +233,7 @@ private:
QString _curVersionText, _newVersionText;
int32 _curVersionWidth, _newVersionWidth;
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
enum UpdatingState {
UpdatingNone,
UpdatingCheck,
@@ -243,6 +244,7 @@ private:
};
UpdatingState _updatingState;
QString _newVersionDownload;
#endif
// chat options
FlatCheckbox _replaceEmojis;
@@ -296,12 +298,10 @@ private:
void offPasswordDone(const MTPBool &result);
bool offPasswordFail(const RPCError &error);
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void setUpdatingState(UpdatingState state, bool force = false);
void setDownloadProgress(qint64 ready, qint64 total);
#endif
#endif
};
class SettingsWidget : public TWidget {
@@ -338,7 +338,7 @@ public:
public slots:
void onParentResize(const QSize &newSize);
private:
void showAll();

View File

@@ -22,6 +22,10 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#define PSAPI_VERSION 1 // fix WinXP
//#define Q_NO_TEMPLATE_FRIENDS // fix some compiler difference issues
#define __STDC_FORMAT_MACROS // fix breakpad for mac
#ifdef __cplusplus
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
@@ -38,6 +42,9 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkProxy>
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QHttpMultiPart>
#ifdef Q_OS_WIN // use Lzma SDK for win
#include <LzmaLib.h>
@@ -45,13 +52,11 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include <lzma.h>
#endif
#if defined Q_OS_WIN
#define _NEED_WIN_GENERATE_DUMP
#elif defined Q_OS_LINUX32 || defined Q_OS_LINUX64
#define _NEED_LINUX_GENERATE_DUMP
extern "C" {
#endif
extern "C" {
#include "zip.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
@@ -59,6 +64,8 @@ extern "C" {
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#ifdef __cplusplus
}
#include "types.h"
@@ -80,3 +87,5 @@ extern "C" {
#include "gui/flatlabel.h"
#include "app.h"
#endif

View File

@@ -717,10 +717,6 @@ inline bool PeerData::canWrite() const {
return isChannel() ? asChannel()->canWrite() : (isChat() ? asChat()->canWrite() : (isUser() ? asUser()->canWrite() : false));
}
inline int32 newMessageFlags(PeerData *p) {
return p->isSelf() ? 0 : (((p->isChat() || (p->isUser() && !p->asUser()->botInfo)) ? MTPDmessage::flag_unread : 0) | MTPDmessage::flag_out);
}
enum ActionOnLoad {
ActionOnLoadNone,
ActionOnLoadOpen,

View File

@@ -79,7 +79,7 @@ void SysBtn::paintEvent(QPaintEvent *e) {
}
p.fillRect(x, y, _st.img.pxWidth(), _st.img.pxHeight(), c);
p.drawPixmap(QPoint(x, y), App::sprite(), _st.img);
if (!_text.isEmpty()) {
p.setFont(st::titleTextButton.font->f);
p.setPen(c);
@@ -148,12 +148,13 @@ UpdateBtn::UpdateBtn(QWidget *parent, Window *window, const QString &text) : Sys
}
void UpdateBtn::onClick() {
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
checkReadyUpdate();
#endif
if (App::app()->updatingState() == Application::UpdatingReady) {
if (Sandboxer::updatingState() == Application::UpdatingReady) {
cSetRestartingUpdate(true);
} else {
} else
#endif
{
cSetRestarting(true);
cSetRestartingToSettings(false);
}

View File

@@ -73,7 +73,12 @@ TitleWidget::TitleWidget(Window *window) : TWidget(window)
_update.hide();
_cancel.hide();
_back.hide();
if (App::app()->updatingState() == Application::UpdatingReady || cHasPasscode()) {
if (
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
Sandboxer::updatingState() == Application::UpdatingReady ||
#endif
cHasPasscode()
) {
showUpdateBtn();
}
stateChanged();
@@ -84,10 +89,11 @@ TitleWidget::TitleWidget(Window *window) : TWidget(window)
connect(&_contacts, SIGNAL(clicked()), this, SLOT(onContacts()));
connect(&_about, SIGNAL(clicked()), this, SLOT(onAbout()));
connect(wnd->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(stateChanged(Qt::WindowState)));
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
connect(App::app(), SIGNAL(updateReady()), this, SLOT(showUpdateBtn()));
#endif
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
Sandboxer::connect(SIGNAL(updateReady()), this, SLOT(showUpdateBtn()));
#endif
if (cPlatform() != dbipWindows) {
_minimize.hide();
_maximize.hide();
@@ -173,10 +179,10 @@ void TitleWidget::resizeEvent(QResizeEvent *e) {
if (cPlatform() == dbipWindows) {
p.setX(p.x() - _close.width());
_close.move(p);
p.setX(p.x() - _maximize.width());
_restore.move(p); _maximize.move(p);
p.setX(p.x() - _minimize.width());
_minimize.move(p);
}
@@ -264,7 +270,7 @@ void TitleWidget::updateCounter() {
bool muted = cIncludeMuted() ? (App::histories().unreadMuted >= counter) : false;
style::color bg = muted ? st::counterMuteBG : st::counterBG;
if (counter > 0) {
int32 size = cRetina() ? -32 : -16;
switch (cScale()) {
@@ -322,7 +328,11 @@ void TitleWidget::showUpdateBtn() {
} else {
_lock.hide();
}
bool updateReady = App::app()->updatingState() == Application::UpdatingReady;
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
bool updateReady = (Sandboxer::updatingState() == Application::UpdatingReady);
#else
bool updateReady = false;
#endif
if (updateReady || cEvalScale(cConfigScale()) != cEvalScale(cRealScale())) {
_update.setText(lang(updateReady ? lng_menu_update : lng_menu_restart));
_update.show();

View File

@@ -99,7 +99,7 @@ namespace {
clock_gettime(CLOCK_REALTIME, &ts);
_msgIdMsStart = 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
#endif
uint32 msgIdRand;
memset_rand(&msgIdRand, sizeof(uint32));
_msgIdStart = (((uint64)((uint32)unixtime()) << 32) | (uint64)msgIdRand);
@@ -187,6 +187,32 @@ namespace {
delete l;
}
int _ffmpegLockManager(void **mutex, AVLockOp op) {
switch (op) {
case AV_LOCK_CREATE: {
t_assert(*mutex == 0);
*mutex = reinterpret_cast<void*>(new QMutex());
} break;
case AV_LOCK_OBTAIN: {
t_assert(*mutex != 0);
reinterpret_cast<QMutex*>(*mutex)->lock();
} break;
case AV_LOCK_RELEASE: {
t_assert(*mutex != 0);
reinterpret_cast<QMutex*>(*mutex)->unlock();
}; break;
case AV_LOCK_DESTROY: {
t_assert(*mutex != 0);
delete reinterpret_cast<QMutex*>(*mutex);
*mutex = 0;
} break;
}
return 0;
}
float64 _msFreq;
float64 _msgIdCoef;
int64 _msStart = 0, _msAddToMsStart = 0, _msAddToUnixtime = 0;
@@ -238,36 +264,47 @@ namespace {
_MsStarter _msStarter;
}
InitOpenSSL::InitOpenSSL() {
if (!RAND_status()) { // should be always inited in all modern OS
char buf[16];
memcpy(buf, &_msStart, 8);
memcpy(buf + 8, &_msFreq, 8);
uchar sha256Buffer[32];
RAND_seed(hashSha256(buf, 16, sha256Buffer), 32);
if (!RAND_status()) {
LOG(("MTP Error: Could not init OpenSSL rand, RAND_status() is 0.."));
namespace ThirdParty {
void start() {
if (!RAND_status()) { // should be always inited in all modern OS
char buf[16];
memcpy(buf, &_msStart, 8);
memcpy(buf + 8, &_msFreq, 8);
uchar sha256Buffer[32];
RAND_seed(hashSha256(buf, 16, sha256Buffer), 32);
if (!RAND_status()) {
LOG(("MTP Error: Could not init OpenSSL rand, RAND_status() is 0.."));
}
}
int32 numLocks = CRYPTO_num_locks();
if (numLocks) {
_sslLocks = new QMutex[numLocks];
CRYPTO_set_locking_callback(_sslLockingCallback);
} else {
LOG(("MTP Error: Could not init OpenSSL threads, CRYPTO_num_locks() returned zero!"));
}
CRYPTO_THREADID_set_callback(_sslThreadId);
CRYPTO_set_dynlock_create_callback(_sslCreateFunction);
CRYPTO_set_dynlock_lock_callback(_sslLockFunction);
CRYPTO_set_dynlock_destroy_callback(_sslDestroyFunction);
av_register_all();
avcodec_register_all();
// av_lockmgr_register(_ffmpegLockManager);
_sslInited = true;
}
int32 numLocks = CRYPTO_num_locks();
if (numLocks) {
_sslLocks = new QMutex[numLocks];
CRYPTO_set_locking_callback(_sslLockingCallback);
} else {
LOG(("MTP Error: Could not init OpenSSL threads, CRYPTO_num_locks() returned zero!"));
void finish() {
av_lockmgr_register(0);
delete[] _sslLocks;
_sslLocks = 0;
}
CRYPTO_THREADID_set_callback(_sslThreadId);
CRYPTO_set_dynlock_create_callback(_sslCreateFunction);
CRYPTO_set_dynlock_lock_callback(_sslLockFunction);
CRYPTO_set_dynlock_destroy_callback(_sslDestroyFunction);
_sslInited = true;
}
InitOpenSSL::~InitOpenSSL() {
delete[] _sslLocks;
_sslLocks = 0;
}
bool checkms() {
@@ -640,10 +677,7 @@ char *hashMd5Hex(const int32 *hashmd5, void *dest) {
}
void memset_rand(void *data, uint32 len) {
if (!_sslInited) {
LOG(("Critical Error: memset_rand() called before OpenSSL init!"));
exit(-1);
}
t_assert(_sslInited);
RAND_bytes((uchar*)data, len);
}
@@ -982,3 +1016,34 @@ MimeType mimeTypeForData(const QByteArray &data) {
}
return MimeType(QMimeDatabase().mimeTypeForData(data));
}
class InterfacesMetadatasMap : public QMap<uint64, InterfacesMetadata*> {
public:
~InterfacesMetadatasMap() {
for (const_iterator i = cbegin(), e = cend(); i != e; ++i) {
delete i.value();
}
}
};
const InterfacesMetadata *GetInterfacesMetadata(uint64 mask) {
typedef QMap<uint64, InterfacesMetadata*> InterfacesMetadatasMap;
static InterfacesMetadatasMap InterfacesMetadatas;
static QMutex InterfacesMetadatasMutex;
QMutexLocker lock(&InterfacesMetadatasMutex);
InterfacesMetadatasMap::const_iterator i = InterfacesMetadatas.constFind(mask);
if (i == InterfacesMetadatas.cend()) {
InterfacesMetadata *meta = new InterfacesMetadata(mask);
if (!meta) { // terminate if we can't allocate memory
throw "Can't allocate memory!";
}
i = InterfacesMetadatas.insert(mask, meta);
}
return i.value();
}
InterfaceWrapStruct InterfaceWraps[64];
QAtomicInt InterfaceIndexLast(0);

View File

@@ -84,6 +84,16 @@ using std::swap;
#include "logs.h"
static volatile int *t_assert_nullptr = 0;
inline void t_noop() {}
inline void t_assert_fail(const char *message, const char *file, int32 line) {
LOG(("Assertion Failed! %1 %2:%3").arg(message).arg(file).arg(line));
*t_assert_nullptr = 0;
}
#define t_assert_full(condition, message, file, line) ((!(condition)) ? t_assert_fail(message, file, line) : t_noop())
#define t_assert_c(condition, comment) t_assert_full(condition, "\"" #condition "\" (" comment ")", __FILE__, __LINE__)
#define t_assert(condition) t_assert_full(condition, "\"" #condition "\"", __FILE__, __LINE__)
class Exception : public exception {
public:
@@ -133,18 +143,19 @@ inline void mylocaltime(struct tm * _Tm, const time_t * _Time) {
#endif
}
class InitOpenSSL {
public:
InitOpenSSL();
~InitOpenSSL();
};
namespace ThirdParty {
void start();
void finish();
}
bool checkms(); // returns true if time has changed
uint64 getms(bool checked = false);
class SingleTimer : public QTimer { // single shot timer with check
Q_OBJECT
public:
SingleTimer();
@@ -236,6 +247,19 @@ private:
#define qsl(s) QStringLiteral(s)
#define qstr(s) QLatin1String(s, sizeof(s) - 1)
inline QString fromUtf8Safe(const char *str, int32 size = -1) {
if (!str || !size) return QString();
if (size < 0) size = int32(strlen(str));
QString result(QString::fromUtf8(str, size));
QByteArray back = result.toUtf8();
if (back.size() != size || memcmp(back.constData(), str, size)) return QString::fromLocal8Bit(str, size);
return result;
}
inline QString fromUtf8Safe(const QByteArray &str) {
return fromUtf8Safe(str.constData(), str.size());
}
static const QRegularExpression::PatternOptions reMultiline(QRegularExpression::DotMatchesEverythingOption | QRegularExpression::MultilineOption);
template <typename T>
@@ -458,6 +482,9 @@ MimeType mimeTypeForName(const QString &mime);
MimeType mimeTypeForFile(const QFileInfo &file);
MimeType mimeTypeForData(const QByteArray &data);
inline int32 rowscount(int32 count, int32 perrow) {
return (count + perrow - 1) / perrow;
}
inline int32 floorclamp(int32 value, int32 step, int32 lowest, int32 highest) {
return qMin(qMax(value / step, lowest), highest);
}
@@ -502,6 +529,174 @@ inline void destroyImplementation(I *&ptr) {
deleteAndMark(ptr);
}
class Interfaces;
typedef void(*InterfaceConstruct)(void *location, Interfaces *interfaces);
typedef void(*InterfaceDestruct)(void *location);
struct InterfaceWrapStruct {
InterfaceWrapStruct() : Size(0), Construct(0), Destruct(0) {
}
InterfaceWrapStruct(int size, InterfaceConstruct construct, InterfaceDestruct destruct)
: Size(size)
, Construct(construct)
, Destruct(destruct) {
}
int Size;
InterfaceConstruct Construct;
InterfaceDestruct Destruct;
};
template <int Value, int Denominator>
struct CeilDivideMinimumOne {
static const int Result = ((Value / Denominator) + ((!Value || (Value % Denominator)) ? 1 : 0));
};
template <typename Type>
struct InterfaceWrapTemplate {
static const int Size = CeilDivideMinimumOne<sizeof(Type), sizeof(uint64)>::Result * sizeof(uint64);
static void Construct(void *location, Interfaces *interfaces) {
new (location) Type(interfaces);
}
static void Destruct(void *location) {
((Type*)location)->~Type();
}
};
extern InterfaceWrapStruct InterfaceWraps[64];
extern QAtomicInt InterfaceIndexLast;
template <typename Type>
class BasicInterface {
public:
static int Index() {
static QAtomicInt _index(0);
if (int index = _index.loadAcquire()) {
return index - 1;
}
while (true) {
int last = InterfaceIndexLast.loadAcquire();
if (InterfaceIndexLast.testAndSetOrdered(last, last + 1)) {
t_assert(last < 64);
if (_index.testAndSetOrdered(0, last + 1)) {
InterfaceWraps[last] = InterfaceWrapStruct(InterfaceWrapTemplate<Type>::Size, InterfaceWrapTemplate<Type>::Construct, InterfaceWrapTemplate<Type>::Destruct);
}
break;
}
}
return _index.loadAcquire() - 1;
}
static uint64 Bit() {
return (1 << Index());
}
};
template <typename Type>
class BasicInterfaceWithPointer : public BasicInterface<Type> {
public:
BasicInterfaceWithPointer(Interfaces *interfaces) : interfaces(interfaces) {
}
Interfaces *interfaces = 0;
};
class InterfacesMetadata {
public:
InterfacesMetadata(uint64 mask) : size(0), last(64), _mask(mask) {
for (int i = 0; i < 64; ++i) {
uint64 m = (1 << i);
if (_mask & m) {
int s = InterfaceWraps[i].Size;
if (s) {
offsets[i] = size;
size += s;
} else {
offsets[i] = -1;
}
} else if (_mask < m) {
last = i;
for (; i < 64; ++i) {
offsets[i] = -1;
}
} else {
offsets[i] = -1;
}
}
}
int size, last;
int offsets[64];
private:
uint64 _mask;
};
const InterfacesMetadata *GetInterfacesMetadata(uint64 mask);
class Interfaces {
public:
Interfaces(uint64 mask = 0) : _meta(GetInterfacesMetadata(mask)), _data(0) {
if (_meta->size) {
_data = malloc(_meta->size);
if (!_data) { // terminate if we can't allocate memory
throw "Can't allocate memory!";
}
for (int i = 0; i < _meta->last; ++i) {
int offset = _meta->offsets[i];
if (offset >= 0) {
try {
InterfaceWraps[i].Construct(_dataptrunsafe(offset), this);
} catch (...) {
while (i > 0) {
--i;
offset = _meta->offsets[--i];
if (offset >= 0) {
InterfaceWraps[i].Destruct(_dataptrunsafe(offset));
}
}
throw;
}
}
}
}
}
~Interfaces() {
if (_data) {
for (int i = 0; i < _meta->last; ++i) {
int offset = _meta->offsets[i];
if (offset >= 0) {
InterfaceWraps[i].Destruct(_dataptrunsafe(offset));
}
}
free(_data);
}
}
template <typename Type>
Type *Get() {
return (Type*)_dataptr(_meta->offsets[Type::Index()]);
}
template <typename Type>
const Type *Get() const {
return (const Type*)_dataptr(_meta->offsets[Type::Index()]);
}
private:
void *_dataptrunsafe(int skip) const {
return (char*)_data + skip;
}
void *_dataptr(int skip) const {
return (skip >= 0) ? _dataptrunsafe(skip) : 0;
}
const InterfacesMetadata *_meta;
void *_data;
};
template <typename R>
class FunctionImplementation {
public:

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