Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
520d82b0ff | ||
|
|
47ead03925 | ||
|
|
0ede06700a | ||
|
|
0b4ddb045e | ||
|
|
980b50f8cf | ||
|
|
dd0c79ee56 | ||
|
|
db9f47c6f2 | ||
|
|
c240295a74 | ||
|
|
2b8ef0e9b2 | ||
|
|
759ede3c57 | ||
|
|
6e8fc37ca9 | ||
|
|
3e30a11af8 | ||
|
|
cec7ebc24f | ||
|
|
f3a4b54601 | ||
|
|
91e1330b59 | ||
|
|
952f24e4e1 | ||
|
|
d9b09e78ad | ||
|
|
fd0a119885 | ||
|
|
837432a4f3 | ||
|
|
ab00a0310a | ||
|
|
b536c075ed | ||
|
|
9d2b83c4d5 | ||
|
|
02961ecc99 | ||
|
|
f7f041f11d | ||
|
|
3e1362628f | ||
|
|
5940ae6411 | ||
|
|
44c12064a6 | ||
|
|
9f7b92eccd | ||
|
|
fa2767cc65 | ||
|
|
b1f267e4dc | ||
|
|
daa0adfff9 | ||
|
|
56fa8a0ee2 | ||
|
|
a0d171bb49 | ||
|
|
dd26de7dd2 | ||
|
|
fb4ee55ffa | ||
|
|
17a319fdb3 | ||
|
|
4fc0b439ae | ||
|
|
74248b0284 | ||
|
|
b0681bc582 | ||
|
|
d28483fad4 | ||
|
|
12716a8c40 | ||
|
|
52c29bac5d | ||
|
|
340246367d | ||
|
|
8eef239b45 | ||
|
|
62c28cb58b | ||
|
|
07c81db79a | ||
|
|
a677f784f5 | ||
|
|
c8d7d23ee6 | ||
|
|
0f4405dbaf | ||
|
|
d672353ff9 | ||
|
|
5f84567bbb | ||
|
|
d2f3fbe3f7 | ||
|
|
58777dbc21 | ||
|
|
f3e560541b | ||
|
|
f6eae71397 | ||
|
|
a6861c3e94 | ||
|
|
fc02f96ef1 | ||
|
|
71f588a4fe | ||
|
|
810c60fd8c | ||
|
|
5531f49c3e | ||
|
|
325e45eafd | ||
|
|
b13579c7d9 | ||
|
|
a72a31e722 | ||
|
|
f66c54ee6b | ||
|
|
f979067dda | ||
|
|
e1d932aaeb | ||
|
|
a39810a9c5 | ||
|
|
3078544892 | ||
|
|
709c18e6fc | ||
|
|
931c249afe | ||
|
|
47dee02de4 | ||
|
|
afb40b8289 | ||
|
|
f2824f79f6 | ||
|
|
2c7fb82708 | ||
|
|
f4d159b2f0 | ||
|
|
b82adc7610 | ||
|
|
61b7da069e | ||
|
|
a6e82cf954 | ||
|
|
b9bed419aa | ||
|
|
4ee52afc4d | ||
|
|
76ca0acfd0 | ||
|
|
42d9537ad1 |
@@ -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.
|
||||
|
||||
14
QTCREATOR.md
14
QTCREATOR.md
@@ -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
|
||||
|
||||
17
README.md
17
README.md
@@ -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
|
||||
|
||||
@@ -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%\
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -24,6 +24,10 @@ namespace App {
|
||||
const QPixmap &sprite();
|
||||
}
|
||||
|
||||
namespace Fonts {
|
||||
void start();
|
||||
}
|
||||
|
||||
class Painter : public QPainter {
|
||||
public:
|
||||
explicit Painter(QPaintDevice *device) : QPainter(device) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ¤tText);
|
||||
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:
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace {
|
||||
emit signalEmitOn->countryChanged();
|
||||
}
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
if (App::app()) App::app()->startUpdateCheck();
|
||||
Sandboxer::startUpdateCheck();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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" = "삭제";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user