Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ce8d9f0b7 | ||
|
|
b23ffe6c94 | ||
|
|
276ef42c8f | ||
|
|
79a41d541d | ||
|
|
61e3f9000b | ||
|
|
a4c13e0720 | ||
|
|
a09460dc84 | ||
|
|
4bcfee22ef | ||
|
|
880c2697d1 | ||
|
|
188e1b61c5 | ||
|
|
696c5df092 | ||
|
|
408b38b41f | ||
|
|
4a6b6fad77 | ||
|
|
f7fa13899f | ||
|
|
f370e2b85d | ||
|
|
5d649f750b | ||
|
|
cfcf4d2336 | ||
|
|
922ab40c75 | ||
|
|
2532245413 | ||
|
|
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 | ||
|
|
09e2fbaa6b | ||
|
|
3009200b76 | ||
|
|
e0910bfb3e | ||
|
|
d874609816 | ||
|
|
aebe171f55 | ||
|
|
c3a5194a6c | ||
|
|
9a3ea063c8 | ||
|
|
276e122b62 | ||
|
|
2c03abaa8e | ||
|
|
fe87d45221 |
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 @@
|
||||
AppVersion=`./Version.sh | awk -F " " '{print $1}'`
|
||||
AppVersionStr=`./Version.sh | awk -F " " '{print $2}'`
|
||||
|
||||
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 @@
|
||||
AppVersion=`./Version.sh | awk -F " " '{print $1}'`
|
||||
AppVersionStr=`./Version.sh | awk -F " " '{print $2}'`
|
||||
|
||||
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 @@
|
||||
AppVersion=`./Version.sh | awk -F " " '{print $1}'`
|
||||
AppVersionStr=`./Version.sh | awk -F " " '{print $2}'`
|
||||
|
||||
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 @@
|
||||
AppVersion=`./Version.sh | awk -F " " '{print $1}'`
|
||||
AppVersionStr=`./Version.sh | awk -F " " '{print $2}'`
|
||||
|
||||
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.0
|
||||
AppVersion=6000
|
||||
AppVersion=`./Version.sh | awk -F " " '{print $1}'`
|
||||
AppVersionStr=`./Version.sh | awk -F " " '{print $2}'`
|
||||
|
||||
if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then
|
||||
echo "Deploy folder for version $AppVersionStr already exists!"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
AppVersionStr=0.6.0
|
||||
AppVersion=6000
|
||||
AppVersion=`./Version.sh | awk -F " " '{print $1}'`
|
||||
AppVersionStr=`./Version.sh | awk -F " " '{print $2}'`
|
||||
|
||||
if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then
|
||||
echo "Deploy folder for version $AppVersionStr already exists!"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
AppVersionStr=0.6.0
|
||||
AppVersion=6000
|
||||
AppVersion=`./Version.sh | awk -F " " '{print $1}'`
|
||||
AppVersionStr=`./Version.sh | awk -F " " '{print $2}'`
|
||||
|
||||
echo ""
|
||||
echo "Preparing version $AppVersionStr.."
|
||||
echo ""
|
||||
|
||||
if [ -d "./../Mac/Release/deploy/$AppVersionStr" ]; then
|
||||
echo "Deploy folder for version $AppVersionStr already exists!"
|
||||
@@ -16,11 +20,6 @@ if [ ! -d "./../Mac/Release/Telegram.app" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "./../Mac/Release/Telegram.app/Contents/_CodeSignature" ]; then
|
||||
echo "Telegram signature not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "./../Mac/Release/Telegram.app/Contents/Resources/Icon.icns" ]; then
|
||||
echo "Icon.icns not found in Resources!"
|
||||
exit 1
|
||||
@@ -36,18 +35,26 @@ 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!"
|
||||
cd ./../Mac/Release && codesign --force --deep --sign "Developer ID Application: John Preston" Telegram.app && cd ./../../Telegram
|
||||
|
||||
if [ ! -d "./../Mac/Release/Telegram.app/Contents/_CodeSignature" ]; then
|
||||
echo "Telegram signature not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Preparing version $AppVersionStr, executing Packer.."
|
||||
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 +62,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,49 @@
|
||||
@echo OFF
|
||||
|
||||
set "AppVersionStrSmall=0.6.13"
|
||||
set "AppVersionStr=0.6.13"
|
||||
set "AppVersionStrFull=0.6.13.0"
|
||||
|
||||
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 /dMyAppVersion=%AppVersionStrSmall% /dMyAppVersionZero=%AppVersionStr% /dMyAppFullVersion=%AppVersionStrFull% ..\..\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,15 @@ 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_service_notifications: "service notifications";
|
||||
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 +79,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 +115,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 +142,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 +175,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,10 +216,19 @@ 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 :(";
|
||||
|
||||
lng_settings_section_cache: "Local storage";
|
||||
lng_settings_no_images_cached: "No cached images found!";
|
||||
lng_settings_image_cached: "Cached: {count} image, {size}";
|
||||
lng_settings_images_cached: "Cached: {count} images, {size}";
|
||||
lng_local_images_clear: "Clear All";
|
||||
lng_local_images_clearing: "Clearing..";
|
||||
lng_local_images_cleared: "Cleared!";
|
||||
lng_local_images_clear_failed: "Clear failed :(";
|
||||
|
||||
lng_settings_section_advanced: "Advanced";
|
||||
lng_connection_type: "Connection type:";
|
||||
lng_connection_auto_connecting: "Default (connecting..)";
|
||||
@@ -214,6 +247,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 +272,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 +349,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 +377,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 +386,14 @@ 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";
|
||||
lng_context_copy_text: "Copy Message Text";
|
||||
lng_context_to_msg: "Go To Message";
|
||||
lng_context_forward_msg: "Forward Message";
|
||||
@@ -372,7 +409,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}?";
|
||||
@@ -427,15 +464,18 @@ 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
|
||||
|
||||
@@ -448,5 +488,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);
|
||||
}
|
||||
@@ -244,9 +245,9 @@ inpDefFlat: flatInput {
|
||||
}
|
||||
|
||||
inpDefGray: flatInput(inpDefFlat) {
|
||||
bgColor: #ebebeb;
|
||||
bgColor: #f2f2f2;
|
||||
borderWidth: 2px;
|
||||
borderColor: #ebebeb;
|
||||
borderColor: #f2f2f2;
|
||||
borderActive: #80cff9;
|
||||
borderError: #ed8080;
|
||||
phColor: #808080;
|
||||
@@ -374,7 +375,7 @@ introCountry: countryInput {
|
||||
width: 300px;
|
||||
height: 41px;
|
||||
top: 24px;
|
||||
bgColor: #ebebeb;
|
||||
bgColor: #f2f2f2;
|
||||
ptrSize: size(15px, 8px);
|
||||
textMrg: margins(16px, 5px, 16px, 15px);
|
||||
font: inpDefFont;
|
||||
@@ -446,7 +447,7 @@ countryList: countryList {
|
||||
color: #000;
|
||||
codeColor: #aaaaaa;//rgb(20, 136, 210);
|
||||
bgColor: #FFF;
|
||||
bgHovered: #f1f1f1;
|
||||
bgHovered: #f5f5f5;
|
||||
margin: 13px;
|
||||
codeWidth: 60px;
|
||||
|
||||
@@ -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;
|
||||
@@ -754,6 +754,7 @@ msgFont: font(fsize);
|
||||
msgNameFont: font(fsize semibold);
|
||||
msgServiceFont: font(fsize semibold);
|
||||
msgServiceNameFont: font(fsize semibold);
|
||||
msgServicePhotoWidth: 100px;
|
||||
msgDateFont: font(13px);
|
||||
msgMinWidth: 190px;
|
||||
msgPhotoSize: 30px;
|
||||
@@ -840,6 +841,10 @@ outTextStyle: textStyle(defaultTextStyle) {
|
||||
selectBG: msgOutSelectBG;
|
||||
selectOverlay: msgOutSelectOverlay;
|
||||
}
|
||||
medviewSaveAsTextStyle: textStyle(defaultTextStyle) {
|
||||
lnkColor: #91d9ff;
|
||||
lnkDownColor: #91d9ff;
|
||||
}
|
||||
|
||||
dlgTextStyle: textStyle(defaultTextStyle) {
|
||||
lnkColor: dlgSystemColor;
|
||||
@@ -872,13 +877,13 @@ mediaInSelectColor: msgInSelectDateColor;
|
||||
mediaOutSelectColor: msgOutSelectDateColor;
|
||||
mediaSaveDelta: 14px; // between bubble and download
|
||||
mediaSaveButton: flatButton(btnDefFlat) {
|
||||
color: btnYesColor;
|
||||
overColor: btnYesHover;
|
||||
downColor: btnYesHover;
|
||||
color: #507da2;
|
||||
overColor: #507da2;
|
||||
downColor: #507da2;
|
||||
|
||||
bgColor: white;
|
||||
overBgColor: btnWhiteHover;
|
||||
downBgColor: btnWhiteHover;
|
||||
overBgColor: #f5f8fa;
|
||||
downBgColor: #f5f8fa;
|
||||
|
||||
width: -28px;
|
||||
height: 34px;
|
||||
@@ -1073,7 +1078,7 @@ profileListPhotoSize: 46px;
|
||||
profileListPadding: size(12px, 6px);
|
||||
profileListNameTop: 8px;
|
||||
profileListStatusBottom: 6px;
|
||||
profileHoverBG: #f1f1f1;
|
||||
profileHoverBG: #f5f5f5;
|
||||
profileActiveBG: #6294b9;
|
||||
profileSubFont: font(fsize);
|
||||
profileCheckRect: sprite(88px, 108px, 24px, 24px);
|
||||
@@ -1222,7 +1227,7 @@ contactsClose: flatButton {
|
||||
overColor: btnYesHover;
|
||||
downColor: btnYesHover;
|
||||
|
||||
bgColor: #fffe;
|
||||
bgColor: white;
|
||||
overBgColor: white;
|
||||
downBgColor: white;
|
||||
|
||||
@@ -1462,48 +1467,115 @@ emojiPanDuration: 200;
|
||||
emojiPanHover: #f0f0f0;
|
||||
emojiPanRound: 2px;
|
||||
|
||||
medviewNavBarWidth: 120px;
|
||||
medviewTopSkip: 66px;
|
||||
medviewBottomSkip: 66px;
|
||||
medviewMainWidth: 600px;
|
||||
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
|
||||
|
||||
@@ -1553,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);
|
||||
vimeoIcon: sprite(336px, 283px, 60px, 60px);
|
||||
locationSize: size(320, 240);
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
|
||||
#define MyAppShortName "Telegram"
|
||||
#define MyAppName "Telegram Desktop"
|
||||
#define MyAppVersion "0.6"
|
||||
#define MyAppVersionZero "0.6.0"
|
||||
#define MyAppFullVersion "0.6.0.0"
|
||||
#define MyAppPublisher "Telegram Messenger LLP"
|
||||
#define MyAppURL "https://tdesktop.com"
|
||||
#define MyAppExeName "Telegram.exe"
|
||||
@@ -28,7 +25,7 @@ DefaultGroupName={#MyAppName}
|
||||
AllowNoIcons=yes
|
||||
OutputDir=.\..\Win32\Deploy
|
||||
OutputBaseFilename=tsetup.{#MyAppVersionZero}
|
||||
SetupIconFile=.\SourceFiles\art\iconround256.ico
|
||||
SetupIconFile=.\SourceFiles\art\icon256.ico
|
||||
UninstallDisplayIcon={app}\Telegram.exe
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
|
||||
@@ -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.0.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, "}:)"},
|
||||
{0x2764U, "<3"},
|
||||
{0xD83DDC4DU, ":like:"},
|
||||
{0xD83DDC4EU, ":dislike:"},
|
||||
{0x261DU, ":up:"},
|
||||
{0x270CU, ":v:"},
|
||||
{0xD83DDC4CU, ":ok:"}
|
||||
};
|
||||
const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace);
|
||||
typedef QMap<QString, uint32> ReplaceMap;
|
||||
@@ -802,8 +802,8 @@ uint64 emojiCategory4[] = {
|
||||
0xD83CDE2FLLU,
|
||||
0xD83CDE33LLU,
|
||||
0xD83CDE35LLU,
|
||||
0xD83CDE32LLU,
|
||||
0xD83CDE34LLU,
|
||||
0xD83CDE32LLU,
|
||||
0xD83CDE50LLU,
|
||||
0xD83CDE39LLU,
|
||||
0xD83CDE3ALLU,
|
||||
@@ -957,6 +957,106 @@ uint64 emojiCategory4[] = {
|
||||
0xD83DDD39LLU,
|
||||
};
|
||||
|
||||
uint64 emojiPostfixed[] = {
|
||||
0x203CLLU,
|
||||
0x2049LLU,
|
||||
0x2139LLU,
|
||||
0x2194LLU,
|
||||
0x2195LLU,
|
||||
0x2196LLU,
|
||||
0x2197LLU,
|
||||
0x2198LLU,
|
||||
0x2199LLU,
|
||||
0x21A9LLU,
|
||||
0x21AALLU,
|
||||
0x231ALLU,
|
||||
0x231BLLU,
|
||||
0x24C2LLU,
|
||||
0x25AALLU,
|
||||
0x25ABLLU,
|
||||
0x25B6LLU,
|
||||
0x25C0LLU,
|
||||
0x25FBLLU,
|
||||
0x25FCLLU,
|
||||
0x25FDLLU,
|
||||
0x25FELLU,
|
||||
0x2600LLU,
|
||||
0x2601LLU,
|
||||
0x260ELLU,
|
||||
0x2611LLU,
|
||||
0x2614LLU,
|
||||
0x2615LLU,
|
||||
0x261DLLU,
|
||||
0x263ALLU,
|
||||
0x2648LLU,
|
||||
0x2649LLU,
|
||||
0x264ALLU,
|
||||
0x264BLLU,
|
||||
0x264CLLU,
|
||||
0x264DLLU,
|
||||
0x264ELLU,
|
||||
0x264FLLU,
|
||||
0x2650LLU,
|
||||
0x2651LLU,
|
||||
0x2652LLU,
|
||||
0x2653LLU,
|
||||
0x2660LLU,
|
||||
0x2663LLU,
|
||||
0x2665LLU,
|
||||
0x2666LLU,
|
||||
0x2668LLU,
|
||||
0x267BLLU,
|
||||
0x267FLLU,
|
||||
0x2693LLU,
|
||||
0x26A0LLU,
|
||||
0x26A1LLU,
|
||||
0x26AALLU,
|
||||
0x26ABLLU,
|
||||
0x26BDLLU,
|
||||
0x26BELLU,
|
||||
0x26C4LLU,
|
||||
0x26C5LLU,
|
||||
0x26D4LLU,
|
||||
0x26EALLU,
|
||||
0x26F2LLU,
|
||||
0x26F3LLU,
|
||||
0x26F5LLU,
|
||||
0x26FALLU,
|
||||
0x26FDLLU,
|
||||
0x2702LLU,
|
||||
0x2708LLU,
|
||||
0x2709LLU,
|
||||
0x270CLLU,
|
||||
0x270FLLU,
|
||||
0x2712LLU,
|
||||
0x2714LLU,
|
||||
0x2716LLU,
|
||||
0x2733LLU,
|
||||
0x2734LLU,
|
||||
0x2744LLU,
|
||||
0x2747LLU,
|
||||
0x2757LLU,
|
||||
0x2764LLU,
|
||||
0x27A1LLU,
|
||||
0x2934LLU,
|
||||
0x2935LLU,
|
||||
0x2B05LLU,
|
||||
0x2B06LLU,
|
||||
0x2B07LLU,
|
||||
0x2B50LLU,
|
||||
0x2B55LLU,
|
||||
0x2B1BLLU,
|
||||
0x2B1CLLU,
|
||||
0x303DLLU,
|
||||
0x3297LLU,
|
||||
0x3299LLU,
|
||||
0xD83CDC04LLU,
|
||||
0xD83CDD7FLLU,
|
||||
0xD83CDE1ALLU,
|
||||
0xD83CDE2FLLU,
|
||||
};
|
||||
QMap<uint64, bool> emojiWithPostfixes;
|
||||
|
||||
uint32 firstCode(uint64 fullCode) {
|
||||
return (fullCode > 0xFFFFFFFFLLU) ? uint32(fullCode >> 32) : (fullCode & 0xFFFFFFFFU);
|
||||
}
|
||||
@@ -990,7 +1090,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;
|
||||
|
||||
QImage sprites[5];
|
||||
int emojisInRow[] = { 27, 29, 33, 34, 34 }; // [[7,27],[4,29],[7,33],[3,34],[6,34]]
|
||||
@@ -1109,6 +1209,10 @@ bool genEmoji(QString emoji_in, const QString &emoji_out, const QString &emoji_p
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, l = sizeof(emojiPostfixed) / sizeof(emojiPostfixed[0]); i < l; ++i) {
|
||||
emojiWithPostfixes.insert(emojiPostfixed[i], true);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
QByteArray cppText;
|
||||
@@ -1149,7 +1253,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com\n\
|
||||
tcpp << "\tEmojiData *toFill = emojis = (EmojiData*)emojisData;\n\n";
|
||||
for (EmojisData::const_iterator i = emojisData.cbegin(), e = emojisData.cend(); i != e; ++i) {
|
||||
int len = i->code2 ? 4 : ((i->code >> 16) ? 2 : 1);
|
||||
tcpp << "\tnew (toFill++) EmojiData(" << (i->x * imSize) << ", " << (i->y * imSize) << ", 0x" << QString("%1").arg(i->code, 0, 16).toUpper().toUtf8().constData() << "U, 0x" << QString("%1").arg(i->code2, 0, 16).toUpper().toUtf8().constData() << "U, " << len << ");\n";
|
||||
bool withPostfix = emojiWithPostfixes.constFind(i->code) != emojiWithPostfixes.constEnd();
|
||||
tcpp << "\tnew (toFill++) EmojiData(" << (i->x * imSize) << ", " << (i->y * imSize) << ", 0x" << QString("%1").arg(i->code, 0, 16).toUpper().toUtf8().constData() << "U, 0x" << QString("%1").arg(i->code2, 0, 16).toUpper().toUtf8().constData() << "U, " << len << (withPostfix ? ", 0xFE0F" : "") << ");\n";
|
||||
}
|
||||
tcpp << "}\n\n";
|
||||
}
|
||||
@@ -1248,7 +1353,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;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#include "mainwidget.h"
|
||||
#include <libexif/exif-data.h>
|
||||
|
||||
#include "localstorage.h"
|
||||
|
||||
namespace {
|
||||
bool quiting = false;
|
||||
|
||||
@@ -43,6 +45,9 @@ namespace {
|
||||
typedef QHash<AudioId, AudioData*> AudiosData;
|
||||
AudiosData audiosData;
|
||||
|
||||
typedef QHash<QString, ImageLinkData*> ImageLinksData;
|
||||
ImageLinksData imageLinksData;
|
||||
|
||||
typedef QHash<DocumentId, DocumentData*> DocumentsData;
|
||||
DocumentsData documentsData;
|
||||
|
||||
@@ -111,7 +116,7 @@ namespace App {
|
||||
bool loggedOut() {
|
||||
Window *w(wnd());
|
||||
if (w) {
|
||||
w->tempDirDelete();
|
||||
w->tempDirDelete(Local::ClearManagerAll);
|
||||
w->notifyClearFast();
|
||||
w->setupIntro(true);
|
||||
}
|
||||
@@ -162,6 +167,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(-3);
|
||||
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) {
|
||||
@@ -179,9 +209,20 @@ namespace App {
|
||||
return dNow.secsTo(dTomorrow);
|
||||
}
|
||||
|
||||
QString onlineText(int32 online, int32 now, bool precise) {
|
||||
if (!online) return lang(lng_status_offline);
|
||||
if (online < 0) return lang(lng_status_invisible);
|
||||
QString onlineText(UserData *user, int32 now, bool precise) {
|
||||
if (isServiceUser(user->id)) {
|
||||
return lang(lng_status_service_notifications);
|
||||
}
|
||||
int32 online = user->onlineTill;
|
||||
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 +281,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 +294,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 +307,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 +315,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 +327,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 +343,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)), (!isServiceUser(data->id) && !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 +357,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 +370,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 +453,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 +579,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 +609,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 +689,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 || isServiceUser(user->id) || user->phone.isEmpty()) ? QString() : App::formatPhone(user->phone), textOneLine(user->username));
|
||||
if (App::main()) App::main()->peerUpdated(user);
|
||||
}
|
||||
}
|
||||
@@ -911,14 +966,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 +1014,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 +1056,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 +1102,37 @@ 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;
|
||||
} 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) {
|
||||
@@ -1116,6 +1178,17 @@ namespace App {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
||||
newItem->history()->itemReplaced(oldItem, newItem);
|
||||
if (App::main()) App::main()->itemReplaced(oldItem, newItem);
|
||||
if (App::hoveredItem() == oldItem) App::hoveredItem(newItem);
|
||||
if (App::pressedItem() == oldItem) App::pressedItem(newItem);
|
||||
if (App::hoveredLinkItem() == oldItem) App::hoveredLinkItem(newItem);
|
||||
if (App::pressedLinkItem() == oldItem) App::pressedLinkItem(newItem);
|
||||
if (App::contextItem() == oldItem) App::contextItem(newItem);
|
||||
if (App::mousedItem() == oldItem) App::mousedItem(newItem);
|
||||
}
|
||||
|
||||
HistoryItem *historyRegItem(HistoryItem *item) {
|
||||
MsgsData::const_iterator i = msgsData.constFind(item->id);
|
||||
if (i == msgsData.cend()) {
|
||||
@@ -1124,10 +1197,7 @@ namespace App {
|
||||
return 0;
|
||||
}
|
||||
if (i.value() != item && !i.value()->block() && item->block()) { // replace search item
|
||||
item->history()->itemReplaced(i.value(), item);
|
||||
if (App::main()) {
|
||||
emit App::main()->historyItemReplaced(i.value(), item);
|
||||
}
|
||||
itemReplaced(i.value(), item);
|
||||
delete i.value();
|
||||
msgsData.insert(item->id, item);
|
||||
return 0;
|
||||
@@ -1167,8 +1237,8 @@ namespace App {
|
||||
}
|
||||
}
|
||||
historyItemDetached(item);
|
||||
if (App::main()) {
|
||||
emit App::main()->historyItemDeleted(item);
|
||||
if (App::main() && !App::quiting()) {
|
||||
App::main()->itemRemoved(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1216,6 +1286,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) {
|
||||
@@ -1261,10 +1332,6 @@ namespace App {
|
||||
textlnkOver(TextLinkPtr());
|
||||
textlnkDown(TextLinkPtr());
|
||||
|
||||
if (completely && App::main()) {
|
||||
App::main()->disconnect(SIGNAL(historyItemDeleted(HistoryItem *)));
|
||||
}
|
||||
|
||||
histories().clear();
|
||||
|
||||
if (completely) {
|
||||
@@ -1593,7 +1660,7 @@ namespace App {
|
||||
}
|
||||
QByteArray encrypted(16 + fullSize, Qt::Uninitialized); // 128bit of sha1 - key128, sizeof(data), data
|
||||
hashSha1(toEncrypt.constData(), toEncrypt.size(), encrypted.data());
|
||||
aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 16, fullSize, &MTP::localKey(), encrypted.constData());
|
||||
aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 16, fullSize, &Local::oldKey(), encrypted.constData());
|
||||
|
||||
DEBUG_LOG(("App Info: writing user config file"));
|
||||
QDataStream configStream(&configFile);
|
||||
@@ -1644,7 +1711,7 @@ namespace App {
|
||||
}
|
||||
|
||||
cSetLocalSalt(salt);
|
||||
MTP::createLocalKey(QByteArray(), &salt);
|
||||
Local::createOldKey(&salt);
|
||||
|
||||
if (data.size() <= 16 || (data.size() & 0x0F)) {
|
||||
LOG(("App Error: bad encrypted part size: %1").arg(data.size()));
|
||||
@@ -1653,7 +1720,7 @@ namespace App {
|
||||
uint32 fullDataLen = data.size() - 16;
|
||||
decrypted.resize(fullDataLen);
|
||||
const char *dataKey = data.constData(), *encrypted = data.constData() + 16;
|
||||
aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, &MTP::localKey(), dataKey);
|
||||
aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, &Local::oldKey(), dataKey);
|
||||
uchar sha1Buffer[20];
|
||||
if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), dataKey, 16)) {
|
||||
LOG(("App Error: bad decrypt key, data from user-config not decrypted"));
|
||||
@@ -1850,17 +1917,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("42") || phone == qsl("111");
|
||||
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() {
|
||||
@@ -1883,7 +1947,6 @@ namespace App {
|
||||
::quiting = true;
|
||||
}
|
||||
|
||||
|
||||
QImage readImage(QByteArray data, QByteArray *format) {
|
||||
QByteArray tmpFormat;
|
||||
QImage result;
|
||||
|
||||
@@ -63,8 +63,9 @@ 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);
|
||||
QString onlineText(UserData *user, int32 nowOnServer, bool precise = false);
|
||||
|
||||
void feedUsers(const MTPVector<MTPUser> &users);
|
||||
void feedChats(const MTPVector<MTPChat> &chats);
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "langloaderplain.h"
|
||||
|
||||
#include "localstorage.h"
|
||||
|
||||
namespace {
|
||||
Application *mainApp = 0;
|
||||
FileUploader *uploader = 0;
|
||||
@@ -131,6 +133,7 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
|
||||
}
|
||||
}
|
||||
|
||||
Local::start();
|
||||
style::startManager();
|
||||
anim::startManager();
|
||||
historyInit();
|
||||
@@ -139,8 +142,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 +152,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 +330,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 +387,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 +455,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);
|
||||
|
||||
@@ -571,9 +620,11 @@ void Application::socketError(QLocalSocket::LocalSocketError e) {
|
||||
}
|
||||
|
||||
void Application::startApp() {
|
||||
Local::ReadMapState state = Local::readMap(QByteArray());
|
||||
|
||||
App::readUserConfig();
|
||||
if (!MTP::localKey().created()) {
|
||||
MTP::createLocalKey(QByteArray());
|
||||
if (!Local::oldKey().created()) {
|
||||
Local::createOldKey();
|
||||
cSetNeedConfigResave(true);
|
||||
}
|
||||
if (cNeedConfigResave()) {
|
||||
@@ -587,12 +638,12 @@ void Application::startApp() {
|
||||
|
||||
readSupportTemplates();
|
||||
|
||||
MTP::setLayer(mtpLayerMax);
|
||||
MTP::start();
|
||||
|
||||
MTP::setStateChangedHandler(mtpStateChanged);
|
||||
MTP::setSessionResetHandler(mtpSessionReset);
|
||||
|
||||
initImageLinkManager();
|
||||
App::initMedia();
|
||||
|
||||
if (MTP::authedId()) {
|
||||
@@ -706,6 +757,7 @@ Application::~Application() {
|
||||
socket.close();
|
||||
closeApplication();
|
||||
App::deinitMedia();
|
||||
deinitImageLinkManager();
|
||||
mainApp = 0;
|
||||
delete updateReply;
|
||||
delete ::uploader;
|
||||
@@ -718,6 +770,7 @@ Application::~Application() {
|
||||
delete window;
|
||||
|
||||
style::stopManager();
|
||||
Local::stop();
|
||||
}
|
||||
|
||||
Application *Application::app() {
|
||||
|
||||
@@ -70,11 +70,18 @@ public:
|
||||
|
||||
void writeUserConfigIn(uint64 ms);
|
||||
|
||||
void killDownloadSessionsStart(int32 dc);
|
||||
void killDownloadSessionsStop(int32 dc);
|
||||
|
||||
void checkLocalTime();
|
||||
|
||||
signals:
|
||||
|
||||
void peerPhotoDone(PeerId peer);
|
||||
void peerPhotoFail(PeerId peer);
|
||||
|
||||
void adjustSingleTimers();
|
||||
|
||||
public slots:
|
||||
|
||||
void startUpdateCheck(bool forceWait = false);
|
||||
@@ -100,10 +107,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 +136,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 |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 35 KiB |
BIN
Telegram/SourceFiles/art/icon128.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
Telegram/SourceFiles/art/icon128@2x.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
Telegram/SourceFiles/art/icon16.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/SourceFiles/art/icon16@2x.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/SourceFiles/art/icon2.ico
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
Telegram/SourceFiles/art/icon256.ico
Normal file
|
After Width: | Height: | Size: 361 KiB |
BIN
Telegram/SourceFiles/art/icon256.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
Telegram/SourceFiles/art/icon256@2x.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
Telegram/SourceFiles/art/icon32.ico
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
Telegram/SourceFiles/art/icon32.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/SourceFiles/art/icon32@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/SourceFiles/art/icon48.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/SourceFiles/art/icon48@2x.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
Telegram/SourceFiles/art/icon512.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
Telegram/SourceFiles/art/icon512@2x.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
Telegram/SourceFiles/art/icon64.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/SourceFiles/art/icon64@2x.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 655 B |
|
Before Width: | Height: | Size: 361 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 361 KiB |
|
Before Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 183 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: 96 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;
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ AddParticipantInner::ContactData *AddParticipantInner::contactData(DialogRow *ro
|
||||
data->inchat = _chat->participants.constFind(user) != _chat->participants.cend();
|
||||
data->check = false;
|
||||
data->name.setText(st::profileListNameFont, user->name, _textNameOptions);
|
||||
data->online = App::onlineText(user->onlineTill, _time);
|
||||
data->online = App::onlineText(user, _time);
|
||||
} else {
|
||||
data = i.value();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) {
|
||||
if (i == _contactsData.cend()) {
|
||||
_contactsData.insert(user, data = new ContactData());
|
||||
data->name.setText(st::profileListNameFont, user->name, _textNameOptions);
|
||||
data->online = App::onlineText(user->onlineTill, _time);
|
||||
data->online = App::onlineText(user, _time);
|
||||
} else {
|
||||
data = i.value();
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ NewGroupInner::ContactData *NewGroupInner::contactData(DialogRow *row) {
|
||||
_contactsData.insert(user, data = new ContactData());
|
||||
data->check = false;
|
||||
data->name.setText(st::profileListNameFont, user->name, _textNameOptions);
|
||||
data->online = App::onlineText(user->onlineTill, _time);
|
||||
data->online = App::onlineText(user, _time);
|
||||
} else {
|
||||
data = i.value();
|
||||
}
|
||||
|
||||
@@ -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,10 +17,12 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
static const int32 AppVersion = 6000;
|
||||
static const wchar_t *AppVersionStr = L"0.6";
|
||||
static const int32 AppVersion = 6013;
|
||||
static const wchar_t *AppVersionStr = L"0.6.13";
|
||||
|
||||
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
|
||||
static const wchar_t *AppName = L"Telegram Desktop";
|
||||
|
||||
static const wchar_t *AppId = L"{53F49750-6209-4FBF-9CA8-7A333C87D1ED}";
|
||||
static const wchar_t *AppFile = L"Telegram";
|
||||
|
||||
@@ -30,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)
|
||||
@@ -77,8 +86,31 @@ enum {
|
||||
AudioVoiceMsgChannels = 2, // stereo
|
||||
AudioVoiceMsgBufferSize = 1024 * 1024, // 1 Mb buffers
|
||||
AudioVoiceMsgInMemory = 1024 * 1024, // 1 Mb audio is hold in memory and auto loaded
|
||||
|
||||
MediaViewImageSizeLimit = 100 * 1024 * 1024, // show up to 100mb jpg/png/gif docs in app
|
||||
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
|
||||
|
||||
WriteMapTimeout = 1000,
|
||||
SaveDraftTimeout = 1000, // save draft after 1 secs of not changing text
|
||||
SaveDraftAnywayTimeout = 5000, // or save anyway each 5 secs
|
||||
};
|
||||
|
||||
inline bool isServiceUser(uint64 id) {
|
||||
return (id == 333000) || (id == 777000);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
inline const GUID &cGUID() {
|
||||
static const GUID gGuid = { 0x87a94ab0, 0xe370, 0x4cde, { 0x98, 0xd3, 0xac, 0xc1, 0x10, 0xc5, 0x96, 0x7d } };
|
||||
@@ -193,12 +225,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
|
||||
@@ -207,11 +239,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,13 +26,11 @@ 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 *)));
|
||||
connect(main, SIGNAL(dialogRowReplaced(DialogRow *, DialogRow *)), this, SLOT(onDialogRowReplaced(DialogRow *, DialogRow *)));
|
||||
connect(main, SIGNAL(historyItemReplaced(HistoryItem *, HistoryItem *)), this, SLOT(onItemReplaced(HistoryItem *, HistoryItem *)));
|
||||
connect(main, SIGNAL(historyItemDeleted(HistoryItem *)), this, SLOT(onItemRemoved(HistoryItem *)));
|
||||
}
|
||||
|
||||
void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||
@@ -78,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);
|
||||
@@ -87,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);
|
||||
@@ -105,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);
|
||||
@@ -149,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;
|
||||
}
|
||||
@@ -278,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);
|
||||
@@ -334,7 +426,7 @@ void DialogsListWidget::onPeerPhotoChanged(PeerData *peer) {
|
||||
}
|
||||
|
||||
void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) {
|
||||
newFilter = textAccentFold(newFilter.trimmed().toLower());
|
||||
newFilter = textSearchKey(newFilter);
|
||||
if (newFilter != filter || force) {
|
||||
QStringList f;
|
||||
if (!newFilter.isEmpty()) {
|
||||
@@ -354,6 +446,7 @@ void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) {
|
||||
if (filter.isEmpty()) {
|
||||
_state = DefaultState;
|
||||
filterResults.clear();
|
||||
peopleResults.clear();
|
||||
searchResults.clear();
|
||||
_lastSearchId = 0;
|
||||
} else {
|
||||
@@ -443,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;
|
||||
@@ -453,7 +547,7 @@ void DialogsListWidget::clearSearchResults() {
|
||||
_lastSearchId = 0;
|
||||
}
|
||||
|
||||
void DialogsListWidget::onItemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
||||
void DialogsListWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
||||
for (int i = 0; i < searchResults.size(); ++i) {
|
||||
if (searchResults[i]->_item == oldItem) {
|
||||
searchResults[i]->_item = newItem;
|
||||
@@ -461,11 +555,12 @@ void DialogsListWidget::onItemReplaced(HistoryItem *oldItem, HistoryItem *newIte
|
||||
}
|
||||
}
|
||||
|
||||
void DialogsListWidget::onItemRemoved(HistoryItem *item) {
|
||||
void DialogsListWidget::itemRemoved(HistoryItem *item) {
|
||||
int wasCount = searchResults.size();
|
||||
for (int i = 0; i < searchResults.size();) {
|
||||
if (searchResults[i]->_item == item) {
|
||||
searchResults.remove(i);
|
||||
if (searchedCount > 0) --searchedCount;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
@@ -491,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);
|
||||
@@ -505,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);
|
||||
@@ -544,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) {
|
||||
@@ -563,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -573,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;
|
||||
@@ -590,6 +698,7 @@ void DialogsListWidget::clearFilter() {
|
||||
if (_state == FilteredState || _state == SearchedState) {
|
||||
_state = DefaultState;
|
||||
filterResults.clear();
|
||||
peopleResults.clear();
|
||||
searchResults.clear();
|
||||
_lastSearchId = 0;
|
||||
filter = QString();
|
||||
@@ -607,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) {
|
||||
@@ -635,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) {
|
||||
@@ -799,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) {
|
||||
@@ -818,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;
|
||||
}
|
||||
@@ -836,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();
|
||||
@@ -884,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;
|
||||
@@ -893,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;
|
||||
}
|
||||
@@ -944,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;
|
||||
}
|
||||
}
|
||||
@@ -965,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;
|
||||
}
|
||||
@@ -986,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()));
|
||||
@@ -1047,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);
|
||||
@@ -1061,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;
|
||||
@@ -1083,8 +1248,12 @@ void DialogsWidget::onCancel() {
|
||||
emit cancelled();
|
||||
}
|
||||
|
||||
void DialogsWidget::clearFiltered() {
|
||||
onCancel();
|
||||
void DialogsWidget::itemRemoved(HistoryItem *item) {
|
||||
list.itemRemoved(item);
|
||||
}
|
||||
|
||||
void DialogsWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
||||
list.itemReplaced(oldItem, newItem);
|
||||
}
|
||||
|
||||
void DialogsWidget::unreadCountsReceived(const QVector<MTPDialog> &dialogs) {
|
||||
@@ -1157,6 +1326,9 @@ bool DialogsWidget::onSearchMessages(bool searchCache) {
|
||||
if (_searchRequest) {
|
||||
_searchRequest = 0;
|
||||
}
|
||||
if (_peopleRequest) {
|
||||
_peopleRequest = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (searchCache) {
|
||||
@@ -1174,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;
|
||||
}
|
||||
|
||||
@@ -1260,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;
|
||||
@@ -1268,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();
|
||||
@@ -1281,11 +1501,10 @@ 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 - 2 * 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 - scroll.height()) {
|
||||
} else if (scroll.scrollTop() > list.dialogsList().list.count * st::dlgHeight - PreloadHeightsCount * scroll.height()) {
|
||||
loadDialogs();
|
||||
}
|
||||
}
|
||||
@@ -1305,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;
|
||||
|
||||
@@ -80,6 +87,8 @@ public:
|
||||
State state() const;
|
||||
|
||||
void onFilterUpdate(QString newFilter, bool force = false);
|
||||
void itemRemoved(HistoryItem *item);
|
||||
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
|
||||
|
||||
~DialogsListWidget();
|
||||
|
||||
@@ -92,20 +101,17 @@ public slots:
|
||||
void onPeerPhotoChanged(PeerData *peer);
|
||||
void onDialogRowReplaced(DialogRow *oldRow, DialogRow *newRow);
|
||||
|
||||
void onItemRemoved(HistoryItem *item);
|
||||
void onItemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
|
||||
|
||||
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;
|
||||
@@ -115,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;
|
||||
@@ -141,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);
|
||||
@@ -175,11 +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:
|
||||
@@ -204,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;
|
||||
@@ -215,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;
|
||||
@@ -225,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));
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "lang.h"
|
||||
|
||||
ContextMenu::ContextMenu(QWidget *parent, const style::iconedButton &st) : TWidget(parent),
|
||||
ContextMenu::ContextMenu(QWidget *parent, const style::iconedButton &st) : TWidget(0),
|
||||
_hiding(false), _buttonStyle(st), _shadow(st::dropdownShadow), _selected(-1), a_opacity(0), _deleteOnHide(false) {
|
||||
resetActions();
|
||||
|
||||
@@ -207,9 +207,9 @@ void ContextMenu::showStart() {
|
||||
a_opacity.start(1);
|
||||
anim::start(this);
|
||||
animStep(0);
|
||||
setParent(0);
|
||||
psUpdateOverlayed(this);
|
||||
show();
|
||||
psShowOverAll(this);
|
||||
windowHandle()->requestActivate();
|
||||
activateWindow();
|
||||
setFocus();
|
||||
@@ -245,8 +245,8 @@ void ContextMenu::popup(const QPoint &p) {
|
||||
if (w.y() + height() - st::dropdownPadding.bottom() > r.y() + r.height()) {
|
||||
w.setY(p.y() - height() + st::dropdownPadding.bottom());
|
||||
}
|
||||
if (w.y() < 0) {
|
||||
w.setY(0);
|
||||
if (w.y() < r.y()) {
|
||||
w.setY(r.y());
|
||||
}
|
||||
move(w);
|
||||
showStart();
|
||||
|
||||
@@ -302,7 +302,7 @@ void CountryList::paintEvent(QPaintEvent *e) {
|
||||
|
||||
int l = countriesNow->size();
|
||||
if (l) {
|
||||
int from = (r.top() > _st.verticalMargin) ? (r.top() - _st.verticalMargin) / _st.rowHeight : 0, to = from + r.height() / _st.rowHeight + 1;
|
||||
int from = (r.top() > _st.verticalMargin) ? (r.top() - _st.verticalMargin) / _st.rowHeight : 0, to = from + (r.height() / _st.rowHeight) + 2;
|
||||
if (to >= l) {
|
||||
if (from >= l) return;
|
||||
to = l;
|
||||
@@ -518,6 +518,12 @@ void CountrySelect::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void CountrySelect::mousePressEvent(QMouseEvent *e) {
|
||||
if (!QRect(_innerLeft, _innerTop, _innerWidth, _innerHeight).contains(e->pos())) {
|
||||
onCountryCancel();
|
||||
}
|
||||
}
|
||||
|
||||
void CountrySelect::onFilterUpdate() {
|
||||
QString newFilter(_filter.text().trimmed().toLower());
|
||||
if (newFilter != lastFilter) {
|
||||
|
||||
@@ -121,6 +121,7 @@ public:
|
||||
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void keyPressEvent(QKeyEvent *e);
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
|
||||
bool animStep(float64 ms);
|
||||
|
||||
@@ -17,6 +17,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "gui/text.h"
|
||||
|
||||
void initEmoji();
|
||||
EmojiPtr getEmoji(uint32 code);
|
||||
|
||||
@@ -38,7 +40,9 @@ inline bool emojiEdge(const QChar *ch) {
|
||||
|
||||
inline QString replaceEmojis(const QString &text) {
|
||||
QString result;
|
||||
const QChar *emojiEnd = text.unicode(), *e = text.cend();
|
||||
LinkRanges lnkRanges = textParseLinks(text);
|
||||
int32 currentLink = 0, lnkCount = lnkRanges.size();
|
||||
const QChar *emojiStart = text.unicode(), *emojiEnd = emojiStart, *e = text.cend();
|
||||
bool canFindEmoji = true, consumePrevious = false;
|
||||
for (const QChar *ch = emojiEnd; ch != e;) {
|
||||
uint32 emojiCode = 0;
|
||||
@@ -46,7 +50,15 @@ inline QString replaceEmojis(const QString &text) {
|
||||
if (canFindEmoji) {
|
||||
findEmoji(ch, e, newEmojiEnd, emojiCode);
|
||||
}
|
||||
if (emojiCode) {
|
||||
|
||||
while (currentLink < lnkCount && ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len) {
|
||||
++currentLink;
|
||||
}
|
||||
if (emojiCode &&
|
||||
(ch == emojiStart || !ch->isLetterOrNumber() || !(ch - 1)->isLetterOrNumber()) &&
|
||||
(newEmojiEnd == e || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) &&
|
||||
(currentLink >= lnkCount || (ch < lnkRanges[currentLink].from && newEmojiEnd <= lnkRanges[currentLink].from) || (ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len && newEmojiEnd > lnkRanges[currentLink].from + lnkRanges[currentLink].len))
|
||||
) {
|
||||
// if (newEmojiEnd < e && newEmojiEnd->unicode() == ' ') ++newEmojiEnd;
|
||||
if (result.isEmpty()) result.reserve(text.size());
|
||||
if (ch > emojiEnd + (consumePrevious ? 1 : 0)) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -219,7 +227,7 @@ QString FlatTextarea::getText(int32 start, int32 end) const {
|
||||
uint32 index = imageName.mid(8).toUInt(0, 16);
|
||||
const EmojiData *emoji = getEmoji(index);
|
||||
if (emoji) {
|
||||
emojiText.reserve(emoji->len);
|
||||
emojiText.reserve(emoji->len + (emoji->postfix ? 1 : 0));
|
||||
switch (emoji->len) {
|
||||
case 1: emojiText.append(QChar(emoji->code & 0xFFFF)); break;
|
||||
case 2:
|
||||
@@ -233,6 +241,7 @@ QString FlatTextarea::getText(int32 start, int32 end) const {
|
||||
emojiText.append(QChar(emoji->code2 & 0xFFFF));
|
||||
break;
|
||||
}
|
||||
if (emoji->postfix) emojiText.append(QChar(emoji->postfix));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,6 +273,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();
|
||||
|
||||
@@ -279,7 +296,7 @@ void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) {
|
||||
}
|
||||
|
||||
void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
|
||||
int32 emojiPosition = 0;
|
||||
int32 emojiPosition = 0, emojiLen = 0;
|
||||
const EmojiData *emoji = 0;
|
||||
|
||||
QTextDocument *doc(document());
|
||||
@@ -308,6 +325,7 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
|
||||
emoji = 0;
|
||||
} else {
|
||||
emojiPosition = p + (ch - t.constData());
|
||||
emojiLen = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -316,6 +334,7 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
|
||||
emoji = getEmoji(ch->unicode());
|
||||
if (emoji) {
|
||||
emojiPosition = p + (ch - t.constData());
|
||||
emojiLen = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -326,7 +345,7 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
|
||||
}
|
||||
if (emoji) {
|
||||
QTextCursor c(doc->docHandle(), emojiPosition);
|
||||
c.setPosition(emojiPosition + emoji->len, QTextCursor::KeepAnchor);
|
||||
c.setPosition(emojiPosition + emojiLen, QTextCursor::KeepAnchor);
|
||||
int32 removedUpto = c.position();
|
||||
|
||||
insertEmoji(emoji, c);
|
||||
@@ -394,6 +413,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 +469,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;
|
||||
|
||||
@@ -29,18 +29,9 @@ namespace {
|
||||
return img;
|
||||
}
|
||||
|
||||
typedef QMap<QByteArray, StorageImage*> StorageImages;
|
||||
typedef QMap<StorageKey, StorageImage*> StorageImages;
|
||||
StorageImages storageImages;
|
||||
|
||||
QByteArray storageKey(int32 dc, const int64 &volume, int32 local, const int64 &secret) {
|
||||
QByteArray result(24, Qt::Uninitialized);
|
||||
memcpy(result.data(), &dc, 4);
|
||||
memcpy(result.data() + 4, &volume, 8);
|
||||
memcpy(result.data() + 12, &local, 4);
|
||||
memcpy(result.data() + 16, &secret, 8);
|
||||
return result;
|
||||
}
|
||||
|
||||
int64 globalAquiredSize = 0;
|
||||
}
|
||||
|
||||
@@ -88,7 +79,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 +92,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 +379,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,17 +470,17 @@ bool StorageImage::loaded() const {
|
||||
return check();
|
||||
}
|
||||
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret) {
|
||||
QByteArray key(storageKey(dc, volume, local, secret));
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) {
|
||||
StorageKey key(storageKey(dc, volume, local));
|
||||
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();
|
||||
}
|
||||
|
||||
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes) {
|
||||
QByteArray key(storageKey(dc, volume, local, secret));
|
||||
StorageKey key(storageKey(dc, volume, local));
|
||||
StorageImages::const_iterator i = storageImages.constFind(key);
|
||||
if (i == storageImages.cend()) {
|
||||
QByteArray bytesArr(bytes);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -51,6 +53,13 @@ public:
|
||||
void forget() const;
|
||||
void restore() const;
|
||||
|
||||
QByteArray savedFormat() const {
|
||||
return format;
|
||||
}
|
||||
QByteArray savedData() const {
|
||||
return saved;
|
||||
}
|
||||
|
||||
virtual ~Image() {
|
||||
invalidateSizeCache();
|
||||
}
|
||||
@@ -104,10 +113,28 @@ private:
|
||||
LocalImage *getImage(const QString &file);
|
||||
LocalImage *getImage(const QPixmap &pixmap, QByteArray format);
|
||||
|
||||
typedef QPair<uint64, uint64> StorageKey;
|
||||
inline uint64 storageMix32To64(int32 a, int32 b) {
|
||||
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32) | uint64(*reinterpret_cast<uint32*>(&b));
|
||||
}
|
||||
inline StorageKey storageKey(int32 dc, const int64 &volume, int32 local) {
|
||||
return StorageKey(storageMix32To64(dc, local), volume);
|
||||
}
|
||||
inline StorageKey storageKey(const MTPDfileLocation &location) {
|
||||
return storageKey(location.vdc_id.v, location.vvolume_id.v, location.vlocal_id.v);
|
||||
}
|
||||
struct StorageImageSaved {
|
||||
StorageImageSaved() : type(mtpc_storage_fileUnknown) {
|
||||
}
|
||||
StorageImageSaved(mtpTypeId type, const QByteArray &data) : type(type), data(data) {
|
||||
}
|
||||
mtpTypeId type;
|
||||
QByteArray data;
|
||||
};
|
||||
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;
|
||||
@@ -121,7 +148,7 @@ public:
|
||||
void load(bool loadFirst = false, bool prior = true) {
|
||||
if (loader) {
|
||||
loader->start(loadFirst, prior);
|
||||
check();
|
||||
if (loader) check();
|
||||
}
|
||||
}
|
||||
void checkload() const {
|
||||
@@ -129,7 +156,7 @@ public:
|
||||
if (!loader->loading()) {
|
||||
loader->start(true);
|
||||
}
|
||||
check();
|
||||
if (loader) check();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +182,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 +193,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:
|
||||
@@ -265,14 +287,52 @@ QString textcmdStopColor() {
|
||||
return result.append(TextCommand).append(QChar(TextCommandNoColor)).append(TextCommand);
|
||||
}
|
||||
|
||||
class TextParser {
|
||||
struct LinkRange {
|
||||
LinkRange() : from(0), len(0) {
|
||||
}
|
||||
const QChar *from;
|
||||
int32 len;
|
||||
};
|
||||
const QChar *skipCommand(const QChar *from, const QChar *end, bool canLink = true) {
|
||||
const QChar *result = from + 1;
|
||||
if (*from != TextCommand || result >= end) return from;
|
||||
|
||||
ushort cmd = result->unicode();
|
||||
++result;
|
||||
if (result >= end) return from;
|
||||
|
||||
switch (cmd) {
|
||||
case TextCommandBold:
|
||||
case TextCommandNoBold:
|
||||
case TextCommandItalic:
|
||||
case TextCommandNoItalic:
|
||||
case TextCommandUnderline:
|
||||
case TextCommandNoUnderline:
|
||||
case TextCommandNoColor:
|
||||
break;
|
||||
|
||||
case TextCommandLinkIndex:
|
||||
if (result->unicode() > 0x7FFF) return from;
|
||||
++result;
|
||||
break;
|
||||
|
||||
case TextCommandLinkText: {
|
||||
ushort len = result->unicode();
|
||||
if (len >= 4096 || !canLink) return from;
|
||||
result += len + 1;
|
||||
} break;
|
||||
|
||||
case TextCommandColor: {
|
||||
const QChar *e = result + 4;
|
||||
if (e >= end) return from;
|
||||
|
||||
for (; result < e; ++result) {
|
||||
if (result->unicode() >= 256) return from;
|
||||
}
|
||||
} break;
|
||||
|
||||
case TextCommandSkipBlock:
|
||||
result += 2;
|
||||
break;
|
||||
}
|
||||
return (result < end && *result == TextCommand) ? (result + 1) : from;
|
||||
}
|
||||
|
||||
class TextParser {
|
||||
public:
|
||||
|
||||
static Qt::LayoutDirection stringDirection(const QString &str, int32 from, int32 to) {
|
||||
@@ -301,133 +361,6 @@ public:
|
||||
return Qt::LayoutDirectionAuto;
|
||||
}
|
||||
|
||||
void prepareLinks() { // support emails and hashtags!
|
||||
if (validProtocols.empty()) {
|
||||
initLinkSets();
|
||||
}
|
||||
int32 len = src.size(), nextCmd = rich ? 0 : len;
|
||||
const QChar *srcData = src.unicode();
|
||||
for (int32 offset = 0; offset < len; ) {
|
||||
if (nextCmd <= offset) {
|
||||
for (nextCmd = offset; nextCmd < len; ++nextCmd) {
|
||||
if (*(srcData + nextCmd) == TextCommand) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
QRegularExpressionMatch mDomain = reDomain.match(src, offset);
|
||||
QRegularExpressionMatch mExplicitDomain = reExplicitDomain.match(src, offset);
|
||||
QRegularExpressionMatch mHashtag = reHashtag.match(src, offset);
|
||||
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break;
|
||||
|
||||
LinkRange link;
|
||||
int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
|
||||
domainEnd = mDomain.hasMatch() ? mDomain.capturedEnd() : INT_MAX,
|
||||
explicitDomainOffset = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedStart() : INT_MAX,
|
||||
explicitDomainEnd = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedEnd() : INT_MAX,
|
||||
hashtagOffset = mHashtag.hasMatch() ? mHashtag.capturedStart() : INT_MAX,
|
||||
hashtagEnd = mHashtag.hasMatch() ? mHashtag.capturedEnd() : INT_MAX;
|
||||
if (mHashtag.hasMatch()) {
|
||||
if (!mHashtag.capturedRef(1).isEmpty()) {
|
||||
++hashtagOffset;
|
||||
}
|
||||
if (!mHashtag.capturedRef(2).isEmpty()) {
|
||||
--hashtagEnd;
|
||||
}
|
||||
}
|
||||
if (explicitDomainOffset < domainOffset) {
|
||||
domainOffset = explicitDomainOffset;
|
||||
domainEnd = explicitDomainEnd;
|
||||
mDomain = mExplicitDomain;
|
||||
}
|
||||
if (hashtagOffset < domainOffset) {
|
||||
if (hashtagOffset > nextCmd) {
|
||||
const QChar *after = skipCommand(srcData + nextCmd, srcData + len);
|
||||
if (after > srcData + nextCmd && hashtagOffset < (after - srcData)) {
|
||||
nextCmd = offset = after - srcData;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
link.from = start + hashtagOffset;
|
||||
link.len = start + hashtagEnd - link.from;
|
||||
} else {
|
||||
if (domainOffset > nextCmd) {
|
||||
const QChar *after = skipCommand(srcData + nextCmd, srcData + len);
|
||||
if (after > srcData + nextCmd && domainOffset < (after - srcData)) {
|
||||
nextCmd = offset = after - srcData;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
QString protocol = mDomain.captured(1).toLower();
|
||||
QString topDomain = mDomain.captured(3).toLower();
|
||||
|
||||
bool isProtocolValid = protocol.isEmpty() || validProtocols.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar)));
|
||||
bool isTopDomainValid = !protocol.isEmpty() || validTopDomains.contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar)));
|
||||
|
||||
if (!isProtocolValid || !isTopDomainValid) {
|
||||
offset = domainEnd;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) {
|
||||
QString forMailName = src.mid(offset, domainOffset - offset - 1);
|
||||
QRegularExpressionMatch mMailName = reMailName.match(forMailName);
|
||||
if (mMailName.hasMatch()) {
|
||||
int32 mailOffset = offset + mMailName.capturedStart();
|
||||
if (mailOffset < offset) {
|
||||
mailOffset = offset;
|
||||
}
|
||||
link.from = start + mailOffset;
|
||||
link.len = domainEnd - mailOffset;
|
||||
}
|
||||
}
|
||||
if (!link.from || !link.len) {
|
||||
link.from = start + domainOffset;
|
||||
|
||||
QStack<const QChar*> parenth;
|
||||
const QChar *domainEnd = start + mDomain.capturedEnd(), *p = domainEnd;
|
||||
for (; p < end; ++p) {
|
||||
QChar ch(*p);
|
||||
if (chIsLinkEnd(ch)) break; // link finished
|
||||
if (chIsAlmostLinkEnd(ch)) {
|
||||
const QChar *endTest = p + 1;
|
||||
while (endTest < end && chIsAlmostLinkEnd(*endTest)) {
|
||||
++endTest;
|
||||
}
|
||||
if (endTest >= end || chIsLinkEnd(*endTest)) {
|
||||
break; // link finished at p
|
||||
}
|
||||
p = endTest;
|
||||
ch = *p;
|
||||
}
|
||||
if (ch == '(' || ch == '[' || ch == '{' || ch == '<') {
|
||||
parenth.push(p);
|
||||
} else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') {
|
||||
if (parenth.isEmpty()) break;
|
||||
const QChar *q = parenth.pop(), open(*q);
|
||||
if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) {
|
||||
p = q;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p > domainEnd) { // check, that domain ended
|
||||
if (domainEnd->unicode() != '/') {
|
||||
offset = domainEnd - start;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
link.len = p - link.from;
|
||||
}
|
||||
}
|
||||
lnkRanges.push_back(link);
|
||||
|
||||
offset = (link.from - start) + link.len;
|
||||
}
|
||||
}
|
||||
|
||||
void blockCreated() {
|
||||
sumWidth += _t->_blocks.back()->f_width();
|
||||
if (sumWidth.floor().toInt() > stopAfterWidth) {
|
||||
@@ -500,53 +433,8 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
const QChar *skipCommand(const QChar *from, const QChar *end) {
|
||||
const QChar *result = from + 1;
|
||||
if (*from != TextCommand || result >= end) return from;
|
||||
|
||||
ushort cmd = result->unicode();
|
||||
++result;
|
||||
if (result >= end) return from;
|
||||
|
||||
switch (cmd) {
|
||||
case TextCommandBold:
|
||||
case TextCommandNoBold:
|
||||
case TextCommandItalic:
|
||||
case TextCommandNoItalic:
|
||||
case TextCommandUnderline:
|
||||
case TextCommandNoUnderline:
|
||||
case TextCommandNoColor:
|
||||
break;
|
||||
|
||||
case TextCommandLinkIndex:
|
||||
if (result->unicode() > 0x7FFF) return from;
|
||||
++result;
|
||||
break;
|
||||
|
||||
case TextCommandLinkText: {
|
||||
ushort len = result->unicode();
|
||||
if (len >= 4096 || links.size() >= 0x7FFF) return from;
|
||||
result += len + 1;
|
||||
} break;
|
||||
|
||||
case TextCommandColor: {
|
||||
const QChar *e = result + 4;
|
||||
if (e >= end) return from;
|
||||
|
||||
for (; result < e; ++result) {
|
||||
if (result->unicode() >= 256) return from;
|
||||
}
|
||||
} break;
|
||||
|
||||
case TextCommandSkipBlock:
|
||||
result += 2;
|
||||
break;
|
||||
}
|
||||
return (result < end && *result == TextCommand) ? (result + 1) : from;
|
||||
}
|
||||
|
||||
bool readCommand() {
|
||||
const QChar *afterCmd = skipCommand(ptr, end);
|
||||
const QChar *afterCmd = skipCommand(ptr, end, links.size() < 0x7FFF);
|
||||
if (afterCmd == ptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -708,8 +596,13 @@ public:
|
||||
_t->_text.push_back(*++ptr);
|
||||
}
|
||||
}
|
||||
int emojiLen = e->len;
|
||||
if (ptr + 1 < end && (ptr + 1)->unicode() == 0xFE0F) {
|
||||
_t->_text.push_back(*++ptr);
|
||||
++emojiLen;
|
||||
}
|
||||
|
||||
createBlock(-e->len);
|
||||
createBlock(-emojiLen);
|
||||
emoji = e;
|
||||
}
|
||||
|
||||
@@ -724,7 +617,7 @@ public:
|
||||
end = start + src.size();
|
||||
|
||||
if (options.flags & TextParseLinks) {
|
||||
prepareLinks();
|
||||
lnkRanges = textParseLinks(src, rich);
|
||||
}
|
||||
|
||||
while (start != end && chIsTrimmed(*start, rich)) {
|
||||
@@ -793,7 +686,6 @@ private:
|
||||
const QChar *start, *end, *ptr;
|
||||
bool rich, multiline;
|
||||
|
||||
typedef QVector<LinkRange> LinkRanges;
|
||||
LinkRanges lnkRanges;
|
||||
const LinkRange *waitingLink, *linksEnd;
|
||||
|
||||
@@ -4082,7 +3974,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;
|
||||
@@ -4102,3 +3994,217 @@ QString textAccentFold(const QString &text) {
|
||||
}
|
||||
return (i < result.size()) ? result.mid(0, i) : result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
EmojiPtr e = 0;
|
||||
if (ch->isHighSurrogate()) {
|
||||
if (ch + 1 < end && (ch + 1)->isLowSurrogate()) {
|
||||
e = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
|
||||
if (!e) {
|
||||
++ch;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ch + 1 < end) {
|
||||
if (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3) {
|
||||
e = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
|
||||
} else if ((ch + 1)->unicode() == 0xFE0F) {
|
||||
e = getEmoji(ch->unicode());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e) {
|
||||
ch += (e->len - 1);
|
||||
if (ch + 1 < end && (ch + 1)->unicode() == 0xFE0F) {
|
||||
++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;
|
||||
|
||||
if (validProtocols.empty()) {
|
||||
initLinkSets();
|
||||
}
|
||||
int32 len = text.size(), nextCmd = rich ? 0 : len;
|
||||
const QChar *start = text.unicode(), *end = start + text.size();
|
||||
for (int32 offset = 0, matchOffset = offset; offset < len;) {
|
||||
if (nextCmd <= offset) {
|
||||
for (nextCmd = offset; nextCmd < len; ++nextCmd) {
|
||||
if (*(start + nextCmd) == TextCommand) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
QRegularExpressionMatch mDomain = reDomain.match(text, matchOffset);
|
||||
QRegularExpressionMatch mExplicitDomain = reExplicitDomain.match(text, matchOffset);
|
||||
QRegularExpressionMatch mHashtag = reHashtag.match(text, matchOffset);
|
||||
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break;
|
||||
|
||||
LinkRange link;
|
||||
int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
|
||||
domainEnd = mDomain.hasMatch() ? mDomain.capturedEnd() : INT_MAX,
|
||||
explicitDomainOffset = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedStart() : INT_MAX,
|
||||
explicitDomainEnd = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedEnd() : INT_MAX,
|
||||
hashtagOffset = mHashtag.hasMatch() ? mHashtag.capturedStart() : INT_MAX,
|
||||
hashtagEnd = mHashtag.hasMatch() ? mHashtag.capturedEnd() : INT_MAX;
|
||||
if (mHashtag.hasMatch()) {
|
||||
if (!mHashtag.capturedRef(1).isEmpty()) {
|
||||
++hashtagOffset;
|
||||
}
|
||||
if (!mHashtag.capturedRef(2).isEmpty()) {
|
||||
--hashtagEnd;
|
||||
}
|
||||
}
|
||||
if (explicitDomainOffset < domainOffset) {
|
||||
domainOffset = explicitDomainOffset;
|
||||
domainEnd = explicitDomainEnd;
|
||||
mDomain = mExplicitDomain;
|
||||
}
|
||||
if (hashtagOffset < domainOffset) {
|
||||
if (hashtagOffset > nextCmd) {
|
||||
const QChar *after = skipCommand(start + nextCmd, start + len);
|
||||
if (after > start + nextCmd && hashtagOffset < (after - start)) {
|
||||
nextCmd = offset = matchOffset = after - start;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
link.from = start + hashtagOffset;
|
||||
link.len = start + hashtagEnd - link.from;
|
||||
} else {
|
||||
if (domainOffset > nextCmd) {
|
||||
const QChar *after = skipCommand(start + nextCmd, start + len);
|
||||
if (after > start + nextCmd && domainOffset < (after - start)) {
|
||||
nextCmd = offset = matchOffset = after - start;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
QString protocol = mDomain.captured(1).toLower();
|
||||
QString topDomain = mDomain.captured(3).toLower();
|
||||
|
||||
bool isProtocolValid = protocol.isEmpty() || validProtocols.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar)));
|
||||
bool isTopDomainValid = !protocol.isEmpty() || validTopDomains.contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar)));
|
||||
|
||||
if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) {
|
||||
QString forMailName = text.mid(offset, domainOffset - offset - 1);
|
||||
QRegularExpressionMatch mMailName = reMailName.match(forMailName);
|
||||
if (mMailName.hasMatch()) {
|
||||
int32 mailOffset = offset + mMailName.capturedStart();
|
||||
if (mailOffset < offset) {
|
||||
mailOffset = offset;
|
||||
}
|
||||
link.from = start + mailOffset;
|
||||
link.len = domainEnd - mailOffset;
|
||||
}
|
||||
}
|
||||
if (!link.from || !link.len) {
|
||||
if (!isProtocolValid || !isTopDomainValid) {
|
||||
matchOffset = domainEnd;
|
||||
continue;
|
||||
}
|
||||
link.from = start + domainOffset;
|
||||
|
||||
QStack<const QChar*> parenth;
|
||||
const QChar *domainEnd = start + mDomain.capturedEnd(), *p = domainEnd;
|
||||
for (; p < end; ++p) {
|
||||
QChar ch(*p);
|
||||
if (chIsLinkEnd(ch)) break; // link finished
|
||||
if (chIsAlmostLinkEnd(ch)) {
|
||||
const QChar *endTest = p + 1;
|
||||
while (endTest < end && chIsAlmostLinkEnd(*endTest)) {
|
||||
++endTest;
|
||||
}
|
||||
if (endTest >= end || chIsLinkEnd(*endTest)) {
|
||||
break; // link finished at p
|
||||
}
|
||||
p = endTest;
|
||||
ch = *p;
|
||||
}
|
||||
if (ch == '(' || ch == '[' || ch == '{' || ch == '<') {
|
||||
parenth.push(p);
|
||||
} else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') {
|
||||
if (parenth.isEmpty()) break;
|
||||
const QChar *q = parenth.pop(), open(*q);
|
||||
if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) {
|
||||
p = q;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p > domainEnd) { // check, that domain ended
|
||||
if (domainEnd->unicode() != '/') {
|
||||
matchOffset = domainEnd - start;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
link.len = p - link.from;
|
||||
}
|
||||
}
|
||||
lnkRanges.push_back(link);
|
||||
|
||||
offset = matchOffset = (link.from - start) + link.len;
|
||||
}
|
||||
|
||||
return lnkRanges;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,23 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// text preprocess
|
||||
QString textClean(const QString &text);
|
||||
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) {
|
||||
}
|
||||
const QChar *from;
|
||||
int32 len;
|
||||
};
|
||||
typedef QVector<LinkRange> LinkRanges;
|
||||
LinkRanges textParseLinks(const QString &text, bool rich = false);
|
||||
|
||||
#include "gui/emoji_config.h"
|
||||
|
||||
#include "../../../QtStatic/qtbase/src/gui/text/qfontengine_p.h"
|
||||
@@ -438,12 +455,6 @@ inline void textstyleRestore() {
|
||||
textstyleSet(0);
|
||||
}
|
||||
|
||||
// text preprocess
|
||||
QString textClean(const QString &text);
|
||||
QString textRichPrepare(const QString &text);
|
||||
QString textOneLine(const QString &text, bool trim = true, bool rich = false);
|
||||
QString textAccentFold(const QString &text);
|
||||
|
||||
// textlnk
|
||||
void textlnkOver(const TextLinkPtr &lnk);
|
||||
const TextLinkPtr &textlnkOver();
|
||||
|
||||
@@ -27,11 +27,17 @@ typedef int32 MsgId;
|
||||
void historyInit();
|
||||
|
||||
class HistoryItem;
|
||||
|
||||
void startGif(HistoryItem *row, const QString &file);
|
||||
void itemRemovedGif(HistoryItem *item);
|
||||
void itemReplacedGif(HistoryItem *oldItem, HistoryItem *newItem);
|
||||
void stopGif();
|
||||
|
||||
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) {
|
||||
@@ -76,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() {
|
||||
}
|
||||
@@ -146,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;
|
||||
@@ -202,6 +188,9 @@ struct PhotoData {
|
||||
ImagePtr full;
|
||||
ChatData *chat; // for chat photos connection
|
||||
// geo, caption
|
||||
|
||||
int32 cachew;
|
||||
QPixmap cache;
|
||||
};
|
||||
|
||||
class PhotoLink : public ITextLink {
|
||||
@@ -231,7 +220,7 @@ enum FileStatus {
|
||||
|
||||
struct VideoData {
|
||||
VideoData(const VideoId &id, 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) :
|
||||
id(id), access(access), user(user), date(date), duration(duration), w(w), h(h), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), fileType(0), openOnSave(0), loader(0) {
|
||||
id(id), access(access), user(user), date(date), duration(duration), w(w), h(h), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), fileType(0), openOnSave(0), openOnSaveMsgId(0), loader(0) {
|
||||
memset(md5, 0, sizeof(md5));
|
||||
}
|
||||
void forget() {
|
||||
@@ -251,7 +240,7 @@ struct VideoData {
|
||||
fileName = QString();
|
||||
modDate = QDateTime();
|
||||
if (!beforeDownload) {
|
||||
openOnSave = 0;
|
||||
openOnSave = openOnSaveMsgId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +281,7 @@ struct VideoData {
|
||||
int32 uploadOffset;
|
||||
|
||||
mtpTypeId fileType;
|
||||
int32 openOnSave;
|
||||
int32 openOnSave, openOnSaveMsgId;
|
||||
mtpFileLoader *loader;
|
||||
QString fileName;
|
||||
QDateTime modDate;
|
||||
@@ -335,7 +324,7 @@ public:
|
||||
|
||||
struct AudioData {
|
||||
AudioData(const AudioId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 dc = 0, int32 size = 0) :
|
||||
id(id), access(access), user(user), date(date), duration(duration), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), loader(0) {
|
||||
id(id), access(access), user(user), date(date), duration(duration), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0) {
|
||||
memset(md5, 0, sizeof(md5));
|
||||
}
|
||||
void forget() {
|
||||
@@ -354,7 +343,7 @@ struct AudioData {
|
||||
fileName = QString();
|
||||
modDate = QDateTime();
|
||||
if (!beforeDownload) {
|
||||
openOnSave = 0;
|
||||
openOnSave = openOnSaveMsgId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,7 +382,7 @@ struct AudioData {
|
||||
FileStatus status;
|
||||
int32 uploadOffset;
|
||||
|
||||
int32 openOnSave;
|
||||
int32 openOnSave, openOnSaveMsgId;
|
||||
mtpFileLoader *loader;
|
||||
QString fileName;
|
||||
QDateTime modDate;
|
||||
@@ -437,7 +426,7 @@ public:
|
||||
|
||||
struct DocumentData {
|
||||
DocumentData(const DocumentId &id, 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) :
|
||||
id(id), access(access), user(user), date(date), name(name), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), loader(0) {
|
||||
id(id), access(access), user(user), date(date), name(name), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0) {
|
||||
memset(md5, 0, sizeof(md5));
|
||||
}
|
||||
void forget() {
|
||||
@@ -457,7 +446,7 @@ struct DocumentData {
|
||||
fileName = QString();
|
||||
modDate = QDateTime();
|
||||
if (!beforeDownload) {
|
||||
openOnSave = 0;
|
||||
openOnSave = openOnSaveMsgId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,7 +485,7 @@ struct DocumentData {
|
||||
FileStatus status;
|
||||
int32 uploadOffset;
|
||||
|
||||
int32 openOnSave;
|
||||
int32 openOnSave, openOnSaveMsgId;
|
||||
mtpFileLoader *loader;
|
||||
QString fileName;
|
||||
QDateTime modDate;
|
||||
@@ -595,6 +584,7 @@ enum HistoryMediaType {
|
||||
MediaTypeContact,
|
||||
MediaTypeAudio,
|
||||
MediaTypeDocument,
|
||||
MediaTypeImageLink,
|
||||
|
||||
MediaTypeCount
|
||||
};
|
||||
@@ -639,6 +629,34 @@ inline MTPMessagesFilter typeToMediaFilter(MediaOverviewType &type) {
|
||||
return MTPMessagesFilter();
|
||||
}
|
||||
|
||||
struct MessageCursor {
|
||||
MessageCursor() : position(0), anchor(0), scroll(QFIXED_MAX) {
|
||||
}
|
||||
MessageCursor(int position, int anchor, int scroll) : position(position), anchor(anchor), scroll(scroll) {
|
||||
}
|
||||
MessageCursor(const QTextEdit &edit) {
|
||||
fillFrom(edit);
|
||||
}
|
||||
void fillFrom(const QTextEdit &edit) {
|
||||
QTextCursor c = edit.textCursor();
|
||||
position = c.position();
|
||||
anchor = c.anchor();
|
||||
QScrollBar *s = edit.verticalScrollBar();
|
||||
scroll = s ? s->value() : QFIXED_MAX;
|
||||
}
|
||||
void applyTo(QTextEdit &edit, bool *lock = 0) {
|
||||
if (lock) *lock = true;
|
||||
QTextCursor c = edit.textCursor();
|
||||
c.setPosition(anchor, QTextCursor::MoveAnchor);
|
||||
c.setPosition(position, QTextCursor::KeepAnchor);
|
||||
edit.setTextCursor(c);
|
||||
QScrollBar *s = edit.verticalScrollBar();
|
||||
if (s) s->setValue(scroll);
|
||||
if (lock) *lock = false;
|
||||
}
|
||||
int position, anchor, scroll;
|
||||
};
|
||||
|
||||
class HistoryMedia;
|
||||
class HistoryMessage;
|
||||
class HistoryUnreadBar;
|
||||
@@ -669,9 +687,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);
|
||||
@@ -692,7 +711,7 @@ struct History : public QList<HistoryBlock*> {
|
||||
MsgId minMsgId() const;
|
||||
MsgId maxMsgId() const;
|
||||
|
||||
int32 geomResize(int32 newWidth, int32 *ytransform = 0); // return new size
|
||||
int32 geomResize(int32 newWidth, int32 *ytransform = 0, bool dontRecountText = false); // return new size
|
||||
int32 width, height, msgCount, unreadCount;
|
||||
int32 inboxReadTill, outboxReadTill;
|
||||
HistoryItem *showFrom;
|
||||
@@ -741,10 +760,12 @@ struct History : public QList<HistoryBlock*> {
|
||||
}
|
||||
|
||||
QString draft;
|
||||
QTextCursor draftCur;
|
||||
MessageCursor draftCursor;
|
||||
int32 lastWidth, lastScrollTop;
|
||||
bool mute;
|
||||
|
||||
mtpRequestId sendRequestId;
|
||||
|
||||
// for dialog drawing
|
||||
Text nameText;
|
||||
void updateNameText();
|
||||
@@ -1066,7 +1087,7 @@ struct HistoryBlock : public QVector<HistoryItem*> {
|
||||
}
|
||||
void removeItem(HistoryItem *item);
|
||||
|
||||
int32 geomResize(int32 newWidth, int32 *ytransform); // return new size
|
||||
int32 geomResize(int32 newWidth, int32 *ytransform, bool dontRecountText); // return new size
|
||||
int32 y, height;
|
||||
History *history;
|
||||
};
|
||||
@@ -1077,7 +1098,8 @@ public:
|
||||
HistoryElem() : _height(0), _maxw(0) {
|
||||
}
|
||||
|
||||
virtual int32 resize(int32 width) = 0; // return new height
|
||||
virtual void initDimensions(const HistoryItem *parent = 0) = 0;
|
||||
virtual int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0) = 0; // return new height
|
||||
|
||||
int32 height() const {
|
||||
return _height;
|
||||
@@ -1171,12 +1193,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;
|
||||
}
|
||||
@@ -1230,20 +1246,22 @@ HistoryItem *regItem(HistoryItem *item, bool returnExisting = false);
|
||||
class HistoryMedia : public HistoryElem {
|
||||
public:
|
||||
|
||||
virtual void initDimensions(const HistoryItem *parent) {
|
||||
HistoryMedia(int32 width = 0) : w(width) {
|
||||
}
|
||||
|
||||
virtual HistoryMediaType type() const = 0;
|
||||
virtual const QString inDialogsText() const = 0;
|
||||
virtual bool hasPoint(int32 x, int32 y, int32 width = -1) const = 0;
|
||||
virtual const QString inHistoryText() 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;
|
||||
}
|
||||
@@ -1262,6 +1280,14 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 currentWidth() const {
|
||||
return w;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
int32 w;
|
||||
|
||||
};
|
||||
|
||||
class HistoryPhoto : public HistoryMedia {
|
||||
@@ -1271,22 +1297,25 @@ public:
|
||||
HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0);
|
||||
|
||||
void init();
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
int32 resize(int32 width);
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypePhoto;
|
||||
}
|
||||
const QString inDialogsText() const;
|
||||
bool hasPoint(int32 x, int32 y, int32 width = -1) const;
|
||||
const QString inHistoryText() 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 {
|
||||
return data;
|
||||
}
|
||||
|
||||
void updateFrom(const MTPMessageMedia &media);
|
||||
|
||||
TextLinkPtr lnk() const {
|
||||
return openl;
|
||||
}
|
||||
@@ -1299,7 +1328,7 @@ public:
|
||||
private:
|
||||
PhotoData *data;
|
||||
TextLinkPtr openl;
|
||||
int32 w;
|
||||
|
||||
};
|
||||
|
||||
QString formatSizeText(qint64 size);
|
||||
@@ -1308,18 +1337,16 @@ class HistoryVideo : public HistoryMedia {
|
||||
public:
|
||||
|
||||
HistoryVideo(const MTPDvideo &video, int32 width = 0);
|
||||
void reinit();
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
int32 resize(int32 width);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeVideo;
|
||||
}
|
||||
const QString inDialogsText() const;
|
||||
bool hasPoint(int32 x, int32 y, int32 width = -1) const;
|
||||
const QString inHistoryText() 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);
|
||||
}
|
||||
@@ -1331,8 +1358,7 @@ public:
|
||||
private:
|
||||
VideoData *data;
|
||||
TextLinkPtr _openl, _savel, _cancell;
|
||||
int32 w;
|
||||
|
||||
|
||||
QString _size;
|
||||
int32 _thumbw, _thumbx, _thumby;
|
||||
|
||||
@@ -1344,16 +1370,15 @@ class HistoryAudio : public HistoryMedia {
|
||||
public:
|
||||
|
||||
HistoryAudio(const MTPDaudio &audio, int32 width = 0);
|
||||
void reinit();
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
int32 resize(int32 width);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeAudio;
|
||||
}
|
||||
const QString inDialogsText() const;
|
||||
bool hasPoint(int32 x, int32 y, int32 width = -1) const;
|
||||
const QString inHistoryText() 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 uploading() const {
|
||||
return (data->status == FileUploading);
|
||||
@@ -1366,7 +1391,6 @@ public:
|
||||
private:
|
||||
AudioData *data;
|
||||
TextLinkPtr _openl, _savel, _cancell;
|
||||
int32 w;
|
||||
|
||||
QString _size;
|
||||
|
||||
@@ -1378,16 +1402,17 @@ class HistoryDocument : public HistoryMedia {
|
||||
public:
|
||||
|
||||
HistoryDocument(const MTPDdocument &document, int32 width = 0);
|
||||
void reinit();
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
|
||||
int32 resize(int32 width);
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeDocument;
|
||||
}
|
||||
const QString inDialogsText() const;
|
||||
bool hasPoint(int32 x, int32 y, int32 width = -1) const;
|
||||
const QString inHistoryText() const;
|
||||
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
|
||||
int32 countHeight(const HistoryItem *parent, int32 width = -1) const;
|
||||
bool uploading() const {
|
||||
return (data->status == FileUploading);
|
||||
}
|
||||
@@ -1407,7 +1432,6 @@ private:
|
||||
|
||||
DocumentData *data;
|
||||
TextLinkPtr _openl, _savel, _cancell;
|
||||
int32 w;
|
||||
|
||||
int32 _namew;
|
||||
QString _name, _size;
|
||||
@@ -1424,12 +1448,13 @@ public:
|
||||
void initDimensions(const HistoryItem *parent);
|
||||
|
||||
void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const;
|
||||
int32 resize(int32 width);
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
HistoryMediaType type() const {
|
||||
return MediaTypeContact;
|
||||
}
|
||||
const QString inDialogsText() const;
|
||||
bool hasPoint(int32 x, int32 y, int32 width) const;
|
||||
const QString inHistoryText() const;
|
||||
bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const;
|
||||
TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width) const;
|
||||
HistoryMedia *clone() const;
|
||||
|
||||
@@ -1437,12 +1462,89 @@ public:
|
||||
|
||||
private:
|
||||
int32 userId;
|
||||
int32 w, phonew;
|
||||
int32 phonew;
|
||||
Text name;
|
||||
QString phone;
|
||||
UserData *contact;
|
||||
};
|
||||
|
||||
void initImageLinkManager();
|
||||
void reinitImageLinkManager();
|
||||
void deinitImageLinkManager();
|
||||
|
||||
enum ImageLinkType {
|
||||
InvalidImageLink = 0,
|
||||
YouTubeLink,
|
||||
VimeoLink,
|
||||
InstagramLink,
|
||||
GoogleMapsLink
|
||||
};
|
||||
struct ImageLinkData {
|
||||
ImageLinkData(const QString &id) : id(id), type(InvalidImageLink), loading(false) {
|
||||
}
|
||||
|
||||
QString id;
|
||||
QString title, duration;
|
||||
ImagePtr thumb;
|
||||
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;
|
||||
const QString inHistoryText() 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;
|
||||
TextLinkPtr link;
|
||||
|
||||
};
|
||||
|
||||
class HistoryMessage : public HistoryItem {
|
||||
public:
|
||||
|
||||
@@ -1453,6 +1555,7 @@ public:
|
||||
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, HistoryMedia *media);
|
||||
|
||||
void initMedia(const MTPMessageMedia &media, QString ¤tText);
|
||||
void initDimensions(const HistoryItem *parent = 0);
|
||||
void initDimensions(const QString &text);
|
||||
void fromNameUpdated() const;
|
||||
|
||||
@@ -1461,15 +1564,13 @@ public:
|
||||
void draw(QPainter &p, uint32 selection) const;
|
||||
virtual void drawMessageText(QPainter &p, const QRect &trect, uint32 selection) const;
|
||||
|
||||
int32 resize(int32 width);
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
bool hasPoint(int32 x, int32 y) const;
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
|
||||
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
|
||||
uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
|
||||
return _text.adjustSelection(from, to, type);
|
||||
}
|
||||
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;
|
||||
@@ -1516,7 +1617,7 @@ public:
|
||||
|
||||
void draw(QPainter &p, uint32 selection) const;
|
||||
void drawMessageText(QPainter &p, const QRect &trect, uint32 selection) const;
|
||||
int32 resize(int32 width);
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
bool hasPoint(int32 x, int32 y) const;
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
|
||||
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
|
||||
@@ -1546,15 +1647,16 @@ public:
|
||||
// HistoryServiceMsg(History *history, HistoryBlock *block, const MTPDgeoChatMessageService &msg);
|
||||
HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, bool out = false, bool unread = false, HistoryMedia *media = 0);
|
||||
|
||||
void initDimensions(const HistoryItem *parent = 0);
|
||||
|
||||
void draw(QPainter &p, uint32 selection) const;
|
||||
int32 resize(int32 width);
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
bool hasPoint(int32 x, int32 y) const;
|
||||
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
|
||||
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
|
||||
uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
|
||||
return _text.adjustSelection(from, to, type);
|
||||
}
|
||||
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;
|
||||
@@ -1613,10 +1715,12 @@ public:
|
||||
|
||||
HistoryUnreadBar(History *history, HistoryBlock *block, int32 count, const QDateTime &date);
|
||||
|
||||
void initDimensions(const HistoryItem *parent = 0);
|
||||
|
||||
void setCount(int32 count);
|
||||
|
||||
void draw(QPainter &p, uint32 selection) const;
|
||||
int32 resize(int32 width);
|
||||
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
|
||||
|
||||
void drawInDialog(QPainter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const;
|
||||
QString notificationText() const;
|
||||
|
||||
@@ -28,6 +28,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
|
||||
#include "fileuploader.h"
|
||||
#include "supporttl.h"
|
||||
|
||||
#include "localstorage.h"
|
||||
|
||||
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
|
||||
|
||||
HistoryList::HistoryList(HistoryWidget *historyWidget, ScrollArea *scroll, History *history) : QWidget(0)
|
||||
@@ -451,8 +453,6 @@ void HistoryList::dragActionStart(const QPoint &screenPos, Qt::MouseButton butto
|
||||
_dragAction = NoDrag;
|
||||
} else if (_dragAction == NoDrag) {
|
||||
_dragItem = 0;
|
||||
} else {
|
||||
connect(App::main(), SIGNAL(historyItemDeleted(HistoryItem*)), this, SLOT(itemRemoved(HistoryItem*)), Qt::UniqueConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,20 +460,23 @@ void HistoryList::dragActionCancel() {
|
||||
_dragItem = 0;
|
||||
_dragAction = NoDrag;
|
||||
_dragStartPos = QPoint(0, 0);
|
||||
_dragSelFrom = _dragSelTo = 0;
|
||||
historyWidget->noSelectingScroll();
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -481,6 +484,20 @@ void HistoryList::itemRemoved(HistoryItem *item) {
|
||||
updateDragSelection(_dragSelFrom, _dragSelTo, _dragSelecting, true);
|
||||
}
|
||||
|
||||
void HistoryList::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
||||
if (_dragItem == oldItem) _dragItem = newItem;
|
||||
|
||||
SelectedItems::iterator i = _selected.find(oldItem);
|
||||
if (i != _selected.cend()) {
|
||||
uint32 v = i.value();
|
||||
_selected.erase(i);
|
||||
_selected.insert(newItem, v);
|
||||
}
|
||||
|
||||
if (_dragSelFrom == oldItem) _dragSelFrom = newItem;
|
||||
if (_dragSelTo == oldItem) _dragSelTo = newItem;
|
||||
}
|
||||
|
||||
void HistoryList::dragActionFinish(const QPoint &screenPos, Qt::MouseButton button) {
|
||||
TextLinkPtr needClick;
|
||||
|
||||
@@ -506,6 +523,8 @@ void HistoryList::dragActionFinish(const QPoint &screenPos, Qt::MouseButton butt
|
||||
}
|
||||
if (needClick) {
|
||||
needClick->onClick(button);
|
||||
dragActionCancel();
|
||||
return;
|
||||
}
|
||||
if (_dragAction == PrepareSelect && !needClick && !_dragWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullItemSel) {
|
||||
SelectedItems::iterator i = _selected.find(_dragItem);
|
||||
@@ -616,106 +635,98 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
}
|
||||
|
||||
_contextMenuLnk = textlnkOver();
|
||||
if (_contextMenuLnk && dynamic_cast<TextLink*>(_contextMenuLnk.data())) {
|
||||
PhotoLink *lnkPhoto = dynamic_cast<PhotoLink*>(_contextMenuLnk.data());
|
||||
VideoLink *lnkVideo = dynamic_cast<VideoLink*>(_contextMenuLnk.data());
|
||||
AudioLink *lnkAudio = dynamic_cast<AudioLink*>(_contextMenuLnk.data());
|
||||
DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data());
|
||||
if (lnkPhoto || lnkVideo || lnkAudio || lnkDocument) {
|
||||
_menu = new ContextMenu(historyWidget);
|
||||
if (isUponSelected > 0) {
|
||||
_menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true);
|
||||
}
|
||||
_menu->addAction(lang(lng_context_open_link), this, SLOT(openContextUrl()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_copy_link), this, SLOT(copyContextUrl()))->setEnabled(true);
|
||||
} else if (_contextMenuLnk && dynamic_cast<EmailLink*>(_contextMenuLnk.data())) {
|
||||
_menu = new ContextMenu(historyWidget);
|
||||
if (isUponSelected > 0) {
|
||||
_menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true);
|
||||
}
|
||||
_menu->addAction(lang(lng_context_open_email), this, SLOT(openContextUrl()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_copy_email), this, SLOT(copyContextUrl()))->setEnabled(true);
|
||||
} else if (_contextMenuLnk && dynamic_cast<HashtagLink*>(_contextMenuLnk.data())) {
|
||||
_menu = new ContextMenu(historyWidget);
|
||||
if (isUponSelected > 0) {
|
||||
_menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true);
|
||||
}
|
||||
_menu->addAction(lang(lng_context_open_hashtag), this, SLOT(openContextUrl()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_copy_hashtag), this, SLOT(copyContextUrl()))->setEnabled(true);
|
||||
} else {
|
||||
PhotoLink *lnkPhoto = dynamic_cast<PhotoLink*>(_contextMenuLnk.data());
|
||||
VideoLink *lnkVideo = dynamic_cast<VideoLink*>(_contextMenuLnk.data());
|
||||
AudioLink *lnkAudio = dynamic_cast<AudioLink*>(_contextMenuLnk.data());
|
||||
DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data());
|
||||
if (lnkPhoto || lnkVideo || lnkAudio || lnkDocument) {
|
||||
_menu = new ContextMenu(historyWidget);
|
||||
if (isUponSelected > 0) {
|
||||
_menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true);
|
||||
}
|
||||
if (lnkPhoto) {
|
||||
_menu->addAction(lang(lng_context_open_image), this, SLOT(openContextUrl()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextImage()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_copy_image), this, SLOT(copyContextImage()))->setEnabled(true);
|
||||
if (lnkPhoto) {
|
||||
_menu->addAction(lang(lng_context_open_image), this, SLOT(openContextUrl()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextImage()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_copy_image), this, SLOT(copyContextImage()))->setEnabled(true);
|
||||
} else {
|
||||
if ((lnkVideo && lnkVideo->video()->loader) || (lnkAudio && lnkAudio->audio()->loader) || (lnkDocument && lnkDocument->document()->loader)) {
|
||||
_menu->addAction(lang(lng_context_cancel_download), this, SLOT(cancelContextDownload()))->setEnabled(true);
|
||||
} else {
|
||||
if ((lnkVideo && lnkVideo->video()->loader) || (lnkAudio && lnkAudio->audio()->loader) || (lnkDocument && lnkDocument->document()->loader)) {
|
||||
_menu->addAction(lang(lng_context_cancel_download), this, SLOT(cancelContextDownload()))->setEnabled(true);
|
||||
} else {
|
||||
if ((lnkVideo && !lnkVideo->video()->already(true).isEmpty()) || (lnkAudio && !lnkAudio->audio()->already(true).isEmpty()) || (lnkDocument && !lnkDocument->document()->already(true).isEmpty())) {
|
||||
_menu->addAction(lang(cPlatform() == dbipMac ? lng_context_show_in_finder : lng_context_show_in_folder), this, SLOT(showContextInFolder()))->setEnabled(true);
|
||||
}
|
||||
_menu->addAction(lang(lnkVideo ? lng_context_open_video : (lnkAudio ? lng_context_open_audio : lng_context_open_document)), this, SLOT(openContextFile()))->setEnabled(true);
|
||||
_menu->addAction(lang(lnkVideo ? lng_context_save_video : (lnkAudio ? lng_context_save_audio : lng_context_save_document)), this, SLOT(saveContextFile()))->setEnabled(true);
|
||||
if ((lnkVideo && !lnkVideo->video()->already(true).isEmpty()) || (lnkAudio && !lnkAudio->audio()->already(true).isEmpty()) || (lnkDocument && !lnkDocument->document()->already(true).isEmpty())) {
|
||||
_menu->addAction(lang(cPlatform() == dbipMac ? lng_context_show_in_finder : lng_context_show_in_folder), this, SLOT(showContextInFolder()))->setEnabled(true);
|
||||
}
|
||||
_menu->addAction(lang(lnkVideo ? lng_context_open_video : (lnkAudio ? lng_context_open_audio : lng_context_open_document)), this, SLOT(openContextFile()))->setEnabled(true);
|
||||
_menu->addAction(lang(lnkVideo ? lng_context_save_video : (lnkAudio ? lng_context_save_audio : lng_context_save_document)), this, SLOT(saveContextFile()))->setEnabled(true);
|
||||
}
|
||||
if (isUponSelected > 1) {
|
||||
_menu->addAction(lang(lng_context_forward_selected), historyWidget, SLOT(onForwardSelected()));
|
||||
_menu->addAction(lang(lng_context_delete_selected), historyWidget, SLOT(onDeleteSelected()));
|
||||
_menu->addAction(lang(lng_context_clear_selection), historyWidget, SLOT(onClearSelected()));
|
||||
} else if (App::hoveredLinkItem()) {
|
||||
if (isUponSelected != -2) {
|
||||
if (dynamic_cast<HistoryMessage*>(App::hoveredLinkItem())) {
|
||||
_menu->addAction(lang(lng_context_forward_msg), historyWidget, SLOT(forwardMessage()))->setEnabled(true);
|
||||
}
|
||||
_menu->addAction(lang(lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true);
|
||||
}
|
||||
_menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true);
|
||||
App::contextItem(App::hoveredLinkItem());
|
||||
}
|
||||
} else { // maybe cursor on some text history item?
|
||||
HistoryItem *item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem();
|
||||
bool canDelete = (item && item->itemType() == HistoryItem::MsgType);
|
||||
bool canForward = canDelete && (item->id > 0) && !item->serviceMsg();
|
||||
|
||||
HistoryMessage *msg = dynamic_cast<HistoryMessage*>(item);
|
||||
HistoryServiceMsg *srv = dynamic_cast<HistoryServiceMsg*>(item);
|
||||
|
||||
if (isUponSelected > 0) {
|
||||
if (!_menu) _menu = new ContextMenu(this);
|
||||
_menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true);
|
||||
} else if (item && !isUponSelected && !_contextMenuLnk) {
|
||||
QString contextMenuText = item->selectedText(FullItemSel);
|
||||
if (!contextMenuText.isEmpty()) {
|
||||
if (!_menu) _menu = new ContextMenu(this);
|
||||
_menu->addAction(lang(lng_context_copy_text), this, SLOT(copyContextText()))->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (isUponSelected > 1) {
|
||||
if (!_menu) _menu = new ContextMenu(this);
|
||||
_menu->addAction(lang(lng_context_forward_selected), historyWidget, SLOT(onForwardSelected()));
|
||||
_menu->addAction(lang(lng_context_delete_selected), historyWidget, SLOT(onDeleteSelected()));
|
||||
_menu->addAction(lang(lng_context_clear_selection), historyWidget, SLOT(onClearSelected()));
|
||||
} else {
|
||||
if (!_menu) _menu = new ContextMenu(this);
|
||||
if (isUponSelected != -2) {
|
||||
if (canForward) {
|
||||
_menu->addAction(lang(lng_context_forward_msg), historyWidget, SLOT(forwardMessage()))->setEnabled(true);
|
||||
}
|
||||
|
||||
if (canDelete) {
|
||||
_menu->addAction(lang((msg && msg->uploading()) ? lng_context_cancel_upload : lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true);
|
||||
}
|
||||
}
|
||||
_menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true);
|
||||
}
|
||||
App::contextItem(item);
|
||||
}
|
||||
if (isUponSelected > 1) {
|
||||
_menu->addAction(lang(lng_context_forward_selected), historyWidget, SLOT(onForwardSelected()));
|
||||
_menu->addAction(lang(lng_context_delete_selected), historyWidget, SLOT(onDeleteSelected()));
|
||||
_menu->addAction(lang(lng_context_clear_selection), historyWidget, SLOT(onClearSelected()));
|
||||
} else if (App::hoveredLinkItem()) {
|
||||
if (isUponSelected != -2) {
|
||||
if (dynamic_cast<HistoryMessage*>(App::hoveredLinkItem())) {
|
||||
_menu->addAction(lang(lng_context_forward_msg), historyWidget, SLOT(forwardMessage()))->setEnabled(true);
|
||||
}
|
||||
_menu->addAction(lang(lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true);
|
||||
}
|
||||
_menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true);
|
||||
App::contextItem(App::hoveredLinkItem());
|
||||
}
|
||||
} else { // maybe cursor on some text history item?
|
||||
HistoryItem *item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem();
|
||||
bool canDelete = (item && item->itemType() == HistoryItem::MsgType);
|
||||
bool canForward = canDelete && (item->id > 0) && !item->serviceMsg();
|
||||
|
||||
HistoryMessage *msg = dynamic_cast<HistoryMessage*>(item);
|
||||
HistoryServiceMsg *srv = dynamic_cast<HistoryServiceMsg*>(item);
|
||||
|
||||
if (isUponSelected > 0) {
|
||||
if (!_menu) _menu = new ContextMenu(this);
|
||||
_menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true);
|
||||
} else if (item && !isUponSelected && !_contextMenuLnk) {
|
||||
QString contextMenuText = item->selectedText(FullItemSel);
|
||||
if (!contextMenuText.isEmpty()) {
|
||||
if (!_menu) _menu = new ContextMenu(this);
|
||||
_menu->addAction(lang(lng_context_copy_text), this, SLOT(copyContextText()))->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (_contextMenuLnk && dynamic_cast<TextLink*>(_contextMenuLnk.data())) {
|
||||
if (!_menu) _menu = new ContextMenu(historyWidget);
|
||||
_menu->addAction(lang(lng_context_open_link), this, SLOT(openContextUrl()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_copy_link), this, SLOT(copyContextUrl()))->setEnabled(true);
|
||||
} else if (_contextMenuLnk && dynamic_cast<EmailLink*>(_contextMenuLnk.data())) {
|
||||
if (!_menu) _menu = new ContextMenu(historyWidget);
|
||||
_menu->addAction(lang(lng_context_open_email), this, SLOT(openContextUrl()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_copy_email), this, SLOT(copyContextUrl()))->setEnabled(true);
|
||||
} else if (_contextMenuLnk && dynamic_cast<HashtagLink*>(_contextMenuLnk.data())) {
|
||||
if (!_menu) _menu = new ContextMenu(historyWidget);
|
||||
_menu->addAction(lang(lng_context_open_hashtag), this, SLOT(openContextUrl()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_copy_hashtag), this, SLOT(copyContextUrl()))->setEnabled(true);
|
||||
} else {
|
||||
}
|
||||
if (isUponSelected > 1) {
|
||||
if (!_menu) _menu = new ContextMenu(this);
|
||||
_menu->addAction(lang(lng_context_forward_selected), historyWidget, SLOT(onForwardSelected()));
|
||||
_menu->addAction(lang(lng_context_delete_selected), historyWidget, SLOT(onDeleteSelected()));
|
||||
_menu->addAction(lang(lng_context_clear_selection), historyWidget, SLOT(onClearSelected()));
|
||||
} else {
|
||||
if (!_menu) _menu = new ContextMenu(this);
|
||||
if (isUponSelected != -2) {
|
||||
if (canForward) {
|
||||
_menu->addAction(lang(lng_context_forward_msg), historyWidget, SLOT(forwardMessage()))->setEnabled(true);
|
||||
}
|
||||
|
||||
if (canDelete) {
|
||||
_menu->addAction(lang((msg && msg->uploading()) ? lng_context_cancel_upload : lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true);
|
||||
}
|
||||
}
|
||||
_menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true);
|
||||
}
|
||||
App::contextItem(item);
|
||||
}
|
||||
|
||||
if (_menu) {
|
||||
_menu->deleteOnHide();
|
||||
connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*)));
|
||||
@@ -821,24 +832,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();
|
||||
}
|
||||
@@ -882,9 +875,9 @@ void HistoryList::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
int32 HistoryList::recountHeight() {
|
||||
int32 HistoryList::recountHeight(bool dontRecountText) {
|
||||
int32 st = hist->lastScrollTop;
|
||||
hist->geomResize(scrollArea->width(), &st);
|
||||
hist->geomResize(scrollArea->width(), &st, dontRecountText);
|
||||
return st;
|
||||
}
|
||||
|
||||
@@ -977,6 +970,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) {
|
||||
@@ -1006,7 +1007,7 @@ void HistoryList::fillSelectedItems(SelectedItemSet &sel, bool forDelete) {
|
||||
for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) {
|
||||
HistoryItem *item = i.key();
|
||||
if (item->itemType() == HistoryItem::MsgType && ((item->id > 0 && !item->serviceMsg()) || forDelete)) {
|
||||
sel.insert(item->y + item->block()->y, item);
|
||||
sel.insert(item->id, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1222,7 +1223,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) {
|
||||
@@ -1528,7 +1529,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
|
||||
, _attachDragDocument(this)
|
||||
, _attachDragPhoto(this)
|
||||
, imageLoader(this)
|
||||
, noTypingUpdate(false)
|
||||
, _synthedTextUpdate(false)
|
||||
, loadingChatId(0)
|
||||
, loadingRequestId(0)
|
||||
, serviceImageCacheSize(0)
|
||||
@@ -1537,7 +1538,10 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
|
||||
, titlePeerTextWidth(0)
|
||||
, bg(st::msgBG)
|
||||
, hiderOffered(false)
|
||||
, _scrollDelta(0) {
|
||||
, _scrollDelta(0)
|
||||
, _typingRequest(0)
|
||||
, _saveDraftStart(0)
|
||||
, _saveDraftText(false) {
|
||||
_scroll.setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
setAcceptDrops(true);
|
||||
@@ -1547,8 +1551,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()));
|
||||
@@ -1558,12 +1562,20 @@ 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()));
|
||||
|
||||
_saveDraftTimer.setSingleShot(true);
|
||||
connect(&_saveDraftTimer, SIGNAL(timeout()), this, SLOT(onDraftSave()));
|
||||
connect(_field.verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onDraftSaveDelayed()));
|
||||
connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onDraftSaveDelayed()));
|
||||
|
||||
_scroll.hide();
|
||||
_scroll.move(0, 0);
|
||||
|
||||
@@ -1594,14 +1606,68 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
|
||||
|
||||
void HistoryWidget::onTextChange() {
|
||||
updateTyping();
|
||||
|
||||
if (!hist || _synthedTextUpdate) return;
|
||||
_saveDraftText = true;
|
||||
onDraftSave(true);
|
||||
}
|
||||
|
||||
void HistoryWidget::onDraftSaveDelayed() {
|
||||
if (!hist || _synthedTextUpdate) return;
|
||||
if (!_field.textCursor().anchor() && !_field.textCursor().position() && !_field.verticalScrollBar()->value()) {
|
||||
if (!Local::hasDraftPositions(hist->peer->id)) return;
|
||||
}
|
||||
onDraftSave(true);
|
||||
}
|
||||
|
||||
void HistoryWidget::onDraftSave(bool delayed) {
|
||||
if (!hist) return;
|
||||
if (delayed) {
|
||||
uint64 ms = getms();
|
||||
if (!_saveDraftStart) {
|
||||
_saveDraftStart = ms;
|
||||
return _saveDraftTimer.start(SaveDraftTimeout);
|
||||
} else if (ms - _saveDraftStart < SaveDraftAnywayTimeout) {
|
||||
return _saveDraftTimer.start(SaveDraftTimeout);
|
||||
}
|
||||
}
|
||||
writeDraft();
|
||||
}
|
||||
|
||||
void HistoryWidget::writeDraft(const QString *text, const MessageCursor *cursor) {
|
||||
bool save = hist && (_saveDraftStart > 0);
|
||||
_saveDraftStart = 0;
|
||||
_saveDraftTimer.stop();
|
||||
if (_saveDraftText) {
|
||||
if (save) Local::writeDraft(hist->peer->id, text ? (*text) : _field.getText());
|
||||
_saveDraftText = false;
|
||||
}
|
||||
if (save) Local::writeDraftPositions(hist->peer->id, cursor ? (*cursor) : MessageCursor(_field));
|
||||
}
|
||||
|
||||
void HistoryWidget::cancelTyping() {
|
||||
if (_typingRequest) {
|
||||
MTP::cancel(_typingRequest);
|
||||
_typingRequest = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::updateTyping(bool typing) {
|
||||
uint64 ms = getms() + 10000;
|
||||
if (noTypingUpdate || !hist || (typing && (hist->myTyping + 5000 > ms)) || (!typing && (hist->myTyping + 5000 <= ms))) return;
|
||||
uint64 ms = getms(true) + 10000;
|
||||
if (_synthedTextUpdate || !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() {
|
||||
@@ -1658,9 +1724,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) {
|
||||
@@ -1685,6 +1748,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||
}
|
||||
updateTyping(false);
|
||||
}
|
||||
stopGif();
|
||||
clearLoadingAround();
|
||||
|
||||
if (_list) {
|
||||
@@ -1699,7 +1763,10 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||
}
|
||||
if (hist) {
|
||||
hist->draft = _field.getText();
|
||||
hist->draftCur = _field.textCursor();
|
||||
hist->draftCursor.fillFrom(_field);
|
||||
|
||||
writeDraft(&hist->draft, &hist->draftCursor);
|
||||
|
||||
if (hist->readyForWork() && _scroll.scrollTop() + 1 <= _scroll.scrollTopMax()) {
|
||||
hist->lastWidth = _list->width();
|
||||
} else {
|
||||
@@ -1747,10 +1814,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;
|
||||
@@ -1780,13 +1844,19 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||
|
||||
App::main()->peerUpdated(histPeer);
|
||||
|
||||
noTypingUpdate = true;
|
||||
_field.setPlainText(hist->draft);
|
||||
_field.setFocus();
|
||||
if (!hist->draft.isEmpty()) {
|
||||
_field.setTextCursor(hist->draftCur);
|
||||
setFieldText(hist->draft);
|
||||
_field.setFocus();
|
||||
hist->draftCursor.applyTo(_field, &_synthedTextUpdate);
|
||||
} else {
|
||||
QString draft = Local::readDraft(hist->peer->id);
|
||||
setFieldText(draft);
|
||||
_field.setFocus();
|
||||
if (!draft.isEmpty()) {
|
||||
MessageCursor cur = Local::readDraftPositions(hist->peer->id);
|
||||
cur.applyTo(_field, &_synthedTextUpdate);
|
||||
}
|
||||
}
|
||||
noTypingUpdate = false;
|
||||
|
||||
connect(&_scroll, SIGNAL(geometryChanged()), _list, SLOT(onParentGeometryChanged()));
|
||||
connect(&_scroll, SIGNAL(scrolled()), _list, SLOT(onUpdateSelected()));
|
||||
@@ -2094,7 +2164,7 @@ void HistoryWidget::loadMessages() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!histPreloading && (!hist->readyForWork() || _scroll.scrollTop() < 3 * _scroll.height())) {
|
||||
if (!histPreloading && (!hist->readyForWork() || _scroll.scrollTop() < PreloadHeightsCount * _scroll.height())) {
|
||||
MsgId min = hist->minMsgId();
|
||||
int32 offset = 0, loadCount = min ? MessagesPerPage : MessagesFirstLoad;
|
||||
if (!min && hist->activeMsgId) {
|
||||
@@ -2125,7 +2195,7 @@ void HistoryWidget::loadMessagesDown() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!histPreloadingDown && hist->readyForWork() && (_scroll.scrollTop() + 3 * _scroll.height() > _scroll.scrollTopMax())) {
|
||||
if (!histPreloadingDown && hist->readyForWork() && (_scroll.scrollTop() + PreloadHeightsCount * _scroll.height() > _scroll.scrollTopMax())) {
|
||||
MsgId max = hist->maxMsgId();
|
||||
if (max) {
|
||||
int32 loadCount = MessagesPerPage, offset = -loadCount;
|
||||
@@ -2156,11 +2226,11 @@ void HistoryWidget::onListScroll() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hist->readyForWork() && (_scroll.scrollTop() + 3 * _scroll.height() > _scroll.scrollTopMax())) {
|
||||
if (hist->readyForWork() && (_scroll.scrollTop() + PreloadHeightsCount * _scroll.height() > _scroll.scrollTopMax())) {
|
||||
loadMessagesDown();
|
||||
}
|
||||
|
||||
if (!hist->readyForWork() || _scroll.scrollTop() < 3 * _scroll.height()) {
|
||||
if (!hist->readyForWork() || _scroll.scrollTop() < PreloadHeightsCount * _scroll.height()) {
|
||||
loadMessages();
|
||||
} else {
|
||||
checkUnreadLoaded(true);
|
||||
@@ -2176,8 +2246,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();
|
||||
}
|
||||
@@ -2189,31 +2259,25 @@ 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()));
|
||||
|
||||
setFieldText(QString());
|
||||
_saveDraftText = true;
|
||||
_saveDraftStart = getms();
|
||||
onDraftSave();
|
||||
|
||||
MTP::send(MTPmessages_SendMessage(histInputPeer, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId));
|
||||
_field.setPlainText("");
|
||||
if (!_attachType.isHidden()) _attachType.hideStart();
|
||||
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
|
||||
}
|
||||
|
||||
_field.setFocus();
|
||||
}
|
||||
|
||||
@@ -2235,23 +2299,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);
|
||||
@@ -2266,14 +2331,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) {
|
||||
@@ -2282,25 +2349,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) {
|
||||
@@ -2668,7 +2739,7 @@ void HistoryWidget::updateOnlineDisplay(int32 x, int32 w) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
text = App::onlineText(histPeer->asUser()->onlineTill, t);
|
||||
text = App::onlineText(histPeer->asUser(), t);
|
||||
}
|
||||
if (titlePeerText != text) {
|
||||
titlePeerText = text;
|
||||
@@ -2735,11 +2806,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();
|
||||
@@ -2776,22 +2851,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;
|
||||
@@ -2812,10 +2890,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) {
|
||||
@@ -2826,7 +2906,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();
|
||||
@@ -2840,7 +2920,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2855,7 +2936,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2871,7 +2953,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2944,7 +3027,19 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown) {
|
||||
void HistoryWidget::itemRemoved(HistoryItem *item) {
|
||||
if (_list) _list->itemRemoved(item);
|
||||
}
|
||||
|
||||
void HistoryWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
||||
if (_list) _list->itemReplaced(oldItem, newItem);
|
||||
}
|
||||
|
||||
void HistoryWidget::itemResized(HistoryItem *row) {
|
||||
updateListSize(0, false, false, row);
|
||||
}
|
||||
|
||||
void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown, HistoryItem *resizedItem) {
|
||||
if (!hist || (!_histInited && !initial)) return;
|
||||
|
||||
if (!App::wnd()->isVisible()) return; // scrollTopMax etc are not working after recountHeight()
|
||||
@@ -2959,12 +3054,22 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown)
|
||||
if (!initial) {
|
||||
hist->lastScrollTop = _scroll.scrollTop();
|
||||
}
|
||||
int32 newSt = _list->recountHeight();
|
||||
int32 newSt = _list->recountHeight(!!resizedItem);
|
||||
bool washidden = _scroll.isHidden();
|
||||
if (washidden) {
|
||||
_scroll.show();
|
||||
}
|
||||
_list->updateSize();
|
||||
if (resizedItem && !resizedItem->detached()) {
|
||||
int32 firstItemY = _list->height() - hist->height - st::historyPadding;
|
||||
if (newSt + _scroll.height() < firstItemY + resizedItem->block()->y + resizedItem->y + resizedItem->height()) {
|
||||
newSt = firstItemY + resizedItem->block()->y + resizedItem->y + resizedItem->height() - _scroll.height();
|
||||
}
|
||||
if (newSt > firstItemY + resizedItem->block()->y + resizedItem->y) {
|
||||
newSt = firstItemY + resizedItem->block()->y + resizedItem->y;
|
||||
}
|
||||
wasAtBottom = false;
|
||||
}
|
||||
if (washidden) {
|
||||
_scroll.hide();
|
||||
}
|
||||
@@ -3067,7 +3172,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
|
||||
void HistoryWidget::onFieldTabbed() {
|
||||
QString v = _field.getText(), t = supportTemplate(v.trimmed());
|
||||
QString v = _field.getText(), t = supportTemplate(v);
|
||||
if (!t.isEmpty()) {
|
||||
bool isImg = t.startsWith(qsl("img:")), isFile = t.startsWith(qsl("file:")), isContact = t.startsWith(qsl("contact:"));
|
||||
if (isImg || isFile) {
|
||||
@@ -3080,11 +3185,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) {
|
||||
@@ -3096,13 +3201,12 @@ void HistoryWidget::onFieldTabbed() {
|
||||
}
|
||||
QStringList data = contact.split(QChar(' '));
|
||||
if (data.size() > 1) {
|
||||
_field.setPlainText(text);
|
||||
|
||||
QString phone = data.at(0).trimmed(), fname = data.at(1).trimmed(), lname = (data.size() > 2) ? data.at(2).trimmed() : QString();
|
||||
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);
|
||||
@@ -3110,6 +3214,17 @@ void HistoryWidget::onFieldTabbed() {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::setFieldText(const QString &text) {
|
||||
_synthedTextUpdate = true;
|
||||
_field.setPlainText(text);
|
||||
_synthedTextUpdate = false;
|
||||
}
|
||||
|
||||
void HistoryWidget::onCancel() {
|
||||
showPeer(0);
|
||||
emit cancelled();
|
||||
}
|
||||
|
||||
void HistoryWidget::peerUpdated(PeerData *data) {
|
||||
if (data && data == histPeer) {
|
||||
updateListSize();
|
||||
@@ -3159,6 +3274,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();
|
||||
}
|
||||
|
||||
@@ -3169,9 +3287,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();
|
||||
}
|
||||
|
||||
@@ -3285,24 +3406,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();
|
||||
}
|
||||
|
||||
@@ -63,19 +63,22 @@ public:
|
||||
void touchScrollUpdated(const QPoint &screenPos);
|
||||
QPoint mapMouseToItem(QPoint p, HistoryItem *item);
|
||||
|
||||
int32 recountHeight();
|
||||
int32 recountHeight(bool dontRecountText);
|
||||
void updateSize();
|
||||
|
||||
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);
|
||||
void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true);
|
||||
void selectItem(HistoryItem *item);
|
||||
|
||||
void itemRemoved(HistoryItem *item);
|
||||
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
|
||||
|
||||
~HistoryList();
|
||||
|
||||
public slots:
|
||||
@@ -85,8 +88,6 @@ public slots:
|
||||
|
||||
void showLinkTip();
|
||||
|
||||
void itemRemoved(HistoryItem *item);
|
||||
|
||||
void openContextUrl();
|
||||
void copyContextUrl();
|
||||
void saveContextImage();
|
||||
@@ -282,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();
|
||||
|
||||
@@ -308,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;
|
||||
@@ -333,6 +333,9 @@ public:
|
||||
void stopAnimActive();
|
||||
|
||||
void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true);
|
||||
void itemRemoved(HistoryItem *item);
|
||||
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
|
||||
void itemResized(HistoryItem *item);
|
||||
|
||||
~HistoryWidget();
|
||||
|
||||
@@ -343,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);
|
||||
@@ -355,7 +362,7 @@ public slots:
|
||||
|
||||
void onListScroll();
|
||||
void onHistoryToEnd();
|
||||
void onSend();
|
||||
void onSend(bool ctrlShiftEnter = false);
|
||||
|
||||
void onPhotoSelect();
|
||||
void onDocumentSelect();
|
||||
@@ -389,14 +396,20 @@ public slots:
|
||||
|
||||
void onAnimActiveStep();
|
||||
|
||||
void onDraftSaveDelayed();
|
||||
void onDraftSave(bool delayed = false);
|
||||
|
||||
private:
|
||||
|
||||
bool messagesFailed(const RPCError &error, mtpRequestId requestId);
|
||||
void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false);
|
||||
void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false, HistoryItem *resizedItem = 0);
|
||||
void addMessagesToFront(const QVector<MTPMessage> &messages);
|
||||
void addMessagesToBack(const QVector<MTPMessage> &messages);
|
||||
void chatLoaded(const MTPmessages_ChatFull &res);
|
||||
|
||||
void writeDraft(const QString *text = 0, const MessageCursor *cursor = 0);
|
||||
void setFieldText(const QString &text);
|
||||
|
||||
QStringList getMediasFromMime(const QMimeData *d);
|
||||
DragState getDragState(const QMimeData *d);
|
||||
|
||||
@@ -431,7 +444,7 @@ private:
|
||||
int32 _selCount; // < 0 - text selected, focus list, not _field
|
||||
|
||||
LocalImageLoader imageLoader;
|
||||
bool noTypingUpdate;
|
||||
bool _synthedTextUpdate;
|
||||
|
||||
PeerId loadingChatId;
|
||||
mtpRequestId loadingRequestId;
|
||||
@@ -458,5 +471,12 @@ private:
|
||||
QTimer _animActiveTimer;
|
||||
float64 _animActiveStart;
|
||||
|
||||
mtpRequestId _typingRequest;
|
||||
QTimer _typingStopTimer;
|
||||
|
||||
uint64 _saveDraftStart;
|
||||
bool _saveDraftText;
|
||||
QTimer _saveDraftTimer;
|
||||
|
||||
};
|
||||
|
||||
|
||||