Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
922ab40c75 | ||
|
|
85ca7e0f05 | ||
|
|
28c8d125cf | ||
|
|
799a81966a | ||
|
|
6333bc59b1 | ||
|
|
d953f894a1 | ||
|
|
868b9843b0 | ||
|
|
c89f13bb53 | ||
|
|
722c801f8a | ||
|
|
269e588ad0 | ||
|
|
4ee33d3bd9 | ||
|
|
85285d9862 | ||
|
|
84226635b2 | ||
|
|
8ed0cb7bf1 | ||
|
|
55649ad6c4 | ||
|
|
379c5f75e7 | ||
|
|
a75f57beb8 | ||
|
|
e0ef1d434d | ||
|
|
cdff62547b | ||
|
|
5dc9cdbd3c | ||
|
|
a8fd1c54c0 | ||
|
|
cc45e06ea9 | ||
|
|
898f8e66c1 | ||
|
|
a9a01cf396 | ||
|
|
0685cf34d3 | ||
|
|
4991b6743f | ||
|
|
12ff311114 | ||
|
|
38f606b612 | ||
|
|
b4427a0073 | ||
|
|
f0900bc02e | ||
|
|
5dfd499e07 | ||
|
|
30ae073080 | ||
|
|
3daa74ff27 | ||
|
|
ccba1c8c6f | ||
|
|
8f8a1e0d1f | ||
|
|
ac81f16107 | ||
|
|
ebe9b4f80f | ||
|
|
b35b3bcb87 | ||
|
|
21a7e0243c | ||
|
|
8c668cfa24 | ||
|
|
87d795807d | ||
|
|
6377f59e23 | ||
|
|
c81beeb023 | ||
|
|
f07606a0ce | ||
|
|
e1adf54b36 |
2
MSVC.md
@@ -137,7 +137,7 @@ There go to Qt directory
|
||||
|
||||
and after that run configure
|
||||
|
||||
configure -debug-and-release -opensource -confirm-license -static -opengl desktop -mp -nomake examples -platform win32-msvc2013
|
||||
configure -debug-and-release -opensource -confirm-license -static -I "D:\TBuild\Libraries\OpenSSL-Win32\include" -L "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib" -l Gdi32 -opengl desktop -openssl-linked OPENSSL_LIBS_DEBUG="D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\ssleay32MTd.lib D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\libeay32MTd.lib" OPENSSL_LIBS_RELEASE="D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\ssleay32MT.lib D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\libeay32MT.lib" -mp -nomake examples -platform win32-msvc2013
|
||||
|
||||
to configure Qt build. After configuration is complete run
|
||||
|
||||
|
||||
16
Telegram/DeployLinux.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
AppVersionStr=0.6.11
|
||||
AppVersion=6011
|
||||
|
||||
if [ ! -f "./../Linux/Release/deploy/$AppVersionStr/tlinuxupd$AppVersion" ]; then
|
||||
echo "tlinuxupd$AppVersion not found!";
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "./../Linux/Release/deploy/$AppVersionStr/tsetup.$AppVersionStr.tar.xz" ]; then
|
||||
echo "tsetup.$AppVersionStr.tar.xz not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
scp ./../Linux/Release/deploy/$AppVersionStr/tlinuxupd$AppVersion tupdates:tdesktop/static/tlinux/
|
||||
scp ./../Linux/Release/deploy/$AppVersionStr/tsetup.$AppVersionStr.tar.xz tupdates:tdesktop/static/tlinux/
|
||||
|
||||
16
Telegram/DeployLinux32.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
AppVersionStr=0.6.11
|
||||
AppVersion=6011
|
||||
|
||||
if [ ! -f "./../Linux/Release/deploy/$AppVersionStr/tlinux32upd$AppVersion" ]; then
|
||||
echo "tlinux32upd$AppVersion not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "./../Linux/Release/deploy/$AppVersionStr/tsetup32.$AppVersionStr.tar.xz" ]; then
|
||||
echo "tsetup32.$AppVersionStr.zip not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
scp ./../Linux/Release/deploy/$AppVersionStr/tlinux32upd$AppVersion tupdates:tdesktop/static/tlinux32/
|
||||
scp ./../Linux/Release/deploy/$AppVersionStr/tsetup32.$AppVersionStr.tar.xz tupdates:tdesktop/static/tlinux32/
|
||||
|
||||
34
Telegram/DeployMacWin.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
AppVersionStr=0.6.11
|
||||
AppVersion=6011
|
||||
|
||||
if [ ! -f "./../Mac/Release/deploy/$AppVersionStr/tmacupd$AppVersion" ]; then
|
||||
echo "tmacupd$AppVersion not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "./../Mac/Release/deploy/$AppVersionStr/tsetup.$AppVersionStr.dmg" ]; then
|
||||
echo "tsetup.$AppVersionStr.dmg not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "./../../tother/tsetup/tupdate$AppVersion" ]; then
|
||||
echo "tupdate$AppVersion not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "./../../tother/tsetup/tportable.$AppVersionStr.zip" ]; then
|
||||
echo "tportable.$AppVersionStr.zip not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "./../../tother/tsetup/tsetup.$AppVersionStr.exe" ]; then
|
||||
echo "tsetup.$AppVersionStr.exe not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
scp ./../Mac/Release/deploy/$AppVersionStr/tmacupd$AppVersion tupdates:tdesktop/static/tmac/
|
||||
scp ./../Mac/Release/deploy/$AppVersionStr/tsetup.$AppVersionStr.dmg tupdates:tdesktop/static/tmac/
|
||||
scp ./../../tother/tsetup/tupdate$AppVersion tupdates:tdesktop/static/tsetup/
|
||||
scp ./../../tother/tsetup/tportable.$AppVersionStr.zip tupdates:tdesktop/static/tsetup/
|
||||
scp ./../../tother/tsetup/tsetup.$AppVersionStr.exe tupdates:tdesktop/static/tsetup/
|
||||
|
||||
21
Telegram/DeployWin.sh
Normal file
@@ -0,0 +1,21 @@
|
||||
AppVersionStr=0.6.11
|
||||
AppVersion=6011
|
||||
|
||||
if [ ! -f "./../Win32/Deploy/deploy/$AppVersionStr/tupdate$AppVersion" ]; then
|
||||
echo "tupdate$AppVersion not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "./../Win32/Deploy/deploy/$AppVersionStr/tportable.$AppVersionStr.zip" ]; then
|
||||
echo "tportable.$AppVersionStr.zip not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "./../Win32/Deploy/deploy/$AppVersionStr/tsetup.$AppVersionStr.exe" ]; then
|
||||
echo "tsetup.$AppVersionStr.exe not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp -v ./../Win32/Deploy/deploy/$AppVersionStr/tupdate$AppVersion /z/TBuild/tother/tsetup/
|
||||
cp -v ./../Win32/Deploy/deploy/$AppVersionStr/tportable.$AppVersionStr.zip /z/TBuild/tother/tsetup/
|
||||
cp -v ./../Win32/Deploy/deploy/$AppVersionStr/tsetup.$AppVersionStr.exe /z/TBuild/tother/tsetup/
|
||||
@@ -516,7 +516,7 @@
|
||||
6DB9C3763D02B1415CD9D565 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0510;
|
||||
LastUpgradeCheck = 0610;
|
||||
};
|
||||
buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "MetaLang" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
@@ -594,11 +594,13 @@
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)Lang$(EFFECTIVE_PLATFORM_NAME)";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1.0;
|
||||
DYLIB_CURRENT_VERSION = 1.0.0;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
@@ -679,12 +681,14 @@
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)Lang$(EFFECTIVE_PLATFORM_NAME)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1.0;
|
||||
DYLIB_CURRENT_VERSION = 1.0.0;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
|
||||
@@ -516,7 +516,7 @@
|
||||
6DB9C3763D02B1415CD9D565 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0510;
|
||||
LastUpgradeCheck = 0610;
|
||||
};
|
||||
buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "MetaStyle" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
@@ -594,11 +594,13 @@
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)Style$(EFFECTIVE_PLATFORM_NAME)";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1.0;
|
||||
DYLIB_CURRENT_VERSION = 1.0.0;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
@@ -679,12 +681,14 @@
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)Style$(EFFECTIVE_PLATFORM_NAME)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1.0;
|
||||
DYLIB_CURRENT_VERSION = 1.0.0;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
|
||||
@@ -483,7 +483,7 @@
|
||||
6DB9C3763D02B1415CD9D565 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0510;
|
||||
LastUpgradeCheck = 0610;
|
||||
};
|
||||
buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "Packer" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
@@ -546,10 +546,12 @@
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1.0;
|
||||
DYLIB_CURRENT_VERSION = 1.0.0;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
@@ -634,11 +636,13 @@
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1.0;
|
||||
DYLIB_CURRENT_VERSION = 1.0.0;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
AppVersionStr=0.6.3
|
||||
AppVersion=6003
|
||||
AppVersionStr=0.6.11
|
||||
AppVersion=6011
|
||||
|
||||
if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then
|
||||
echo "Deploy folder for version $AppVersionStr already exists!"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
AppVersionStr=0.6.3
|
||||
AppVersion=6003
|
||||
AppVersionStr=0.6.11
|
||||
AppVersion=6011
|
||||
|
||||
if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then
|
||||
echo "Deploy folder for version $AppVersionStr already exists!"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
AppVersionStr=0.6.3
|
||||
AppVersion=6003
|
||||
AppVersionStr=0.6.11
|
||||
AppVersion=6011
|
||||
|
||||
echo ""
|
||||
echo "Preparing version $AppVersionStr.."
|
||||
echo ""
|
||||
|
||||
if [ -d "./../Mac/Release/deploy/$AppVersionStr" ]; then
|
||||
echo "Deploy folder for version $AppVersionStr already exists!"
|
||||
@@ -36,18 +40,20 @@ if [ ! -f "./../Mac/Release/Telegram.app/Contents/Frameworks/Updater" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "./../Mac/Release/Telegram.app.dmg" ]; then
|
||||
echo "Telegram.app.dmg not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Preparing version $AppVersionStr, executing Packer.."
|
||||
cd ./../Mac/Release && codesign --force --deep --sign "Developer ID Application: John Preston" Telegram.app && cd ./../../Telegram
|
||||
cd ./../Mac/Release
|
||||
temppath=`hdiutil attach -readwrite tsetup.dmg | awk -F "\t" 'END {print $3}'`
|
||||
cp -R ./Telegram.app "$temppath/"
|
||||
bless --folder "$temppath/" --openfolder "$temppath/"
|
||||
hdiutil detach "$temppath"
|
||||
hdiutil convert tsetup.dmg -format UDZO -imagekey zlib-level=9 -ov -o tsetup.$AppVersionStr.dmg
|
||||
cd ./../../Telegram
|
||||
cd ./../Mac/Release && ./Packer.app/Contents/MacOS/Packer -path Telegram.app -version $AppVersion && cd ./../../Telegram
|
||||
echo "Packer done!"
|
||||
|
||||
if [ ! -d "./../Mac/Release/deploy/" ]; then
|
||||
mkdir "./../Mac/Release/deploy"
|
||||
fi
|
||||
|
||||
echo "Copying Telegram.app and tmacupd$AppVersion to deploy/$AppVersionStr..";
|
||||
mkdir "./../Mac/Release/deploy/$AppVersionStr"
|
||||
mkdir "./../Mac/Release/deploy/$AppVersionStr/Telegram"
|
||||
@@ -55,6 +61,6 @@ cp -r ./../Mac/Release/Telegram.app ./../Mac/Release/deploy/$AppVersionStr/Teleg
|
||||
rm ./../Mac/Release/Telegram.app/Contents/MacOS/Telegram
|
||||
rm ./../Mac/Release/Telegram.app/Contents/Frameworks/Updater
|
||||
mv ./../Mac/Release/tmacupd$AppVersion ./../Mac/Release/deploy/$AppVersionStr/
|
||||
mv ./../Mac/Release/Telegram.app.dmg ./../Mac/Release/deploy/$AppVersionStr/tsetup.$AppVersionStr.dmg
|
||||
mv ./../Mac/Release/tsetup.$AppVersionStr.dmg ./../Mac/Release/deploy/$AppVersionStr/tsetup.$AppVersionStr.dmg
|
||||
echo "Version $AppVersionStr prepared!";
|
||||
|
||||
|
||||
@@ -1,3 +1,46 @@
|
||||
@echo OFF
|
||||
|
||||
set "AppVersionStr=0.6.11"
|
||||
echo.
|
||||
echo Preparing version %AppVersionStr%..
|
||||
echo.
|
||||
|
||||
set "PATH=%PATH%;C:\Program Files\7-Zip;C:\Program Files (x86)\Inno Setup 5"
|
||||
cd ..\Win32\Deploy
|
||||
Prepare.exe -path Telegram.exe -path Updater.exe
|
||||
|
||||
call ..\..\..\TelegramPrivate\Sign.bat Telegram.exe
|
||||
if %errorlevel% neq 0 goto error1
|
||||
|
||||
call ..\..\..\TelegramPrivate\Sign.bat Updater.exe
|
||||
if %errorlevel% neq 0 goto error1
|
||||
|
||||
iscc ..\..\Telegram\Setup.iss
|
||||
if %errorlevel% neq 0 goto error1
|
||||
|
||||
call ..\..\..\TelegramPrivate\Sign.bat tsetup.%AppVersionStr%.exe
|
||||
if %errorlevel% neq 0 goto error1
|
||||
|
||||
call Prepare.exe -path Telegram.exe -path Updater.exe
|
||||
if %errorlevel% neq 0 goto error1
|
||||
|
||||
cd deploy\%AppVersionStr%
|
||||
mkdir Telegram
|
||||
move Telegram.exe Telegram\
|
||||
7z a -mx9 tportable.%AppVersionStr%.zip Telegram\
|
||||
if %errorlevel% neq 0 goto error2
|
||||
|
||||
echo .
|
||||
echo Version %AppVersionStr% is ready for deploy!
|
||||
echo .
|
||||
|
||||
cd ..\..\..\..\Telegram
|
||||
goto eof
|
||||
|
||||
:error2
|
||||
cd ..\..
|
||||
:error1
|
||||
cd ..\..\Telegram
|
||||
echo ERROR occured!
|
||||
exit /b %errorlevel%
|
||||
|
||||
:eof
|
||||
|
||||
@@ -22,6 +22,7 @@ lng_maintitle: "Telegram D";
|
||||
lng_menu_contacts: "Contacts";
|
||||
lng_menu_settings: "Settings";
|
||||
lng_menu_about: "About";
|
||||
lng_menu_update: "Update";
|
||||
|
||||
lng_open_from_tray: "Open Telegram";
|
||||
lng_minimize_to_tray: "Minimize to tray";
|
||||
@@ -57,11 +58,14 @@ lng_month_day: "{month} {day}";
|
||||
|
||||
lng_cancel: "Cancel";
|
||||
lng_continue: "Continue";
|
||||
lng_connecting: "Connecting...";
|
||||
lng_reconnecting: "Reconnect in %1 s...";
|
||||
lng_connecting: "Connecting..";
|
||||
lng_reconnecting: "Reconnect in %1 s..";
|
||||
lng_reconnecting_try_now: "Try now";
|
||||
|
||||
lng_status_offline: "offline";
|
||||
lng_status_offline: "last seen a long time ago";
|
||||
lng_status_recently: "last seen recently";
|
||||
lng_status_last_week: "last seen within a week";
|
||||
lng_status_last_month: "last seen within a month";
|
||||
lng_status_invisible: "invisible";
|
||||
lng_status_lastseen: "last seen {when}";
|
||||
lng_status_lastseen_now: "just now";
|
||||
@@ -74,6 +78,7 @@ lng_status_lastseen_yesterday: "yesterday at {time}";
|
||||
lng_status_lastseen_date: "{date}";
|
||||
lng_status_lastseen_date_time: "{date} at {time}";
|
||||
lng_status_online: "online";
|
||||
lng_status_connecting: "connecting..";
|
||||
|
||||
lng_chat_no_members: "Group is unaccessible";
|
||||
lng_chat_members: "%1 members";
|
||||
@@ -109,7 +114,7 @@ lng_code_ph: "Your code";
|
||||
lng_code_desc: "We have sent you an SMS with activation
|
||||
code to your phone. Please enter it below.";
|
||||
lng_code_call: "Telegram will dial your number in %1:%2";
|
||||
lng_code_calling: "Requesting a call from Telegram...";
|
||||
lng_code_calling: "Requesting a call from Telegram..";
|
||||
lng_code_called: "Telegram dialed your number";
|
||||
|
||||
lng_bad_phone: "Invalid phone number. Please try again.";
|
||||
@@ -136,7 +141,25 @@ lng_settings_cancel: "Cancel";
|
||||
lng_settings_upload: "Set Profile Photo";
|
||||
lng_settings_badsize: "This image has bad size, please try other.";
|
||||
lng_settings_crop_profile: "Select square area for your profile photo";
|
||||
lng_settings_uploading_photo: "Uploading photo...";
|
||||
lng_settings_uploading_photo: "Uploading photo..";
|
||||
|
||||
lng_username_title: "Change username";
|
||||
lng_username_about: "You can choose a username on Telegram.
|
||||
If you do, other people will be able to find
|
||||
you by this username and contact you
|
||||
without knowing your phone number.
|
||||
|
||||
You can use a-z, 0-9 and underscores.
|
||||
Minimum length is 5 characters.";
|
||||
lng_username_invalid: "This name is invalid.";
|
||||
lng_username_occupied: "This name is already occupied.";
|
||||
lng_username_too_short: "This name is too short.";
|
||||
|
||||
lng_settings_section_contact_info: "Contact info";
|
||||
lng_settings_phone_number: "Phone number:";
|
||||
lng_settings_username: "Username:";
|
||||
lng_settings_choose_username: "choose username";
|
||||
lng_settings_change_username: "Change";
|
||||
|
||||
lng_settings_section_notify: "Notifications";
|
||||
lng_settings_desktop_notify: "Desktop notifications";
|
||||
@@ -151,9 +174,9 @@ lng_settings_section_general: "General";
|
||||
lng_settings_auto_update: "Update automatically";
|
||||
lng_settings_current_version: "Version {version}";
|
||||
lng_settings_check_now: "Check for updates";
|
||||
lng_settings_update_checking: "Checking for updates...";
|
||||
lng_settings_update_checking: "Checking for updates..";
|
||||
lng_settings_latest_installed: "Latest version is installed";
|
||||
lng_settings_downloading: "Downloading update {ready} / {total} Mb...";
|
||||
lng_settings_downloading: "Downloading update {ready} / {total} Mb..";
|
||||
lng_settings_update_ready: "New version is ready";
|
||||
lng_settings_update_now: "Restart Now";
|
||||
lng_settings_update_fail: "Update check failed :(";
|
||||
@@ -192,7 +215,7 @@ lng_download_path_settings: "Go to Settings";
|
||||
lng_download_finish_failed: "File download could not be finished.
|
||||
|
||||
Would you like to try again?";
|
||||
lng_download_path_clearing: "Clearing...";
|
||||
lng_download_path_clearing: "Clearing..";
|
||||
lng_download_path_cleared: "Cleared!";
|
||||
lng_download_path_clear_failed: "Clear failed :(";
|
||||
|
||||
@@ -214,6 +237,7 @@ lng_connection_save: "Save";
|
||||
lng_settings_reset: "Reset other sessions";
|
||||
lng_settings_reset_done: "Sessions reset done";
|
||||
lng_settings_logout: "Log Out";
|
||||
lng_sure_logout: "Are you sure you want to log out?";
|
||||
|
||||
lng_settings_need_restart: "You need to restart for applying
|
||||
some of the new settings. Restart now?";
|
||||
@@ -238,7 +262,7 @@ lng_profile_add_participant: "Add Member";
|
||||
lng_profile_delete_and_exit: "Leave";
|
||||
lng_profile_kick: "Kick";
|
||||
lng_profile_sure_kick: "Kick {user} from the group?";
|
||||
lng_profile_loading: "Loading...";
|
||||
lng_profile_loading: "Loading..";
|
||||
lng_profile_shared_media: "Shared media";
|
||||
lng_profile_no_media: "No media in this conversation.";
|
||||
lng_profile_photo: "{count} photo »";
|
||||
@@ -315,7 +339,7 @@ lng_in_dlg_audio: "Audio";
|
||||
lng_in_dlg_document: "Document";
|
||||
|
||||
lng_send_button: "Send";
|
||||
lng_message_ph: "Write a message...";
|
||||
lng_message_ph: "Write a message..";
|
||||
lng_empty_history: "";
|
||||
lng_willbe_history: "Please select chat to start messaging";
|
||||
lng_message_with_from: "[c]{from}:[/c] {message}";
|
||||
@@ -343,7 +367,7 @@ lng_context_copy_email: "Copy email address";
|
||||
lng_context_open_hashtag: "Search by hashtag";
|
||||
lng_context_copy_hashtag: "Copy hashtag";
|
||||
lng_context_open_image: "Open Image";
|
||||
lng_context_save_image: "Save Image As...";
|
||||
lng_context_save_image: "Save Image As..";
|
||||
lng_context_forward_image: "Forward Image";
|
||||
lng_context_delete_image: "Delete Image";
|
||||
lng_context_copy_image: "Copy Image";
|
||||
@@ -352,11 +376,11 @@ lng_context_cancel_download: "Cancel Download";
|
||||
lng_context_show_in_folder: "Show in Folder";
|
||||
lng_context_show_in_finder: "Show in Finder";
|
||||
lng_context_open_video: "Open Video";
|
||||
lng_context_save_video: "Save Video As...";
|
||||
lng_context_save_video: "Save Video As..";
|
||||
lng_context_open_audio: "Open Audio";
|
||||
lng_context_save_audio: "Save Audio As...";
|
||||
lng_context_save_audio: "Save Audio As..";
|
||||
lng_context_open_document: "Open File";
|
||||
lng_context_save_document: "Save File As...";
|
||||
lng_context_save_document: "Save File As..";
|
||||
lng_context_forward_file: "Forward File";
|
||||
lng_context_delete_file: "Delete File";
|
||||
lng_context_close_file: "Close File";
|
||||
@@ -375,7 +399,7 @@ lng_really_send_file: "Do you want to send this file?";
|
||||
lng_really_share_contact: "Do you want to share this contact?";
|
||||
lng_send_image_compressed: "Send compressed image";
|
||||
|
||||
lng_forward_choose: "Choose recipient...";
|
||||
lng_forward_choose: "Choose recipient..";
|
||||
lng_forward_confirm: "Forward to {recipient}?";
|
||||
lng_forward_share_contact: "Share contact to {recipient}?";
|
||||
lng_forward_send_file_confirm: "Send «{name}» to {recipient}?";
|
||||
@@ -430,17 +454,19 @@ lng_about_done: "Done";
|
||||
lng_search_no_results: "No messages found";
|
||||
lng_search_one_result: "Found {count} message";
|
||||
lng_search_n_results: "Found {count} messages";
|
||||
lng_search_global_results: "Global search results";
|
||||
|
||||
lng_mediaview_close: "Close";
|
||||
lng_mediaview_save: "Save as";
|
||||
lng_mediaview_save: "Download";
|
||||
lng_mediaview_forward: "Forward";
|
||||
lng_mediaview_delete: "Delete";
|
||||
lng_mediaview_single_photo: "Single Photo";
|
||||
lng_mediaview_group_photo: "Group Photo";
|
||||
lng_mediaview_profile_photo: "Profile Photo";
|
||||
lng_mediaview_n_of_count: "{n} of {count}";
|
||||
lng_mediaview_n_of_count: "Photo {n} of {count}";
|
||||
lng_mediaview_doc_image: "Document";
|
||||
|
||||
lng_mediaview_saved: "Image was saved to your [c]Downloads[/c] folder";
|
||||
|
||||
// Mac specific
|
||||
|
||||
lng_mac_choose_app: "Choose Application";
|
||||
@@ -452,5 +478,21 @@ lng_mac_always_open_with: "Always Open With";
|
||||
lng_mac_this_app_can_open: "This application can open \"{file}\".";
|
||||
lng_mac_not_known_app: "It's not known if this application can open \"{file}\".";
|
||||
|
||||
// Keys finished
|
||||
lng_mac_menu_about: "About Telegram";
|
||||
lng_mac_menu_preferences: "Preferences...";
|
||||
lng_mac_menu_file: "File";
|
||||
lng_mac_menu_logout: "Log Out";
|
||||
lng_mac_menu_edit: "Edit";
|
||||
lng_mac_menu_undo: "Undo";
|
||||
lng_mac_menu_redo: "Redo";
|
||||
lng_mac_menu_cut: "Cut";
|
||||
lng_mac_menu_copy: "Copy";
|
||||
lng_mac_menu_paste: "Paste";
|
||||
lng_mac_menu_delete: "Delete";
|
||||
lng_mac_menu_select_all: "Select All";
|
||||
lng_mac_menu_window: "Window";
|
||||
lng_mac_menu_contacts: "Contacts";
|
||||
lng_mac_menu_new_group: "New Group";
|
||||
lng_mac_menu_show: "Show Telegram";
|
||||
|
||||
// Keys finished
|
||||
|
||||
@@ -29,7 +29,7 @@ emojiPadding: 0px;
|
||||
counterBG: #f23c34;
|
||||
counterMuteBG: #888;
|
||||
counterColor: #fff;
|
||||
counterMacInvColor: #045fd5;
|
||||
counterMacInvColor: #ffffff01;
|
||||
|
||||
lineWidth: 1px;
|
||||
|
||||
@@ -86,6 +86,7 @@ sysUpd: sysButton {
|
||||
overColor: white;
|
||||
duration: 150;
|
||||
}
|
||||
updateBlinkDuration: 500;
|
||||
sysMin: sysButton(sysUpd) {
|
||||
img: sprite(207px, 1px, 19px, 19px);
|
||||
}
|
||||
@@ -484,10 +485,9 @@ setTop: 26px;
|
||||
setNameLeft: 3px;
|
||||
setNameTop: 1px;
|
||||
setNameFont: font(22px);
|
||||
setPhoneFont: font(16px);
|
||||
setPhoneColor: #999;
|
||||
setPhoneTop: 39px;
|
||||
setPhoneLeft: 1px;
|
||||
setStatusTop: 39px;
|
||||
setStatusLeft: 1px;
|
||||
setStatusFont: font(16px);
|
||||
setPhotoSize: 120px;
|
||||
setHeaderFont: font(20px);
|
||||
setHeaderColor: black;
|
||||
@@ -841,6 +841,10 @@ outTextStyle: textStyle(defaultTextStyle) {
|
||||
selectBG: msgOutSelectBG;
|
||||
selectOverlay: msgOutSelectOverlay;
|
||||
}
|
||||
medviewSaveAsTextStyle: textStyle(defaultTextStyle) {
|
||||
lnkColor: #91d9ff;
|
||||
lnkDownColor: #91d9ff;
|
||||
}
|
||||
|
||||
dlgTextStyle: textStyle(defaultTextStyle) {
|
||||
lnkColor: dlgSystemColor;
|
||||
@@ -1463,49 +1467,115 @@ emojiPanDuration: 200;
|
||||
emojiPanHover: #f0f0f0;
|
||||
emojiPanRound: 2px;
|
||||
|
||||
medviewNavBarWidth: 120px;
|
||||
medviewTopSkip: 66px;
|
||||
medviewBottomSkip: 66px;
|
||||
medviewMainWidth: 600px;
|
||||
medviewControlsBgOpacity: 0.5;
|
||||
medviewLightOpacity: 0.7;
|
||||
medviewDarkOpacity: 0.8;
|
||||
medviewNavBarWidth: 132px;
|
||||
medviewLightNav: 0.5;
|
||||
medviewHeaderFont: font(semibold 18px);
|
||||
medviewDateFont: font(fsize);
|
||||
medviewNameTop: 3px;
|
||||
medviewDateTop: 25px;
|
||||
medviewHeaderColor: #ffffffc0;
|
||||
medviewNameColor: medviewHeaderColor;
|
||||
medviewNameOverColor: #fff;
|
||||
medviewDarkNav: 1;
|
||||
medviewMinWidth: 600;
|
||||
medviewLeft: sprite(0px, 340px, 22px, 40px);
|
||||
medviewRight: sprite(22px, 340px, 22px, 40px);
|
||||
medviewHeaderFont: font(semibold 18px);
|
||||
medviewNameFont: font(16px);
|
||||
medviewDateFont: font(14px);
|
||||
medviewNameTop: 13px;
|
||||
medviewDateTop: 39px;
|
||||
medviewLeft: sprite(340px, 79px, 28px, 48px);
|
||||
medviewRight: sprite(368px, 79px, 28px, 48px);
|
||||
medviewDeltaFromLastAction: 5px;
|
||||
medviewSwipeDistance: 80px;
|
||||
medviewButton: flatButton(btnDefFlat) {
|
||||
color: #ffffff80;
|
||||
overColor: #fff;
|
||||
downColor: #fff;
|
||||
|
||||
medviewSaveMsgCheck: sprite(341px, 174px, 22px, 18px);
|
||||
medviewSaveMsgFont: font(16px);
|
||||
medviewSaveMsgPadding: margins(55px, 19px, 29px, 20px);
|
||||
medviewSaveMsgCheckPos: point(23px, 21px);
|
||||
medviewSaveMsgRadius: 3px;
|
||||
medviewSaveMsgShowing: 200;
|
||||
medviewSaveMsgShown: 2000;
|
||||
medviewSaveMsgHiding: 2500;
|
||||
medviewSaveMsg: #000000b2;
|
||||
|
||||
medviewOverview: iconedButton(btnDefIconed) {
|
||||
bgColor: #0000;
|
||||
overBgColor: #00000055;
|
||||
downBgColor: #00000055;
|
||||
|
||||
width: 100px;
|
||||
height: 46px;
|
||||
|
||||
textTop: 13px;
|
||||
overTextTop: 13px;
|
||||
downTextTop: 14px;
|
||||
|
||||
overBgColor: #00000040;
|
||||
font: font(16px);
|
||||
overFont: font(16px);
|
||||
|
||||
opacity: 0.77;
|
||||
overOpacity: 1;
|
||||
|
||||
icon: sprite(340px, 129px, 19px, 19px);
|
||||
iconPos: point(16px, 14px);
|
||||
downIcon: sprite(340px, 129px, 19px, 19px);
|
||||
downIconPos: point(16px, 14px);
|
||||
|
||||
width: -69px;
|
||||
height: 47px;
|
||||
|
||||
color: white;
|
||||
|
||||
textPos: point(51px, 13px);
|
||||
downTextPos: point(51px, 14px);
|
||||
}
|
||||
medviewForward: iconedButton(medviewOverview) {
|
||||
icon: sprite(357px, 58px, 22px, 17px);
|
||||
iconPos: point(16px, 15px);
|
||||
downIcon: sprite(357px, 58px, 22px, 17px);
|
||||
downIconPos: point(16px, 15px);
|
||||
|
||||
width: -69px;
|
||||
}
|
||||
medviewDelete: iconedButton(medviewForward) {
|
||||
icon: sprite(340px, 58px, 15px, 19px);
|
||||
iconPos: point(16px, 14px);
|
||||
downIcon: sprite(340px, 58px, 15px, 19px);
|
||||
downIconPos: point(16px, 14px);
|
||||
}
|
||||
medviewClose: iconedButton(medviewOverview) {
|
||||
icon: sprite(340px, 0px, 56px, 56px);
|
||||
iconPos: point(0px, 0px);
|
||||
downIcon: sprite(340px, 0px, 56px, 56px);
|
||||
downIconPos: point(0px, 0px);
|
||||
|
||||
opacity: 0.6;
|
||||
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
medviewBottomBar: 87px;
|
||||
medviewBG: #272727D9;
|
||||
medviewBottomBG: #272727;
|
||||
medviewNavOpacity: 0.6;
|
||||
medviewCloseOpacity: 0.6;
|
||||
medviewNavBGOpacity: 0.4;
|
||||
medviewNavOverOpacity: 1;
|
||||
medviewCloseOverOpacity: 1;
|
||||
medviewNameColor: black;
|
||||
medviewDateColor: #999;
|
||||
medviewSaveAs: iconedButton(medviewOverview) {
|
||||
bgColor: #38abe6;
|
||||
overBgColor: #299fdc;
|
||||
|
||||
opacity: 1;
|
||||
|
||||
icon: sprite(361px, 129px, 12px, 19px);
|
||||
iconPos: point(18px, 15px);
|
||||
downIcon: sprite(361px, 129px, 12px, 19px);
|
||||
downIconPos: point(18px, 15px);
|
||||
|
||||
width: -62px;
|
||||
height: 47px;
|
||||
|
||||
textPos: point(44px, 13px);
|
||||
downTextPos: point(44px, 14px);
|
||||
}
|
||||
medviewSaveAsDisabledOpacity: 0.8;
|
||||
medviewPolaroid: margins(17px, 18px, 17px, 72px);
|
||||
medviewPolaroidMin: size(480px, 360px);
|
||||
medviewDocumentSprite: sprite(341px, 150px, 20px, 22px);
|
||||
medviewDocumentSpritePos: point(16px, 13px);
|
||||
medviewPhotoSprite: sprite(363px, 150px, 23px, 20px);
|
||||
medviewPhotoSpritePos: point(14px, 14px);
|
||||
|
||||
overviewPhotoSkip: 10px;
|
||||
overviewPhotoMinSize: 100px;
|
||||
overviewPhotoCheck: sprite(245px, 308px, 32px, 32px);
|
||||
overviewPhotoChecked: sprite(278px, 308px, 32px, 32px);
|
||||
overviewPhotoSelectOverlay: #0a7bb03f;
|
||||
|
||||
// Mac specific
|
||||
|
||||
@@ -1555,3 +1625,22 @@ mediaviewLoaderSkip: 9px;
|
||||
|
||||
minPhotoWidth: 90px;
|
||||
minPhotoHeight: 90px;
|
||||
maxMediaSize: 420px;
|
||||
|
||||
usernameFont: font(14px);
|
||||
usernameColor: #777;
|
||||
usernameWidth: 336px;
|
||||
usernameSkip: 32px;
|
||||
usernameInput: flatInput(inpAddContact) {
|
||||
bgColor: transparent;
|
||||
}
|
||||
usernameDone: flatButton(btnSelectDone) {
|
||||
width: 168px;
|
||||
}
|
||||
usernameCancel: flatButton(btnSelectCancel) {
|
||||
width: 167px;
|
||||
}
|
||||
|
||||
youtubeIcon: sprite(336px, 221px, 60px, 60px);
|
||||
instagramIcon: sprite(336px, 283px, 60px, 60px);
|
||||
locationSize: size(320, 240);
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
#define MyAppShortName "Telegram"
|
||||
#define MyAppName "Telegram Desktop"
|
||||
#define MyAppVersion "0.6.3"
|
||||
#define MyAppVersionZero "0.6.3"
|
||||
#define MyAppFullVersion "0.6.3.0"
|
||||
#define MyAppVersion "0.6.11"
|
||||
#define MyAppVersionZero "0.6.11"
|
||||
#define MyAppFullVersion "0.6.11.0"
|
||||
#define MyAppPublisher "Telegram Messenger LLP"
|
||||
#define MyAppURL "https://tdesktop.com"
|
||||
#define MyAppExeName "Telegram.exe"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
codesign --force --deep --sign "Developer ID Application: John Preston" ./../Mac/Release/Telegram.app
|
||||
@@ -1,4 +0,0 @@
|
||||
cd ..\Win32\Deploy
|
||||
call ..\..\..\TelegramPrivate\Sign.bat Telegram.exe
|
||||
call ..\..\..\TelegramPrivate\Sign.bat Updater.exe
|
||||
cd ..\..\Telegram
|
||||
@@ -1,3 +0,0 @@
|
||||
cd ..\Win32\Deploy
|
||||
call ..\..\..\TelegramPrivate\Sign.bat tsetup.0.6.3.exe
|
||||
cd ..\..\Telegram
|
||||
@@ -52,38 +52,38 @@ struct EmojiReplace {
|
||||
};
|
||||
|
||||
EmojiReplace replaces[] = {
|
||||
{0xD83DDE0A, ":-)"},
|
||||
{0xD83DDE03, ":-D"},
|
||||
{0xD83DDE09, ";-)"},
|
||||
{0xD83DDE06, "xD"},
|
||||
{0xD83DDE1C, ";-P"},
|
||||
{0xD83DDE0B, ":-p"},
|
||||
{0xD83DDE0D, "8-)"},
|
||||
{0xD83DDE0E, "B-)"},
|
||||
{0xD83DDE12, ":-("},
|
||||
{0xD83DDE0F, ":]"},
|
||||
{0xD83DDE14, "3("},
|
||||
{0xD83DDE22, ":'("},
|
||||
{0xD83DDE2D, ":_("},
|
||||
{0xD83DDE29, ":(("},
|
||||
{0xD83DDE28, ":o"},
|
||||
{0xD83DDE10, ":|"},
|
||||
{0xD83DDE0C, "3-)"},
|
||||
{0xD83DDE20, ">("},
|
||||
{0xD83DDE21, ">(("},
|
||||
{0xD83DDE07, "O:)"},
|
||||
{0xD83DDE30, ";o"},
|
||||
{0xD83DDE33, "8|"},
|
||||
{0xD83DDE32, "8o"},
|
||||
{0xD83DDE37, ":X"},
|
||||
{0xD83DDE1A, ":-*"},
|
||||
{0xD83DDE08, "}:)"},
|
||||
{0x2764, "<3"},
|
||||
{0xD83DDC4D, ":like:"},
|
||||
{0xD83DDC4E, ":dislike:"},
|
||||
{0x261D, ":up:"},
|
||||
{0x270C, ":v:"},
|
||||
{0xD83DDC4C, ":ok:"}
|
||||
{0xD83DDE0AU, ":-)"},
|
||||
{0xD83DDE03U, ":-D"},
|
||||
{0xD83DDE09U, ";-)"},
|
||||
{0xD83DDE06U, "xD"},
|
||||
{0xD83DDE1CU, ";-P"},
|
||||
{0xD83DDE0BU, ":-p"},
|
||||
{0xD83DDE0DU, "8-)"},
|
||||
{0xD83DDE0EU, "B-)"},
|
||||
{0xD83DDE12U, ":-("},
|
||||
{0xD83DDE0FU, ":]"},
|
||||
{0xD83DDE14U, "3("},
|
||||
{0xD83DDE22U, ":'("},
|
||||
{0xD83DDE2DU, ":_("},
|
||||
{0xD83DDE29U, ":(("},
|
||||
{0xD83DDE28U, ":o"},
|
||||
{0xD83DDE10U, ":|"},
|
||||
{0xD83DDE0CU, "3-)"},
|
||||
{0xD83DDE20U, ">("},
|
||||
{0xD83DDE21U, ">(("},
|
||||
{0xD83DDE07U, "O:)"},
|
||||
{0xD83DDE30U, ";o"},
|
||||
{0xD83DDE33U, "8|"},
|
||||
{0xD83DDE32U, "8o"},
|
||||
{0xD83DDE37U, ":X"},
|
||||
{0xD83DDE1AU, ":-*"},
|
||||
{0xD83DDE08U, "}:)"},
|
||||
{0x2764FE0FU, "<3"},
|
||||
{0xD83DDC4DU, ":like:"},
|
||||
{0xD83DDC4EU, ":dislike:"},
|
||||
{0x261DFE0FU, ":up:"},
|
||||
{0x270CFE0FU, ":v:"},
|
||||
{0xD83DDC4CU, ":ok:"}
|
||||
};
|
||||
const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace);
|
||||
typedef QMap<QString, uint32> ReplaceMap;
|
||||
@@ -102,7 +102,7 @@ uint64 emojiCategory0[] = {
|
||||
0xD83DDE03LLU,
|
||||
0xD83DDE00LLU,
|
||||
0xD83DDE0ALLU,
|
||||
0x263ALLU,
|
||||
0x263AFE0FLLU,
|
||||
0xD83DDE09LLU,
|
||||
0xD83DDE0DLLU,
|
||||
0xD83DDE18LLU,
|
||||
@@ -208,7 +208,7 @@ uint64 emojiCategory0[] = {
|
||||
0xD83DDC4CLLU,
|
||||
0xD83DDC4ALLU,
|
||||
0x270ALLU,
|
||||
0x270CLLU,
|
||||
0x270CFE0FLLU,
|
||||
0xD83DDC4BLLU,
|
||||
0x270BLLU,
|
||||
0xD83DDC50LLU,
|
||||
@@ -218,7 +218,7 @@ uint64 emojiCategory0[] = {
|
||||
0xD83DDC48LLU,
|
||||
0xD83DDE4CLLU,
|
||||
0xD83DDE4FLLU,
|
||||
0x261DLLU,
|
||||
0x261DFE0FLLU,
|
||||
0xD83DDC4FLLU,
|
||||
0xD83DDCAALLU,
|
||||
0xD83DDEB6LLU,
|
||||
@@ -270,7 +270,7 @@ uint64 emojiCategory0[] = {
|
||||
0xD83DDC99LLU,
|
||||
0xD83DDC9CLLU,
|
||||
0xD83DDC9ALLU,
|
||||
0x2764LLU,
|
||||
0x2764FE0FLLU,
|
||||
0xD83DDC94LLU,
|
||||
0xD83DDC97LLU,
|
||||
0xD83DDC93LLU,
|
||||
@@ -395,12 +395,12 @@ uint64 emojiCategory1[] = {
|
||||
0xD83CDF0CLLU,
|
||||
0xD83CDF20LLU,
|
||||
0x2B50LLU,
|
||||
0x2600LLU,
|
||||
0x2600FE0FLLU,
|
||||
0x26C5LLU,
|
||||
0x2601LLU,
|
||||
0x2601FE0FLLU,
|
||||
0x26A1LLU,
|
||||
0x2614LLU,
|
||||
0x2744LLU,
|
||||
0x2744FE0FLLU,
|
||||
0x26C4LLU,
|
||||
0xD83CDF00LLU,
|
||||
0xD83CDF01LLU,
|
||||
@@ -440,7 +440,7 @@ uint64 emojiCategory2[] = {
|
||||
0xD83DDCBELLU,
|
||||
0xD83DDCBBLLU,
|
||||
0xD83DDCF1LLU,
|
||||
0x260ELLU,
|
||||
0x260EFE0FLLU,
|
||||
0xD83DDCDELLU,
|
||||
0xD83DDCDFLLU,
|
||||
0xD83DDCE0LLU,
|
||||
@@ -497,7 +497,7 @@ uint64 emojiCategory2[] = {
|
||||
0xD83DDCE7LLU,
|
||||
0xD83DDCE5LLU,
|
||||
0xD83DDCE4LLU,
|
||||
0x2709LLU,
|
||||
0x2709FE0FLLU,
|
||||
0xD83DDCE9LLU,
|
||||
0xD83DDCE8LLU,
|
||||
0xD83DDCEFLLU,
|
||||
@@ -521,11 +521,11 @@ uint64 emojiCategory2[] = {
|
||||
0xD83DDCC7LLU,
|
||||
0xD83DDCC1LLU,
|
||||
0xD83DDCC2LLU,
|
||||
0x2702LLU,
|
||||
0x2702FE0FLLU,
|
||||
0xD83DDCCCLLU,
|
||||
0xD83DDCCELLU,
|
||||
0x2712LLU,
|
||||
0x270FLLU,
|
||||
0x2712FE0FLLU,
|
||||
0x270FFE0FLLU,
|
||||
0xD83DDCCFLLU,
|
||||
0xD83DDCD0LLU,
|
||||
0xD83DDCD5LLU,
|
||||
@@ -564,7 +564,7 @@ uint64 emojiCategory2[] = {
|
||||
0xD83CDFC8LLU,
|
||||
0xD83CDFC0LLU,
|
||||
0x26BDLLU,
|
||||
0x26BELLU,
|
||||
0x26BEFE0FLLU,
|
||||
0xD83CDFBELLU,
|
||||
0xD83CDFB1LLU,
|
||||
0xD83CDFC9LLU,
|
||||
@@ -680,7 +680,7 @@ uint64 emojiCategory3[] = {
|
||||
0xD83DDEA3LLU,
|
||||
0x2693LLU,
|
||||
0xD83DDE80LLU,
|
||||
0x2708LLU,
|
||||
0x2708FE0FLLU,
|
||||
0xD83DDCBALLU,
|
||||
0xD83DDE81LLU,
|
||||
0xD83DDE82LLU,
|
||||
@@ -721,13 +721,13 @@ uint64 emojiCategory3[] = {
|
||||
0xD83CDFABLLU,
|
||||
0xD83DDEA6LLU,
|
||||
0xD83DDEA5LLU,
|
||||
0x26A0LLU,
|
||||
0x26A0FE0FLLU,
|
||||
0xD83DDEA7LLU,
|
||||
0xD83DDD30LLU,
|
||||
0x26FDLLU,
|
||||
0xD83CDFEELLU,
|
||||
0xD83CDFB0LLU,
|
||||
0x2668LLU,
|
||||
0x2668FE0FLLU,
|
||||
0xD83DDDFFLLU,
|
||||
0xD83CDFAALLU,
|
||||
0xD83CDFADLLU,
|
||||
@@ -760,33 +760,33 @@ uint64 emojiCategory4[] = {
|
||||
0xD83DDD22LLU,
|
||||
0x2320E3LLU,
|
||||
0xD83DDD23LLU,
|
||||
0x2B06LLU,
|
||||
0x2B07LLU,
|
||||
0x2B05LLU,
|
||||
0x27A1LLU,
|
||||
0x2B06FE0FLLU,
|
||||
0x2B07FE0FLLU,
|
||||
0x2B05FE0FLLU,
|
||||
0x27A1FE0FLLU,
|
||||
0xD83DDD20LLU,
|
||||
0xD83DDD21LLU,
|
||||
0xD83DDD24LLU,
|
||||
0x2197LLU,
|
||||
0x2196LLU,
|
||||
0x2198LLU,
|
||||
0x2199LLU,
|
||||
0x2194LLU,
|
||||
0x2195LLU,
|
||||
0x2197FE0FLLU,
|
||||
0x2196FE0FLLU,
|
||||
0x2198FE0FLLU,
|
||||
0x2199FE0FLLU,
|
||||
0x2194FE0FLLU,
|
||||
0x2195FE0FLLU,
|
||||
0xD83DDD04LLU,
|
||||
0x25C0LLU,
|
||||
0x25B6LLU,
|
||||
0x25C0FE0FLLU,
|
||||
0x25B6FE0FLLU,
|
||||
0xD83DDD3CLLU,
|
||||
0xD83DDD3DLLU,
|
||||
0x21A9LLU,
|
||||
0x21AALLU,
|
||||
0x2139LLU,
|
||||
0x21A9FE0FLLU,
|
||||
0x21AAFE0FLLU,
|
||||
0x2139FE0FLLU,
|
||||
0x23EALLU,
|
||||
0x23E9LLU,
|
||||
0x23EBLLU,
|
||||
0x23ECLLU,
|
||||
0x2935LLU,
|
||||
0x2934LLU,
|
||||
0x2935FE0FLLU,
|
||||
0x2934FE0FLLU,
|
||||
0xD83CDD97LLU,
|
||||
0xD83DDD00LLU,
|
||||
0xD83DDD01LLU,
|
||||
@@ -822,14 +822,14 @@ uint64 emojiCategory4[] = {
|
||||
0xD83CDE37LLU,
|
||||
0xD83CDE38LLU,
|
||||
0xD83CDE02LLU,
|
||||
0x24C2LLU,
|
||||
0x24C2FE0FLLU,
|
||||
0xD83DDEC2LLU,
|
||||
0xD83DDEC4LLU,
|
||||
0xD83DDEC5LLU,
|
||||
0xD83DDEC3LLU,
|
||||
0xD83CDE51LLU,
|
||||
0x3299LLU,
|
||||
0x3297LLU,
|
||||
0x3299FE0FLLU,
|
||||
0x3297FE0FLLU,
|
||||
0xD83CDD91LLU,
|
||||
0xD83CDD98LLU,
|
||||
0xD83CDD94LLU,
|
||||
@@ -842,11 +842,11 @@ uint64 emojiCategory4[] = {
|
||||
0xD83DDEB7LLU,
|
||||
0xD83DDEB8LLU,
|
||||
0x26D4LLU,
|
||||
0x2733LLU,
|
||||
0x2747LLU,
|
||||
0x2733FE0FLLU,
|
||||
0x2747FE0FLLU,
|
||||
0x274ELLU,
|
||||
0x2705LLU,
|
||||
0x2734LLU,
|
||||
0x2734FE0FLLU,
|
||||
0xD83DDC9FLLU,
|
||||
0xD83CDD9ALLU,
|
||||
0xD83DDCF3LLU,
|
||||
@@ -857,7 +857,7 @@ uint64 emojiCategory4[] = {
|
||||
0xD83CDD7ELLU,
|
||||
0xD83DDCA0LLU,
|
||||
0x27BFLLU,
|
||||
0x267BLLU,
|
||||
0x267BFE0FLLU,
|
||||
0x2648LLU,
|
||||
0x2649LLU,
|
||||
0x264ALLU,
|
||||
@@ -880,8 +880,8 @@ uint64 emojiCategory4[] = {
|
||||
0xAELLU,
|
||||
0x2122LLU,
|
||||
0x274CLLU,
|
||||
0x203CLLU,
|
||||
0x2049LLU,
|
||||
0x203CFE0FLLU,
|
||||
0x2049FE0FLLU,
|
||||
0x2757LLU,
|
||||
0x2753LLU,
|
||||
0x2755LLU,
|
||||
@@ -921,26 +921,26 @@ uint64 emojiCategory4[] = {
|
||||
0x2795LLU,
|
||||
0x2796LLU,
|
||||
0x2797LLU,
|
||||
0x2660LLU,
|
||||
0x2665LLU,
|
||||
0x2663LLU,
|
||||
0x2666LLU,
|
||||
0x2660FE0FLLU,
|
||||
0x2665FE0FLLU,
|
||||
0x2663FE0FLLU,
|
||||
0x2666FE0FLLU,
|
||||
0xD83DDCAELLU,
|
||||
0xD83DDCAFLLU,
|
||||
0x2714LLU,
|
||||
0x2611LLU,
|
||||
0x2714FE0FLLU,
|
||||
0x2611FE0FLLU,
|
||||
0xD83DDD18LLU,
|
||||
0xD83DDD17LLU,
|
||||
0x27B0LLU,
|
||||
0x3030LLU,
|
||||
0x303DLLU,
|
||||
0x303DFE0FLLU,
|
||||
0xD83DDD31LLU,
|
||||
0x25FCLLU,
|
||||
0x25FBLLU,
|
||||
0x25FCFE0FLLU,
|
||||
0x25FBFE0FLLU,
|
||||
0x25FELLU,
|
||||
0x25FDLLU,
|
||||
0x25AALLU,
|
||||
0x25ABLLU,
|
||||
0x25AAFE0FLLU,
|
||||
0x25ABFE0FLLU,
|
||||
0xD83DDD3ALLU,
|
||||
0xD83DDD32LLU,
|
||||
0xD83DDD33LLU,
|
||||
@@ -957,6 +957,62 @@ uint64 emojiCategory4[] = {
|
||||
0xD83DDD39LLU,
|
||||
};
|
||||
|
||||
uint64 emojiClones[] = {
|
||||
0x263ALLU,
|
||||
0x270CLLU,
|
||||
0x261DLLU,
|
||||
0x2764LLU,
|
||||
0x2600LLU,
|
||||
0x2601LLU,
|
||||
0x2744LLU,
|
||||
0x260ELLU,
|
||||
0x2709LLU,
|
||||
0x2702LLU,
|
||||
0x2712LLU,
|
||||
0x270FLLU,
|
||||
0x26BELLU,
|
||||
0x2708LLU,
|
||||
0x26A0LLU,
|
||||
0x2668LLU,
|
||||
0x2B06LLU,
|
||||
0x2B07LLU,
|
||||
0x2B05LLU,
|
||||
0x27A1LLU,
|
||||
0x2197LLU,
|
||||
0x2196LLU,
|
||||
0x2198LLU,
|
||||
0x2199LLU,
|
||||
0x2194LLU,
|
||||
0x2195LLU,
|
||||
0x25C0LLU,
|
||||
0x25B6LLU,
|
||||
0x21A9LLU,
|
||||
0x21AALLU,
|
||||
0x2139LLU,
|
||||
0x2935LLU,
|
||||
0x2934LLU,
|
||||
0x24C2LLU,
|
||||
0x3299LLU,
|
||||
0x3297LLU,
|
||||
0x2733LLU,
|
||||
0x2747LLU,
|
||||
0x2734LLU,
|
||||
0x267BLLU,
|
||||
0x203CLLU,
|
||||
0x2049LLU,
|
||||
0x2660LLU,
|
||||
0x2665LLU,
|
||||
0x2663LLU,
|
||||
0x2666LLU,
|
||||
0x2714LLU,
|
||||
0x2611LLU,
|
||||
0x303DLLU,
|
||||
0x25FCLLU,
|
||||
0x25FBLLU,
|
||||
0x25AALLU,
|
||||
0x25ABLLU,
|
||||
};
|
||||
|
||||
uint32 firstCode(uint64 fullCode) {
|
||||
return (fullCode > 0xFFFFFFFFLLU) ? uint32(fullCode >> 32) : (fullCode & 0xFFFFFFFFU);
|
||||
}
|
||||
@@ -990,7 +1046,7 @@ void writeEmojiCategory(QTextStream &tcpp, uint64 *emojiCategory, uint32 size, c
|
||||
|
||||
bool genEmoji(QString emoji_in, const QString &emoji_out, const QString &emoji_png) {
|
||||
int currentRow = 0, currentColumn = 0;
|
||||
uint32 min1 = 0xFFFFFFFF, max1 = 0, min2 = 0xFFFFFFFF, max2 = 0;
|
||||
uint32 min1 = 0xFFFFFFFFU, max1 = 0, min2 = 0xFFFFFFFFU, max2 = 0, min3 = 0xFFFFFFFFU, max3 = 0;
|
||||
|
||||
QImage sprites[5];
|
||||
int emojisInRow[] = { 27, 29, 33, 34, 34 }; // [[7,27],[4,29],[7,33],[3,34],[6,34]]
|
||||
@@ -1044,8 +1100,13 @@ bool genEmoji(QString emoji_in, const QString &emoji_out, const QString &emoji_p
|
||||
}
|
||||
} else if (high == 35 || (high >= 48 && high < 58)) { // digits
|
||||
} else {
|
||||
if (data.code < min2) min2 = data.code;
|
||||
if (data.code > max2) max2 = data.code;
|
||||
if (high < 0xD000) {
|
||||
if (data.code < min2) min2 = data.code;
|
||||
if (data.code > max2) max2 = data.code;
|
||||
} else {
|
||||
if (data.code < min3) min3 = data.code;
|
||||
if (data.code > max3) max3 = data.code;
|
||||
}
|
||||
}
|
||||
EmojisData::const_iterator k = emojisData.constFind(data.code);
|
||||
if (k != emojisData.cend()) {
|
||||
@@ -1109,6 +1170,25 @@ bool genEmoji(QString emoji_in, const QString &emoji_out, const QString &emoji_p
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, l = sizeof(emojiClones) / sizeof(emojiClones[0]); i < l; ++i) {
|
||||
uint64 cloneCode = emojiClones[i], originalCode = (cloneCode << 16) | 0xFE0F;
|
||||
EmojisData::const_iterator j = emojisData.constFind(originalCode);
|
||||
if (j == emojisData.cend()) {
|
||||
cout << "Could not find data for emoji clone 0x" << QString::number(cloneCode, 16).toUpper().toUtf8().constData() << "!\n";
|
||||
return false;
|
||||
}
|
||||
EmojiData clone;
|
||||
clone.code = firstCode(cloneCode);
|
||||
clone.code2 = secondCode(cloneCode);
|
||||
clone.category = -1;
|
||||
clone.index = -1;
|
||||
clone.x = j->x;
|
||||
clone.y = j->y;
|
||||
emojisData.insert(cloneCode, clone);
|
||||
if (clone.code < min1) min1 = clone.code;
|
||||
if (clone.code > max1) max1 = clone.code;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
QByteArray cppText;
|
||||
@@ -1203,7 +1283,18 @@ Copyright (c) 2014 John Preston, https://tdesktop.com\n\
|
||||
tcpp << "\t\treturn 0;\n";
|
||||
tcpp << "\t}\n\n";
|
||||
|
||||
tcpp << "\tif (code < 0x" << QString("%1").arg(min2, 0, 16).toUpper().toUtf8().constData() << "U || code > 0x" << QString("%1").arg(max2, 0, 16).toUpper().toUtf8().constData() << "U) return 0;\n\n";
|
||||
tcpp << "\tif (code >= 0x" << QString("%1").arg(min2, 0, 16).toUpper().toUtf8().constData() << "U && code <= 0x" << QString("%1").arg(max2, 0, 16).toUpper().toUtf8().constData() << "U) {\n";
|
||||
tcpp << "\t\tif ((code & 0xFFFFU) != 0xFE0F) return 0;\n\n";
|
||||
tcpp << "\t\tswitch (code) {\n";
|
||||
for (; i != e; ++i) {
|
||||
if (i->code2 || ((i->code >> 16) >= 0xD000)) break;
|
||||
tcpp << "\t\t\tcase 0x" << QString("%1").arg(i->code, 0, 16).toUpper().toUtf8().constData() << "U: return &emojis[" << (index++) << "];\n";
|
||||
}
|
||||
tcpp << "\t\t}\n\n";
|
||||
tcpp << "\t\treturn 0;\n";
|
||||
tcpp << "\t}\n\n";
|
||||
|
||||
tcpp << "\tif (code < 0x" << QString("%1").arg(min3, 0, 16).toUpper().toUtf8().constData() << "U || code > 0x" << QString("%1").arg(max3, 0, 16).toUpper().toUtf8().constData() << "U) return 0;\n\n";
|
||||
tcpp << "\tswitch (code) {\n";
|
||||
for (; i != e; ++i) {
|
||||
tcpp << "\tcase 0x" << QString("%1").arg(i->code, 0, 16).toUpper().toUtf8().constData() << "U: return &emojis[" << (index++) << "];\n";
|
||||
@@ -1248,7 +1339,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com\n\
|
||||
}
|
||||
tcpp << tab.repeated(1 + chars.size()) << "newEmojiEnd = ch + " << chars.size() << ";\n";
|
||||
tcpp << tab.repeated(1 + chars.size()) << "if (newEmojiEnd == e || emojiEdge(newEmojiEnd) || newEmojiEnd->unicode() == ' ') {\n";
|
||||
tcpp << tab.repeated(1 + chars.size()) << "\temojiCode = " << i.value() << "U;\n";
|
||||
tcpp << tab.repeated(1 + chars.size()) << "\temojiCode = 0x" << QString("%1").arg(i.value(), 0, 16).toUpper().toUtf8().constData() << "U;\n";
|
||||
tcpp << tab.repeated(1 + chars.size()) << "\treturn;\n";
|
||||
tcpp << tab.repeated(1 + chars.size()) << "}\n";
|
||||
}
|
||||
|
||||
@@ -94,6 +94,5 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
int res = prepare(f, paths);
|
||||
system("PAUSE");
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ namespace {
|
||||
typedef QHash<AudioId, AudioData*> AudiosData;
|
||||
AudiosData audiosData;
|
||||
|
||||
typedef QHash<QString, ImageLinkData*> ImageLinksData;
|
||||
ImageLinksData imageLinksData;
|
||||
|
||||
typedef QHash<DocumentId, DocumentData*> DocumentsData;
|
||||
DocumentsData documentsData;
|
||||
|
||||
@@ -162,6 +165,31 @@ namespace App {
|
||||
return (peer_id & 0x100000000L) ? int32(peer_id & 0xFFFFFFFFL) : 0;
|
||||
}
|
||||
|
||||
int32 onlineForSort(int32 online, int32 now) {
|
||||
if (online <= 0) {
|
||||
switch (online) {
|
||||
case -2: {
|
||||
QDate yesterday(date(now).date());
|
||||
yesterday.addDays(-1);
|
||||
return int32(QDateTime(yesterday).toTime_t());
|
||||
} break;
|
||||
|
||||
case -3: {
|
||||
QDate weekago(date(now).date());
|
||||
weekago.addDays(-7);
|
||||
return int32(QDateTime(weekago).toTime_t());
|
||||
} break;
|
||||
|
||||
case -4: {
|
||||
QDate monthago(date(now).date());
|
||||
monthago.addDays(-30);
|
||||
return int32(QDateTime(monthago).toTime_t());
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return online;
|
||||
}
|
||||
|
||||
int32 onlineWillChangeIn(int32 online, int32 now) {
|
||||
if (online <= 0) return 86400;
|
||||
if (online > now) {
|
||||
@@ -180,8 +208,15 @@ namespace App {
|
||||
}
|
||||
|
||||
QString onlineText(int32 online, int32 now, bool precise) {
|
||||
if (!online) return lang(lng_status_offline);
|
||||
if (online < 0) return lang(lng_status_invisible);
|
||||
if (online <= 0) {
|
||||
switch (online) {
|
||||
case 0: return lang(lng_status_offline);
|
||||
case -2: return lang(lng_status_recently);
|
||||
case -3: return lang(lng_status_last_week);
|
||||
case -4: return lang(lng_status_last_month);
|
||||
}
|
||||
return lang(lng_status_invisible);
|
||||
}
|
||||
if (online > now) {
|
||||
return lang(lng_status_online);
|
||||
}
|
||||
@@ -240,7 +275,7 @@ namespace App {
|
||||
data = App::user(peer);
|
||||
data->input = MTP_inputPeerContact(d.vid);
|
||||
data->inputUser = MTP_inputUserContact(d.vid);
|
||||
data->setName(lang(lng_deleted), QString(), QString());
|
||||
data->setName(lang(lng_deleted), QString(), QString(), QString());
|
||||
data->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
data->access = 0;
|
||||
wasContact = (data->contact > 0);
|
||||
@@ -253,7 +288,7 @@ namespace App {
|
||||
data = App::user(peer);
|
||||
data->input = MTP_inputPeerContact(d.vid);
|
||||
data->inputUser = MTP_inputUserContact(d.vid);
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString());
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString(), textOneLine(qs(d.vusername)));
|
||||
data->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
data->access = 0;
|
||||
wasContact = (data->contact > 0);
|
||||
@@ -266,7 +301,7 @@ namespace App {
|
||||
data = App::user(peer);
|
||||
data->input = MTP_inputPeerSelf();
|
||||
data->inputUser = MTP_inputUserSelf();
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString());
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString(), textOneLine(qs(d.vusername)));
|
||||
data->setPhoto(d.vphoto);
|
||||
data->setPhone(qs(d.vphone));
|
||||
data->access = 0;
|
||||
@@ -274,7 +309,10 @@ namespace App {
|
||||
data->contact = -1;
|
||||
status = &d.vstatus;
|
||||
|
||||
::self = data;
|
||||
if (::self != data) {
|
||||
::self = data;
|
||||
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
||||
}
|
||||
} break;
|
||||
case mtpc_userContact: {
|
||||
const MTPDuserContact &d(user.c_userContact());
|
||||
@@ -283,7 +321,7 @@ namespace App {
|
||||
data = App::user(peer);
|
||||
data->input = MTP_inputPeerContact(d.vid);
|
||||
data->inputUser = MTP_inputUserContact(d.vid);
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString());
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString(), textOneLine(qs(d.vusername)));
|
||||
data->setPhoto(d.vphoto);
|
||||
data->setPhone(qs(d.vphone));
|
||||
data->access = d.vaccess_hash.v;
|
||||
@@ -299,7 +337,7 @@ namespace App {
|
||||
data->input = MTP_inputPeerForeign(d.vid, d.vaccess_hash);
|
||||
data->inputUser = MTP_inputUserForeign(d.vid, d.vaccess_hash);
|
||||
data->setPhone(qs(d.vphone));
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), (data->id != 333000 && !data->phone.isEmpty()) ? formatPhone(data->phone) : QString());
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), (data->id != 333000 && !data->phone.isEmpty()) ? formatPhone(data->phone) : QString(), textOneLine(qs(d.vusername)));
|
||||
data->setPhoto(d.vphoto);
|
||||
data->access = d.vaccess_hash.v;
|
||||
wasContact = (data->contact > 0);
|
||||
@@ -313,7 +351,7 @@ namespace App {
|
||||
data = App::user(peer);
|
||||
data->input = MTP_inputPeerForeign(d.vid, d.vaccess_hash);
|
||||
data->inputUser = MTP_inputUserForeign(d.vid, d.vaccess_hash);
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString());
|
||||
data->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), QString(), textOneLine(qs(d.vusername)));
|
||||
data->setPhoto(d.vphoto);
|
||||
data->access = d.vaccess_hash.v;
|
||||
wasContact = (data->contact > 0);
|
||||
@@ -326,6 +364,10 @@ namespace App {
|
||||
|
||||
data->loaded = true;
|
||||
if (status) switch (status->type()) {
|
||||
case mtpc_userStatusEmpty: data->onlineTill = 0; break;
|
||||
case mtpc_userStatusRecently: data->onlineTill = -2; break;
|
||||
case mtpc_userStatusLastWeek: data->onlineTill = -3; break;
|
||||
case mtpc_userStatusLastMonth: data->onlineTill = -4; break;
|
||||
case mtpc_userStatusOffline: data->onlineTill = status->c_userStatusOffline().vwas_online.v; break;
|
||||
case mtpc_userStatusOnline: data->onlineTill = status->c_userStatusOnline().vexpires.v; break;
|
||||
}
|
||||
@@ -405,7 +447,7 @@ namespace App {
|
||||
if (!data) continue;
|
||||
|
||||
data->loaded = true;
|
||||
data->updateName(title.trimmed(), QString());
|
||||
data->updateName(title.trimmed(), QString(), QString());
|
||||
|
||||
if (App::main()) App::main()->peerUpdated(data);
|
||||
}
|
||||
@@ -531,7 +573,7 @@ namespace App {
|
||||
const MTPDphotoSize &d(size.c_photoSize());
|
||||
if (d.vlocation.type() == mtpc_fileLocation) {
|
||||
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
|
||||
return ImagePtr(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v);
|
||||
return ImagePtr(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v, d.vsize.v);
|
||||
}
|
||||
} break;
|
||||
case mtpc_photoCachedSize: {
|
||||
@@ -561,20 +603,27 @@ namespace App {
|
||||
}
|
||||
|
||||
void feedWereDeleted(const QVector<MTPint> &msgsIds) {
|
||||
bool resized = false;
|
||||
for (QVector<MTPint>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
|
||||
MsgsData::const_iterator j = msgsData.constFind(i->v);
|
||||
if (j != msgsData.cend()) {
|
||||
History *h = (*j)->history();
|
||||
(*j)->destroy();
|
||||
if (App::main() && h->peer == App::main()->peer()) {
|
||||
resized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (resized) {
|
||||
App::main()->itemResized(0);
|
||||
}
|
||||
}
|
||||
|
||||
void feedUserLinks(const MTPVector<MTPcontacts_Link> &links) {
|
||||
const QVector<MTPcontacts_Link> &v(links.c_vector().v);
|
||||
for (QVector<MTPcontacts_Link>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
|
||||
const MTPDcontacts_link &dv(i->c_contacts_link());
|
||||
feedUsers(MTP_vector<MTPUser>(QVector<MTPUser>(1, dv.vuser)));
|
||||
feedUsers(MTP_vector<MTPUser>(1, dv.vuser));
|
||||
MTPint userId(MTP_int(0));
|
||||
switch (dv.vuser.type()) {
|
||||
case mtpc_userEmpty: userId = dv.vuser.c_userEmpty().vid; break;
|
||||
@@ -634,7 +683,7 @@ namespace App {
|
||||
App::main()->removeContact(user);
|
||||
}
|
||||
}
|
||||
user->setName(textOneLine(user->firstName), textOneLine(user->lastName), (user->contact || user->id == 333000 || user->phone.isEmpty()) ? QString() : App::formatPhone(user->phone));
|
||||
user->setName(textOneLine(user->firstName), textOneLine(user->lastName), (user->contact || user->id == 333000 || user->phone.isEmpty()) ? QString() : App::formatPhone(user->phone), textOneLine(user->username));
|
||||
if (App::main()) App::main()->peerUpdated(user);
|
||||
}
|
||||
}
|
||||
@@ -911,14 +960,6 @@ namespace App {
|
||||
return result;
|
||||
}
|
||||
|
||||
void forgetPhotos() {
|
||||
lastPhotos.clear();
|
||||
lastPhotosMap.clear();
|
||||
for (PhotosData::const_iterator i = photosData.cbegin(), e = photosData.cend(); i != e; ++i) {
|
||||
i.value()->forget();
|
||||
}
|
||||
}
|
||||
|
||||
VideoData *video(const VideoId &video, VideoData *convert, const uint64 &access, int32 user, int32 date, int32 duration, int32 w, int32 h, const ImagePtr &thumb, int32 dc, int32 size) {
|
||||
if (convert) {
|
||||
if (convert->id != video) {
|
||||
@@ -967,12 +1008,6 @@ namespace App {
|
||||
return result;
|
||||
}
|
||||
|
||||
void forgetVideos() {
|
||||
for (VideosData::const_iterator i = videosData.cbegin(), e = videosData.cend(); i != e; ++i) {
|
||||
i.value()->forget();
|
||||
}
|
||||
}
|
||||
|
||||
AudioData *audio(const AudioId &audio, AudioData *convert, const uint64 &access, int32 user, int32 date, int32 duration, int32 dc, int32 size) {
|
||||
if (convert) {
|
||||
if (convert->id != audio) {
|
||||
@@ -1015,12 +1050,6 @@ namespace App {
|
||||
return result;
|
||||
}
|
||||
|
||||
void forgetAudios() {
|
||||
for (AudiosData::const_iterator i = audiosData.cbegin(), e = audiosData.cend(); i != e; ++i) {
|
||||
i.value()->forget();
|
||||
}
|
||||
}
|
||||
|
||||
DocumentData *document(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 user, int32 date, const QString &name, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) {
|
||||
if (convert) {
|
||||
if (convert->id != document) {
|
||||
@@ -1067,10 +1096,38 @@ namespace App {
|
||||
return result;
|
||||
}
|
||||
|
||||
void forgetDocuments() {
|
||||
ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type, const QString &url) {
|
||||
ImageLinksData::const_iterator i = imageLinksData.constFind(imageLink);
|
||||
ImageLinkData *result;
|
||||
if (i == imageLinksData.cend()) {
|
||||
result = new ImageLinkData(imageLink);
|
||||
imageLinksData.insert(imageLink, result);
|
||||
result->type = type;
|
||||
result->openl = TextLinkPtr(new TextLink(url));
|
||||
} else {
|
||||
result = i.value();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void forgetMedia() {
|
||||
lastPhotos.clear();
|
||||
lastPhotosMap.clear();
|
||||
for (PhotosData::const_iterator i = photosData.cbegin(), e = photosData.cend(); i != e; ++i) {
|
||||
i.value()->forget();
|
||||
}
|
||||
for (VideosData::const_iterator i = videosData.cbegin(), e = videosData.cend(); i != e; ++i) {
|
||||
i.value()->forget();
|
||||
}
|
||||
for (AudiosData::const_iterator i = audiosData.cbegin(), e = audiosData.cend(); i != e; ++i) {
|
||||
i.value()->forget();
|
||||
}
|
||||
for (DocumentsData::const_iterator i = documentsData.cbegin(), e = documentsData.cend(); i != e; ++i) {
|
||||
i.value()->forget();
|
||||
}
|
||||
for (ImageLinksData::const_iterator i = imageLinksData.cbegin(), e = imageLinksData.cend(); i != e; ++i) {
|
||||
i.value()->thumb->forget();
|
||||
}
|
||||
}
|
||||
|
||||
MTPPhoto photoFromUserPhoto(MTPint userId, MTPint date, const MTPUserProfilePhoto &photo) {
|
||||
@@ -1175,7 +1232,9 @@ namespace App {
|
||||
}
|
||||
}
|
||||
historyItemDetached(item);
|
||||
if (App::main()) App::main()->itemRemoved(item);
|
||||
if (App::main() && !App::quiting()) {
|
||||
App::main()->itemRemoved(item);
|
||||
}
|
||||
}
|
||||
|
||||
void historyClearMsgs() {
|
||||
@@ -1222,6 +1281,7 @@ namespace App {
|
||||
lastPhotos.clear();
|
||||
lastPhotosMap.clear();
|
||||
::self = 0;
|
||||
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
||||
}
|
||||
/* // don't delete history without deleting its' peerdata
|
||||
void deleteHistory(const PeerId &peer) {
|
||||
@@ -1852,17 +1912,14 @@ namespace App {
|
||||
void checkImageCacheSize() {
|
||||
int64 nowImageCacheSize = imageCacheSize();
|
||||
if (nowImageCacheSize > serviceImageCacheSize + MemoryForImageCache) {
|
||||
App::forgetPhotos();
|
||||
App::forgetVideos();
|
||||
App::forgetAudios();
|
||||
App::forgetDocuments();
|
||||
App::forgetMedia();
|
||||
serviceImageCacheSize = imageCacheSize();
|
||||
}
|
||||
}
|
||||
|
||||
bool isValidPhone(QString phone) {
|
||||
phone = phone.replace(QRegularExpression(qsl("[^\\d]")), QString());
|
||||
return phone.length() >= 8 || phone == qsl("777") || phone == qsl("333") || phone == qsl("111") || (phone.startsWith(qsl("42")) && (phone.length() == 2 || phone.length() == 5));
|
||||
return phone.length() >= 8 || phone == qsl("777") || phone == qsl("333") || phone == qsl("111") || (phone.startsWith(qsl("42")) && (phone.length() == 2 || phone.length() == 5 || phone == qsl("4242")));
|
||||
}
|
||||
|
||||
void quit() {
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace App {
|
||||
int32 userFromPeer(const PeerId &peer_id);
|
||||
int32 chatFromPeer(const PeerId &peer_id);
|
||||
|
||||
int32 onlineForSort(int32 online, int32 now);
|
||||
int32 onlineWillChangeIn(int32 onlineOnServer, int32 nowOnServer);
|
||||
QString onlineText(int32 onlineOnServer, int32 nowOnServer, bool precise = false);
|
||||
|
||||
@@ -102,13 +103,11 @@ namespace App {
|
||||
ChatData *chat(int32 chat);
|
||||
QString peerName(const PeerData *peer, bool forDialogs = false);
|
||||
PhotoData *photo(const PhotoId &photo, PhotoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr());
|
||||
void forgetPhotos();
|
||||
VideoData *video(const VideoId &video, VideoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
|
||||
void forgetVideos();
|
||||
AudioData *audio(const AudioId &audio, AudioData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 dc = 0, int32 size = 0);
|
||||
void forgetAudios();
|
||||
DocumentData *document(const DocumentId &document, DocumentData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const QString &name = QString(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
|
||||
void forgetDocuments();
|
||||
ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type = InvalidImageLink, const QString &url = QString());
|
||||
void forgetMedia();
|
||||
|
||||
MTPPhoto photoFromUserPhoto(MTPint userId, MTPint date, const MTPUserProfilePhoto &photo);
|
||||
|
||||
|
||||
@@ -139,8 +139,6 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
|
||||
|
||||
psInstallEventFilter();
|
||||
|
||||
updateCheckTimer.setSingleShot(true);
|
||||
|
||||
connect(&socket, SIGNAL(connected()), this, SLOT(socketConnected()));
|
||||
connect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
|
||||
connect(&socket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(socketError(QLocalSocket::LocalSocketError)));
|
||||
@@ -151,9 +149,12 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
|
||||
connect(&updateCheckTimer, SIGNAL(timeout()), this, SLOT(startUpdateCheck()));
|
||||
connect(this, SIGNAL(updateFailed()), this, SLOT(onUpdateFailed()));
|
||||
connect(this, SIGNAL(updateReady()), this, SLOT(onUpdateReady()));
|
||||
connect(this, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(onAppStateChanged(Qt::ApplicationState)));
|
||||
connect(&writeUserConfigTimer, SIGNAL(timeout()), this, SLOT(onWriteUserConfig()));
|
||||
writeUserConfigTimer.setSingleShot(true);
|
||||
|
||||
connect(&killDownloadSessionsTimer, SIGNAL(timeout()), this, SLOT(killDownloadSessions()));
|
||||
|
||||
if (cManyInstance()) {
|
||||
startApp();
|
||||
} else {
|
||||
@@ -326,10 +327,54 @@ void Application::writeUserConfigIn(uint64 ms) {
|
||||
}
|
||||
}
|
||||
|
||||
void Application::killDownloadSessionsStart(int32 dc) {
|
||||
if (killDownloadSessionTimes.constFind(dc) == killDownloadSessionTimes.cend()) {
|
||||
killDownloadSessionTimes.insert(dc, getms() + MTPAckSendWaiting + MTPKillFileSessionTimeout);
|
||||
}
|
||||
if (!killDownloadSessionsTimer.isActive()) {
|
||||
killDownloadSessionsTimer.start(MTPAckSendWaiting + MTPKillFileSessionTimeout + 5);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::killDownloadSessionsStop(int32 dc) {
|
||||
killDownloadSessionTimes.remove(dc);
|
||||
if (killDownloadSessionTimes.isEmpty() && killDownloadSessionsTimer.isActive()) {
|
||||
killDownloadSessionsTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::checkLocalTime() {
|
||||
if (App::main()) App::main()->checkLastUpdate(checkms());
|
||||
}
|
||||
|
||||
void Application::onWriteUserConfig() {
|
||||
App::writeUserConfig();
|
||||
}
|
||||
|
||||
void Application::onAppStateChanged(Qt::ApplicationState state) {
|
||||
checkLocalTime();
|
||||
}
|
||||
|
||||
void Application::killDownloadSessions() {
|
||||
uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout;
|
||||
for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {
|
||||
if (i.value() <= ms) {
|
||||
for (int j = 1; j < MTPDownloadSessionsCount; ++j) {
|
||||
MTP::killSession(MTP::dld[j] + i.key());
|
||||
}
|
||||
i = killDownloadSessionTimes.erase(i);
|
||||
} else {
|
||||
if (i.value() - ms < left) {
|
||||
left = i.value() - ms;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (!killDownloadSessionTimes.isEmpty()) {
|
||||
killDownloadSessionsTimer.start(left);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::photoUpdated(MsgId msgId, const MTPInputFile &file) {
|
||||
if (!App::self()) return;
|
||||
|
||||
@@ -339,7 +384,8 @@ void Application::photoUpdated(MsgId msgId, const MTPInputFile &file) {
|
||||
if (peer == App::self()->id) {
|
||||
MTP::send(MTPphotos_UploadProfilePhoto(file, MTP_string(""), MTP_inputGeoPointEmpty(), MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100))), rpcDone(&Application::selfPhotoDone), rpcFail(&Application::peerPhotoFail, peer));
|
||||
} else {
|
||||
MTP::send(MTPmessages_EditChatPhoto(MTP_int(peer & 0xFFFFFFFF), MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&Application::chatPhotoDone, peer), rpcFail(&Application::peerPhotoFail, peer));
|
||||
History *hist = App::history(peer);
|
||||
hist->sendRequestId = MTP::send(MTPmessages_EditChatPhoto(MTP_int(peer & 0xFFFFFFFF), MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&Application::chatPhotoDone, peer), rpcFail(&Application::peerPhotoFail, peer), 0, 0, hist->sendRequestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,7 +452,7 @@ void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId)
|
||||
int32 filesize = 0;
|
||||
QByteArray data;
|
||||
|
||||
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg);
|
||||
ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false);
|
||||
|
||||
connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), App::app(), SLOT(photoUpdated(MsgId, const MTPInputFile &)), Qt::UniqueConnection);
|
||||
|
||||
@@ -587,12 +633,12 @@ void Application::startApp() {
|
||||
|
||||
readSupportTemplates();
|
||||
|
||||
MTP::setLayer(mtpLayerMax);
|
||||
MTP::start();
|
||||
|
||||
MTP::setStateChangedHandler(mtpStateChanged);
|
||||
MTP::setSessionResetHandler(mtpSessionReset);
|
||||
|
||||
initImageLinkManager();
|
||||
App::initMedia();
|
||||
|
||||
if (MTP::authedId()) {
|
||||
@@ -706,6 +752,7 @@ Application::~Application() {
|
||||
socket.close();
|
||||
closeApplication();
|
||||
App::deinitMedia();
|
||||
deinitImageLinkManager();
|
||||
mainApp = 0;
|
||||
delete updateReply;
|
||||
delete ::uploader;
|
||||
|
||||
@@ -70,6 +70,11 @@ public:
|
||||
|
||||
void writeUserConfigIn(uint64 ms);
|
||||
|
||||
void killDownloadSessionsStart(int32 dc);
|
||||
void killDownloadSessionsStop(int32 dc);
|
||||
|
||||
void checkLocalTime();
|
||||
|
||||
signals:
|
||||
|
||||
void peerPhotoDone(PeerId peer);
|
||||
@@ -100,10 +105,16 @@ public slots:
|
||||
void onEnableDebugMode();
|
||||
void onWriteUserConfig();
|
||||
|
||||
void killDownloadSessions();
|
||||
void onAppStateChanged(Qt::ApplicationState state);
|
||||
|
||||
private:
|
||||
|
||||
QMap<MsgId, PeerId> photoUpdates;
|
||||
|
||||
QMap<int32, uint64> killDownloadSessionTimes;
|
||||
SingleTimer killDownloadSessionsTimer;
|
||||
|
||||
void startApp();
|
||||
|
||||
typedef QPair<QLocalSocket*, QByteArray> ClientSocket;
|
||||
@@ -123,10 +134,10 @@ private:
|
||||
mtpRequestId updateRequestId;
|
||||
QNetworkAccessManager updateManager;
|
||||
QNetworkReply *updateReply;
|
||||
QTimer updateCheckTimer;
|
||||
SingleTimer updateCheckTimer;
|
||||
QThread *updateThread;
|
||||
PsUpdateDownloader *updateDownloader;
|
||||
|
||||
QTimer writeUserConfigTimer;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 528 KiB After Width: | Height: | Size: 528 KiB |
|
Before Width: | Height: | Size: 722 KiB After Width: | Height: | Size: 722 KiB |
|
Before Width: | Height: | Size: 1003 KiB After Width: | Height: | Size: 1003 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
BIN
Telegram/SourceFiles/art/iconbig128.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
Telegram/SourceFiles/art/osxsetup.tif
Normal file
BIN
Telegram/SourceFiles/art/osxsetup.tiff
Normal file
BIN
Telegram/SourceFiles/art/osxsetup@2x.tif
Normal file
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 97 KiB |
@@ -348,7 +348,6 @@ void VoiceMessagesFader::onTimer() {
|
||||
VoiceMessages *voice = audioVoice();
|
||||
if (!voice) return;
|
||||
|
||||
uint64 ms = getms();
|
||||
for (int32 i = 0; i < AudioVoiceMsgSimultaneously; ++i) {
|
||||
VoiceMessages::Msg &m(voice->_data[i]);
|
||||
if (m.state == VoiceMessageStopped || m.state == VoiceMessagePaused || !m.source) continue;
|
||||
|
||||
@@ -251,15 +251,16 @@ void AddContactBox::onSend() {
|
||||
}
|
||||
|
||||
void AddContactBox::onSaveSelfDone(const MTPUser &user) {
|
||||
App::feedUsers(MTP_vector<MTPUser>(QVector<MTPUser>(1, user)));
|
||||
App::feedUsers(MTP_vector<MTPUser>(1, user));
|
||||
emit closed();
|
||||
}
|
||||
|
||||
bool AddContactBox::onSaveSelfFail(const RPCError &error) {
|
||||
_addRequest = 0;
|
||||
QString err(error.type());
|
||||
QString firstName = textOneLine(_firstInput.text()), lastName = textOneLine(_lastInput.text());
|
||||
if (err == "NAME_NOT_MODIFIED") {
|
||||
App::self()->setName(firstName, lastName, firstName + ' ' + lastName);
|
||||
App::self()->setName(firstName, lastName, firstName + ' ' + lastName, textOneLine(App::self()->username));
|
||||
emit closed();
|
||||
return true;
|
||||
} else if (err == "FIRSTNAME_INVALID") {
|
||||
@@ -276,10 +277,11 @@ bool AddContactBox::onSaveSelfFail(const RPCError &error) {
|
||||
}
|
||||
|
||||
bool AddContactBox::onSaveFail(const RPCError &error) {
|
||||
_addRequest = 0;
|
||||
QString err(error.type());
|
||||
QString firstName = _firstInput.text().trimmed(), lastName = _lastInput.text().trimmed();
|
||||
if (err == "CHAT_TITLE_NOT_MODIFIED") {
|
||||
_peer->updateName(firstName, QString());
|
||||
_peer->updateName(firstName, QString(), QString());
|
||||
emit closed();
|
||||
return true;
|
||||
} else if (err == "NO_CHAT_TITLE") {
|
||||
|
||||
@@ -58,7 +58,7 @@ private:
|
||||
PeerData *_peer;
|
||||
QString _boxTitle;
|
||||
|
||||
int32 _width, _height, _thumbw, _thumbh;
|
||||
int32 _width, _height;
|
||||
FlatButton _addButton, _retryButton, _cancelButton;
|
||||
FlatInput _firstInput, _lastInput, _phoneInput;
|
||||
|
||||
|
||||
@@ -231,6 +231,7 @@ void AddParticipantInner::mousePressEvent(QMouseEvent *e) {
|
||||
}
|
||||
|
||||
void AddParticipantInner::chooseParticipant() {
|
||||
_time = unixtime();
|
||||
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, from;
|
||||
if (_filter.isEmpty()) {
|
||||
if (!_sel || contactData(_sel)->inchat) return;
|
||||
@@ -293,6 +294,7 @@ void AddParticipantInner::updateSel() {
|
||||
}
|
||||
|
||||
void AddParticipantInner::updateFilter(QString filter) {
|
||||
_time = unixtime();
|
||||
QStringList f;
|
||||
if (!filter.isEmpty()) {
|
||||
QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts);
|
||||
@@ -405,6 +407,7 @@ AddParticipantInner::~AddParticipantInner() {
|
||||
}
|
||||
|
||||
void AddParticipantInner::selectSkip(int32 dir) {
|
||||
_time = unixtime();
|
||||
_mouseSel = false;
|
||||
int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, origDir = dir;
|
||||
if (_filter.isEmpty()) {
|
||||
|
||||
@@ -205,6 +205,7 @@ void ConnectionBox::onSave() {
|
||||
}
|
||||
App::writeConfig();
|
||||
MTP::restart();
|
||||
reinitImageLinkManager();
|
||||
emit closed();
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ a_opacity(0, 1) {
|
||||
|
||||
void PhotoSendBox::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
|
||||
onSend();
|
||||
onSend((e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier)) && e->modifiers().testFlag(Qt::ShiftModifier));
|
||||
} else if (e->key() == Qt::Key_Escape) {
|
||||
onCancel();
|
||||
}
|
||||
@@ -200,18 +200,19 @@ void PhotoSendBox::animStep(float64 ms) {
|
||||
update();
|
||||
}
|
||||
|
||||
void PhotoSendBox::onSend() {
|
||||
void PhotoSendBox::onSend(bool ctrlShiftEnter) {
|
||||
if (!_img) {
|
||||
if (App::main()) App::main()->confirmShareContact(_phone, _fname, _lname);
|
||||
if (App::main()) App::main()->confirmShareContact(ctrlShiftEnter, _phone, _fname, _lname);
|
||||
} else {
|
||||
if (!_compressed.isHidden()) {
|
||||
cSetCompressPastedImage(_compressed.checked());
|
||||
App::writeUserConfig();
|
||||
}
|
||||
if (_compressed.isHidden() || _compressed.checked()) {
|
||||
_img->ctrlShiftEnter = ctrlShiftEnter;
|
||||
if (App::main()) App::main()->confirmSendImage(*_img);
|
||||
} else {
|
||||
if (App::main()) App::main()->confirmSendImageUncompressed();
|
||||
if (App::main()) App::main()->confirmSendImageUncompressed(ctrlShiftEnter);
|
||||
}
|
||||
}
|
||||
emit closed();
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
|
||||
public slots:
|
||||
|
||||
void onSend();
|
||||
void onSend(bool ctrlShiftEnter = false);
|
||||
void onCancel();
|
||||
|
||||
private:
|
||||
|
||||
273
Telegram/SourceFiles/boxes/usernamebox.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
an unofficial desktop messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "lang.h"
|
||||
|
||||
#include "application.h"
|
||||
#include "usernamebox.h"
|
||||
#include "mainwidget.h"
|
||||
#include "window.h"
|
||||
|
||||
UsernameInput::UsernameInput(QWidget *parent, const style::flatInput &st, const QString &ph, const QString &val) : FlatInput(parent, st, ph, val) {
|
||||
}
|
||||
|
||||
void UsernameInput::correctValue(QKeyEvent *e, const QString &was) {
|
||||
QString oldText(text()), newText;
|
||||
int32 oldPos(cursorPosition()), newPos(-1), oldLen(oldText.length());
|
||||
newText.reserve(oldLen);
|
||||
|
||||
for (int32 i = 0; i < oldLen; ++i) {
|
||||
if (i == oldPos) {
|
||||
newPos = newText.length();
|
||||
}
|
||||
|
||||
QChar ch = oldText[i];
|
||||
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || (ch == '@' && !i)) {
|
||||
if (newText.size() < MaxUsernameLength) {
|
||||
newText.append(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newPos < 0) {
|
||||
newPos = newText.length();
|
||||
}
|
||||
if (newText != oldText) {
|
||||
setText(newText);
|
||||
setCursorPosition(newPos);
|
||||
}
|
||||
}
|
||||
|
||||
UsernameBox::UsernameBox() :
|
||||
_saveButton(this, lang(lng_settings_save), st::usernameDone),
|
||||
_cancelButton(this, lang(lng_cancel), st::usernameCancel),
|
||||
_usernameInput(this, st::inpAddContact, qsl("@username"), App::self()->username),
|
||||
_saveRequest(0), _checkRequest(0), _about(st::usernameWidth - 2 * st::addContactTitlePos.x()),
|
||||
a_opacity(0, 1), _hiding(false) {
|
||||
_about.setRichText(st::usernameFont, lang(lng_username_about));
|
||||
initBox();
|
||||
}
|
||||
|
||||
void UsernameBox::initBox() {
|
||||
_width = st::usernameWidth;
|
||||
_height = st::addContactTitleHeight + st::addContactPadding.top() + _usernameInput.height() + st::addContactPadding.bottom() + _about.countHeight(st::usernameWidth - 2 * st::addContactTitlePos.x()) + st::usernameSkip + _saveButton.height();
|
||||
_usernameInput.setGeometry(st::addContactPadding.left(), st::addContactTitleHeight + st::addContactPadding.top(), _width - st::addContactPadding.left() - st::addContactPadding.right(), _usernameInput.height());
|
||||
|
||||
int32 buttonTop = _height - _cancelButton.height();
|
||||
_cancelButton.move(0, buttonTop);
|
||||
_saveButton.move(_width - _saveButton.width(), buttonTop);
|
||||
|
||||
connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSave()));
|
||||
connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel()));
|
||||
connect(&_usernameInput, SIGNAL(changed()), this, SLOT(onChanged()));
|
||||
|
||||
_checkTimer.setSingleShot(true);
|
||||
connect(&_checkTimer, SIGNAL(timeout()), this, SLOT(onCheck()));
|
||||
|
||||
resize(_width, _height);
|
||||
|
||||
showAll();
|
||||
_cache = myGrab(this, rect());
|
||||
hideAll();
|
||||
}
|
||||
|
||||
void UsernameBox::hideAll() {
|
||||
_usernameInput.hide();
|
||||
_saveButton.hide();
|
||||
_cancelButton.hide();
|
||||
}
|
||||
|
||||
void UsernameBox::showAll() {
|
||||
_usernameInput.show();
|
||||
_saveButton.show();
|
||||
_cancelButton.show();
|
||||
}
|
||||
|
||||
void UsernameBox::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
|
||||
onSave();
|
||||
} else if (e->key() == Qt::Key_Escape) {
|
||||
onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
void UsernameBox::parentResized() {
|
||||
QSize s = parentWidget()->size();
|
||||
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
|
||||
update();
|
||||
}
|
||||
|
||||
void UsernameBox::paintEvent(QPaintEvent *e) {
|
||||
QPainter p(this);
|
||||
if (_cache.isNull()) {
|
||||
if (!_hiding || a_opacity.current() > 0.01) {
|
||||
// fill bg
|
||||
p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b);
|
||||
|
||||
// paint shadows
|
||||
p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b);
|
||||
p.fillRect(0, size().height() - st::usernameCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b);
|
||||
|
||||
// paint button sep
|
||||
p.fillRect(st::usernameCancel.width, size().height() - st::usernameCancel.height, st::lineWidth, st::usernameCancel.height, st::btnSelectSep->b);
|
||||
|
||||
// draw box title / text
|
||||
p.setPen(st::black->p);
|
||||
p.setFont(st::addContactTitleFont->f);
|
||||
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_username_title));
|
||||
|
||||
if (!_errorText.isEmpty()) {
|
||||
p.setPen(st::setErrColor->p);
|
||||
p.setFont(st::setErrFont->f);
|
||||
int32 w = st::setErrFont->m.width(_errorText);
|
||||
p.drawText((_width - w) / 2, _usernameInput.y() + _usernameInput.height() + ((st::usernameSkip - st::setErrFont->height) / 2) + st::setErrFont->ascent, _errorText);
|
||||
}
|
||||
p.setPen(st::usernameColor->p);
|
||||
_about.draw(p, st::addContactTitlePos.x(), _usernameInput.y() + _usernameInput.height() + st::usernameSkip, width() - 2 * st::addContactTitlePos.x());
|
||||
}
|
||||
} else {
|
||||
p.setOpacity(a_opacity.current());
|
||||
p.drawPixmap(0, 0, _cache);
|
||||
}
|
||||
}
|
||||
|
||||
void UsernameBox::animStep(float64 dt) {
|
||||
if (dt >= 1) {
|
||||
a_opacity.finish();
|
||||
_cache = QPixmap();
|
||||
if (!_hiding) {
|
||||
showAll();
|
||||
_usernameInput.setFocus();
|
||||
}
|
||||
} else {
|
||||
a_opacity.update(dt, anim::linear);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void UsernameBox::onSave() {
|
||||
if (_saveRequest) return;
|
||||
|
||||
_sentUsername = getName();
|
||||
_saveRequest = MTP::send(MTPaccount_UpdateUsername(MTP_string(_sentUsername)), rpcDone(&UsernameBox::onUpdateDone), rpcFail(&UsernameBox::onUpdateFail));
|
||||
}
|
||||
|
||||
void UsernameBox::onCheck() {
|
||||
if (_checkRequest) {
|
||||
MTP::cancel(_checkRequest);
|
||||
}
|
||||
QString name = getName();
|
||||
if (name.size() >= MinUsernameLength) {
|
||||
_checkUsername = getName();
|
||||
_checkRequest = MTP::send(MTPaccount_CheckUsername(MTP_string(name)), rpcDone(&UsernameBox::onCheckDone), rpcFail(&UsernameBox::onCheckFail));
|
||||
}
|
||||
}
|
||||
|
||||
void UsernameBox::onChanged() {
|
||||
QString name = getName();
|
||||
if (name.isEmpty()) {
|
||||
if (!_errorText.isEmpty()) {
|
||||
_errorText = QString();
|
||||
update();
|
||||
}
|
||||
_checkTimer.stop();
|
||||
} else if (name.size() < MinUsernameLength) {
|
||||
if (_errorText != lang(lng_username_too_short)) {
|
||||
_errorText = lang(lng_username_too_short);
|
||||
update();
|
||||
}
|
||||
_checkTimer.stop();
|
||||
} else {
|
||||
if (!_errorText.isEmpty()) {
|
||||
_errorText = QString();
|
||||
update();
|
||||
}
|
||||
_checkTimer.start(UsernameCheckTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
void UsernameBox::onUpdateDone(const MTPUser &user) {
|
||||
App::feedUsers(MTP_vector<MTPUser>(1, user));
|
||||
emit closed();
|
||||
}
|
||||
|
||||
bool UsernameBox::onUpdateFail(const RPCError &error) {
|
||||
_saveRequest = 0;
|
||||
QString err(error.type()), name = getName();
|
||||
if (err == "USERNAME_NOT_MODIFIED" || _sentUsername == App::self()->username) {
|
||||
App::self()->setName(textOneLine(App::self()->firstName), textOneLine(App::self()->lastName), textOneLine(App::self()->nameOrPhone), textOneLine(name));
|
||||
emit closed();
|
||||
return true;
|
||||
} else if (err == "USERNAME_INVALID") {
|
||||
_usernameInput.setFocus();
|
||||
_usernameInput.notaBene();
|
||||
_errorText = lang(lng_username_invalid);
|
||||
return true;
|
||||
} else if (err == "USERNAME_OCCUPIED" || err == "USERNAMES_UNAVAILABLE") {
|
||||
_usernameInput.setFocus();
|
||||
_usernameInput.notaBene();
|
||||
_errorText = lang(lng_username_occupied);
|
||||
return true;
|
||||
}
|
||||
_usernameInput.setFocus();
|
||||
return true;
|
||||
}
|
||||
|
||||
void UsernameBox::onCheckDone(const MTPBool &result) {
|
||||
_checkRequest = 0;
|
||||
QString newError = (result.v || _checkUsername == App::self()->username) ? QString() : lang(lng_username_occupied);
|
||||
if (_errorText != newError) {
|
||||
_errorText = newError;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
bool UsernameBox::onCheckFail(const RPCError &error) {
|
||||
_checkRequest = 0;
|
||||
QString err(error.type());
|
||||
if (err == "USERNAME_INVALID") {
|
||||
_errorText = lang(lng_username_invalid);
|
||||
update();
|
||||
return true;
|
||||
} else if (err == "USERNAME_OCCUPIED" && _checkUsername != App::self()->username) {
|
||||
_errorText = lang(lng_username_occupied);
|
||||
update();
|
||||
return true;
|
||||
}
|
||||
_usernameInput.setFocus();
|
||||
return true;
|
||||
}
|
||||
|
||||
QString UsernameBox::getName() const {
|
||||
return _usernameInput.text().replace('@', QString()).trimmed();
|
||||
}
|
||||
|
||||
void UsernameBox::onCancel() {
|
||||
emit closed();
|
||||
}
|
||||
|
||||
void UsernameBox::startHide() {
|
||||
_hiding = true;
|
||||
if (_cache.isNull()) {
|
||||
_cache = myGrab(this, rect());
|
||||
hideAll();
|
||||
}
|
||||
a_opacity.start(0);
|
||||
}
|
||||
|
||||
UsernameBox::~UsernameBox() {
|
||||
}
|
||||
82
Telegram/SourceFiles/boxes/usernamebox.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
an unofficial desktop messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "layerwidget.h"
|
||||
|
||||
class UsernameInput : public FlatInput {
|
||||
public:
|
||||
|
||||
UsernameInput(QWidget *parent, const style::flatInput &st, const QString &ph = QString(), const QString &val = QString());
|
||||
|
||||
protected:
|
||||
|
||||
void correctValue(QKeyEvent *e, const QString &was);
|
||||
|
||||
};
|
||||
|
||||
class UsernameBox : public LayeredWidget, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
UsernameBox();
|
||||
void parentResized();
|
||||
void animStep(float64 dt);
|
||||
void keyPressEvent(QKeyEvent *e);
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void startHide();
|
||||
~UsernameBox();
|
||||
|
||||
public slots:
|
||||
|
||||
void onSave();
|
||||
void onCancel();
|
||||
|
||||
void onCheck();
|
||||
void onChanged();
|
||||
|
||||
private:
|
||||
|
||||
void hideAll();
|
||||
void showAll();
|
||||
|
||||
void onUpdateDone(const MTPUser &result);
|
||||
bool onUpdateFail(const RPCError &error);
|
||||
|
||||
void onCheckDone(const MTPBool &result);
|
||||
bool onCheckFail(const RPCError &error);
|
||||
|
||||
QString getName() const;
|
||||
void initBox();
|
||||
|
||||
int32 _width, _height;
|
||||
FlatButton _saveButton, _cancelButton;
|
||||
UsernameInput _usernameInput;
|
||||
|
||||
QPixmap _cache;
|
||||
|
||||
mtpRequestId _saveRequest, _checkRequest;
|
||||
QString _sentUsername, _checkUsername, _errorText;
|
||||
|
||||
Text _about;
|
||||
QTimer _checkTimer;
|
||||
|
||||
anim::fvalue a_opacity;
|
||||
bool _hiding;
|
||||
};
|
||||
@@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
static const int32 AppVersion = 6003;
|
||||
static const wchar_t *AppVersionStr = L"0.6.3";
|
||||
static const int32 AppVersion = 6011;
|
||||
static const wchar_t *AppVersionStr = L"0.6.11";
|
||||
|
||||
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
|
||||
static const wchar_t *AppName = L"Telegram Desktop";
|
||||
@@ -32,18 +32,25 @@ enum {
|
||||
MTPShortBufferSize = 65535, // of ints, 256 kb
|
||||
MTPPacketSizeMax = 67108864, // 64 mb
|
||||
MTPIdsBufferSize = 400, // received msgIds and wereAcked msgIds count stored
|
||||
MTPCheckResendTimeout = 5000, // how much time passed from send till we resend request or check it's state, in ms
|
||||
MTPCheckResendTimeout = 10000, // how much time passed from send till we resend request or check it's state, in ms
|
||||
MTPCheckResendWaiting = 1000, // how much time to wait for some more requests, when resending request or checking it's state, in ms
|
||||
MTPAckSendWaiting = 10000, // how much time to wait for some more requests, when sending msg acks
|
||||
MTPResendThreshold = 1, // how much ints should message contain for us not to resend, but to check it's state
|
||||
MTPContainerLives = 600, // container lives 10 minutes in haveSent map
|
||||
MTPMinReceiveDelay = 4000, // 4 seconds
|
||||
MTPMaxReceiveDelay = 64000, // 64 seconds
|
||||
MTPConnectionOldTimeout = 192000, // 192 seconds
|
||||
MTPTcpConnectionWaitTimeout = 3000, // 3 seconds waiting for tcp, until we accept http
|
||||
MTPMillerRabinIterCount = 30, // 30 Miller-Rabin iterations for dh_prime primality check
|
||||
|
||||
MTPUploadSessionsCount = 4, // max 4 upload sessions is created
|
||||
MTPDownloadSessionsCount = 4, // max 4 download sessions is created
|
||||
MTPKillFileSessionTimeout = 5000, // how much time without upload / download causes additional session kill
|
||||
|
||||
MTPEnumDCTimeout = 4000, // 4 seconds timeout for help_getConfig to work (them move to other dc)
|
||||
|
||||
MinReceiveDelay = 1000, // 1 seconds
|
||||
MTPDebugBufferSize = 1024 * 1024, // 1 mb start size
|
||||
|
||||
MaxSelectedItems = 100,
|
||||
|
||||
MaxPhoneTailLength = 18, // rest of the phone number, without country code (seen 12 at least)
|
||||
@@ -84,6 +91,16 @@ enum {
|
||||
MaxZoomLevel = 7, // x8
|
||||
|
||||
PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request
|
||||
EmojiPadPerRow = 7,
|
||||
EmojiPadRowsPerPage = 6,
|
||||
|
||||
SearchPeopleLimit = 5,
|
||||
MinUsernameLength = 5,
|
||||
MaxUsernameLength = 32,
|
||||
UsernameCheckTimeout = 200,
|
||||
|
||||
MaxMessageSize = 4096,
|
||||
MaxHttpRedirects = 5, // when getting external data/images
|
||||
};
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -200,12 +217,12 @@ enum {
|
||||
|
||||
LinkCropLimit = 360, // 360px link length max
|
||||
|
||||
DownloadPartSize = 32 * 1024, // 32kb for photo
|
||||
DownloadPartSize = 64 * 1024, // 64kb for photo
|
||||
DocumentDownloadPartSize = 128 * 1024, // 128kb for document
|
||||
MaxUploadPhotoSize = 10 * 1024 * 1024, // 10mb photos max
|
||||
MaxUploadDocumentSize = 1500 * 1024 * 1024, // 1500mb documents max
|
||||
UseBigFilesFrom = 10 * 1024 * 1024, // mtp big files methods used for files greater than 10mb
|
||||
MaxFileQueries = 32, // max 32 file parts downloaded at the same time
|
||||
MaxFileQueries = 16, // max 16 file parts downloaded at the same time
|
||||
|
||||
UploadPartSize = 32 * 1024, // 32kb for photo
|
||||
DocumentMaxPartsCount = 3000, // no more than 3000 parts
|
||||
@@ -214,11 +231,13 @@ enum {
|
||||
DocumentUploadPartSize2 = 128 * 1024, // 128kb for small document ( <= 375mb )
|
||||
DocumentUploadPartSize3 = 256 * 1024, // 256kb for medium document ( <= 750mb )
|
||||
DocumentUploadPartSize4 = 512 * 1024, // 512kb for large document ( <= 1500mb )
|
||||
MaxUploadFileParallelSize = 512 * 1024, // max 512kb uploaded at the same time
|
||||
MaxUploadFileParallelSize = MTPUploadSessionsCount * 512 * 1024, // max 512kb uploaded at the same time in each session
|
||||
UploadRequestInterval = 500, // one part each half second, if not uploaded faster
|
||||
|
||||
MaxPhotosInMemory = 50, // try to clear some memory after 50 photos are created
|
||||
NoUpdatesTimeout = 180 * 1000, // if nothing is received in 3 min we reconnect
|
||||
NoUpdatesTimeout = 180 * 1000, // if nothing is received in 3 min we getDifference
|
||||
NoUpdatesAfterSleepTimeout = 60 * 1000, // if nothing is received in 1 min when was a sleepmode we getDifference
|
||||
WaitForSeqTimeout = 1000, // 1s wait for skipped seq in updates
|
||||
|
||||
MemoryForImageCache = 64 * 1024 * 1024, // after 64mb of unpacked images we try to clear some memory
|
||||
NotifyWindowsCount = 3, // 3 desktop notifies at the same time
|
||||
|
||||
@@ -26,7 +26,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#include "boxes/newgroupbox.h"
|
||||
|
||||
DialogsListWidget::DialogsListWidget(QWidget *parent, MainWidget *main) : QWidget(parent),
|
||||
dialogs(false), contactsNoDialogs(true), contacts(true), sel(0), contactSel(false), selByMouse(false), filteredSel(-1), searchedCount(0), searchedSel(-1), _lastSearchId(0), _state(DefaultState) {
|
||||
dialogs(false), contactsNoDialogs(true), contacts(true), sel(0), contactSel(false), selByMouse(false), filteredSel(-1), searchedCount(0), searchedSel(-1), peopleSel(-1), _lastSearchId(0), _state(DefaultState) {
|
||||
connect(main, SIGNAL(dialogToTop(const History::DialogLinks &)), this, SLOT(onDialogToTop(const History::DialogLinks &)));
|
||||
connect(main, SIGNAL(peerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)), this, SLOT(onPeerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)));
|
||||
connect(main, SIGNAL(peerPhotoChanged(PeerData *)), this, SLOT(onPeerPhotoChanged(PeerData *)));
|
||||
@@ -76,6 +76,33 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!peopleResults.isEmpty()) {
|
||||
p.fillRect(0, 0, width(), st::searchedBarHeight, st::searchedBarBG->b);
|
||||
p.setFont(st::searchedBarFont->f);
|
||||
p.setPen(st::searchedBarColor->p);
|
||||
p.drawText(QRect(0, 0, width(), st::searchedBarHeight), lang(lng_search_global_results), style::al_center);
|
||||
p.translate(0, st::searchedBarHeight);
|
||||
|
||||
int32 skip = filterResults.size() * st::dlgHeight + st::searchedBarHeight;
|
||||
int32 from = (r.top() - skip) / int32(st::dlgHeight);
|
||||
if (from < 0) {
|
||||
from = 0;
|
||||
} else if (from > peopleResults.size()) {
|
||||
from = peopleResults.size();
|
||||
}
|
||||
p.translate(0, from * st::dlgHeight);
|
||||
if (from < peopleResults.size()) {
|
||||
int32 to = ((r.bottom() - skip) / int32(st::dlgHeight)) + 1, w = width();
|
||||
if (to > peopleResults.size()) to = peopleResults.size();
|
||||
for (; from < to; ++from) {
|
||||
bool active = (peopleResults[from] == App::main()->activePeer() && !App::main()->activeMsgId());
|
||||
bool selected = (from == peopleSel);
|
||||
peopleResultPaint(peopleResults[from], p, w, active, selected);
|
||||
p.translate(0, st::dlgHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_state == SearchedState || !searchResults.isEmpty()) {
|
||||
QString text = searchResults.isEmpty() ? lang(lng_search_no_results) : lang(searchedCount > 1 ? lng_search_n_results : lng_search_one_result).replace(qsl("{count}"), QString::number(searchedCount));
|
||||
p.fillRect(0, 0, width(), st::searchedBarHeight, st::searchedBarBG->b);
|
||||
@@ -85,13 +112,17 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||
p.translate(0, st::searchedBarHeight);
|
||||
|
||||
int32 skip = filterResults.size() * st::dlgHeight + st::searchedBarHeight;
|
||||
if (!peopleResults.isEmpty()) skip += peopleResults.size() * st::dlgHeight + st::searchedBarHeight;
|
||||
int32 from = (r.top() - skip) / int32(st::dlgHeight);
|
||||
if (from < 0) from = 0;
|
||||
if (from < 0) {
|
||||
from = 0;
|
||||
} else if (from > searchResults.size()) {
|
||||
from = searchResults.size();
|
||||
}
|
||||
p.translate(0, from * st::dlgHeight);
|
||||
if (from < searchResults.size()) {
|
||||
int32 to = ((r.bottom() - skip) / int32(st::dlgHeight)) + 1, w = width();
|
||||
if (to > searchResults.size()) to = searchResults.size();
|
||||
|
||||
p.translate(0, from * st::dlgHeight);
|
||||
for (; from < to; ++from) {
|
||||
bool active = (searchResults[from]->_item->id == App::main()->activeMsgId());
|
||||
bool selected = (from == searchedSel);
|
||||
@@ -103,6 +134,47 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void DialogsListWidget::peopleResultPaint(UserData *user, QPainter &p, int32 w, bool act, bool sel) const {
|
||||
QRect fullRect(0, 0, w, st::dlgHeight);
|
||||
p.fillRect(fullRect, (act ? st::dlgActiveBG : (sel ? st::dlgHoverBG : st::dlgBG))->b);
|
||||
|
||||
History *history = App::history(user->id);
|
||||
|
||||
p.drawPixmap(st::dlgPaddingHor, st::dlgPaddingVer, history->peer->photo->pix(st::dlgPhotoSize));
|
||||
|
||||
int32 nameleft = st::dlgPaddingHor + st::dlgPhotoSize + st::dlgPhotoPadding;
|
||||
int32 namewidth = w - nameleft - st::dlgPaddingHor;
|
||||
QRect rectForName(nameleft, st::dlgPaddingVer + st::dlgNameTop, namewidth, st::msgNameFont->height);
|
||||
|
||||
// draw chat icon
|
||||
if (history->peer->chat) {
|
||||
p.drawPixmap(QPoint(rectForName.left() + st::dlgChatImgLeft, rectForName.top() + st::dlgChatImgTop), App::sprite(), (act ? st::dlgActiveChatImg : st::dlgChatImg));
|
||||
rectForName.setLeft(rectForName.left() + st::dlgChatImgSkip);
|
||||
}
|
||||
|
||||
QRect tr(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, namewidth, st::dlgFont->height);
|
||||
p.setFont(st::dlgHistFont->f);
|
||||
if (!act && user->username.toLower().startsWith(peopleQuery)) {
|
||||
QString first = '@' + user->username.mid(0, peopleQuery.size()), second = user->username.mid(peopleQuery.size());
|
||||
int32 w = st::dlgHistFont->m.width(first);
|
||||
if (w >= tr.width()) {
|
||||
p.setPen(st::dlgSystemColor->p);
|
||||
p.drawText(tr.left(), tr.top() + st::dlgHistFont->ascent, st::dlgHistFont->m.elidedText(first, Qt::ElideRight, tr.width()));
|
||||
} else {
|
||||
p.setPen(st::dlgSystemColor->p);
|
||||
p.drawText(tr.left(), tr.top() + st::dlgHistFont->ascent, first);
|
||||
p.setPen(st::dlgTextColor->p);
|
||||
p.drawText(tr.left() + w, tr.top() + st::dlgHistFont->ascent, st::dlgHistFont->m.elidedText(second, Qt::ElideRight, tr.width() - w));
|
||||
}
|
||||
} else {
|
||||
p.setPen((act ? st::dlgActiveColor : st::dlgSystemColor)->p);
|
||||
p.drawText(tr.left(), tr.top() + st::dlgHistFont->ascent, st::dlgHistFont->m.elidedText('@' + user->username, Qt::ElideRight, tr.width()));
|
||||
}
|
||||
|
||||
p.setPen((act ? st::dlgActiveColor : st::dlgNameColor)->p);
|
||||
history->nameText.drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
|
||||
}
|
||||
|
||||
void DialogsListWidget::activate() {
|
||||
if (_state == DefaultState && !sel) {
|
||||
selectSkip(1);
|
||||
@@ -147,9 +219,21 @@ void DialogsListWidget::onUpdateSelected(bool force) {
|
||||
parentWidget()->update();
|
||||
}
|
||||
}
|
||||
mouseY -= filterResults.size() * st::dlgHeight + st::searchedBarHeight;
|
||||
if (!peopleResults.isEmpty()) {
|
||||
int32 newPeopleSel = (mouseY >= 0) ? (mouseY / int32(st::dlgHeight)) : -1;
|
||||
if (newPeopleSel < 0 || newPeopleSel >= peopleResults.size()) {
|
||||
newPeopleSel = -1;
|
||||
}
|
||||
if (newPeopleSel != peopleSel) {
|
||||
peopleSel = newPeopleSel;
|
||||
setCursor((peopleSel >= 0) ? style::cur_pointer : style::cur_default);
|
||||
parentWidget()->update();
|
||||
}
|
||||
mouseY -= peopleResults.size() * st::dlgHeight + st::searchedBarHeight;
|
||||
}
|
||||
if (_state == SearchedState && !searchResults.isEmpty()) {
|
||||
mouseY -= filterResults.size() * st::dlgHeight + st::searchedBarHeight;
|
||||
int32 newSearchedSel = (mouseY >= 0) ? mouseY / int32(st::dlgHeight) : -1;
|
||||
int32 newSearchedSel = (mouseY >= 0) ? (mouseY / int32(st::dlgHeight)) : -1;
|
||||
if (newSearchedSel < 0 || newSearchedSel >= searchResults.size()) {
|
||||
newSearchedSel = -1;
|
||||
}
|
||||
@@ -276,8 +360,18 @@ void DialogsListWidget::dlgUpdated(History *history) {
|
||||
}
|
||||
++cnt;
|
||||
}
|
||||
if (!searchResults.isEmpty()) {
|
||||
if (!peopleResults.isEmpty()) {
|
||||
int32 cnt = 0, add = filterResults.size() * st::dlgHeight + st::searchedBarHeight;
|
||||
for (PeopleResults::const_iterator i = peopleResults.cbegin(), e = peopleResults.cend(); i != e; ++i) {
|
||||
if ((*i) == history->peer) {
|
||||
update(0, add + cnt * st::dlgHeight, width(), st::dlgHeight);
|
||||
break;
|
||||
}
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
if (!searchResults.isEmpty()) {
|
||||
int32 cnt = 0, add = (filterResults.size() + peopleResults.size()) * st::dlgHeight + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight;
|
||||
for (SearchResults::const_iterator i = searchResults.cbegin(), e = searchResults.cend(); i != e; ++i) {
|
||||
if ((*i)->_item->history() == history) {
|
||||
update(0, add + cnt * st::dlgHeight, width(), st::dlgHeight);
|
||||
@@ -352,6 +446,7 @@ void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) {
|
||||
if (filter.isEmpty()) {
|
||||
_state = DefaultState;
|
||||
filterResults.clear();
|
||||
peopleResults.clear();
|
||||
searchResults.clear();
|
||||
_lastSearchId = 0;
|
||||
} else {
|
||||
@@ -441,7 +536,8 @@ DialogsListWidget::~DialogsListWidget() {
|
||||
clearSearchResults();
|
||||
}
|
||||
|
||||
void DialogsListWidget::clearSearchResults() {
|
||||
void DialogsListWidget::clearSearchResults(bool clearPeople) {
|
||||
if (clearPeople) peopleResults.clear();
|
||||
if (!searchResults.isEmpty()) {
|
||||
for (SearchResults::const_iterator i = searchResults.cbegin(), e = searchResults.cend(); i != e; ++i) {
|
||||
delete *i;
|
||||
@@ -490,7 +586,7 @@ void DialogsListWidget::dialogsReceived(const QVector<MTPDialog> &added) {
|
||||
|
||||
void DialogsListWidget::searchReceived(const QVector<MTPMessage> &messages, bool fromStart, int32 fullCount) {
|
||||
if (fromStart) {
|
||||
clearSearchResults();
|
||||
clearSearchResults(false);
|
||||
}
|
||||
for (QVector<MTPMessage>::const_iterator i = messages.cbegin(), e = messages.cend(); i != e; ++i) {
|
||||
HistoryItem *item = App::histories().addToBack(*i, -1);
|
||||
@@ -504,6 +600,20 @@ void DialogsListWidget::searchReceived(const QVector<MTPMessage> &messages, bool
|
||||
refresh();
|
||||
}
|
||||
|
||||
void DialogsListWidget::peopleReceived(const QString &query, const QVector<MTPContactFound> &people) {
|
||||
peopleQuery = query.toLower().trimmed();
|
||||
peopleResults.clear();
|
||||
peopleResults.reserve(people.size());
|
||||
for (QVector<MTPContactFound>::const_iterator i = people.cbegin(), e = people.cend(); i != e; ++i) {
|
||||
int32 uid = i->c_contactFound().vuser_id.v;
|
||||
History *h = App::historyLoaded(uid);
|
||||
if (h && !h->isEmpty()) continue; // skip dialogs
|
||||
|
||||
peopleResults.push_back(App::user(uid));
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
void DialogsListWidget::contactsReceived(const QVector<MTPContact> &contacts) {
|
||||
for (QVector<MTPContact>::const_iterator i = contacts.cbegin(), e = contacts.cend(); i != e; ++i) {
|
||||
addNewContact(i->c_contact().vuser_id.v);
|
||||
@@ -543,9 +653,9 @@ void DialogsListWidget::refresh(bool toTop) {
|
||||
if (_state == DefaultState) {
|
||||
h = (dialogs.list.count + contactsNoDialogs.list.count) * st::dlgHeight;
|
||||
} else if (_state == FilteredState) {
|
||||
h = (filterResults.count() + searchResults.count()) * st::dlgHeight + (searchResults.isEmpty() ? 0 : st::searchedBarHeight);
|
||||
h = (filterResults.count() + peopleResults.count() + searchResults.count()) * st::dlgHeight + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + (searchResults.isEmpty() ? 0 : st::searchedBarHeight);
|
||||
} else if (_state == SearchedState) {
|
||||
h = (filterResults.count() + searchResults.count()) * st::dlgHeight + st::searchedBarHeight;
|
||||
h = (filterResults.count() + peopleResults.count() + searchResults.count()) * st::dlgHeight + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight;
|
||||
}
|
||||
resize(width(), h);
|
||||
if (toTop) {
|
||||
@@ -562,8 +672,7 @@ void DialogsListWidget::setMouseSel(bool msel, bool toTop) {
|
||||
sel = (dialogs.list.count ? dialogs.list.begin : (contactsNoDialogs.list.count ? contactsNoDialogs.list.begin : 0));
|
||||
contactSel = !dialogs.list.count && contactsNoDialogs.list.count;
|
||||
} else if (_state == FilteredState || _state == SearchedState) { // don't select first elem in search
|
||||
filteredSel = -1;
|
||||
searchedSel = -1;
|
||||
filteredSel = peopleSel = searchedSel = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -572,7 +681,7 @@ void DialogsListWidget::setState(State newState) {
|
||||
_state = newState;
|
||||
if (_state == DefaultState) {
|
||||
clearSearchResults();
|
||||
searchedSel = filteredSel = -1;
|
||||
searchedSel = peopleSel = filteredSel = -1;
|
||||
} else if (_state == DefaultState || _state == SearchedState) {
|
||||
filterResults.clear();
|
||||
filteredSel = -1;
|
||||
@@ -589,6 +698,7 @@ void DialogsListWidget::clearFilter() {
|
||||
if (_state == FilteredState || _state == SearchedState) {
|
||||
_state = DefaultState;
|
||||
filterResults.clear();
|
||||
peopleResults.clear();
|
||||
searchResults.clear();
|
||||
_lastSearchId = 0;
|
||||
filter = QString();
|
||||
@@ -606,7 +716,6 @@ void DialogsListWidget::addDialog(const MTPDdialog &dialog) {
|
||||
}
|
||||
|
||||
void DialogsListWidget::selectSkip(int32 direction) {
|
||||
int32 skipMore = 0;
|
||||
if (_state == DefaultState) {
|
||||
if (!sel) {
|
||||
if (dialogs.list.count && direction > 0) {
|
||||
@@ -634,55 +743,40 @@ void DialogsListWidget::selectSkip(int32 direction) {
|
||||
int32 fromY = (sel->pos + (contactSel ? dialogs.list.count : 0)) * st::dlgHeight;
|
||||
emit mustScrollTo(fromY, fromY + st::dlgHeight);
|
||||
} else if (_state == FilteredState || _state == SearchedState) {
|
||||
if (filterResults.isEmpty() && searchResults.isEmpty()) return;
|
||||
if (filteredSel < 0 || filteredSel >= filterResults.size()) {
|
||||
if (searchedSel < 0 || searchedSel >= searchResults.size()) {
|
||||
if (filterResults.isEmpty()) {
|
||||
searchedSel = 0;
|
||||
} else {
|
||||
filteredSel = 0;
|
||||
}
|
||||
} else if (direction < 0 && !searchedSel && !filterResults.isEmpty()) {
|
||||
searchedSel = -1;
|
||||
filteredSel = filterResults.size() + direction;
|
||||
if (filteredSel < 0) filteredSel = 0;
|
||||
if (filterResults.isEmpty() && peopleResults.isEmpty() && searchResults.isEmpty()) return;
|
||||
if ((filteredSel < 0 || filteredSel >= filterResults.size()) &&
|
||||
(peopleSel < 0 || peopleSel >= peopleResults.size()) &&
|
||||
(searchedSel < 0 || searchedSel >= searchResults.size())) {
|
||||
if (filterResults.isEmpty() && peopleResults.isEmpty()) {
|
||||
searchedSel = 0;
|
||||
} else if (filterResults.isEmpty()) {
|
||||
peopleSel = 0;
|
||||
} else {
|
||||
if (direction < -1 && searchedSel + direction < 0) {
|
||||
skipMore = direction + searchedSel;
|
||||
if (skipMore == direction) {
|
||||
skipMore = 0;
|
||||
} else {
|
||||
direction -= skipMore;
|
||||
}
|
||||
}
|
||||
searchedSel = snap(searchedSel + direction, 0, searchResults.size() - 1);
|
||||
filteredSel = 0;
|
||||
}
|
||||
} else if (direction > 0 && filteredSel == filterResults.size() - 1 && !searchResults.isEmpty()) {
|
||||
filteredSel = -1;
|
||||
searchedSel = direction - 1;
|
||||
if (searchedSel > searchResults.size() - 1) searchedSel = searchResults.size() - 1;
|
||||
} else {
|
||||
if (direction > 1 && filteredSel + direction > filterResults.size() - 1) {
|
||||
skipMore = direction - (filterResults.size() - 1 - filteredSel);
|
||||
if (skipMore == direction) {
|
||||
skipMore = 0;
|
||||
} else {
|
||||
direction -= skipMore;
|
||||
}
|
||||
int32 cur = (filteredSel >= 0 && filteredSel < filterResults.size()) ? filteredSel : ((peopleSel >= 0 && peopleSel < peopleResults.size()) ? (peopleSel + filterResults.size()) : (searchedSel + peopleResults.size() + filterResults.size()));
|
||||
cur = snap(cur + direction, 0, filterResults.size() + peopleResults.size() + searchResults.size() - 1);
|
||||
if (cur < filterResults.size()) {
|
||||
filteredSel = cur;
|
||||
peopleSel = searchedSel = -1;
|
||||
} else if (cur < filterResults.size() + peopleResults.size()) {
|
||||
peopleSel = cur - filterResults.size();
|
||||
filteredSel = searchedSel = -1;
|
||||
} else {
|
||||
filteredSel = peopleSel = -1;
|
||||
searchedSel = cur - filterResults.size() - peopleResults.size();
|
||||
}
|
||||
filteredSel = snap(filteredSel + direction, 0, filterResults.size() - 1);
|
||||
}
|
||||
if (filteredSel >= 0 && filteredSel < filterResults.size()) {
|
||||
emit mustScrollTo(filteredSel * st::dlgHeight, (filteredSel + 1) * st::dlgHeight);
|
||||
} else if (peopleSel >= 0 && peopleSel < peopleResults.size()) {
|
||||
emit mustScrollTo((peopleSel + filterResults.size()) * st::dlgHeight + (peopleSel ? st::searchedBarHeight : 0), (peopleSel + filterResults.size() + 1) * st::dlgHeight);
|
||||
} else {
|
||||
emit mustScrollTo((searchedSel + filterResults.size()) * st::dlgHeight + (searchedSel ? st::searchedBarHeight : 0), (searchedSel + filterResults.size() + 1) * st::dlgHeight + st::searchedBarHeight);
|
||||
emit mustScrollTo((searchedSel + peopleResults.size() + filterResults.size()) * st::dlgHeight + (peopleResults.size() ? st::searchedBarHeight : 0) + (searchedSel ? st::searchedBarHeight : 0), (searchedSel + peopleResults.size() + filterResults.size() + 1) * st::dlgHeight + (peopleResults.size() ? st::searchedBarHeight : 0) + st::searchedBarHeight);
|
||||
}
|
||||
}
|
||||
if (skipMore) {
|
||||
return selectSkip(skipMore);
|
||||
} else {
|
||||
parentWidget()->update();
|
||||
}
|
||||
parentWidget()->update();
|
||||
}
|
||||
|
||||
void DialogsListWidget::scrollToPeer(const PeerId &peer, MsgId msgId) {
|
||||
@@ -798,8 +892,18 @@ void DialogsListWidget::loadPeerPhotos(int32 yFrom) {
|
||||
|
||||
from = (yFrom > st::searchedBarHeight ? ((yFrom - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size();
|
||||
if (from < 0) from = 0;
|
||||
if (from < searchResults.size()) {
|
||||
if (from < peopleResults.size()) {
|
||||
int32 to = (yTo > st::searchedBarHeight ? ((yTo - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() + 1, w = width();
|
||||
if (to > peopleResults.size()) to = peopleResults.size();
|
||||
|
||||
for (; from < to; ++from) {
|
||||
peopleResults[from]->photo->load();
|
||||
}
|
||||
}
|
||||
from = (yFrom > ((peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight) ? ((yFrom - (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() - peopleResults.size();
|
||||
if (from < 0) from = 0;
|
||||
if (from < searchResults.size()) {
|
||||
int32 to = (yTo >(peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight ? ((yTo - (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() - peopleResults.size() + 1, w = width();
|
||||
if (to > searchResults.size()) to = searchResults.size();
|
||||
|
||||
for (; from < to; ++from) {
|
||||
@@ -817,15 +921,21 @@ bool DialogsListWidget::choosePeer() {
|
||||
} else if (_state == FilteredState || _state == SearchedState) {
|
||||
if (filteredSel >= 0 && filteredSel < filterResults.size()) {
|
||||
history = filterResults[filteredSel]->history;
|
||||
} else if (peopleSel >= 0 && peopleSel < peopleResults.size()) {
|
||||
history = App::history(peopleResults[peopleSel]->id);
|
||||
} else if (searchedSel >= 0 && searchedSel < searchResults.size()) {
|
||||
history = searchResults[searchedSel]->_item->history();
|
||||
msgId = searchResults[searchedSel]->_item->id;
|
||||
}
|
||||
}
|
||||
if (history) {
|
||||
emit peerChosen(history->peer->id, msgId);
|
||||
bool chosen = (!App::main()->selectingPeer() && (_state == FilteredState || _state == SearchedState) && filteredSel >= 0 && filteredSel < filterResults.size());
|
||||
App::main()->showPeer(history->peer->id, msgId);
|
||||
if (chosen) {
|
||||
emit searchResultChosen();
|
||||
}
|
||||
sel = 0;
|
||||
filteredSel = -1;
|
||||
filteredSel = peopleSel = searchedSel = -1;
|
||||
parentWidget()->update();
|
||||
return true;
|
||||
}
|
||||
@@ -835,10 +945,10 @@ bool DialogsListWidget::choosePeer() {
|
||||
void DialogsListWidget::destroyData() {
|
||||
sel = 0;
|
||||
contactSel = false;
|
||||
filteredSel = 0;
|
||||
filteredSel = -1;
|
||||
filterResults.clear();
|
||||
filter.clear();
|
||||
searchedSel = 0;
|
||||
searchedSel = peopleSel = -1;
|
||||
clearSearchResults();
|
||||
contacts.clear();
|
||||
contactsNoDialogs.clear();
|
||||
@@ -883,6 +993,33 @@ void DialogsListWidget::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (searchResults.at(0)->_item->history()->peer == inPeer && searchResults.at(0)->_item->id == inMsg) {
|
||||
outMsg = 0;
|
||||
if (peopleResults.isEmpty()) {
|
||||
if (filterResults.isEmpty()) {
|
||||
outPeer = 0;
|
||||
} else {
|
||||
outPeer = filterResults.back()->history->peer;
|
||||
}
|
||||
} else {
|
||||
outPeer = peopleResults.back();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!peopleResults.isEmpty() && peopleResults.at(0) == inPeer) {
|
||||
outPeer = filterResults.isEmpty() ? 0 : filterResults.back()->history->peer;
|
||||
outMsg = 0;
|
||||
return;
|
||||
}
|
||||
if (!peopleResults.isEmpty()) {
|
||||
for (PeopleResults::const_iterator b = peopleResults.cbegin(), i = b + 1, e = peopleResults.cend(); i != e; ++i) {
|
||||
if ((*i) == inPeer) {
|
||||
outPeer = (*(i - 1));
|
||||
outMsg = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (filterResults.isEmpty() || filterResults.at(0)->history->peer == inPeer) {
|
||||
outPeer = 0;
|
||||
@@ -892,8 +1029,7 @@ void DialogsListWidget::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData
|
||||
|
||||
for (FilteredDialogs::const_iterator b = filterResults.cbegin(), i = b + 1, e = filterResults.cend(); i != e; ++i) {
|
||||
if ((*i)->history->peer == inPeer) {
|
||||
FilteredDialogs::const_iterator j = i - 1;
|
||||
outPeer = (*j)->history->peer;
|
||||
outPeer = (*(i - 1))->history->peer;
|
||||
outMsg = 0;
|
||||
return;
|
||||
}
|
||||
@@ -943,11 +1079,32 @@ void DialogsListWidget::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PeopleResults::const_iterator i = peopleResults.cbegin(), e = peopleResults.cend(); i != e; ++i) {
|
||||
if ((*i) == inPeer) {
|
||||
++i;
|
||||
if (i == e && !searchResults.isEmpty()) {
|
||||
outPeer = searchResults.front()->_item->history()->peer;
|
||||
outMsg = searchResults.front()->_item->id;
|
||||
} else {
|
||||
outPeer = (i == e) ? 0 : (*i);
|
||||
outMsg = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (FilteredDialogs::const_iterator i = filterResults.cbegin(), e = filterResults.cend(); i != e; ++i) {
|
||||
if ((*i)->history->peer == inPeer) {
|
||||
++i;
|
||||
outPeer = (i == e) ? 0 : (*i)->history->peer;
|
||||
outMsg = 0;
|
||||
if (i == e && !peopleResults.isEmpty()) {
|
||||
outPeer = peopleResults.front();
|
||||
outMsg = 0;
|
||||
} else if (i == e && !searchResults.isEmpty()) {
|
||||
outPeer = searchResults.front()->_item->history()->peer;
|
||||
outMsg = searchResults.front()->_item->id;
|
||||
} else {
|
||||
outPeer = (i == e) ? 0 : (*i)->history->peer;
|
||||
outMsg = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -964,6 +1121,14 @@ DialogsIndexed &DialogsListWidget::dialogsList() {
|
||||
return dialogs;
|
||||
}
|
||||
|
||||
DialogsListWidget::FilteredDialogs &DialogsListWidget::filteredList() {
|
||||
return filterResults;
|
||||
}
|
||||
|
||||
DialogsListWidget::PeopleResults &DialogsListWidget::peopleList() {
|
||||
return peopleResults;
|
||||
}
|
||||
|
||||
DialogsListWidget::SearchResults &DialogsListWidget::searchList() {
|
||||
return searchResults;
|
||||
}
|
||||
@@ -985,13 +1150,14 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : QWidget(parent)
|
||||
, scroll(this, st::dlgScroll)
|
||||
, list(&scroll, parent)
|
||||
, _searchFull(false)
|
||||
, _peopleFull(false)
|
||||
{
|
||||
scroll.setWidget(&list);
|
||||
scroll.setFocusPolicy(Qt::NoFocus);
|
||||
connect(&list, SIGNAL(mustScrollTo(int, int)), &scroll, SLOT(scrollToY(int, int)));
|
||||
connect(&list, SIGNAL(dialogToTopFrom(int)), this, SLOT(onDialogToTopFrom(int)));
|
||||
connect(&list, SIGNAL(peerChosen(const PeerId &, MsgId)), this, SIGNAL(peerChosen(const PeerId &, MsgId)));
|
||||
connect(&list, SIGNAL(searchMessages()), this, SLOT(onNeedSearchMessages()));
|
||||
connect(&list, SIGNAL(searchResultChosen()), this, SLOT(onCancel()));
|
||||
connect(&scroll, SIGNAL(geometryChanged()), &list, SLOT(onParentGeometryChanged()));
|
||||
connect(&scroll, SIGNAL(scrolled()), &list, SLOT(onUpdateSelected()));
|
||||
connect(&scroll, SIGNAL(scrolled()), this, SLOT(onListScroll()));
|
||||
@@ -1046,7 +1212,7 @@ void DialogsWidget::setInnerFocus() {
|
||||
}
|
||||
|
||||
void DialogsWidget::regTyping(History *history, UserData *user) {
|
||||
uint64 ms = getms();
|
||||
uint64 ms = getms(true);
|
||||
history->typing[user] = ms + 6000;
|
||||
|
||||
Histories::TypingHistories::const_iterator i = App::histories().typing.find(history);
|
||||
@@ -1060,7 +1226,7 @@ void DialogsWidget::regTyping(History *history, UserData *user) {
|
||||
}
|
||||
|
||||
bool DialogsWidget::animStep(float64) {
|
||||
uint64 ms = getms();
|
||||
uint64 ms = getms(true);
|
||||
Histories::TypingHistories &typing(App::histories().typing);
|
||||
for (Histories::TypingHistories::iterator i = typing.begin(), e = typing.end(); i != e;) {
|
||||
uint32 typingFrame = (ms - i.value()) / 150;
|
||||
@@ -1082,10 +1248,6 @@ void DialogsWidget::onCancel() {
|
||||
emit cancelled();
|
||||
}
|
||||
|
||||
void DialogsWidget::clearFiltered() {
|
||||
onCancel();
|
||||
}
|
||||
|
||||
void DialogsWidget::itemRemoved(HistoryItem *item) {
|
||||
list.itemRemoved(item);
|
||||
}
|
||||
@@ -1164,6 +1326,9 @@ bool DialogsWidget::onSearchMessages(bool searchCache) {
|
||||
if (_searchRequest) {
|
||||
_searchRequest = 0;
|
||||
}
|
||||
if (_peopleRequest) {
|
||||
_peopleRequest = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (searchCache) {
|
||||
@@ -1181,6 +1346,22 @@ bool DialogsWidget::onSearchMessages(bool searchCache) {
|
||||
_searchRequest = MTP::send(MTPmessages_Search(MTP_inputPeerEmpty(), MTP_string(_searchQuery), MTP_inputMessagesFilterEmpty(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(SearchPerPage)), rpcDone(&DialogsWidget::searchReceived, true), rpcFail(&DialogsWidget::searchFailed));
|
||||
_searchQueries.insert(_searchRequest, _searchQuery);
|
||||
}
|
||||
if (q.size() >= MinUsernameLength) {
|
||||
if (searchCache) {
|
||||
PeopleCache::const_iterator i = _peopleCache.constFind(q);
|
||||
if (i != _peopleCache.cend()) {
|
||||
_peopleQuery = q;
|
||||
_peopleRequest = 0;
|
||||
peopleReceived(i.value(), 0);
|
||||
return true;
|
||||
}
|
||||
} else if (_peopleQuery != q) {
|
||||
_peopleQuery = q;
|
||||
_peopleFull = false;
|
||||
_peopleRequest = MTP::send(MTPcontacts_Search(MTP_string(_peopleQuery), MTP_int(SearchPeopleLimit)), rpcDone(&DialogsWidget::peopleReceived), rpcFail(&DialogsWidget::peopleFailed));
|
||||
_peopleQueries.insert(_peopleRequest, _peopleQuery);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1267,6 +1448,30 @@ void DialogsWidget::searchReceived(bool fromStart, const MTPmessages_Messages &r
|
||||
}
|
||||
}
|
||||
|
||||
void DialogsWidget::peopleReceived(const MTPcontacts_Found &result, mtpRequestId req) {
|
||||
QString q = _peopleQuery;
|
||||
if (list.state() == DialogsListWidget::FilteredState || list.state() == DialogsListWidget::SearchedState) {
|
||||
PeopleQueries::iterator i = _peopleQueries.find(req);
|
||||
if (i != _peopleQueries.cend()) {
|
||||
q = i.value();
|
||||
_peopleCache[q] = result;
|
||||
_peopleQueries.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (_peopleRequest == req) {
|
||||
switch (result.type()) {
|
||||
case mtpc_contacts_found: {
|
||||
App::feedUsers(result.c_contacts_found().vusers);
|
||||
list.peopleReceived(q, result.c_contacts_found().vresults.c_vector().v);
|
||||
} break;
|
||||
}
|
||||
|
||||
_peopleRequest = 0;
|
||||
onListScroll();
|
||||
}
|
||||
}
|
||||
|
||||
bool DialogsWidget::searchFailed(const RPCError &error, mtpRequestId req) {
|
||||
if (_searchRequest == req) {
|
||||
_searchRequest = 0;
|
||||
@@ -1275,6 +1480,14 @@ bool DialogsWidget::searchFailed(const RPCError &error, mtpRequestId req) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DialogsWidget::peopleFailed(const RPCError &error, mtpRequestId req) {
|
||||
if (_peopleRequest == req) {
|
||||
_peopleRequest = 0;
|
||||
_peopleFull = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DialogsWidget::addNewContact(int32 uid, bool show) {
|
||||
_filter.setText(QString());
|
||||
onFilterUpdate();
|
||||
@@ -1288,8 +1501,7 @@ bool DialogsWidget::addNewContact(int32 uid, bool show) {
|
||||
void DialogsWidget::onListScroll() {
|
||||
list.loadPeerPhotos(scroll.scrollTop());
|
||||
if (list.state() == DialogsListWidget::SearchedState) {
|
||||
DialogsListWidget::SearchResults &res(list.searchList());
|
||||
if (scroll.scrollTop() > res.size() * st::dlgHeight - PreloadHeightsCount * scroll.height()) {
|
||||
if (scroll.scrollTop() > (list.searchList().size() + list.filteredList().size() + list.peopleList().size()) * st::dlgHeight - PreloadHeightsCount * scroll.height()) {
|
||||
onSearchMore(list.lastSearchId());
|
||||
}
|
||||
} else if (scroll.scrollTop() > list.dialogsList().list.count * st::dlgHeight - PreloadHeightsCount * scroll.height()) {
|
||||
@@ -1312,6 +1524,11 @@ void DialogsWidget::onFilterUpdate() {
|
||||
_cancelSearch.show();
|
||||
_newGroup.hide();
|
||||
}
|
||||
if (filterText.size() < MinUsernameLength) {
|
||||
_peopleCache.clear();
|
||||
_peopleQueries.clear();
|
||||
_peopleQuery = QString();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogsWidget::resizeEvent(QResizeEvent *e) {
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
|
||||
void dialogsReceived(const QVector<MTPDialog> &dialogs);
|
||||
void searchReceived(const QVector<MTPMessage> &messages, bool fromStart, int32 fullCount);
|
||||
void peopleReceived(const QString &query, const QVector<MTPContactFound> &people);
|
||||
void showMore(int32 pixels);
|
||||
|
||||
void activate();
|
||||
@@ -41,6 +42,8 @@ public:
|
||||
void enterEvent(QEvent *e);
|
||||
void leaveEvent(QEvent *e);
|
||||
|
||||
void peopleResultPaint(UserData *user, QPainter &p, int32 w, bool act, bool sel) const;
|
||||
|
||||
void selectSkip(int32 direction);
|
||||
void selectSkipPage(int32 pixels, int32 direction);
|
||||
|
||||
@@ -62,10 +65,14 @@ public:
|
||||
void peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) const;
|
||||
void scrollToPeer(const PeerId &peer, MsgId msgId);
|
||||
|
||||
typedef QVector<DialogRow*> FilteredDialogs;
|
||||
typedef QVector<UserData*> PeopleResults;
|
||||
typedef QVector<FakeDialogRow*> SearchResults;
|
||||
|
||||
DialogsIndexed &contactsList();
|
||||
DialogsIndexed &dialogsList();
|
||||
FilteredDialogs &filteredList();
|
||||
PeopleResults &peopleList();
|
||||
SearchResults &searchList();
|
||||
MsgId lastSearchId() const;
|
||||
|
||||
@@ -96,15 +103,15 @@ public slots:
|
||||
|
||||
signals:
|
||||
|
||||
void peerChosen(const PeerId &, MsgId);
|
||||
void mustScrollTo(int scrollToTop, int scrollToBottom);
|
||||
void dialogToTopFrom(int movedFrom);
|
||||
void searchMessages();
|
||||
void searchResultChosen();
|
||||
|
||||
private:
|
||||
|
||||
void addDialog(const MTPDdialog &dialog);
|
||||
void clearSearchResults();
|
||||
void clearSearchResults(bool clearPeople = true);
|
||||
|
||||
DialogsIndexed dialogs;
|
||||
DialogsIndexed contactsNoDialogs;
|
||||
@@ -114,13 +121,16 @@ private:
|
||||
bool selByMouse;
|
||||
|
||||
QString filter;
|
||||
typedef QVector<DialogRow*> FilteredDialogs;
|
||||
FilteredDialogs filterResults;
|
||||
int32 filteredSel;
|
||||
|
||||
SearchResults searchResults;
|
||||
int32 searchedCount, searchedSel;
|
||||
|
||||
QString peopleQuery;
|
||||
PeopleResults peopleResults;
|
||||
int32 peopleSel;
|
||||
|
||||
MsgId _lastSearchId;
|
||||
|
||||
State _state;
|
||||
@@ -140,6 +150,7 @@ public:
|
||||
void dialogsReceived(const MTPmessages_Dialogs &dialogs);
|
||||
void contactsReceived(const MTPcontacts_Contacts &contacts);
|
||||
void searchReceived(bool fromStart, const MTPmessages_Messages &result, mtpRequestId req);
|
||||
void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req);
|
||||
bool addNewContact(int32 uid, bool show = true);
|
||||
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
@@ -174,14 +185,12 @@ public:
|
||||
|
||||
void searchMessages(const QString &query);
|
||||
void onSearchMore(MsgId minMsgId);
|
||||
void clearFiltered();
|
||||
|
||||
void itemRemoved(HistoryItem *item);
|
||||
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
|
||||
|
||||
signals:
|
||||
|
||||
void peerChosen(const PeerId &, MsgId);
|
||||
void cancelled();
|
||||
|
||||
public slots:
|
||||
@@ -206,6 +215,7 @@ private:
|
||||
bool dialogsFailed(const RPCError &e);
|
||||
bool contactsFailed();
|
||||
bool searchFailed(const RPCError &error, mtpRequestId req);
|
||||
bool peopleFailed(const RPCError &error, mtpRequestId req);
|
||||
|
||||
int32 dlgOffset, dlgCount;
|
||||
mtpRequestId dlgPreloading;
|
||||
@@ -217,9 +227,9 @@ private:
|
||||
DialogsListWidget list;
|
||||
|
||||
QTimer _searchTimer;
|
||||
QString _searchQuery;
|
||||
bool _searchFull;
|
||||
mtpRequestId _searchRequest;
|
||||
QString _searchQuery, _peopleQuery;
|
||||
bool _searchFull, _peopleFull;
|
||||
mtpRequestId _searchRequest, _peopleRequest;
|
||||
|
||||
typedef QMap<QString, MTPmessages_Messages> SearchCache;
|
||||
SearchCache _searchCache;
|
||||
@@ -227,4 +237,10 @@ private:
|
||||
typedef QMap<mtpRequestId, QString> SearchQueries;
|
||||
SearchQueries _searchQueries;
|
||||
|
||||
typedef QMap<QString, MTPcontacts_Found> PeopleCache;
|
||||
PeopleCache _peopleCache;
|
||||
|
||||
typedef QMap<mtpRequestId, QString> PeopleQueries;
|
||||
PeopleQueries _peopleQueries;
|
||||
|
||||
};
|
||||
|
||||
@@ -315,10 +315,8 @@ bool DragArea::animStep(float64 ms) {
|
||||
return res;
|
||||
}
|
||||
|
||||
static const int emojiPerRow = 7, emojiRowsPerPage = 6;
|
||||
|
||||
EmojiPanInner::EmojiPanInner(QWidget *parent) : QWidget(parent), _tab(cEmojiTab()), _selected(-1), _pressedSel(-1) {
|
||||
resize(emojiPerRow * st::emojiPanSize.width(), emojiRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
|
||||
resize(EmojiPadPerRow * st::emojiPanSize.width(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
|
||||
setMouseTracking(true);
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
_saveConfigTimer.setSingleShot(true);
|
||||
@@ -337,11 +335,11 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) {
|
||||
|
||||
QRect r = e ? e->rect() : rect();
|
||||
|
||||
int32 rows = (size / emojiPerRow) + ((size % emojiPerRow) ? 1 : 0);
|
||||
int32 rows = (size / EmojiPadPerRow) + ((size % EmojiPadPerRow) ? 1 : 0);
|
||||
int32 fromrow = qMax(qFloor(r.top() / st::emojiPanSize.height()), 0), torow = qMin(qCeil(r.bottom() / st::emojiPanSize.height()) + 1, rows);
|
||||
for (int32 i = fromrow; i < torow; ++i) {
|
||||
for (int32 j = 0; j < emojiPerRow; ++j) {
|
||||
int32 index = i * emojiPerRow + j;
|
||||
for (int32 j = 0; j < EmojiPadPerRow; ++j) {
|
||||
int32 index = i * EmojiPadPerRow + j;
|
||||
if (index >= size) break;
|
||||
|
||||
float64 hover = _hovers[index];
|
||||
@@ -395,7 +393,7 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
}
|
||||
}
|
||||
if (i == e) {
|
||||
while (recent.size() >= emojiPerRow * emojiRowsPerPage) recent.pop_back();
|
||||
while (recent.size() >= EmojiPadPerRow * EmojiPadRowsPerPage) recent.pop_back();
|
||||
recent.push_back(qMakePair(emoji, 1));
|
||||
for (i = recent.end() - 1; i != recent.begin(); --i) {
|
||||
if ((i - 1)->second > i->second) {
|
||||
@@ -428,8 +426,8 @@ void EmojiPanInner::leaveEvent(QEvent *e) {
|
||||
void EmojiPanInner::updateSelected() {
|
||||
int32 selIndex = -1;
|
||||
QPoint p(mapFromGlobal(_lastMousePos));
|
||||
if (p.x() >= 0 && p.y() >= 0 && p.x() < emojiPerRow * st::emojiPanSize.width()) {
|
||||
selIndex = qFloor(p.y() / st::emojiPanSize.height()) * emojiPerRow + qFloor(p.x() / st::emojiPanSize.width());
|
||||
if (p.x() >= 0 && p.y() >= 0 && p.x() < EmojiPadPerRow * st::emojiPanSize.width()) {
|
||||
selIndex = qFloor(p.y() / st::emojiPanSize.height()) * EmojiPadPerRow + qFloor(p.x() / st::emojiPanSize.width());
|
||||
if (selIndex >= _emojis.size()) {
|
||||
selIndex = -1;
|
||||
}
|
||||
@@ -479,7 +477,7 @@ void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) {
|
||||
_emojiAnimations.clear();
|
||||
_selected = _pressedSel = -1;
|
||||
int32 size = _emojis.size();
|
||||
int32 h = qMax(((size / emojiPerRow) + ((size % emojiPerRow) ? 1 : 0)) * st::emojiPanSize.height(), emojiRowsPerPage * st::emojiPanSize.height() - int(st::emojiPanSub));
|
||||
int32 h = qMax(((size / EmojiPadPerRow) + ((size % EmojiPadPerRow) ? 1 : 0)) * st::emojiPanSize.height(), EmojiPadRowsPerPage * st::emojiPanSize.height() - int(st::emojiPanSub));
|
||||
resize(width(), h);
|
||||
_lastMousePos = QCursor::pos();
|
||||
updateSelected();
|
||||
@@ -501,7 +499,7 @@ _scroll(this, st::emojiScroll), _inner() {
|
||||
|
||||
_inner.showEmojiPack(cEmojiTab());
|
||||
|
||||
_scroll.setGeometry(st::dropdownPadding.left() + st::emojiPanPadding.left(), st::dropdownPadding.top() + _recent.height() + st::emojiPanPadding.top(), st::emojiPanPadding.left() + _inner.width() + st::emojiPanPadding.right(), emojiRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
|
||||
_scroll.setGeometry(st::dropdownPadding.left() + st::emojiPanPadding.left(), st::dropdownPadding.top() + _recent.height() + st::emojiPanPadding.top(), st::emojiPanPadding.left() + _inner.width() + st::emojiPanPadding.right(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
|
||||
_scroll.setWidget(&_inner);
|
||||
|
||||
_width = st::dropdownPadding.left() + st::emojiPanPadding.left() + _scroll.width() + st::emojiPanPadding.right() + st::dropdownPadding.right();
|
||||
|
||||
@@ -19,8 +19,11 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#include "fileuploader.h"
|
||||
|
||||
FileUploader::FileUploader() : sentSize(0), uploading(0) {
|
||||
memset(sentSizes, 0, sizeof(sentSizes));
|
||||
nextTimer.setSingleShot(true);
|
||||
connect(&nextTimer, SIGNAL(timeout()), this, SLOT(sendNext()));
|
||||
killSessionsTimer.setSingleShot(true);
|
||||
connect(&killSessionsTimer, SIGNAL(timeout()), this, SLOT(killSessions()));
|
||||
}
|
||||
|
||||
void FileUploader::uploadMedia(MsgId msgId, const ReadyLocalMedia &media) {
|
||||
@@ -60,16 +63,36 @@ void FileUploader::currentFailed() {
|
||||
|
||||
requestsSent.clear();
|
||||
docRequestsSent.clear();
|
||||
queue.remove(uploading);
|
||||
dcMap.clear();
|
||||
uploading = 0;
|
||||
sentSize = 0;
|
||||
for (int i = 0; i < MTPUploadSessionsCount; ++i) {
|
||||
sentSizes[i] = 0;
|
||||
}
|
||||
|
||||
sendNext();
|
||||
}
|
||||
|
||||
void FileUploader::sendNext() {
|
||||
if (sentSize >= MaxUploadFileParallelSize || queue.isEmpty()) return;
|
||||
void FileUploader::killSessions() {
|
||||
for (int i = 0; i < MTPUploadSessionsCount; ++i) {
|
||||
MTP::killSession(MTP::upl[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void FileUploader::sendNext() {
|
||||
if (sentSize >= MaxUploadFileParallelSize) return;
|
||||
|
||||
bool killing = killSessionsTimer.isActive();
|
||||
if (queue.isEmpty()) {
|
||||
if (!killing) {
|
||||
killSessionsTimer.start(MTPAckSendWaiting + MTPKillFileSessionTimeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (killing) {
|
||||
killSessionsTimer.stop();
|
||||
}
|
||||
Queue::iterator i = uploading ? queue.find(uploading) : queue.begin();
|
||||
if (!uploading) {
|
||||
uploading = i.key();
|
||||
@@ -77,6 +100,12 @@ void FileUploader::sendNext() {
|
||||
i = queue.begin();
|
||||
uploading = i.key();
|
||||
}
|
||||
int todc = 0;
|
||||
for (int dc = 1; dc < MTPUploadSessionsCount; ++dc) {
|
||||
if (sentSizes[dc] < sentSizes[todc]) {
|
||||
todc = dc;
|
||||
}
|
||||
}
|
||||
if (i->media.parts.isEmpty()) {
|
||||
if (i->docSentParts >= i->docPartsCount) {
|
||||
if (requestsSent.isEmpty() && docRequestsSent.isEmpty()) {
|
||||
@@ -125,20 +154,24 @@ void FileUploader::sendNext() {
|
||||
}
|
||||
mtpRequestId requestId;
|
||||
if (i->docSize > UseBigFilesFrom) {
|
||||
requestId = MTP::send(MTPupload_SaveBigFilePart(MTP_long(i->media.id), MTP_int(i->docSentParts), MTP_int(i->docPartsCount), MTP_string(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl);
|
||||
requestId = MTP::send(MTPupload_SaveBigFilePart(MTP_long(i->media.id), MTP_int(i->docSentParts), MTP_int(i->docPartsCount), MTP_string(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl[todc]);
|
||||
} else {
|
||||
requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->media.id), MTP_int(i->docSentParts), MTP_string(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl);
|
||||
requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->media.id), MTP_int(i->docSentParts), MTP_string(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl[todc]);
|
||||
}
|
||||
docRequestsSent.insert(requestId, i->docSentParts);
|
||||
dcMap.insert(requestId, todc);
|
||||
sentSize += i->docPartSize;
|
||||
sentSizes[todc] += i->docPartSize;
|
||||
|
||||
i->docSentParts++;
|
||||
} else {
|
||||
LocalFileParts::iterator part = i->media.parts.begin();
|
||||
|
||||
mtpRequestId requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->media.jpeg_id), MTP_int(part.key()), MTP_string(part.value())), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl);
|
||||
mtpRequestId requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->media.jpeg_id), MTP_int(part.key()), MTP_string(part.value())), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::upl[todc]);
|
||||
requestsSent.insert(requestId, part.value());
|
||||
dcMap.insert(requestId, todc);
|
||||
sentSize += part.value().size();
|
||||
sentSizes[todc] += part.value().size();
|
||||
|
||||
i->media.parts.erase(part);
|
||||
}
|
||||
@@ -168,7 +201,13 @@ void FileUploader::clear() {
|
||||
MTP::cancel(i.key());
|
||||
}
|
||||
docRequestsSent.clear();
|
||||
dcMap.clear();
|
||||
sentSize = 0;
|
||||
for (int32 i = 0; i < MTPUploadSessionsCount; ++i) {
|
||||
MTP::killSession(MTP::upl[i]);
|
||||
sentSizes[i] = 0;
|
||||
}
|
||||
killSessionsTimer.stop();
|
||||
}
|
||||
|
||||
void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
|
||||
@@ -182,12 +221,22 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
|
||||
currentFailed();
|
||||
return;
|
||||
} else {
|
||||
QMap<mtpRequestId, int32>::iterator dcIt = dcMap.find(requestId);
|
||||
if (dcIt == dcMap.cend()) { // must not happen
|
||||
currentFailed();
|
||||
return;
|
||||
}
|
||||
int32 dc = dcIt.value();
|
||||
dcMap.erase(dcIt);
|
||||
|
||||
Queue::const_iterator k = queue.constFind(uploading);
|
||||
if (i != requestsSent.cend()) {
|
||||
sentSize -= i.value().size();
|
||||
sentSizes[dc] -= i.value().size();
|
||||
requestsSent.erase(i);
|
||||
} else {
|
||||
sentSize -= k->docPartSize;
|
||||
sentSizes[dc] -= k->docPartSize;
|
||||
docRequestsSent.erase(j);
|
||||
}
|
||||
if (k->media.type == ToPreparePhoto) {
|
||||
|
||||
@@ -38,6 +38,7 @@ public:
|
||||
public slots:
|
||||
|
||||
void sendNext();
|
||||
void killSessions();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -98,11 +99,13 @@ private:
|
||||
|
||||
QMap<mtpRequestId, QByteArray> requestsSent;
|
||||
QMap<mtpRequestId, int32> docRequestsSent;
|
||||
QMap<mtpRequestId, int32> dcMap;
|
||||
uint32 sentSize;
|
||||
uint32 sentSizes[MTPUploadSessionsCount];
|
||||
|
||||
MsgId uploading;
|
||||
Queue queue;
|
||||
Queue uploaded;
|
||||
QTimer nextTimer;
|
||||
QTimer nextTimer, killSessionsTimer;
|
||||
|
||||
};
|
||||
|
||||
@@ -58,6 +58,9 @@ namespace anim {
|
||||
const float64 ¤t() const {
|
||||
return _cur;
|
||||
}
|
||||
float64 to() const {
|
||||
return _from + _delta;
|
||||
}
|
||||
fvalue &update(const float64 &dt, transition func) {
|
||||
_cur = _from + (*func)(_delta, dt);
|
||||
return *this;
|
||||
@@ -93,6 +96,9 @@ namespace anim {
|
||||
int32 current() const {
|
||||
return _cur;
|
||||
}
|
||||
int32 to() const {
|
||||
return _from + _delta;
|
||||
}
|
||||
ivalue &update(const float64 &dt, transition func) {
|
||||
_cur = qRound(_from + (*func)(_delta, dt));
|
||||
return *this;
|
||||
@@ -145,6 +151,14 @@ namespace anim {
|
||||
const QColor ¤t() const {
|
||||
return _cur;
|
||||
}
|
||||
QColor to() const {
|
||||
QColor result;
|
||||
result.setRedF(_from_r + _delta_r);
|
||||
result.setGreenF(_from_g + _delta_g);
|
||||
result.setBlueF(_from_b + _delta_b);
|
||||
result.setAlphaF(_from_a + _delta_a);
|
||||
return result;
|
||||
}
|
||||
cvalue &update(const float64 &dt, transition func) {
|
||||
_cur.setRedF(_from_r + (*func)(_delta_r, dt));
|
||||
_cur.setGreenF(_from_g + (*func)(_delta_g, dt));
|
||||
|
||||
@@ -209,6 +209,7 @@ void ContextMenu::showStart() {
|
||||
animStep(0);
|
||||
psUpdateOverlayed(this);
|
||||
show();
|
||||
psShowOverAll(this);
|
||||
windowHandle()->requestActivate();
|
||||
activateWindow();
|
||||
setFocus();
|
||||
|
||||
@@ -189,7 +189,7 @@ QString filedialogDefaultName(const QString &prefix, const QString &extension, c
|
||||
QChar zero('0');
|
||||
|
||||
QString name;
|
||||
QString base = prefix + QString("_%1-%2-%3_%4-%5-%6").arg(tm.tm_year + 1900).arg(tm.tm_mon + 1, 2, 10, zero).arg(tm.tm_mday, 2, 10, zero).arg(tm.tm_hour, 2, 10, zero).arg(tm.tm_min, 2, 10, zero).arg(tm.tm_sec, 2, 10, zero);
|
||||
QString base = prefix + qsl("_%1-%2-%3_%4-%5-%6").arg(tm.tm_year + 1900).arg(tm.tm_mon + 1, 2, 10, zero).arg(tm.tm_mday, 2, 10, zero).arg(tm.tm_hour, 2, 10, zero).arg(tm.tm_min, 2, 10, zero).arg(tm.tm_sec, 2, 10, zero);
|
||||
if (skipExistance) {
|
||||
name = base + extension;
|
||||
} else {
|
||||
@@ -197,8 +197,23 @@ QString filedialogDefaultName(const QString &prefix, const QString &extension, c
|
||||
QString nameBase = dir.absolutePath() + '/' + base;
|
||||
name = nameBase + extension;
|
||||
for (int i = 0; QFileInfo(name).exists(); ++i) {
|
||||
name = nameBase + QString(" (%1)").arg(i + 2) + extension;
|
||||
name = nameBase + qsl(" (%1)").arg(i + 2) + extension;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
QString filedialogNextFilename(const QString &name, const QString &cur, const QString &path) {
|
||||
QDir dir(path.isEmpty() ? cDialogLastPath() : path);
|
||||
int32 extIndex = name.lastIndexOf('.');
|
||||
QString prefix = name, extension;
|
||||
if (extIndex >= 0) {
|
||||
extension = name.mid(extIndex);
|
||||
prefix = name.mid(0, extIndex);
|
||||
}
|
||||
QString nameBase = dir.absolutePath() + '/' + prefix, result = nameBase + extension;
|
||||
for (int i = 0; result.toLower() != cur.toLower() && QFileInfo(result).exists(); ++i) {
|
||||
result = nameBase + qsl(" (%1)").arg(i + 2) + extension;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -24,3 +24,4 @@ bool filedialogGetSaveFile(QString &file, const QString &caption, const QString
|
||||
bool filedialogGetDir(QString &dir, const QString &caption);
|
||||
|
||||
QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path = QString(), bool skipExistance = false);
|
||||
QString filedialogNextFilename(const QString &name, const QString &cur, const QString &path = QString());
|
||||
|
||||
@@ -133,15 +133,15 @@ LinkButton::~LinkButton() {
|
||||
}
|
||||
|
||||
IconedButton::IconedButton(QWidget *parent, const style::iconedButton &st, const QString &text) : Button(parent),
|
||||
_text(text), _st(st), a_opacity(_st.opacity), a_bg(_st.bgColor->c), _opacity(1) {
|
||||
_text(text), _st(st), _width(_st.width), a_opacity(_st.opacity), a_bg(_st.bgColor->c), _opacity(1) {
|
||||
|
||||
if (_st.width < 0) {
|
||||
_st.width = _st.font->m.width(text) - _st.width;
|
||||
} else if (!_st.width) {
|
||||
_st.width = _st.font->m.width(text) + _st.height - _st.font->height;
|
||||
if (_width < 0) {
|
||||
_width = _st.font->m.width(text) - _width;
|
||||
} else if (!_width) {
|
||||
_width = _st.font->m.width(text) + _st.height - _st.font->height;
|
||||
}
|
||||
connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource)));
|
||||
resize(_st.width, _st.height);
|
||||
resize(_width, _st.height);
|
||||
setCursor(_st.cursor);
|
||||
}
|
||||
|
||||
@@ -151,8 +151,16 @@ void IconedButton::setOpacity(float64 opacity) {
|
||||
}
|
||||
|
||||
void IconedButton::setText(const QString &text) {
|
||||
_text = text;
|
||||
update();
|
||||
if (_text != text) {
|
||||
_text = text;
|
||||
if (_st.width < 0) {
|
||||
_width = _st.font->m.width(text) - _st.width;
|
||||
} else if (!_st.width) {
|
||||
_width = _st.font->m.width(text) + _st.height - _st.font->height;
|
||||
}
|
||||
resize(_width, _st.height);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
bool IconedButton::animStep(float64 ms) {
|
||||
|
||||
@@ -106,9 +106,10 @@ public slots:
|
||||
private:
|
||||
|
||||
QString _text;
|
||||
int32 _textWidth;
|
||||
|
||||
style::iconedButton _st;
|
||||
int32 _width;
|
||||
|
||||
anim::fvalue a_opacity;
|
||||
anim::cvalue a_bg;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#include "style.h"
|
||||
|
||||
#include "flatinput.h"
|
||||
#include "window.h"
|
||||
|
||||
namespace {
|
||||
class FlatInputStyle : public QCommonStyle {
|
||||
@@ -57,6 +58,7 @@ FlatInput::FlatInput(QWidget *parent, const style::flatInput &st, const QString
|
||||
|
||||
connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(onTextChange(const QString &)));
|
||||
connect(this, SIGNAL(textEdited(const QString &)), this, SLOT(onTextEdited()));
|
||||
if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu()));
|
||||
|
||||
setStyle(&_flatInputStyle);
|
||||
setTextMargins(0, 0, 0, 0);
|
||||
@@ -262,10 +264,12 @@ void FlatInput::onTextEdited() {
|
||||
_oldtext = text();
|
||||
if (was != _oldtext) emit changed();
|
||||
updatePlaceholder();
|
||||
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
||||
}
|
||||
|
||||
void FlatInput::onTextChange(const QString &text) {
|
||||
_oldtext = text;
|
||||
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
||||
}
|
||||
|
||||
void FlatInput::notaBene() {
|
||||
|
||||
@@ -27,7 +27,6 @@ class FlatInput : public QLineEdit, public Animated {
|
||||
public:
|
||||
|
||||
FlatInput(QWidget *parent, const style::flatInput &st, const QString &ph = QString(), const QString &val = QString());
|
||||
QString val() const;
|
||||
|
||||
bool event(QEvent *e);
|
||||
void touchEvent(QTouchEvent *e);
|
||||
|
||||
@@ -19,11 +19,12 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#include "style.h"
|
||||
|
||||
#include "flattextarea.h"
|
||||
#include "window.h"
|
||||
|
||||
FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &pholder, const QString &v) : QTextEdit(v, parent),
|
||||
_ph(pholder), _oldtext(v), _phVisible(!v.length()),
|
||||
a_phLeft(_phVisible ? 0 : st.phShift), a_phAlpha(_phVisible ? 1 : 0), a_phColor(st.phColor->c),
|
||||
_st(st), _fakeMargin(0),
|
||||
_st(st), _undoAvailable(false), _redoAvailable(false), _fakeMargin(0),
|
||||
_touchPress(false), _touchRightButton(false), _touchMove(false), _replacingEmojis(false) {
|
||||
setAcceptRichText(false);
|
||||
resize(_st.width, _st.font->height);
|
||||
@@ -58,6 +59,9 @@ FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const
|
||||
|
||||
connect(document(), SIGNAL(contentsChange(int, int, int)), this, SLOT(onDocumentContentsChange(int, int, int)));
|
||||
connect(document(), SIGNAL(contentsChanged()), this, SLOT(onDocumentContentsChanged()));
|
||||
connect(this, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool)));
|
||||
connect(this, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool)));
|
||||
if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu()));
|
||||
}
|
||||
|
||||
void FlatTextarea::onTouchTimer() {
|
||||
@@ -118,6 +122,10 @@ QRect FlatTextarea::getTextRect() const {
|
||||
return rect().marginsRemoved(_st.textMrg + st::textRectMargins);
|
||||
}
|
||||
|
||||
int32 FlatTextarea::fakeMargin() const {
|
||||
return _fakeMargin;
|
||||
}
|
||||
|
||||
void FlatTextarea::paintEvent(QPaintEvent *e) {
|
||||
QPainter p(viewport());
|
||||
p.fillRect(rect(), _st.bgColor->b);
|
||||
@@ -195,7 +203,7 @@ QString FlatTextarea::getText(int32 start, int32 end) const {
|
||||
QString t(fragment.text());
|
||||
if (!full) {
|
||||
if (p < start) {
|
||||
t = t.mid(start - p, end - start - p);
|
||||
t = t.mid(start - p, end - start);
|
||||
} else if (e > end) {
|
||||
t = t.mid(0, end - p);
|
||||
}
|
||||
@@ -264,6 +272,14 @@ bool FlatTextarea::hasText() const {
|
||||
return (from.next() != till);
|
||||
}
|
||||
|
||||
bool FlatTextarea::isUndoAvailable() const {
|
||||
return _undoAvailable;
|
||||
}
|
||||
|
||||
bool FlatTextarea::isRedoAvailable() const {
|
||||
return _redoAvailable;
|
||||
}
|
||||
|
||||
void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) {
|
||||
c.removeSelectedText();
|
||||
|
||||
@@ -301,7 +317,7 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
|
||||
|
||||
QString t(fragment.text());
|
||||
for (const QChar *ch = t.constData(), *e = ch + t.size(); ch != e; ++ch) {
|
||||
if (ch + 1 < e && (ch->isHighSurrogate() || (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3))) {
|
||||
if (ch + 1 < e && (ch->isHighSurrogate() || (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3) || (ch + 1)->unicode() == 0xFE0F)) {
|
||||
emoji = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
|
||||
if (emoji) {
|
||||
if (emoji->len == 4 && (ch + 3 >= e || ((uint32((ch + 2)->unicode()) << 16) | uint32((ch + 3)->unicode())) != emoji->code2)) {
|
||||
@@ -394,6 +410,17 @@ void FlatTextarea::onDocumentContentsChanged() {
|
||||
emit changed();
|
||||
}
|
||||
updatePlaceholder();
|
||||
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
||||
}
|
||||
|
||||
void FlatTextarea::onUndoAvailable(bool avail) {
|
||||
_undoAvailable = avail;
|
||||
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
||||
}
|
||||
|
||||
void FlatTextarea::onRedoAvailable(bool avail) {
|
||||
_redoAvailable = avail;
|
||||
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
||||
}
|
||||
|
||||
bool FlatTextarea::animStep(float64 ms) {
|
||||
@@ -439,11 +466,11 @@ QMimeData *FlatTextarea::createMimeDataFromSelection() const {
|
||||
|
||||
void FlatTextarea::keyPressEvent(QKeyEvent *e) {
|
||||
bool shift = e->modifiers().testFlag(Qt::ShiftModifier);
|
||||
bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier), ctrlGood = (ctrl && cCtrlEnter()) || (!ctrl && !shift && !cCtrlEnter());
|
||||
bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier), ctrlGood = (ctrl && cCtrlEnter()) || (!ctrl && !shift && !cCtrlEnter()) || (ctrl && shift);
|
||||
bool enter = (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return);
|
||||
|
||||
if (enter && ctrlGood) {
|
||||
emit submitted();
|
||||
emit submitted(ctrl && shift);
|
||||
} else if (e->key() == Qt::Key_Escape) {
|
||||
emit cancelled();
|
||||
} else if (e->key() == Qt::Key_Tab || (ctrl && e->key() == Qt::Key_Backtab)) {
|
||||
|
||||
@@ -41,6 +41,7 @@ public:
|
||||
void updatePlaceholder();
|
||||
|
||||
QRect getTextRect() const;
|
||||
int32 fakeMargin() const;
|
||||
|
||||
bool animStep(float64 ms);
|
||||
|
||||
@@ -50,6 +51,9 @@ public:
|
||||
QString getText(int32 start = 0, int32 end = -1) const;
|
||||
bool hasText() const;
|
||||
|
||||
bool isUndoAvailable() const;
|
||||
bool isRedoAvailable() const;
|
||||
|
||||
public slots:
|
||||
|
||||
void onTouchTimer();
|
||||
@@ -57,10 +61,13 @@ public slots:
|
||||
void onDocumentContentsChange(int position, int charsRemoved, int charsAdded);
|
||||
void onDocumentContentsChanged();
|
||||
|
||||
void onUndoAvailable(bool avail);
|
||||
void onRedoAvailable(bool avail);
|
||||
|
||||
signals:
|
||||
|
||||
void changed();
|
||||
void submitted();
|
||||
void submitted(bool ctrlShiftEnter);
|
||||
void cancelled();
|
||||
void tabbed();
|
||||
|
||||
@@ -81,6 +88,8 @@ private:
|
||||
anim::cvalue a_phColor;
|
||||
style::flatTextarea _st;
|
||||
|
||||
bool _undoAvailable, _redoAvailable;
|
||||
|
||||
int32 _fakeMargin;
|
||||
|
||||
QTimer _touchTimer;
|
||||
|
||||
@@ -88,7 +88,7 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0x8000000000000000L | (uint64(w) << 32) | uint64(h);
|
||||
uint64 k = 0x8000000000000000LL | (uint64(w) << 32) | uint64(h);
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend()) {
|
||||
QPixmap p(pixBlurredNoCache(w, h));
|
||||
@@ -101,6 +101,58 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
|
||||
return i.value();
|
||||
}
|
||||
|
||||
const QPixmap &Image::pixSingle(int32 w, int32 h) const {
|
||||
restore();
|
||||
checkload();
|
||||
|
||||
if (w <= 0 || !width() || !height()) {
|
||||
w = width() * cIntRetinaFactor();
|
||||
} else if (cRetina()) {
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0LL;
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend() || i->width() != w || (h && i->height() != h)) {
|
||||
if (i != _sizesCache.cend()) {
|
||||
globalAquiredSize -= int64(i->width()) * i->height() * 4;
|
||||
}
|
||||
QPixmap p(pixNoCache(w, h, true));
|
||||
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
|
||||
i = _sizesCache.insert(k, p);
|
||||
if (!p.isNull()) {
|
||||
globalAquiredSize += int64(p.width()) * p.height() * 4;
|
||||
}
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const {
|
||||
restore();
|
||||
checkload();
|
||||
|
||||
if (w <= 0 || !width() || !height()) {
|
||||
w = width() * cIntRetinaFactor();
|
||||
} else if (cRetina()) {
|
||||
w *= cIntRetinaFactor();
|
||||
h *= cIntRetinaFactor();
|
||||
}
|
||||
uint64 k = 0x8000000000000000LL | 0LL;
|
||||
Sizes::const_iterator i = _sizesCache.constFind(k);
|
||||
if (i == _sizesCache.cend() || i->width() != w || (h && i->height() != h)) {
|
||||
if (i != _sizesCache.cend()) {
|
||||
globalAquiredSize -= int64(i->width()) * i->height() * 4;
|
||||
}
|
||||
QPixmap p(pixBlurredNoCache(w, h));
|
||||
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
|
||||
i = _sizesCache.insert(k, p);
|
||||
if (!p.isNull()) {
|
||||
globalAquiredSize += int64(p.width()) * p.height() * 4;
|
||||
}
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
namespace {
|
||||
static inline uint64 _blurGetColors(const uchar *p) {
|
||||
return p[0] + (p[1] << 16) + ((uint64)p[2] << 32);
|
||||
@@ -336,7 +388,7 @@ int64 imageCacheSize() {
|
||||
return globalAquiredSize;
|
||||
}
|
||||
|
||||
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret) : w(width), h(height), loader(new mtpFileLoader(dc, volume, local, secret)) {
|
||||
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) : w(width), h(height), loader(new mtpFileLoader(dc, volume, local, secret, size)) {
|
||||
}
|
||||
|
||||
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, QByteArray &bytes) : w(width), h(height), loader(0) {
|
||||
@@ -427,11 +479,11 @@ bool StorageImage::loaded() const {
|
||||
return check();
|
||||
}
|
||||
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret) {
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) {
|
||||
QByteArray key(storageKey(dc, volume, local, secret));
|
||||
StorageImages::const_iterator i = storageImages.constFind(key);
|
||||
if (i == storageImages.cend()) {
|
||||
i = storageImages.insert(key, new StorageImage(width, height, dc, volume, local, secret));
|
||||
i = storageImages.insert(key, new StorageImage(width, height, dc, volume, local, secret, size));
|
||||
}
|
||||
return i.value();
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ public:
|
||||
}
|
||||
const QPixmap &pix(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixSingle(int32 w = 0, int32 h = 0) const;
|
||||
const QPixmap &pixBlurredSingle(int32 w = 0, int32 h = 0) const;
|
||||
QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false) const;
|
||||
QPixmap pixBlurredNoCache(int32 w, int32 h = 0) const;
|
||||
|
||||
@@ -107,7 +109,7 @@ LocalImage *getImage(const QPixmap &pixmap, QByteArray format);
|
||||
class StorageImage : public Image {
|
||||
public:
|
||||
|
||||
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret);
|
||||
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
|
||||
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, QByteArray &bytes);
|
||||
|
||||
int32 width() const;
|
||||
@@ -155,7 +157,7 @@ private:
|
||||
mutable mtpFileLoader *loader;
|
||||
};
|
||||
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret);
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes);
|
||||
Image *getImage(int32 width, int32 height, const MTPFileLocation &location);
|
||||
|
||||
@@ -166,7 +168,7 @@ public:
|
||||
}
|
||||
ImagePtr(const QPixmap &pixmap, QByteArray format) : Parent(getImage(pixmap, format)) {
|
||||
}
|
||||
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret) : Parent(getImage(width, height, dc, volume, local, secret)) {
|
||||
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0) : Parent(getImage(width, height, dc, volume, local, secret, size)) {
|
||||
}
|
||||
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes) : Parent(getImage(width, height, dc, volume, local, secret, bytes)) {
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace {
|
||||
return ch.isSpace() || (ch < 32 && !(rich && ch == TextCommand)) || (ch == QChar::ParagraphSeparator) || (ch == QChar::LineSeparator) || (ch == QChar::ObjectReplacementCharacter) || (ch == QChar::SoftHyphen) || (ch == QChar::CarriageReturn) || (ch == QChar::Tabulation);
|
||||
}
|
||||
inline bool chIsBad(QChar ch) {
|
||||
return (ch == 0) || (ch >= 8232 && ch < 8239) || (ch >= 65024 && ch < 65040) || (ch >= 127 && ch < 160 && ch != 156);
|
||||
return (ch == 0) || (ch >= 8232 && ch < 8239) || (ch >= 65024 && ch < 65040 && ch != 65039) || (ch >= 127 && ch < 160 && ch != 156);
|
||||
}
|
||||
inline bool chIsTrimmed(QChar ch, bool rich = false) {
|
||||
return (!rich || ch != TextCommand) && (chIsSpace(ch) || chIsBad(ch));
|
||||
@@ -101,6 +101,28 @@ namespace {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool chIsSentenceEnd(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case '.':
|
||||
case '?':
|
||||
case '!':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool chIsSentencePartEnd(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case ',':
|
||||
case ':':
|
||||
case ';':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool chIsParagraphSeparator(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case QChar::LineFeed:
|
||||
@@ -541,6 +563,12 @@ public:
|
||||
ch = *ptr;
|
||||
chInt = (chInt << 16) | 0x20E3;
|
||||
}
|
||||
} else if (ptr + 1 < end && (ptr + 1)->unicode() == 0xFE0F) { // check for 32bit not surrogate emoji
|
||||
_t->_text.push_back(ch);
|
||||
skipBack = -1;
|
||||
++ptr;
|
||||
ch = *ptr;
|
||||
chInt = (chInt << 16) | 0xFE0F;
|
||||
}
|
||||
|
||||
lastSkipped = skip;
|
||||
@@ -3947,7 +3975,7 @@ QString textAccentFold(const QString &text) {
|
||||
continue;
|
||||
}
|
||||
if (ch->isHighSurrogate() && ch + 1 < e && (ch + 1)->isLowSurrogate()) {
|
||||
QChar noAccent = QChar::surrogateToUcs4(*ch, *(ch + 1));
|
||||
QChar noAccent = chNoAccent(QChar::surrogateToUcs4(*ch, *(ch + 1)));
|
||||
if (noAccent.unicode() > 0) {
|
||||
copying = true;
|
||||
result[i] = noAccent;
|
||||
@@ -3972,6 +4000,74 @@ QString textSearchKey(const QString &text) {
|
||||
return textAccentFold(text.trimmed().toLower());
|
||||
}
|
||||
|
||||
bool textSplit(QString &sendingText, QString &leftText, int32 limit) {
|
||||
if (leftText.isEmpty() || !limit) return false;
|
||||
|
||||
LinkRanges lnkRanges = textParseLinks(leftText);
|
||||
int32 currentLink = 0, lnkCount = lnkRanges.size();
|
||||
|
||||
int32 s = 0, half = limit / 2, goodLevel = 0;
|
||||
for (const QChar *start = leftText.constData(), *ch = start, *end = leftText.constEnd(), *good = ch; ch != end; ++ch, ++s) {
|
||||
while (currentLink < lnkCount && ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len) {
|
||||
++currentLink;
|
||||
}
|
||||
|
||||
bool inLink = (currentLink < lnkCount) && (ch > lnkRanges[currentLink].from) && (ch < lnkRanges[currentLink].from + lnkRanges[currentLink].len);
|
||||
if (s > half) {
|
||||
if (inLink) {
|
||||
if (!goodLevel) good = ch;
|
||||
} else {
|
||||
if (chIsNewline(*ch)) {
|
||||
if (ch + 1 < end && chIsNewline(*(ch + 1)) && goodLevel <= 7) {
|
||||
goodLevel = 7;
|
||||
good = ch;
|
||||
} else if (goodLevel <= 6) {
|
||||
goodLevel = 6;
|
||||
good = ch;
|
||||
}
|
||||
} else if (chIsSpace(*ch)) {
|
||||
if (chIsSentenceEnd(*(ch - 1)) && goodLevel <= 5) {
|
||||
goodLevel = 5;
|
||||
good = ch;
|
||||
} else if (chIsSentencePartEnd(*(ch - 1)) && goodLevel <= 4) {
|
||||
goodLevel = 4;
|
||||
good = ch;
|
||||
} else if (goodLevel <= 3) {
|
||||
goodLevel = 3;
|
||||
good = ch;
|
||||
}
|
||||
} else if (chIsWordSeparator(*(ch - 1)) && goodLevel <= 2) {
|
||||
goodLevel = 2;
|
||||
good = ch;
|
||||
} else if (goodLevel <= 1) {
|
||||
goodLevel = 1;
|
||||
good = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ch->isHighSurrogate()) {
|
||||
if (ch + 1 < end && (ch + 1)->isLowSurrogate()) {
|
||||
++ch;
|
||||
}
|
||||
} else {
|
||||
if (ch + 1 < end && ((((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3) || (ch + 1)->unicode() == 0xFE0F)) {
|
||||
if (getEmoji((ch->unicode() << 16) | (ch + 1)->unicode())) {
|
||||
++ch;
|
||||
++s;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s >= limit) {
|
||||
sendingText = leftText.mid(0, good - start);
|
||||
leftText = leftText.mid(good - start);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
sendingText = leftText;
|
||||
leftText = QString();
|
||||
return true;
|
||||
}
|
||||
|
||||
LinkRanges textParseLinks(const QString &text, bool rich) {
|
||||
LinkRanges lnkRanges;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ QString textRichPrepare(const QString &text);
|
||||
QString textOneLine(const QString &text, bool trim = true, bool rich = false);
|
||||
QString textAccentFold(const QString &text);
|
||||
QString textSearchKey(const QString &text);
|
||||
bool textSplit(QString &sendingText, QString &leftText, int32 limit);
|
||||
|
||||
struct LinkRange {
|
||||
LinkRange() : from(0), len(0) {
|
||||
|
||||
@@ -37,7 +37,7 @@ static const uint32 FullItemSel = 0xFFFFFFFF;
|
||||
|
||||
typedef QMap<int32, HistoryItem*> SelectedItemSet;
|
||||
|
||||
extern TextParseOptions _textNameOptions;
|
||||
extern TextParseOptions _textNameOptions, _textDlgOptions;
|
||||
|
||||
struct NotifySettings {
|
||||
NotifySettings() : mute(0), sound("default"), previews(true), events(1) {
|
||||
@@ -82,30 +82,9 @@ struct PeerData {
|
||||
ChatData *asChat();
|
||||
const ChatData *asChat() const;
|
||||
|
||||
void updateName(const QString &newName, const QString &newNameOrPhone);
|
||||
void updateName(const QString &newName, const QString &newNameOrPhone, const QString &newUsername);
|
||||
|
||||
void fillNames() {
|
||||
names.clear();
|
||||
chars.clear();
|
||||
QString toIndex = textAccentFold(name);
|
||||
if (nameOrPhone != name) {
|
||||
toIndex += qsl(" ") + textAccentFold(nameOrPhone);
|
||||
}
|
||||
if (cRussianLetters().match(toIndex).hasMatch()) {
|
||||
toIndex += qsl(" ") + translitRusEng(toIndex);
|
||||
}
|
||||
toIndex += qsl(" ") + rusKeyboardLayoutSwitch(toIndex);
|
||||
|
||||
if (name.midRef(0, 8) == "Telegram") {
|
||||
int a = 0;
|
||||
}
|
||||
|
||||
QStringList namesList = toIndex.toLower().split(cWordSplit(), QString::SkipEmptyParts);
|
||||
for (QStringList::const_iterator i = namesList.cbegin(), e = namesList.cend(); i != e; ++i) {
|
||||
names.insert(*i);
|
||||
chars.insert(i->at(0));
|
||||
}
|
||||
}
|
||||
void fillNames();
|
||||
|
||||
virtual void nameUpdated() {
|
||||
}
|
||||
@@ -152,12 +131,13 @@ struct UserData : public PeerData {
|
||||
UserData(const PeerId &id) : PeerData(id), lnk(new PeerLink(this)), onlineTill(0), contact(-1), photosCount(-1) {
|
||||
}
|
||||
void setPhoto(const MTPUserProfilePhoto &photo);
|
||||
void setName(const QString &first, const QString &last, const QString &phoneName);
|
||||
void setName(const QString &first, const QString &last, const QString &phoneName, const QString &username);
|
||||
void setPhone(const QString &newPhone);
|
||||
void nameUpdated();
|
||||
|
||||
QString firstName;
|
||||
QString lastName;
|
||||
QString username;
|
||||
QString phone;
|
||||
Text nameText;
|
||||
PhotoId photoId;
|
||||
@@ -208,6 +188,9 @@ struct PhotoData {
|
||||
ImagePtr full;
|
||||
ChatData *chat; // for chat photos connection
|
||||
// geo, caption
|
||||
|
||||
int32 cachew;
|
||||
QPixmap cache;
|
||||
};
|
||||
|
||||
class PhotoLink : public ITextLink {
|
||||
@@ -601,6 +584,7 @@ enum HistoryMediaType {
|
||||
MediaTypeContact,
|
||||
MediaTypeAudio,
|
||||
MediaTypeDocument,
|
||||
MediaTypeImageLink,
|
||||
|
||||
MediaTypeCount
|
||||
};
|
||||
@@ -675,9 +659,10 @@ struct History : public QList<HistoryBlock*> {
|
||||
HistoryItem *doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem *adding, bool newMsg);
|
||||
|
||||
void newItemAdded(HistoryItem *item);
|
||||
void unregTyping(UserData *from);
|
||||
|
||||
void inboxRead(bool byThisInstance = false);
|
||||
void outboxRead();
|
||||
void inboxRead(HistoryItem *wasRead);
|
||||
void outboxRead(HistoryItem *wasRead);
|
||||
|
||||
void setUnreadCount(int32 newUnreadCount, bool psUpdate = true);
|
||||
void setMsgCount(int32 newMsgCount);
|
||||
@@ -751,6 +736,8 @@ struct History : public QList<HistoryBlock*> {
|
||||
int32 lastWidth, lastScrollTop;
|
||||
bool mute;
|
||||
|
||||
mtpRequestId sendRequestId;
|
||||
|
||||
// for dialog drawing
|
||||
Text nameText;
|
||||
void updateNameText();
|
||||
@@ -1178,12 +1165,6 @@ public:
|
||||
virtual uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
|
||||
return (from << 16) | to;
|
||||
}
|
||||
virtual bool getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const {
|
||||
return false;
|
||||
}
|
||||
virtual bool getVideoCoords(VideoData *video, int32 &x, int32 &y, int32 &w) const {
|
||||
return false;
|
||||
}
|
||||
virtual int32 itemType() const {
|
||||
return MsgType;
|
||||
}
|
||||
@@ -1237,20 +1218,21 @@ HistoryItem *regItem(HistoryItem *item, bool returnExisting = false);
|
||||
class HistoryMedia : public HistoryElem {
|
||||
public:
|
||||
|
||||
HistoryMedia(int32 width = 0) : w(width) {
|
||||
}
|
||||
|
||||
virtual HistoryMediaType type() const = 0;
|
||||
virtual const QString inDialogsText() const = 0;
|
||||
virtual bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0;
|
||||
virtual int32 countHeight(const HistoryItem *parent, int32 width = -1) const {
|
||||
return height();
|
||||
}
|
||||
virtual int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0) {
|
||||
w = qMin(width, _maxw);
|
||||
return _height;
|
||||
}
|
||||
virtual TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0;
|
||||
virtual void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const = 0;
|
||||
virtual bool getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const {
|
||||
return false;
|
||||
}
|
||||
virtual bool getVideoCoords(VideoData *video, int32 &x, int32 &y, int32 &w) const {
|
||||
return false;
|
||||
}
|
||||
virtual bool uploading() const {
|
||||
return false;
|
||||
}
|
||||
@@ -1269,6 +1251,14 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 currentWidth() const {
|
||||
return w;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
int32 w;
|
||||
|
||||
};
|
||||
|
||||
class HistoryPhoto : public HistoryMedia {
|
||||
@@ -1288,7 +1278,6 @@ public:
|
||||
const QString inDialogsText() const;
|
||||
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
bool getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const;
|
||||
HistoryMedia *clone() const;
|
||||
|
||||
PhotoData *photo() const {
|
||||
@@ -1307,7 +1296,7 @@ public:
|
||||
private:
|
||||
PhotoData *data;
|
||||
TextLinkPtr openl;
|
||||
int32 w;
|
||||
|
||||
};
|
||||
|
||||
QString formatSizeText(qint64 size);
|
||||
@@ -1319,14 +1308,12 @@ public:
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeVideo;
|
||||
}
|
||||
const QString inDialogsText() const;
|
||||
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
bool getVideoCoords(VideoData *video, int32 &x, int32 &y, int32 &w) const;
|
||||
bool uploading() const {
|
||||
return (data->status == FileUploading);
|
||||
}
|
||||
@@ -1338,8 +1325,7 @@ public:
|
||||
private:
|
||||
VideoData *data;
|
||||
TextLinkPtr _openl, _savel, _cancell;
|
||||
int32 w;
|
||||
|
||||
|
||||
QString _size;
|
||||
int32 _thumbw, _thumbx, _thumby;
|
||||
|
||||
@@ -1354,7 +1340,6 @@ public:
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeAudio;
|
||||
}
|
||||
@@ -1372,7 +1357,6 @@ public:
|
||||
private:
|
||||
AudioData *data;
|
||||
TextLinkPtr _openl, _savel, _cancell;
|
||||
int32 w;
|
||||
|
||||
QString _size;
|
||||
|
||||
@@ -1413,7 +1397,6 @@ private:
|
||||
|
||||
DocumentData *data;
|
||||
TextLinkPtr _openl, _savel, _cancell;
|
||||
int32 w;
|
||||
|
||||
int32 _namew;
|
||||
QString _name, _size;
|
||||
@@ -1449,6 +1432,81 @@ private:
|
||||
UserData *contact;
|
||||
};
|
||||
|
||||
void initImageLinkManager();
|
||||
void reinitImageLinkManager();
|
||||
void deinitImageLinkManager();
|
||||
|
||||
enum ImageLinkType {
|
||||
InvalidImageLink = 0,
|
||||
YouTubeLink,
|
||||
InstagramLink,
|
||||
GoogleMapsLink
|
||||
};
|
||||
struct ImageLinkData {
|
||||
ImageLinkData(const QString &id) : id(id), type(InvalidImageLink), loading(false) {
|
||||
}
|
||||
|
||||
QString id;
|
||||
QString title, duration;
|
||||
ImagePtr thumb;
|
||||
TextLinkPtr openl;
|
||||
ImageLinkType type;
|
||||
bool loading;
|
||||
|
||||
void load();
|
||||
};
|
||||
|
||||
class ImageLinkManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ImageLinkManager() : manager(0), black(0) {
|
||||
}
|
||||
void init();
|
||||
void reinit();
|
||||
void deinit();
|
||||
|
||||
void getData(ImageLinkData *data);
|
||||
|
||||
~ImageLinkManager() {
|
||||
deinit();
|
||||
}
|
||||
|
||||
public slots:
|
||||
void onFinished(QNetworkReply *reply);
|
||||
void onFailed(QNetworkReply *reply);
|
||||
|
||||
private:
|
||||
void failed(ImageLinkData *data);
|
||||
|
||||
QNetworkAccessManager *manager;
|
||||
QMap<QNetworkReply*, ImageLinkData*> dataLoadings, imageLoadings;
|
||||
QMap<ImageLinkData*, int32> serverRedirects;
|
||||
ImagePtr *black;
|
||||
};
|
||||
|
||||
class HistoryImageLink : public HistoryMedia {
|
||||
public:
|
||||
|
||||
HistoryImageLink(const QString &url, int32 width = 0);
|
||||
int32 fullWidth() const;
|
||||
int32 fullHeight() const;
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeImageLink;
|
||||
}
|
||||
const QString inDialogsText() const;
|
||||
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
HistoryMedia *clone() const;
|
||||
|
||||
private:
|
||||
ImageLinkData *data;
|
||||
|
||||
};
|
||||
|
||||
class HistoryMessage : public HistoryItem {
|
||||
public:
|
||||
|
||||
@@ -1475,8 +1533,6 @@ public:
|
||||
uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
|
||||
return _text.adjustSelection(from, to, type);
|
||||
}
|
||||
bool getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const;
|
||||
bool getVideoCoords(VideoData *video, int32 &x, int32 &y, int32 &w) const;
|
||||
|
||||
void drawInDialog(QPainter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const;
|
||||
QString notificationHeader() const;
|
||||
@@ -1563,7 +1619,6 @@ public:
|
||||
uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
|
||||
return _text.adjustSelection(from, to, type);
|
||||
}
|
||||
bool getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const;
|
||||
|
||||
void drawInDialog(QPainter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const;
|
||||
QString notificationText() const;
|
||||
|
||||
@@ -463,16 +463,18 @@ void HistoryList::dragActionCancel() {
|
||||
}
|
||||
|
||||
void HistoryList::itemRemoved(HistoryItem *item) {
|
||||
if (_dragItem == item) {
|
||||
dragActionCancel();
|
||||
}
|
||||
|
||||
SelectedItems::iterator i = _selected.find(item);
|
||||
if (i != _selected.cend()) {
|
||||
_selected.erase(i);
|
||||
historyWidget->updateTopBarSelection();
|
||||
}
|
||||
|
||||
if (_dragAction == NoDrag) return;
|
||||
|
||||
if (_dragItem == item) {
|
||||
dragActionCancel();
|
||||
}
|
||||
|
||||
onUpdateSelected();
|
||||
|
||||
if (_dragSelFrom == item) _dragSelFrom = 0;
|
||||
@@ -836,24 +838,6 @@ void HistoryList::copyContextText() {
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryList::getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const {
|
||||
HistoryItem *hoveredItem = App::hoveredLinkItem();
|
||||
if (hoveredItem && hoveredItem->getPhotoCoords(photo, x, y, w)) {
|
||||
y += height() - hist->height - st::historyPadding + hoveredItem->block()->y + hoveredItem->y;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HistoryList::getVideoCoords(VideoData *video, int32 &x, int32 &y, int32 &w) const {
|
||||
HistoryItem *hoveredItem = App::hoveredItem();
|
||||
if (hoveredItem && hoveredItem->getVideoCoords(video, x, y, w)) {
|
||||
y += height() - hist->height - st::historyPadding + hoveredItem->block()->y + hoveredItem->y;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void HistoryList::resizeEvent(QResizeEvent *e) {
|
||||
onUpdateSelected();
|
||||
}
|
||||
@@ -992,6 +976,14 @@ HistoryItem *HistoryList::nextItem(HistoryItem *item) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool HistoryList::canCopySelected() const {
|
||||
return !_selected.isEmpty();
|
||||
}
|
||||
|
||||
bool HistoryList::canDeleteSelected() const {
|
||||
return !_selected.isEmpty() && (_selected.cbegin().value() == FullItemSel);
|
||||
}
|
||||
|
||||
void HistoryList::getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const {
|
||||
selectedForForward = selectedForDelete = 0;
|
||||
for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) {
|
||||
@@ -1237,7 +1229,7 @@ MessageField::MessageField(HistoryWidget *history, const style::flatTextarea &st
|
||||
}
|
||||
|
||||
void MessageField::onChange() {
|
||||
int newh = ceil(document()->size().height());
|
||||
int newh = ceil(document()->size().height()) + 2 * fakeMargin();
|
||||
if (newh > st::maxFieldHeight) {
|
||||
newh = st::maxFieldHeight;
|
||||
} else if (newh < st::minFieldHeight) {
|
||||
@@ -1552,7 +1544,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
|
||||
, titlePeerTextWidth(0)
|
||||
, bg(st::msgBG)
|
||||
, hiderOffered(false)
|
||||
, _scrollDelta(0) {
|
||||
, _scrollDelta(0)
|
||||
, _typingRequest(0) {
|
||||
_scroll.setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
setAcceptDrops(true);
|
||||
@@ -1562,8 +1555,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
|
||||
connect(&_send, SIGNAL(clicked()), this, SLOT(onSend()));
|
||||
connect(&_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
|
||||
connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
|
||||
connect(&_field, SIGNAL(submitted()), this, SLOT(onSend()));
|
||||
connect(&_field, SIGNAL(cancelled()), this, SIGNAL(cancelled()));
|
||||
connect(&_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool)));
|
||||
connect(&_field, SIGNAL(cancelled()), this, SLOT(onCancel()));
|
||||
connect(&_field, SIGNAL(tabbed()), this, SLOT(onFieldTabbed()));
|
||||
connect(&_field, SIGNAL(resized()), this, SLOT(onFieldResize()));
|
||||
connect(&_field, SIGNAL(focused()), this, SLOT(onFieldFocused()));
|
||||
@@ -1573,9 +1566,12 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
|
||||
connect(App::wnd()->windowHandle(), SIGNAL(visibleChanged(bool)), this, SLOT(onVisibleChanged()));
|
||||
connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer()));
|
||||
connect(&_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr)));
|
||||
connect(&_typingStopTimer, SIGNAL(timeout()), this, SLOT(cancelTyping()));
|
||||
|
||||
_scrollTimer.setSingleShot(false);
|
||||
|
||||
_typingStopTimer.setSingleShot(true);
|
||||
|
||||
_animActiveTimer.setSingleShot(false);
|
||||
connect(&_animActiveTimer, SIGNAL(timeout()), this, SLOT(onAnimActiveStep()));
|
||||
|
||||
@@ -1611,12 +1607,29 @@ void HistoryWidget::onTextChange() {
|
||||
updateTyping();
|
||||
}
|
||||
|
||||
void HistoryWidget::cancelTyping() {
|
||||
if (_typingRequest) {
|
||||
MTP::cancel(_typingRequest);
|
||||
_typingRequest = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::updateTyping(bool typing) {
|
||||
uint64 ms = getms() + 10000;
|
||||
uint64 ms = getms(true) + 10000;
|
||||
if (noTypingUpdate || !hist || (typing && (hist->myTyping + 5000 > ms)) || (!typing && (hist->myTyping + 5000 <= ms))) return;
|
||||
|
||||
hist->myTyping = typing ? ms : 0;
|
||||
if (typing) MTP::send(MTPmessages_SetTyping(histPeer->input, MTP_bool(typing)));
|
||||
cancelTyping();
|
||||
if (typing) {
|
||||
_typingRequest = MTP::send(MTPmessages_SetTyping(histPeer->input, typing ? MTP_sendMessageTypingAction() : MTP_sendMessageCancelAction()), rpcDone(&HistoryWidget::typingDone));
|
||||
_typingStopTimer.start(5000);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::typingDone(const MTPBool &result, mtpRequestId req) {
|
||||
if (_typingRequest == req) {
|
||||
_typingRequest = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::activate() {
|
||||
@@ -1673,9 +1686,6 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||
App::main()->offerPeer(peer);
|
||||
return;
|
||||
}
|
||||
if (peer && !msgId) {
|
||||
App::main()->dialogsClear();
|
||||
}
|
||||
if (hist) {
|
||||
if (histPeer->id == peer) {
|
||||
if (msgId != hist->activeMsgId) {
|
||||
@@ -1763,10 +1773,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||
App::mousedItem(0);
|
||||
|
||||
if (peer) {
|
||||
App::forgetPhotos();
|
||||
App::forgetVideos();
|
||||
App::forgetAudios();
|
||||
App::forgetDocuments();
|
||||
App::forgetMedia();
|
||||
serviceImageCacheSize = imageCacheSize();
|
||||
MTP::clearLoaderPriorities();
|
||||
histInputPeer = histPeer->input;
|
||||
@@ -1797,7 +1804,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||
App::main()->peerUpdated(histPeer);
|
||||
|
||||
noTypingUpdate = true;
|
||||
_field.setPlainText(hist->draft);
|
||||
setFieldText(hist->draft);
|
||||
_field.setFocus();
|
||||
if (!hist->draft.isEmpty()) {
|
||||
_field.setTextCursor(hist->draftCur);
|
||||
@@ -2192,8 +2199,8 @@ QString HistoryWidget::prepareMessage(QString result) {
|
||||
|
||||
result = result.replace(" --", QString::fromUtf8(" \xe2\x80\x94"));
|
||||
result = result.replace("-- ", QString::fromUtf8("\xe2\x80\x94 "));
|
||||
result = result.replace("<<", qsl("\xab"));
|
||||
result = result.replace(">>", qsl("\xbb"));
|
||||
result = result.replace("<<", QString::fromUtf8("\xc2\xab"));
|
||||
result = result.replace(">>", QString::fromUtf8("\xc2\xbb"));
|
||||
|
||||
return (cReplaceEmojis() ? replaceEmojis(result) : result).trimmed();
|
||||
}
|
||||
@@ -2205,31 +2212,21 @@ void HistoryWidget::onHistoryToEnd() {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::onSend() {
|
||||
void HistoryWidget::onSend(bool ctrlShiftEnter) {
|
||||
if (!hist) return;
|
||||
|
||||
QString text = prepareMessage(_field.getText());
|
||||
if (!text.isEmpty()) {
|
||||
App::main()->readServerHistory(hist, false);
|
||||
|
||||
MsgId newId = clientMsgId();
|
||||
uint64 randomId = MTP::nonce<uint64>();
|
||||
|
||||
App::historyRegRandom(randomId, newId);
|
||||
|
||||
hist->loadAround(0);
|
||||
|
||||
MTPstring msgText(MTP_string(text));
|
||||
hist->addToBack(MTP_message(MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(histPeer->id), MTP_bool(true), MTP_bool(true), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty()));
|
||||
App::main()->historyToDown(hist);
|
||||
App::main()->dialogsToUp();
|
||||
peerMessagesUpdated();
|
||||
App::main()->sendPreparedText(hist, prepareMessage(_field.getText()));
|
||||
|
||||
MTP::send(MTPmessages_SendMessage(histInputPeer, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId));
|
||||
_field.setPlainText("");
|
||||
setFieldText(QString());
|
||||
if (!_attachType.isHidden()) _attachType.hideStart();
|
||||
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
|
||||
}
|
||||
|
||||
_field.setFocus();
|
||||
}
|
||||
|
||||
@@ -2251,23 +2248,24 @@ mtpRequestId HistoryWidget::onForward(const PeerId &peer, SelectedItemSet toForw
|
||||
|
||||
hist->loadAround(0);
|
||||
if (item->id > 0 && msg) {
|
||||
App::main()->readServerHistory(item->history(), false);
|
||||
App::main()->readServerHistory(hist, false);
|
||||
|
||||
newId = clientMsgId();
|
||||
hist->addToBackForwarded(newId, msg);
|
||||
MTP::send(MTPmessages_ForwardMessage(histPeer->input, MTP_int(item->id), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId));
|
||||
hist->sendRequestId = MTP::send(MTPmessages_ForwardMessage(histPeer->input, MTP_int(item->id), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
} else if (srv || (msg && msg->selectedText(FullItemSel).isEmpty())) {
|
||||
// newId = clientMsgId();
|
||||
// MTP::send(MTPmessages_ForwardMessage(histPeer->input, MTP_int(item->id), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId));
|
||||
// hist->sendRequestId = MTP::send(MTPmessages_ForwardMessage(histPeer->input, MTP_int(item->id), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
} else if (msg) {
|
||||
App::main()->readServerHistory(item->history(), false);
|
||||
App::main()->readServerHistory(hist, false);
|
||||
|
||||
newId = clientMsgId();
|
||||
|
||||
MTPstring msgText(MTP_string(msg->selectedText(FullItemSel)));
|
||||
|
||||
hist->addToBack(MTP_message(MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(histPeer->id), MTP_bool(true), MTP_bool(true), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty()));
|
||||
MTP::send(MTPmessages_SendMessage(histPeer->input, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId));
|
||||
int32 flags = 0x01 | 0x02; // unread, out
|
||||
hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(histPeer->id), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty()));
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(histPeer->input, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
}
|
||||
if (newId) {
|
||||
App::historyRegRandom(randomId, newId);
|
||||
@@ -2282,14 +2280,16 @@ mtpRequestId HistoryWidget::onForward(const PeerId &peer, SelectedItemSet toForw
|
||||
PeerData *toPeer = App::peerLoaded(peer);
|
||||
if (!toPeer) return 0;
|
||||
|
||||
App::main()->readServerHistory(App::history(toPeer->id), false);
|
||||
History *hist = App::history(peer);
|
||||
App::main()->readServerHistory(hist, false);
|
||||
|
||||
QVector<MTPint> ids;
|
||||
ids.reserve(toForward.size());
|
||||
for (SelectedItemSet::const_iterator i = toForward.cbegin(), e = toForward.cend(); i != e; ++i) {
|
||||
ids.push_back(MTP_int(i.value()->id));
|
||||
}
|
||||
return MTP::send(MTPmessages_ForwardMessages(toPeer->input, MTP_vector<MTPint>(ids)), App::main()->rpcDone(&MainWidget::forwardDone, peer));
|
||||
hist->sendRequestId = MTP::send(MTPmessages_ForwardMessages(toPeer->input, MTP_vector<MTPint>(ids)), App::main()->rpcDone(&MainWidget::forwardDone, peer), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
return hist->sendRequestId;
|
||||
}
|
||||
|
||||
void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) {
|
||||
@@ -2298,25 +2298,29 @@ void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) {
|
||||
App::main()->showPeer(peer, 0, false, true);
|
||||
if (!hist) return;
|
||||
|
||||
shareContact(contact->phone, contact->firstName, contact->lastName, int32(contact->id & 0xFFFFFFFF));
|
||||
shareContact(peer, contact->phone, contact->firstName, contact->lastName, int32(contact->id & 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
void HistoryWidget::shareContact(const QString &phone, const QString &fname, const QString &lname, int32 userId) {
|
||||
App::main()->readServerHistory(hist, false);
|
||||
void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, int32 userId) {
|
||||
History *h = App::history(peer);
|
||||
App::main()->readServerHistory(h, false);
|
||||
|
||||
uint64 randomId = MTP::nonce<uint64>();
|
||||
MsgId newId = clientMsgId();
|
||||
|
||||
hist->loadAround(0);
|
||||
h->loadAround(0);
|
||||
|
||||
hist->addToBack(MTP_message(MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(histPeer->id), MTP_bool(true), MTP_bool(true), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname), MTP_int(userId))));
|
||||
int32 flags = 0x01 | 0x02; // unread, out
|
||||
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(peer), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname), MTP_int(userId))));
|
||||
|
||||
MTP::send(MTPmessages_SendMedia(histPeer->input, MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId));
|
||||
h->sendRequestId = MTP::send(MTPmessages_SendMedia(App::peer(peer)->input, MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
|
||||
App::historyRegRandom(randomId, newId);
|
||||
App::main()->historyToDown(hist);
|
||||
if (hist && histPeer && peer == histPeer->id) {
|
||||
App::main()->historyToDown(hist);
|
||||
}
|
||||
App::main()->dialogsToUp();
|
||||
peerMessagesUpdated();
|
||||
peerMessagesUpdated(peer);
|
||||
}
|
||||
|
||||
void HistoryWidget::onSendPaths(const PeerId &peer) {
|
||||
@@ -2751,11 +2755,15 @@ void HistoryWidget::shareContactConfirmation(const QString &phone, const QString
|
||||
App::wnd()->showLayer(new PhotoSendBox(phone, fname, lname));
|
||||
}
|
||||
|
||||
void HistoryWidget::uploadConfirmImageUncompressed() {
|
||||
void HistoryWidget::uploadConfirmImageUncompressed(bool ctrlShiftEnter) {
|
||||
if (!hist || !confirmImageId || confirmImage.isNull()) return;
|
||||
|
||||
App::wnd()->activateWindow();
|
||||
imageLoader.append(confirmImage, histPeer->id, ToPrepareDocument);
|
||||
PeerId peerId = histPeer->id;
|
||||
if (confirmWithText) {
|
||||
onSend(ctrlShiftEnter);
|
||||
}
|
||||
imageLoader.append(confirmImage, peerId, ToPrepareDocument, ctrlShiftEnter);
|
||||
confirmImageId = 0;
|
||||
confirmWithText = false;
|
||||
confirmImage = QImage();
|
||||
@@ -2792,22 +2800,25 @@ void HistoryWidget::onPhotoReady() {
|
||||
void HistoryWidget::onPhotoFailed(quint64 id) {
|
||||
}
|
||||
|
||||
void HistoryWidget::confirmShareContact(const QString &phone, const QString &fname, const QString &lname) {
|
||||
void HistoryWidget::confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname) {
|
||||
if (!histPeer) return;
|
||||
|
||||
PeerId peerId = histPeer->id;
|
||||
if (0xFFFFFFFFFFFFFFFFL == confirmImageId) {
|
||||
if (confirmWithText) {
|
||||
onSend();
|
||||
onSend(ctrlShiftEnter);
|
||||
}
|
||||
confirmImageId = 0;
|
||||
confirmWithText = false;
|
||||
confirmImage = QImage();
|
||||
}
|
||||
shareContact(phone, fname, lname);
|
||||
shareContact(peerId, phone, fname, lname);
|
||||
}
|
||||
|
||||
void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
|
||||
if (img.id == confirmImageId) {
|
||||
if (confirmWithText) {
|
||||
onSend();
|
||||
onSend(img.ctrlShiftEnter);
|
||||
}
|
||||
confirmImageId = 0;
|
||||
confirmWithText = false;
|
||||
@@ -2828,10 +2839,12 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
|
||||
History *h = App::history(img.peer);
|
||||
if (img.type == ToPreparePhoto) {
|
||||
h->loadAround(0);
|
||||
h->addToBack(MTP_message(MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTP_bool(true), MTP_bool(true), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(img.photo)));
|
||||
int32 flags = 0x01 | 0x02; // unread, out
|
||||
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(img.photo)));
|
||||
} else if (img.type == ToPrepareDocument) {
|
||||
h->loadAround(0);
|
||||
h->addToBack(MTP_message(MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTP_bool(true), MTP_bool(true), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaDocument(img.document)));
|
||||
int32 flags = 0x01 | 0x02; // unread, out
|
||||
h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaDocument(img.document)));
|
||||
}
|
||||
|
||||
if (hist && histPeer && img.peer == histPeer->id) {
|
||||
@@ -2842,7 +2855,7 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
|
||||
}
|
||||
|
||||
void HistoryWidget::cancelSendImage() {
|
||||
if (confirmImageId && confirmWithText) _field.setPlainText(QString());
|
||||
if (confirmImageId && confirmWithText) setFieldText(QString());
|
||||
confirmImageId = 0;
|
||||
confirmWithText = false;
|
||||
confirmImage = QImage();
|
||||
@@ -2856,7 +2869,8 @@ void HistoryWidget::onPhotoUploaded(MsgId newId, const MTPInputFile &file) {
|
||||
|
||||
uint64 randomId = MTP::nonce<uint64>();
|
||||
App::historyRegRandom(randomId, newId);
|
||||
MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedPhoto(file), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId));
|
||||
History *hist = item->history();
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedPhoto(file), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2871,7 +2885,8 @@ void HistoryWidget::onDocumentUploaded(MsgId newId, const MTPInputFile &file) {
|
||||
uint64 randomId = MTP::nonce<uint64>();
|
||||
App::historyRegRandom(randomId, newId);
|
||||
DocumentData *document = media->document();
|
||||
MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedDocument(file, MTP_string(document->name), MTP_string(document->mime)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId));
|
||||
History *hist = item->history();
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedDocument(file, MTP_string(document->name), MTP_string(document->mime)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2887,7 +2902,8 @@ void HistoryWidget::onThumbDocumentUploaded(MsgId newId, const MTPInputFile &fil
|
||||
uint64 randomId = MTP::nonce<uint64>();
|
||||
App::historyRegRandom(randomId, newId);
|
||||
DocumentData *document = media->document();
|
||||
MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->name), MTP_string(document->mime)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId));
|
||||
History *hist = item->history();
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->name), MTP_string(document->mime)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3118,11 +3134,11 @@ void HistoryWidget::onFieldTabbed() {
|
||||
if (isImg) {
|
||||
QImage img(cWorkingDir() + fname);
|
||||
if (!img.isNull()) {
|
||||
_field.setPlainText(text);
|
||||
setFieldText(text);
|
||||
uploadImage(img, !text.isEmpty());
|
||||
}
|
||||
} else {
|
||||
_field.setPlainText(text);
|
||||
setFieldText(text);
|
||||
uploadFile(cWorkingDir() + fname, !text.isEmpty());
|
||||
}
|
||||
} else if (isContact) {
|
||||
@@ -3134,13 +3150,12 @@ void HistoryWidget::onFieldTabbed() {
|
||||
}
|
||||
QStringList data = contact.split(QChar(' '));
|
||||
if (data.size() > 1) {
|
||||
_field.setPlainText(text);
|
||||
|
||||
setFieldText(text);
|
||||
QString phone = data.at(0).trimmed(), fname = data.at(1).trimmed(), lname = (data.size() > 2) ? static_cast<QStringList>(data.mid(2)).join(QChar(' ')).trimmed() : QString();
|
||||
shareContactConfirmation(phone, fname, lname, !text.isEmpty());
|
||||
}
|
||||
} else {
|
||||
_field.setPlainText(t);
|
||||
setFieldText(t);
|
||||
QTextCursor c = _field.textCursor();
|
||||
c.movePosition(QTextCursor::End);
|
||||
_field.setTextCursor(c);
|
||||
@@ -3148,6 +3163,17 @@ void HistoryWidget::onFieldTabbed() {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::setFieldText(const QString &text) {
|
||||
noTypingUpdate = true;
|
||||
_field.setPlainText(text);
|
||||
noTypingUpdate = false;
|
||||
}
|
||||
|
||||
void HistoryWidget::onCancel() {
|
||||
showPeer(0);
|
||||
emit cancelled();
|
||||
}
|
||||
|
||||
void HistoryWidget::peerUpdated(PeerData *data) {
|
||||
if (data && data == histPeer) {
|
||||
updateListSize();
|
||||
@@ -3197,6 +3223,9 @@ void HistoryWidget::onDeleteSelectedSure() {
|
||||
for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) {
|
||||
i.value()->destroy();
|
||||
}
|
||||
if (App::main() && App::main()->peer() == peer()) {
|
||||
App::main()->itemResized(0);
|
||||
}
|
||||
App::wnd()->hideLayer();
|
||||
}
|
||||
|
||||
@@ -3207,9 +3236,12 @@ void HistoryWidget::onDeleteContextSure() {
|
||||
}
|
||||
|
||||
if (item->id > 0) {
|
||||
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(QVector<MTPint>(1, MTP_int(item->id)))));
|
||||
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(1, MTP_int(item->id))));
|
||||
}
|
||||
item->destroy();
|
||||
if (App::main() && App::main()->peer() == peer()) {
|
||||
App::main()->itemResized(0);
|
||||
}
|
||||
App::wnd()->hideLayer();
|
||||
}
|
||||
|
||||
@@ -3323,24 +3355,6 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryWidget::getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const {
|
||||
if (_list && _list->getPhotoCoords(photo, x, y, w)) {
|
||||
x += _list->x();
|
||||
y += _list->y();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HistoryWidget::getVideoCoords(VideoData *video, int32 &x, int32 &y, int32 &w) const {
|
||||
if (_list && _list->getVideoCoords(video, x, y, w)) {
|
||||
x += _list->x();
|
||||
y += _list->y();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QRect HistoryWidget::historyRect() const {
|
||||
return _scroll.geometry();
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ public:
|
||||
|
||||
void updateMsg(const HistoryItem *msg);
|
||||
|
||||
bool getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const;
|
||||
bool getVideoCoords(VideoData *video, int32 &x, int32 &y, int32 &w) const;
|
||||
bool canCopySelected() const;
|
||||
bool canDeleteSelected() const;
|
||||
|
||||
void getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const;
|
||||
void clearSelectedItems(bool onlyTextSelection = false);
|
||||
@@ -283,20 +283,19 @@ public:
|
||||
void historyToDown(History *history);
|
||||
void historyWasRead(bool force = true);
|
||||
|
||||
bool getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const;
|
||||
bool getVideoCoords(VideoData *video, int32 &x, int32 &y, int32 &w) const;
|
||||
QRect historyRect() const;
|
||||
|
||||
void updateTyping(bool typing = true);
|
||||
void typingDone(const MTPBool &result, mtpRequestId req);
|
||||
|
||||
void destroyData();
|
||||
void uploadImage(const QImage &img, bool withText = false);
|
||||
void uploadFile(const QString &file, bool withText = false); // with confirmation
|
||||
void shareContactConfirmation(const QString &phone, const QString &fname, const QString &lname, bool withText = false);
|
||||
void uploadConfirmImageUncompressed();
|
||||
void uploadConfirmImageUncompressed(bool ctrlShiftEnter);
|
||||
void uploadMedias(const QStringList &files, ToPrepareMediaType type);
|
||||
void uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type);
|
||||
void confirmShareContact(const QString &phone, const QString &fname, const QString &lname);
|
||||
void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname);
|
||||
void confirmSendImage(const ReadyLocalMedia &img);
|
||||
void cancelSendImage();
|
||||
|
||||
@@ -309,7 +308,7 @@ public:
|
||||
void onShareContact(const PeerId &peer, UserData *contact);
|
||||
void onSendPaths(const PeerId &peer);
|
||||
|
||||
void shareContact(const QString &phone, const QString &fname, const QString &lname, int32 userId = 0);
|
||||
void shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, int32 userId = 0);
|
||||
|
||||
PeerData *peer() const;
|
||||
PeerData *activePeer() const;
|
||||
@@ -347,8 +346,12 @@ signals:
|
||||
|
||||
public slots:
|
||||
|
||||
void onCancel();
|
||||
|
||||
void peerUpdated(PeerData *data);
|
||||
|
||||
void cancelTyping();
|
||||
|
||||
void onPhotoUploaded(MsgId msgId, const MTPInputFile &file);
|
||||
void onDocumentUploaded(MsgId msgId, const MTPInputFile &file);
|
||||
void onThumbDocumentUploaded(MsgId msgId, const MTPInputFile &file, const MTPInputFile &thumb);
|
||||
@@ -359,7 +362,7 @@ public slots:
|
||||
|
||||
void onListScroll();
|
||||
void onHistoryToEnd();
|
||||
void onSend();
|
||||
void onSend(bool ctrlShiftEnter = false);
|
||||
|
||||
void onPhotoSelect();
|
||||
void onDocumentSelect();
|
||||
@@ -401,6 +404,8 @@ private:
|
||||
void addMessagesToBack(const QVector<MTPMessage> &messages);
|
||||
void chatLoaded(const MTPmessages_ChatFull &res);
|
||||
|
||||
void setFieldText(const QString &text);
|
||||
|
||||
QStringList getMediasFromMime(const QMimeData *d);
|
||||
DragState getDragState(const QMimeData *d);
|
||||
|
||||
@@ -462,5 +467,8 @@ private:
|
||||
QTimer _animActiveTimer;
|
||||
float64 _animActiveStart;
|
||||
|
||||
mtpRequestId _typingRequest;
|
||||
QTimer _typingStopTimer;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -237,9 +237,15 @@ void IntroPhone::phoneSubmitDone(const MTPauth_SentCode &result) {
|
||||
stopCheck();
|
||||
enableAll(false);
|
||||
|
||||
const MTPDauth_sentCode &d(result.c_auth_sentCode());
|
||||
intro()->setPhone(sentPhone, d.vphone_code_hash.c_string().v.c_str(), d.vphone_registered.v);
|
||||
intro()->setCallTimeout(result.c_auth_sentCode().vsend_call_timeout.v);
|
||||
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(), d.vphone_registered.v);
|
||||
intro()->setCallTimeout(d.vsend_call_timeout.v);
|
||||
} else if (result.type() == mtpc_auth_sentAppCode) {
|
||||
const MTPDauth_sentAppCode &d(result.c_auth_sentAppCode());
|
||||
intro()->setPhone(sentPhone, d.vphone_code_hash.c_string().v.c_str(), d.vphone_registered.v);
|
||||
intro()->setCallTimeout(d.vsend_call_timeout.v);
|
||||
}
|
||||
intro()->onIntroNext();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
PeerId peer;
|
||||
uint64 id, jpeg_id = 0;
|
||||
ToPrepareMediaType type;
|
||||
bool ctrlShiftEnter = false;
|
||||
{
|
||||
QMutexLocker lock(loader->toPrepareMutex());
|
||||
ToPrepareMedias &list(loader->toPrepareMedias());
|
||||
@@ -49,6 +50,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
peer = list.front().peer;
|
||||
id = list.front().id;
|
||||
type = list.front().type;
|
||||
ctrlShiftEnter = list.front().ctrlShiftEnter;
|
||||
}
|
||||
|
||||
if (img.isNull()) {
|
||||
@@ -182,7 +184,7 @@ void LocalImageLoaderPrivate::prepareImages() {
|
||||
|
||||
{
|
||||
QMutexLocker lock(loader->readyMutex());
|
||||
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, jpeg_id, peer, photo, photoThumbs, document, jpeg));
|
||||
loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, jpeg_id, peer, photo, photoThumbs, document, jpeg, ctrlShiftEnter));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -208,7 +210,7 @@ void LocalImageLoader::append(const QStringList &files, const PeerId &peer, ToPr
|
||||
{
|
||||
QMutexLocker lock(toPrepareMutex());
|
||||
for (QStringList::const_iterator i = files.cbegin(), e = files.cend(); i != e; ++i) {
|
||||
toPrepare.push_back(ToPrepareMedia(*i, peer, t));
|
||||
toPrepare.push_back(ToPrepareMedia(*i, peer, t, false));
|
||||
}
|
||||
}
|
||||
if (!thread) {
|
||||
@@ -223,7 +225,7 @@ PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, ToPr
|
||||
PhotoId result = 0;
|
||||
{
|
||||
QMutexLocker lock(toPrepareMutex());
|
||||
toPrepare.push_back(ToPrepareMedia(img, peer, t));
|
||||
toPrepare.push_back(ToPrepareMedia(img, peer, t, false));
|
||||
result = toPrepare.back().id;
|
||||
}
|
||||
if (!thread) {
|
||||
@@ -235,11 +237,11 @@ PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, ToPr
|
||||
return result;
|
||||
}
|
||||
|
||||
PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, ToPrepareMediaType t) {
|
||||
PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) {
|
||||
PhotoId result = 0;
|
||||
{
|
||||
QMutexLocker lock(toPrepareMutex());
|
||||
toPrepare.push_back(ToPrepareMedia(img, peer, t));
|
||||
toPrepare.push_back(ToPrepareMedia(img, peer, t, ctrlShiftEnter));
|
||||
result = toPrepare.back().id;
|
||||
}
|
||||
if (!thread) {
|
||||
@@ -255,7 +257,7 @@ PhotoId LocalImageLoader::append(const QString &file, const PeerId &peer, ToPrep
|
||||
PhotoId result = 0;
|
||||
{
|
||||
QMutexLocker lock(toPrepareMutex());
|
||||
toPrepare.push_back(ToPrepareMedia(file, peer, t));
|
||||
toPrepare.push_back(ToPrepareMedia(file, peer, t, false));
|
||||
result = toPrepare.back().id;
|
||||
}
|
||||
if (!thread) {
|
||||
|
||||
@@ -25,11 +25,11 @@ enum ToPrepareMediaType {
|
||||
};
|
||||
|
||||
struct ToPrepareMedia {
|
||||
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t) {
|
||||
ToPrepareMedia(const QString &file, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) : id(MTP::nonce<PhotoId>()), file(file), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter) {
|
||||
}
|
||||
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t) {
|
||||
ToPrepareMedia(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) : id(MTP::nonce<PhotoId>()), img(img), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter) {
|
||||
}
|
||||
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t) {
|
||||
ToPrepareMedia(const QByteArray &data, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) : id(MTP::nonce<PhotoId>()), data(data), peer(peer), type(t), ctrlShiftEnter(ctrlShiftEnter) {
|
||||
}
|
||||
PhotoId id;
|
||||
QString file;
|
||||
@@ -37,13 +37,14 @@ struct ToPrepareMedia {
|
||||
QByteArray data;
|
||||
PeerId peer;
|
||||
ToPrepareMediaType type;
|
||||
bool ctrlShiftEnter;
|
||||
};
|
||||
typedef QList<ToPrepareMedia> ToPrepareMedias;
|
||||
|
||||
typedef QMap<int32, QByteArray> LocalFileParts;
|
||||
struct ReadyLocalMedia {
|
||||
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &jpeg_id, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg) :
|
||||
type(type), file(file), filename(filename), filesize(filesize), data(data), id(id), jpeg_id(jpeg_id), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs) {
|
||||
ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &jpeg_id, const PeerId &peer, const MTPPhoto &photo, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter) :
|
||||
type(type), file(file), filename(filename), filesize(filesize), data(data), id(id), jpeg_id(jpeg_id), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) {
|
||||
if (!jpeg.isEmpty()) {
|
||||
int32 size = jpeg.size();
|
||||
for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) {
|
||||
@@ -65,6 +66,8 @@ struct ReadyLocalMedia {
|
||||
PreparedPhotoThumbs photoThumbs;
|
||||
LocalFileParts parts;
|
||||
QByteArray jpeg_md5;
|
||||
|
||||
bool ctrlShiftEnter;
|
||||
};
|
||||
typedef QList<ReadyLocalMedia> ReadyLocalMedias;
|
||||
|
||||
@@ -101,7 +104,7 @@ public:
|
||||
LocalImageLoader(QObject *parent);
|
||||
void append(const QStringList &files, const PeerId &peer, ToPrepareMediaType t = ToPrepareAuto);
|
||||
PhotoId append(const QByteArray &img, const PeerId &peer, ToPrepareMediaType t = ToPrepareAuto);
|
||||
PhotoId append(const QImage &img, const PeerId &peer, ToPrepareMediaType t = ToPreparePhoto);
|
||||
PhotoId append(const QImage &img, const PeerId &peer, ToPrepareMediaType t = ToPreparePhoto, bool ctrlShiftEnter = false);
|
||||
PhotoId append(const QString &file, const PeerId &peer, ToPrepareMediaType t = ToPrepareAuto);
|
||||
|
||||
QMutex *readyMutex();
|
||||
|
||||
@@ -344,3 +344,12 @@ QString logVectorLong(const QVector<MTPlong> &ids) {
|
||||
}
|
||||
return idsStr + "]";
|
||||
}
|
||||
|
||||
QString logVectorLong(const QVector<uint64> &ids) {
|
||||
if (!ids.size()) return "[void list]";
|
||||
QString idsStr = QString("[%1").arg(*ids.cbegin());
|
||||
for (QVector<uint64>::const_iterator i = ids.cbegin() + 1, e = ids.cend(); i != e; ++i) {
|
||||
idsStr += QString(", %2").arg(*i);
|
||||
}
|
||||
return idsStr + "]";
|
||||
}
|
||||
|
||||
@@ -49,15 +49,15 @@ inline DebugLogMemoryBuffer mb(const void *ptr, uint32 size) {
|
||||
}
|
||||
|
||||
void debugLogWrite(const char *file, int32 line, const QString &v);
|
||||
#define DEBUG_LOG(msg) (debugLogWrite(__FILE__, __LINE__, QString msg))
|
||||
#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) (tcpLogWrite(QString msg))
|
||||
#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) (mtpLogWrite(dc, QString msg))
|
||||
#define MTP_LOG(dc, msg) { if (cDebug()) mtpLogWrite(dc, QString msg); }
|
||||
//usage MTP_LOG(dc, ("log: %1 %2").arg(1).arg(2))
|
||||
|
||||
#else
|
||||
@@ -72,6 +72,7 @@ inline const char *logBool(bool v) {
|
||||
|
||||
class MTPlong;
|
||||
QString logVectorLong(const QVector<MTPlong> &ids);
|
||||
QString logVectorLong(const QVector<uint64> &ids);
|
||||
|
||||
#define LOG(msg) (logWrite(QString msg))
|
||||
//usage LOG(("log: %1 %2").arg(1).arg(2))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
an unofficial desktop messaging app, see https://telegram.org
|
||||
|
||||
@@ -275,17 +275,18 @@ MainWidget *TopBarWidget::main() {
|
||||
|
||||
MainWidget::MainWidget(Window *window) : QWidget(window), failedObjId(0), _dialogsWidth(st::dlgMinWidth),
|
||||
dialogs(this), history(this), profile(0), overview(0), _topBar(this), hider(0), _mediaType(this), _mediaTypeMask(0),
|
||||
updPts(0), updDate(0), updQts(0), updSeq(0), updInited(false), onlineRequest(0) {
|
||||
updPts(0), updDate(0), updQts(-1), updSeq(0), updInited(false), onlineRequest(0), _failDifferenceTimeout(1), _lastUpdateTime(0) {
|
||||
setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight));
|
||||
|
||||
connect(window, SIGNAL(resized(const QSize &)), this, SLOT(onParentResize(const QSize &)));
|
||||
connect(&dialogs, SIGNAL(peerChosen(const PeerId &, MsgId)), this, SLOT(showPeer(const PeerId &, MsgId)));
|
||||
connect(&dialogs, SIGNAL(cancelled()), this, SLOT(dialogsCancelled()));
|
||||
connect(&history, SIGNAL(cancelled()), &dialogs, SLOT(activate()));
|
||||
connect(this, SIGNAL(peerPhotoChanged(PeerData *)), this, SIGNAL(dialogsUpdated()));
|
||||
connect(&noUpdatesTimer, SIGNAL(timeout()), this, SLOT(getDifference()));
|
||||
connect(&onlineTimer, SIGNAL(timeout()), this, SLOT(setOnline()));
|
||||
connect(&onlineUpdater, SIGNAL(timeout()), this, SLOT(updateOnlineDisplay()));
|
||||
connect(&_bySeqTimer, SIGNAL(timeout()), this, SLOT(getDifference()));
|
||||
connect(&_failDifferenceTimer, SIGNAL(timeout()), this, SLOT(getDifferenceForce()));
|
||||
connect(this, SIGNAL(peerUpdated(PeerData*)), &history, SLOT(peerUpdated(PeerData*)));
|
||||
connect(&_topBar, SIGNAL(clicked()), this, SLOT(onTopBarClick()));
|
||||
connect(&history, SIGNAL(peerShown(PeerData*)), this, SLOT(onPeerShown(PeerData*)));
|
||||
@@ -295,11 +296,6 @@ dialogs(this), history(this), profile(0), overview(0), _topBar(this), hider(0),
|
||||
connect(audioVoice(), SIGNAL(stopped(AudioData*)), this, SLOT(audioPlayProgress(AudioData*)));
|
||||
}
|
||||
|
||||
noUpdatesTimer.setSingleShot(true);
|
||||
onlineTimer.setSingleShot(true);
|
||||
onlineUpdater.setSingleShot(true);
|
||||
updateNotifySettingTimer.setSingleShot(true);
|
||||
|
||||
dialogs.show();
|
||||
history.show();
|
||||
_topBar.hide();
|
||||
@@ -414,7 +410,7 @@ void MainWidget::deleteHistory(PeerData *peer, const MTPmessages_StatedMessage &
|
||||
|
||||
void MainWidget::deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHistory &result) {
|
||||
const MTPDmessages_affectedHistory &d(result.c_messages_affectedHistory());
|
||||
App::main()->updUpdated(d.vpts.v, 0, 0, d.vseq.v);
|
||||
App::main()->updUpdated(d.vpts.v, d.vseq.v);
|
||||
|
||||
int32 offset = d.voffset.v;
|
||||
if (!MTP::authedId() || offset <= 0) return;
|
||||
@@ -424,13 +420,13 @@ void MainWidget::deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHis
|
||||
|
||||
void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result) {
|
||||
const MTPDcontacts_link &d(result.c_contacts_link());
|
||||
App::feedUsers(MTP_vector<MTPUser>(QVector<MTPUser>(1, d.vuser)));
|
||||
App::feedUsers(MTP_vector<MTPUser>(1, d.vuser));
|
||||
App::feedUserLink(MTP_int(user->id & 0xFFFFFFFF), d.vmy_link, d.vforeign_link);
|
||||
}
|
||||
|
||||
void MainWidget::deleteHistoryAndContact(UserData *user, const MTPcontacts_Link &result) {
|
||||
const MTPDcontacts_link &d(result.c_contacts_link());
|
||||
App::feedUsers(MTP_vector<MTPUser>(QVector<MTPUser>(1, d.vuser)));
|
||||
App::feedUsers(MTP_vector<MTPUser>(1, d.vuser));
|
||||
App::feedUserLink(MTP_int(user->id & 0xFFFFFFFF), d.vmy_link, d.vforeign_link);
|
||||
|
||||
if ((profile && profile->peer() == user) || (overview && overview->peer() == user) || _stack.contains(user) || history.peer() == user) {
|
||||
@@ -547,26 +543,30 @@ DialogsIndexed &MainWidget::contactsList() {
|
||||
return dialogs.contactsList();
|
||||
}
|
||||
|
||||
void MainWidget::sendMessage(History *hist, const QString &text) {
|
||||
readServerHistory(hist, false);
|
||||
QString msg = history.prepareMessage(text);
|
||||
if (!msg.isEmpty()) {
|
||||
void MainWidget::sendPreparedText(History *hist, const QString &text) {
|
||||
QString sendingText, leftText = text;
|
||||
while (textSplit(sendingText, leftText, MaxMessageSize)) {
|
||||
MsgId newId = clientMsgId();
|
||||
uint64 randomId = MTP::nonce<uint64>();
|
||||
|
||||
App::historyRegRandom(randomId, newId);
|
||||
|
||||
hist->loadAround(0);
|
||||
|
||||
MTPstring msgText(MTP_string(msg));
|
||||
hist->addToBack(MTP_message(MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTP_bool(true), MTP_bool(true), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty()));
|
||||
historyToDown(hist);
|
||||
if (history.peer() == hist->peer) {
|
||||
history.peerMessagesUpdated();
|
||||
}
|
||||
|
||||
MTP::send(MTPmessages_SendMessage(hist->peer->input, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId));
|
||||
}
|
||||
App::historyRegRandom(randomId, newId);
|
||||
|
||||
MTPstring msgText(MTP_string(sendingText));
|
||||
int32 flags = 0x01 | 0x02; // unread, out
|
||||
hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty()));
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(hist->peer->input, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||
}
|
||||
|
||||
historyToDown(hist);
|
||||
if (history.peer() == hist->peer) {
|
||||
history.peerMessagesUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::sendMessage(History *hist, const QString &text) {
|
||||
readServerHistory(hist, false);
|
||||
hist->loadAround(0);
|
||||
sendPreparedText(hist, history.prepareMessage(text));
|
||||
}
|
||||
|
||||
void MainWidget::readServerHistory(History *hist, bool force) {
|
||||
@@ -574,8 +574,8 @@ void MainWidget::readServerHistory(History *hist, bool force) {
|
||||
|
||||
ReadRequests::const_iterator i = _readRequests.constFind(hist->peer);
|
||||
if (i == _readRequests.cend()) {
|
||||
hist->inboxRead(true);
|
||||
_readRequests.insert(hist->peer, MTP::send(MTPmessages_ReadHistory(hist->peer->input, MTP_int(0), MTP_int(0)), rpcDone(&MainWidget::partWasRead, hist->peer)));
|
||||
hist->inboxRead(0);
|
||||
_readRequests.insert(hist->peer, MTP::send(MTPmessages_ReadHistory(hist->peer->input, MTP_int(0), MTP_int(0), MTP_bool(true)), rpcDone(&MainWidget::partWasRead, hist->peer)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,7 +715,7 @@ void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
||||
}
|
||||
|
||||
void MainWidget::itemResized(HistoryItem *row) {
|
||||
if (history.peer() == row->history()->peer && !row->detached()) {
|
||||
if (!row || (history.peer() == row->history()->peer && !row->detached())) {
|
||||
history.itemResized(row);
|
||||
}
|
||||
if (overview) {
|
||||
@@ -755,6 +755,26 @@ void MainWidget::loadMediaBack(PeerData *peer, MediaOverviewType type, bool many
|
||||
_overviewLoad[type].insert(hist->peer, MTP::send(MTPmessages_Search(hist->peer->input, MTPstring(), filter, MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(minId), MTP_int(limit)), rpcDone(&MainWidget::photosLoaded, hist)));
|
||||
}
|
||||
|
||||
void MainWidget::peerUsernameChanged(PeerData *peer) {
|
||||
if (profile && profile->peer() == peer) {
|
||||
profile->update();
|
||||
}
|
||||
if (App::settings() && peer == App::self()) {
|
||||
App::settings()->usernameChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::checkLastUpdate(bool afterSleep) {
|
||||
uint64 n = getms(true);
|
||||
if (_lastUpdateTime && n > _lastUpdateTime + (afterSleep ? NoUpdatesAfterSleepTimeout : NoUpdatesTimeout)) {
|
||||
getDifference();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::showNewGroup() {
|
||||
dialogs.onNewGroup();
|
||||
}
|
||||
|
||||
void MainWidget::photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req) {
|
||||
OverviewsPreload::iterator it;
|
||||
MediaOverviewType type = OverviewCount;
|
||||
@@ -814,13 +834,13 @@ void MainWidget::photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpR
|
||||
|
||||
void MainWidget::partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result) {
|
||||
const MTPDmessages_affectedHistory &d(result.c_messages_affectedHistory());
|
||||
App::main()->updUpdated(d.vpts.v, 0, 0, d.vseq.v);
|
||||
App::main()->updUpdated(d.vpts.v, d.vseq.v);
|
||||
|
||||
int32 offset = d.voffset.v;
|
||||
if (!MTP::authedId() || offset <= 0) {
|
||||
_readRequests.remove(peer);
|
||||
} else {
|
||||
_readRequests[peer] = MTP::send(MTPmessages_ReadHistory(peer->input, MTP_int(0), MTP_int(offset)), rpcDone(&MainWidget::partWasRead, peer));
|
||||
_readRequests[peer] = MTP::send(MTPmessages_ReadHistory(peer->input, MTP_int(0), MTP_int(offset), MTP_bool(true)), rpcDone(&MainWidget::partWasRead, peer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,16 +1001,16 @@ void MainWidget::updateOnlineDisplay() {
|
||||
if (App::wnd()->settingsWidget()) App::wnd()->settingsWidget()->updateOnlineDisplay();
|
||||
}
|
||||
|
||||
void MainWidget::confirmShareContact(const QString &phone, const QString &fname, const QString &lname) {
|
||||
history.confirmShareContact(phone, fname, lname);
|
||||
void MainWidget::confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname) {
|
||||
history.confirmShareContact(ctrlShiftEnter, phone, fname, lname);
|
||||
}
|
||||
|
||||
void MainWidget::confirmSendImage(const ReadyLocalMedia &img) {
|
||||
history.confirmSendImage(img);
|
||||
}
|
||||
|
||||
void MainWidget::confirmSendImageUncompressed() {
|
||||
history.uploadConfirmImageUncompressed();
|
||||
void MainWidget::confirmSendImageUncompressed(bool ctrlShiftEnter) {
|
||||
history.uploadConfirmImageUncompressed(ctrlShiftEnter);
|
||||
}
|
||||
|
||||
void MainWidget::cancelSendImage() {
|
||||
@@ -1101,7 +1121,7 @@ void MainWidget::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPe
|
||||
}
|
||||
|
||||
PeerData *MainWidget::peer() {
|
||||
return history.peer();
|
||||
return overview ? overview->peer() : history.peer();
|
||||
}
|
||||
|
||||
PeerData *MainWidget::activePeer() {
|
||||
@@ -1261,12 +1281,15 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
|
||||
case mtpc_messages_sentMessage: {
|
||||
const MTPDmessages_sentMessage &d(result.c_messages_sentMessage());
|
||||
|
||||
if (randomId) feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
|
||||
|
||||
if (updInited && d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq) return;
|
||||
if (d.vseq.v > updSeq + 1) return getDifference();
|
||||
if (d.vseq.v > updSeq + 1) {
|
||||
_bySeqSentMessage.insert(d.vseq.v, result);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
|
||||
if (updInited) {
|
||||
updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v);
|
||||
}
|
||||
@@ -1275,12 +1298,15 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
|
||||
case mtpc_messages_sentMessageLink: {
|
||||
const MTPDmessages_sentMessageLink &d(result.c_messages_sentMessageLink());
|
||||
|
||||
if (randomId) feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
|
||||
|
||||
if (updInited && d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq) return;
|
||||
if (d.vseq.v > updSeq + 1) return getDifference();
|
||||
if (d.vseq.v > updSeq + 1) {
|
||||
_bySeqSentMessage.insert(d.vseq.v, result);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
|
||||
if (updInited) {
|
||||
updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v);
|
||||
}
|
||||
@@ -1321,7 +1347,11 @@ void MainWidget::sentFullDataReceived(uint64 randomId, const MTPmessages_StatedM
|
||||
App::feedMessageMedia(msgId, *msg);
|
||||
}
|
||||
if (updInited && d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference();
|
||||
if (d.vseq.v <= updSeq) return;
|
||||
if (d.vseq.v > updSeq + 1) {
|
||||
_bySeqStatedMessage.insert(d.vseq.v, result);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
}
|
||||
if (!randomId) {
|
||||
feedUpdate(MTP_updateNewMessage(d.vmessage, d.vpts));
|
||||
@@ -1340,7 +1370,11 @@ void MainWidget::sentFullDataReceived(uint64 randomId, const MTPmessages_StatedM
|
||||
App::feedMessageMedia(msgId, *msg);
|
||||
}
|
||||
if (updInited && d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference();
|
||||
if (d.vseq.v <= updSeq) return;
|
||||
if (d.vseq.v > updSeq + 1) {
|
||||
_bySeqStatedMessage.insert(d.vseq.v, result);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
}
|
||||
if (!randomId) {
|
||||
feedUpdate(MTP_updateNewMessage(d.vmessage, d.vpts));
|
||||
@@ -1359,7 +1393,11 @@ void MainWidget::sentFullDatasReceived(const MTPmessages_StatedMessages &result)
|
||||
case mtpc_messages_statedMessages: {
|
||||
const MTPDmessages_statedMessages &d(result.c_messages_statedMessages());
|
||||
if (updInited && d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference();
|
||||
if (d.vseq.v <= updSeq) return;
|
||||
if (d.vseq.v > updSeq + 1) {
|
||||
_bySeqStatedMessages.insert(d.vseq.v, result);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
App::feedUsers(d.vusers);
|
||||
@@ -1376,7 +1414,11 @@ void MainWidget::sentFullDatasReceived(const MTPmessages_StatedMessages &result)
|
||||
const MTPDmessages_statedMessagesLinks &d(result.c_messages_statedMessagesLinks());
|
||||
|
||||
if (updInited && d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference();
|
||||
if (d.vseq.v <= updSeq) return;
|
||||
if (d.vseq.v > updSeq + 1) {
|
||||
_bySeqStatedMessages.insert(d.vseq.v, result);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
App::feedUsers(d.vusers);
|
||||
@@ -1414,10 +1456,6 @@ void MainWidget::dialogsToUp() {
|
||||
dialogs.dialogsToUp();
|
||||
}
|
||||
|
||||
void MainWidget::dialogsClear() {
|
||||
dialogs.clearFiltered();
|
||||
}
|
||||
|
||||
void MainWidget::newUnreadMsg(History *hist, MsgId msgId) {
|
||||
history.newUnreadMsg(hist, msgId);
|
||||
}
|
||||
@@ -1615,8 +1653,78 @@ bool MainWidget::updateFail(const RPCError &e) {
|
||||
void MainWidget::updSetState(int32 pts, int32 date, int32 qts, int32 seq) {
|
||||
if (updPts < pts) updPts = pts;
|
||||
if (updDate < date) updDate = date;
|
||||
if (updQts < qts) updQts = qts;
|
||||
if (seq) updSeq = seq;
|
||||
if (qts && updQts < qts) {
|
||||
updQts = qts;
|
||||
}
|
||||
if (seq && seq != updSeq) {
|
||||
updSeq = seq;
|
||||
if (_bySeqTimer.isActive()) _bySeqTimer.stop();
|
||||
for (QMap<int32, MTPUpdates>::iterator i = _bySeqUpdates.begin(); i != _bySeqUpdates.end();) {
|
||||
int32 s = i.key();
|
||||
if (s <= seq + 1) {
|
||||
MTPUpdates v = i.value();
|
||||
i = _bySeqUpdates.erase(i);
|
||||
if (s == seq + 1) {
|
||||
return handleUpdates(v);
|
||||
}
|
||||
} else {
|
||||
if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSeqTimeout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (QMap<int32, MTPmessages_SentMessage>::iterator i = _bySeqSentMessage.begin(); i != _bySeqSentMessage.end();) {
|
||||
int32 s = i.key();
|
||||
if (s <= seq + 1) {
|
||||
MTPmessages_SentMessage v = i.value();
|
||||
i = _bySeqSentMessage.erase(i);
|
||||
if (s == seq + 1) {
|
||||
return sentDataReceived(0, v);
|
||||
}
|
||||
} else {
|
||||
if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSeqTimeout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (QMap<int32, MTPmessages_StatedMessage>::iterator i = _bySeqStatedMessage.begin(); i != _bySeqStatedMessage.end();) {
|
||||
int32 s = i.key();
|
||||
if (s <= seq + 1) {
|
||||
MTPmessages_StatedMessage v = i.value();
|
||||
i = _bySeqStatedMessage.erase(i);
|
||||
if (s == seq + 1) {
|
||||
return sentFullDataReceived(0, v);
|
||||
}
|
||||
} else {
|
||||
if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSeqTimeout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (QMap<int32, MTPmessages_StatedMessages>::iterator i = _bySeqStatedMessages.begin(); i != _bySeqStatedMessages.end();) {
|
||||
int32 s = i.key();
|
||||
if (s <= seq + 1) {
|
||||
MTPmessages_StatedMessages v = i.value();
|
||||
i = _bySeqStatedMessages.erase(i);
|
||||
if (s == seq + 1) {
|
||||
return sentFullDatasReceived(v);
|
||||
}
|
||||
} else {
|
||||
if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSeqTimeout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (QMap<int32, int32>::iterator i = _bySeqPart.begin(); i != _bySeqPart.end();) {
|
||||
int32 s = i.key();
|
||||
if (s <= seq + 1) {
|
||||
int32 v = i.value();
|
||||
i = _bySeqPart.erase(i);
|
||||
if (s == seq + 1) {
|
||||
return updUpdated(v, s);
|
||||
}
|
||||
} else {
|
||||
if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSeqTimeout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::gotState(const MTPupdates_State &state) {
|
||||
@@ -1624,7 +1732,9 @@ void MainWidget::gotState(const MTPupdates_State &state) {
|
||||
updSetState(d.vpts.v, d.vdate.v, d.vqts.v, d.vseq.v);
|
||||
|
||||
MTP::setGlobalDoneHandler(rpcDone(&MainWidget::updateReceived));
|
||||
_lastUpdateTime = getms(true);
|
||||
noUpdatesTimer.start(NoUpdatesTimeout);
|
||||
LOG(("Started no updates timeout, %1").arg(_lastUpdateTime));
|
||||
updInited = true;
|
||||
|
||||
dialogs.loadDialogs();
|
||||
@@ -1632,13 +1742,17 @@ void MainWidget::gotState(const MTPupdates_State &state) {
|
||||
}
|
||||
|
||||
void MainWidget::gotDifference(const MTPupdates_Difference &diff) {
|
||||
_failDifferenceTimeout = 1;
|
||||
|
||||
switch (diff.type()) {
|
||||
case mtpc_updates_differenceEmpty: {
|
||||
const MTPDupdates_differenceEmpty &d(diff.c_updates_differenceEmpty());
|
||||
updSetState(updPts, d.vdate.v, updQts, d.vseq.v);
|
||||
|
||||
MTP::setGlobalDoneHandler(rpcDone(&MainWidget::updateReceived));
|
||||
_lastUpdateTime = getms(true);
|
||||
noUpdatesTimer.start(NoUpdatesTimeout);
|
||||
LOG(("Started no updates timeout, %1").arg(_lastUpdateTime));
|
||||
updInited = true;
|
||||
} break;
|
||||
case mtpc_updates_differenceSlice: {
|
||||
@@ -1661,10 +1775,13 @@ void MainWidget::gotDifference(const MTPupdates_Difference &diff) {
|
||||
};
|
||||
}
|
||||
|
||||
void MainWidget::updUpdated(int32 pts, int32 date, int32 qts, int32 seq) {
|
||||
void MainWidget::updUpdated(int32 pts, int32 seq) {
|
||||
if (!updInited) return;
|
||||
if (seq && (seq < updSeq || seq > updSeq + 1)) return getDifference();
|
||||
updSetState(pts, date, qts, seq);
|
||||
if (seq && (seq < updSeq || seq > updSeq + 1)) {
|
||||
_bySeqPart.insert(seq, pts);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
updSetState(pts, 0, 0, seq);
|
||||
}
|
||||
|
||||
void MainWidget::feedDifference(const MTPVector<MTPUser> &users, const MTPVector<MTPChat> &chats, const MTPVector<MTPMessage> &msgs, const MTPVector<MTPUpdate> &other) {
|
||||
@@ -1678,15 +1795,33 @@ void MainWidget::feedDifference(const MTPVector<MTPUser> &users, const MTPVector
|
||||
|
||||
bool MainWidget::failDifference(const RPCError &e) {
|
||||
LOG(("RPC Error: %1 %2: %3").arg(e.code()).arg(e.type()).arg(e.description()));
|
||||
_failDifferenceTimer.start(_failDifferenceTimeout * 1000);
|
||||
if (_failDifferenceTimeout < 64) _failDifferenceTimeout *= 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWidget::getDifferenceForce() {
|
||||
if (MTP::authedId()) {
|
||||
updInited = true;
|
||||
getDifference();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWidget::getDifference() {
|
||||
LOG(("Getting difference! no updates timer: %1, remains: %2").arg(noUpdatesTimer.isActive() ? 1 : 0).arg(noUpdatesTimer.remainingTime()));
|
||||
if (!updInited) return;
|
||||
|
||||
_bySeqUpdates.clear();
|
||||
_bySeqSentMessage.clear();
|
||||
_bySeqStatedMessage.clear();
|
||||
_bySeqStatedMessages.clear();
|
||||
_bySeqPart.clear();
|
||||
_bySeqTimer.stop();
|
||||
|
||||
noUpdatesTimer.stop();
|
||||
_failDifferenceTimer.stop();
|
||||
|
||||
LOG(("Getting difference for %1, %2").arg(updPts).arg(updDate));
|
||||
updInited = false;
|
||||
MTP::setGlobalDoneHandler(RPCDoneHandlerPtr(0));
|
||||
MTP::send(MTPupdates_GetDifference(MTP_int(updPts), MTP_int(updDate), MTP_int(updQts)), rpcDone(&MainWidget::gotDifference), rpcFail(&MainWidget::failDifference));
|
||||
@@ -1695,7 +1830,7 @@ void MainWidget::getDifference() {
|
||||
void MainWidget::start(const MTPUser &user) {
|
||||
MTP::authed(user.c_userSelf().vid.v);
|
||||
App::initMedia();
|
||||
App::feedUsers(MTP_vector<MTPUser>(QVector<MTPUser>(1, user)));
|
||||
App::feedUsers(MTP_vector<MTPUser>(1, user));
|
||||
App::app()->startUpdateCheck();
|
||||
MTP::send(MTPupdates_GetState(), rpcDone(&MainWidget::gotState));
|
||||
update();
|
||||
@@ -1906,82 +2041,105 @@ void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) {
|
||||
try {
|
||||
MTPUpdates updates(from, end);
|
||||
|
||||
_lastUpdateTime = getms(true);
|
||||
noUpdatesTimer.start(NoUpdatesTimeout);
|
||||
LOG(("Started no updates timeout, %1").arg(_lastUpdateTime));
|
||||
|
||||
switch (updates.type()) {
|
||||
case mtpc_updates: {
|
||||
const MTPDupdates &d(updates.c_updates());
|
||||
if (d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference();
|
||||
}
|
||||
|
||||
App::feedChats(d.vchats);
|
||||
App::feedUsers(d.vusers);
|
||||
feedUpdates(d.vupdates);
|
||||
|
||||
updSetState(updPts, d.vdate.v, updQts, d.vseq.v);
|
||||
} break;
|
||||
|
||||
case mtpc_updatesCombined: {
|
||||
const MTPDupdatesCombined &d(updates.c_updatesCombined());
|
||||
if (d.vseq.v) {
|
||||
if (d.vseq_start.v <= updSeq || d.vseq_start.v > updSeq + 1) return getDifference();
|
||||
}
|
||||
|
||||
App::feedChats(d.vchats);
|
||||
App::feedUsers(d.vusers);
|
||||
feedUpdates(d.vupdates);
|
||||
|
||||
updSetState(updPts, d.vdate.v, updQts, d.vseq.v);
|
||||
} break;
|
||||
|
||||
case mtpc_updateShort: {
|
||||
const MTPDupdateShort &d(updates.c_updateShort());
|
||||
|
||||
feedUpdate(d.vupdate);
|
||||
|
||||
updSetState(updPts, d.vdate.v, updQts, updSeq);
|
||||
} break;
|
||||
|
||||
case mtpc_updateShortMessage: {
|
||||
const MTPDupdateShortMessage &d(updates.c_updateShortMessage());
|
||||
if (d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference();
|
||||
}
|
||||
|
||||
if (!App::userLoaded(d.vfrom_id.v)) return getDifference();
|
||||
HistoryItem *item = App::histories().addToBack(MTP_message(d.vid, d.vfrom_id, MTP_peerUser(MTP_int(MTP::authedId())), MTP_bool(false), MTP_bool(true), d.vdate, d.vmessage, MTP_messageMediaEmpty()));
|
||||
if (item) {
|
||||
history.peerMessagesUpdated(item->history()->peer->id);
|
||||
}
|
||||
|
||||
updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v);
|
||||
} break;
|
||||
|
||||
case mtpc_updateShortChatMessage: {
|
||||
const MTPDupdateShortChatMessage &d(updates.c_updateShortChatMessage());
|
||||
if (d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference();
|
||||
}
|
||||
|
||||
if (!App::chatLoaded(d.vchat_id.v) || !App::userLoaded(d.vfrom_id.v)) return getDifference();
|
||||
HistoryItem *item = App::histories().addToBack(MTP_message(d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), MTP_bool(false), MTP_bool(true), d.vdate, d.vmessage, MTP_messageMediaEmpty()));
|
||||
if (item) {
|
||||
history.peerMessagesUpdated(item->history()->peer->id);
|
||||
}
|
||||
|
||||
updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v);
|
||||
} break;
|
||||
|
||||
case mtpc_updatesTooLong: {
|
||||
return getDifference();
|
||||
} break;
|
||||
}
|
||||
handleUpdates(updates);
|
||||
} catch(mtpErrorUnexpected &e) { // just some other type
|
||||
}
|
||||
}
|
||||
update();
|
||||
/**/
|
||||
}
|
||||
|
||||
void MainWidget::handleUpdates(const MTPUpdates &updates) {
|
||||
switch (updates.type()) {
|
||||
case mtpc_updates: {
|
||||
const MTPDupdates &d(updates.c_updates());
|
||||
if (d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq) return;
|
||||
if (d.vseq.v > updSeq + 1) {
|
||||
_bySeqUpdates.insert(d.vseq.v, updates);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
App::feedChats(d.vchats);
|
||||
App::feedUsers(d.vusers);
|
||||
feedUpdates(d.vupdates);
|
||||
|
||||
updSetState(updPts, d.vdate.v, updQts, d.vseq.v);
|
||||
} break;
|
||||
|
||||
case mtpc_updatesCombined: {
|
||||
const MTPDupdatesCombined &d(updates.c_updatesCombined());
|
||||
if (d.vseq_start.v) {
|
||||
if (d.vseq_start.v <= updSeq) return;
|
||||
if (d.vseq_start.v > updSeq + 1) {
|
||||
_bySeqUpdates.insert(d.vseq_start.v, updates);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
App::feedChats(d.vchats);
|
||||
App::feedUsers(d.vusers);
|
||||
feedUpdates(d.vupdates);
|
||||
|
||||
updSetState(updPts, d.vdate.v, updQts, d.vseq.v);
|
||||
} break;
|
||||
|
||||
case mtpc_updateShort: {
|
||||
const MTPDupdateShort &d(updates.c_updateShort());
|
||||
|
||||
feedUpdate(d.vupdate);
|
||||
|
||||
updSetState(updPts, d.vdate.v, updQts, updSeq);
|
||||
} break;
|
||||
|
||||
case mtpc_updateShortMessage: {
|
||||
const MTPDupdateShortMessage &d(updates.c_updateShortMessage());
|
||||
if (d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq) return;
|
||||
if (d.vseq.v > updSeq + 1) {
|
||||
_bySeqUpdates.insert(d.vseq.v, updates);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
if (!App::userLoaded(d.vfrom_id.v)) return getDifference();
|
||||
int32 flags = 0x01; // unread
|
||||
HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), d.vid, d.vfrom_id, MTP_peerUser(MTP_int(MTP::authedId())), d.vdate, d.vmessage, MTP_messageMediaEmpty()));
|
||||
if (item) {
|
||||
history.peerMessagesUpdated(item->history()->peer->id);
|
||||
}
|
||||
|
||||
updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v);
|
||||
} break;
|
||||
|
||||
case mtpc_updateShortChatMessage: {
|
||||
const MTPDupdateShortChatMessage &d(updates.c_updateShortChatMessage());
|
||||
if (d.vseq.v) {
|
||||
if (d.vseq.v <= updSeq) return;
|
||||
if (d.vseq.v > updSeq + 1) {
|
||||
_bySeqUpdates.insert(d.vseq.v, updates);
|
||||
return _bySeqTimer.start(WaitForSeqTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
if (!App::chatLoaded(d.vchat_id.v) || !App::userLoaded(d.vfrom_id.v)) return getDifference();
|
||||
int32 flags = 0x01; // unread
|
||||
HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vdate, d.vmessage, MTP_messageMediaEmpty()));
|
||||
if (item) {
|
||||
history.peerMessagesUpdated(item->history()->peer->id);
|
||||
}
|
||||
|
||||
updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v);
|
||||
} break;
|
||||
|
||||
case mtpc_updatesTooLong: {
|
||||
return getDifference();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
@@ -2056,7 +2214,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
History *history = App::historyLoaded(App::peerFromUser(d.vuser_id));
|
||||
UserData *user = App::userLoaded(d.vuser_id.v);
|
||||
if (history && user) {
|
||||
dialogs.regTyping(history, user);
|
||||
if (d.vaction.type() == mtpc_sendMessageTypingAction) {
|
||||
dialogs.regTyping(history, user);
|
||||
} else if (d.vaction.type() == mtpc_sendMessageCancelAction) {
|
||||
history->unregTyping(user);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -2093,6 +2255,9 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
if (user) {
|
||||
switch (d.vstatus.type()) {
|
||||
case mtpc_userStatusEmpty: user->onlineTill = 0; break;
|
||||
case mtpc_userStatusRecently: user->onlineTill = -2; break;
|
||||
case mtpc_userStatusLastWeek: user->onlineTill = -3; break;
|
||||
case mtpc_userStatusLastMonth: user->onlineTill = -4; break;
|
||||
case mtpc_userStatusOffline: user->onlineTill = d.vstatus.c_userStatusOffline().vwas_online.v; break;
|
||||
case mtpc_userStatusOnline: user->onlineTill = d.vstatus.c_userStatusOnline().vexpires.v; break;
|
||||
}
|
||||
@@ -2104,8 +2269,12 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
case mtpc_updateUserName: {
|
||||
const MTPDupdateUserName &d(update.c_updateUserName());
|
||||
UserData *user = App::userLoaded(d.vuser_id.v);
|
||||
if (user && user->contact <= 0) {
|
||||
user->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), user->nameOrPhone);
|
||||
if (user) {
|
||||
if (user->contact <= 0) {
|
||||
user->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), user->nameOrPhone, textOneLine(qs(d.vusername)));
|
||||
} else {
|
||||
user->setName(textOneLine(user->firstName), textOneLine(user->lastName), user->nameOrPhone, textOneLine(qs(d.vusername)));
|
||||
}
|
||||
if (App::main()) App::main()->peerUpdated(user);
|
||||
}
|
||||
} break;
|
||||
@@ -2165,7 +2334,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateNewEncryptedMessage: {
|
||||
const MTPDupdateNewEncryptedMessage &d(update.c_updateNewEncryptedMessage());
|
||||
if (updQts < d.vqts.v) updQts = d.vqts.v;
|
||||
// if (d.vqts.v && updQts < d.vqts.v) updQts = d.vqts.v;
|
||||
} break;
|
||||
|
||||
case mtpc_updateEncryptedChatTyping: {
|
||||
@@ -2200,5 +2369,10 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||
const MTPDupdateDcOptions &d(update.c_updateDcOptions());
|
||||
MTP::updateDcOptions(d.vdc_options.c_vector().v);
|
||||
} break;
|
||||
|
||||
case mtpc_updateServiceNotification: {
|
||||
const MTPDupdateServiceNotification &d(update.c_updateServiceNotification());
|
||||
//
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,9 +196,8 @@ public:
|
||||
void msgUpdated(PeerId peer, const HistoryItem *msg);
|
||||
void historyToDown(History *hist);
|
||||
void dialogsToUp();
|
||||
void dialogsClear(); // after showing peer history
|
||||
void newUnreadMsg(History *history, MsgId msgId);
|
||||
void updUpdated(int32 pts, int32 date, int32 qts, int32 seq);
|
||||
void updUpdated(int32 pts, int32 seq);
|
||||
void historyWasRead();
|
||||
|
||||
void peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg);
|
||||
@@ -213,9 +212,9 @@ public:
|
||||
void showBackFromStack();
|
||||
QRect historyRect() const;
|
||||
|
||||
void confirmShareContact(const QString &phone, const QString &fname, const QString &lname);
|
||||
void confirmShareContact(bool ctrlShiftEnter, const QString &phone, const QString &fname, const QString &lname);
|
||||
void confirmSendImage(const ReadyLocalMedia &img);
|
||||
void confirmSendImageUncompressed();
|
||||
void confirmSendImageUncompressed(bool ctrlShiftEnter);
|
||||
void cancelSendImage();
|
||||
|
||||
void destroyData();
|
||||
@@ -267,6 +266,7 @@ public:
|
||||
DialogsIndexed &contactsList();
|
||||
|
||||
void sendMessage(History *history, const QString &text);
|
||||
void sendPreparedText(History *hist, const QString &text);
|
||||
|
||||
void readServerHistory(History *history, bool force = true);
|
||||
|
||||
@@ -282,6 +282,10 @@ public:
|
||||
void itemResized(HistoryItem *row);
|
||||
|
||||
void loadMediaBack(PeerData *peer, MediaOverviewType type, bool many = false);
|
||||
void peerUsernameChanged(PeerData *peer);
|
||||
|
||||
void checkLastUpdate(bool afterSleep);
|
||||
void showNewGroup();
|
||||
|
||||
~MainWidget();
|
||||
|
||||
@@ -312,6 +316,7 @@ public slots:
|
||||
|
||||
void onParentResize(const QSize &newSize);
|
||||
void getDifference();
|
||||
void getDifferenceForce();
|
||||
|
||||
void setOnline(int windowState = -1);
|
||||
void mainStateChanged(Qt::WindowState state);
|
||||
@@ -348,6 +353,7 @@ private:
|
||||
void feedUpdate(const MTPUpdate &update);
|
||||
|
||||
void updateReceived(const mtpPrime *from, const mtpPrime *end);
|
||||
void handleUpdates(const MTPUpdates &updates);
|
||||
bool updateFail(const RPCError &e);
|
||||
|
||||
void hideAll();
|
||||
@@ -377,18 +383,30 @@ private:
|
||||
|
||||
int updPts, updDate, updQts, updSeq;
|
||||
bool updInited;
|
||||
QTimer noUpdatesTimer;
|
||||
SingleTimer noUpdatesTimer;
|
||||
|
||||
mtpRequestId onlineRequest;
|
||||
QTimer onlineTimer;
|
||||
QTimer onlineUpdater;
|
||||
SingleTimer onlineTimer;
|
||||
SingleTimer onlineUpdater;
|
||||
|
||||
QSet<PeerData*> updateNotifySettingPeers;
|
||||
QTimer updateNotifySettingTimer;
|
||||
SingleTimer updateNotifySettingTimer;
|
||||
|
||||
typedef QMap<PeerData*, mtpRequestId> ReadRequests;
|
||||
ReadRequests _readRequests;
|
||||
|
||||
typedef QMap<PeerData*, mtpRequestId> OverviewsPreload;
|
||||
OverviewsPreload _overviewPreload[OverviewCount], _overviewLoad[OverviewCount];
|
||||
|
||||
QMap<int32, MTPUpdates> _bySeqUpdates;
|
||||
QMap<int32, MTPmessages_SentMessage> _bySeqSentMessage;
|
||||
QMap<int32, MTPmessages_StatedMessage> _bySeqStatedMessage;
|
||||
QMap<int32, MTPmessages_StatedMessages> _bySeqStatedMessages;
|
||||
QMap<int32, int32> _bySeqPart;
|
||||
SingleTimer _bySeqTimer;
|
||||
|
||||
int32 _failDifferenceTimeout; // growing timeout for getDifference calls, if it fails
|
||||
SingleTimer _failDifferenceTimer;
|
||||
|
||||
uint64 _lastUpdateTime;
|
||||
};
|
||||
|
||||
@@ -24,15 +24,44 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#include "application.h"
|
||||
#include "gui/filedialog.h"
|
||||
|
||||
namespace {
|
||||
class SaveMsgLink : public ITextLink {
|
||||
public:
|
||||
|
||||
SaveMsgLink(MediaView *view) : _view(view) {
|
||||
}
|
||||
|
||||
void onClick(Qt::MouseButton button) const {
|
||||
if (button == Qt::LeftButton) {
|
||||
_view->showSaveMsgFile();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
MediaView *_view;
|
||||
};
|
||||
}
|
||||
|
||||
MediaView::MediaView() : TWidget(App::wnd()),
|
||||
_photo(0), _doc(0), _leftNavVisible(false), _rightNavVisible(false), _animStarted(getms()), _maxWidth(0), _maxHeight(0), _width(0),
|
||||
_x(0), _y(0), _w(0), _h(0), _xStart(0), _yStart(0), _zoom(0), _pressed(false), _dragging(0), _full(-1),
|
||||
_history(0), _peer(0), _user(0), _from(0), _index(-1), _msgid(0), _loadRequest(0), _over(OverNone), _down(OverNone), _lastAction(-st::medviewDeltaFromLastAction, -st::medviewDeltaFromLastAction),
|
||||
_close(this, lang(lng_mediaview_close), st::medviewButton),
|
||||
_save(this, lang(lng_mediaview_save), st::medviewButton),
|
||||
_forward(this, lang(lng_mediaview_forward), st::medviewButton),
|
||||
_delete(this, lang(lng_mediaview_delete), st::medviewButton),
|
||||
_menu(0), _receiveMouse(true), _touchPress(false), _touchMove(false), _touchRightButton(false) {
|
||||
_photo(0), _doc(0), _availBottom(0), _leftNavVisible(false), _rightNavVisible(false), _animStarted(getms()),
|
||||
_maxWidth(0), _maxHeight(0), _width(0), _x(0), _y(0), _w(0), _h(0), _xStart(0), _yStart(0),
|
||||
_zoom(0), _pressed(false), _dragging(0), _full(-1), _history(0), _peer(0), _user(0), _from(0), _index(-1), _msgid(0),
|
||||
_loadRequest(0), _over(OverNone), _down(OverNone), _lastAction(-st::medviewDeltaFromLastAction, -st::medviewDeltaFromLastAction),
|
||||
_close(this, st::medviewClose),
|
||||
_save(this, st::medviewSaveAs, lang(lng_mediaview_save)),
|
||||
_forward(this, st::medviewForward, lang(lng_mediaview_forward)),
|
||||
_delete(this, st::medviewDelete, lang(lng_mediaview_delete)),
|
||||
_overview(this, st::medviewOverview, lang(lng_mediaview_single_photo)),
|
||||
_menu(0), _receiveMouse(true), _touchPress(false), _touchMove(false), _touchRightButton(false),
|
||||
_saveMsgStarted(0), _saveMsgOpacity(0)
|
||||
{
|
||||
TextCustomTagsMap custom;
|
||||
custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink()));
|
||||
_saveMsgText.setRichText(st::medviewSaveMsgFont, lang(lng_mediaview_saved), _textDlgOptions, custom);
|
||||
_saveMsg = QRect(0, 0, _saveMsgText.maxWidth() + st::medviewSaveMsgPadding.left() + st::medviewSaveMsgPadding.right(), st::medviewSaveMsgFont->height + st::medviewSaveMsgPadding.top() + st::medviewSaveMsgPadding.bottom());
|
||||
_saveMsgText.setLink(1, TextLinkPtr(new SaveMsgLink(this)));
|
||||
|
||||
setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool | Qt::NoDropShadowWindowHint);
|
||||
moveToScreen();
|
||||
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
@@ -41,9 +70,13 @@ _menu(0), _receiveMouse(true), _touchPress(false), _touchMove(false), _touchRigh
|
||||
hide();
|
||||
|
||||
connect(&_close, SIGNAL(clicked()), this, SLOT(onClose()));
|
||||
connect(&_save, SIGNAL(clicked()), this, SLOT(onSave()));
|
||||
connect(&_save, SIGNAL(clicked()), this, SLOT(onDownload()));
|
||||
connect(&_forward, SIGNAL(clicked()), this, SLOT(onForward()));
|
||||
connect(&_delete, SIGNAL(clicked()), this, SLOT(onDelete()));
|
||||
connect(&_overview, SIGNAL(clicked()), this, SLOT(onOverview()));
|
||||
|
||||
_saveMsgUpdater.setSingleShot(true);
|
||||
connect(&_saveMsgUpdater, SIGNAL(timeout()), this, SLOT(updateImage()));
|
||||
|
||||
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onCheckActive()));
|
||||
|
||||
@@ -54,28 +87,26 @@ _menu(0), _receiveMouse(true), _touchPress(false), _touchMove(false), _touchRigh
|
||||
|
||||
void MediaView::moveToScreen() {
|
||||
QPoint wndCenter(App::wnd()->x() + App::wnd()->width() / 2, App::wnd()->y() + App::wnd()->height() / 2);
|
||||
QRect geom = QDesktopWidget().screenGeometry(wndCenter);
|
||||
_avail = QDesktopWidget().availableGeometry(wndCenter);
|
||||
if (geom != geometry()) {
|
||||
setGeometry(geom);
|
||||
_avail = QDesktopWidget().screenGeometry(wndCenter);
|
||||
if (_avail != geometry()) {
|
||||
setGeometry(_avail);
|
||||
}
|
||||
if (!geom.contains(_avail)) {
|
||||
_avail = geom;
|
||||
}
|
||||
_avail.moveTo(_avail.x() - geom.x(), _avail.y() - geom.y());
|
||||
_maxWidth = _avail.width() - 2 * st::medviewNavBarWidth;
|
||||
_maxHeight = _avail.height() - st::medviewTopSkip - st::medviewBottomSkip;
|
||||
_leftNav = QRect(0, 0, st::medviewNavBarWidth, height());
|
||||
_rightNav = QRect(width() - st::medviewNavBarWidth, 0, st::medviewNavBarWidth, height());
|
||||
_avail.moveTo(0, 0);
|
||||
_availBottom = _avail.height() - st::medviewBottomBar;
|
||||
_maxWidth = _avail.width() - 2 * st::medviewNavBarWidth - st::medviewPolaroid.left() - st::medviewPolaroid.right();
|
||||
_maxHeight = _avail.height() - st::medviewBottomBar - st::medviewPolaroid.top() - st::medviewPolaroid.bottom();
|
||||
_leftNav = QRect(0, 0, st::medviewNavBarWidth, _availBottom);
|
||||
_rightNav = QRect(width() - st::medviewNavBarWidth, 0, st::medviewNavBarWidth + (cRetina() ? 1 : 0), _availBottom);
|
||||
|
||||
int32 w = st::medviewMainWidth + (st::medviewTopSkip - _save.height()), l = _avail.x() + (_avail.width() - w) / 2;
|
||||
_topActions = QRect(l, _avail.y(), w, st::medviewTopSkip);
|
||||
_bottomActions = QRect(l, _avail.y() + _avail.height() - st::medviewBottomSkip, w, st::medviewBottomSkip);
|
||||
_bottomBar = QRect(0, _availBottom, width(), height() - _availBottom);
|
||||
|
||||
_close.move(_avail.x() + (_avail.width() + st::medviewMainWidth) / 2 - _close.width(), _avail.y() + (st::medviewTopSkip - _close.height()) / 2);
|
||||
_save.move(_avail.x() + (_avail.width() - st::medviewMainWidth) / 2, _avail.y() + (st::medviewTopSkip - _save.height()) / 2);
|
||||
_delete.move(_avail.x() + (_avail.width() + st::medviewMainWidth) / 2 - _delete.width(), _avail.y() + _avail.height() - (st::medviewTopSkip + _delete.height()) / 2);
|
||||
_forward.move(_avail.x() + (_avail.width() - st::medviewMainWidth) / 2, _avail.y() + _avail.height() - (st::medviewTopSkip + _forward.height()) / 2);
|
||||
_close.move(width() - ((_close.width() + st::medviewNavBarWidth) / 2), (st::medviewNavBarWidth - _close.width()) / 2);
|
||||
_save.move(width() - _save.width() - ((st::medviewBottomBar - _save.height()) / 2), _availBottom + ((st::medviewBottomBar - _save.height()) / 2));
|
||||
_forward.move(_save.x() - _forward.width() - ((st::medviewBottomBar - _forward.height()) / 2), _availBottom + ((st::medviewBottomBar - _forward.height()) / 2));
|
||||
_delete.move(_forward.isHidden() ? _save.x() : _forward.x() - _delete.width() - ((st::medviewBottomBar - _delete.height()) / 2), _availBottom + ((st::medviewBottomBar - _delete.height()) / 2));
|
||||
_overview.move(((st::medviewBottomBar - _overview.height()) / 2), _availBottom + ((st::medviewBottomBar - _overview.height()) / 2));
|
||||
|
||||
_saveMsg.moveTo((width() - _saveMsg.width()) / 2, (_availBottom - st::medviewPolaroid.bottom() - _saveMsg.height() + st::medviewPolaroid.top()) / 2);
|
||||
}
|
||||
|
||||
void MediaView::mediaOverviewUpdated(PeerData *peer) {
|
||||
@@ -123,14 +154,17 @@ void MediaView::updateControls() {
|
||||
HistoryItem *item = App::histItemById(_msgid);
|
||||
if (dynamic_cast<HistoryMessage*>(item)) {
|
||||
_forward.show();
|
||||
_delete.move(_forward.x() - _delete.width() - ((st::medviewBottomBar - _delete.height()) / 2), _availBottom + ((st::medviewBottomBar - _delete.height()) / 2));
|
||||
} else {
|
||||
_forward.hide();
|
||||
_delete.move(_save.x() - _delete.width() - ((st::medviewBottomBar - _delete.height()) / 2), _availBottom + ((st::medviewBottomBar - _delete.height()) / 2));
|
||||
}
|
||||
_delete.show();
|
||||
} else {
|
||||
_forward.hide();
|
||||
if (_photo && ((App::self() && App::self()->photoId == _photo->id) || (_photo->chat && _photo->chat->photoId == _photo->id))) {
|
||||
_delete.show();
|
||||
_delete.move(_save.x() - _delete.width() - ((st::medviewBottomBar - _delete.height()) / 2), _availBottom + ((st::medviewBottomBar - _delete.height()) / 2));
|
||||
} else {
|
||||
_delete.hide();
|
||||
}
|
||||
@@ -143,18 +177,14 @@ void MediaView::updateControls() {
|
||||
} else {
|
||||
_dateText = lang(lng_status_lastseen_date_time).replace(qsl("{date}"), d.date().toString(qsl("dd.MM.yy"))).replace(qsl("{time}"), d.time().toString(qsl("hh:mm")));
|
||||
}
|
||||
int32 nameWidth = _from->nameText.maxWidth(), maxWidth = _delete.x() - _forward.x() - _forward.width(), dateWidth = st::medviewDateFont->m.width(_dateText);
|
||||
if (nameWidth > maxWidth) {
|
||||
nameWidth = maxWidth;
|
||||
}
|
||||
_nameNav = QRect(_forward.x() + _forward.width() + (maxWidth - nameWidth) / 2, _forward.y() + st::medviewNameTop, nameWidth, st::msgNameFont->height);
|
||||
_dateNav = QRect(_forward.x() + _forward.width() + (maxWidth - dateWidth) / 2, _forward.y() + st::medviewDateTop, dateWidth, st::medviewDateFont->height);
|
||||
_fromName.setText(st::medviewNameFont, _from->name);
|
||||
updateHeader();
|
||||
_leftNavVisible = _photo && (_index > 0 || (_index == 0 && _history && _history->_overview[OverviewPhotos].size() < _history->_overviewCount[OverviewPhotos]));
|
||||
_rightNavVisible = _photo && (_index >= 0 && (
|
||||
(_history && _index + 1 < _history->_overview[OverviewPhotos].size()) ||
|
||||
(_user && (_index + 1 < _user->photos.size() || _index + 1 < _user->photosCount))));
|
||||
updateOver(mapFromGlobal(QCursor::pos()));
|
||||
updatePolaroid();
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -169,7 +199,7 @@ bool MediaView::animStep(float64 msp) {
|
||||
case OverDate: update(_dateNav); break;
|
||||
default: break;
|
||||
}
|
||||
float64 dt = float64(ms - start) / st::medviewButton.duration;
|
||||
float64 dt = float64(ms - start) / st::medviewOverview.duration;
|
||||
if (dt >= 1) {
|
||||
_animOpacities.remove(i.key());
|
||||
i = _animations.erase(i);
|
||||
@@ -185,18 +215,27 @@ MediaView::~MediaView() {
|
||||
delete _menu;
|
||||
}
|
||||
|
||||
void MediaView::showSaveMsgFile() {
|
||||
psShowInFolder(_saveMsgFilename);
|
||||
}
|
||||
|
||||
void MediaView::onClose() {
|
||||
if (App::wnd()) App::wnd()->layerHidden();
|
||||
}
|
||||
|
||||
void MediaView::onSave() {
|
||||
QString file;
|
||||
if (_doc) {
|
||||
QString cur = _doc->already(true), file;
|
||||
QString cur = _doc->already(true);
|
||||
if (cur.isEmpty()) {
|
||||
_save.hide();
|
||||
return;
|
||||
}
|
||||
if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), cur)) {
|
||||
|
||||
psBringToBack(this);
|
||||
bool gotName = filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), cur);
|
||||
psShowOverAll(this);
|
||||
if (gotName) {
|
||||
if (!file.isEmpty() && file != cur) {
|
||||
QFile(cur).copy(file);
|
||||
}
|
||||
@@ -204,8 +243,10 @@ void MediaView::onSave() {
|
||||
} else {
|
||||
if (!_photo || !_photo->full->loaded()) return;
|
||||
|
||||
QString file;
|
||||
if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg")))) {
|
||||
psBringToBack(this);
|
||||
bool gotName = filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg")));
|
||||
psShowOverAll(this);
|
||||
if (gotName) {
|
||||
if (!file.isEmpty()) {
|
||||
_photo->full->pix().toImage().save(file, "JPG");
|
||||
}
|
||||
@@ -213,6 +254,48 @@ void MediaView::onSave() {
|
||||
}
|
||||
}
|
||||
|
||||
void MediaView::onDownload() {
|
||||
if (cAskDownloadPath()) {
|
||||
return onSave();
|
||||
}
|
||||
|
||||
QString path;
|
||||
if (cDownloadPath().isEmpty()) {
|
||||
path = psDownloadPath();
|
||||
} else if (cDownloadPath() == qsl("tmp")) {
|
||||
path = cTempDir();
|
||||
} else {
|
||||
path = cDownloadPath();
|
||||
}
|
||||
QString toName;
|
||||
if (_doc) {
|
||||
QString cur = _doc->already(true);
|
||||
if (cur.isEmpty()) {
|
||||
_save.hide();
|
||||
} else {
|
||||
toName = filedialogNextFilename(_doc->name, cur, path);
|
||||
if (toName != cur && !QFile(cur).copy(toName)) {
|
||||
toName = QString();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!_photo || !_photo->full->loaded()) {
|
||||
_save.hide();
|
||||
} else {
|
||||
toName = filedialogDefaultName(qsl("photo"), qsl(".jpg"), path);
|
||||
if (!_photo->full->pix().toImage().save(toName, "JPG")) {
|
||||
toName = QString();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!toName.isEmpty()) {
|
||||
_saveMsgFilename = toName;
|
||||
_saveMsgStarted = getms();
|
||||
_saveMsgOpacity.start(1);
|
||||
updateImage();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaView::onShowInFolder() {
|
||||
QString already(_doc->already(true));
|
||||
if (!already.isEmpty()) psShowInFolder(already);
|
||||
@@ -248,6 +331,11 @@ void MediaView::onDelete() {
|
||||
}
|
||||
}
|
||||
|
||||
void MediaView::onOverview() {
|
||||
onClose();
|
||||
if (_history->peer) App::main()->showMediaOverview(_history->peer, OverviewPhotos);
|
||||
}
|
||||
|
||||
void MediaView::onCopy() {
|
||||
if (_doc) {
|
||||
QApplication::clipboard()->setPixmap(_current);
|
||||
@@ -262,7 +350,7 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) {
|
||||
_history = context ? context->history() : 0;
|
||||
_peer = 0;
|
||||
_user = 0;
|
||||
|
||||
_saveMsgStarted = 0;
|
||||
_loadRequest = 0;
|
||||
_over = OverNone;
|
||||
_pressed = false;
|
||||
@@ -277,6 +365,7 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) {
|
||||
|
||||
_index = -1;
|
||||
_msgid = context ? context->id : 0;
|
||||
_photo = photo;
|
||||
if (_history) {
|
||||
for (int i = 0, l = _history->_overview[OverviewPhotos].size(); i < l; ++i) {
|
||||
if (_history->_overview[OverviewPhotos].at(i) == _msgid) {
|
||||
@@ -298,7 +387,7 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) {
|
||||
_history = 0;
|
||||
_peer = context;
|
||||
_user = context->chat ? 0 : context->asUser();
|
||||
|
||||
_saveMsgStarted = 0;
|
||||
_loadRequest = 0;
|
||||
_over = OverNone;
|
||||
if (!_animations.isEmpty()) {
|
||||
@@ -310,6 +399,7 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) {
|
||||
|
||||
_msgid = 0;
|
||||
_index = -1;
|
||||
_photo = photo;
|
||||
if (_user) {
|
||||
if (_user->photos.isEmpty() && _user->photosCount < 0 && _user->photoId) {
|
||||
_index = 0;
|
||||
@@ -332,6 +422,7 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) {
|
||||
void MediaView::showDocument(DocumentData *doc, QPixmap pix, HistoryItem *context) {
|
||||
_photo = 0;
|
||||
_history = context ? context->history() : 0;
|
||||
_saveMsgStarted = 0;
|
||||
_peer = 0;
|
||||
_user = 0;
|
||||
_zoom = 0;
|
||||
@@ -359,8 +450,8 @@ void MediaView::showDocument(DocumentData *doc, QPixmap pix, HistoryItem *contex
|
||||
}
|
||||
_w = _current.width() / cIntRetinaFactor();
|
||||
_h = _current.height() / cIntRetinaFactor();
|
||||
_x = _avail.x() + (_avail.width() - _w) / 2;
|
||||
_y = _avail.y() + (_avail.height() - _h) / 2;
|
||||
_x = (_avail.width() - _w) / 2;
|
||||
_y = st::medviewPolaroid.top() + (_avail.height() - st::medviewPolaroid.top() - st::medviewPolaroid.bottom() - st::medviewBottomBar - _h) / 2;
|
||||
_width = _w;
|
||||
_from = App::user(_doc->user);
|
||||
_full = 1;
|
||||
@@ -368,6 +459,7 @@ void MediaView::showDocument(DocumentData *doc, QPixmap pix, HistoryItem *contex
|
||||
if (isHidden()) {
|
||||
psUpdateOverlayed(this);
|
||||
show();
|
||||
psShowOverAll(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,13 +472,8 @@ void MediaView::showPhoto(PhotoData *photo) {
|
||||
_full = -1;
|
||||
_current = QPixmap();
|
||||
_down = OverNone;
|
||||
_w = photo->full->width();
|
||||
_h = photo->full->height();
|
||||
switch (cScale()) {
|
||||
case dbisOneAndQuarter: _w = qRound(float64(_w) * 1.25 - 0.01); _h = qRound(float64(_h) * 1.25 - 0.01); break;
|
||||
case dbisOneAndHalf: _w = qRound(float64(_w) * 1.5 - 0.01); _h = qRound(float64(_h) * 1.5 - 0.01); break;
|
||||
case dbisTwo: _w *= 2; _h *= 2; break;
|
||||
}
|
||||
_w = convertScale(photo->full->width());
|
||||
_h = convertScale(photo->full->height());
|
||||
if (isHidden()) {
|
||||
moveToScreen();
|
||||
}
|
||||
@@ -398,14 +485,15 @@ void MediaView::showPhoto(PhotoData *photo) {
|
||||
_w = qRound(_w * _maxHeight / float64(_h));
|
||||
_h = _maxHeight;
|
||||
}
|
||||
_x = _avail.x() + (_avail.width() - _w) / 2;
|
||||
_y = _avail.y() + (_avail.height() - _h) / 2;
|
||||
_x = (_avail.width() - _w) / 2;
|
||||
_y = st::medviewPolaroid.top() + (_avail.height() - st::medviewPolaroid.top() - st::medviewPolaroid.bottom() - st::medviewBottomBar - _h) / 2;
|
||||
_width = _w;
|
||||
_from = App::user(_photo->user);
|
||||
updateControls();
|
||||
if (isHidden()) {
|
||||
psUpdateOverlayed(this);
|
||||
show();
|
||||
psShowOverAll(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,31 +505,26 @@ void MediaView::paintEvent(QPaintEvent *e) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
|
||||
// main bg
|
||||
p.setOpacity(st::medviewLightOpacity);
|
||||
QRect r_bg(st::medviewNavBarWidth, 0, width() - 2 * st::medviewNavBarWidth, height());
|
||||
if (r_bg.intersects(r)) p.fillRect(r_bg.intersected(r), st::black->b);
|
||||
QRect r_bg(st::medviewNavBarWidth, 0, _avail.width() - 2 * st::medviewNavBarWidth, _avail.height() - st::medviewBottomBar);
|
||||
if (r_bg.intersects(r)) p.fillRect(r_bg.intersected(r), st::medviewBG->b);
|
||||
if (_bottomBar.intersects(r)) p.fillRect(_bottomBar.intersected(r), st::medviewBottomBG->b);
|
||||
|
||||
// left nav bar bg
|
||||
if (_leftNav.intersects(r)) {
|
||||
if (_leftNavVisible) {
|
||||
float64 o = overLevel(OverLeftNav);
|
||||
p.setOpacity(o * st::medviewDarkOpacity + (1 - o) * st::medviewLightOpacity);
|
||||
p.fillRect(_leftNav.intersected(r), st::black->b);
|
||||
p.fillRect(_leftNav.intersected(r), overColor(st::medviewBG->c, 1, st::black->c, overLevel(OverLeftNav) * st::medviewNavBGOpacity));
|
||||
} else {
|
||||
p.setOpacity(st::medviewLightOpacity);
|
||||
p.fillRect(_leftNav.intersected(r), st::black->b);
|
||||
p.fillRect(_leftNav.intersected(r), st::medviewBG->c);
|
||||
}
|
||||
}
|
||||
|
||||
// right nav bar
|
||||
if (_rightNav.intersects(r)) {
|
||||
if (_rightNavVisible) {
|
||||
p.fillRect(_rightNav.intersected(r), overColor(st::medviewBG->c, 1, st::black->c, overLevel(OverRightNav) * st::medviewNavBGOpacity));
|
||||
float64 o = overLevel(OverRightNav);
|
||||
p.setOpacity(o * st::medviewDarkOpacity + (1 - o) * st::medviewLightOpacity);
|
||||
p.fillRect(_rightNav.intersected(r), st::black->b);
|
||||
} else {
|
||||
p.setOpacity(st::medviewLightOpacity);
|
||||
p.fillRect(_rightNav.intersected(r), st::black->b);
|
||||
p.fillRect(_rightNav.intersected(r), st::medviewBG->b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,16 +533,20 @@ void MediaView::paintEvent(QPaintEvent *e) {
|
||||
|
||||
// photo
|
||||
if (_photo) {
|
||||
int32 w = _width * cIntRetinaFactor();
|
||||
if (_full <= 0 && _photo->full->loaded()) {
|
||||
_current = _photo->full->pixNoCache(_width * cIntRetinaFactor(), 0, true);
|
||||
int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999);
|
||||
_current = _photo->full->pixNoCache(w, h, true);
|
||||
if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor());
|
||||
_full = 1;
|
||||
} else if (_full < 0 && _photo->medium->loaded()) {
|
||||
_current = _photo->medium->pixBlurredNoCache(_width * cIntRetinaFactor());
|
||||
int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999);
|
||||
_current = _photo->medium->pixBlurredNoCache(w, h);
|
||||
if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor());
|
||||
_full = 0;
|
||||
} else if (_current.isNull() && _photo->thumb->loaded()) {
|
||||
_current = _photo->thumb->pixBlurredNoCache(_width * cIntRetinaFactor());
|
||||
int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999);
|
||||
_current = _photo->thumb->pixBlurredNoCache(w, h);
|
||||
if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
}
|
||||
@@ -468,40 +555,40 @@ void MediaView::paintEvent(QPaintEvent *e) {
|
||||
if (imgRect.intersects(r)) {
|
||||
if (_zoom) {
|
||||
bool was = (p.renderHints() & QPainter::SmoothPixmapTransform);
|
||||
if (!was) p.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
if (!was) p.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||
p.drawPixmap(QRect(_x, _y, _w, _h), _current);
|
||||
if (!was) p.setRenderHint(QPainter::SmoothPixmapTransform, false);
|
||||
} else {
|
||||
p.drawPixmap(_x, _y, _current);
|
||||
}
|
||||
if (imgRect.intersects(_topActions)) {
|
||||
p.setOpacity(st::medviewControlsBgOpacity);
|
||||
p.fillRect(imgRect.intersected(_topActions), st::black->b);
|
||||
p.setOpacity(1);
|
||||
}
|
||||
if (_polaroidOut.intersects(r)) {
|
||||
// polaroid
|
||||
p.fillRect(_polaroidOut.x(), _polaroidOut.y(), _polaroidIn.x() - _polaroidOut.x(), _polaroidOut.height(), st::white->b);
|
||||
p.fillRect(_polaroidIn.x() + _polaroidIn.width(), _polaroidOut.y(), _polaroidOut.x() + _polaroidOut.width() - _polaroidIn.x() - _polaroidIn.width(), _polaroidOut.height(), st::white->b);
|
||||
p.fillRect(_polaroidIn.x(), _polaroidOut.y(), _polaroidIn.width(), _polaroidIn.y() - _polaroidOut.y(), st::white->b);
|
||||
p.fillRect(_polaroidIn.x(), _polaroidIn.y() + _polaroidIn.height(), _polaroidIn.width(), _polaroidOut.y() + _polaroidOut.height() - _polaroidIn.y() - _polaroidIn.height(), st::white->b);
|
||||
}
|
||||
if (imgRect.intersects(r)) {
|
||||
uint64 ms = 0;
|
||||
if (imgRect.intersects(_leftNav)) {
|
||||
p.fillRect(imgRect.intersected(_leftNav), _leftNavVisible ? overColor(st::medviewBG->c, 1, st::black->c, overLevel(OverLeftNav) * st::medviewNavBGOpacity) : st::medviewBG->c);
|
||||
}
|
||||
if (imgRect.intersects(_bottomActions)) {
|
||||
p.setOpacity(st::medviewControlsBgOpacity);
|
||||
p.fillRect(imgRect.intersected(_bottomActions), st::black->b);
|
||||
p.setOpacity(1);
|
||||
if (imgRect.intersects(_rightNav)) {
|
||||
p.fillRect(imgRect.intersected(_rightNav), _rightNavVisible ? overColor(st::medviewBG->c, 1, st::black->c, overLevel(OverRightNav) * st::medviewNavBGOpacity) : st::medviewBG->c);
|
||||
}
|
||||
if (_leftNavVisible && imgRect.intersects(_leftNav)) {
|
||||
float64 o = overLevel(OverLeftNav);
|
||||
p.setOpacity(o * st::medviewDarkOpacity + (1 - o) * st::medviewControlsBgOpacity);
|
||||
p.fillRect(imgRect.intersected(_leftNav), st::black->b);
|
||||
p.setOpacity(1);
|
||||
}
|
||||
if (_rightNavVisible && imgRect.intersects(_rightNav)) {
|
||||
float64 o = overLevel(OverRightNav);
|
||||
p.setOpacity(o * st::medviewDarkOpacity + (1 - o) * st::medviewControlsBgOpacity);
|
||||
p.fillRect(imgRect.intersected(_rightNav), st::black->b);
|
||||
p.setOpacity(1);
|
||||
if (imgRect.intersects(_bottomBar)) {
|
||||
p.fillRect(imgRect.intersected(_bottomBar), st::medviewBG->b);
|
||||
}
|
||||
if (_full < 1) {
|
||||
uint64 dt = getms() - _animStarted;
|
||||
ms = getms();
|
||||
uint64 dt = ms - _animStarted;
|
||||
int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta);
|
||||
|
||||
int32 x = _avail.x() + (_avail.width() - st::mediaviewLoader.width()) / 2, y = _avail.y() + (_avail.height() - st::mediaviewLoader.height()) / 2;
|
||||
int32 x = (_avail.width() - st::mediaviewLoader.width()) / 2;
|
||||
int32 y = st::medviewPolaroid.top() + (_availBottom - st::medviewPolaroid.top() - st::medviewPolaroid.bottom() - st::mediaviewLoader.height()) / 2;
|
||||
p.fillRect(x, y, st::mediaviewLoader.width(), st::mediaviewLoader.height(), st::photoLoaderBg->b);
|
||||
|
||||
x += (st::mediaviewLoader.width() - cnt * st::mediaviewLoaderPoint.width() - (cnt - 1) * st::mediaviewLoaderSkip) / 2;
|
||||
y += (st::mediaviewLoader.height() - st::mediaviewLoaderPoint.height()) / 2;
|
||||
QColor c(st::white->c);
|
||||
@@ -515,15 +602,55 @@ void MediaView::paintEvent(QPaintEvent *e) {
|
||||
b.setColor(c);
|
||||
p.fillRect(x + i * (st::mediaviewLoaderPoint.width() + st::mediaviewLoaderSkip), y, st::mediaviewLoaderPoint.width(), st::mediaviewLoaderPoint.height(), b);
|
||||
}
|
||||
QTimer::singleShot(AnimationTimerDelta, this, SLOT(updateImage()));
|
||||
_saveMsgUpdater.start(AnimationTimerDelta);
|
||||
}
|
||||
if (_saveMsgStarted) {
|
||||
if (!ms) ms = getms();
|
||||
float64 dt = float64(ms) - _saveMsgStarted, hidingDt = dt - st::medviewSaveMsgShowing - st::medviewSaveMsgShown;
|
||||
if (dt < st::medviewSaveMsgShowing + st::medviewSaveMsgShown + st::medviewSaveMsgHiding) {
|
||||
if (hidingDt >= 0 && _saveMsgOpacity.to() > 0.5) {
|
||||
_saveMsgOpacity.start(0);
|
||||
}
|
||||
float64 progress = (hidingDt >= 0) ? (hidingDt / st::medviewSaveMsgHiding) : (dt / st::medviewSaveMsgShowing);
|
||||
_saveMsgOpacity.update(qMin(progress, 1.), anim::linear);
|
||||
if (_saveMsgOpacity.current() > 0) {
|
||||
p.setOpacity(_saveMsgOpacity.current());
|
||||
p.setBrush(st::medviewSaveMsg->b);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.drawRoundedRect(_saveMsg, st::medviewSaveMsgRadius, st::medviewSaveMsgRadius);
|
||||
p.drawPixmap(_saveMsg.topLeft() + st::medviewSaveMsgCheckPos, App::sprite(), st::medviewSaveMsgCheck);
|
||||
|
||||
p.setPen(st::white->p);
|
||||
textstyleSet(&st::medviewSaveAsTextStyle);
|
||||
_saveMsgText.draw(p, _saveMsg.x() + st::medviewSaveMsgPadding.left(), _saveMsg.y() + st::medviewSaveMsgPadding.top(), _saveMsg.width() - st::medviewSaveMsgPadding.left() - st::medviewSaveMsgPadding.right());
|
||||
textstyleRestore();
|
||||
p.setOpacity(1);
|
||||
}
|
||||
if (_full >= 1) {
|
||||
uint64 nextFrame = (dt < st::medviewSaveMsgShowing || hidingDt >= 0) ? int(AnimationTimerDelta) : (st::medviewSaveMsgShowing + st::medviewSaveMsgShown + 1 - dt);
|
||||
_saveMsgUpdater.start(nextFrame);
|
||||
}
|
||||
} else {
|
||||
_saveMsgStarted = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// disabled download button
|
||||
if (_save.isHidden()) {
|
||||
p.fillRect(_save.geometry(), st::medviewSaveAs.bgColor->b);
|
||||
p.setOpacity(st::medviewSaveAsDisabledOpacity);
|
||||
p.setPen(st::medviewSaveAs.color->p);
|
||||
p.setFont(st::medviewSaveAs.font->f);
|
||||
p.drawPixmap(_save.geometry().topLeft() + st::medviewSaveAs.iconPos, App::sprite(), st::medviewSaveAs.icon);
|
||||
p.drawText(_save.geometry().topLeft() + st::medviewSaveAs.textPos + QPoint(0, st::medviewSaveAs.font->ascent), lang(lng_mediaview_save));
|
||||
p.setOpacity(1);
|
||||
}
|
||||
|
||||
// left nav bar
|
||||
// left nav arrow
|
||||
if (_leftNavVisible) {
|
||||
QPoint p_left((st::medviewNavBarWidth - st::medviewLeft.pxWidth()) / 2, (height() - st::medviewLeft.pxHeight()) / 2);
|
||||
QPoint p_left((st::medviewNavBarWidth - st::medviewLeft.pxWidth()) / 2, (height() - st::medviewBottomBar - st::medviewLeft.pxHeight()) / 2);
|
||||
if (QRect(p_left.x(), p_left.y(), st::medviewLeft.pxWidth(), st::medviewLeft.pxHeight()).intersects(r)) {
|
||||
float64 o = overLevel(OverLeftNav);
|
||||
p.setOpacity(o * st::medviewDarkNav + (1 - o) * st::medviewLightNav);
|
||||
@@ -531,9 +658,9 @@ void MediaView::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
// right nav bar
|
||||
// right nav arrow
|
||||
if (_rightNavVisible) {
|
||||
QPoint p_right(width() - (st::medviewNavBarWidth + st::medviewRight.pxWidth()) / 2, (height() - st::medviewRight.pxHeight()) / 2);
|
||||
QPoint p_right(width() - (st::medviewNavBarWidth + st::medviewRight.pxWidth()) / 2, (height() - st::medviewBottomBar - st::medviewRight.pxHeight()) / 2);
|
||||
if (QRect(p_right.x(), p_right.y(), st::medviewRight.pxWidth(), st::medviewRight.pxHeight()).intersects(r)) {
|
||||
float64 o = overLevel(OverRightNav);
|
||||
p.setOpacity(o * st::medviewDarkNav + (1 - o) * st::medviewLightNav);
|
||||
@@ -543,19 +670,26 @@ void MediaView::paintEvent(QPaintEvent *e) {
|
||||
p.setOpacity(1);
|
||||
|
||||
// header
|
||||
p.setPen(st::medviewHeaderColor->p);
|
||||
p.setFont(st::medviewHeaderFont->f);
|
||||
QRect r_header(_save.x() + _save.width(), _save.y(), _close.x() - _save.x() - _save.width(), _save.height());
|
||||
if (r_header.intersects(r)) p.drawText(r_header, _header, style::al_center);
|
||||
if (_overview.isHidden()) {
|
||||
QRect r_header(_overview.x(), _overview.y(), st::medviewHeaderFont->m.width(_header) - st::medviewOverview.width, _overview.height());
|
||||
if (r_header.intersects(r)) {
|
||||
p.setOpacity(st::medviewOverview.opacity);
|
||||
p.setPen(st::medviewOverview.color->p);
|
||||
p.setFont(st::medviewOverview.font->f);
|
||||
p.drawPixmap(_overview.geometry().topLeft() + (_photo ? st::medviewPhotoSpritePos : st::medviewDocumentSpritePos), App::sprite(), _photo ? st::medviewPhotoSprite : st::medviewDocumentSprite);
|
||||
p.drawText(r_header.topLeft() + st::medviewOverview.textPos + QPoint(0, st::medviewHeaderFont->ascent), _header);
|
||||
p.setOpacity(1);
|
||||
}
|
||||
}
|
||||
|
||||
// name
|
||||
p.setPen(nameDateColor(overLevel(OverName)));
|
||||
if (_over == OverName) _from->nameText.replaceFont(st::msgNameFont->underline());
|
||||
if (_nameNav.intersects(r)) _from->nameText.drawElided(p, _nameNav.left(), _nameNav.top(), _nameNav.width());
|
||||
if (_over == OverName) _from->nameText.replaceFont(st::msgNameFont);
|
||||
p.setPen(st::medviewNameColor->p);
|
||||
if (_over == OverName) _fromName.replaceFont(st::medviewNameFont->underline());
|
||||
if (_nameNav.intersects(r)) _fromName.drawElided(p, _nameNav.left(), _nameNav.top(), _nameNav.width());
|
||||
if (_over == OverName) _fromName.replaceFont(st::medviewNameFont);
|
||||
|
||||
// date
|
||||
p.setPen(nameDateColor(overLevel(OverDate)));
|
||||
p.setPen(st::medviewDateColor->p);
|
||||
p.setFont((_over == OverDate ? st::medviewDateFont->underline() : st::medviewDateFont)->f);
|
||||
if (_dateNav.intersects(r)) p.drawText(_dateNav.left(), _dateNav.top() + st::medviewDateFont->ascent, _dateText);
|
||||
}
|
||||
@@ -579,8 +713,8 @@ void MediaView::keyPressEvent(QKeyEvent *e) {
|
||||
if (newZoom > -MaxZoomLevel) --newZoom;
|
||||
} else {
|
||||
newZoom = 0;
|
||||
_x = _avail.x() - _width / 2;
|
||||
_y = _avail.y() - (_current.height() / cIntRetinaFactor()) / 2;
|
||||
_x = -_width / 2;
|
||||
_y = st::medviewPolaroid.top() - ((_current.height() / cIntRetinaFactor()) / 2);
|
||||
if (_zoom >= 0) {
|
||||
_x *= _zoom + 1;
|
||||
_y *= _zoom + 1;
|
||||
@@ -589,7 +723,8 @@ void MediaView::keyPressEvent(QKeyEvent *e) {
|
||||
_y /= -_zoom + 1;
|
||||
}
|
||||
_x += _avail.width() / 2;
|
||||
_y += _avail.height() / 2;
|
||||
_y += (_avail.height() - st::medviewBottomBar - st::medviewPolaroid.top() - st::medviewPolaroid.bottom()) / 2;
|
||||
updatePolaroid();
|
||||
update();
|
||||
}
|
||||
while ((newZoom < 0 && (-newZoom + 1) > _w) || (-newZoom + 1) > _h) {
|
||||
@@ -619,7 +754,6 @@ void MediaView::keyPressEvent(QKeyEvent *e) {
|
||||
_y = int32(ny / (-_zoom + 1) + _avail.height() / 2.);
|
||||
}
|
||||
snapXY();
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
@@ -700,41 +834,45 @@ void MediaView::preloadPhotos(int32 delta) {
|
||||
void MediaView::mousePressEvent(QMouseEvent *e) {
|
||||
updateOver(e->pos());
|
||||
if (_menu || !_receiveMouse) return;
|
||||
textlnkDown(textlnkOver());
|
||||
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
_down = OverNone;
|
||||
if (_over == OverLeftNav && _index >= 0) {
|
||||
moveToPhoto(-1);
|
||||
_lastAction = e->pos();
|
||||
} else if (_over == OverRightNav && _index >= 0) {
|
||||
moveToPhoto(1);
|
||||
_lastAction = e->pos();
|
||||
} else if (_over == OverName) {
|
||||
_down = OverName;
|
||||
} else if (_over == OverDate) {
|
||||
_down = OverDate;
|
||||
} else if (!_topActions.contains(e->pos()) && !_bottomActions.contains(e->pos())) {
|
||||
_pressed = true;
|
||||
_dragging = 0;
|
||||
setCursor(style::cur_default);
|
||||
_mStart = e->pos();
|
||||
_xStart = _x;
|
||||
_yStart = _y;
|
||||
if (!textlnkDown()) {
|
||||
if (_over == OverLeftNav && _index >= 0) {
|
||||
moveToPhoto(-1);
|
||||
_lastAction = e->pos();
|
||||
} else if (_over == OverRightNav && _index >= 0) {
|
||||
moveToPhoto(1);
|
||||
_lastAction = e->pos();
|
||||
} else if (_over == OverName) {
|
||||
_down = OverName;
|
||||
} else if (_over == OverDate) {
|
||||
_down = OverDate;
|
||||
} else if (!_bottomBar.contains(e->pos()) && (!_saveMsg.contains(e->pos()) || !_saveMsgStarted)) {
|
||||
_pressed = true;
|
||||
_dragging = 0;
|
||||
setCursor(style::cur_default);
|
||||
_mStart = e->pos();
|
||||
_xStart = _x;
|
||||
_yStart = _y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaView::snapXY() {
|
||||
int32 xmin = _avail.x() + _avail.width() - _w - st::medviewNavBarWidth, xmax = _avail.x() + st::medviewNavBarWidth;
|
||||
int32 ymin = _avail.y() + _avail.height() - _h - st::medviewTopSkip, ymax = _avail.y() + st::medviewTopSkip;
|
||||
if (xmin > _avail.x() + ((_avail.width() - _w) / 2)) xmin = _avail.x() + ((_avail.width() - _w) / 2);
|
||||
if (xmax < _avail.x() + ((_avail.width() - _w) / 2)) xmax = _avail.x() + ((_avail.width() - _w) / 2);
|
||||
if (ymin > _avail.y() + ((_avail.height() - _h) / 2)) ymin = _avail.y() + ((_avail.height() - _h) / 2);
|
||||
if (ymax < _avail.y() + ((_avail.height() - _h) / 2)) ymax = _avail.y() + ((_avail.height() - _h) / 2);
|
||||
int32 xmin = _avail.width() - _w - st::medviewNavBarWidth - st::medviewPolaroid.right(), xmax = st::medviewPolaroid.left() + st::medviewNavBarWidth;
|
||||
int32 ymin = _avail.height() - _h - st::medviewPolaroid.bottom() - st::medviewBottomBar, ymax = st::medviewPolaroid.top();
|
||||
if (xmin > (_avail.width() - _w) / 2) xmin = (_avail.width() - _w) / 2;
|
||||
if (xmax < (_avail.width() - _w) / 2) xmax = (_avail.width() - _w) / 2;
|
||||
if (ymin > (_avail.height() - _h - st::medviewBottomBar - st::medviewPolaroid.bottom() + st::medviewPolaroid.top()) / 2) ymin = (_avail.height() - _h - st::medviewBottomBar - st::medviewPolaroid.bottom() + st::medviewPolaroid.top()) / 2;
|
||||
if (ymax < (_avail.height() - _h - st::medviewBottomBar - st::medviewPolaroid.bottom() + st::medviewPolaroid.top()) / 2) ymax = (_avail.height() - _h - st::medviewBottomBar - st::medviewPolaroid.bottom() + st::medviewPolaroid.top()) / 2;
|
||||
if (_x < xmin) _x = xmin;
|
||||
if (_x > xmax) _x = xmax;
|
||||
if (_y < ymin) _y = ymin;
|
||||
if (_y > ymax) _y = ymax;
|
||||
updatePolaroid();
|
||||
}
|
||||
|
||||
void MediaView::mouseMoveEvent(QMouseEvent *e) {
|
||||
@@ -746,7 +884,7 @@ void MediaView::mouseMoveEvent(QMouseEvent *e) {
|
||||
if (!_dragging && (e->pos() - _mStart).manhattanLength() >= QApplication::startDragDistance()) {
|
||||
_dragging = QRect(_x, _y, _w, _h).contains(_mStart) ? 1 : -1;
|
||||
if (_dragging > 0) {
|
||||
if (_w > _avail.width() - 2 * st::medviewNavBarWidth || _h > _avail.height() - 2 * st::medviewTopSkip) {
|
||||
if (_w > _avail.width() - 2 * st::medviewNavBarWidth - st::medviewPolaroid.left() - st::medviewPolaroid.right() || _h > _avail.height() - st::medviewPolaroid.top() - st::medviewPolaroid.bottom() - st::medviewBottomBar) {
|
||||
setCursor(style::cur_sizeall);
|
||||
} else {
|
||||
setCursor(style::cur_default);
|
||||
@@ -797,6 +935,17 @@ bool MediaView::updateOverState(OverState newState) {
|
||||
}
|
||||
|
||||
void MediaView::updateOver(const QPoint &pos) {
|
||||
TextLinkPtr lnk;
|
||||
bool inText;
|
||||
if (_saveMsgStarted) {
|
||||
_saveMsgText.getState(lnk, inText, pos.x() - _saveMsg.x() - st::medviewSaveMsgPadding.left(), pos.y() - _saveMsg.y() - st::medviewSaveMsgPadding.top(), _saveMsg.width() - st::medviewSaveMsgPadding.left() - st::medviewSaveMsgPadding.right());
|
||||
}
|
||||
if (lnk != textlnkOver()) {
|
||||
textlnkOver(lnk);
|
||||
setCursor((textlnkOver() || textlnkDown()) ? style::cur_pointer : style::cur_default);
|
||||
updateImage();
|
||||
}
|
||||
|
||||
if (_pressed || _dragging) return;
|
||||
|
||||
if (_leftNavVisible && _leftNav.contains(pos)) {
|
||||
@@ -831,6 +980,10 @@ void MediaView::updateOver(const QPoint &pos) {
|
||||
|
||||
void MediaView::mouseReleaseEvent(QMouseEvent *e) {
|
||||
updateOver(e->pos());
|
||||
if (textlnkDown() && textlnkOver() == textlnkDown()) {
|
||||
textlnkDown()->onClick(e->button());
|
||||
}
|
||||
textlnkDown(TextLinkPtr());
|
||||
if (_over == OverName && _down == OverName) {
|
||||
if (App::wnd()) {
|
||||
onClose();
|
||||
@@ -976,6 +1129,7 @@ void MediaView::hide() {
|
||||
_save.clearState();
|
||||
_forward.clearState();
|
||||
_delete.clearState();
|
||||
_overview.clearState();
|
||||
}
|
||||
|
||||
void MediaView::onMenuDestroy(QObject *obj) {
|
||||
@@ -1004,9 +1158,7 @@ void MediaView::onTouchTimer() {
|
||||
}
|
||||
|
||||
void MediaView::updateImage() {
|
||||
if (_current.isNull()) return;
|
||||
|
||||
update(_x, _y, _w, _h);
|
||||
update(_saveMsg);
|
||||
}
|
||||
|
||||
void MediaView::loadPhotosBack() {
|
||||
@@ -1058,7 +1210,9 @@ void MediaView::userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mt
|
||||
|
||||
void MediaView::updateHeader() {
|
||||
if (!_photo) {
|
||||
_header = lang(lng_mediaview_doc_image);
|
||||
_header = _doc ? _doc->name : QString();
|
||||
if (_header.isEmpty()) _header = lang(lng_mediaview_doc_image);
|
||||
if (!_overview.isHidden()) _overview.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1071,26 +1225,77 @@ void MediaView::updateHeader() {
|
||||
}
|
||||
if (_index >= 0 && _index < count && count > 1) {
|
||||
_header = lang(lng_mediaview_n_of_count).replace(qsl("{n}"), QString::number(index + 1)).replace(qsl("{count}"), QString::number(count));
|
||||
_overview.setText(_header);
|
||||
if (_history) {
|
||||
if (_overview.isHidden()) _overview.show();
|
||||
} else {
|
||||
if (!_overview.isHidden()) _overview.hide();
|
||||
}
|
||||
} else if (_user) {
|
||||
_header = lang(lng_mediaview_profile_photo);
|
||||
if (!_overview.isHidden()) _overview.hide();
|
||||
} else if (_peer) {
|
||||
_header = lang(lng_mediaview_group_photo);
|
||||
if (!_overview.isHidden()) _overview.hide();
|
||||
} else {
|
||||
_header = lang(lng_mediaview_single_photo);
|
||||
if (!_overview.isHidden()) _overview.hide();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaView::updatePolaroid() {
|
||||
int32 pminw = qMin(st::medviewPolaroidMin.width(), int(_avail.width() - 2 * st::medviewNavBarWidth));
|
||||
|
||||
int32 pl = _x - st::medviewPolaroid.left(), plw = st::medviewPolaroid.left();
|
||||
if (pl < st::medviewNavBarWidth) pl = st::medviewNavBarWidth;
|
||||
int32 pr = _x + _w + st::medviewPolaroid.right(), prw = st::medviewPolaroid.right();
|
||||
if (pr > _avail.width() - st::medviewNavBarWidth) pr = _avail.width() - st::medviewNavBarWidth;
|
||||
|
||||
if (_w + st::medviewPolaroid.left() + st::medviewPolaroid.right() < pminw) {
|
||||
pl = (_avail.width() - pminw) / 2;
|
||||
plw = _x - pl;
|
||||
pr = pl + pminw;
|
||||
prw = pr - (_x + _w);
|
||||
}
|
||||
|
||||
int32 pminh = qMin(st::medviewPolaroidMin.height(), int(_avail.height() - st::medviewBottomBar));
|
||||
|
||||
int32 pt = _y - st::medviewPolaroid.top(), pth = st::medviewPolaroid.top();
|
||||
if (pt < 0) pt = 0;
|
||||
int32 pb = _y + _h + st::medviewPolaroid.bottom(), pbh = st::medviewPolaroid.bottom();
|
||||
if (pb > _avail.height() - st::medviewBottomBar) pb = _avail.height() - st::medviewBottomBar;
|
||||
|
||||
if (_h + st::medviewPolaroid.top() + st::medviewPolaroid.bottom() < pminh) {
|
||||
pt = (_avail.height() - st::medviewBottomBar - pminh) / 2;
|
||||
pth = _y - pt;
|
||||
pb = pt + pminh;
|
||||
pbh = pb - (_y + _h);
|
||||
}
|
||||
|
||||
_polaroidOut = QRect(pl, pt, pr - pl, pb - pt);
|
||||
_polaroidIn = QRect(pl + plw, pt + pth, pr - pl - prw - plw, pb - pt - pbh - pth);
|
||||
|
||||
int32 nameWidth = _fromName.maxWidth(), maxWidth = _polaroidOut.width() - st::medviewPolaroid.left() - st::medviewPolaroid.right(), dateWidth = st::medviewDateFont->m.width(_dateText);
|
||||
if (nameWidth > maxWidth) {
|
||||
nameWidth = maxWidth;
|
||||
}
|
||||
_nameNav = QRect(_polaroidIn.x() + ((_polaroidIn.width() - nameWidth) / 2), _polaroidOut.y() + _polaroidOut.height() - st::medviewPolaroid.bottom() + st::medviewNameTop, nameWidth, st::medviewNameFont->height);
|
||||
_dateNav = QRect(_polaroidIn.x() + ((_polaroidIn.width() - dateWidth) / 2), _polaroidOut.y() + _polaroidOut.height() - st::medviewPolaroid.bottom() + st::medviewDateTop, dateWidth, st::medviewDateFont->height);
|
||||
}
|
||||
|
||||
QColor MediaView::overColor(const QColor &a, float64 ca, const QColor &b, float64 cb) {
|
||||
QColor res;
|
||||
float64 o = a.alphaF() * ca + b.alphaF() * cb - a.alphaF() * ca * b.alphaF() * cb;
|
||||
float64 ka = (o > 0.001) ? (a.alphaF() * ca * (1 - (b.alphaF() * cb)) / o) : 0;
|
||||
float64 kb = (o > 0.001) ? (b.alphaF() * cb / o) : 0;
|
||||
res.setRedF(a.redF() * ka + b.redF() * kb);
|
||||
res.setGreenF(a.greenF() * ka + b.greenF() * kb);
|
||||
res.setBlueF(a.blueF() * ka + b.blueF() * kb);
|
||||
res.setAlphaF(o);
|
||||
return res;
|
||||
}
|
||||
|
||||
float64 MediaView::overLevel(OverState control) {
|
||||
ShowingOpacities::const_iterator i = _animOpacities.constFind(control);
|
||||
return (i == _animOpacities.cend()) ? (_over == control ? 1 : 0) : i->current();
|
||||
}
|
||||
|
||||
QColor MediaView::nameDateColor(float64 over) {
|
||||
float64 mover = 1 - over;
|
||||
QColor result;
|
||||
result.setRedF(over * st::medviewNameOverColor->c.redF() + mover * st::medviewNameColor->c.redF());
|
||||
result.setGreenF(over * st::medviewNameOverColor->c.greenF() + mover * st::medviewNameColor->c.greenF());
|
||||
result.setBlueF(over * st::medviewNameOverColor->c.blueF() + mover * st::medviewNameColor->c.blueF());
|
||||
result.setAlphaF(over * st::medviewNameOverColor->c.alphaF() + mover * st::medviewNameColor->c.alphaF());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -52,15 +52,19 @@ public:
|
||||
|
||||
bool animStep(float64 dt);
|
||||
|
||||
void showSaveMsgFile();
|
||||
|
||||
~MediaView();
|
||||
|
||||
public slots:
|
||||
|
||||
void onClose();
|
||||
void onSave();
|
||||
void onDownload();
|
||||
void onShowInFolder();
|
||||
void onForward();
|
||||
void onDelete();
|
||||
void onOverview();
|
||||
void onCopy();
|
||||
void onMenuDestroy(QObject *obj);
|
||||
void receiveMouse();
|
||||
@@ -79,12 +83,14 @@ private:
|
||||
void userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mtpRequestId req);
|
||||
|
||||
void updateHeader();
|
||||
void updatePolaroid();
|
||||
void snapXY();
|
||||
|
||||
QTimer _timer;
|
||||
PhotoData *_photo;
|
||||
DocumentData *_doc;
|
||||
QRect _avail, _leftNav, _rightNav, _nameNav, _dateNav, _topActions, _bottomActions;
|
||||
QRect _avail, _leftNav, _rightNav, _bottomBar, _nameNav, _dateNav, _polaroidOut, _polaroidIn;
|
||||
int32 _availBottom;
|
||||
bool _leftNavVisible, _rightNavVisible;
|
||||
QString _dateText;
|
||||
|
||||
@@ -101,6 +107,7 @@ private:
|
||||
History *_history; // if conversation photos overview
|
||||
PeerData *_peer;
|
||||
UserData *_user, *_from; // if user profile photos overview
|
||||
Text _fromName;
|
||||
int32 _index; // index in photos array, -1 if just photo
|
||||
MsgId _msgid; // msgId of current photo
|
||||
|
||||
@@ -118,7 +125,7 @@ private:
|
||||
OverState _over, _down;
|
||||
QPoint _lastAction;
|
||||
|
||||
FlatButton _close, _save, _forward, _delete;
|
||||
IconedButton _close, _save, _forward, _delete, _overview;
|
||||
ContextMenu *_menu;
|
||||
bool _receiveMouse;
|
||||
|
||||
@@ -126,6 +133,13 @@ private:
|
||||
QTimer _touchTimer;
|
||||
QPoint _touchStart;
|
||||
|
||||
QString _saveMsgFilename;
|
||||
uint64 _saveMsgStarted;
|
||||
anim::fvalue _saveMsgOpacity;
|
||||
QRect _saveMsg;
|
||||
QTimer _saveMsgUpdater;
|
||||
Text _saveMsgText;
|
||||
|
||||
typedef QMap<OverState, uint64> Showing;
|
||||
Showing _animations;
|
||||
typedef QMap<OverState, anim::fvalue> ShowingOpacities;
|
||||
@@ -133,5 +147,6 @@ private:
|
||||
|
||||
bool updateOverState(OverState newState);
|
||||
float64 overLevel(OverState control);
|
||||
QColor nameDateColor(float64 over);
|
||||
QColor overColor(const QColor &a, float64 ca, const QColor &b, float64 cb);
|
||||
|
||||
};
|
||||
|
||||
@@ -177,11 +177,11 @@ with open('scheme.tl') as f:
|
||||
funcsText += '\tMTP' + name + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n\t}\n';
|
||||
funcsText += '\n';
|
||||
|
||||
funcsText += '\tuint32 size() const {\n'; # count size
|
||||
funcsText += '\tuint32 innerLength() const {\n'; # count size
|
||||
size = [];
|
||||
for k in prmsList:
|
||||
v = prms[k];
|
||||
size.append('v' + k + '.size()');
|
||||
size.append('v' + k + '.innerLength()');
|
||||
if (not len(size)):
|
||||
size.append('0');
|
||||
funcsText += '\t\treturn ' + ' + '.join(size) + ';\n';
|
||||
@@ -255,20 +255,25 @@ def addTextSerialize(dct):
|
||||
|
||||
if len(result):
|
||||
result += '\n';
|
||||
result += '\t\tcase mtpc_' + name + ':\n';
|
||||
result += '\t\t\tcase mtpc_' + name + ':\n';
|
||||
if (len(prms)):
|
||||
result += '\t\t\tresult += "\\n" + add;\n';
|
||||
result += '\t\t\t\tif (stage) {\n';
|
||||
result += '\t\t\t\t\tto.add(",\\n").addSpaces(lev);\n';
|
||||
result += '\t\t\t\t} else {\n';
|
||||
result += '\t\t\t\t\tto.add("{ ' + name + '");\n';
|
||||
result += '\t\t\t\t\tto.add("\\n").addSpaces(lev);\n';
|
||||
result += '\t\t\t\t}\n';
|
||||
result += '\t\t\t\tswitch (stage) {\n';
|
||||
stage = 0;
|
||||
for k in prmsList:
|
||||
v = prms[k];
|
||||
result += '\t\t\tresult += " ' + k + ': " + mtpTextSerialize(from, end';
|
||||
result += '\t\t\t\tcase ' + str(stage) + ': to.add(" ' + k + ': "); ++stages.back(); types.push_back(';
|
||||
vtypeget = re.match(r'^[Vv]ector<MTP([A-Za-z0-9\._]+)>', v);
|
||||
if (vtypeget):
|
||||
if (not re.match(r'^[A-Z]', v)):
|
||||
result += ', mtpc_vector';
|
||||
result += 'mtpc_vector';
|
||||
else:
|
||||
result += ', 0';
|
||||
result += ', level + 1';
|
||||
|
||||
result += '0';
|
||||
restype = vtypeget.group(1);
|
||||
try:
|
||||
if boxed[restype]:
|
||||
@@ -290,18 +295,26 @@ def addTextSerialize(dct):
|
||||
if (len(conses) > 1):
|
||||
print('Complex bare type found: "' + restype + '" trying to serialize "' + k + '" of type "' + v + '"');
|
||||
continue;
|
||||
result += ', mtpc_' + conses[0][0];
|
||||
if (vtypeget):
|
||||
result += '); vtypes.push_back(';
|
||||
result += 'mtpc_' + conses[0][0];
|
||||
if (not vtypeget):
|
||||
result += '); vtypes.push_back(0';
|
||||
except KeyError:
|
||||
result += ', mtpc_' + restype;
|
||||
if (not vtypeget):
|
||||
result += ', level + 1';
|
||||
if (vtypeget):
|
||||
result += '); vtypes.push_back(';
|
||||
result += 'mtpc_' + restype;
|
||||
if (not vtypeget):
|
||||
result += '); vtypes.push_back(0';
|
||||
else:
|
||||
if (not vtypeget):
|
||||
result += ', 0, level + 1';
|
||||
result += ') + ",\\n" + add;\n';
|
||||
result += '0); vtypes.push_back(0';
|
||||
result += '); stages.push_back(0); break;\n';
|
||||
stage = stage + 1;
|
||||
result += '\t\t\t\tdefault: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); break;\n';
|
||||
result += '\t\t\t\t}\n';
|
||||
else:
|
||||
result += '\t\t\tresult = " ";\n';
|
||||
result += '\t\treturn "{ ' + name + '" + result + "}";\n';
|
||||
result += '\t\t\t\tto.add("{ ' + name + ' }"); types.pop_back(); vtypes.pop_back(); stages.pop_back();\n';
|
||||
result += '\t\t\tbreak;\n';
|
||||
return result;
|
||||
|
||||
textSerialize += addTextSerialize(typesDict) + '\n';
|
||||
@@ -389,7 +402,7 @@ for restype in typesList:
|
||||
writeText += '\t\t';
|
||||
readText += '\tv.v' + paramName + '.read(from, end);\n';
|
||||
writeText += '\tv.v' + paramName + '.write(to);\n';
|
||||
sizeList.append('v.v' + paramName + '.size()');
|
||||
sizeList.append('v.v' + paramName + '.innerLength()');
|
||||
|
||||
forwards += 'class MTPD' + name + ';\n'; # data class forward declaration
|
||||
|
||||
@@ -492,8 +505,8 @@ for restype in typesList:
|
||||
if (withData):
|
||||
typesText += getters;
|
||||
|
||||
typesText += '\n\tuint32 size() const;\n'; # size method
|
||||
inlineMethods += '\ninline uint32 MTP' + restype + '::size() const {\n';
|
||||
typesText += '\n\tuint32 innerLength() const;\n'; # size method
|
||||
inlineMethods += '\ninline uint32 MTP' + restype + '::innerLength() const {\n';
|
||||
if (withType and sizeCases):
|
||||
inlineMethods += '\tswitch (_type) {\n';
|
||||
inlineMethods += sizeCases;
|
||||
@@ -571,26 +584,41 @@ for restype in typesList:
|
||||
inlineMethods += creatorsText;
|
||||
typesText += 'typedef MTPBoxed<MTP' + restype + '> MTP' + resType + ';\n'; # boxed type definition
|
||||
|
||||
textSerializeFull = '\ninline QString mtpTextSerialize(const mtpPrime *&from, const mtpPrime *end, mtpPrime cons, uint32 level, mtpPrime vcons) {\n';
|
||||
textSerializeFull += '\tQString add = QString(" ").repeated(level * 2);\n\n';
|
||||
textSerializeFull = '\nvoid mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons, uint32 level, mtpPrime vcons) {\n';
|
||||
textSerializeFull += '\tQVector<mtpTypeId> types, vtypes;\n';
|
||||
textSerializeFull += '\tQVector<int32> stages;\n';
|
||||
textSerializeFull += '\ttypes.reserve(20); vtypes.reserve(20); stages.reserve(20);\n';
|
||||
textSerializeFull += '\ttypes.push_back(mtpTypeId(cons)); vtypes.push_back(mtpTypeId(vcons)); stages.push_back(0);\n\n';
|
||||
textSerializeFull += '\tconst mtpPrime *start = from;\n';
|
||||
textSerializeFull += '\tmtpTypeId type = cons, vtype = vcons;\n';
|
||||
textSerializeFull += '\tint32 stage = 0;\n';
|
||||
textSerializeFull += '\ttry {\n';
|
||||
textSerializeFull += '\t\tif (!cons) {\n';
|
||||
textSerializeFull += '\t\t\tif (from >= end) {\n';
|
||||
textSerializeFull += '\t\t\t\tthrow Exception("from >= 2");\n';
|
||||
textSerializeFull += '\t\twhile (!types.isEmpty()) {\n';
|
||||
textSerializeFull += '\t\t\ttype = types.back();\n';
|
||||
textSerializeFull += '\t\t\tvtype = vtypes.back();\n';
|
||||
textSerializeFull += '\t\t\tstage = stages.back();\n';
|
||||
textSerializeFull += '\t\t\tif (!type) {\n';
|
||||
textSerializeFull += '\t\t\t\tif (from >= end) {\n';
|
||||
textSerializeFull += '\t\t\t\t\tthrow Exception("from >= end");\n';
|
||||
textSerializeFull += '\t\t\t\t} else if (stage) {\n';
|
||||
textSerializeFull += '\t\t\t\t\tthrow Exception("unknown type on stage > 0");\n';
|
||||
textSerializeFull += '\t\t\t\t}\n';
|
||||
textSerializeFull += '\t\t\t\ttypes.back() = type = *from;\n';
|
||||
textSerializeFull += '\t\t\t\tstart = ++from;\n';
|
||||
textSerializeFull += '\t\t\t}\n\n';
|
||||
textSerializeFull += '\t\t\tint32 lev = level + types.size() - 1;\n';
|
||||
textSerializeFull += '\t\t\tswitch (type) {\n' + textSerialize + '\n';
|
||||
textSerializeFull += '\t\t\tdefault:\n';
|
||||
textSerializeFull += '\t\t\t\tmtpTextSerializeCore(to, from, end, type, lev, vtype);\n';
|
||||
textSerializeFull += '\t\t\t\ttypes.pop_back(); vtypes.pop_back(); stages.pop_back();\n';
|
||||
textSerializeFull += '\t\t\tbreak;\n';
|
||||
textSerializeFull += '\t\t\t}\n';
|
||||
textSerializeFull += '\t\t\tcons = *from;\n';
|
||||
textSerializeFull += '\t\t\t++from;\n';
|
||||
textSerializeFull += '\t\t\t++start;\n';
|
||||
textSerializeFull += '\t\t}\n\n';
|
||||
textSerializeFull += '\t\tQString result;\n';
|
||||
textSerializeFull += '\t\tswitch (mtpTypeId(cons)) {\n' + textSerialize + '\t\t}\n\n';
|
||||
textSerializeFull += '\t\treturn mtpTextSerializeCore(from, end, cons, level, vcons);\n';
|
||||
textSerializeFull += '\t\t}\n';
|
||||
textSerializeFull += '\t} catch (Exception &e) {\n';
|
||||
textSerializeFull += '\t\tQString result = "(" + QString(e.what()) + QString("), cons: %1").arg(cons);\n';
|
||||
textSerializeFull += '\t\tif (vcons) result += QString(", vcons: %1").arg(vcons);\n';
|
||||
textSerializeFull += '\t\tresult += ", " + mb(start, (end - start) * sizeof(mtpPrime)).str();\n';
|
||||
textSerializeFull += '\t\treturn "[ERROR] " + result;\n';
|
||||
textSerializeFull += '\t\tto.add("[ERROR] ");\n';
|
||||
textSerializeFull += '\t\tto.add("(").add(e.what()).add("), cons: 0x").add(mtpWrapNumber(type, 16));\n';
|
||||
textSerializeFull += '\t\tif (vtype) to.add(", vcons: 0x").add(mtpWrapNumber(vtype));\n';
|
||||
textSerializeFull += '\t\tto.add(", ").add(mb(start, (end - start) * sizeof(mtpPrime)).str());\n';
|
||||
textSerializeFull += '\t}\n';
|
||||
textSerializeFull += '}\n';
|
||||
|
||||
@@ -601,6 +629,32 @@ 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' + textSerializeFull + '\n#endif\n');
|
||||
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');
|
||||
|
||||
outCpp = open('mtpScheme.cpp', 'w');
|
||||
outCpp.write('/*\n');
|
||||
outCpp.write('Created from \'/SourceFiles/mtproto/scheme.tl\' by \'/SourceFiles/mtproto/generate.py\' script\n\n');
|
||||
outCpp.write('WARNING! All changes made in this file will be lost!\n\n');
|
||||
outCpp.write('This file is part of Telegram Desktop,\n');
|
||||
outCpp.write('an unofficial desktop messaging app, see https://telegram.org\n');
|
||||
outCpp.write('\n');
|
||||
outCpp.write('Telegram Desktop is free software: you can redistribute it and/or modify\n');
|
||||
outCpp.write('it under the terms of the GNU General Public License as published by\n');
|
||||
outCpp.write('the Free Software Foundation, either version 3 of the License, or\n');
|
||||
outCpp.write('(at your option) any later version.\n');
|
||||
outCpp.write('\n');
|
||||
outCpp.write('It is distributed in the hope that it will be useful,\n');
|
||||
outCpp.write('but WITHOUT ANY WARRANTY; without even the implied warranty of\n');
|
||||
outCpp.write('MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n');
|
||||
outCpp.write('GNU General Public License for more details.\n');
|
||||
outCpp.write('\n');
|
||||
outCpp.write('Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n');
|
||||
outCpp.write('Copyright (c) 2014 John Preston, https://tdesktop.com\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(textSerializeFull + '\n#endif\n');
|
||||
|
||||
print('Done, written {0} constructors, {1} functions.'.format(consts, funcs));
|
||||
|
||||
print('Done, written {0} constructors, {1} functions.'.format(consts, funcs));
|
||||
|
||||
@@ -36,9 +36,11 @@ namespace {
|
||||
|
||||
typedef QMap<mtpRequestId, RPCResponseHandler> ParserMap;
|
||||
ParserMap parserMap;
|
||||
QMutex parserMapLock;
|
||||
|
||||
typedef QMap<mtpRequestId, mtpRequest> RequestMap;
|
||||
RequestMap requestMap;
|
||||
QReadWriteLock requestMapLock;
|
||||
|
||||
typedef QPair<mtpRequestId, uint64> DelayedRequest;
|
||||
typedef QList<DelayedRequest> DelayedRequestsList;
|
||||
@@ -62,7 +64,8 @@ namespace {
|
||||
mtpAuthKey _localKey;
|
||||
|
||||
void importDone(const MTPauth_Authorization &result, mtpRequestId req) {
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
QMutexLocker locker1(&requestByDCLock);
|
||||
|
||||
RequestsByDC::iterator i = requestsByDC.find(req);
|
||||
if (i == requestsByDC.end()) {
|
||||
LOG(("MTP Error: auth import request not found in requestsByDC, requestId: %1").arg(req));
|
||||
@@ -77,6 +80,7 @@ namespace {
|
||||
DCAuthWaiters &waiters(authWaiters[newdc]);
|
||||
MTProtoSessionPtr session(_mtp_internal::getSession(newdc));
|
||||
if (waiters.size()) {
|
||||
QReadLocker locker(&requestMapLock);
|
||||
for (DCAuthWaiters::iterator i = waiters.begin(), e = waiters.end(); i != e; ++i) {
|
||||
mtpRequestId requestId = *i;
|
||||
RequestMap::const_iterator j = requestMap.constFind(requestId);
|
||||
@@ -166,19 +170,24 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
RequestMap::const_iterator i = requestMap.constFind(requestId);
|
||||
if (i == requestMap.cend()) {
|
||||
LOG(("MTP Error: could not find request %1").arg(requestId));
|
||||
return false;
|
||||
mtpRequest req;
|
||||
{
|
||||
QReadLocker locker(&requestMapLock);
|
||||
RequestMap::const_iterator i = requestMap.constFind(requestId);
|
||||
if (i == requestMap.cend()) {
|
||||
LOG(("MTP Error: could not find request %1").arg(requestId));
|
||||
return false;
|
||||
}
|
||||
req = i.value();
|
||||
}
|
||||
_mtp_internal::registerRequest(requestId, (dc < 0) ? -newdc : newdc);
|
||||
_mtp_internal::getSession(newdc)->sendPrepared(i.value());
|
||||
_mtp_internal::getSession(newdc)->sendPrepared(req);
|
||||
return true;
|
||||
} else if ((m = QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err)).hasMatch()) {
|
||||
if (!requestId) return false;
|
||||
|
||||
int32 secs = m.captured(1).toInt();
|
||||
uint64 sendAt = getms() + secs * 1000 + 10;
|
||||
uint64 sendAt = getms(true) + secs * 1000 + 10;
|
||||
DelayedRequestsList::iterator i = delayedRequests.begin(), e = delayedRequests.end();
|
||||
for (; i != e; ++i) {
|
||||
if (i->first == requestId) return true;
|
||||
@@ -215,10 +224,15 @@ namespace {
|
||||
if (badGuestDC) badGuestDCRequests.insert(requestId);
|
||||
return true;
|
||||
} else if (err == qsl("CONNECTION_NOT_INITED") || err == qsl("CONNECTION_LAYER_INVALID")) {
|
||||
RequestMap::const_iterator i = requestMap.constFind(requestId);
|
||||
if (i == requestMap.cend()) {
|
||||
LOG(("MTP Error: could not find request %1").arg(requestId));
|
||||
return false;
|
||||
mtpRequest req;
|
||||
{
|
||||
QReadLocker locker(&requestMapLock);
|
||||
RequestMap::const_iterator i = requestMap.constFind(requestId);
|
||||
if (i == requestMap.cend()) {
|
||||
LOG(("MTP Error: could not find request %1").arg(requestId));
|
||||
return false;
|
||||
}
|
||||
req = i.value();
|
||||
}
|
||||
int32 dc = 0;
|
||||
{
|
||||
@@ -232,7 +246,66 @@ namespace {
|
||||
}
|
||||
if (!dc) return false;
|
||||
|
||||
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPreparedWithInit(i.value());
|
||||
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPreparedWithInit(req);
|
||||
return true;
|
||||
} else if (err == qsl("MSG_WAIT_FAILED")) {
|
||||
mtpRequest req;
|
||||
{
|
||||
QReadLocker locker(&requestMapLock);
|
||||
RequestMap::const_iterator i = requestMap.constFind(requestId);
|
||||
if (i == requestMap.cend()) {
|
||||
LOG(("MTP Error: could not find request %1").arg(requestId));
|
||||
return false;
|
||||
}
|
||||
req = i.value();
|
||||
}
|
||||
if (!req->after) {
|
||||
LOG(("MTP Error: wait failed for not dependent request %1").arg(requestId));
|
||||
return false;
|
||||
}
|
||||
int32 dc = 0;
|
||||
{
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
RequestsByDC::iterator i = requestsByDC.find(requestId), j = requestsByDC.find(req->after->requestId);
|
||||
if (i == requestsByDC.end()) {
|
||||
LOG(("MTP Error: could not find request %1 by dc").arg(requestId));
|
||||
} else if (j == requestsByDC.end()) {
|
||||
LOG(("MTP Error: could not find dependent request %1 by dc").arg(req->after->requestId));
|
||||
} else {
|
||||
dc = i.value();
|
||||
if (i.value() != j.value()) {
|
||||
req->after = mtpRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dc) return false;
|
||||
|
||||
if (!req->after) {
|
||||
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPreparedWithInit(req);
|
||||
} else {
|
||||
int32 newdc = abs(dc) % _mtp_internal::dcShift;
|
||||
DCAuthWaiters &waiters(authWaiters[newdc]);
|
||||
if (waiters.indexOf(req->after->requestId) >= 0) {
|
||||
if (waiters.indexOf(requestId) < 0) {
|
||||
waiters.push_back(requestId);
|
||||
}
|
||||
if (badGuestDCRequests.constFind(req->after->requestId) != badGuestDCRequests.cend()) {
|
||||
if (badGuestDCRequests.constFind(requestId) == badGuestDCRequests.cend()) {
|
||||
badGuestDCRequests.insert(requestId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint64 at = 0;
|
||||
DelayedRequestsList::iterator i = delayedRequests.begin(), e = delayedRequests.end();
|
||||
for (; i != e; ++i) {
|
||||
if (i->first == requestId) return true;
|
||||
if (i->first == req->after->requestId) break;
|
||||
}
|
||||
if (i != e) {
|
||||
delayedRequests.insert(i, DelayedRequest(requestId, i->second));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (badGuestDC) badGuestDCRequests.remove(requestId);
|
||||
@@ -268,45 +341,79 @@ namespace _mtp_internal {
|
||||
}
|
||||
|
||||
void unregisterRequest(mtpRequestId requestId) {
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
RequestsByDC::iterator i = requestsByDC.find(requestId);
|
||||
if (i != requestsByDC.end()) {
|
||||
requestsByDC.erase(i);
|
||||
{
|
||||
QWriteLocker locker(&requestMapLock);
|
||||
requestMap.remove(requestId);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 getLayer() {
|
||||
return layer;
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
requestsByDC.remove(requestId);
|
||||
}
|
||||
|
||||
mtpRequestId storeRequest(mtpRequest &request, const RPCResponseHandler &parser) {
|
||||
mtpRequestId res = reqid();
|
||||
request->requestId = res;
|
||||
if (parser.onDone || parser.onFail) {
|
||||
QMutexLocker locker(&parserMapLock);
|
||||
parserMap.insert(res, parser);
|
||||
}
|
||||
{
|
||||
QWriteLocker locker(&requestMapLock);
|
||||
requestMap.insert(res, request);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void replaceRequest(mtpRequest &newRequest, const mtpRequest &oldRequest) {
|
||||
newRequest->requestId = oldRequest->requestId;
|
||||
RequestMap::iterator i = requestMap.find(oldRequest->requestId);
|
||||
if (i != requestMap.cend()) {
|
||||
i.value() = newRequest;
|
||||
mtpRequest getRequest(mtpRequestId reqId) {
|
||||
static mtpRequest zero;
|
||||
mtpRequest req;
|
||||
{
|
||||
QReadLocker locker(&requestMapLock);
|
||||
RequestMap::const_iterator i = requestMap.constFind(reqId);
|
||||
req = (i == requestMap.cend()) ? zero : i.value();
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
void wrapInvokeAfter(mtpRequest &to, const mtpRequest &from, const mtpRequestMap &haveSent, int32 skipBeforeRequest) {
|
||||
mtpMsgId afterId(*(mtpMsgId*)(from->after->data() + 4));
|
||||
mtpRequestMap::const_iterator i = afterId ? haveSent.constFind(afterId) : haveSent.cend();
|
||||
int32 size = to->size(), lenInInts = (from.innerLength() >> 2), headlen = 4, fulllen = headlen + lenInInts;
|
||||
if (i == haveSent.constEnd()) { // no invoke after or such msg was not sent or was completed recently
|
||||
to->resize(size + fulllen + skipBeforeRequest);
|
||||
if (skipBeforeRequest) {
|
||||
memcpy(to->data() + size, from->constData() + 4, headlen * sizeof(mtpPrime));
|
||||
memcpy(to->data() + size + headlen + skipBeforeRequest, from->constData() + 4 + headlen, lenInInts * sizeof(mtpPrime));
|
||||
} else {
|
||||
memcpy(to->data() + size, from->constData() + 4, fulllen * sizeof(mtpPrime));
|
||||
}
|
||||
} else {
|
||||
to->resize(size + fulllen + skipBeforeRequest + 3);
|
||||
memcpy(to->data() + size, from->constData() + 4, headlen * sizeof(mtpPrime));
|
||||
(*to)[size + 3] += 3 * sizeof(mtpPrime);
|
||||
*((mtpTypeId*)&((*to)[size + headlen + skipBeforeRequest])) = mtpc_invokeAfterMsg;
|
||||
memcpy(to->data() + size + headlen + skipBeforeRequest + 1, &afterId, 2 * sizeof(mtpPrime));
|
||||
memcpy(to->data() + size + headlen + skipBeforeRequest + 3, from->constData() + 4 + headlen, lenInInts * sizeof(mtpPrime));
|
||||
if (size + 3 != 7) (*to)[7] += 3 * sizeof(mtpPrime);
|
||||
}
|
||||
}
|
||||
|
||||
void clearCallbacks(mtpRequestId requestId, int32 errorCode) {
|
||||
ParserMap::iterator i = parserMap.find(requestId);
|
||||
if (i != parserMap.end()) {
|
||||
if (errorCode) {
|
||||
rpcErrorOccured(requestId, i.value(), rpcClientError("CLEAR_CALLBACK", QString("did not handle request %1, error code %2").arg(requestId).arg(errorCode)));
|
||||
RPCResponseHandler h;
|
||||
bool found = false;
|
||||
{
|
||||
QMutexLocker locker(&parserMapLock);
|
||||
ParserMap::iterator i = parserMap.find(requestId);
|
||||
if (i != parserMap.end()) {
|
||||
h = i.value();
|
||||
found = true;
|
||||
|
||||
parserMap.erase(i);
|
||||
}
|
||||
parserMap.erase(i);
|
||||
}
|
||||
requestMap.remove(requestId);
|
||||
_mtp_internal::unregisterRequest(requestId);
|
||||
if (errorCode && found) {
|
||||
rpcErrorOccured(requestId, h, rpcClientError("CLEAR_CALLBACK", QString("did not handle request %1, error code %2").arg(requestId).arg(errorCode)));
|
||||
}
|
||||
}
|
||||
|
||||
void clearCallbacksDelayed(const RPCCallbackClears &requestIds) {
|
||||
@@ -336,23 +443,31 @@ namespace _mtp_internal {
|
||||
if (!toClear.isEmpty()) {
|
||||
for (RPCCallbackClears::iterator i = toClear.begin(), e = toClear.end(); i != e; ++i) {
|
||||
if (cDebug()) {
|
||||
QMutexLocker locker(&parserMapLock);
|
||||
if (parserMap.find(i->requestId) != parserMap.end()) {
|
||||
DEBUG_LOG(("RPC Info: clearing delayed callback %1, error code %2").arg(i->requestId).arg(i->errorCode));
|
||||
}
|
||||
}
|
||||
clearCallbacks(i->requestId, i->errorCode);
|
||||
_mtp_internal::unregisterRequest(i->requestId);
|
||||
}
|
||||
toClear.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) {
|
||||
ParserMap::iterator i = parserMap.find(requestId);
|
||||
if (i != parserMap.cend()) {
|
||||
RPCResponseHandler h(i.value());
|
||||
parserMap.erase(i);
|
||||
RPCResponseHandler h;
|
||||
{
|
||||
QMutexLocker locker(&parserMapLock);
|
||||
ParserMap::iterator i = parserMap.find(requestId);
|
||||
if (i != parserMap.cend()) {
|
||||
h = i.value();
|
||||
parserMap.erase(i);
|
||||
|
||||
DEBUG_LOG(("RPC Info: found parser for request %1, trying to parse response..").arg(requestId));
|
||||
DEBUG_LOG(("RPC Info: found parser for request %1, trying to parse response..").arg(requestId));
|
||||
}
|
||||
}
|
||||
if (h.onDone || h.onFail) {
|
||||
try {
|
||||
if (from >= end) throw mtpErrorInsufficient();
|
||||
|
||||
@@ -360,6 +475,7 @@ namespace _mtp_internal {
|
||||
RPCError err(MTPRpcError(from, end));
|
||||
DEBUG_LOG(("RPC Info: error received, code %1, type %2, description: %3").arg(err.code()).arg(err.type()).arg(err.description()));
|
||||
if (!rpcErrorOccured(requestId, h, err)) {
|
||||
QMutexLocker locker(&parserMapLock);
|
||||
parserMap.insert(requestId, h);
|
||||
return;
|
||||
}
|
||||
@@ -368,17 +484,23 @@ namespace _mtp_internal {
|
||||
}
|
||||
} catch (Exception &e) {
|
||||
if (!rpcErrorOccured(requestId, h, rpcClientError("RESPONSE_PARSE_FAILED", QString("exception text: ") + e.what()))) {
|
||||
QMutexLocker locker(&parserMapLock);
|
||||
parserMap.insert(requestId, h);
|
||||
return;
|
||||
}
|
||||
}
|
||||
requestMap.remove(requestId);
|
||||
} else {
|
||||
DEBUG_LOG(("RPC Info: parser not found for %1").arg(requestId));
|
||||
}
|
||||
unregisterRequest(requestId);
|
||||
}
|
||||
|
||||
bool hasCallbacks(mtpRequestId requestId) {
|
||||
QMutexLocker locker(&parserMapLock);
|
||||
ParserMap::iterator i = parserMap.find(requestId);
|
||||
return (i != parserMap.cend());
|
||||
}
|
||||
|
||||
void globalCallback(const mtpPrime *from, const mtpPrime *end) {
|
||||
if (globalHandler.onDone) (*globalHandler.onDone)(0, from, end); // some updates were received
|
||||
}
|
||||
@@ -400,8 +522,12 @@ namespace _mtp_internal {
|
||||
return true;
|
||||
}
|
||||
|
||||
RequestResender::RequestResender() {
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(checkDelayed()));
|
||||
}
|
||||
|
||||
void RequestResender::checkDelayed() {
|
||||
uint64 now = getms();
|
||||
uint64 now = getms(true);
|
||||
while (!delayedRequests.isEmpty() && now >= delayedRequests.front().second) {
|
||||
mtpRequestId requestId = delayedRequests.front().first;
|
||||
delayedRequests.pop_front();
|
||||
@@ -418,16 +544,21 @@ namespace _mtp_internal {
|
||||
}
|
||||
}
|
||||
|
||||
RequestMap::const_iterator j = requestMap.constFind(requestId);
|
||||
if (j == requestMap.cend()) {
|
||||
DEBUG_LOG(("MTP Error: could not find request %1").arg(requestId));
|
||||
continue;
|
||||
mtpRequest req;
|
||||
{
|
||||
QReadLocker locker(&requestMapLock);
|
||||
RequestMap::const_iterator j = requestMap.constFind(requestId);
|
||||
if (j == requestMap.cend()) {
|
||||
DEBUG_LOG(("MTP Error: could not find request %1").arg(requestId));
|
||||
continue;
|
||||
}
|
||||
req = j.value();
|
||||
}
|
||||
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(j.value(), 0, false);
|
||||
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req, 0, false);
|
||||
}
|
||||
|
||||
if (!delayedRequests.isEmpty()) {
|
||||
QTimer::singleShot(delayedRequests.front().second - now, this, SLOT(checkDelayed()));
|
||||
_timer.start(delayedRequests.front().second - now);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -494,15 +625,6 @@ namespace MTP {
|
||||
}
|
||||
}
|
||||
|
||||
void setLayer(uint32 l) {
|
||||
if (l > mtpLayerMax) {
|
||||
l = mtpLayerMax;
|
||||
} else if (!l) {
|
||||
l = 1;
|
||||
}
|
||||
layer = l - 1;
|
||||
}
|
||||
|
||||
void setdc(int32 dc, bool fromZeroOnly) {
|
||||
if (!started) return;
|
||||
|
||||
@@ -552,11 +674,21 @@ namespace MTP {
|
||||
}
|
||||
|
||||
void cancel(mtpRequestId requestId) {
|
||||
mtpMsgId msgId = 0;
|
||||
{
|
||||
QWriteLocker locker(&requestMapLock);
|
||||
RequestMap::iterator i = requestMap.find(requestId);
|
||||
if (i != requestMap.end()) {
|
||||
msgId = *(mtpMsgId*)(i.value()->constData() + 4);
|
||||
requestMap.erase(i);
|
||||
}
|
||||
}
|
||||
{
|
||||
QMutexLocker locker(&requestByDCLock);
|
||||
RequestsByDC::iterator i = requestsByDC.find(requestId);
|
||||
if (i != requestsByDC.end()) {
|
||||
_mtp_internal::getSession(abs(i.value()))->cancel(requestId);
|
||||
_mtp_internal::getSession(abs(i.value()))->cancel(requestId, msgId);
|
||||
requestsByDC.erase(i);
|
||||
}
|
||||
}
|
||||
_mtp_internal::clearCallbacks(requestId);
|
||||
@@ -586,6 +718,8 @@ namespace MTP {
|
||||
for (Sessions::iterator i = sessions.begin(), e = sessions.end(); i != e; ++i) {
|
||||
i.value()->stop();
|
||||
}
|
||||
sessions.clear();
|
||||
mainSession = MTProtoSessionPtr();
|
||||
delete resender;
|
||||
resender = 0;
|
||||
mtpDestroyConfigLoader();
|
||||
|
||||
@@ -26,16 +26,16 @@ namespace _mtp_internal {
|
||||
void registerRequest(mtpRequestId requestId, int32 dc);
|
||||
void unregisterRequest(mtpRequestId requestId);
|
||||
|
||||
uint32 getLayer();
|
||||
|
||||
static const uint32 dcShift = 10000;
|
||||
|
||||
mtpRequestId storeRequest(mtpRequest &request, const RPCResponseHandler &parser);
|
||||
void replaceRequest(mtpRequest &newRequest, const mtpRequest &oldRequest);
|
||||
mtpRequest getRequest(mtpRequestId req);
|
||||
void wrapInvokeAfter(mtpRequest &to, const mtpRequest &from, const mtpRequestMap &haveSent, int32 skipBeforeRequest = 0);
|
||||
void clearCallbacks(mtpRequestId requestId, int32 errorCode = RPCError::NoError); // 0 - do not toggle onError callback
|
||||
void clearCallbacksDelayed(const RPCCallbackClears &requestIds);
|
||||
void performDelayedClear();
|
||||
void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end);
|
||||
bool hasCallbacks(mtpRequestId requestId);
|
||||
void globalCallback(const mtpPrime *from, const mtpPrime *end);
|
||||
void onStateChange(int32 dc, int32 state);
|
||||
void onSessionReset(int32 dc);
|
||||
@@ -47,9 +47,17 @@ namespace _mtp_internal {
|
||||
class RequestResender : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
RequestResender();
|
||||
|
||||
public slots:
|
||||
|
||||
void checkDelayed();
|
||||
|
||||
private:
|
||||
|
||||
SingleTimer _timer;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -58,28 +66,39 @@ namespace MTP {
|
||||
mtpAuthKey &localKey();
|
||||
void createLocalKey(const QByteArray &pass, QByteArray *salt = 0);
|
||||
|
||||
static const uint32 dld = 1 * _mtp_internal::dcShift; // send(req, callbacks, MTP::dld + dc) - for download
|
||||
static const uint32 upl = 2 * _mtp_internal::dcShift; // send(req, callbacks, MTP::upl + dc) - for upload
|
||||
static const uint32 cfg = 3 * _mtp_internal::dcShift; // send(MTPhelp_GetConfig(), MTP::cfg + dc) - for dc enum
|
||||
static const uint32 cfg = 1 * _mtp_internal::dcShift; // send(MTPhelp_GetConfig(), MTP::cfg + dc) - for dc enum
|
||||
static const uint32 dld[MTPDownloadSessionsCount] = { // send(req, callbacks, MTP::dld[i] + dc) - for download
|
||||
0x10 * _mtp_internal::dcShift,
|
||||
0x11 * _mtp_internal::dcShift,
|
||||
0x12 * _mtp_internal::dcShift,
|
||||
0x13 * _mtp_internal::dcShift,
|
||||
};
|
||||
static const uint32 upl[MTPUploadSessionsCount] = { // send(req, callbacks, MTP::upl[i] + dc) - for upload
|
||||
0x20 * _mtp_internal::dcShift,
|
||||
0x21 * _mtp_internal::dcShift,
|
||||
0x22 * _mtp_internal::dcShift,
|
||||
0x23 * _mtp_internal::dcShift,
|
||||
};
|
||||
|
||||
void start();
|
||||
void restart();
|
||||
void restart(int32 dcMask);
|
||||
|
||||
void setLayer(uint32 layer);
|
||||
|
||||
void setdc(int32 dc, bool fromZeroOnly = false);
|
||||
int32 maindc();
|
||||
int32 dcstate(int32 dc = 0);
|
||||
QString dctransport(int32 dc = 0);
|
||||
void initdc(int32 dc);
|
||||
template <typename TRequest>
|
||||
inline mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), int32 dc = 0, uint64 msCanWait = 0) {
|
||||
return _mtp_internal::getSession(dc)->send(request, callbacks, msCanWait, _mtp_internal::getLayer(), !dc);
|
||||
inline mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) {
|
||||
MTProtoSessionPtr session = _mtp_internal::getSession(dc);
|
||||
if (!session) return 0;
|
||||
|
||||
return session->send(request, callbacks, msCanWait, true, !dc, after);
|
||||
}
|
||||
template <typename TRequest>
|
||||
inline mtpRequestId send(const TRequest &request, RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail = RPCFailHandlerPtr(), int32 dc = 0, uint64 msCanWait = 0) {
|
||||
return send(request, RPCResponseHandler(onDone, onFail), dc, msCanWait);
|
||||
inline mtpRequestId send(const TRequest &request, RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail = RPCFailHandlerPtr(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) {
|
||||
return send(request, RPCResponseHandler(onDone, onFail), dc, msCanWait, after);
|
||||
}
|
||||
void cancel(mtpRequestId req);
|
||||
void killSession(int32 dc);
|
||||
|
||||
@@ -106,6 +106,13 @@ class MTPabstractConnection : public QObject {
|
||||
|
||||
public:
|
||||
|
||||
MTPabstractConnection() : _sentEncrypted(false) {
|
||||
}
|
||||
|
||||
void setSentEncrypted() {
|
||||
_sentEncrypted = true;
|
||||
}
|
||||
|
||||
virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32
|
||||
virtual void disconnectFromServer() = 0;
|
||||
virtual void connectToServer(const QString &addr, int32 port) = 0;
|
||||
@@ -135,6 +142,7 @@ signals:
|
||||
protected:
|
||||
|
||||
BuffersQueue receivedQueue; // list of received packets, not processed yet
|
||||
bool _sentEncrypted;
|
||||
|
||||
};
|
||||
|
||||
@@ -189,6 +197,8 @@ public slots:
|
||||
void onSocketDisconnected();
|
||||
void onHttpStart();
|
||||
|
||||
void onTcpTimeoutTimer();
|
||||
|
||||
protected:
|
||||
|
||||
void socketPacket(mtpPrime *packet, uint32 packetSize);
|
||||
@@ -215,6 +225,10 @@ private:
|
||||
typedef QSet<QNetworkReply*> Requests;
|
||||
Requests requests;
|
||||
|
||||
QString _addr;
|
||||
int32 _port, _tcpTimeout;
|
||||
QTimer tcpTimeoutTimer;
|
||||
|
||||
};
|
||||
|
||||
class MTPtcpConnection : public MTPabstractTcpConnection {
|
||||
@@ -282,6 +296,8 @@ public:
|
||||
MTProtoConnectionPrivate(QThread *thread, MTProtoConnection *owner, MTPSessionData *data, uint32 dc);
|
||||
~MTProtoConnectionPrivate();
|
||||
|
||||
void stop();
|
||||
|
||||
int32 getDC() const;
|
||||
|
||||
int32 getState() const;
|
||||
@@ -333,6 +349,7 @@ private:
|
||||
|
||||
void createConn();
|
||||
|
||||
mtpMsgId placeToContainer(mtpRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, mtpRequest &req);
|
||||
mtpMsgId prepareToSend(mtpRequest &request, mtpMsgId currentLastId);
|
||||
mtpMsgId replaceMsgId(mtpRequest &request, mtpMsgId newId);
|
||||
|
||||
@@ -356,25 +373,24 @@ private:
|
||||
MTProtoConnection *_owner;
|
||||
MTPabstractConnection *conn;
|
||||
|
||||
QTimer retryTimer; // exp retry timer
|
||||
SingleTimer retryTimer; // exp retry timer
|
||||
uint32 retryTimeout;
|
||||
quint64 retryWillFinish;
|
||||
|
||||
QTimer oldConnectionTimer;
|
||||
SingleTimer oldConnectionTimer;
|
||||
bool oldConnection;
|
||||
|
||||
QTimer connCheckTimer;
|
||||
SingleTimer connCheckTimer;
|
||||
uint32 receiveDelay;
|
||||
int64 firstSentAt;
|
||||
|
||||
MTPMsgsAck ackRequest;
|
||||
QVector<MTPlong> *ackRequestData;
|
||||
QVector<MTPlong> ackRequestData, resendRequestData;
|
||||
|
||||
// if badTime received - search for ids in sessionData->haveSent and sessionData->wereAcked and sync time/salt, return true if found
|
||||
bool requestsFixTimeSalt(const QVector<MTPlong> &ids, int32 serverTime, uint64 serverSalt);
|
||||
|
||||
// remove msgs with such ids from sessionData->haveSent, add to sessionData->wereAcked
|
||||
void requestsAcked(const QVector<MTPlong> &ids);
|
||||
void requestsAcked(const QVector<MTPlong> &ids, bool byResponse = false);
|
||||
|
||||
mtpPingId pingId, toSendPingId;
|
||||
mtpMsgId pingMsgId;
|
||||
@@ -390,6 +406,7 @@ private:
|
||||
bool restarted;
|
||||
|
||||
uint64 keyId;
|
||||
QReadWriteLock sessionDataMutex;
|
||||
MTPSessionData *sessionData;
|
||||
bool myKeyLock;
|
||||
void lockKey();
|
||||
@@ -417,10 +434,8 @@ private:
|
||||
uint32 retries;
|
||||
MTPlong retry_id;
|
||||
|
||||
string dh_prime;
|
||||
int32 g;
|
||||
string g_a;
|
||||
|
||||
|
||||
uchar aesKey[32], aesIV[32];
|
||||
uint32 auth_key[64];
|
||||
MTPlong auth_key_hash;
|
||||
@@ -428,7 +443,13 @@ private:
|
||||
uint32 req_num; // sent not encrypted request number
|
||||
uint32 msgs_sent;
|
||||
};
|
||||
struct AuthKeyCreateStrings {
|
||||
QByteArray dh_prime;
|
||||
QByteArray g_a;
|
||||
};
|
||||
AuthKeyCreateData *authKeyData;
|
||||
AuthKeyCreateStrings *authKeyStrings;
|
||||
|
||||
void dhClientParamsSend();
|
||||
void authKeyCreated();
|
||||
void clearAuthKeyData();
|
||||
|
||||
194
Telegram/SourceFiles/mtproto/mtpCoreTypes.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
an unofficial desktop messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "mtpCoreTypes.h"
|
||||
|
||||
#if defined _DEBUG || defined _WITH_DEBUG
|
||||
|
||||
QString mtpWrapNumber(float64 number) {
|
||||
return QString::number(number);
|
||||
}
|
||||
|
||||
void mtpTextSerializeCore(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons, uint32 level, mtpPrime vcons) {
|
||||
switch (mtpTypeId(cons)) {
|
||||
case mtpc_int: {
|
||||
MTPint value(from, end, cons);
|
||||
to.add(mtpWrapNumber(value.v)).add(" [INT]");
|
||||
} break;
|
||||
|
||||
case mtpc_long: {
|
||||
MTPlong value(from, end, cons);
|
||||
to.add(mtpWrapNumber(value.v)).add(" [LONG]");
|
||||
} break;
|
||||
|
||||
case mtpc_int128: {
|
||||
MTPint128 value(from, end, cons);
|
||||
to.add(mtpWrapNumber(value.h)).add(" * 2^64 + ").add(mtpWrapNumber(value.l)).add(" [INT128]");
|
||||
} break;
|
||||
|
||||
case mtpc_int256: {
|
||||
MTPint256 value(from, end, cons);
|
||||
to.add(mtpWrapNumber(value.h.h)).add(" * 2^192 + ").add(mtpWrapNumber(value.h.l)).add(" * 2^128 + ").add(mtpWrapNumber(value.l.h)).add(" * 2 ^ 64 + ").add(mtpWrapNumber(value.l.l)).add(" [INT256]");
|
||||
} break;
|
||||
|
||||
case mtpc_double: {
|
||||
MTPdouble value(from, end, cons);
|
||||
to.add(mtpWrapNumber(value.v)).add(" [DOUBLE]");
|
||||
} break;
|
||||
|
||||
case mtpc_string: {
|
||||
MTPstring value(from, end, cons);
|
||||
QByteArray strUtf8(value.c_string().v.c_str(), value.c_string().v.length());
|
||||
QString str = QString::fromUtf8(strUtf8);
|
||||
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]");
|
||||
} else {
|
||||
to.add(mb(strUtf8.constData(), 16).str()).add(".. [").add(mtpWrapNumber(strUtf8.size())).add(" BYTES]");
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_boolTrue:
|
||||
case mtpc_boolFalse: {
|
||||
MTPbool value(from, end, cons);
|
||||
to.add(value.v ? "[TRUE]" : "[FALSE]");
|
||||
} break;
|
||||
|
||||
case mtpc_vector: {
|
||||
if (from >= end) {
|
||||
throw Exception("from >= end in vector");
|
||||
}
|
||||
int32 cnt = *(from++);
|
||||
to.add("[ vector<0x").add(mtpWrapNumber(vcons, 16)).add(">");
|
||||
if (cnt) {
|
||||
to.add("\n").addSpaces(level);
|
||||
for (int32 i = 0; i < cnt; ++i) {
|
||||
to.add(" ");
|
||||
mtpTextSerializeType(to, from, end, vcons, level + 1);
|
||||
to.add(",\n").addSpaces(level);
|
||||
}
|
||||
} else {
|
||||
to.add(" ");
|
||||
}
|
||||
to.add("]");
|
||||
} break;
|
||||
|
||||
case mtpc_error: {
|
||||
to.add("{ error");
|
||||
to.add("\n").addSpaces(level);
|
||||
to.add(" code: "); mtpTextSerializeType(to, from, end, mtpc_int, level + 1); to.add(",\n").addSpaces(level);
|
||||
to.add(" text: "); mtpTextSerializeType(to, from, end, mtpc_string, level + 1); to.add(",\n").addSpaces(level);
|
||||
to.add("}");
|
||||
} break;
|
||||
|
||||
case mtpc_null: {
|
||||
to.add("{ null");
|
||||
to.add(" ");
|
||||
to.add("}");
|
||||
} break;
|
||||
|
||||
case mtpc_rpc_result: {
|
||||
to.add("{ rpc_result");
|
||||
to.add("\n").addSpaces(level);
|
||||
to.add(" req_msg_id: "); mtpTextSerializeType(to, from, end, mtpc_long, level + 1); to.add(",\n").addSpaces(level);
|
||||
to.add(" result: "); mtpTextSerializeType(to, from, end, 0, level + 1); to.add(",\n").addSpaces(level);
|
||||
to.add("}");
|
||||
} break;
|
||||
|
||||
case mtpc_msg_container: {
|
||||
to.add("{ msg_container");
|
||||
to.add("\n").addSpaces(level);
|
||||
to.add(" messages: "); mtpTextSerializeType(to, from, end, mtpc_vector, level + 1, mtpc_core_message); to.add(",\n").addSpaces(level);
|
||||
to.add("}");
|
||||
} break;
|
||||
|
||||
case mtpc_core_message: {
|
||||
to.add("{ core_message");
|
||||
to.add("\n").addSpaces(level);
|
||||
to.add(" msg_id: "); mtpTextSerializeType(to, from, end, mtpc_long, level + 1); to.add(",\n").addSpaces(level);
|
||||
to.add(" seq_no: "); mtpTextSerializeType(to, from, end, mtpc_int, level + 1); to.add(",\n").addSpaces(level);
|
||||
to.add(" bytes: "); mtpTextSerializeType(to, from, end, mtpc_int, level + 1); to.add(",\n").addSpaces(level);
|
||||
to.add(" body: "); mtpTextSerializeType(to, from, end, 0, level + 1); to.add(",\n").addSpaces(level);
|
||||
to.add("}");
|
||||
} break;
|
||||
|
||||
case mtpc_gzip_packed: {
|
||||
MTPstring packed(from, end); // read packed string as serialized mtp string type
|
||||
uint32 packedLen = packed.c_string().v.size(), unpackedChunk = packedLen;
|
||||
mtpBuffer result; // * 4 because of mtpPrime type
|
||||
result.resize(0);
|
||||
|
||||
z_stream stream;
|
||||
stream.zalloc = 0;
|
||||
stream.zfree = 0;
|
||||
stream.opaque = 0;
|
||||
stream.avail_in = 0;
|
||||
stream.next_in = 0;
|
||||
int res = inflateInit2(&stream, 16 + MAX_WBITS);
|
||||
if (res != Z_OK) {
|
||||
throw Exception(QString("ungzip init, code: %1").arg(res));
|
||||
}
|
||||
stream.avail_in = packedLen;
|
||||
stream.next_in = (Bytef*)&packed._string().v[0];
|
||||
stream.avail_out = 0;
|
||||
while (!stream.avail_out) {
|
||||
result.resize(result.size() + unpackedChunk);
|
||||
stream.avail_out = unpackedChunk * sizeof(mtpPrime);
|
||||
stream.next_out = (Bytef*)&result[result.size() - unpackedChunk];
|
||||
int res = inflate(&stream, Z_NO_FLUSH);
|
||||
if (res != Z_OK && res != Z_STREAM_END) {
|
||||
inflateEnd(&stream);
|
||||
throw Exception(QString("ungzip unpack, code: %1").arg(res));
|
||||
}
|
||||
}
|
||||
if (stream.avail_out & 0x03) {
|
||||
uint32 badSize = result.size() * sizeof(mtpPrime) - stream.avail_out;
|
||||
throw Exception(QString("ungzip bad length, size: %1").arg(badSize));
|
||||
}
|
||||
result.resize(result.size() - (stream.avail_out >> 2));
|
||||
inflateEnd(&stream);
|
||||
|
||||
if (!result.size()) {
|
||||
throw Exception("ungzip void data");
|
||||
}
|
||||
const mtpPrime *newFrom = result.constData(), *newEnd = result.constData() + result.size();
|
||||
to.add("[GZIPPED] "); mtpTextSerializeType(to, newFrom, newEnd, 0, level);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
for (uint32 i = 1; i < mtpLayerMaxSingle; ++i) {
|
||||
if (cons == mtpLayers[i]) {
|
||||
to.add("[LAYER").add(mtpWrapNumber(i + 1)).add("] "); mtpTextSerializeType(to, from, end, 0, level);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cons == mtpc_invokeWithLayer) {
|
||||
if (from >= end) {
|
||||
throw Exception("from >= end in invokeWithLayer");
|
||||
}
|
||||
int32 layer = *(from++);
|
||||
to.add("[LAYER").add(mtpWrapNumber(layer)).add("] "); mtpTextSerializeType(to, from, end, 0, level);
|
||||
return;
|
||||
}
|
||||
throw Exception(QString("unknown cons 0x%1").arg(cons, 0, 16));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
explicit mtpRequest(mtpRequestData *ptr) : QSharedPointer<mtpRequestData>(ptr) {
|
||||
}
|
||||
|
||||
uint32 size() const;
|
||||
uint32 innerLength() const;
|
||||
void write(mtpBuffer &to) const;
|
||||
|
||||
typedef void ResponseType; // don't know real response type =(
|
||||
@@ -74,14 +74,18 @@ public:
|
||||
// in toSend: = 0 - must send in container, > 0 - can send without container
|
||||
// in haveSent: = 0 - container with msgIds, > 0 - when was sent
|
||||
uint64 msDate;
|
||||
mtpRequestId requestId;
|
||||
|
||||
mtpRequestData(bool/* sure*/) : msDate(0) {
|
||||
mtpRequestId requestId;
|
||||
mtpRequest after;
|
||||
bool needsLayer;
|
||||
|
||||
mtpRequestData(bool/* sure*/) : msDate(0), requestId(0), needsLayer(false) {
|
||||
}
|
||||
|
||||
static mtpRequest prepare(uint32 requestSize) {
|
||||
static mtpRequest prepare(uint32 requestSize, uint32 maxSize = 0) {
|
||||
if (!maxSize) maxSize = requestSize;
|
||||
mtpRequest result(new mtpRequestData(true));
|
||||
result->reserve(8 + requestSize + _padding(requestSize)); // 2: salt, 2: session_id, 2: msg_id, 1: seq_no, 1: message_length
|
||||
result->reserve(8 + maxSize + _padding(maxSize)); // 2: salt, 2: session_id, 2: msg_id, 1: seq_no, 1: message_length
|
||||
result->resize(7);
|
||||
result->push_back(requestSize << 2);
|
||||
return result;
|
||||
@@ -90,7 +94,7 @@ public:
|
||||
static void padding(mtpRequest &request) {
|
||||
if (request->size() < 9) return;
|
||||
|
||||
uint32 requestSize = ((*request)[7] >> 2), padding = _padding(requestSize), fullSize = 8 + requestSize + padding; // 2: salt, 2: session_id, 2: msg_id, 1: seq_no, 1: message_length
|
||||
uint32 requestSize = (request.innerLength() >> 2), padding = _padding(requestSize), fullSize = 8 + requestSize + padding; // 2: salt, 2: session_id, 2: msg_id, 1: seq_no, 1: message_length
|
||||
if (uint32(request->size()) != fullSize) {
|
||||
request->resize(fullSize);
|
||||
if (padding) {
|
||||
@@ -101,7 +105,7 @@ public:
|
||||
|
||||
static uint32 messageSize(const mtpRequest &request) {
|
||||
if (request->size() < 9) return 0;
|
||||
return 4 + ((*request)[7] >> 2); // 2: msg_id, 1: seq_no, q: message_length
|
||||
return 4 + (request.innerLength() >> 2); // 2: msg_id, 1: seq_no, q: message_length
|
||||
}
|
||||
|
||||
static bool isSentContainer(const mtpRequest &request); // "request-like" wrap for msgIds vector
|
||||
@@ -117,7 +121,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
inline uint32 mtpRequest::size() const { // for template MTP requests and MTPBoxed instanciation
|
||||
inline uint32 mtpRequest::innerLength() const { // for template MTP requests and MTPBoxed instanciation
|
||||
mtpRequestData *value = data();
|
||||
if (!value || value->size() < 9) return 0;
|
||||
return value->at(7);
|
||||
@@ -126,7 +130,7 @@ inline uint32 mtpRequest::size() const { // for template MTP requests and MTPBox
|
||||
inline void mtpRequest::write(mtpBuffer &to) const {
|
||||
mtpRequestData *value = data();
|
||||
if (!value || value->size() < 9) return;
|
||||
uint32 was = to.size(), s = size() / sizeof(mtpPrime);
|
||||
uint32 was = to.size(), s = innerLength() / sizeof(mtpPrime);
|
||||
to.resize(was + s);
|
||||
memcpy(to.data() + was, value->constData() + 8, s * sizeof(mtpPrime));
|
||||
}
|
||||
@@ -150,8 +154,8 @@ public:
|
||||
|
||||
typedef QMap<mtpRequestId, mtpRequest> mtpPreRequestMap;
|
||||
typedef QMap<mtpMsgId, mtpRequest> mtpRequestMap;
|
||||
|
||||
class mtpMsgIdsSet : public QMap<mtpMsgId, bool> {
|
||||
typedef QMap<mtpMsgId, bool> mtpMsgIdsSet;
|
||||
class mtpMsgIdsMap : public QMap<mtpMsgId, bool> {
|
||||
public:
|
||||
typedef QMap<mtpMsgId, bool> ParentType;
|
||||
|
||||
@@ -172,12 +176,12 @@ public:
|
||||
}
|
||||
|
||||
mtpMsgId min() const {
|
||||
return size() ? cbegin().key() : 0;
|
||||
return isEmpty() ? 0 : cbegin().key();
|
||||
}
|
||||
|
||||
mtpMsgId max() const {
|
||||
ParentType::const_iterator e(cend());
|
||||
return size() ? (--e).key() : 0;
|
||||
return isEmpty() ? 0 : (--e).key();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -329,6 +333,11 @@ enum {
|
||||
mtpc_invokeWithLayer13 = 0x427c8ea2,
|
||||
mtpc_invokeWithLayer14 = 0x2b9b08fa,
|
||||
mtpc_invokeWithLayer15 = 0xb4418b64,
|
||||
mtpc_invokeWithLayer16 = 0xcf5f0987,
|
||||
mtpc_invokeWithLayer17 = 0x50858a19,
|
||||
mtpc_invokeWithLayer18 = 0x1c900537,
|
||||
|
||||
mtpc_invokeWithLayer = 0xda9b0d0d, // after 18 layer
|
||||
|
||||
// manually parsed
|
||||
mtpc_rpc_result = 0xf35c6d01,
|
||||
@@ -354,7 +363,11 @@ static const mtpTypeId mtpLayers[] = {
|
||||
mtpc_invokeWithLayer13,
|
||||
mtpc_invokeWithLayer14,
|
||||
mtpc_invokeWithLayer15,
|
||||
}, mtpLayerMax = sizeof(mtpLayers) / sizeof(mtpLayers[0]);
|
||||
mtpc_invokeWithLayer16,
|
||||
mtpc_invokeWithLayer17,
|
||||
mtpc_invokeWithLayer18,
|
||||
}, mtpLayerMaxSingle = sizeof(mtpLayers) / sizeof(mtpLayers[0]);
|
||||
static const mtpPrime mtpCurrentLayer = 19;
|
||||
|
||||
template <typename bareT>
|
||||
class MTPBoxed : public bareT {
|
||||
@@ -378,8 +391,8 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
return sizeof(mtpTypeId) + bareT::size();
|
||||
uint32 innerLength() const {
|
||||
return sizeof(mtpTypeId) + bareT::innerLength();
|
||||
}
|
||||
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) {
|
||||
if (from + 1 > end) throw mtpErrorInsufficient();
|
||||
@@ -406,7 +419,7 @@ public:
|
||||
read(from, end, cons);
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
uint32 innerLength() const {
|
||||
return sizeof(int32);
|
||||
}
|
||||
mtpTypeId type() const {
|
||||
@@ -449,7 +462,7 @@ public:
|
||||
read(from, end, cons);
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
uint32 innerLength() const {
|
||||
return sizeof(uint64);
|
||||
}
|
||||
mtpTypeId type() const {
|
||||
@@ -495,7 +508,7 @@ public:
|
||||
read(from, end, cons);
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
uint32 innerLength() const {
|
||||
return sizeof(uint64) + sizeof(uint64);
|
||||
}
|
||||
mtpTypeId type() const {
|
||||
@@ -544,8 +557,8 @@ public:
|
||||
read(from, end, cons);
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
return l.size() + h.size();
|
||||
uint32 innerLength() const {
|
||||
return l.innerLength() + h.innerLength();
|
||||
}
|
||||
mtpTypeId type() const {
|
||||
return mtpc_int256;
|
||||
@@ -588,7 +601,7 @@ public:
|
||||
read(from, end, cons);
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
uint32 innerLength() const {
|
||||
return sizeof(float64);
|
||||
}
|
||||
mtpTypeId type() const {
|
||||
@@ -658,7 +671,7 @@ public:
|
||||
return *(const MTPDstring*)data;
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
uint32 innerLength() const {
|
||||
uint32 l = c_string().v.length();
|
||||
if (l < 254) {
|
||||
l += 1;
|
||||
@@ -762,7 +775,7 @@ public:
|
||||
read(from, end, cons);
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
uint32 innerLength() const {
|
||||
return 0;
|
||||
}
|
||||
mtpTypeId type() const {
|
||||
@@ -809,6 +822,8 @@ public:
|
||||
}
|
||||
MTPDvector(uint32 count) : v(count) {
|
||||
}
|
||||
MTPDvector(uint32 count, const T &value) : v(count, value) {
|
||||
}
|
||||
MTPDvector(const QVector<T> &vec) : v(vec) {
|
||||
}
|
||||
|
||||
@@ -823,6 +838,9 @@ class MTPvector;
|
||||
template <typename T>
|
||||
MTPvector<T> MTP_vector(uint32 count);
|
||||
|
||||
template <typename T>
|
||||
MTPvector<T> MTP_vector(uint32 count, const T &value);
|
||||
|
||||
template <typename T>
|
||||
MTPvector<T> MTP_vector(const QVector<T> &v);
|
||||
|
||||
@@ -845,10 +863,10 @@ public:
|
||||
return *(const MTPDvector<T>*)data;
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
uint32 innerLength() const {
|
||||
uint32 result(sizeof(uint32));
|
||||
for (typename VType::const_iterator i = c_vector().v.cbegin(), e = c_vector().v.cend(); i != e; ++i) {
|
||||
result += i->size();
|
||||
result += i->innerLength();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -880,6 +898,7 @@ private:
|
||||
}
|
||||
|
||||
friend MTPvector<T> MTP_vector<T>(uint32 count);
|
||||
friend MTPvector<T> MTP_vector<T>(uint32 count, const T &value);
|
||||
friend MTPvector<T> MTP_vector<T>(const QVector<T> &v);
|
||||
typedef typename MTPDvector<T>::VType VType;
|
||||
};
|
||||
@@ -888,6 +907,10 @@ inline MTPvector<T> MTP_vector(uint32 count) {
|
||||
return MTPvector<T>(new MTPDvector<T>(count));
|
||||
}
|
||||
template <typename T>
|
||||
inline MTPvector<T> MTP_vector(uint32 count, const T &value) {
|
||||
return MTPvector<T>(new MTPDvector<T>(count, value));
|
||||
}
|
||||
template <typename T>
|
||||
inline MTPvector<T> MTP_vector(const QVector<T> &v) {
|
||||
return MTPvector<T>(new MTPDvector<T>(v));
|
||||
}
|
||||
@@ -898,6 +921,8 @@ public:
|
||||
}
|
||||
MTPVector(uint32 count) : MTPBoxed<MTPvector<T> >(MTP_vector<T>(count)) {
|
||||
}
|
||||
MTPVector(uint32 count, const T &value) : MTPBoxed<MTPvector<T> >(MTP_vector<T>(count, value)) {
|
||||
}
|
||||
MTPVector(const MTPvector<T> &v) : MTPBoxed<MTPvector<T> >(v) {
|
||||
}
|
||||
MTPVector(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPvector<T> >(from, end, cons) {
|
||||
@@ -941,8 +966,8 @@ public:
|
||||
return *(const MTPDerror*)data;
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
return c_error().vcode.size() + c_error().vtext.size();
|
||||
uint32 innerLength() const {
|
||||
return c_error().vcode.innerLength() + c_error().vtext.innerLength();
|
||||
}
|
||||
mtpTypeId type() const {
|
||||
return mtpc_error;
|
||||
@@ -979,7 +1004,7 @@ public:
|
||||
read(from, end, cons);
|
||||
}
|
||||
|
||||
uint32 size() const {
|
||||
uint32 innerLength() const {
|
||||
return 0;
|
||||
}
|
||||
mtpTypeId type() const {
|
||||
@@ -999,150 +1024,68 @@ typedef MTPBoxed<MTPnull> MTPNull;
|
||||
// Human-readable text serialization
|
||||
#if (defined _DEBUG || defined _WITH_DEBUG)
|
||||
|
||||
QString mtpTextSerialize(const mtpPrime *&from, const mtpPrime *end, mtpPrime cons = 0, uint32 level = 0, mtpPrime vcons = 0);
|
||||
template <typename Type>
|
||||
QString mtpWrapNumber(Type number, int32 base = 10) {
|
||||
return QString::number(number, base);
|
||||
}
|
||||
|
||||
inline QString mtpTextSerializeCore(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons, uint32 level, mtpPrime vcons = 0) {
|
||||
QString add = QString(" ").repeated(level * 2);
|
||||
|
||||
switch (mtpTypeId(cons)) {
|
||||
case mtpc_int: {
|
||||
MTPint value(from, end, cons);
|
||||
return QString("%1 [INT]").arg(value.v); }
|
||||
|
||||
case mtpc_long: {
|
||||
MTPlong value(from, end, cons);
|
||||
return QString("%2 [LONG]").arg(value.v); }
|
||||
|
||||
case mtpc_int128: {
|
||||
MTPint128 value(from, end, cons);
|
||||
return QString("%1 * 2^64 + %2 [INT128]").arg(value.h).arg(value.l); }
|
||||
|
||||
case mtpc_int256: {
|
||||
MTPint256 value(from, end, cons);
|
||||
return QString("%1 * 2^192 + %2 * 2^128 + %3 * 2^64 + %4 [INT256]").arg(value.h.h).arg(value.h.l).arg(value.l.h).arg(value.l.l); }
|
||||
|
||||
case mtpc_double: {
|
||||
MTPdouble value(from, end, cons);
|
||||
return QString("%1 [DOUBLE]").arg(value.v); }
|
||||
|
||||
case mtpc_string: {
|
||||
MTPstring value(from, end, cons);
|
||||
QByteArray strUtf8(value.c_string().v.c_str(), value.c_string().v.length());
|
||||
QString str = QString::fromUtf8(strUtf8);
|
||||
if (str.toUtf8() == strUtf8) {
|
||||
str = QString("\"%1\" [STRING]").arg(str.replace('\\', "\\\\").replace('"', "\\\"").replace('\n', "\\n"));
|
||||
} else {
|
||||
str = mb(strUtf8.constData(), strUtf8.size()).str() + QString(" [%1 BYTES]").arg(strUtf8.size());
|
||||
}
|
||||
return str; }
|
||||
|
||||
case mtpc_boolTrue:
|
||||
case mtpc_boolFalse: {
|
||||
MTPbool value(from, end, cons);
|
||||
return (value.v ? "[TRUE]" : "[FALSE]"); }
|
||||
|
||||
case mtpc_vector: {
|
||||
if (from >= end) {
|
||||
throw Exception("from >= end in vector");
|
||||
}
|
||||
int32 cnt = *(from++);
|
||||
QString result;
|
||||
if (cnt) {
|
||||
result += "\n" + add;
|
||||
for (int32 i = 0; i < cnt; ++i) {
|
||||
result += " " + mtpTextSerialize(from, end, vcons, level + 1) + ",\n" + add;
|
||||
}
|
||||
} else {
|
||||
result = " ";
|
||||
}
|
||||
return QString("[ vector<%1>").arg(vcons) + result + "]"; }
|
||||
|
||||
case mtpc_error: {
|
||||
QString result;
|
||||
result += "\n" + add;
|
||||
result += " code: " + mtpTextSerialize(from, end, mtpc_int, level + 1) + ",\n" + add;
|
||||
result += " text: " + mtpTextSerialize(from, end, mtpc_string, level + 1) + ",\n" + add;
|
||||
return "{ error" + result + "}"; }
|
||||
|
||||
case mtpc_null: {
|
||||
QString result;
|
||||
result = " ";
|
||||
return "{ null" + result + "}"; }
|
||||
|
||||
case mtpc_rpc_result: {
|
||||
QString result;
|
||||
result += "\n" + add;
|
||||
result += " req_msg_id: " + mtpTextSerialize(from, end, mtpc_long, level + 1) + ",\n" + add;
|
||||
result += " result: " + mtpTextSerialize(from, end, 0, level + 1) + ",\n" + add;
|
||||
return "{ rpc_result" + result + "}"; }
|
||||
|
||||
case mtpc_msg_container: {
|
||||
QString result;
|
||||
result += "\n" + add;
|
||||
result += " messages: " + mtpTextSerialize(from, end, mtpc_vector, level + 1, mtpc_core_message) + ",\n" + add;
|
||||
return "{ msg_container" + result + "}"; }
|
||||
|
||||
case mtpc_core_message: {
|
||||
QString result;
|
||||
result += "\n" + add;
|
||||
result += " msg_id: " + mtpTextSerialize(from, end, mtpc_long, level + 1) + ",\n" + add;
|
||||
result += " seq_no: " + mtpTextSerialize(from, end, mtpc_int, level + 1) + ",\n" + add;
|
||||
result += " bytes: " + mtpTextSerialize(from, end, mtpc_int, level + 1) + ",\n" + add;
|
||||
result += " body: " + mtpTextSerialize(from, end, 0, level + 1) + ",\n" + add;
|
||||
return "{ core_message" + result + "}"; }
|
||||
|
||||
case mtpc_gzip_packed: {
|
||||
MTPstring packed(from, end); // read packed string as serialized mtp string type
|
||||
uint32 packedLen = packed.c_string().v.size(), unpackedChunk = packedLen;
|
||||
mtpBuffer result; // * 4 because of mtpPrime type
|
||||
result.resize(0);
|
||||
|
||||
z_stream stream;
|
||||
stream.zalloc = 0;
|
||||
stream.zfree = 0;
|
||||
stream.opaque = 0;
|
||||
stream.avail_in = 0;
|
||||
stream.next_in = 0;
|
||||
int res = inflateInit2(&stream, 16 + MAX_WBITS);
|
||||
if (res != Z_OK) {
|
||||
throw Exception(QString("ungzip init, code: %1").arg(res));
|
||||
}
|
||||
stream.avail_in = packedLen;
|
||||
stream.next_in = (Bytef*)&packed._string().v[0];
|
||||
stream.avail_out = 0;
|
||||
while (!stream.avail_out) {
|
||||
result.resize(result.size() + unpackedChunk);
|
||||
stream.avail_out = unpackedChunk * sizeof(mtpPrime);
|
||||
stream.next_out = (Bytef*)&result[result.size() - unpackedChunk];
|
||||
int res = inflate(&stream, Z_NO_FLUSH);
|
||||
if (res != Z_OK && res != Z_STREAM_END) {
|
||||
inflateEnd(&stream);
|
||||
throw Exception(QString("ungzip unpack, code: %1").arg(res));
|
||||
}
|
||||
}
|
||||
if (stream.avail_out & 0x03) {
|
||||
uint32 badSize = result.size() * sizeof(mtpPrime) - stream.avail_out;
|
||||
throw Exception(QString("ungzip bad length, size: %1").arg(badSize));
|
||||
}
|
||||
result.resize(result.size() - (stream.avail_out >> 2));
|
||||
inflateEnd(&stream);
|
||||
|
||||
if (!result.size()) {
|
||||
throw Exception("ungzip void data");
|
||||
}
|
||||
const mtpPrime *newFrom = result.constData(), *newEnd = result.constData() + result.size();
|
||||
return "[GZIPPED] " + mtpTextSerialize(newFrom, newEnd, 0, level); }
|
||||
|
||||
default: {
|
||||
for (uint32 i = 1; i < mtpLayerMax; ++i) {
|
||||
if (cons == mtpLayers[i]) {
|
||||
return QString("[LAYER%1] ").arg(i + 1) + mtpTextSerialize(from, end, 0, level);
|
||||
}
|
||||
}
|
||||
struct MTPStringLogger {
|
||||
MTPStringLogger() : p(new char[MTPDebugBufferSize]), size(0), alloced(MTPDebugBufferSize) {
|
||||
}
|
||||
~MTPStringLogger() {
|
||||
delete[] p;
|
||||
}
|
||||
|
||||
throw Exception(QString("unknown cons %1").arg(cons));
|
||||
MTPStringLogger &add(const QString &data) {
|
||||
QByteArray d = data.toUtf8();
|
||||
return add(d.constData(), d.size());
|
||||
}
|
||||
|
||||
MTPStringLogger &add(const char *data, int32 len = -1) {
|
||||
if (len < 0) len = strlen(data);
|
||||
if (!len) return (*this);
|
||||
|
||||
ensureLength(len);
|
||||
memcpy(p + size, data, len);
|
||||
size += len;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
MTPStringLogger &addSpaces(int32 level) {
|
||||
int32 len = level * 2;
|
||||
if (!len) return (*this);
|
||||
|
||||
ensureLength(len);
|
||||
for (char *ptr = p + size, *end = ptr + len; ptr != end; ++ptr) {
|
||||
*ptr = ' ';
|
||||
}
|
||||
size += len;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
void ensureLength(int32 add) {
|
||||
if (size + add <= alloced) return;
|
||||
|
||||
int32 newsize = size + add;
|
||||
if (newsize % MTPDebugBufferSize) newsize += MTPDebugBufferSize - (newsize % MTPDebugBufferSize);
|
||||
char *b = new char[newsize];
|
||||
memcpy(b, p, size);
|
||||
alloced = newsize;
|
||||
delete p;
|
||||
p = b;
|
||||
}
|
||||
char *p;
|
||||
int32 size, alloced;
|
||||
};
|
||||
|
||||
void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons = 0, uint32 level = 0, mtpPrime vcons = 0);
|
||||
|
||||
void mtpTextSerializeCore(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons, uint32 level, mtpPrime vcons = 0);
|
||||
|
||||
inline QString mtpTextSerialize(const mtpPrime *&from, const mtpPrime *end) {
|
||||
MTPStringLogger to;
|
||||
mtpTextSerializeType(to, from, end, mtpc_core_message);
|
||||
return QString::fromUtf8(to.p, to.size);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -350,7 +350,7 @@ void mtpSetDC(int32 dc) {
|
||||
}
|
||||
}
|
||||
|
||||
MTProtoDC::MTProtoDC(int32 id, const mtpAuthKeyPtr &key) : _id(id), _key(key), _connectionInited(false), _connectionInitSent(false) {
|
||||
MTProtoDC::MTProtoDC(int32 id, const mtpAuthKeyPtr &key) : _id(id), _key(key), _connectionInited(false) {
|
||||
connect(this, SIGNAL(authKeyCreated()), this, SLOT(authKeyWrite()), Qt::QueuedConnection);
|
||||
|
||||
QMutexLocker lock(&_keysMapForWriteMutex);
|
||||
@@ -371,6 +371,7 @@ void MTProtoDC::authKeyWrite() {
|
||||
void MTProtoDC::setKey(const mtpAuthKeyPtr &key) {
|
||||
DEBUG_LOG(("AuthKey Info: MTProtoDC::setKey(%1), emitting authKeyCreated, dc %2").arg(key ? key->keyId() : 0).arg(_id));
|
||||
_key = key;
|
||||
_connectionInited = false;
|
||||
emit authKeyCreated();
|
||||
|
||||
QMutexLocker lock(&_keysMapForWriteMutex);
|
||||
@@ -443,7 +444,6 @@ void mtpUpdateDcOptions(const QVector<MTPDcOption> &options) {
|
||||
|
||||
MTProtoConfigLoader::MTProtoConfigLoader() : _enumCurrent(0), _enumRequest(0) {
|
||||
connect(&_enumDCTimer, SIGNAL(timeout()), this, SLOT(enumDC()));
|
||||
_enumDCTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
void MTProtoConfigLoader::load() {
|
||||
|
||||
@@ -29,13 +29,6 @@ public:
|
||||
void setKey(const mtpAuthKeyPtr &key);
|
||||
void destroyKey();
|
||||
|
||||
bool needConnectionInit() {
|
||||
QMutexLocker lock(&initLock);
|
||||
if (_connectionInited || _connectionInitSent) return false;
|
||||
_connectionInitSent = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool connectionInited() const {
|
||||
QMutexLocker lock(&initLock);
|
||||
bool res = _connectionInited;
|
||||
@@ -49,6 +42,7 @@ public:
|
||||
signals:
|
||||
|
||||
void authKeyCreated();
|
||||
void layerWasInited(bool was);
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -61,7 +55,6 @@ private:
|
||||
int32 _id;
|
||||
mtpAuthKeyPtr _key;
|
||||
bool _connectionInited;
|
||||
bool _connectionInitSent;
|
||||
};
|
||||
|
||||
typedef QSharedPointer<MTProtoDC> MTProtoDCPtr;
|
||||
@@ -97,7 +90,7 @@ signals:
|
||||
|
||||
private:
|
||||
|
||||
QTimer _enumDCTimer;
|
||||
SingleTimer _enumDCTimer;
|
||||
int32 _enumCurrent;
|
||||
mtpRequestId _enumRequest;
|
||||
|
||||
|
||||
@@ -19,8 +19,17 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#include "mainwidget.h"
|
||||
#include "window.h"
|
||||
|
||||
#include "application.h"
|
||||
|
||||
namespace {
|
||||
int32 _priority = 1;
|
||||
struct DataRequested {
|
||||
DataRequested() {
|
||||
memset(v, 0, sizeof(v));
|
||||
}
|
||||
int64 v[MTPDownloadSessionsCount];
|
||||
};
|
||||
QMap<int32, DataRequested> _dataRequested;
|
||||
}
|
||||
struct mtpFileLoaderQueue {
|
||||
mtpFileLoaderQueue() : queries(0), start(0), end(0) {
|
||||
@@ -34,10 +43,10 @@ namespace {
|
||||
LoaderQueues queues;
|
||||
}
|
||||
|
||||
mtpFileLoader::mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret) : prev(0), next(0),
|
||||
priority(0), inQueue(false), complete(false), requestId(0),
|
||||
dc(dc), locationType(0), volume(volume), local(local), secret(secret),
|
||||
id(0), access(0), initialSize(0), size(0), type(MTP_storage_fileUnknown()) {
|
||||
mtpFileLoader::mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) : prev(0), next(0),
|
||||
priority(0), inQueue(false), complete(false), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
|
||||
dc(dc), locationType(0), volume(volume), local(local), secret(secret),
|
||||
id(0), access(0), fileIsOpen(false), size(size), type(MTP_storage_fileUnknown()) {
|
||||
LoaderQueues::iterator i = queues.find(dc);
|
||||
if (i == queues.cend()) {
|
||||
i = queues.insert(dc, mtpFileLoaderQueue());
|
||||
@@ -46,29 +55,29 @@ mtpFileLoader::mtpFileLoader(int32 dc, const int64 &volume, int32 local, const i
|
||||
}
|
||||
|
||||
mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size) : prev(0), next(0),
|
||||
priority(0), inQueue(false), complete(false), requestId(0),
|
||||
priority(0), inQueue(false), complete(false), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
|
||||
dc(dc), locationType(locType),
|
||||
id(id), access(access), file(to), duplicateInData(false), initialSize(size), type(MTP_storage_fileUnknown()) {
|
||||
LoaderQueues::iterator i = queues.find(MTP::dld + dc);
|
||||
id(id), access(access), file(to), fname(to), fileIsOpen(false), duplicateInData(false), size(size), type(MTP_storage_fileUnknown()) {
|
||||
LoaderQueues::iterator i = queues.find(MTP::dld[0] + dc);
|
||||
if (i == queues.cend()) {
|
||||
i = queues.insert(MTP::dld + dc, mtpFileLoaderQueue());
|
||||
i = queues.insert(MTP::dld[0] + dc, mtpFileLoaderQueue());
|
||||
}
|
||||
queue = &i.value();
|
||||
}
|
||||
|
||||
mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size, bool todata) : prev(0), next(0),
|
||||
priority(0), inQueue(false), complete(false), requestId(0),
|
||||
priority(0), inQueue(false), complete(false), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
|
||||
dc(dc), locationType(locType),
|
||||
id(id), access(access), file(to), duplicateInData(todata), initialSize(size), type(MTP_storage_fileUnknown()) {
|
||||
LoaderQueues::iterator i = queues.find(MTP::dld + dc);
|
||||
id(id), access(access), file(to), fname(to), fileIsOpen(false), duplicateInData(todata), size(size), type(MTP_storage_fileUnknown()) {
|
||||
LoaderQueues::iterator i = queues.find(MTP::dld[0] + dc);
|
||||
if (i == queues.cend()) {
|
||||
i = queues.insert(MTP::dld + dc, mtpFileLoaderQueue());
|
||||
i = queues.insert(MTP::dld[0] + dc, mtpFileLoaderQueue());
|
||||
}
|
||||
queue = &i.value();
|
||||
}
|
||||
|
||||
QString mtpFileLoader::fileName() const {
|
||||
return file.fileName();
|
||||
return fname;
|
||||
}
|
||||
|
||||
bool mtpFileLoader::done() const {
|
||||
@@ -89,17 +98,17 @@ float64 mtpFileLoader::currentProgress() const {
|
||||
return float64(currentOffset()) / fullSize();
|
||||
}
|
||||
|
||||
int32 mtpFileLoader::currentOffset() const {
|
||||
return file.isOpen() ? file.size() : data.size();
|
||||
int32 mtpFileLoader::currentOffset(bool includeSkipped) const {
|
||||
return (fileIsOpen ? file.size() : data.size()) - (includeSkipped ? 0 : skippedBytes);
|
||||
}
|
||||
|
||||
int32 mtpFileLoader::fullSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
void mtpFileLoader::setFileName(const QString &fname) {
|
||||
if (duplicateInData && file.fileName().isEmpty()) {
|
||||
file.setFileName(fname);
|
||||
void mtpFileLoader::setFileName(const QString &fileName) {
|
||||
if (duplicateInData && fname.isEmpty()) {
|
||||
file.setFileName(fname = fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,31 +118,34 @@ uint64 mtpFileLoader::objId() const {
|
||||
|
||||
void mtpFileLoader::loadNext() {
|
||||
if (queue->queries >= MaxFileQueries) return;
|
||||
for (mtpFileLoader *i = queue->start; i; i = i->next) {
|
||||
if (i->loadPart() && queue->queries >= MaxFileQueries) return;
|
||||
for (mtpFileLoader *i = queue->start; i;) {
|
||||
if (i->loadPart()) {
|
||||
if (queue->queries >= MaxFileQueries) return;
|
||||
} else {
|
||||
i = i->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mtpFileLoader::finishFail() {
|
||||
bool started = currentOffset() > 0;
|
||||
if (requestId) {
|
||||
requestId = 0;
|
||||
--queue->queries;
|
||||
}
|
||||
bool started = currentOffset(true) > 0;
|
||||
cancelRequests();
|
||||
type = MTP_storage_fileUnknown();
|
||||
complete = true;
|
||||
if (file.isOpen()) {
|
||||
if (fileIsOpen) {
|
||||
file.close();
|
||||
fileIsOpen = false;
|
||||
file.remove();
|
||||
}
|
||||
data = QByteArray();
|
||||
emit failed(this, started);
|
||||
file.setFileName(QString());
|
||||
file.setFileName(fname = QString());
|
||||
loadNext();
|
||||
}
|
||||
|
||||
bool mtpFileLoader::loadPart() {
|
||||
if (complete || requestId) return false;
|
||||
if (complete || lastComplete || (!requests.isEmpty() && !size)) return false;
|
||||
if (size && nextRequestOffset >= size) return false;
|
||||
|
||||
int32 limit = DocumentDownloadPartSize;
|
||||
MTPInputFileLocation loc;
|
||||
@@ -148,54 +160,101 @@ bool mtpFileLoader::loadPart() {
|
||||
break;
|
||||
}
|
||||
|
||||
int32 offset = nextRequestOffset, dcIndex = 0;
|
||||
DataRequested &dr(_dataRequested[dc]);
|
||||
if (size) {
|
||||
for (int32 i = 1; i < MTPDownloadSessionsCount; ++i) {
|
||||
if (dr.v[i] < dr.v[dcIndex]) {
|
||||
dcIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dcIndex) {
|
||||
App::app()->killDownloadSessionsStop(dc);
|
||||
}
|
||||
|
||||
mtpRequestId reqId = MTP::send(MTPupload_GetFile(MTPupload_getFile(loc, MTP_int(offset), MTP_int(limit))), rpcDone(&mtpFileLoader::partLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::dld[dcIndex] + dc, 50);
|
||||
|
||||
++queue->queries;
|
||||
int32 offset = currentOffset();
|
||||
MTPupload_GetFile request(MTPupload_getFile(loc, MTP_int(offset), MTP_int(limit)));
|
||||
requestId = MTP::send(request, rpcDone(&mtpFileLoader::partLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::dld + dc, 50);
|
||||
dr.v[dcIndex] += limit;
|
||||
requests.insert(reqId, dcIndex);
|
||||
nextRequestOffset += limit;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result) {
|
||||
if (requestId) {
|
||||
--queue->queries;
|
||||
requestId = 0;
|
||||
}
|
||||
if (offset == currentOffset()) {
|
||||
int32 limit = locationType ? DocumentDownloadPartSize : DownloadPartSize;
|
||||
const MTPDupload_file &d(result.c_upload_file());
|
||||
const string &bytes(d.vbytes.c_string().v);
|
||||
if (bytes.size()) {
|
||||
if (file.isOpen()) {
|
||||
if (file.write(bytes.data(), bytes.size()) != qint64(bytes.size())) {
|
||||
return finishFail();
|
||||
}
|
||||
} else {
|
||||
data.append(bytes.data(), bytes.size());
|
||||
void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRequestId req) {
|
||||
Requests::iterator i = requests.find(req);
|
||||
if (i == requests.cend()) return;
|
||||
|
||||
int32 limit = locationType ? DocumentDownloadPartSize : DownloadPartSize;
|
||||
int32 dcIndex = i.value();
|
||||
_dataRequested[dc].v[dcIndex] -= limit;
|
||||
|
||||
--queue->queries;
|
||||
requests.erase(i);
|
||||
|
||||
const MTPDupload_file &d(result.c_upload_file());
|
||||
const string &bytes(d.vbytes.c_string().v);
|
||||
if (bytes.size()) {
|
||||
if (fileIsOpen) {
|
||||
int64 fsize = file.size();
|
||||
if (offset < fsize) {
|
||||
skippedBytes -= bytes.size();
|
||||
} else if (offset > fsize) {
|
||||
skippedBytes += offset - fsize;
|
||||
}
|
||||
file.seek(offset);
|
||||
if (file.write(bytes.data(), bytes.size()) != qint64(bytes.size())) {
|
||||
return finishFail();
|
||||
}
|
||||
}
|
||||
if (bytes.size() && !(bytes.size() % 1024)) { // good next offset
|
||||
// offset += bytes.size();
|
||||
} else {
|
||||
if (duplicateInData && !file.fileName().isEmpty()) {
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
return finishFail();
|
||||
}
|
||||
if (file.write(data) != qint64(data.size())) {
|
||||
return finishFail();
|
||||
}
|
||||
data.reserve(offset + bytes.size());
|
||||
if (offset > data.size()) {
|
||||
skippedBytes += offset - data.size();
|
||||
data.resize(offset);
|
||||
}
|
||||
type = d.vtype;
|
||||
complete = true;
|
||||
if (file.isOpen()) {
|
||||
file.close();
|
||||
psPostprocessFile(QFileInfo(file).absoluteFilePath());
|
||||
if (offset == data.size()) {
|
||||
data.append(bytes.data(), bytes.size());
|
||||
} else {
|
||||
skippedBytes -= bytes.size();
|
||||
if (int64(offset + bytes.size()) > data.size()) {
|
||||
data.resize(offset + bytes.size());
|
||||
}
|
||||
memcpy(data.data() + offset, bytes.data(), bytes.size());
|
||||
}
|
||||
removeFromQueue();
|
||||
App::wnd()->update();
|
||||
App::wnd()->notifyUpdateAllPhotos();
|
||||
}
|
||||
emit progress(this);
|
||||
}
|
||||
if (!bytes.size() || (bytes.size() % 1024)) { // bad next offset
|
||||
lastComplete = true;
|
||||
}
|
||||
if (requests.isEmpty() && (lastComplete || (size && nextRequestOffset >= size))) {
|
||||
if (!fname.isEmpty() && duplicateInData) {
|
||||
if (!fileIsOpen) fileIsOpen = file.open(QIODevice::WriteOnly);
|
||||
if (!fileIsOpen) {
|
||||
return finishFail();
|
||||
}
|
||||
if (file.write(data) != qint64(data.size())) {
|
||||
return finishFail();
|
||||
}
|
||||
}
|
||||
type = d.vtype;
|
||||
complete = true;
|
||||
if (fileIsOpen) {
|
||||
file.close();
|
||||
fileIsOpen = false;
|
||||
psPostprocessFile(QFileInfo(file).absoluteFilePath());
|
||||
}
|
||||
removeFromQueue();
|
||||
App::wnd()->update();
|
||||
App::wnd()->notifyUpdateAllPhotos();
|
||||
|
||||
if (!queue->queries && dcIndex) {
|
||||
App::app()->killDownloadSessionsStart(dc);
|
||||
}
|
||||
}
|
||||
emit progress(this);
|
||||
loadNext();
|
||||
}
|
||||
|
||||
@@ -229,8 +288,9 @@ void mtpFileLoader::pause() {
|
||||
void mtpFileLoader::start(bool loadFirst, bool prior) {
|
||||
if (complete) return;
|
||||
|
||||
if (!file.fileName().isEmpty() && !duplicateInData) {
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
if (!fname.isEmpty() && !duplicateInData && !fileIsOpen) {
|
||||
fileIsOpen = file.open(QIODevice::WriteOnly);
|
||||
if (!fileIsOpen) {
|
||||
finishFail();
|
||||
return;
|
||||
}
|
||||
@@ -325,15 +385,12 @@ void mtpFileLoader::start(bool loadFirst, bool prior) {
|
||||
}
|
||||
|
||||
void mtpFileLoader::cancel() {
|
||||
bool started = currentOffset() > 0;
|
||||
if (requestId) {
|
||||
requestId = 0;
|
||||
--queue->queries;
|
||||
}
|
||||
cancelRequests();
|
||||
type = MTP_storage_fileUnknown();
|
||||
complete = true;
|
||||
if (file.isOpen()) {
|
||||
if (fileIsOpen) {
|
||||
file.close();
|
||||
fileIsOpen = false;
|
||||
file.remove();
|
||||
}
|
||||
data = QByteArray();
|
||||
@@ -342,6 +399,26 @@ void mtpFileLoader::cancel() {
|
||||
loadNext();
|
||||
}
|
||||
|
||||
void mtpFileLoader::cancelRequests() {
|
||||
if (requests.isEmpty()) return;
|
||||
|
||||
int32 limit = locationType ? DocumentDownloadPartSize : DownloadPartSize;
|
||||
bool wasIndex = false;
|
||||
DataRequested &dr(_dataRequested[dc]);
|
||||
for (Requests::const_iterator i = requests.cbegin(), e = requests.cend(); i != e; ++i) {
|
||||
MTP::cancel(i.key());
|
||||
int32 dcIndex = i.value();
|
||||
dr.v[dcIndex] -= limit;
|
||||
if (dcIndex) wasIndex = true;
|
||||
}
|
||||
queue->queries -= requests.size();
|
||||
requests.clear();
|
||||
|
||||
if (!queue->queries && wasIndex) {
|
||||
App::app()->killDownloadSessionsStart(dc);
|
||||
}
|
||||
}
|
||||
|
||||
bool mtpFileLoader::loading() const {
|
||||
return inQueue;
|
||||
}
|
||||
@@ -353,6 +430,7 @@ void mtpFileLoader::started(bool loadFirst, bool prior) {
|
||||
|
||||
mtpFileLoader::~mtpFileLoader() {
|
||||
removeFromQueue();
|
||||
cancelRequests();
|
||||
}
|
||||
|
||||
namespace MTP {
|
||||
|
||||
@@ -27,7 +27,7 @@ class mtpFileLoader : public QObject, public RPCSender {
|
||||
|
||||
public:
|
||||
|
||||
mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret);
|
||||
mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
|
||||
mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size);
|
||||
mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size, bool todata);
|
||||
bool done() const;
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
const QByteArray &bytes() const;
|
||||
QString fileName() const;
|
||||
float64 currentProgress() const;
|
||||
int32 currentOffset() const;
|
||||
int32 currentOffset(bool includeSkipped = false) const;
|
||||
int32 fullSize() const;
|
||||
|
||||
void setFileName(const QString &filename); // set filename for duplicateInData loader
|
||||
@@ -61,14 +61,22 @@ private:
|
||||
|
||||
mtpFileLoaderQueue *queue;
|
||||
bool inQueue, complete;
|
||||
int32 requestId;
|
||||
|
||||
void cancelRequests();
|
||||
|
||||
typedef QMap<mtpRequestId, int32> Requests;
|
||||
Requests requests;
|
||||
int32 skippedBytes;
|
||||
int32 nextRequestOffset;
|
||||
bool lastComplete;
|
||||
|
||||
void started(bool loadFirst, bool prior);
|
||||
void removeFromQueue();
|
||||
|
||||
void loadNext();
|
||||
void finishFail();
|
||||
bool loadPart();
|
||||
void partLoaded(int32 offset, const MTPupload_File &result);
|
||||
void partLoaded(int32 offset, const MTPupload_File &result, mtpRequestId req);
|
||||
bool partFailed(const RPCError &error);
|
||||
|
||||
int32 dc;
|
||||
@@ -81,8 +89,9 @@ private:
|
||||
uint64 id; // for other locations
|
||||
uint64 access;
|
||||
QFile file;
|
||||
QString fname;
|
||||
bool fileIsOpen;
|
||||
bool duplicateInData;
|
||||
int32 initialSize;
|
||||
|
||||
QByteArray data;
|
||||
|
||||
|
||||
5388
Telegram/SourceFiles/mtproto/mtpScheme.cpp
Normal file
@@ -86,7 +86,6 @@ void MTProtoSession::start(int32 dcenter, uint32 connects) {
|
||||
connect(this, SIGNAL(startSendTimer(int)), &sender, SLOT(start(int)));
|
||||
connect(this, SIGNAL(stopSendTimer()), &sender, SLOT(stop()));
|
||||
connect(this, SIGNAL(needToSendAsync()), this, SIGNAL(needToSend()));
|
||||
sender.setSingleShot(true);
|
||||
|
||||
MTProtoDCMap &dcs(mtpDCMap());
|
||||
|
||||
@@ -114,8 +113,11 @@ void MTProtoSession::start(int32 dcenter, uint32 connects) {
|
||||
|
||||
ReadLockerAttempt lock(keyMutex());
|
||||
data.setKey(lock ? dc->getKey() : mtpAuthKeyPtr(0));
|
||||
|
||||
if (lock && dc->connectionInited()) {
|
||||
data.setLayerWasInited(true);
|
||||
}
|
||||
connect(dc.data(), SIGNAL(authKeyCreated()), this, SLOT(authKeyCreatedForDC()));
|
||||
connect(dc.data(), SIGNAL(layerWasInited(bool)), this, SLOT(layerWasInitedForDC(bool)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,24 +129,47 @@ void MTProtoSession::restart() {
|
||||
}
|
||||
|
||||
void MTProtoSession::stop() {
|
||||
while (connections.size()) {
|
||||
while (!connections.isEmpty()) {
|
||||
connections.back()->stop();
|
||||
connections.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoSession::checkRequestsByTimer() {
|
||||
MTPMsgsStateReq stateRequest(MTP_msgs_state_req(MTP_vector<MTPlong>(0)));
|
||||
QVector<MTPlong> &stateRequestIds(stateRequest._msgs_state_req().vmsg_ids._vector().v);
|
||||
void MTProtoSession::sendAnything(uint64 msCanWait) {
|
||||
uint64 ms = getms(true);
|
||||
if (msSendCall) {
|
||||
if (ms > msSendCall + msWait) {
|
||||
msWait = 0;
|
||||
} else {
|
||||
msWait = (msSendCall + msWait) - ms;
|
||||
if (msWait > msCanWait) {
|
||||
msWait = msCanWait;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msWait = msCanWait;
|
||||
}
|
||||
if (msWait) {
|
||||
msSendCall = ms;
|
||||
emit startSendTimer(msWait);
|
||||
DEBUG_LOG(("MTP Info: can wait for %1ms from current %2").arg(msWait).arg(msSendCall));
|
||||
} else {
|
||||
emit stopSendTimer();
|
||||
msSendCall = 0;
|
||||
emit needToSendAsync();
|
||||
}
|
||||
}
|
||||
|
||||
void MTProtoSession::checkRequestsByTimer() {
|
||||
QVector<mtpMsgId> resendingIds;
|
||||
QVector<mtpMsgId> removingIds; // remove very old (10 minutes) containers and resend requests
|
||||
QVector<mtpMsgId> stateRequestIds;
|
||||
|
||||
{
|
||||
QReadLocker locker(data.haveSentMutex());
|
||||
mtpRequestMap &haveSent(data.haveSentMap());
|
||||
uint32 haveSentCount(haveSent.size());
|
||||
uint64 ms = getms();
|
||||
uint64 ms = getms(true);
|
||||
for (mtpRequestMap::iterator i = haveSent.begin(), e = haveSent.end(); i != e; ++i) {
|
||||
mtpRequest &req(i.value());
|
||||
if (req->msDate > 0) {
|
||||
@@ -155,7 +180,7 @@ void MTProtoSession::checkRequestsByTimer() {
|
||||
} else {
|
||||
req->msDate = ms;
|
||||
stateRequestIds.reserve(haveSentCount);
|
||||
stateRequestIds.push_back(MTP_long(i.key()));
|
||||
stateRequestIds.push_back(i.key());
|
||||
}
|
||||
}
|
||||
} else if (unixtime() > (int32)(i.key() >> 32) + MTPContainerLives) {
|
||||
@@ -167,19 +192,26 @@ void MTProtoSession::checkRequestsByTimer() {
|
||||
|
||||
if (stateRequestIds.size()) {
|
||||
DEBUG_LOG(("MTP Info: requesting state of msgs: %1").arg(logVectorLong(stateRequestIds)));
|
||||
send(stateRequest, RPCResponseHandler(), MTPCheckResendWaiting);
|
||||
{
|
||||
QWriteLocker locker(data.stateRequestMutex());
|
||||
for (uint32 i = 0, l = stateRequestIds.size(); i < l; ++i) {
|
||||
data.stateRequestMap().insert(stateRequestIds[i], true);
|
||||
}
|
||||
}
|
||||
sendAnything(MTPCheckResendWaiting);
|
||||
}
|
||||
for (uint32 i = 0, l = resendingIds.size(); i < l; ++i) {
|
||||
DEBUG_LOG(("MTP Info: resending request %1").arg(resendingIds[i]));
|
||||
resend(resendingIds[i], MTPCheckResendWaiting);
|
||||
if (!resendingIds.isEmpty()) {
|
||||
for (uint32 i = 0, l = resendingIds.size(); i < l; ++i) {
|
||||
DEBUG_LOG(("MTP Info: resending request %1").arg(resendingIds[i]));
|
||||
resend(resendingIds[i], MTPCheckResendWaiting);
|
||||
}
|
||||
}
|
||||
uint32 removingIdsCount = removingIds.size();
|
||||
if (removingIdsCount) {
|
||||
if (!removingIds.isEmpty()) {
|
||||
RPCCallbackClears clearCallbacks;
|
||||
{
|
||||
QWriteLocker locker(data.haveSentMutex());
|
||||
mtpRequestMap &haveSent(data.haveSentMap());
|
||||
for (uint32 i = 0; i < removingIdsCount; ++i) {
|
||||
for (uint32 i = 0, l = removingIds.size(); i < l; ++i) {
|
||||
mtpRequestMap::iterator j = haveSent.find(removingIds[i]);
|
||||
if (j != haveSent.cend()) {
|
||||
if (j.value()->requestId) {
|
||||
@@ -201,11 +233,15 @@ void MTProtoSession::onResetDone() {
|
||||
_mtp_internal::onSessionReset(dcId);
|
||||
}
|
||||
|
||||
void MTProtoSession::cancel(mtpRequestId requestId) {
|
||||
QWriteLocker locker(data.toSendMutex());
|
||||
mtpPreRequestMap &toSend(data.toSendMap());
|
||||
mtpPreRequestMap::iterator i = toSend.find(requestId);
|
||||
if (i != toSend.end()) toSend.erase(i);
|
||||
void MTProtoSession::cancel(mtpRequestId requestId, mtpMsgId msgId) {
|
||||
if (requestId) {
|
||||
QWriteLocker locker(data.toSendMutex());
|
||||
data.toSendMap().remove(requestId);
|
||||
}
|
||||
if (msgId) {
|
||||
QWriteLocker locker(data.haveSentMutex());
|
||||
data.haveSentMap().remove(msgId);
|
||||
}
|
||||
}
|
||||
|
||||
int32 MTProtoSession::requestState(mtpRequestId requestId) const {
|
||||
@@ -300,7 +336,7 @@ mtpRequestId MTProtoSession::resend(mtpMsgId msgId, uint64 msCanWait, bool force
|
||||
}
|
||||
return 0xFFFFFFFF;
|
||||
} else if (!mtpRequestData::isStateRequest(request)) {
|
||||
request->msDate = forceContainer ? 0 : getms();
|
||||
request->msDate = forceContainer ? 0 : getms(true);
|
||||
sendPrepared(request, msCanWait, false);
|
||||
{
|
||||
QWriteLocker locker(data.toResendMutex());
|
||||
@@ -340,43 +376,12 @@ void MTProtoSession::sendPrepared(const mtpRequest &request, uint64 msCanWait, b
|
||||
|
||||
DEBUG_LOG(("MTP Info: added, requestId %1").arg(request->requestId));
|
||||
|
||||
uint64 ms = getms();
|
||||
if (msSendCall) {
|
||||
if (ms > msSendCall + msWait) {
|
||||
msWait = 0;
|
||||
} else {
|
||||
msWait = (msSendCall + msWait) - ms;
|
||||
if (msWait > msCanWait) {
|
||||
msWait = msCanWait;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msWait = msCanWait;
|
||||
}
|
||||
if (msWait) {
|
||||
msSendCall = ms;
|
||||
emit startSendTimer(msWait);
|
||||
DEBUG_LOG(("MTP Info: can wait for %1ms from current %2").arg(msWait).arg(msSendCall));
|
||||
} else {
|
||||
emit stopSendTimer();
|
||||
msSendCall = 0;
|
||||
emit needToSendAsync();
|
||||
}
|
||||
sendAnything(msCanWait);
|
||||
}
|
||||
|
||||
void MTProtoSession::sendPreparedWithInit(const mtpRequest &request, uint64 msCanWait) { // returns true, if emit of needToSend() is needed
|
||||
if (request->size() > 8 && request->at(8) == mtpc_initConnection) {
|
||||
sendPrepared(request, msCanWait, false);
|
||||
return;
|
||||
}
|
||||
MTPInitConnection<mtpRequest> requestWrap(MTPinitConnection<mtpRequest>(MTP_int(ApiId), MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(ApiLang), request));
|
||||
uint32 requestSize = requestWrap.size() >> 2;
|
||||
mtpRequest reqSerialized(mtpRequestData::prepare(requestSize));
|
||||
requestWrap.write(*reqSerialized);
|
||||
|
||||
reqSerialized->msDate = getms(); // > 0 - can send without container
|
||||
_mtp_internal::replaceRequest(reqSerialized, request);
|
||||
sendPrepared(reqSerialized, msCanWait);
|
||||
void MTProtoSession::sendPreparedWithInit(const mtpRequest &request, uint64 msCanWait) {
|
||||
request->needsLayer = true;
|
||||
sendPrepared(request, msCanWait, false);
|
||||
}
|
||||
|
||||
QReadWriteLock *MTProtoSession::keyMutex() const {
|
||||
@@ -389,11 +394,22 @@ void MTProtoSession::authKeyCreatedForDC() {
|
||||
emit authKeyCreated();
|
||||
}
|
||||
|
||||
void MTProtoSession::keyCreated(const mtpAuthKeyPtr &key) {
|
||||
void MTProtoSession::notifyKeyCreated(const mtpAuthKeyPtr &key) {
|
||||
DEBUG_LOG(("AuthKey Info: MTProtoSession::keyCreated(), setting, dc %1").arg(dcId));
|
||||
dc->setKey(key);
|
||||
}
|
||||
|
||||
void MTProtoSession::layerWasInitedForDC(bool wasInited) {
|
||||
DEBUG_LOG(("MTP Info: MTProtoSession::layerWasInitedForDC slot, dc %1").arg(dcId));
|
||||
data.setLayerWasInited(wasInited);
|
||||
}
|
||||
|
||||
void MTProtoSession::notifyLayerInited(bool wasInited) {
|
||||
DEBUG_LOG(("MTP Info: emitting MTProtoDC::layerWasInited(%1), dc %2").arg(logBool(wasInited)).arg(dcId));
|
||||
dc->setConnectionInited(wasInited);
|
||||
emit dc->layerWasInited(wasInited);
|
||||
}
|
||||
|
||||
void MTProtoSession::destroyKey() {
|
||||
if (!dc) return;
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ public:
|
||||
|
||||
MTPSessionData(MTProtoSession *creator)
|
||||
: _session(0), _salt(0)
|
||||
, _messagesSent(0), fakeRequestId(-2000000000)
|
||||
, _owner(creator), keyChecked(false) {
|
||||
, _messagesSent(0), _fakeRequestId(-2000000000)
|
||||
, _owner(creator), _keyChecked(false), _layerInited(false) {
|
||||
}
|
||||
|
||||
void setSession(uint64 session) {
|
||||
@@ -45,6 +45,14 @@ public:
|
||||
QReadLocker locker(&lock);
|
||||
return _session;
|
||||
}
|
||||
bool layerWasInited() const {
|
||||
QReadLocker locker(&lock);
|
||||
return _layerInited;
|
||||
}
|
||||
void setLayerWasInited(bool was) {
|
||||
QWriteLocker locker(&lock);
|
||||
_layerInited = was;
|
||||
}
|
||||
|
||||
void setSalt(uint64 salt) {
|
||||
QWriteLocker locker(&lock);
|
||||
@@ -56,26 +64,31 @@ public:
|
||||
}
|
||||
|
||||
const mtpAuthKeyPtr &getKey() const {
|
||||
return authKey;
|
||||
return _authKey;
|
||||
}
|
||||
void setKey(const mtpAuthKeyPtr &key) {
|
||||
if (authKey != key) {
|
||||
if (_authKey != key) {
|
||||
uint64 session;
|
||||
memsetrnd(session);
|
||||
authKey = key;
|
||||
_authKey = key;
|
||||
|
||||
DEBUG_LOG(("MTP Info: new auth key set in SessionData, id %1, setting random server_session %2").arg(key ? key->keyId() : 0).arg(session));
|
||||
setSession(session);
|
||||
QWriteLocker locker(&lock);
|
||||
if (_session != session) {
|
||||
_session = session;
|
||||
_messagesSent = 0;
|
||||
}
|
||||
_layerInited = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isCheckedKey() const {
|
||||
QReadLocker locker(&lock);
|
||||
return keyChecked;
|
||||
return _keyChecked;
|
||||
}
|
||||
void setCheckedKey(bool checked) {
|
||||
QWriteLocker locker(&lock);
|
||||
keyChecked = checked;
|
||||
_keyChecked = checked;
|
||||
}
|
||||
|
||||
QReadWriteLock *keyMutex() const;
|
||||
@@ -98,6 +111,9 @@ public:
|
||||
QReadWriteLock *haveReceivedMutex() const {
|
||||
return &haveReceivedLock;
|
||||
}
|
||||
QReadWriteLock *stateRequestMutex() const {
|
||||
return &stateRequestLock;
|
||||
}
|
||||
|
||||
mtpPreRequestMap &toSendMap() {
|
||||
return toSend;
|
||||
@@ -117,10 +133,10 @@ public:
|
||||
const mtpRequestIdsMap &toResendMap() const {
|
||||
return toResend;
|
||||
}
|
||||
mtpMsgIdsSet &receivedIdsSet() {
|
||||
mtpMsgIdsMap &receivedIdsSet() {
|
||||
return receivedIds;
|
||||
}
|
||||
const mtpMsgIdsSet &receivedIdsSet() const {
|
||||
const mtpMsgIdsMap &receivedIdsSet() const {
|
||||
return receivedIds;
|
||||
}
|
||||
mtpRequestIdsMap &wereAckedMap() {
|
||||
@@ -135,14 +151,20 @@ public:
|
||||
const mtpResponseMap &haveReceivedMap() const {
|
||||
return haveReceived;
|
||||
}
|
||||
mtpMsgIdsSet &stateRequestMap() {
|
||||
return stateRequest;
|
||||
}
|
||||
const mtpMsgIdsSet &stateRequestMap() const {
|
||||
return stateRequest;
|
||||
}
|
||||
|
||||
mtpRequestId nextFakeRequestId() { // must be locked by haveReceivedMutex()
|
||||
if (haveReceived.isEmpty() || haveReceived.cbegin().key() > 0) {
|
||||
fakeRequestId = -2000000000;
|
||||
_fakeRequestId = -2000000000;
|
||||
} else {
|
||||
++fakeRequestId;
|
||||
++_fakeRequestId;
|
||||
}
|
||||
return fakeRequestId;
|
||||
return _fakeRequestId;
|
||||
}
|
||||
|
||||
MTProtoSession *owner() {
|
||||
@@ -165,19 +187,20 @@ private:
|
||||
uint64 _session, _salt;
|
||||
|
||||
uint32 _messagesSent;
|
||||
mtpRequestId fakeRequestId;
|
||||
mtpRequestId _fakeRequestId;
|
||||
|
||||
MTProtoSession *_owner;
|
||||
|
||||
mtpAuthKeyPtr authKey;
|
||||
bool keyChecked;
|
||||
mtpAuthKeyPtr _authKey;
|
||||
bool _keyChecked, _layerInited;
|
||||
|
||||
mtpPreRequestMap toSend; // map of request_id -> request, that is waiting to be sent
|
||||
mtpRequestMap haveSent; // map of msg_id -> request, that was sent, msDate = 0 for msgs_state_req (no resend / state req), msDate = 0, seqNo = 0 for containers
|
||||
mtpRequestIdsMap toResend; // map of msg_id -> request_id, that request_id -> request lies in toSend and is waiting to be resent
|
||||
mtpMsgIdsSet receivedIds; // set of received msg_id's, for checking new msg_ids
|
||||
mtpMsgIdsMap receivedIds; // set of received msg_id's, for checking new msg_ids
|
||||
mtpRequestIdsMap wereAcked; // map of msg_id -> request_id, this msg_ids already were acked or do not need ack
|
||||
mtpResponseMap haveReceived; // map of request_id -> response, that should be processed in other thread
|
||||
mtpMsgIdsSet stateRequest; // set of msg_id's, whose state should be requested
|
||||
|
||||
// mutexes
|
||||
mutable QReadWriteLock lock;
|
||||
@@ -187,6 +210,7 @@ private:
|
||||
mutable QReadWriteLock receivedIdsLock;
|
||||
mutable QReadWriteLock wereAckedLock;
|
||||
mutable QReadWriteLock haveReceivedLock;
|
||||
mutable QReadWriteLock stateRequestLock;
|
||||
|
||||
};
|
||||
|
||||
@@ -205,13 +229,15 @@ public:
|
||||
~MTProtoSession();
|
||||
|
||||
QReadWriteLock *keyMutex() const;
|
||||
void keyCreated(const mtpAuthKeyPtr &key);
|
||||
void notifyKeyCreated(const mtpAuthKeyPtr &key);
|
||||
void destroyKey();
|
||||
void notifyLayerInited(bool wasInited);
|
||||
|
||||
template <typename TRequest>
|
||||
mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), uint64 msCanWait = 0, uint32 layer = 0, bool toMainDC = false); // send mtp request
|
||||
mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), uint64 msCanWait = 0, bool needsLayer = false, bool toMainDC = false, mtpRequestId after = 0); // send mtp request
|
||||
void sendAnything(uint64 msCanWait);
|
||||
|
||||
void cancel(mtpRequestId requestId);
|
||||
void cancel(mtpRequestId requestId, mtpMsgId msgId);
|
||||
int32 requestState(mtpRequestId requestId) const;
|
||||
int32 getState() const;
|
||||
QString transport() const;
|
||||
@@ -235,6 +261,7 @@ signals:
|
||||
public slots:
|
||||
|
||||
void authKeyCreatedForDC();
|
||||
void layerWasInitedForDC(bool wasInited);
|
||||
|
||||
void tryToReceive();
|
||||
void checkRequestsByTimer();
|
||||
@@ -243,9 +270,6 @@ public slots:
|
||||
|
||||
private:
|
||||
|
||||
template <typename TRequest>
|
||||
mtpRequestId sendFirst(const MTPInitConnection<TRequest> &request, RPCResponseHandler callbacks = RPCResponseHandler(), uint64 msCanWait = 0, uint32 layer = 0, bool toMainDC = false); // send first mtp request
|
||||
|
||||
typedef QList<MTProtoConnection*> MTProtoConnections;
|
||||
MTProtoConnections connections;
|
||||
|
||||
@@ -257,7 +281,7 @@ private:
|
||||
uint64 msSendCall, msWait;
|
||||
|
||||
QTimer timeouter;
|
||||
QTimer sender;
|
||||
SingleTimer sender;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -18,23 +18,19 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#pragma once
|
||||
|
||||
template <typename TRequest>
|
||||
mtpRequestId MTProtoSession::send(const TRequest &request, RPCResponseHandler callbacks, uint64 msCanWait, uint32 layer, bool toMainDC) {
|
||||
mtpRequestId MTProtoSession::send(const TRequest &request, RPCResponseHandler callbacks, uint64 msCanWait, bool needsLayer, bool toMainDC, mtpRequestId after) {
|
||||
mtpRequestId requestId = 0;
|
||||
if (layer && dc->needConnectionInit()) {
|
||||
MTPInitConnection<TRequest> requestWrap(MTPinitConnection<TRequest>(MTP_int(ApiId), MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(ApiLang), request));
|
||||
return sendFirst(requestWrap, callbacks, msCanWait, layer, toMainDC);
|
||||
}
|
||||
try {
|
||||
uint32 requestSize = request.size() >> 2;
|
||||
if (dc->connectionInited()) layer = 0;
|
||||
mtpRequest reqSerialized(mtpRequestData::prepare(requestSize + (layer ? 1 : 0)));
|
||||
if (layer) reqSerialized->push_back(mtpLayers[layer]);
|
||||
uint32 requestSize = request.innerLength() >> 2;
|
||||
mtpRequest reqSerialized(mtpRequestData::prepare(requestSize));
|
||||
request.write(*reqSerialized);
|
||||
|
||||
DEBUG_LOG(("MTP Info: adding request to toSendMap, msCanWait %1").arg(msCanWait));
|
||||
|
||||
reqSerialized->msDate = getms(); // > 0 - can send without container
|
||||
requestId = _mtp_internal::storeRequest(reqSerialized, callbacks);
|
||||
reqSerialized->msDate = getms(true); // > 0 - can send without container
|
||||
reqSerialized->needsLayer = needsLayer;
|
||||
if (after) reqSerialized->after = _mtp_internal::getRequest(after);
|
||||
requestId = _mtp_internal::storeRequest(reqSerialized, callbacks);
|
||||
|
||||
sendPrepared(reqSerialized, msCanWait);
|
||||
} catch (Exception &e) {
|
||||
@@ -44,43 +40,3 @@ mtpRequestId MTProtoSession::send(const TRequest &request, RPCResponseHandler ca
|
||||
if (requestId) _mtp_internal::registerRequest(requestId, toMainDC ? -getDC() : getDC());
|
||||
return requestId;
|
||||
}
|
||||
|
||||
class RPCWrappedDcDoneHandler : public RPCAbstractDoneHandler {
|
||||
public:
|
||||
RPCWrappedDcDoneHandler(const MTProtoDCPtr &dc, const RPCDoneHandlerPtr &ondone) : _dc(dc), _ondone(ondone) {
|
||||
}
|
||||
|
||||
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) const {
|
||||
_dc->setConnectionInited();
|
||||
if (_ondone) (*_ondone)(requestId, from, end);
|
||||
}
|
||||
|
||||
private:
|
||||
MTProtoDCPtr _dc;
|
||||
RPCDoneHandlerPtr _ondone;
|
||||
};
|
||||
|
||||
template <typename TRequest>
|
||||
mtpRequestId MTProtoSession::sendFirst(const MTPInitConnection<TRequest> &request, RPCResponseHandler callbacks, uint64 msCanWait, uint32 layer, bool toMainDC) {
|
||||
mtpRequestId requestId = 0;
|
||||
try {
|
||||
uint32 requestSize = request.size() >> 2;
|
||||
mtpRequest reqSerialized(mtpRequestData::prepare(requestSize + (layer ? 1 : 0)));
|
||||
if (layer) reqSerialized->push_back(mtpLayers[layer]);
|
||||
request.write(*reqSerialized);
|
||||
|
||||
DEBUG_LOG(("MTP Info: adding wrapped to init connection request to toSendMap, msCanWait %1").arg(msCanWait));
|
||||
callbacks.onDone = RPCDoneHandlerPtr(new RPCWrappedDcDoneHandler(dc, callbacks.onDone));
|
||||
reqSerialized->msDate = getms(); // > 0 - can send without container
|
||||
requestId = _mtp_internal::storeRequest(reqSerialized, callbacks);
|
||||
|
||||
sendPrepared(reqSerialized, msCanWait);
|
||||
} catch (Exception &e) {
|
||||
requestId = 0;
|
||||
_mtp_internal::rpcErrorOccured(requestId, callbacks, rpcClientError("NO_REQUEST_ID", QString("sendFirst() failed to queue request, exception: %1").arg(e.what())));
|
||||
}
|
||||
if (requestId) {
|
||||
_mtp_internal::registerRequest(requestId, toMainDC ? -getDC() : getDC());
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
/////////////////// Layer cons
|
||||
///////////////////////////////
|
||||
|
||||
|
||||
//invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
|
||||
//invokeAfterMsgs#3dc4b4f0 msg_ids:Vector<long> query:!X = X;
|
||||
//invokeWithLayer1#53835315 query:!X = X;
|
||||
@@ -31,6 +30,10 @@
|
||||
//invokeWithLayer13#427c8ea2 query:!X = X;
|
||||
//invokeWithLayer14#2b9b08fa query:!X = X;
|
||||
//invokeWithLayer15#b4418b64 query:!X = X;
|
||||
//invokeWithLayer16#cf5f0987 query:!X = X;
|
||||
//invokeWithLayer17#50858a19 query:!X = X;
|
||||
//invokeWithLayer18#1c900537 query:!X = X;
|
||||
//invokeWithLayer#da9b0d0d layer:int query:!X = X; // after 18 layer
|
||||
|
||||
///////////////////////////////
|
||||
/// Authorization key creation
|
||||
@@ -123,8 +126,6 @@ register.saveDeveloperInfo#9a5f6e95 name:string email:string phone_number:string
|
||||
|
||||
---types---
|
||||
|
||||
|
||||
|
||||
inputPeerEmpty#7f3b18ea = InputPeer;
|
||||
inputPeerSelf#7da07ec9 = InputPeer;
|
||||
inputPeerContact#1023dbe8 user_id:int = InputPeer;
|
||||
@@ -188,11 +189,11 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL
|
||||
fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
|
||||
|
||||
userEmpty#200250ba id:int = User;
|
||||
userSelf#720535ec id:int first_name:string last_name:string phone:string photo:UserProfilePhoto status:UserStatus inactive:Bool = User;
|
||||
userContact#f2fb8319 id:int first_name:string last_name:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User;
|
||||
userRequest#22e8ceb0 id:int first_name:string last_name:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User;
|
||||
userForeign#5214c89d id:int first_name:string last_name:string access_hash:long photo:UserProfilePhoto status:UserStatus = User;
|
||||
userDeleted#b29ad7cc id:int first_name:string last_name:string = User;
|
||||
userSelf#7007b451 id:int first_name:string last_name:string username:string phone:string photo:UserProfilePhoto status:UserStatus inactive:Bool = User;
|
||||
userContact#cab35e18 id:int first_name:string last_name:string username:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User;
|
||||
userRequest#d9ccc4ef id:int first_name:string last_name:string username:string access_hash:long phone:string photo:UserProfilePhoto status:UserStatus = User;
|
||||
userForeign#75cf7a8 id:int first_name:string last_name:string username:string access_hash:long photo:UserProfilePhoto status:UserStatus = User;
|
||||
userDeleted#d6016d7a id:int first_name:string last_name:string username:string = User;
|
||||
|
||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||
userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto;
|
||||
@@ -216,9 +217,9 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
|
||||
chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
|
||||
|
||||
messageEmpty#83e5de54 id:int = Message;
|
||||
message#22eb6aba id:int from_id:int to_id:Peer out:Bool unread:Bool date:int message:string media:MessageMedia = Message;
|
||||
messageForwarded#5f46804 id:int fwd_from_id:int fwd_date:int from_id:int to_id:Peer out:Bool unread:Bool date:int message:string media:MessageMedia = Message;
|
||||
messageService#9f8d60bb id:int from_id:int to_id:Peer out:Bool unread:Bool date:int action:MessageAction = Message;
|
||||
message#567699b3 flags:int id:int from_id:int to_id:Peer date:int message:string media:MessageMedia = Message;
|
||||
messageForwarded#a367e716 flags:int id:int fwd_from_id:int fwd_date:int from_id:int to_id:Peer date:int message:string media:MessageMedia = Message;
|
||||
messageService#1d86f70e flags:int id:int from_id:int to_id:Peer date:int action:MessageAction = Message;
|
||||
|
||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||
messageMediaPhoto#c8c45a2a photo:Photo = MessageMedia;
|
||||
@@ -284,11 +285,9 @@ importedContact#d0028438 user_id:int client_id:long = ImportedContact;
|
||||
|
||||
contactBlocked#561bc879 user_id:int date:int = ContactBlocked;
|
||||
|
||||
contactFound#ea879f95 user_id:int = ContactFound;
|
||||
|
||||
contactSuggested#3de191a1 user_id:int mutual_contacts:int = ContactSuggested;
|
||||
|
||||
contactStatus#aa77b873 user_id:int expires:int = ContactStatus;
|
||||
contactStatus#d3680c61 user_id:int status:UserStatus = ContactStatus;
|
||||
|
||||
chatLocated#3631cf4c chat_id:int distance:int = ChatLocated;
|
||||
|
||||
@@ -310,8 +309,6 @@ contacts.importedContacts#ad524315 imported:Vector<ImportedContact> retry_contac
|
||||
contacts.blocked#1c138d15 blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
|
||||
contacts.blockedSlice#900802a1 count:int blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
|
||||
|
||||
contacts.found#566000e results:Vector<ContactFound> users:Vector<User> = contacts.Found;
|
||||
|
||||
contacts.suggested#5649dcc5 results:Vector<ContactSuggested> users:Vector<User> = contacts.Suggested;
|
||||
|
||||
messages.dialogs#15ba6c40 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
|
||||
@@ -349,11 +346,11 @@ updateMessageID#4e90bfd6 id:int random_id:long = Update;
|
||||
updateReadMessages#c6649e31 messages:Vector<int> pts:int = Update;
|
||||
updateDeleteMessages#a92bfe26 messages:Vector<int> pts:int = Update;
|
||||
updateRestoreMessages#d15de04d messages:Vector<int> pts:int = Update;
|
||||
updateUserTyping#6baa8508 user_id:int = Update;
|
||||
updateChatUserTyping#3c46cfe6 chat_id:int user_id:int = Update;
|
||||
updateUserTyping#5c486927 user_id:int action:SendMessageAction = Update;
|
||||
updateChatUserTyping#9a65ea1f chat_id:int user_id:int action:SendMessageAction = Update;
|
||||
updateChatParticipants#7761198 participants:ChatParticipants = Update;
|
||||
updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update;
|
||||
updateUserName#da22d9ad user_id:int first_name:string last_name:string = Update;
|
||||
updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update;
|
||||
updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update;
|
||||
updateContactRegistered#2575bbb9 user_id:int date:int = Update;
|
||||
updateContactLink#51a48a9a user_id:int my_link:contacts.MyLink foreign_link:contacts.ForeignLink = Update;
|
||||
@@ -446,19 +443,6 @@ inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation
|
||||
encryptedMessage#ed18c118 random_id:long chat_id:int date:int bytes:bytes file:EncryptedFile = EncryptedMessage;
|
||||
encryptedMessageService#23734b06 random_id:long chat_id:int date:int bytes:bytes = EncryptedMessage;
|
||||
|
||||
decryptedMessageLayer#99a438cf layer:int message:DecryptedMessage = DecryptedMessageLayer;
|
||||
|
||||
decryptedMessage#1f814f1f random_id:long random_bytes:bytes message:string media:DecryptedMessageMedia = DecryptedMessage;
|
||||
decryptedMessageService#aa48327d random_id:long random_bytes:bytes action:DecryptedMessageAction = DecryptedMessage;
|
||||
|
||||
decryptedMessageMediaEmpty#89f5c4a = DecryptedMessageMedia;
|
||||
decryptedMessageMediaPhoto#32798a8c thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
|
||||
decryptedMessageMediaVideo#524a415d thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
|
||||
decryptedMessageMediaGeoPoint#35480a59 lat:double long:double = DecryptedMessageMedia;
|
||||
decryptedMessageMediaContact#588a0a97 phone_number:string first_name:string last_name:string user_id:int = DecryptedMessageMedia;
|
||||
|
||||
decryptedMessageActionSetMessageTTL#a1733aec ttl_seconds:int = DecryptedMessageAction;
|
||||
|
||||
messages.dhConfigNotModified#c0e24635 random:bytes = messages.DhConfig;
|
||||
messages.dhConfig#2c221edd g:int p:bytes version:int random:bytes = messages.DhConfig;
|
||||
|
||||
@@ -491,9 +475,6 @@ inputDocument#18798952 id:long access_hash:long = InputDocument;
|
||||
inputAudioFileLocation#74dc404d id:long access_hash:long = InputFileLocation;
|
||||
inputDocumentFileLocation#4e45abe9 id:long access_hash:long = InputFileLocation;
|
||||
|
||||
decryptedMessageMediaDocument#b095434b thumb:bytes thumb_w:int thumb_h:int file_name:string mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
|
||||
decryptedMessageMediaAudio#57e0a9cb duration:int mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
|
||||
|
||||
audioEmpty#586988d8 id:long = Audio;
|
||||
audio#c7ac6496 id:long access_hash:long user_id:int date:int duration:int mime_type:string size:int dc_id:int = Audio;
|
||||
|
||||
@@ -502,12 +483,6 @@ document#9efc6326 id:long access_hash:long user_id:int date:int file_name:string
|
||||
|
||||
help.support#17c6b5f6 phone_number:string user:User = help.Support;
|
||||
|
||||
decryptedMessageActionReadMessages#c4f40be random_ids:Vector<long> = DecryptedMessageAction;
|
||||
decryptedMessageActionDeleteMessages#65614304 random_ids:Vector<long> = DecryptedMessageAction;
|
||||
decryptedMessageActionScreenshotMessages#8ac1f475 random_ids:Vector<long> = DecryptedMessageAction;
|
||||
decryptedMessageActionFlushHistory#6719e45c = DecryptedMessageAction;
|
||||
decryptedMessageActionNotifyLayer#f3048883 layer:int = DecryptedMessageAction;
|
||||
|
||||
notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
|
||||
notifyUsers#b4c83b4c = NotifyPeer;
|
||||
notifyChats#c007cec3 = NotifyPeer;
|
||||
@@ -516,6 +491,53 @@ notifyAll#74d07c60 = NotifyPeer;
|
||||
updateUserBlocked#80ece81a user_id:int blocked:Bool = Update;
|
||||
updateNotifySettings#bec268ef peer:NotifyPeer notify_settings:PeerNotifySettings = Update;
|
||||
|
||||
auth.sentAppCode#e325edcf phone_registered:Bool phone_code_hash:string send_call_timeout:int is_password:Bool = auth.SentCode;
|
||||
|
||||
sendMessageTypingAction#16bf744e = SendMessageAction;
|
||||
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
|
||||
sendMessageRecordVideoAction#a187d66f = SendMessageAction;
|
||||
sendMessageUploadVideoAction#92042ff7 = SendMessageAction;
|
||||
sendMessageRecordAudioAction#d52f73f7 = SendMessageAction;
|
||||
sendMessageUploadAudioAction#e6ac8a6f = SendMessageAction;
|
||||
sendMessageUploadPhotoAction#990a3c1a = SendMessageAction;
|
||||
sendMessageUploadDocumentAction#8faee98e = SendMessageAction;
|
||||
sendMessageGeoLocationAction#176f8ba1 = SendMessageAction;
|
||||
sendMessageChooseContactAction#628cbc6f = SendMessageAction;
|
||||
|
||||
contactFound#ea879f95 user_id:int = ContactFound;
|
||||
|
||||
contacts.found#566000e results:Vector<ContactFound> users:Vector<User> = contacts.Found;
|
||||
|
||||
updateServiceNotification#382dd3e4 type:string message:string media:MessageMedia popup:Bool = Update;
|
||||
|
||||
userStatusRecently#e26f42f1 = UserStatus;
|
||||
userStatusLastWeek#7bf09fc = UserStatus;
|
||||
userStatusLastMonth#77ebc742 = UserStatus;
|
||||
|
||||
updatePrivacy#ee3b272a key:PrivacyKey rules:Vector<PrivacyRule> = Update;
|
||||
|
||||
inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey;
|
||||
|
||||
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
|
||||
|
||||
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
|
||||
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
|
||||
inputPrivacyValueAllowUsers#131cc67f users:Vector<InputUser> = InputPrivacyRule;
|
||||
inputPrivacyValueDisallowContacts#ba52007 = InputPrivacyRule;
|
||||
inputPrivacyValueDisallowAll#d66b66c9 = InputPrivacyRule;
|
||||
inputPrivacyValueDisallowUsers#90110467 users:Vector<InputUser> = InputPrivacyRule;
|
||||
|
||||
privacyValueAllowContacts#fffe1bac = PrivacyRule;
|
||||
privacyValueAllowAll#65427b82 = PrivacyRule;
|
||||
privacyValueAllowUsers#4d5bbe0c users:Vector<int> = PrivacyRule;
|
||||
privacyValueDisallowContacts#f888fa1a = PrivacyRule;
|
||||
privacyValueDisallowAll#8b73e763 = PrivacyRule;
|
||||
privacyValueDisallowUsers#c7f49b7 users:Vector<int> = PrivacyRule;
|
||||
|
||||
account.privacyRules#554abb6f rules:Vector<PrivacyRule> users:Vector<User> = account.PrivacyRules;
|
||||
|
||||
accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
|
||||
@@ -532,6 +554,7 @@ auth.resetAuthorizations#9fab0d1a = Bool;
|
||||
auth.sendInvites#771c1d97 phone_numbers:Vector<string> message:string = Bool;
|
||||
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
|
||||
auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
|
||||
auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
|
||||
|
||||
account.registerDevice#446c712c token_type:int token:string device_model:string system_version:string app_version:string app_sandbox:Bool lang_code:string = Bool;
|
||||
account.unregisterDevice#65c55b40 token_type:int token:string = Bool;
|
||||
@@ -548,24 +571,25 @@ users.getFullUser#ca30a5b1 id:InputUser = UserFull;
|
||||
contacts.getStatuses#c4a353ee = Vector<ContactStatus>;
|
||||
contacts.getContacts#22c6aa08 hash:string = contacts.Contacts;
|
||||
contacts.importContacts#da30b32d contacts:Vector<InputContact> replace:Bool = contacts.ImportedContacts;
|
||||
contacts.search#11f812d8 q:string limit:int = contacts.Found;
|
||||
contacts.getSuggested#cd773428 limit:int = contacts.Suggested;
|
||||
contacts.deleteContact#8e953744 id:InputUser = contacts.Link;
|
||||
contacts.deleteContacts#59ab389e id:Vector<InputUser> = Bool;
|
||||
contacts.block#332b49fc id:InputUser = Bool;
|
||||
contacts.unblock#e54100bd id:InputUser = Bool;
|
||||
contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked;
|
||||
contacts.exportCard#84e53737 = Vector<int>;
|
||||
contacts.importCard#4fe196fe export_card:Vector<int> = User;
|
||||
|
||||
messages.getMessages#4222fa74 id:Vector<int> = messages.Messages;
|
||||
messages.getDialogs#eccf1df6 offset:int max_id:int limit:int = messages.Dialogs;
|
||||
messages.getHistory#92a1df2f peer:InputPeer offset:int max_id:int limit:int = messages.Messages;
|
||||
messages.search#7e9f2ab peer:InputPeer q:string filter:MessagesFilter min_date:int max_date:int offset:int max_id:int limit:int = messages.Messages;
|
||||
messages.readHistory#b04f2510 peer:InputPeer max_id:int offset:int = messages.AffectedHistory;
|
||||
messages.readHistory#eed884c6 peer:InputPeer max_id:int offset:int read_contents:Bool = messages.AffectedHistory;
|
||||
messages.deleteHistory#f4f8fb61 peer:InputPeer offset:int = messages.AffectedHistory;
|
||||
messages.deleteMessages#14f2dd0a id:Vector<int> = Vector<int>;
|
||||
messages.restoreMessages#395f9d7e id:Vector<int> = Vector<int>;
|
||||
messages.receivedMessages#28abcb68 max_id:int = Vector<int>;
|
||||
messages.setTyping#719839e9 peer:InputPeer typing:Bool = Bool;
|
||||
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
|
||||
messages.sendMessage#4cde0aab peer:InputPeer message:string random_id:long = messages.SentMessage;
|
||||
messages.sendMedia#a3c85d76 peer:InputPeer media:InputMedia random_id:long = messages.StatedMessage;
|
||||
messages.forwardMessages#514cd10f peer:InputPeer id:Vector<int> = messages.StatedMessages;
|
||||
@@ -626,3 +650,18 @@ upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int
|
||||
initConnection#69796de9 api_id:int device_model:string system_version:string app_version:string lang_code:string query:!X = X;
|
||||
|
||||
help.getSupport#9cdf08cd = help.Support;
|
||||
|
||||
auth.sendSms#da9f3e8 phone_number:string phone_code_hash:string = Bool;
|
||||
|
||||
messages.readMessageContents#354b5bc2 id:Vector<int> = Vector<int>;
|
||||
|
||||
account.checkUsername#2714d86c username:string = Bool;
|
||||
account.updateUsername#3e0bdd7c username:string = User;
|
||||
|
||||
contacts.search#11f812d8 q:string limit:int = contacts.Found;
|
||||
|
||||
account.getPrivacy#dadbc950 key:InputPrivacyKey = account.PrivacyRules;
|
||||
account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> = account.PrivacyRules;
|
||||
account.deleteAccount#418d4e0b reason:string = Bool;
|
||||
account.getAccountTTL#8fc711d = AccountDaysTTL;
|
||||
account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool;
|
||||
|
||||
@@ -40,6 +40,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, const
|
||||
, _hist(App::history(peer->id))
|
||||
, _photosInRow(1)
|
||||
, _photosToAdd(0)
|
||||
, _selMode(false)
|
||||
, _width(0)
|
||||
, _height(0)
|
||||
, _minHeight(0)
|
||||
@@ -425,7 +426,7 @@ void OverviewInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton bu
|
||||
dragActionUpdate(screenPos);
|
||||
|
||||
if (textlnkOver()) {
|
||||
if (textlnkDown() == textlnkOver() && _dragAction != Dragging) {
|
||||
if (textlnkDown() == textlnkOver() && _dragAction != Dragging && !_selMode) {
|
||||
needClick = textlnkDown();
|
||||
}
|
||||
}
|
||||
@@ -553,10 +554,14 @@ QPixmap OverviewInner::genPix(PhotoData *photo, int32 size) {
|
||||
if (!photo->full->loaded() && !photo->medium->loaded()) {
|
||||
img = imageBlur(img);
|
||||
}
|
||||
if (img.width() > img.height()) {
|
||||
img = img.scaled(img.width() * size / img.height(), size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
if (img.width() == img.height()) {
|
||||
if (img.width() != size) {
|
||||
img = img.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
}
|
||||
} else if (img.width() > img.height()) {
|
||||
img = img.copy((img.width() - img.height()) / 2, 0, img.height(), img.height()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
} else {
|
||||
img = img.scaled(size, img.height() * size / img.width(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
img = img.copy(0, (img.height() - img.width()) / 2, img.width(), img.width()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
}
|
||||
img.setDevicePixelRatio(cRetinaFactor());
|
||||
photo->forget();
|
||||
@@ -625,26 +630,13 @@ void OverviewInner::paintEvent(QPaintEvent *e) {
|
||||
it->vsize = _vsize;
|
||||
it->pix = genPix(photo, _vsize);
|
||||
}
|
||||
QPixmap &pix(it->pix);
|
||||
QPoint pos(int32(i * w + st::overviewPhotoSkip), _addToY + row * (_vsize + st::overviewPhotoSkip) + st::overviewPhotoSkip);
|
||||
int32 w = pix.width(), h = pix.height(), size;
|
||||
if (w == h) {
|
||||
p.drawPixmap(pos, pix);
|
||||
size = w;
|
||||
} else if (w > h) {
|
||||
p.drawPixmap(pos, pix, QRect((w - h) / 2, 0, h, h));
|
||||
size = h;
|
||||
} else {
|
||||
p.drawPixmap(pos, pix, QRect(0, (h - w) / 2, w, w));
|
||||
size = w;
|
||||
}
|
||||
size /= cIntRetinaFactor();
|
||||
|
||||
p.drawPixmap(pos, it->pix);
|
||||
if (!quality) {
|
||||
uint64 dt = itemAnimations().animate(item, getms());
|
||||
int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta);
|
||||
|
||||
int32 x = pos.x() + (size - st::overviewLoader.width()) / 2, y = pos.y() + (size - st::overviewLoader.height()) / 2;
|
||||
int32 x = pos.x() + (_vsize - st::overviewLoader.width()) / 2, y = pos.y() + (_vsize - st::overviewLoader.height()) / 2;
|
||||
p.fillRect(x, y, st::overviewLoader.width(), st::overviewLoader.height(), st::photoLoaderBg->b);
|
||||
x += (st::overviewLoader.width() - cnt * st::overviewLoaderPoint.width() - (cnt - 1) * st::overviewLoaderSkip) / 2;
|
||||
y += (st::overviewLoader.height() - st::overviewLoaderPoint.height()) / 2;
|
||||
@@ -671,7 +663,10 @@ void OverviewInner::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
if (sel == FullItemSel) {
|
||||
p.fillRect(QRect(pos.x(), pos.y(), size / cIntRetinaFactor(), size / cIntRetinaFactor()), st::msgInSelectOverlay->b);
|
||||
p.fillRect(QRect(pos.x(), pos.y(), _vsize, _vsize), st::overviewPhotoSelectOverlay->b);
|
||||
p.drawPixmap(QPoint(pos.x() + _vsize - st::overviewPhotoChecked.pxWidth(), pos.y() + _vsize - st::overviewPhotoChecked.pxHeight()), App::sprite(), st::overviewPhotoChecked);
|
||||
} else if (_selMode/* || (selfrom < count && selfrom <= selto && 0 <= selto)*/) {
|
||||
p.drawPixmap(QPoint(pos.x() + _vsize - st::overviewPhotoChecked.pxWidth(), pos.y() + _vsize - st::overviewPhotoChecked.pxHeight()), App::sprite(), st::overviewPhotoCheck);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
@@ -1145,6 +1140,10 @@ void OverviewInner::switchType(MediaOverviewType type) {
|
||||
if (App::wnd()) App::wnd()->update();
|
||||
}
|
||||
|
||||
void OverviewInner::setSelectMode(bool enabled) {
|
||||
_selMode = enabled;
|
||||
}
|
||||
|
||||
void OverviewInner::openContextUrl() {
|
||||
HistoryItem *was = App::hoveredLinkItem();
|
||||
App::hoveredLinkItem(App::contextItem());
|
||||
@@ -1451,6 +1450,7 @@ void OverviewInner::itemResized(HistoryItem *item) {
|
||||
if (_addToY + _height - _items[i].y < _scroll->scrollTop()) {
|
||||
_scroll->scrollToY(_addToY + _height - _items[i].y);
|
||||
}
|
||||
parentWidget()->update();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1628,6 +1628,8 @@ MediaOverviewType OverviewWidget::type() const {
|
||||
}
|
||||
|
||||
void OverviewWidget::switchType(MediaOverviewType type) {
|
||||
_selCount = 0;
|
||||
_inner.setSelectMode(false);
|
||||
_inner.switchType(type);
|
||||
switch (type) {
|
||||
case OverviewPhotos: _header = lang(lng_profile_photos_header); break;
|
||||
@@ -1636,7 +1638,6 @@ void OverviewWidget::switchType(MediaOverviewType type) {
|
||||
case OverviewAudios: _header = lang(lng_profile_audios_header); break;
|
||||
}
|
||||
noSelectingScroll();
|
||||
_selCount = 0;
|
||||
App::main()->topBar()->showSelected(0);
|
||||
updateTopBarSelection();
|
||||
_scroll.scrollToY(_scroll.scrollTopMax());
|
||||
@@ -1647,6 +1648,7 @@ void OverviewWidget::updateTopBarSelection() {
|
||||
int32 selectedForForward, selectedForDelete;
|
||||
_inner.getSelectionState(selectedForForward, selectedForDelete);
|
||||
_selCount = selectedForDelete ? selectedForDelete : selectedForForward;
|
||||
_inner.setSelectMode(_selCount > 0);
|
||||
if (App::main()) {
|
||||
App::main()->topBar()->showSelected(_selCount > 0 ? _selCount : 0);
|
||||
App::main()->topBar()->update();
|
||||
@@ -1742,7 +1744,7 @@ void OverviewWidget::itemRemoved(HistoryItem *row) {
|
||||
}
|
||||
|
||||
void OverviewWidget::itemResized(HistoryItem *row) {
|
||||
if (row->history()->peer == peer()) {
|
||||
if (!row || row->history()->peer == peer()) {
|
||||
_inner.itemResized(row);
|
||||
}
|
||||
}
|
||||
@@ -1838,6 +1840,9 @@ void OverviewWidget::onDeleteSelectedSure() {
|
||||
for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) {
|
||||
i.value()->destroy();
|
||||
}
|
||||
if (App::main() && App::main()->peer() == peer()) {
|
||||
App::main()->itemResized(0);
|
||||
}
|
||||
App::wnd()->hideLayer();
|
||||
}
|
||||
|
||||
@@ -1848,9 +1853,12 @@ void OverviewWidget::onDeleteContextSure() {
|
||||
}
|
||||
|
||||
if (item->id > 0) {
|
||||
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(QVector<MTPint>(1, MTP_int(item->id)))));
|
||||
MTP::send(MTPmessages_DeleteMessages(MTP_vector<MTPint>(1, MTP_int(item->id))));
|
||||
}
|
||||
item->destroy();
|
||||
if (App::main() && App::main()->peer() == peer()) {
|
||||
App::main()->itemResized(0);
|
||||
}
|
||||
App::wnd()->hideLayer();
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ public:
|
||||
MediaOverviewType type() const;
|
||||
void switchType(MediaOverviewType type);
|
||||
|
||||
void setSelectMode(bool enabled);
|
||||
|
||||
void mediaOverviewUpdated();
|
||||
void changingMsgId(HistoryItem *row, MsgId newId);
|
||||
void msgUpdated(const HistoryItem *msg);
|
||||
@@ -124,6 +126,7 @@ private:
|
||||
} CachedSize;
|
||||
typedef QMap<PhotoData*, CachedSize> CachedSizes;
|
||||
CachedSizes _cached;
|
||||
bool _selMode;
|
||||
|
||||
// other
|
||||
typedef struct _CachedItem {
|
||||
|
||||
@@ -252,7 +252,7 @@ void ProfileInner::gotFullUser(const MTPUserFull &user) {
|
||||
_loadingId = 0;
|
||||
const MTPDuserFull &d(user.c_userFull());
|
||||
App::feedPhoto(d.vprofile_photo);
|
||||
App::feedUsers(MTP_vector<MTPUser>(QVector<MTPUser>(1, d.vuser)));
|
||||
App::feedUsers(MTP_vector<MTPUser>(1, d.vuser));
|
||||
PhotoData *userPhoto = _peerUser->photoId ? App::photo(_peerUser->photoId) : 0;
|
||||
if (userPhoto && userPhoto->date) {
|
||||
_photoLink = TextLinkPtr(new PhotoLink(userPhoto, _peer));
|
||||
@@ -340,13 +340,13 @@ void ProfileInner::reorderParticipants() {
|
||||
UserData *self = App::self();
|
||||
for (ChatData::Participants::const_iterator i = _peerChat->participants.cbegin(), e = _peerChat->participants.cend(); i != e; ++i) {
|
||||
UserData *user = i.key();
|
||||
int32 until = user->onlineTill;
|
||||
int32 until = App::onlineForSort(user->onlineTill, t);
|
||||
Participants::iterator before = _participants.begin();
|
||||
if (user != self) {
|
||||
if (before != _participants.end() && (*before) == self) {
|
||||
++before;
|
||||
}
|
||||
while (before != _participants.end() && (*before)->onlineTill >= until) {
|
||||
while (before != _participants.end() && App::onlineForSort((*before)->onlineTill, t) >= until) {
|
||||
++before;
|
||||
}
|
||||
}
|
||||
@@ -414,21 +414,27 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
|
||||
_nameText.drawElided(p, _left + st::profilePhotoSize + st::profileNameLeft, top + st::profileNameTop, _width - st::profilePhotoSize - st::profileNameLeft);
|
||||
|
||||
p.setFont(st::profileStatusFont->f);
|
||||
int32 addbyname = 0;
|
||||
if (_peerUser && !_peerUser->username.isEmpty()) {
|
||||
addbyname = st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent);
|
||||
p.setPen(st::black->p);
|
||||
p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + st::profileStatusTop + st::linkFont->ascent, '@' + _peerUser->username);
|
||||
}
|
||||
p.setPen((_peerUser && _peerUser->onlineTill >= l_time ? st::profileOnlineColor : st::profileOfflineColor)->p);
|
||||
p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + st::profileStatusTop + st::linkFont->ascent, _onlineText);
|
||||
p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop + st::linkFont->ascent, _onlineText);
|
||||
if (!_cancelPhoto.isHidden()) {
|
||||
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _cancelPhoto.y() + st::linkFont->ascent, lang(lng_settings_uploading_photo));
|
||||
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _cancelPhoto.y() + addbyname + st::linkFont->ascent, lang(lng_settings_uploading_photo));
|
||||
}
|
||||
|
||||
if (!_errorText.isEmpty()) {
|
||||
p.setFont(st::setErrFont->f);
|
||||
p.setPen(st::setErrColor->p);
|
||||
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profilePhoneTop + st::profilePhoneFont->ascent, _errorText);
|
||||
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, top + addbyname + st::profilePhoneTop + st::profilePhoneFont->ascent, _errorText);
|
||||
}
|
||||
if (!_phoneText.isEmpty()) {
|
||||
p.setPen(st::black->p);
|
||||
p.setFont(st::linkFont->f);
|
||||
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profilePhoneTop + st::profilePhoneFont->ascent, _phoneText);
|
||||
p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, top + addbyname + st::profilePhoneTop + st::profilePhoneFont->ascent, _phoneText);
|
||||
}
|
||||
top += st::profilePhotoSize;
|
||||
top += st::profileButtonTop;
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace {
|
||||
};
|
||||
|
||||
PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/iconround256.png")) {
|
||||
posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/iconround256.png")), wndIcon(QPixmap::fromImage(icon256)) {
|
||||
connect(&psIdleTimer, SIGNAL(timeout()), this, SLOT(psIdleTimeout()));
|
||||
psIdleTimer.setSingleShot(false);
|
||||
}
|
||||
@@ -115,6 +115,8 @@ void PsMainWindow::psUpdateWorkmode() {
|
||||
}
|
||||
|
||||
void PsMainWindow::psUpdateCounter() {
|
||||
setWindowIcon(wndIcon);
|
||||
|
||||
int32 counter = App::histories().unreadFull;
|
||||
|
||||
setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram"));
|
||||
@@ -293,6 +295,14 @@ QRect psDesktopRect() {
|
||||
return _monitorRect;
|
||||
}
|
||||
|
||||
void psShowOverAll(QWidget *w, bool canFocus) {
|
||||
w->show();
|
||||
}
|
||||
|
||||
void psBringToBack(QWidget *w) {
|
||||
w->hide();
|
||||
}
|
||||
|
||||
void PsMainWindow::psActivateNotify(NotifyWindow *w) {
|
||||
}
|
||||
|
||||
@@ -950,8 +960,9 @@ void psOpenFile(const QString &name, bool openWith) {
|
||||
}
|
||||
|
||||
void psShowInFolder(const QString &name) {
|
||||
QDesktopServices::openUrl(QFileInfo(name).absoluteDir().absolutePath());
|
||||
// system(("nautilus " + QFileInfo(name).absoluteDir().absolutePath()).toUtf8().constData());
|
||||
// QDesktopServices::openUrl(QFileInfo(name).absoluteDir().absolutePath());
|
||||
App::wnd()->layerHidden();
|
||||
system(("nautilus \"" + QFileInfo(name).absoluteDir().absolutePath() + "\"").toUtf8().constData());
|
||||
//objc_showInFinder(name, QFileInfo(name).absolutePath());
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ protected:
|
||||
QSystemTrayIcon *trayIcon;
|
||||
QMenu *trayIconMenu;
|
||||
QImage icon256;
|
||||
QIcon wndIcon;
|
||||
|
||||
virtual void setupTrayIcon() = 0;
|
||||
virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0;
|
||||
@@ -170,6 +171,8 @@ void psAutoStart(bool start, bool silent = false);
|
||||
void psSendToMenu(bool send, bool silent = false);
|
||||
|
||||
QRect psDesktopRect();
|
||||
void psShowOverAll(QWidget *w, bool canFocus = true);
|
||||
void psBringToBack(QWidget *w);
|
||||
|
||||
int psCleanup();
|
||||
int psFixPrevious();
|
||||
|
||||
@@ -21,6 +21,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#include "lang.h"
|
||||
#include "application.h"
|
||||
#include "mainwidget.h"
|
||||
#include "historywidget.h"
|
||||
|
||||
namespace {
|
||||
bool frameless = true;
|
||||
@@ -48,6 +49,12 @@ void MacPrivate::activeSpaceChanged() {
|
||||
}
|
||||
}
|
||||
|
||||
void MacPrivate::darkModeChanged() {
|
||||
if (App::wnd()) {
|
||||
App::wnd()->psUpdateCounter();
|
||||
}
|
||||
}
|
||||
|
||||
void MacPrivate::notifyClicked(unsigned long long peer) {
|
||||
History *history = App::history(PeerId(peer));
|
||||
|
||||
@@ -64,7 +71,8 @@ void MacPrivate::notifyReplied(unsigned long long peer, const char *str) {
|
||||
}
|
||||
|
||||
PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/iconround256.png")) {
|
||||
posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/iconround256.png")), wndIcon(QPixmap(qsl(":/gui/art/iconbig128.png"))),
|
||||
psLogout(0), psUndo(0), psRedo(0), psCut(0), psCopy(0), psPaste(0), psDelete(0), psSelectAll(0), psContacts(0), psNewGroup(0), psShowTelegram(0) {
|
||||
QImage tray(qsl(":/gui/art/osxtray.png"));
|
||||
trayImg = tray.copy(0, cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4));
|
||||
trayImgSel = tray.copy(tray.width() / (cRetina() ? 2 : 4), cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4));
|
||||
@@ -141,6 +149,7 @@ void PsMainWindow::psUpdateWorkmode() {
|
||||
}
|
||||
trayIcon = 0;
|
||||
}
|
||||
setWindowIcon(wndIcon);
|
||||
}
|
||||
|
||||
void _placeCounter(QImage &img, int size, int count, style::color bg, style::color color) {
|
||||
@@ -182,18 +191,20 @@ void PsMainWindow::psUpdateCounter() {
|
||||
int32 counter = App::histories().unreadFull;
|
||||
|
||||
setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram"));
|
||||
setWindowIcon(wndIcon);
|
||||
|
||||
QString cnt = (counter < 1000) ? QString("%1").arg(counter) : QString("..%1").arg(counter % 100, 2, 10, QChar('0'));
|
||||
_private.setWindowBadge(counter ? cnt : QString());
|
||||
|
||||
if (trayIcon) {
|
||||
style::color bg = (App::histories().unreadMuted < counter) ? st::counterBG : st::counterMuteBG;
|
||||
bool dm = objc_darkMode(), important = (App::histories().unreadMuted < counter);
|
||||
style::color bg = important ? st::counterBG : st::counterMuteBG;
|
||||
QIcon icon;
|
||||
QImage img(psTrayIcon()), imgsel(psTrayIcon(true));
|
||||
QImage img(psTrayIcon(dm)), imgsel(psTrayIcon(true));
|
||||
img.detach();
|
||||
imgsel.detach();
|
||||
int32 size = cRetina() ? 44 : 22;
|
||||
_placeCounter(img, size, counter, bg, st::counterColor);
|
||||
_placeCounter(img, size, counter, bg, (dm && !important) ? st::counterMacInvColor : st::counterColor);
|
||||
_placeCounter(imgsel, size, counter, st::white, st::counterMacInvColor);
|
||||
icon.addPixmap(QPixmap::fromImage(img));
|
||||
icon.addPixmap(QPixmap::fromImage(imgsel), QIcon::Selected);
|
||||
@@ -335,6 +346,81 @@ void PsMainWindow::psFirstShow() {
|
||||
show();
|
||||
}
|
||||
posInited = true;
|
||||
|
||||
// init global menu
|
||||
QMenu *main = psMainMenu.addMenu(qsl("Telegram"));
|
||||
main->addAction(lang(lng_mac_menu_about), App::wnd()->getTitle(), SLOT(onAbout()))->setMenuRole(QAction::AboutQtRole);
|
||||
main->addSeparator();
|
||||
QAction *prefs = main->addAction(lang(lng_mac_menu_preferences), App::wnd(), SLOT(showSettings()));
|
||||
prefs->setMenuRole(QAction::PreferencesRole);
|
||||
|
||||
QMenu *file = psMainMenu.addMenu(lang(lng_mac_menu_file));
|
||||
psLogout = file->addAction(lang(lng_mac_menu_logout), App::wnd(), SLOT(onLogout()));
|
||||
|
||||
QMenu *edit = psMainMenu.addMenu(lang(lng_mac_menu_edit));
|
||||
psUndo = edit->addAction(lang(lng_mac_menu_undo), this, SLOT(psMacUndo()), QKeySequence::Undo);
|
||||
psRedo = edit->addAction(lang(lng_mac_menu_redo), this, SLOT(psMacRedo()), QKeySequence::Redo);
|
||||
edit->addSeparator();
|
||||
psCut = edit->addAction(lang(lng_mac_menu_cut), this, SLOT(psMacCut()), QKeySequence::Cut);
|
||||
psCopy = edit->addAction(lang(lng_mac_menu_copy), this, SLOT(psMacCopy()), QKeySequence::Copy);
|
||||
psPaste = edit->addAction(lang(lng_mac_menu_paste), this, SLOT(psMacPaste()), QKeySequence::Paste);
|
||||
psDelete = edit->addAction(lang(lng_mac_menu_delete), this, SLOT(psMacDelete()), QKeySequence(Qt::ControlModifier | Qt::Key_Backspace));
|
||||
edit->addSeparator();
|
||||
psSelectAll = edit->addAction(lang(lng_mac_menu_select_all), this, SLOT(psMacSelectAll()), QKeySequence::SelectAll);
|
||||
|
||||
QMenu *window = psMainMenu.addMenu(lang(lng_mac_menu_window));
|
||||
psContacts = window->addAction(lang(lng_mac_menu_contacts), App::wnd()->getTitle(), SLOT(onContacts()));
|
||||
window->addSeparator();
|
||||
psNewGroup = window->addAction(lang(lng_mac_menu_new_group), App::wnd(), SLOT(onShowNewGroup()));
|
||||
window->addSeparator();
|
||||
psShowTelegram = window->addAction(lang(lng_mac_menu_show), App::wnd(), SLOT(showFromTray()));
|
||||
|
||||
psMacUpdateMenu();
|
||||
}
|
||||
|
||||
namespace {
|
||||
void _sendKeySequence(Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier) {
|
||||
QWidget *focused = QApplication::focusWidget();
|
||||
if (qobject_cast<QLineEdit*>(focused) || qobject_cast<FlatTextarea*>(focused) || qobject_cast<HistoryList*>(focused)) {
|
||||
QApplication::postEvent(focused, new QKeyEvent(QEvent::KeyPress, key, modifiers));
|
||||
QApplication::postEvent(focused, new QKeyEvent(QEvent::KeyRelease, key, modifiers));
|
||||
}
|
||||
}
|
||||
void _forceDisabled(QAction *action, bool disabled) {
|
||||
if (action->isEnabled()) {
|
||||
if (disabled) action->setDisabled(true);
|
||||
} else if (!disabled) {
|
||||
action->setDisabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PsMainWindow::psMacUndo() {
|
||||
_sendKeySequence(Qt::Key_Z, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void PsMainWindow::psMacRedo() {
|
||||
_sendKeySequence(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier);
|
||||
}
|
||||
|
||||
void PsMainWindow::psMacCut() {
|
||||
_sendKeySequence(Qt::Key_X, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void PsMainWindow::psMacCopy() {
|
||||
_sendKeySequence(Qt::Key_C, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void PsMainWindow::psMacPaste() {
|
||||
_sendKeySequence(Qt::Key_V, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void PsMainWindow::psMacDelete() {
|
||||
_sendKeySequence(Qt::Key_Delete);
|
||||
}
|
||||
|
||||
void PsMainWindow::psMacSelectAll() {
|
||||
_sendKeySequence(Qt::Key_A, Qt::ControlModifier);
|
||||
}
|
||||
|
||||
bool PsMainWindow::psHandleTitle() {
|
||||
@@ -350,6 +436,40 @@ void PsMainWindow::psUpdateSysMenu(Qt::WindowState state) {
|
||||
void PsMainWindow::psUpdateMargins() {
|
||||
}
|
||||
|
||||
void PsMainWindow::psMacUpdateMenu() {
|
||||
if (!posInited) return;
|
||||
|
||||
QWidget *focused = QApplication::focusWidget();
|
||||
bool isLogged = !!App::self(), canUndo = false, canRedo = false, canCut = false, canCopy = false, canPaste = false, canDelete = false, canSelectAll = false;
|
||||
if (QLineEdit *edit = qobject_cast<QLineEdit*>(focused)) {
|
||||
canCut = canCopy = canDelete = edit->hasSelectedText();
|
||||
canSelectAll = !edit->text().isEmpty();
|
||||
canUndo = edit->isUndoAvailable();
|
||||
canRedo = edit->isRedoAvailable();
|
||||
canPaste = !App::app()->clipboard()->text().isEmpty();
|
||||
} else if (FlatTextarea *edit = qobject_cast<FlatTextarea*>(focused)) {
|
||||
canCut = canCopy = canDelete = edit->textCursor().hasSelection();
|
||||
canSelectAll = edit->hasText();
|
||||
canUndo = edit->isUndoAvailable();
|
||||
canRedo = edit->isRedoAvailable();
|
||||
canPaste = !App::app()->clipboard()->text().isEmpty();
|
||||
} else if (HistoryList *list = qobject_cast<HistoryList*>(focused)) {
|
||||
canCopy = list->canCopySelected();
|
||||
canDelete = list->canDeleteSelected();
|
||||
}
|
||||
_forceDisabled(psLogout, !isLogged);
|
||||
_forceDisabled(psUndo, !canUndo);
|
||||
_forceDisabled(psRedo, !canRedo);
|
||||
_forceDisabled(psCut, !canCut);
|
||||
_forceDisabled(psCopy, !canCopy);
|
||||
_forceDisabled(psPaste, !canPaste);
|
||||
_forceDisabled(psDelete, !canDelete);
|
||||
_forceDisabled(psSelectAll, !canSelectAll);
|
||||
_forceDisabled(psContacts, !isLogged);
|
||||
_forceDisabled(psNewGroup, !isLogged);
|
||||
_forceDisabled(psShowTelegram, psIsActive());
|
||||
}
|
||||
|
||||
void PsMainWindow::psFlash() {
|
||||
_private.startBounce();
|
||||
}
|
||||
@@ -363,7 +483,7 @@ void PsMainWindow::psClearNotifies(PeerId peerId) {
|
||||
}
|
||||
|
||||
void PsMainWindow::psActivateNotify(NotifyWindow *w) {
|
||||
_private.activateWnd(w->winId());
|
||||
objc_activateWnd(w->winId());
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -372,7 +492,7 @@ namespace {
|
||||
}
|
||||
|
||||
QRect psDesktopRect() {
|
||||
uint64 tnow = getms();
|
||||
uint64 tnow = getms(true);
|
||||
if (tnow > _monitorLastGot + 1000 || tnow < _monitorLastGot) {
|
||||
_monitorLastGot = tnow;
|
||||
_monitorRect = QApplication::desktop()->availableGeometry(App::wnd());
|
||||
@@ -380,11 +500,19 @@ QRect psDesktopRect() {
|
||||
return _monitorRect;
|
||||
}
|
||||
|
||||
void psShowOverAll(QWidget *w, bool canFocus) {
|
||||
objc_showOverAll(w->winId(), canFocus);
|
||||
}
|
||||
|
||||
void psBringToBack(QWidget *w) {
|
||||
objc_bringToBack(w->winId());
|
||||
}
|
||||
|
||||
void PsMainWindow::psNotifyShown(NotifyWindow *w) {
|
||||
w->hide();
|
||||
_private.holdOnTop(w->winId());
|
||||
objc_holdOnTop(w->winId());
|
||||
w->show();
|
||||
_private.showOverAll(w->winId());
|
||||
psShowOverAll(w, false);
|
||||
}
|
||||
|
||||
void PsMainWindow::psPlatformNotify(HistoryItem *item) {
|
||||
@@ -394,6 +522,16 @@ void PsMainWindow::psPlatformNotify(HistoryItem *item) {
|
||||
_private.showNotify(item->history()->peer->id, title, subtitle, msg, (cNotifyView() <= dbinvShowPreview));
|
||||
}
|
||||
|
||||
bool PsMainWindow::eventFilter(QObject *obj, QEvent *evt) {
|
||||
QEvent::Type t = evt->type();
|
||||
if (t == QEvent::FocusIn || t == QEvent::FocusOut) {
|
||||
if (qobject_cast<QLineEdit*>(obj) || qobject_cast<FlatTextarea*>(obj) || qobject_cast<HistoryList*>(obj)) {
|
||||
psMacUpdateMenu();
|
||||
}
|
||||
}
|
||||
return QMainWindow::eventFilter(obj, evt);
|
||||
}
|
||||
|
||||
PsApplication::PsApplication(int &argc, char **argv) : QApplication(argc, argv) {
|
||||
}
|
||||
|
||||
|
||||