diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f906731af4..435e40e9dc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,6 +11,7 @@ This document describes how you can contribute to Telegram Desktop. Please read
* [Pull upstream changes into your fork regularly](#pull-upstream-changes-into-your-fork-regularly)
* [How to get your pull request accepted](#how-to-get-your-pull-request-accepted)
* [Keep your pull requests limited to a single issue](#keep-your-pull-requests-limited-to-a-single-issue)
+ * [Squash your commits to a single commit](#squash-your-commits-to-a-single-commit)
* [Don't mix code changes with whitespace cleanup](#dont-mix-code-changes-with-whitespace-cleanup)
* [Keep your code simple!](#keep-your-code-simple)
* [Test your changes!](#test-your-changes)
@@ -107,6 +108,21 @@ Pull requests should be as small/atomic as possible. Large, wide-sweeping change
* If you are making spelling corrections in the docs, don't modify other files.
* If you are adding new functions don't '*cleanup*' unrelated functions. That cleanup belongs in another pull request.
+#### Squash your commits to a single commit
+
+To keep the history of the project clean, you should make one commit per pull request.
+If you already have multiple commits, you can add the commits together (squash them) with the following commands in Git Bash:
+
+1. Open `Git Bash` (or `Git Shell`)
+2. Enter following command to squash the recent {N} commits: `git reset --soft HEAD~{N} && git commit` (replace `{N}` with the number of commits you want to squash)
+3. Press i to get into Insert-mode
+4. Enter the commit message of the new commit (and add the [signature](#sign-your-work) at the and)
+5. After adding the message, press ESC to get out of the Insert-mode
+6. Write `:wq` and press Enter to save the new message or write `:q!` to discard your changes
+7. Enter `git push --force` to push the new commit to the remote repository
+
+For example, if you want to squash the last 5 commits, use `git reset --soft HEAD~5 && git commit`
+
### Don't mix code changes with whitespace cleanup
If you change two lines of code and correct 200 lines of whitespace issues in a file the diff on that pull request is functionally unreadable and will be **rejected**. Whitespace cleanups need to be in their own pull request.
diff --git a/MSVC.md b/MSVC.md
index e3bdafcdf6..a9f3453094 100644
--- a/MSVC.md
+++ b/MSVC.md
@@ -141,7 +141,7 @@ Open **VS2015 x86 Native Tools Command Prompt.bat** (should be in **Start Menu >
PKG_CONFIG_PATH="/mingw64/lib/pkgconfig:$PKG_CONFIG_PATH"
- ./configure --toolchain=msvc --disable-programs --disable-everything --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=wavpack --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-decoder=flac --enable-encoder=libopus --enable-parser=aac --enable-parser=aac_latm --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-parser=flac --enable-demuxer=aac --enable-demuxer=wav --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=mov --enable-demuxer=flac --enable-muxer=ogg --enable-muxer=opus --extra-ldflags="-libpath:/d/TBuild/Libraries/opus/win32/VS2010/Win32/Release celt.lib silk_common.lib silk_float.lib"
+ ./configure --toolchain=msvc --disable-programs --disable-doc --disable-everything --disable-w32threads --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=wavpack --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=h264_d3d11va --enable-hwaccel=h264_dxva2 --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus --extra-ldflags="-libpath:/d/TBuild/Libraries/opus/win32/VS2010/Win32/Release celt.lib silk_common.lib silk_float.lib"
make
make install
@@ -169,7 +169,7 @@ and run
#####Apply the patch
cd qtbase && git apply ../../../tdesktop/Telegram/_qtbase_5_5_1_patch.diff && cd ..
-
+
#####Install Windows SDKs
diff --git a/QTCREATOR.md b/QTCREATOR.md
index da103b0488..d963f9badf 100644
--- a/QTCREATOR.md
+++ b/QTCREATOR.md
@@ -44,13 +44,24 @@ Download [opus-1.1 sources](http://downloads.xiph.org/releases/opus/opus-1.1.tar
####FFmpeg
-Download sources [ffmpeg-2.6.3.tar.bz2](http://ffmpeg.org/releases/ffmpeg-2.6.3.tar.bz2) from https://www.ffmpeg.org/download.html, extract to **/home/user/TBuild/Libraries** to have **/home/user/TBuild/Libraries/ffmpeg-2.6.3**, go to **/home/user/TBuild/Libraries/ffmpeg-2.6.3** and run
+In Terminal go to **/home/user/TBuild/Libraries** and run
+
+ git clone git://anongit.freedesktop.org/git/libva
+ cd libva
+ ./autogen.sh --enable-static
+ make
+ sudo make install
+ cd ..
+
+ git clone https://github.com/FFmpeg/FFmpeg.git ffmpeg
+ cd ffmpeg
+ git checkout release/2.8
sudo apt-get update
sudo apt-get -y --force-yes install autoconf automake build-essential libass-dev libfreetype6-dev libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev
sudo apt-get install yasm
- ./configure --prefix=/usr/local --disable-programs --disable-everything --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=wavpack --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-decoder=flac --enable-encoder=libopus --enable-parser=aac --enable-parser=aac_latm --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-parser=flac --enable-demuxer=aac --enable-demuxer=wav --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=mov --enable-demuxer=flac --enable-muxer=ogg --enable-muxer=opus
+ ./configure --prefix=/usr/local --disable-programs --disable-doc --disable-pthreads --disable-mmx --disable-everything --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=h264_vdpau --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=mpeg4_vdpau --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=opus --enable-decoder=vorbis --enable-decoder=wavpack --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=h264_vaapi --enable-hwaccel=h264_vdpau --enable-hwaccel=mpeg4_vaapi --enable-hwaccel=mpeg4_vdpau --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus
make
sudo make install
diff --git a/Telegram/Build.bat b/Telegram/Build.bat
index 33c14d499e..650a937ee5 100644
--- a/Telegram/Build.bat
+++ b/Telegram/Build.bat
@@ -31,6 +31,10 @@ set "DeployPath=%ReleasePath%\deploy\%AppVersionStrMajor%\%AppVersionStrFull%"
set "SignPath=..\..\TelegramPrivate\Sign.bat"
if %BetaVersion% neq 0 (
+ if exist %DeployPath%\ (
+ echo Deploy folder for version %AppVersionStr% already exists!
+ exit /b 1
+ )
if exist %ReleasePath%\%BetaKeyFile% (
echo Beta version key file for version %AppVersion% already exists!
exit /b 1
@@ -40,19 +44,21 @@ if %BetaVersion% neq 0 (
echo Deploy folder for version %AppVersionStr%.dev already exists!
exit /b 1
)
+ if exist %ReleasePath%\deploy\%AppVersionStrMajor%\%AppVersionStr%\ (
+ echo Deploy folder for version %AppVersionStr% already exists!
+ exit /b 1
+ )
if exist %ReleasePath%\tupdate%AppVersion% (
echo Update file for version %AppVersion% already exists!
exit /b 1
)
)
-
-if exist %ReleasePath%\deploy\%AppVersionStrMajor%\%AppVersionStr%\ (
- echo Deploy folder for version %AppVersionStr% already exists!
- exit /b 1
-)
-
cd SourceFiles\
-rem copy telegram.qrc /B+,,/Y
+if "%1" == "fast" (
+ echo Skipping touching of telegram.qrc..
+) else (
+ copy telegram.qrc /B+,,/Y
+)
cd ..\
if %errorlevel% neq 0 goto error
@@ -149,7 +155,7 @@ xcopy %DeployPath%\%PortableFile% %FinalDeployPath%\
if %BetaVersion% equ 0 (
xcopy %DeployPath%\%SetupFile% %FinalDeployPath%\
) else (
- xcopy %DeployPath%\%BetaKeyFile% %FinalDeployPath%\
+ xcopy %DeployPath%\%BetaKeyFile% %FinalDeployPath%\ /Y
)
xcopy %DeployPath%\Telegram.pdb %FinalDeployPath%\
xcopy %DeployPath%\Updater.exe %FinalDeployPath%\
diff --git a/Telegram/FixMake.sh b/Telegram/FixMake.sh
index 77a933e1a1..21abada2b2 100755
--- a/Telegram/FixMake.sh
+++ b/Telegram/FixMake.sh
@@ -27,4 +27,6 @@ Replace '\-lopenal' '\/usr\/local\/lib\/libopenal\.a'
Replace '\-lavformat' '\/usr\/local\/lib\/libavformat\.a'
Replace '\-lavcodec' '\/usr\/local\/lib\/libavcodec\.a'
Replace '\-lswresample' '\/usr\/local\/lib\/libswresample\.a'
+Replace '\-lswscale' '\/usr\/local\/lib\/libswscale\.a'
Replace '\-lavutil' '\/usr\/local\/lib\/libavutil\.a'
+Replace '\-lva' '\/usr\/local\/lib\/libva\.a'
diff --git a/Telegram/FixMake32.sh b/Telegram/FixMake32.sh
index 4b7591ab5e..833bca6c6d 100755
--- a/Telegram/FixMake32.sh
+++ b/Telegram/FixMake32.sh
@@ -27,4 +27,6 @@ Replace '\-lopenal' '\/usr\/local\/lib\/libopenal\.a'
Replace '\-lavformat' '\/usr\/local\/lib\/libavformat\.a'
Replace '\-lavcodec' '\/usr\/local\/lib\/libavcodec\.a'
Replace '\-lswresample' '\/usr\/local\/lib\/libswresample\.a'
+Replace '\-lswscale' '\/usr\/local\/lib\/libswscale\.a'
Replace '\-lavutil' '\/usr\/local\/lib\/libavutil\.a'
+Replace '\-lva' '\/usr\/local\/lib\/libva\.a'
diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings
index fd065b167a..79aee3ad76 100644
--- a/Telegram/Resources/lang.strings
+++ b/Telegram/Resources/lang.strings
@@ -48,6 +48,19 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_month11" = "November";
"lng_month12" = "December";
+"lng_month1_small" = "Jan";
+"lng_month2_small" = "Feb";
+"lng_month3_small" = "Mar";
+"lng_month4_small" = "Apr";
+"lng_month5_small" = "May";
+"lng_month6_small" = "Jun";
+"lng_month7_small" = "Jul";
+"lng_month8_small" = "Aug";
+"lng_month9_small" = "Sep";
+"lng_month10_small" = "Oct";
+"lng_month11_small" = "Nov";
+"lng_month12_small" = "Dec";
+
"lng_weekday1" = "Mon";
"lng_weekday2" = "Tue";
"lng_weekday3" = "Wed";
@@ -66,6 +79,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_month_day" = "{month} {day}";
"lng_month_day_year" = "{month} {day}, {year}";
+"lng_month_year" = "{month}, {year}";
"lng_box_ok" = "OK";
@@ -108,6 +122,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_server_error" = "Internal server error.";
"lng_flood_error" = "Too many tries. Please try again later.";
+"lng_gif_error" = "An error has occured while reading GIF animation :(";
"lng_deleted" = "Unknown";
"lng_deleted_message" = "Deleted message";
@@ -470,8 +485,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_create_channel_crop" = "Select a square area for channel photo";
"lng_failed_add_participant" = "Could not add user. Please try again later.";
-"lng_failed_add_not_mutual" = "Sorry, if a person leaves a group, only a\nmutual contact can bring them back\n(they need to have your phone\nnumber, and you need theirs).";
-"lng_failed_add_not_mutual_channel" = "Sorry, if a person leaves a channel, only a\nmutual contact can bring them back\n(they need to have your phone\nnumber, and you need theirs).";
+"lng_failed_add_not_mutual" = "Sorry, if a person leaves a group, only a mutual contact can bring them back (they need to have your phone number, and you need theirs).";
+"lng_failed_add_not_mutual_channel" = "Sorry, if a person leaves a channel, only a mutual contact can bring them back (they need to have your phone number, and you need theirs).";
"lng_sure_delete_contact" = "Are you sure, you want to delete {contact} from your contact list?";
"lng_sure_delete_history" = "Are you sure, you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
@@ -483,7 +498,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_sure_delete_group" = "Are you sure, you want to delete this group? All members will be removed and all messages will be lost.";
"lng_message_empty" = "Empty Message";
-"lng_media_unsupported" = "Media Unsupported";
+"lng_message_unsupported" = "This message is not supported by your version of Telegram Desktop. Please update to the last version in Settings or install it from {link}";
"lng_action_add_user" = "{from} added {user}";
"lng_action_add_users_many" = "{from} added {users}";
@@ -561,6 +576,14 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_media_video" = "Video file";
"lng_media_audio" = "Voice message";
+"lng_media_auto_settings" = "Automatic media download settings";
+"lng_media_auto_photo" = "Automatic photo download";
+"lng_media_auto_audio" = "Automatic voice message download";
+"lng_media_auto_gif" = "Automatic GIF download";
+"lng_media_auto_private_chats" = "Private chats";
+"lng_media_auto_groups" = "Groups and channels";
+"lng_media_auto_play" = "Autoplay";
+
"lng_emoji_category0" = "Frequently used";
"lng_emoji_category1" = "People";
"lng_emoji_category2" = "Nature";
@@ -571,8 +594,14 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_emoji_category7" = "Symbols & Flags";
"lng_switch_stickers" = "Stickers";
+"lng_switch_stickers_gifs" = "GIFs & Stickers";
"lng_switch_emoji" = "Emoji";
+"lng_saved_gifs" = "Saved GIFs";
+"lng_inline_bot_results" = "Results from {inline_bot}";
+"lng_inline_bot_no_results" = "No results";
+"lng_inline_bot_via" = "via {inline_bot}";
+
"lng_box_remove" = "Remove";
"lng_custom_stickers" = "Custom stickers";
@@ -664,6 +693,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_save_file" = "Save file";
"lng_save_downloaded" = "{ready} / {total} {mb}";
"lng_duration_and_size" = "{duration}, {size}";
+"lng_duration_played" = "{played} / {duration}";
+"lng_date_and_duration" = "{date}, {duration}";
"lng_choose_images" = "Choose images";
"lng_context_view_profile" = "View profile";
@@ -699,6 +730,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_context_delete_file" = "Delete File";
"lng_context_close_file" = "Close File";
"lng_context_copy_text" = "Copy Text";
+"lng_context_save_gif" = "Save GIF";
"lng_context_to_msg" = "Go To Message";
"lng_context_reply_msg" = "Reply";
"lng_context_forward_msg" = "Forward Message";
@@ -762,7 +794,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_selected_delete" = "Delete";
"lng_selected_forward" = "Forward";
"lng_selected_count" = "{count:_not_used_|# message|# messages}";
-"lng_selected_cancel_sure_this" = "Do you want to cancel this upload?";
+"lng_selected_cancel_sure_this" = "Cancel uploading?";
+"lng_selected_upload_stop" = "Stop";
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
"lng_selected_delete_sure" = "Do you want to delete {count:_not_used_|# message|# messages}?";
"lng_box_delete" = "Delete";
@@ -801,7 +834,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}";
"lng_new_version_minor" = "— Bug fixes and other minor improvements";
-"lng_new_version_text" = "— Sticker management: manually rearrange your sticker packs, pack order is now synced across all your devices\n— Click and hold on a sticker to preview it before sending\n— New context menu for chats in chats list\n— Support for all existing emoji";
+"lng_new_version_text" = "GIF revolution: 10x faster sending and downloading, autoplay, save your favorite GIFs to a dedicated tab on the sticker panel.\n\nMore about GIFs:\n{gifs_link}\n\nInline bots: A new way to add bot content to any chat. Type a bot's username and your query in the text field to get instant results and send them to your chat partner. Try typing “@gif dog” in your next chat. Sample bots: @gif, @wiki, @bing, @vid, @bold.\n\nMore about inline bots:\n{bots_link}\n\nAlso in this release: New cute design for media, automatic download settings for photos, voice messages and GIFs.";
"lng_menu_insert_unicode" = "Insert Unicode control character";
diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt
index 81fc1f8e14..a1f7a785cd 100644
--- a/Telegram/Resources/style.txt
+++ b/Telegram/Resources/style.txt
@@ -554,8 +554,8 @@ taDefFlat: flatTextarea {
font: inpDefFont;
cursor: cursor(text);
- phColor: #AAA;
- phFocusColor: #CCC;
+ phColor: #999;
+ phFocusColor: #AAA;
phAlign: align(topleft);
phPos: point(2px, 0px);
phShift: 50px;
@@ -776,8 +776,8 @@ setClose: iconedButton(btnDefIconed) {
height: 43px;
}
setClosePos: point(32px, 32px);
-setPhotoImg: sprite(0px, 220px, 120px, 120px);
-setOverPhotoImg: sprite(122px, 220px, 120px, 120px);
+setPhotoImg: sprite(0px, 218px, 120px, 120px);
+setOverPhotoImg: sprite(122px, 218px, 120px, 120px);
setPhotoDuration: 150;
setPadding: 26px;
@@ -1015,25 +1015,25 @@ msgPadding: margins(13px, 7px, 13px, 8px);
msgMargin: margins(13px, 4px, 53px, 4px);
msgLnkPadding: 2px; // for media open / save links
msgBorder: #f0f0f0;
-msgOutBg: #effdde;
msgInBg: #fff;
-msgOutSelectBg: #b7dbdb;
-msgInSelectBg: #c2dcf2; // #358cd4 with 30% opacity
+msgInBgSelected: #c2dcf2; // #358cd4 with 30% opacity
+msgOutBg: #effdde;
+msgOutBgSelected: #b7dbdb;
msgSelectOverlay: #358cd44c;
msgStickerOverlay: #358cd47f;
-msgOutServiceColor: #3a8e26;
-msgInServiceColor: #0e7acd;
-msgOutServiceSelColor: #367570;
-msgInServiceSelColor: #0e7acd;
+msgInServiceFg: #0e7acd;
+msgInServiceFgSelected: #0e7acd;
+msgOutServiceFg: #3a8e26;
+msgOutServiceFgSelected: #367570;
msgShadow: 2px;
msgInShadow: #748ea229;
+msgInShadowSelected: #548dbb29;
msgOutShadow: #3ac34740;
-msgInSelectShadow: #548dbb29;
-msgOutSelectShadow: #37a78e40;
-msgInDateColor: #a0acb6;
-msgOutDateColor: #6cc264;
-msgInSelectDateColor: #6a9cc5;
-msgOutSelectDateColor: #50a79c;
+msgOutShadowSelected: #37a78e40;
+msgInDateFg: #a0acb6;
+msgInDateFgSelected: #6a9cc5;
+msgOutDateFg: #6cc264;
+msgOutDateFgSelected: #50a79c;
msgReplyPadding: margins(6px, 6px, 11px, 6px);
msgReplyBarPos: point(1px, 0px);
@@ -1086,11 +1086,12 @@ msgDateDelta: point(2px, 5px);
msgDateImgDelta: 4px;
msgDateImgColor: #fff;
msgDateImgBg: #00000054;
-msgDateImgSelectBg: #1c4a7187;
+msgDateImgBgOver: #00000074;
+msgDateImgBgSelected: #1c4a7187;
msgDateImgPadding: point(8px, 2px);
msgDateImgCheckSpace: 4px;
-msgDogImg: sprite(213px, 93px, 126px, 126px);
+msgDogImg: sprite(216px, 92px, 126px, 126px);
historyPadding: 10px;
collapseButton: flatButton(btnDefFlat) {
@@ -1117,7 +1118,7 @@ defaultTextStyle: textStyle {
linkFg: btnYesColor;
linkFgDown: btnYesHover;
monoFg: #777;
- selectBg: msgInSelectBg;
+ selectBg: msgInBgSelected;
selectOverlay: msgSelectOverlay;
lineHeight: 0px;
}
@@ -1135,12 +1136,12 @@ serviceTextStyle: textStyle(defaultTextStyle) {
}
inTextStyle: textStyle(defaultTextStyle) {
monoFg: #4e7391;
- selectBg: msgInSelectBg;
+ selectBg: msgInBgSelected;
selectOverlay: msgSelectOverlay;
}
outTextStyle: textStyle(defaultTextStyle) {
monoFg: #469165;
- selectBg: msgOutSelectBg;
+ selectBg: msgOutBgSelected;
selectOverlay: msgSelectOverlay;
}
medviewSaveAsTextStyle: textStyle(defaultTextStyle) {
@@ -1165,52 +1166,109 @@ introErrLabelTextStyle: textStyle(defaultTextStyle) {
lineHeight: 27px;
}
-mediaMaxWidth: 250px;
-mediaFont: font(fsize);
-mediaPadding: margins(7px, 6px, 7px, 6px);
+mediaPadding: margins(0px, 0px, 0px, 0px);//1px, 1px, 1px, 1px);//2px, 2px, 2px, 2px);
+mediaCaptionSkip: 5px;
+mediaHeaderSkip: 5px;
mediaThumbSize: 48px;
mediaNameTop: 3px;
mediaDetailsShift: 3px;
-mediaDocOutImg: sprite(6px, 146px, 48px, 48px);
-mediaDocInImg: sprite(56px, 146px, 48px, 48px);
-mediaAudioOutImg: sprite(106px, 146px, 48px, 48px);
-mediaAudioInImg: sprite(156px, 146px, 48px, 48px);
-mediaMusicOutImg: sprite(322px, 345px, 48px, 48px);
-mediaMusicInImg: sprite(322px, 395px, 48px, 48px);
-mediaPlayOutImg: sprite(122px, 341px, 48px, 48px);
-mediaPlayInImg: sprite(172px, 341px, 48px, 48px);
-mediaPauseOutImg: sprite(222px, 341px, 48px, 48px);
-mediaPauseInImg: sprite(272px, 341px, 48px, 48px);
-mediaInColor: msgInDateColor;
-mediaOutColor: msgOutDateColor;
-mediaInSelectColor: msgInSelectDateColor;
-mediaOutSelectColor: msgOutSelectDateColor;
-mediaOutUnreadColor: #6aad60;
-mediaOutUnreadSelectColor: #5aa382;
-mediaInUnreadColor: #999;
-mediaInUnreadSelectColor: #7b95aa;
-mediaUnreadSize: 4px;
+mediaInFg: msgInDateFg;
+mediaInFgSelected: msgInDateFgSelected;
+mediaOutFg: msgOutDateFg;
+mediaOutFgSelected: msgOutDateFgSelected;
+mediaInUnreadFg: #999;
+mediaInUnreadFgSelected: #7b95aa;
+mediaOutUnreadFg: #6aad60;
+mediaOutUnreadFgSelected: #5aa382;
+mediaUnreadSize: 7px;
mediaUnreadSkip: 5px;
-mediaSaveDelta: 14px; // between bubble and download
-mediaSaveButton: flatButton(btnDefFlat) {
- color: #507da2;
- overColor: #507da2;
- downColor: #507da2;
+mediaUnreadTop: 6px;
- bgColor: white;
- overBgColor: overBg;
- downBgColor: overBg;
-
- width: -28px;
- height: 34px;
-
- textTop: 7px;
- overTextTop: 7px;
- downTextTop: 8px;
-
- font: font(fsize);
- overFont: font(fsize);
+mediaInStyle: textStyle(defaultTextStyle) {
+ linkFg: mediaInFg;
+ linkFgDown: mediaInFg;
}
+mediaInStyleSelected: textStyle(defaultTextStyle) {
+ linkFg: mediaInFgSelected;
+ linkFgDown: mediaInFgSelected;
+}
+
+msgFileRedColor: #e47272;
+msgFileYellowColor: #efc274;
+msgFileGreenColor: #61b96e;
+msgFileBlueColor: #72b1df;
+msgFileRedDark: #cd5b5e;
+msgFileYellowDark: #e6a561;
+msgFileGreenDark: #4da859;
+msgFileBlueDark: #5c9ece;
+msgFileRedOver: #c35154;
+msgFileYellowOver: #dc9c5a;
+msgFileGreenOver: #44a050;
+msgFileBlueOver: #5294c4;
+msgFileRedSelected: #9f6a82;
+msgFileYellowSelected: #b19d84;
+msgFileGreenSelected: #46a07e;
+msgFileBlueSelected: #5099d0;
+
+msgFileMenuSize: size(36px, 36px);
+msgFileSize: 44px;
+msgFilePadding: margins(14px, 12px, 11px, 12px);
+msgFileThumbSize: 72px;
+msgFileThumbPadding: margins(10px, 10px, 14px, 10px);
+msgFileThumbNameTop: 12px;
+msgFileThumbStatusTop: 32px;
+msgFileThumbLinkTop: 60px;
+msgFileThumbLinkInFg: #3da5e0;
+msgFileThumbLinkInFgSelected: #3da5e0;
+msgFileThumbLinkOutFg: #5eba5b;
+msgFileThumbLinkOutFgSelected: #31a298;
+msgFileNameTop: 16px;
+msgFileStatusTop: 37px;
+msgFileMinWidth: 294px;
+msgFileInBg: #59b6eb;
+msgFileInBgOver: #4eade3;
+msgFileInBgSelected: #51a3d3;
+msgFileOutBg: #78c67f;
+msgFileOutBgOver: #6bc272;
+msgFileOutBgSelected: #5fb389;
+
+msgFileOutImage: sprite(0px, 146px, 18px, 18px);
+msgFileOutImageSelected: sprite(18px, 146px, 18px, 18px);
+msgFileInImage: sprite(0px, 164px, 18px, 18px);
+msgFileInImageSelected: sprite(18px, 164px, 18px, 18px);
+msgFileOutFile: sprite(36px, 146px, 18px, 18px);
+msgFileOutFileSelected: sprite(54px, 146px, 18px, 18px);
+msgFileInFile: sprite(36px, 164px, 18px, 18px);
+msgFileInFileSelected: sprite(54px, 164px, 18px, 18px);
+msgFileOutDownload: sprite(72px, 142px, 14px, 20px);
+msgFileOutDownloadSelected: sprite(86px, 142px, 14px, 20px);
+msgFileInDownload: sprite(72px, 162px, 14px, 20px);
+msgFileInDownloadSelected: sprite(86px, 162px, 14px, 20px);
+msgFileOutCancel: sprite(100px, 147px, 16px, 16px);
+msgFileOutCancelSelected: sprite(116px, 147px, 16px, 16px);
+msgFileInCancel: sprite(100px, 165px, 16px, 16px);
+msgFileInCancelSelected: sprite(116px, 165px, 16px, 16px);
+msgFileOutPause: sprite(132px, 147px, 14px, 16px);
+msgFileOutPauseSelected: sprite(146px, 147px, 14px, 16px);
+msgFileInPause: sprite(132px, 165px, 14px, 16px);
+msgFileInPauseSelected: sprite(146px, 165px, 14px, 16px);
+msgFileOutPlay: sprite(160px, 146px, 20px, 18px);
+msgFileOutPlaySelected: sprite(180px, 146px, 20px, 18px);
+msgFileInPlay: sprite(160px, 164px, 20px, 18px);
+msgFileInPlaySelected: sprite(180px, 164px, 20px, 18px);
+
+msgFileRed: sprite(0px, 425px, 20px, 20px);
+msgFileYellow: sprite(20px, 425px, 20px, 20px);
+msgFileGreen: sprite(40px, 425px, 20px, 20px);
+msgFileBlue: sprite(60px, 425px, 20px, 20px);
+
+msgFileOverDuration: 200;
+msgFileRadialLine: 3px;
+
+msgFileExtPadding: 8px;
+msgFileExtTop: 30px;
+
+msgVideoSize: size(320px, 240px);
sendPadding: 9px;
btnSend: flatButton(btnDefFlat) {
@@ -1261,6 +1319,13 @@ btnAttachEmoji: iconedButton(btnAttachDocument) {
width: 33px;
}
+emojiCircle: size(19px, 19px);
+emojiCirclePeriod: 1500;
+emojiCircleDuration: 500;
+emojiCircleTop: 13px;
+emojiCircleLine: 2px;
+emojiCircleFg: #b9b9b9;
+emojiCirclePart: 3.5;
btnBotKbShow: iconedButton(btnAttachEmoji) {
icon: sprite(375px, 74px, 21px, 21px);
iconPos: point(6px, 12px);
@@ -1480,7 +1545,6 @@ notifySlowHideFunc: transition(easeInCirc);
notifyWaitShortHide: 0;
notifyWaitLongHide: 20000;
notifyFastAnim: 150;
-notifyFastAnimFunc: transition(linear);
notifyWidth: 316px;
notifyHeight: 80px;
notifyDeltaX: 6px;
@@ -1550,7 +1614,7 @@ profileMinBtnPadding: 10px;
membersPadding: margins(0px, 10px, 0px, 10px);
forwardMargins: margins(30px, 10px, 30px, 10px);
-forwardFont: font(16px);
+forwardFont: font(16px);
forwardBg: rgba(0, 0, 0, 76);
btnProfileCancel: flatButton(btnDefFlat, btnDefBig) {
color: #666d78;
@@ -1820,6 +1884,8 @@ emojiObjectsActive: sprite(308px, 264px, 21px, 22px);
emojiSymbolsOver: sprite(84px, 196px, 21px, 22px);
emojiSymbolsActive: sprite(287px, 286px, 21px, 22px);
stickersSettings: sprite(140px, 124px, 21px, 22px);
+savedGifsOver: sprite(329px, 286px, 21px, 22px);
+savedGifsActive: sprite(350px, 286px, 21px, 22px);
emojiPanCategories: #f7f7f7;
@@ -1975,6 +2041,11 @@ botKbScroll: flatScroll(solidScroll) {
width: 10px;
}
+minPhotoSize: 100px;
+maxMediaSize: 420px;
+maxStickerSize: 256px;
+maxGifSize: 320px;
+
mvBgColor: #222;
mvBgOpacity: 0.92;
mvThickFont: semiboldFont;
@@ -2063,14 +2134,12 @@ mvDocExtFont: font(semibold 18px);
mvDocExtColor: white;
mvDocExtPadding: 10px;
mvDocLinksTop: 57px;
-mvDocRed: sprite(0px, 400px, 80px, 80px);
-mvDocRedColor: #e47272;
-mvDocYellow: sprite(80px, 400px, 80px, 80px);
-mvDocYellowColor: #efc274;
-mvDocGreen: sprite(160px, 400px, 80px, 80px);
-mvDocGreenColor: #61b96e;
-mvDocBlue: sprite(240px, 400px, 80px, 80px);
-mvDocBlueColor: #72b1df;
+mvDocRed: sprite(0px, 400px, 25px, 25px);
+mvDocYellow: sprite(25px, 400px, 25px, 25px);
+mvDocGreen: sprite(50px, 400px, 25px, 25px);
+mvDocBlue: sprite(75px, 400px, 25px, 25px);
+mvDocIconSize: 80px;
+
mvDocLink: linkButton(btnDefLink) {
color: #4595d3;
overColor: #4595d3;
@@ -2098,11 +2167,20 @@ medviewSaveMsg: #000000b2;
mvTransparentBrush: sprite(9px, 124px, 8px, 8px);
overviewPhotoSkip: 10px;
-overviewPhotoMinSize: 100px;
+overviewPhotoBg: #F1F1F1;
+overviewPhotoMinSize: minPhotoSize;
overviewPhotoCheck: sprite(245px, 308px, 32px, 32px);
overviewPhotoChecked: sprite(278px, 308px, 32px, 32px);
overviewPhotoSelectOverlay: #0a7bb03f;
+overviewFilePadding: margins(0px, 3px, 16px, 3px);
+overviewFileSize: 70px;
+overviewFileNameTop: 7px;
+overviewFileStatusTop: 27px;
+overviewFileDateTop: 49px;
+overviewFileChecked: #2fa9e2;
+overviewFileCheck: #00000066;
+
// Mac specific
macAccessory: size(450, 90);
@@ -2159,10 +2237,6 @@ mediaviewLoader: size(78px, 33px);
mediaviewLoaderPoint: size(9px, 9px);
mediaviewLoaderSkip: 9px;
-minPhotoSize: 100px;
-maxMediaSize: 420px;
-maxStickerSize: 256px;
-
downloadPathSkip: 10px;
usernamePadding: margins(23px, 22px, 21px, 12px);
@@ -2172,10 +2246,9 @@ usernameTextStyle: textStyle(defaultTextStyle) {
}
usernameDefaultFg: #777;
-youtubeIcon: sprite(336px, 221px, 60px, 60px);
-vimeoIcon: sprite(336px, 283px, 60px, 60px);
+youtubeIcon: sprite(116px, 338px, 72px, 50px);
videoIcon: sprite(0px, 340px, 60px, 60px);
-locationSize: size(320, 240);
+locationSize: size(320px, 240px);
boxOptionListPadding: margins(2px, 20px, 2px, 2px);
@@ -2293,11 +2366,24 @@ playlistPadding: 10px;
linksSearchMargin: margins(20px, 20px, 20px, 0px);
linksMaxWidth: 520px;
linksLetterFont: font(24px);
-linksMargin: 5px;
+linksMargin: margins(0px, 7px, 0px, 5px);
+linksTextTop: 6px;
linksBorder: 1px;
-linksBorderColor: #eaeaea;
-linksDateColor: #000;
-linksDateMargin: 15px;
+linksBorderFg: #eaeaea;
+linksDateColor: #808080;
+linksDateMargin: margins(0px, 15px, 0px, 2px);
linksPhotoCheck: sprite(184px, 196px, 16px, 16px);
linksPhotoChecked: sprite(168px, 196px, 16px, 16px);
+
+inlineResultsLeft: 15px;
+inlineResultsSkip: 3px;
+inlineMediaHeight: 96px;
+inlineThumbSize: 64px;
+inlineThumbSkip: 10px;
+inlineDescriptionFg: #8a8a8a;
+inlineRowMargin: 6px;
+inlineRowBorder: linksBorder;
+inlineRowBorderFg: linksBorderFg;
+inlineResultsMinWidth: 64px;
+inlineDurationMargin: 3px;
diff --git a/Telegram/SourceFiles/_other/updater.cpp b/Telegram/SourceFiles/_other/updater.cpp
index 103803a882..a1dd35aa01 100644
--- a/Telegram/SourceFiles/_other/updater.cpp
+++ b/Telegram/SourceFiles/_other/updater.cpp
@@ -333,9 +333,8 @@ void updateRegistry() {
int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdParamarg, int cmdShow) {
openLog();
-#ifdef _NEED_WIN_GENERATE_DUMP
_oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter);
-#endif
+// CAPIHook apiHook("kernel32.dll", "SetUnhandledExceptionFilter", (PROC)RedirectedSetUnhandledExceptionFilter);
writeLog(L"Updaters started..");
@@ -465,7 +464,6 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
return 0;
}
-#ifdef _NEED_WIN_GENERATE_DUMP
static const WCHAR *_programName = L"Telegram Desktop"; // folder in APPDATA, if current path is unavailable for writing
static const WCHAR *_exeName = L"Updater.exe";
@@ -507,10 +505,10 @@ HANDLE _generateDumpFileAtPath(const WCHAR *path) {
GetLocalTime(&stLocalTime);
- wsprintf(szFileName, L"%s%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
- szPath, szExeName, updaterVersionStr,
- stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
- stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
+ wsprintf(szFileName, L"%s%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
+ szPath, szExeName, updaterVersionStr,
+ stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
+ stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
GetCurrentProcessId(), GetCurrentThreadId());
return CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
}
@@ -546,7 +544,7 @@ void _generateDump(EXCEPTION_POINTERS* pExceptionPointers) {
hDumpFile = _generateDumpFileAtPath(wstrPath);
}
}
-
+
if (!hDumpFile || hDumpFile == INVALID_HANDLE_VALUE) {
return;
}
@@ -564,4 +562,10 @@ LONG CALLBACK _exceptionFilter(EXCEPTION_POINTERS* pExceptionPointers) {
return _oldWndExceptionFilter ? (*_oldWndExceptionFilter)(pExceptionPointers) : EXCEPTION_CONTINUE_SEARCH;
}
-#endif
+// see http://www.codeproject.com/Articles/154686/SetUnhandledExceptionFilter-and-the-C-C-Runtime-Li
+LPTOP_LEVEL_EXCEPTION_FILTER WINAPI RedirectedSetUnhandledExceptionFilter(_In_opt_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) {
+ // When the CRT calls SetUnhandledExceptionFilter with NULL parameter
+ // our handler will not get removed.
+ _oldWndExceptionFilter = lpTopLevelExceptionFilter;
+ return 0;
+}
diff --git a/Telegram/SourceFiles/_other/updater.h b/Telegram/SourceFiles/_other/updater.h
index 6afd341a33..b7f8fd8c0b 100644
--- a/Telegram/SourceFiles/_other/updater.h
+++ b/Telegram/SourceFiles/_other/updater.h
@@ -32,12 +32,9 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
using std::deque;
using std::wstring;
-#define _NEED_WIN_GENERATE_DUMP
-
-#ifdef _NEED_WIN_GENERATE_DUMP
extern LPTOP_LEVEL_EXCEPTION_FILTER _oldWndExceptionFilter;
LONG CALLBACK _exceptionFilter(EXCEPTION_POINTERS* pExceptionPointers);
-#endif _NEED_WIN_GENERATE_DUMP
+LPTOP_LEVEL_EXCEPTION_FILTER WINAPI RedirectedSetUnhandledExceptionFilter(_In_opt_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
static int updaterVersion = 1000;
static const WCHAR *updaterVersionStr = L"0.1.0";
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index d81582eff5..69366c14ce 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -64,36 +64,6 @@ void ApiWrap::itemRemoved(HistoryItem *item) {
}
}
-void ApiWrap::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
- if (HistoryReply *reply = oldItem->toHistoryReply()) {
- ChannelData *channel = reply->history()->peer->asChannel();
- ReplyToRequests *requests(replyToRequests(channel, true));
- if (requests) {
- ReplyToRequests::iterator i = requests->find(reply->replyToId());
- if (i != requests->cend()) {
- for (QList::iterator j = i->replies.begin(); j != i->replies.end();) {
- if ((*j) == reply) {
- if (HistoryReply *newReply = newItem->toHistoryReply()) {
- *j = newReply;
- ++j;
- } else {
- j = i->replies.erase(j);
- }
- } else {
- ++j;
- }
- }
- if (i->replies.isEmpty()) {
- requests->erase(i);
- }
- }
- if (channel && requests->isEmpty()) {
- _channelReplyToRequests.remove(channel);
- }
- }
- }
-}
-
void ApiWrap::requestReplyTo(HistoryReply *reply, ChannelData *channel, MsgId id) {
ReplyToRequest &req(channel ? _channelReplyToRequests[channel][id] : _replyToRequests[id]);
req.replies.append(reply);
@@ -460,7 +430,7 @@ void ApiWrap::requestBots(ChannelData *peer) {
void ApiWrap::gotChat(PeerData *peer, const MTPmessages_Chats &result) {
_peerRequests.remove(peer);
-
+
if (result.type() == mtpc_messages_chats) {
const QVector &v(result.c_messages_chats().vchats.c_vector().v);
bool badVersion = false;
@@ -702,9 +672,9 @@ void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) {
void ApiWrap::requestStickerSets() {
for (QMap >::iterator i = _stickerSetRequests.begin(), j = i, e = _stickerSetRequests.end(); i != e; i = j) {
+ ++j;
if (i.value().second) continue;
- ++j;
int32 wait = (j == e) ? 0 : 10;
i.value().second = MTP::send(MTPmessages_GetStickerSet(MTP_inputStickerSetID(MTP_long(i.key()), MTP_long(i.value().first))), rpcDone(&ApiWrap::gotStickerSet, i.key()), rpcFail(&ApiWrap::gotStickerSetFail, i.key()), 0, wait);
}
@@ -712,10 +682,10 @@ void ApiWrap::requestStickerSets() {
void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
_stickerSetRequests.remove(setId);
-
+
if (result.type() != mtpc_messages_stickerSet) return;
const MTPDmessages_stickerSet &d(result.c_messages_stickerSet());
-
+
if (d.vset.type() != mtpc_stickerSet) return;
const MTPDstickerSet &s(d.vset.c_stickerSet());
@@ -761,12 +731,32 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result)
++i;
}
}
+
if (pack.isEmpty()) {
int32 removeIndex = cStickerSetsOrder().indexOf(setId);
if (removeIndex >= 0) cRefStickerSetsOrder().removeAt(removeIndex);
sets.erase(it);
} else {
it->stickers = pack;
+ it->emoji.clear();
+ const QVector &v(d.vpacks.c_vector().v);
+ for (int32 i = 0, l = v.size(); i < l; ++i) {
+ if (v.at(i).type() != mtpc_stickerPack) continue;
+
+ const MTPDstickerPack &pack(v.at(i).c_stickerPack());
+ if (EmojiPtr e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) {
+ const QVector &stickers(pack.vdocuments.c_vector().v);
+ StickerPack p;
+ p.reserve(stickers.size());
+ for (int32 j = 0, c = stickers.size(); j < c; ++j) {
+ DocumentData *doc = App::document(stickers.at(j).v);
+ if (!doc || !doc->sticker()) continue;
+
+ p.push_back(doc);
+ }
+ it->emoji.insert(e, p);
+ }
+ }
}
if (writeRecent) {
@@ -916,12 +906,11 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs
}
}
- MainWidget *m = App::main();
for (QMap::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
HistoryItem *item = App::histories().addNewMessage(v->at(i.value()), NewMessageExisting);
if (item) {
item->initDimensions();
- if (m) m->itemResized(item);
+ Notify::historyItemResized(item);
}
}
@@ -934,7 +923,7 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs
if (j != items.cend()) {
for (HistoryItemsMap::const_iterator k = j.value().cbegin(), e = j.value().cend(); k != e; ++k) {
k.key()->initDimensions();
- if (m) m->itemResized(k.key());
+ Notify::historyItemResized(k.key());
}
}
}
@@ -946,5 +935,5 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs
}
ApiWrap::~ApiWrap() {
- App::deinitMedia(false);
+ App::clearHistories();
}
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index 1bf5370987..5daabcf74f 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -29,7 +29,6 @@ public:
void init();
void itemRemoved(HistoryItem *item);
- void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void requestReplyTo(HistoryReply *reply, ChannelData *channel, MsgId id);
diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp
index 81ba97b9d4..c375fea478 100644
--- a/Telegram/SourceFiles/app.cpp
+++ b/Telegram/SourceFiles/app.cpp
@@ -45,21 +45,14 @@ namespace {
typedef QMap UpdatedPeers;
UpdatedPeers updatedPeers;
- typedef QHash PhotosData;
PhotosData photosData;
-
- typedef QHash VideosData;
VideosData videosData;
-
- typedef QHash AudiosData;
AudiosData audiosData;
+ DocumentsData documentsData;
typedef QHash ImageLinksData;
ImageLinksData imageLinksData;
- typedef QHash DocumentsData;
- DocumentsData documentsData;
-
typedef QHash WebPagesData;
WebPagesData webPagesData;
@@ -69,16 +62,17 @@ namespace {
typedef QMap ChannelReplyMarkups;
ChannelReplyMarkups channelReplyMarkups;
+ PhotoItems photoItems;
VideoItems videoItems;
AudioItems audioItems;
DocumentItems documentItems;
WebPageItems webPageItems;
+ SharedContactItems sharedContactItems;
+ GifItems gifItems;
+
typedef QMap > RepliesTo;
RepliesTo repliesTo;
- typedef QMap SharedContactPhones;
- SharedContactPhones sharedContactPhones;
-
Histories histories;
typedef QHash MsgsData;
@@ -119,6 +113,9 @@ namespace {
typedef QHash LastPhotosMap;
LastPhotosMap lastPhotosMap;
+ typedef QMap InlineResultLoaders;
+ InlineResultLoaders inlineResultLoaders;
+
style::color _msgServiceBg;
style::color _msgServiceSelectBg;
style::color _historyScrollBarColor;
@@ -208,7 +205,7 @@ namespace App {
globalNotifyAllPtr = UnknownNotifySettings;
globalNotifyUsersPtr = UnknownNotifySettings;
globalNotifyChatsPtr = UnknownNotifySettings;
- App::uploader()->clear();
+ if (App::uploader()) App::uploader()->clear();
clearStorageImages();
if (w) {
w->getTitle()->updateBackButton();
@@ -430,6 +427,7 @@ namespace App {
data->setBotInfoVersion(d.vbot_info_version.v);
data->botInfo->readsAllHistory = d.is_bot_chat_history();
data->botInfo->cantJoinGroups = d.is_bot_nochats();
+ data->botInfo->inlinePlaceholder = d.has_bot_inline_placeholder() ? '_' + qs(d.vbot_inline_placeholder) : QString();
} else {
data->setBotInfoVersion(-1);
}
@@ -465,10 +463,8 @@ namespace App {
data->contact = 0;
}
if (App::main()) {
- if (data->contact > 0 && !wasContact) {
- App::main()->addNewContact(peerToUser(data->id), false);
- } else if (wasContact && data->contact <= 0) {
- App::main()->removeContact(data);
+ if ((data->contact > 0 && !wasContact) || (wasContact && data->contact < 1)) {
+ Notify::userIsContactChanged(data);
}
if (emitPeerUpdated) {
@@ -573,7 +569,7 @@ namespace App {
ChannelData *cdata = data->asChannel();
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
-
+
QString uname = d.has_username() ? textOneLine(qs(d.vusername)) : QString();
cdata->setName(qs(d.vtitle), uname);
@@ -905,25 +901,51 @@ namespace App {
peerId = peerFromUser(m.vfrom_id);
}
if (HistoryItem *existing = App::histItemById(peerToChannel(peerId), m.vid.v)) {
- bool hasLinks = m.has_entities() && !m.ventities.c_vector().v.isEmpty();
- if ((hasLinks && !existing->hasTextLinks()) || (!hasLinks && existing->textHasLinks())) {
- existing->setText(qs(m.vmessage), m.has_entities() ? entitiesFromMTP(m.ventities.c_vector().v) : EntitiesInText());
- existing->initDimensions();
- if (App::main()) App::main()->itemResized(existing);
- if (existing->hasTextLinks() && existing->indexInOverview()) {
- existing->history()->addToOverview(existing, OverviewLinks);
- }
+ existing->setText(qs(m.vmessage), m.has_entities() ? entitiesFromMTP(m.ventities.c_vector().v) : EntitiesInText());
+ existing->updateMedia(m.has_media() ? (&m.vmedia) : 0);
+ existing->setViewsCount(m.has_views() ? m.vviews.v : -1, false);
+ existing->initDimensions();
+ Notify::historyItemResized(existing);
+
+ existing->addToOverview(AddToOverviewNew);
+
+ if (!existing->detached()) {
+ App::checkSavedGif(existing);
+ return true;
}
- existing->updateMedia(m.has_media() ? (&m.vmedia) : 0, true);
-
- existing->setViewsCount(m.has_views() ? m.vviews.v : -1);
-
- return !existing->detached();
+ return false;
}
return false;
}
+ void addSavedGif(DocumentData *doc) {
+ SavedGifs &saved(cRefSavedGifs());
+ int32 index = saved.indexOf(doc);
+ if (index) {
+ if (index > 0) saved.remove(index);
+ saved.push_front(doc);
+ if (saved.size() > cSavedGifsLimit()) saved.pop_back();
+ Local::writeSavedGifs();
+
+ if (App::main()) emit App::main()->savedGifsUpdated();
+ cSetLastSavedGifsUpdate(0);
+ App::main()->updateStickers();
+ }
+ }
+
+ void checkSavedGif(HistoryItem *item) {
+ if (!item->toHistoryForwarded() && (item->out() || item->history()->peer == App::self())) {
+ if (HistoryMedia *media = item->getMedia()) {
+ if (DocumentData *doc = media->getDocument()) {
+ if (doc->isGifv()) {
+ addSavedGif(doc);
+ }
+ }
+ }
+ }
+ }
+
void feedMsgs(const QVector &msgs, NewMessageType type) {
QMap msgsIds;
for (int32 i = 0, l = msgs.size(); i < l; ++i) {
@@ -975,7 +997,7 @@ namespace App {
const string &s(d.vbytes.c_string().v);
QByteArray bytes(s.data(), s.size());
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, 0, 0, 0, 0), bytes);
- }
+ }
} break;
}
return ImagePtr();
@@ -1002,7 +1024,7 @@ namespace App {
}
return StorageImageLocation();
}
-
+
void feedInboxRead(const PeerId &peer, MsgId upTo) {
History *h = App::historyLoaded(peer);
if (h) {
@@ -1060,7 +1082,7 @@ namespace App {
}
}
if (resized) {
- App::main()->itemResized(0);
+ Notify::historyItemsResized();
}
if (main()) {
for (QMap::const_iterator i = historiesToCheck.cbegin(), e = historiesToCheck.cend(); i != e; ++i) {
@@ -1113,17 +1135,13 @@ namespace App {
user->contact = -1;
break;
}
- if (user->contact > 0) {
- if (!wasContact) {
- App::main()->addNewContact(peerToUser(user->id), false);
- }
- } else {
+ if (user->contact < 1) {
if (user->contact < 0 && !user->phone.isEmpty() && peerToUser(user->id) != MTP::authedId()) {
user->contact = 0;
}
- if (wasContact) {
- App::main()->removeContact(user);
- }
+ }
+ if ((user->contact > 0 && !wasContact) || (wasContact && user->contact < 1)) {
+ Notify::userIsContactChanged(user);
}
bool showPhone = !isServiceUser(user->id) && !user->isSelf() && !user->contact;
@@ -1182,7 +1200,7 @@ namespace App {
case 'm': newThumbLevel = 2; newMediumLevel = 0; newFullLevel = 3; break; // box 320x320
case 'x': newThumbLevel = 5; newMediumLevel = 3; newFullLevel = 1; break; // box 800x800
case 'y': newThumbLevel = 6; newMediumLevel = 6; newFullLevel = 0; break; // box 1280x1280
- case 'w': newThumbLevel = 8; newMediumLevel = 8; newFullLevel = 2; break; // box 2560x2560
+ case 'w': newThumbLevel = 8; newMediumLevel = 8; newFullLevel = 2; break; // box 2560x2560 // if loading this fix HistoryPhoto::updateFrom
case 'a': newThumbLevel = 1; newMediumLevel = 4; newFullLevel = 8; break; // crop 160x160
case 'b': newThumbLevel = 3; newMediumLevel = 1; newFullLevel = 7; break; // crop 320x320
case 'c': newThumbLevel = 4; newMediumLevel = 2; newFullLevel = 6; break; // crop 640x640
@@ -1228,7 +1246,7 @@ namespace App {
const string &s(i->c_photoSize().vtype.c_string().v);
if (s.size()) size = s[0];
} break;
-
+
case mtpc_photoCachedSize: {
const string &s(i->c_photoCachedSize().vtype.c_string().v);
if (s.size()) size = s[0];
@@ -1269,7 +1287,7 @@ namespace App {
}
return App::photoSet(photo.vid.v, convert, 0, 0, ImagePtr(), ImagePtr(), ImagePtr());
}
-
+
VideoData *feedVideo(const MTPDvideo &video, VideoData *convert) {
return App::videoSet(video.vid.v, convert, video.vaccess_hash.v, video.vdate.v, video.vduration.v, video.vw.v, video.vh.v, App::image(video.vthumb), video.vdc_id.v, video.vsize.v);
}
@@ -1412,18 +1430,31 @@ namespace App {
}
PeerData *peerByName(const QString &username) {
+ QString uname(username.trimmed());
for (PeersData::const_iterator i = peersData.cbegin(), e = peersData.cend(); i != e; ++i) {
- if (!i.value()->userName().compare(username.trimmed(), Qt::CaseInsensitive)) {
+ if (!i.value()->userName().compare(uname, Qt::CaseInsensitive)) {
return i.value()->asUser();
}
}
return 0;
}
+ void updateImage(ImagePtr &old, ImagePtr now) {
+ if (now->isNull()) return;
+ if (old->isNull()) {
+ old = now;
+ } else if (DelayedStorageImage *img = old->toDelayedStorageImage()) {
+ StorageImageLocation loc = now->location();
+ if (!loc.isNull()) {
+ img->setStorageLocation(loc);
+ }
+ }
+ }
+
PhotoData *photo(const PhotoId &photo) {
- PhotosData::const_iterator i = photosData.constFind(photo);
- if (i == photosData.cend()) {
- i = photosData.insert(photo, new PhotoData(photo));
+ PhotosData::const_iterator i = ::photosData.constFind(photo);
+ if (i == ::photosData.cend()) {
+ i = ::photosData.insert(photo, new PhotoData(photo));
}
return i.value();
}
@@ -1431,38 +1462,40 @@ namespace App {
PhotoData *photoSet(const PhotoId &photo, PhotoData *convert, const uint64 &access, int32 date, const ImagePtr &thumb, const ImagePtr &medium, const ImagePtr &full) {
if (convert) {
if (convert->id != photo) {
- PhotosData::iterator i = photosData.find(convert->id);
- if (i != photosData.cend() && i.value() == convert) {
- photosData.erase(i);
+ PhotosData::iterator i = ::photosData.find(convert->id);
+ if (i != ::photosData.cend() && i.value() == convert) {
+ ::photosData.erase(i);
}
convert->id = photo;
+ delete convert->uploadingData;
+ convert->uploadingData = 0;
}
- convert->access = access;
- if (!convert->date && date) {
+ if (date) {
+ convert->access = access;
convert->date = date;
- convert->thumb = thumb;
- convert->medium = medium;
- convert->full = full;
+ updateImage(convert->thumb, thumb);
+ updateImage(convert->medium, medium);
+ updateImage(convert->full, full);
}
}
- PhotosData::const_iterator i = photosData.constFind(photo);
+ PhotosData::const_iterator i = ::photosData.constFind(photo);
PhotoData *result;
LastPhotosMap::iterator inLastIter = lastPhotosMap.end();
- if (i == photosData.cend()) {
+ if (i == ::photosData.cend()) {
if (convert) {
result = convert;
} else {
result = new PhotoData(photo, access, date, thumb, medium, full);
}
- photosData.insert(photo, result);
+ ::photosData.insert(photo, result);
} else {
result = i.value();
- if (result != convert && !result->date && date) {
+ if (result != convert && date) {
result->access = access;
result->date = date;
- result->thumb = thumb;
- result->medium = medium;
- result->full = full;
+ updateImage(result->thumb, thumb);
+ updateImage(result->medium, medium);
+ updateImage(result->full, full);
}
inLastIter = lastPhotosMap.find(result);
}
@@ -1481,9 +1514,9 @@ namespace App {
}
VideoData *video(const VideoId &video) {
- VideosData::const_iterator i = videosData.constFind(video);
- if (i == videosData.cend()) {
- i = videosData.insert(video, new VideoData(video));
+ VideosData::const_iterator i = ::videosData.constFind(video);
+ if (i == ::videosData.cend()) {
+ i = ::videosData.insert(video, new VideoData(video));
}
return i.value();
}
@@ -1491,42 +1524,42 @@ namespace App {
VideoData *videoSet(const VideoId &video, VideoData *convert, const uint64 &access, int32 date, int32 duration, int32 w, int32 h, const ImagePtr &thumb, int32 dc, int32 size) {
if (convert) {
if (convert->id != video) {
- VideosData::iterator i = videosData.find(convert->id);
- if (i != videosData.cend() && i.value() == convert) {
- videosData.erase(i);
+ VideosData::iterator i = ::videosData.find(convert->id);
+ if (i != ::videosData.cend() && i.value() == convert) {
+ ::videosData.erase(i);
}
convert->id = video;
convert->status = FileReady;
}
- convert->access = access;
- if (!convert->date && date) {
+ if (date) {
+ convert->access = access;
convert->date = date;
+ updateImage(convert->thumb, thumb);
convert->duration = duration;
convert->w = w;
convert->h = h;
- convert->thumb = thumb;
convert->dc = dc;
convert->size = size;
}
}
- VideosData::const_iterator i = videosData.constFind(video);
+ VideosData::const_iterator i = ::videosData.constFind(video);
VideoData *result;
- if (i == videosData.cend()) {
+ if (i == ::videosData.cend()) {
if (convert) {
result = convert;
} else {
result = new VideoData(video, access, date, duration, w, h, thumb, dc, size);
}
- videosData.insert(video, result);
+ ::videosData.insert(video, result);
} else {
result = i.value();
- if (result != convert && !result->date && date) {
+ if (result != convert && date) {
result->access = access;
result->date = date;
result->duration = duration;
result->w = w;
result->h = h;
- result->thumb = thumb;
+ updateImage(result->thumb, thumb);
result->dc = dc;
result->size = size;
}
@@ -1535,9 +1568,9 @@ namespace App {
}
AudioData *audio(const AudioId &audio) {
- AudiosData::const_iterator i = audiosData.constFind(audio);
- if (i == audiosData.cend()) {
- i = audiosData.insert(audio, new AudioData(audio));
+ AudiosData::const_iterator i = ::audiosData.constFind(audio);
+ if (i == ::audiosData.cend()) {
+ i = ::audiosData.insert(audio, new AudioData(audio));
}
return i.value();
}
@@ -1545,15 +1578,15 @@ namespace App {
AudioData *audioSet(const AudioId &audio, AudioData *convert, const uint64 &access, int32 date, const QString &mime, int32 duration, int32 dc, int32 size) {
if (convert) {
if (convert->id != audio) {
- AudiosData::iterator i = audiosData.find(convert->id);
- if (i != audiosData.cend() && i.value() == convert) {
- audiosData.erase(i);
+ AudiosData::iterator i = ::audiosData.find(convert->id);
+ if (i != ::audiosData.cend() && i.value() == convert) {
+ ::audiosData.erase(i);
}
convert->id = audio;
convert->status = FileReady;
}
- convert->access = access;
- if (!convert->date && date) {
+ if (date) {
+ convert->access = access;
convert->date = date;
convert->mime = mime;
convert->duration = duration;
@@ -1561,18 +1594,18 @@ namespace App {
convert->size = size;
}
}
- AudiosData::const_iterator i = audiosData.constFind(audio);
+ AudiosData::const_iterator i = ::audiosData.constFind(audio);
AudioData *result;
- if (i == audiosData.cend()) {
+ if (i == ::audiosData.cend()) {
if (convert) {
result = convert;
} else {
result = new AudioData(audio, access, date, mime, duration, dc, size);
}
- audiosData.insert(audio, result);
+ ::audiosData.insert(audio, result);
} else {
result = i.value();
- if (result != convert && !result->date && date) {
+ if (result != convert && date) {
result->access = access;
result->date = date;
result->mime = mime;
@@ -1585,9 +1618,9 @@ namespace App {
}
DocumentData *document(const DocumentId &document) {
- DocumentsData::const_iterator i = documentsData.constFind(document);
- if (i == documentsData.cend()) {
- i = documentsData.insert(document, new DocumentData(document));
+ DocumentsData::const_iterator i = ::documentsData.constFind(document);
+ if (i == ::documentsData.cend()) {
+ i = ::documentsData.insert(document, new DocumentData(document));
}
return i.value();
}
@@ -1596,40 +1629,33 @@ namespace App {
bool sentSticker = false;
if (convert) {
if (convert->id != document) {
- DocumentsData::iterator i = documentsData.find(convert->id);
- if (i != documentsData.cend() && i.value() == convert) {
- documentsData.erase(i);
+ DocumentsData::iterator i = ::documentsData.find(convert->id);
+ if (i != ::documentsData.cend() && i.value() == convert) {
+ ::documentsData.erase(i);
}
+ Local::copyStickerImage(mediaKey(DocumentFileLocation, convert->dc, convert->id), mediaKey(DocumentFileLocation, dc, document));
convert->id = document;
convert->status = FileReady;
sentSticker = !!convert->sticker();
}
- convert->access = access;
- if (!convert->date && date) {
+ if (date) {
+ convert->access = access;
convert->date = date;
convert->setattributes(attributes);
convert->mime = mime;
- convert->thumb = thumb;
- convert->dc = dc;
- convert->size = size;
- } else {
if (!thumb->isNull() && (convert->thumb->isNull() || convert->thumb->width() < thumb->width() || convert->thumb->height() < thumb->height())) {
convert->thumb = thumb;
}
- if (convert->sticker() && !attributes.isEmpty() && (convert->sticker()->alt.isEmpty() || convert->sticker()->set.type() == mtpc_inputStickerSetEmpty)) {
- for (QVector::const_iterator i = attributes.cbegin(), e = attributes.cend(); i != e; ++i) {
- if (i->type() == mtpc_documentAttributeSticker) {
- const MTPDdocumentAttributeSticker &d(i->c_documentAttributeSticker());
- if (d.valt.c_string().v.length() > 0) {
- convert->sticker()->alt = qs(d.valt);
- convert->sticker()->set = d.vstickerset;
- }
- }
- }
+ convert->dc = dc;
+ convert->size = size;
+ convert->recountIsImage();
+ if (convert->sticker() && convert->sticker()->loc.isNull() && !thumbLocation.isNull()) {
+ convert->sticker()->loc = thumbLocation;
}
}
- if (convert->sticker() && !convert->sticker()->loc.dc && thumbLocation.dc) {
- convert->sticker()->loc = thumbLocation;
+
+ if (cSavedGifs().indexOf(convert) >= 0) { // id changed
+ Local::writeSavedGifs();
}
const FileLocation &loc(convert->location(true));
@@ -1637,45 +1663,32 @@ namespace App {
Local::writeFileLocation(mediaKey(DocumentFileLocation, convert->dc, convert->id), loc);
}
}
- DocumentsData::const_iterator i = documentsData.constFind(document);
+ DocumentsData::const_iterator i = ::documentsData.constFind(document);
DocumentData *result;
- if (i == documentsData.cend()) {
+ if (i == ::documentsData.cend()) {
if (convert) {
result = convert;
} else {
result = new DocumentData(document, access, date, attributes, mime, thumb, dc, size);
+ result->recountIsImage();
if (result->sticker()) result->sticker()->loc = thumbLocation;
}
- documentsData.insert(document, result);
+ ::documentsData.insert(document, result);
} else {
result = i.value();
- if (result != convert) {
- if (!result->date && date) {
- result->access = access;
- result->date = date;
- result->setattributes(attributes);
- result->mime = mime;
+ if (result != convert && date) {
+ result->access = access;
+ result->date = date;
+ result->setattributes(attributes);
+ result->mime = mime;
+ if (!thumb->isNull() && (result->thumb->isNull() || result->thumb->width() < thumb->width() || result->thumb->height() < thumb->height())) {
result->thumb = thumb;
- result->dc = dc;
- result->size = size;
- } else {
- if (!thumb->isNull() && (result->thumb->isNull() || result->thumb->width() < thumb->width() || result->thumb->height() < thumb->height())) {
- result->thumb = thumb;
- }
- if (result->sticker() && !attributes.isEmpty() && (result->sticker()->alt.isEmpty() || result->sticker()->set.type() == mtpc_inputStickerSetEmpty)) {
- for (QVector::const_iterator i = attributes.cbegin(), e = attributes.cend(); i != e; ++i) {
- if (i->type() == mtpc_documentAttributeSticker) {
- const MTPDdocumentAttributeSticker &d(i->c_documentAttributeSticker());
- if (d.valt.c_string().v.length() > 0) {
- result->sticker()->alt = qs(d.valt);
- result->sticker()->set = d.vstickerset;
- }
- }
- }
- }
- if (result->sticker() && !result->sticker()->loc.dc && thumbLocation.dc) {
- result->sticker()->loc = thumbLocation;
- }
+ }
+ result->dc = dc;
+ result->size = size;
+ result->recountIsImage();
+ if (result->sticker() && result->sticker()->loc.isNull() && !thumbLocation.isNull()) {
+ result->sticker()->loc = thumbLocation;
}
}
}
@@ -1750,7 +1763,7 @@ namespace App {
}
return result;
}
-
+
ImageLinkData *imageLink(const QString &imageLink) {
ImageLinksData::const_iterator i = imageLinksData.constFind(imageLink);
if (i == imageLinksData.cend()) {
@@ -1758,7 +1771,7 @@ namespace App {
}
return i.value();
}
-
+
ImageLinkData *imageLinkSet(const QString &imageLink, ImageLinkType type, const QString &url) {
ImageLinksData::const_iterator i = imageLinksData.constFind(imageLink);
ImageLinkData *result;
@@ -1775,16 +1788,16 @@ namespace App {
void forgetMedia() {
lastPhotos.clear();
lastPhotosMap.clear();
- for (PhotosData::const_iterator i = photosData.cbegin(), e = photosData.cend(); i != e; ++i) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
@@ -1795,7 +1808,7 @@ namespace App {
MTPPhoto photoFromUserPhoto(MTPint userId, MTPint date, const MTPUserProfilePhoto &photo) {
if (photo.type() == mtpc_userProfilePhoto) {
const MTPDuserProfilePhoto &uphoto(photo.c_userProfilePhoto());
-
+
QVector photoSizes;
photoSizes.push_back(MTP_photoSize(MTP_string("a"), uphoto.vphoto_small, MTP_int(160), MTP_int(160), MTP_int(0)));
photoSizes.push_back(MTP_photoSize(MTP_string("c"), uphoto.vphoto_big, MTP_int(640), MTP_int(640), MTP_int(0)));
@@ -1820,7 +1833,7 @@ namespace App {
History *historyFromDialog(const PeerId &peer, int32 unreadCnt, int32 maxInboxRead) {
return ::histories.findOrInsert(peer, unreadCnt, maxInboxRead);
}
-
+
History *historyLoaded(const PeerId &peer) {
return ::histories.find(peer);
}
@@ -1836,47 +1849,16 @@ namespace App {
return 0;
}
- void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
- if (HistoryReply *r = oldItem->toHistoryReply()) {
- QMap &replies(::repliesTo[r->replyToMessage()]);
- replies.remove(r);
- if (HistoryReply *n = newItem->toHistoryReply()) {
- replies.insert(n, true);
- }
- }
- RepliesTo::iterator i = ::repliesTo.find(oldItem);
- if (i != ::repliesTo.cend() && oldItem != newItem) {
- QMap replies = i.value();
- ::repliesTo.erase(i);
- ::repliesTo[newItem] = replies;
- for (QMap::iterator i = replies.begin(), e = replies.end(); i != e; ++i) {
- i.key()->replyToReplaced(oldItem, 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) {
+ void historyRegItem(HistoryItem *item) {
MsgsData *data = fetchMsgsData(item->channelId());
MsgsData::const_iterator i = data->constFind(item->id);
if (i == data->cend()) {
data->insert(item->id, item);
- return 0;
- }
- if (i.value() != item && !i.value()->block() && item->block()) { // replace search item
- itemReplaced(i.value(), item);
- delete i.value();
+ } else if (i.value() != item) {
+ LOG(("App Error: trying to historyRegItem() an already registered item"));
+ i.value()->destroy();
data->insert(item->id, item);
- return 0;
}
- return (i.value() == item) ? 0 : i.value();
}
void historyItemDetached(HistoryItem *item) {
@@ -1927,6 +1909,8 @@ namespace App {
}
void historyClearMsgs() {
+ ::repliesTo.clear();
+
QVector toDelete;
for (MsgsData::const_iterator i = msgsData.cbegin(), e = msgsData.cend(); i != e; ++i) {
if ((*i)->detached()) {
@@ -1952,7 +1936,6 @@ namespace App {
}
void historyClearItems() {
- historyClearMsgs();
randomData.clear();
sentData.clear();
mutedPeers.clear();
@@ -1963,39 +1946,41 @@ namespace App {
delete *i;
}
peersData.clear();
- for (PhotosData::const_iterator i = photosData.cbegin(), e = photosData.cend(); i != e; ++i) {
+ for (PhotosData::const_iterator i = ::photosData.cbegin(), e = ::photosData.cend(); i != e; ++i) {
delete *i;
}
- photosData.clear();
- for (VideosData::const_iterator i = videosData.cbegin(), e = videosData.cend(); i != e; ++i) {
+ ::photosData.clear();
+ for (VideosData::const_iterator i = ::videosData.cbegin(), e = ::videosData.cend(); i != e; ++i) {
delete *i;
}
- videosData.clear();
- for (AudiosData::const_iterator i = audiosData.cbegin(), e = audiosData.cend(); i != e; ++i) {
+ ::videosData.clear();
+ for (AudiosData::const_iterator i = ::audiosData.cbegin(), e = ::audiosData.cend(); i != e; ++i) {
delete *i;
}
- audiosData.clear();
- for (DocumentsData::const_iterator i = documentsData.cbegin(), e = documentsData.cend(); i != e; ++i) {
+ ::audiosData.clear();
+ for (DocumentsData::const_iterator i = ::documentsData.cbegin(), e = ::documentsData.cend(); i != e; ++i) {
delete *i;
}
- documentsData.clear();
+ ::documentsData.clear();
for (WebPagesData::const_iterator i = webPagesData.cbegin(), e = webPagesData.cend(); i != e; ++i) {
delete *i;
}
webPagesData.clear();
if (api()) api()->clearWebPageRequests();
cSetRecentStickers(RecentStickerPack());
- cSetStickersHash(0);
cSetStickerSets(StickerSets());
cSetStickerSetsOrder(StickerSetsOrder());
cSetLastStickersUpdate(0);
+ cSetSavedGifs(SavedGifs());
+ cSetLastSavedGifsUpdate(0);
cSetReportSpamStatuses(ReportSpamStatuses());
+ ::photoItems.clear();
::videoItems.clear();
::audioItems.clear();
::documentItems.clear();
::webPageItems.clear();
- ::sharedContactPhones.clear();
- ::repliesTo.clear();
+ ::sharedContactItems.clear();
+ ::gifItems.clear();
lastPhotos.clear();
lastPhotosMap.clear();
::self = 0;
@@ -2085,7 +2070,6 @@ namespace App {
}
void initMedia() {
- deinitMedia(false);
audioInit();
if (!::monofont) {
@@ -2126,9 +2110,9 @@ namespace App {
prepareCorners(ServiceSelectedCorners, st::msgRadius, st::msgServiceSelectBg);
prepareCorners(SelectedOverlayCorners, st::msgRadius, st::msgSelectOverlay);
prepareCorners(DateCorners, st::msgRadius, st::msgDateImgBg);
- prepareCorners(DateSelectedCorners, st::msgRadius, st::msgDateImgSelectBg);
+ prepareCorners(DateSelectedCorners, st::msgRadius, st::msgDateImgBgSelected);
prepareCorners(InShadowCorners, st::msgRadius, st::msgInShadow);
- prepareCorners(InSelectedShadowCorners, st::msgRadius, st::msgInSelectShadow);
+ prepareCorners(InSelectedShadowCorners, st::msgRadius, st::msgInShadowSelected);
prepareCorners(ForwardCorners, st::msgRadius, st::forwardBg);
prepareCorners(MediaviewSaveCorners, st::msgRadius, st::medviewSaveMsg);
prepareCorners(EmojiHoverCorners, st::msgRadius, st::emojiPanHover);
@@ -2138,58 +2122,56 @@ namespace App {
prepareCorners(BotKeyboardDownCorners, st::msgRadius, st::botKbDownBg);
prepareCorners(PhotoSelectOverlayCorners, st::msgRadius, st::overviewPhotoSelectOverlay);
- prepareCorners(DocRedCorners, st::msgRadius, st::mvDocRedColor);
- prepareCorners(DocYellowCorners, st::msgRadius, st::mvDocYellowColor);
- prepareCorners(DocGreenCorners, st::msgRadius, st::mvDocGreenColor);
- prepareCorners(DocBlueCorners, st::msgRadius, st::mvDocBlueColor);
+ prepareCorners(DocBlueCorners, st::msgRadius, st::msgFileBlueColor);
+ prepareCorners(DocGreenCorners, st::msgRadius, st::msgFileGreenColor);
+ prepareCorners(DocRedCorners, st::msgRadius, st::msgFileRedColor);
+ prepareCorners(DocYellowCorners, st::msgRadius, st::msgFileYellowColor);
prepareCorners(MessageInCorners, st::msgRadius, st::msgInBg, &st::msgInShadow);
- prepareCorners(MessageInSelectedCorners, st::msgRadius, st::msgInSelectBg, &st::msgInSelectShadow);
+ prepareCorners(MessageInSelectedCorners, st::msgRadius, st::msgInBgSelected, &st::msgInShadowSelected);
prepareCorners(MessageOutCorners, st::msgRadius, st::msgOutBg, &st::msgOutShadow);
- prepareCorners(MessageOutSelectedCorners, st::msgRadius, st::msgOutSelectBg, &st::msgOutSelectShadow);
- prepareCorners(ButtonHoverCorners, st::msgRadius, st::mediaSaveButton.overBgColor, &st::msgInShadow);
-
+ prepareCorners(MessageOutSelectedCorners, st::msgRadius, st::msgOutBgSelected, &st::msgOutShadowSelected);
}
-
- void deinitMedia(bool completely) {
+
+ void clearHistories() {
textlnkOver(TextLinkPtr());
textlnkDown(TextLinkPtr());
histories().clear();
- if (completely) {
- audioFinish();
-
- delete ::sprite;
- ::sprite = 0;
- delete ::emoji;
- ::emoji = 0;
- delete ::emojiLarge;
- ::emojiLarge = 0;
- for (int32 j = 0; j < 4; ++j) {
- for (int32 i = 0; i < RoundCornersCount; ++i) {
- delete ::corners[i].p[j]; ::corners[i].p[j] = 0;
- }
- delete ::cornersMask[j]; ::cornersMask[j] = 0;
- }
- for (CornersMap::const_iterator i = ::cornersMap.cbegin(), e = ::cornersMap.cend(); i != e; ++i) {
- for (int32 j = 0; j < 4; ++j) {
- delete i->p[j];
- }
- }
- ::cornersMap.clear();
- mainEmojiMap.clear();
- otherEmojiMap.clear();
-
- clearAllImages();
- } else {
- clearStorageImages();
- cSetServerBackgrounds(WallPapers());
- }
+ clearStorageImages();
+ cSetServerBackgrounds(WallPapers());
serviceImageCacheSize = imageCacheSize();
}
+ void deinitMedia() {
+ audioFinish();
+
+ delete ::sprite;
+ ::sprite = 0;
+ delete ::emoji;
+ ::emoji = 0;
+ delete ::emojiLarge;
+ ::emojiLarge = 0;
+ for (int32 j = 0; j < 4; ++j) {
+ for (int32 i = 0; i < RoundCornersCount; ++i) {
+ delete ::corners[i].p[j]; ::corners[i].p[j] = 0;
+ }
+ delete ::cornersMask[j]; ::cornersMask[j] = 0;
+ }
+ for (CornersMap::const_iterator i = ::cornersMap.cbegin(), e = ::cornersMap.cend(); i != e; ++i) {
+ for (int32 j = 0; j < 4; ++j) {
+ delete i->p[j];
+ }
+ }
+ ::cornersMap.clear();
+ mainEmojiMap.clear();
+ otherEmojiMap.clear();
+
+ clearAllImages();
+ }
+
void hoveredItem(HistoryItem *item) {
::hoveredItem = item;
}
@@ -2205,7 +2187,7 @@ namespace App {
HistoryItem *pressedItem() {
return ::pressedItem;
}
-
+
void hoveredLinkItem(HistoryItem *item) {
::hoveredLinkItem = item;
}
@@ -2221,7 +2203,7 @@ namespace App {
HistoryItem *pressedLinkItem() {
return ::pressedLinkItem;
}
-
+
void contextItem(HistoryItem *item) {
::contextItem = item;
}
@@ -2317,16 +2299,24 @@ namespace App {
if (!format) {
format = &tmpFormat;
}
- QImageReader reader(&buffer, *format);
- if (animated) *animated = reader.supportsAnimation() && reader.imageCount() > 1;
- if (!reader.read(&result)) {
- return QImage();
+ {
+ QImageReader reader(&buffer, *format);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
+ reader.setAutoTransform(true);
+#endif
+ if (animated) *animated = reader.supportsAnimation() && reader.imageCount() > 1;
+ QByteArray fmt = reader.format();
+ if (!fmt.isEmpty()) *format = fmt;
+ if (!reader.read(&result)) {
+ return QImage();
+ }
+ fmt = reader.format();
+ if (!fmt.isEmpty()) *format = fmt;
}
-
buffer.seek(0);
- *format = reader.format();
- QString fmt = QString::fromUtf8(*format).toLower() ;
+ QString fmt = QString::fromUtf8(*format).toLower();
if (fmt == "jpg" || fmt == "jpeg") {
+#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
ExifData *exifData = exif_data_new_from_data((const uchar*)(data.constData()), data.size());
if (exifData) {
ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
@@ -2347,6 +2337,7 @@ namespace App {
}
exif_data_free(exifData);
}
+#endif
} else if (opaque && result.hasAlphaChannel()) {
QImage solid(result.width(), result.height(), QImage::Format_ARGB32_Premultiplied);
solid.fill(st::white->c);
@@ -2370,8 +2361,24 @@ namespace App {
return result;
}
+ void regPhotoItem(PhotoData *data, HistoryItem *item) {
+ ::photoItems[data].insert(item, NullType());
+ }
+
+ void unregPhotoItem(PhotoData *data, HistoryItem *item) {
+ ::photoItems[data].remove(item);
+ }
+
+ const PhotoItems &photoItems() {
+ return ::photoItems;
+ }
+
+ const PhotosData &photosData() {
+ return ::photosData;
+ }
+
void regVideoItem(VideoData *data, HistoryItem *item) {
- ::videoItems[data][item] = true;
+ ::videoItems[data].insert(item, NullType());
}
void unregVideoItem(VideoData *data, HistoryItem *item) {
@@ -2382,8 +2389,12 @@ namespace App {
return ::videoItems;
}
+ const VideosData &videosData() {
+ return ::videosData;
+ }
+
void regAudioItem(AudioData *data, HistoryItem *item) {
- ::audioItems[data][item] = true;
+ ::audioItems[data].insert(item, NullType());
}
void unregAudioItem(AudioData*data, HistoryItem *item) {
@@ -2394,8 +2405,12 @@ namespace App {
return ::audioItems;
}
+ const AudiosData &audiosData() {
+ return ::audiosData;
+ }
+
void regDocumentItem(DocumentData *data, HistoryItem *item) {
- ::documentItems[data][item] = true;
+ ::documentItems[data].insert(item, NullType());
}
void unregDocumentItem(DocumentData *data, HistoryItem *item) {
@@ -2406,8 +2421,12 @@ namespace App {
return ::documentItems;
}
+ const DocumentsData &documentsData() {
+ return ::documentsData;
+ }
+
void regWebPageItem(WebPageData *data, HistoryItem *item) {
- ::webPageItems[data][item] = true;
+ ::webPageItems[data].insert(item, NullType());
}
void unregWebPageItem(WebPageData *data, HistoryItem *item) {
@@ -2418,12 +2437,46 @@ namespace App {
return ::webPageItems;
}
- void regSharedContactPhone(int32 userId, const QString &phone) {
- ::sharedContactPhones[userId] = phone;
+ void regSharedContactItem(int32 userId, HistoryItem *item) {
+ ::sharedContactItems[userId].insert(item, NullType());
+ }
+
+ void unregSharedContactItem(int32 userId, HistoryItem *item) {
+ ::sharedContactItems[userId].remove(item);
+ }
+
+ const SharedContactItems &sharedContactItems() {
+ return ::sharedContactItems;
+ }
+
+ void regGifItem(ClipReader *reader, HistoryItem *item) {
+ ::gifItems.insert(reader, item);
+ }
+
+ void unregGifItem(ClipReader *reader) {
+ ::gifItems.remove(reader);
+ }
+
+ void stopGifItems() {
+ if (!::gifItems.isEmpty()) {
+ GifItems gifs = ::gifItems;
+ for (GifItems::const_iterator i = gifs.cbegin(), e = gifs.cend(); i != e; ++i) {
+ if (HistoryMedia *media = i.value()->getMedia()) {
+ media->stopInline(i.value());
+ }
+ }
+ }
}
QString phoneFromSharedContact(int32 userId) {
- return ::sharedContactPhones.value(userId);
+ SharedContactItems::const_iterator i = ::sharedContactItems.constFind(userId);
+ if (i != ::sharedContactItems.cend()) {
+ HistoryMedia *media = i->cbegin().key()->getMedia();
+ if (media && media->type() == MediaTypeContact) {
+ return static_cast(media)->phone();
+ }
+ }
+ return QString();
}
void regMuted(PeerData *peer, int32 changeIn) {
@@ -2454,6 +2507,19 @@ namespace App {
if (changeInMin) App::main()->updateMutedIn(changeInMin);
}
+ void regInlineResultLoader(FileLoader *loader, InlineResult *result) {
+ ::inlineResultLoaders.insert(loader, result);
+ }
+
+ void unregInlineResultLoader(FileLoader *loader) {
+ ::inlineResultLoaders.remove(loader);
+ }
+
+ InlineResult *inlineResultFromLoader(FileLoader *loader) {
+ InlineResultLoaders::const_iterator i = ::inlineResultLoaders.find(loader);
+ return (i == ::inlineResultLoaders.cend()) ? 0 : i.value();
+ }
+
inline void insertReplyMarkup(ChannelId channelId, MsgId msgId, const ReplyMarkup &markup) {
if (channelId == NoChannel) {
replyMarkups.insert(msgId, markup);
@@ -2469,7 +2535,7 @@ namespace App {
case mtpc_replyKeyboardMarkup: {
const MTPDreplyKeyboardMarkup &d(markup.c_replyKeyboardMarkup());
data.flags = d.vflags.v;
-
+
const QVector &v(d.vrows.c_vector().v);
if (!v.isEmpty()) {
commands.reserve(v.size());
@@ -2542,12 +2608,15 @@ namespace App {
}
void setProxySettings(QNetworkAccessManager &manager) {
+ manager.setProxy(getHttpProxySettings());
+ }
+
+ QNetworkProxy getHttpProxySettings() {
if (cConnectionType() == dbictHttpProxy) {
const ConnectionProxy &p(cConnectionProxy());
- manager.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, p.host, p.port, p.user, p.password));
- } else {
- manager.setProxy(QNetworkProxy(QNetworkProxy::DefaultProxy));
+ return QNetworkProxy(QNetworkProxy::HttpProxy, p.host, p.port, p.user, p.password);
}
+ return QNetworkProxy(QNetworkProxy::DefaultProxy);
}
void setProxySettings(QTcpSocket &socket) {
@@ -2557,7 +2626,7 @@ namespace App {
} else {
socket.setProxy(QNetworkProxy(QNetworkProxy::NoProxy));
}
- }
+ }
QImage **cornersMask() {
return ::cornersMask;
diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h
index 1402a7e056..c01fdf9802 100644
--- a/Telegram/SourceFiles/app.h
+++ b/Telegram/SourceFiles/app.h
@@ -32,12 +32,22 @@ class Color;
class FileUploader;
#include "history.h"
+#include "layout.h"
-typedef QMap HistoryItemsMap;
+typedef QMap HistoryItemsMap;
+typedef QHash PhotoItems;
typedef QHash VideoItems;
typedef QHash AudioItems;
typedef QHash DocumentItems;
typedef QHash WebPageItems;
+typedef QHash SharedContactItems;
+typedef QHash GifItems;
+
+typedef QHash PhotosData;
+typedef QHash VideosData;
+typedef QHash AudiosData;
+typedef QHash DocumentsData;
+
struct ReplyMarkup {
ReplyMarkup(int32 flags = 0) : flags(flags) {
}
@@ -46,40 +56,6 @@ struct ReplyMarkup {
int32 flags;
};
-enum RoundCorners {
- NoneCorners = 0x00, // for images
- BlackCorners,
- ServiceCorners,
- ServiceSelectedCorners,
- SelectedOverlayCorners,
- DateCorners,
- DateSelectedCorners,
- ForwardCorners,
- MediaviewSaveCorners,
- EmojiHoverCorners,
- StickerHoverCorners,
- BotKeyboardCorners,
- BotKeyboardOverCorners,
- BotKeyboardDownCorners,
- PhotoSelectOverlayCorners,
-
- DocRedCorners,
- DocYellowCorners,
- DocGreenCorners,
- DocBlueCorners,
-
- InShadowCorners, // for photos without bg
- InSelectedShadowCorners,
-
- MessageInCorners, // with shadow
- MessageInSelectedCorners,
- MessageOutCorners,
- MessageOutSelectedCorners,
- ButtonHoverCorners,
-
- RoundCornersCount
-};
-
class LayeredWidget;
namespace App {
@@ -109,6 +85,8 @@ namespace App {
void feedChatAdmins(const MTPDupdateChatAdmins &d, bool emitPeerUpdated = true);
void feedParticipantAdmin(const MTPDupdateChatParticipantAdmin &d, bool emitPeerUpdated = true);
bool checkEntitiesAndViewsUpdate(const MTPDmessage &m); // returns true if item found and it is not detached
+ void addSavedGif(DocumentData *doc);
+ void checkSavedGif(HistoryItem *item);
void feedMsgs(const QVector &msgs, NewMessageType type);
void feedMsgs(const MTPVector &msgs, NewMessageType type);
void feedInboxRead(const PeerId &peer, MsgId upTo);
@@ -180,7 +158,7 @@ namespace App {
inline HistoryItem *histItemById(const FullMsgId &msgId) {
return histItemById(msgId.channel, msgId.msg);
}
- HistoryItem *historyRegItem(HistoryItem *item);
+ void historyRegItem(HistoryItem *item);
void historyItemDetached(HistoryItem *item);
void historyUnregItem(HistoryItem *item);
void historyClearMsgs();
@@ -214,8 +192,10 @@ namespace App {
const QPixmap &emojiLarge();
const QPixmap &emojiSingle(EmojiPtr emoji, int32 fontHeight);
+ void clearHistories();
+
void initMedia();
- void deinitMedia(bool completely = true);
+ void deinitMedia();
void playSound();
void checkImageCacheSize();
@@ -229,34 +209,53 @@ namespace App {
QImage readImage(QByteArray data, QByteArray *format = 0, bool opaque = true, bool *animated = 0);
QImage readImage(const QString &file, QByteArray *format = 0, bool opaque = true, bool *animated = 0, QByteArray *content = 0);
+ void regPhotoItem(PhotoData *data, HistoryItem *item);
+ void unregPhotoItem(PhotoData *data, HistoryItem *item);
+ const PhotoItems &photoItems();
+ const PhotosData &photosData();
+
void regVideoItem(VideoData *data, HistoryItem *item);
void unregVideoItem(VideoData *data, HistoryItem *item);
const VideoItems &videoItems();
+ const VideosData &videosData();
void regAudioItem(AudioData *data, HistoryItem *item);
void unregAudioItem(AudioData*data, HistoryItem *item);
const AudioItems &audioItems();
+ const AudiosData &audiosData();
void regDocumentItem(DocumentData *data, HistoryItem *item);
void unregDocumentItem(DocumentData *data, HistoryItem *item);
const DocumentItems &documentItems();
+ const DocumentsData &documentsData();
void regWebPageItem(WebPageData *data, HistoryItem *item);
void unregWebPageItem(WebPageData *data, HistoryItem *item);
const WebPageItems &webPageItems();
- void regSharedContactPhone(int32 userId, const QString &phone);
+ void regSharedContactItem(int32 userId, HistoryItem *item);
+ void unregSharedContactItem(int32 userId, HistoryItem *item);
+ const SharedContactItems &sharedContactItems();
QString phoneFromSharedContact(int32 userId);
+ void regGifItem(ClipReader *reader, HistoryItem *item);
+ void unregGifItem(ClipReader *reader);
+ void stopGifItems();
+
void regMuted(PeerData *peer, int32 changeIn);
void unregMuted(PeerData *peer);
void updateMuted();
+ void regInlineResultLoader(FileLoader *loader, InlineResult *result);
+ void unregInlineResultLoader(FileLoader *loader);
+ InlineResult *inlineResultFromLoader(FileLoader *loader);
+
void feedReplyMarkup(ChannelId channelId, MsgId msgId, const MTPReplyMarkup &markup);
void clearReplyMarkup(ChannelId channelId, MsgId msgId);
const ReplyMarkup &replyMarkup(ChannelId channelId, MsgId msgId);
void setProxySettings(QNetworkAccessManager &manager);
+ QNetworkProxy getHttpProxySettings();
void setProxySettings(QTcpSocket &socket);
QImage **cornersMask();
@@ -295,25 +294,4 @@ namespace App {
};
-inline int32 stickersCountHash(bool checkOfficial = false) {
- uint32 acc = 0;
- bool foundOfficial = false, foundBad = false;;
- const StickerSets &sets(cStickerSets());
- const StickerSetsOrder &order(cStickerSetsOrder());
- for (StickerSetsOrder::const_iterator i = order.cbegin(), e = order.cend(); i != e; ++i) {
- StickerSets::const_iterator j = sets.constFind(*i);
- if (j != sets.cend()) {
- if (j->id == 0) {
- foundBad = true;
- } else if (j->flags & MTPDstickerSet::flag_official) {
- foundOfficial = true;
- }
- if (!(j->flags & MTPDstickerSet::flag_disabled)) {
- acc = (acc * 20261) + j->hash;
- }
- }
- }
- return (!checkOfficial || (!foundBad && foundOfficial)) ? int32(acc & 0x7FFFFFFF) : 0;
-}
-
#include "facades.h"
diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp
index d6e7ae35fb..856174b53f 100644
--- a/Telegram/SourceFiles/application.cpp
+++ b/Telegram/SourceFiles/application.cpp
@@ -208,7 +208,7 @@ void Application::updateGotCurrent() {
if (!updateReply || updateThread) return;
cSetLastUpdateCheck(unixtime());
- QRegularExpressionMatch m = QRegularExpression(qsl("^\\s*(\\d+)\\s*:\\s*([\\x21-\\x7f]+)\\s*$")).match(QString::fromUtf8(updateReply->readAll()));
+ QRegularExpressionMatch m = QRegularExpression(qsl("^\\s*(\\d+)\\s*:\\s*([\\x21-\\x7f]+)\\s*$")).match(QString::fromLatin1(updateReply->readAll()));
if (m.hasMatch()) {
uint64 currentVersion = m.captured(1).toULongLong();
QString url = m.captured(2);
@@ -445,7 +445,7 @@ void Application::onSwitchDebugMode() {
f.write("1");
f.close();
}
- App::wnd()->hideLayer();
+ Ui::hideLayer();
}
}
@@ -489,7 +489,7 @@ int32 Application::updatingReady() {
#endif
FileUploader *Application::uploader() {
- if (!::uploader) ::uploader = new FileUploader();
+ if (!::uploader && !App::quiting()) ::uploader = new FileUploader();
return ::uploader;
}
@@ -548,9 +548,9 @@ void Application::stopUpdate() {
void Application::startUpdateCheck(bool forceWait) {
updateCheckTimer.stop();
if (updateRequestId || updateThread || updateReply || !cAutoUpdate()) return;
-
+
int32 constDelay = cBetaVersion() ? 600 : UpdateDelayConstPart, randDelay = cBetaVersion() ? 300 : UpdateDelayRandPart;
- int32 updateInSecs = cLastUpdateCheck() + constDelay + (rand() % randDelay) - unixtime();
+ int32 updateInSecs = cLastUpdateCheck() + constDelay + int32(MTP::nonce() % randDelay) - unixtime();
bool sendRequest = (updateInSecs <= 0 || updateInSecs > (constDelay + randDelay));
if (!sendRequest && !forceWait) {
QDir updates(cWorkingDir() + "tupdates");
@@ -642,7 +642,7 @@ void Application::socketConnected() {
}
commands += qsl("CMD:show;");
DEBUG_LOG(("Application Info: writing commands %1").arg(commands));
- socket.write(commands.toLocal8Bit());
+ socket.write(commands.toLatin1());
}
void Application::socketWritten(qint64/* bytes*/) {
@@ -684,7 +684,7 @@ void Application::socketError(QLocalSocket::LocalSocketError e) {
socket.close();
psCheckLocalSocket(serverName);
-
+
if (!server.listen(serverName)) {
DEBUG_LOG(("Application Error: failed to start listening to %1 server, error %2").arg(serverName).arg(int(server.serverError())));
return App::quit();
@@ -705,10 +705,10 @@ void Application::checkMapVersion() {
if (Local::oldMapVersion() < AppVersion) {
if (Local::oldMapVersion()) {
QString versionFeatures;
- if (cDevVersion() && Local::oldMapVersion() < 9014) {
- versionFeatures = QString::fromUtf8("\xe2\x80\x94 Sticker management: manually rearrange your sticker packs, pack order is now synced across all your devices\n\xe2\x80\x94 Click and hold on a sticker to preview it before sending\n\xe2\x80\x94 New context menu for chats in chats list\n\xe2\x80\x94 Support for all existing emoji");// .replace('@', qsl("@") + QChar(0x200D));
- } else if (Local::oldMapVersion() < 9015) {
- versionFeatures = lang(lng_new_version_text).trimmed();
+ if (cDevVersion() && Local::oldMapVersion() < 9019) {
+ versionFeatures = QString::fromUtf8("\xe2\x80\x94 Choose an emoticon and see the suggested stickers\n\xe2\x80\x94 Bug fixes in minor improvements");// .replace('@', qsl("@") + QChar(0x200D));
+ } else if (Local::oldMapVersion() < 9016) {
+ versionFeatures = lng_new_version_text(lt_gifs_link, qsl("https://telegram.org/blog/gif-revolution"), lt_bots_link, qsl("https://telegram.org/blog/inline-bots")).trimmed();
} else {
versionFeatures = lang(lng_new_version_minor).trimmed();
}
@@ -769,7 +769,7 @@ void Application::startApp() {
}
QNetworkProxyFactory::setUseSystemConfiguration(true);
-
+
if (state != Local::ReadMapPassNeeded) {
checkMapVersion();
}
@@ -799,13 +799,13 @@ void Application::readClients() {
for (ClientSockets::iterator i = clients.begin(), e = clients.end(); i != e; ++i) {
i->second.append(i->first->readAll());
if (i->second.size()) {
- QString cmds(QString::fromLocal8Bit(i->second));
+ QString cmds(QString::fromLatin1(i->second));
int32 from = 0, l = cmds.length();
for (int32 to = cmds.indexOf(QChar(';'), from); to >= from; to = (from < l) ? cmds.indexOf(QChar(';'), from) : -1) {
QStringRef cmd(&cmds, from, to - from);
if (cmd.startsWith(qsl("CMD:"))) {
execExternal(cmds.mid(from + 4, to - from - 4));
- QByteArray response(qsl("RES:%1;").arg(QCoreApplication::applicationPid()).toUtf8());
+ QByteArray response(qsl("RES:%1;").arg(QCoreApplication::applicationPid()).toLatin1());
i->first->write(response.data(), response.size());
} else if (cmd.startsWith(qsl("SEND:"))) {
if (cSendPaths().isEmpty()) {
@@ -878,16 +878,20 @@ void Application::closeApplication() {
Application::~Application() {
App::setQuiting();
+
window->setParent(0);
anim::stopManager();
socket.close();
closeApplication();
+ stopWebLoadManager();
App::deinitMedia();
deinitImageLinkManager();
+
mainApp = 0;
delete ::uploader;
+
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
delete updateReply;
updateReply = 0;
@@ -897,7 +901,9 @@ Application::~Application() {
updateThread = 0;
#endif
- delete window;
+ Window *w = window;
+ window = 0;
+ delete w;
delete cChatBackground();
cSetChatBackground(0);
@@ -906,7 +912,7 @@ Application::~Application() {
cSetChatDogImage(0);
style::stopManager();
-
+
delete _translator;
}
diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png
index b40fa32970..ce32997180 100644
Binary files a/Telegram/SourceFiles/art/sprite.png and b/Telegram/SourceFiles/art/sprite.png differ
diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png
index 05d810234d..16b454f057 100644
Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/SourceFiles/art/sprite_200x.png differ
diff --git a/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/audio.cpp
index 433e549d3c..1f86108be0 100644
--- a/Telegram/SourceFiles/audio.cpp
+++ b/Telegram/SourceFiles/audio.cpp
@@ -27,15 +27,6 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#define AL_ALEXT_PROTOTYPES
#include
-extern "C" {
-
-#include
-#include
-#include
-#include
-
-}
-
#ifdef Q_OS_MAC
extern "C" {
@@ -106,15 +97,11 @@ bool _checkALError() {
Q_DECLARE_METATYPE(AudioMsgId);
Q_DECLARE_METATYPE(SongMsgId);
void audioInit() {
- av_register_all();
- avcodec_register_all();
-
if (!capture) {
capture = new AudioCapture();
cSetHasAudioCapture(capture->check());
}
- uint64 ms = getms();
if (audioDevice) return;
audioDevice = alcOpenDevice(0);
@@ -122,7 +109,7 @@ void audioInit() {
LOG(("Audio Error: default sound device not present."));
return;
}
-
+
ALCint attributes[] = { ALC_STEREO_SOURCES, 8, 0 };
audioContext = alcCreateContext(audioDevice, attributes);
alcMakeContextCurrent(audioContext);
@@ -223,7 +210,6 @@ void audioInit() {
player = new AudioPlayer();
alcDevicePauseSOFT(audioDevice);
- LOG(("Audio init time: %1").arg(getms() - ms));
cSetHasAudioPlayer(true);
}
@@ -464,7 +450,7 @@ void AudioPlayer::play(const AudioMsgId &audio, int64 position) {
}
current->audio = audio;
current->file = audio.audio->location(true);
- current->data = audio.audio->data;
+ current->data = audio.audio->data();
if (current->file.isEmpty() && current->data.isEmpty()) {
setStoppedState(current, AudioPlayerStoppedAtError);
onError(audio);
@@ -508,14 +494,11 @@ void AudioPlayer::play(const SongMsgId &song, int64 position) {
}
current->song = song;
current->file = song.song->location(true);
- current->data = song.song->data;
+ current->data = song.song->data();
if (current->file.isEmpty() && current->data.isEmpty()) {
setStoppedState(current);
- if (!song.song->loader) {
+ if (!song.song->loading()) {
DocumentOpenLink::doOpen(song.song);
- song.song->openOnSave = 0;
- song.song->openOnSaveMsgId = FullMsgId();
- if (song.song->loader) song.song->loader->start(true, true);
}
} else {
current->state = fadedStart ? AudioPlayerStarting : AudioPlayerPlaying;
@@ -528,7 +511,7 @@ void AudioPlayer::play(const SongMsgId &song, int64 position) {
bool AudioPlayer::checkCurrentALError(MediaOverviewType type) {
if (_checkALError()) return true;
-
+
switch (type) {
case OverviewAudios:
setStoppedState(&_audioData[_audioCurrent], AudioPlayerStoppedAtError);
@@ -1103,7 +1086,7 @@ protected:
QFile f;
int32 dataPos;
-
+
bool openFile() {
if (data.isEmpty()) {
if (f.isOpen()) f.close();
@@ -1126,7 +1109,6 @@ protected:
};
-static const uint32 AVBlockSize = 4096; // 4Kb
static const AVSampleFormat _toFormat = AV_SAMPLE_FMT_S16;
static const int64_t _toChannelLayout = AV_CH_LAYOUT_STEREO;
static const int32 _toChannels = 2;
@@ -1189,14 +1171,14 @@ public:
return false;
}
- freq = fmtContext->streams[streamId]->codec->sample_rate;
+ freq = codecContext->sample_rate;
if (fmtContext->streams[streamId]->duration == AV_NOPTS_VALUE) {
len = (fmtContext->duration * freq) / AV_TIME_BASE;
} else {
len = (fmtContext->streams[streamId]->duration * freq * fmtContext->streams[streamId]->time_base.num) / fmtContext->streams[streamId]->time_base.den;
}
- uint64_t layout = fmtContext->streams[streamId]->codec->channel_layout;
- inputFormat = fmtContext->streams[streamId]->codec->sample_fmt;
+ uint64_t layout = codecContext->channel_layout;
+ inputFormat = codecContext->sample_fmt;
switch (layout) {
case AV_CH_LAYOUT_MONO:
switch (inputFormat) {
@@ -1302,7 +1284,7 @@ public:
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
LOG(("Audio Error: Unable to avcodec_decode_audio4() file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
- av_free_packet(&avpkt);
+ av_packet_unref(&avpkt);
if (res == AVERROR_INVALIDDATA) return 0; // try to skip bad packet
return -1;
}
@@ -1319,7 +1301,7 @@ public:
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
LOG(("Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
- av_free_packet(&avpkt);
+ av_packet_unref(&avpkt);
return -1;
}
}
@@ -1327,7 +1309,7 @@ public:
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
LOG(("Audio Error: Unable to swr_convert for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
- av_free_packet(&avpkt);
+ av_packet_unref(&avpkt);
return -1;
}
int32 resultLen = av_samples_get_buffer_size(0, _toChannels, res, _toFormat, 1);
@@ -1339,7 +1321,7 @@ public:
}
}
}
- av_free_packet(&avpkt);
+ av_packet_unref(&avpkt);
return 1;
}
@@ -1897,7 +1879,7 @@ void AudioCaptureInner::onInit() {
}
void AudioCaptureInner::onStart() {
-
+
// Start OpenAL Capture
const ALCchar *dName = alcGetString(0, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
DEBUG_LOG(("Audio Info: Capture device name '%1'").arg(dName));
@@ -1918,7 +1900,7 @@ void AudioCaptureInner::onStart() {
// Create encoding context
d->ioBuffer = (uchar*)av_malloc(AVBlockSize);
-
+
d->ioContext = avio_alloc_context(d->ioBuffer, AVBlockSize, 1, static_cast(d), &AudioCapturePrivate::_read_data, &AudioCapturePrivate::_write_data, &AudioCapturePrivate::_seek_data);
int res = 0;
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
@@ -2403,7 +2385,7 @@ public:
QString title() {
return _title;
}
-
+
QString performer() {
return _performer;
}
diff --git a/Telegram/SourceFiles/autoupdater.cpp b/Telegram/SourceFiles/autoupdater.cpp
index a34a471c6e..a1a77a4ff0 100644
--- a/Telegram/SourceFiles/autoupdater.cpp
+++ b/Telegram/SourceFiles/autoupdater.cpp
@@ -51,7 +51,7 @@ void UpdateDownloader::initOutput() {
fileName = m.captured(1).replace(QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")), QString());
}
if (fileName.isEmpty()) {
- fileName = qsl("tupdate-%1").arg(rand());
+ fileName = qsl("tupdate-%1").arg(MTP::nonce() % 1000000);
}
QString dirStr = cWorkingDir() + qsl("tupdates/");
fileName = dirStr + fileName;
@@ -614,4 +614,3 @@ QString countBetaVersionSignature(uint64 version) { // duplicated in packer.cpp
signature = signature.replace('-', '8').replace('_', 'B');
return QString::fromUtf8(signature.mid(19, 32));
}
-
diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp
index ee2db425d9..a6bad679e4 100644
--- a/Telegram/SourceFiles/boxes/aboutbox.cpp
+++ b/Telegram/SourceFiles/boxes/aboutbox.cpp
@@ -84,7 +84,7 @@ void AboutBox::onVersion() {
App::app()->clipboard()->setText(url);
- App::showLayer(new InformBox("The link to the current private beta version of Telegram Desktop was copied to the clipboard."));
+ Ui::showLayer(new InformBox("The link to the current private beta version of Telegram Desktop was copied to the clipboard."));
} else {
QDesktopServices::openUrl(qsl("https://desktop.telegram.org/?_hash=changelog"));
}
diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp
index 10c8d43933..e61b40fcee 100644
--- a/Telegram/SourceFiles/boxes/abstractbox.cpp
+++ b/Telegram/SourceFiles/boxes/abstractbox.cpp
@@ -36,7 +36,7 @@ void BlueTitleShadow::paintEvent(QPaintEvent *e) {
BlueTitleClose::BlueTitleClose(QWidget *parent) : Button(parent)
, a_iconFg(st::boxBlueCloseBg->c)
-, _a_over(animFunc(this, &BlueTitleClose::animStep_over)) {
+, _a_over(animation(this, &BlueTitleClose::step_over)) {
resize(st::boxTitleHeight, st::boxTitleHeight);
setCursor(style::cur_pointer);
connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource)));
@@ -49,18 +49,15 @@ void BlueTitleClose::onStateChange(int oldState, ButtonStateChangeSource source)
}
}
-bool BlueTitleClose::animStep_over(float64 ms) {
+void BlueTitleClose::step_over(float64 ms, bool timer) {
float64 dt = ms / st::boxBlueCloseDuration;
- bool res = true;
if (dt >= 1) {
- res = false;
+ _a_over.stop();
a_iconFg.finish();
} else {
a_iconFg.update(dt, anim::linear);
}
- update((st::boxTitleHeight - st::boxBlueCloseIcon.pxWidth()) / 2, (st::boxTitleHeight - st::boxBlueCloseIcon.pxHeight()) / 2, st::boxBlueCloseIcon.pxWidth(), st::boxBlueCloseIcon.pxHeight());
- return res;
-
+ if (timer) update((st::boxTitleHeight - st::boxBlueCloseIcon.pxWidth()) / 2, (st::boxTitleHeight - st::boxBlueCloseIcon.pxHeight()) / 2, st::boxBlueCloseIcon.pxWidth(), st::boxBlueCloseIcon.pxHeight());
}
void BlueTitleClose::paintEvent(QPaintEvent *e) {
@@ -156,7 +153,7 @@ void AbstractBox::paintEvent(QPaintEvent *e) {
if (paint(p)) return;
}
-void AbstractBox::animStep(float64 ms) {
+void AbstractBox::showStep(float64 ms) {
if (ms >= 1) {
a_opacity.finish();
_cache = QPixmap();
diff --git a/Telegram/SourceFiles/boxes/abstractbox.h b/Telegram/SourceFiles/boxes/abstractbox.h
index 8f3d47f970..4920cebd13 100644
--- a/Telegram/SourceFiles/boxes/abstractbox.h
+++ b/Telegram/SourceFiles/boxes/abstractbox.h
@@ -41,7 +41,7 @@ public slots:
void onStateChange(int oldState, ButtonStateChangeSource source);
private:
- bool animStep_over(float64 ms);
+ void step_over(float64 ms, bool timer);
anim::cvalue a_iconFg;
Animation _a_over;
@@ -54,7 +54,7 @@ public:
AbstractBox(int32 w = st::boxWideWidth);
void parentResized();
- void animStep(float64 ms);
+ void showStep(float64 ms);
void keyPressEvent(QKeyEvent *e);
void resizeEvent(QResizeEvent *e);
void paintEvent(QPaintEvent *e);
diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp
index 05fe432e51..3ba4eac434 100644
--- a/Telegram/SourceFiles/boxes/addcontactbox.cpp
+++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp
@@ -235,8 +235,8 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
}
}
if (uid) {
- App::main()->addNewContact(uid);
- App::wnd()->hideLayer();
+ Notify::userIsContactChanged(App::userLoaded(peerFromUser(uid)), true);
+ Ui::hideLayer();
} else {
_save.hide();
_first.hide();
@@ -336,13 +336,13 @@ void NewGroupBox::resizeEvent(QResizeEvent *e) {
}
void NewGroupBox::onNext() {
- App::wnd()->replaceLayer(new GroupInfoBox(_group.checked() ? CreatingGroupGroup : CreatingGroupChannel, true));
+ Ui::showLayer(new GroupInfoBox(_group.checked() ? CreatingGroupGroup : CreatingGroupChannel, true), KeepOtherLayers);
}
GroupInfoBox::GroupInfoBox(CreatingGroupType creating, bool fromTypeChoose) : AbstractBox(),
_creating(creating),
a_photoOver(0, 0),
-_a_photoOver(animFunc(this, &GroupInfoBox::animStep_photoOver)),
+_a_photoOver(animation(this, &GroupInfoBox::step_photoOver)),
_photoOver(false),
_title(this, st::defaultInputField, lang(_creating == CreatingGroupChannel ? lng_dlg_new_channel_name : lng_dlg_new_group_name)),
_description(this, st::newGroupDescription, lang(lng_create_group_description)),
@@ -464,17 +464,15 @@ void GroupInfoBox::leaveEvent(QEvent *e) {
updateSelected(QCursor::pos());
}
-bool GroupInfoBox::animStep_photoOver(float64 ms) {
+void GroupInfoBox::step_photoOver(float64 ms, bool timer) {
float64 dt = ms / st::setPhotoDuration;
- bool res = true;
if (dt >= 1) {
- res = false;
+ _a_photoOver.stop();
a_photoOver.finish();
} else {
a_photoOver.update(dt, anim::linear);
}
- update(photoRect());
- return res;
+ if (timer) update(photoRect());
}
void GroupInfoBox::onNameSubmit() {
@@ -498,7 +496,7 @@ void GroupInfoBox::onNext() {
return;
}
if (_creating == CreatingGroupGroup) {
- App::wnd()->replaceLayer(new ContactsBox(title, _photoBig));
+ Ui::showLayer(new ContactsBox(title, _photoBig), KeepOtherLayers);
} else {
bool mega = false;
int32 flags = mega ? MTPchannels_CreateChannel::flag_megagroup : MTPchannels_CreateChannel::flag_broadcast;
@@ -551,7 +549,7 @@ void GroupInfoBox::exportDone(const MTPExportedChatInvite &result) {
if (result.type() == mtpc_chatInviteExported) {
_createdChannel->invitationUrl = qs(result.c_chatInviteExported().vlink);
}
- App::wnd()->showLayer(new SetupChannelBox(_createdChannel));
+ Ui::showLayer(new SetupChannelBox(_createdChannel));
}
void GroupInfoBox::onDescriptionResized() {
@@ -595,7 +593,7 @@ void GroupInfoBox::onPhoto() {
}
PhotoCropBox *box = new PhotoCropBox(img, (_creating == CreatingGroupChannel) ? peerFromChannel(0) : peerFromChat(0));
connect(box, SIGNAL(ready(const QImage&)), this, SLOT(onPhotoReady(const QImage&)));
- App::wnd()->replaceLayer(box);
+ Ui::showLayer(box, KeepOtherLayers);
}
void GroupInfoBox::onPhotoReady(const QImage &img) {
@@ -604,23 +602,25 @@ void GroupInfoBox::onPhotoReady(const QImage &img) {
_photoSmall.setDevicePixelRatio(cRetinaFactor());
}
-SetupChannelBox::SetupChannelBox(ChannelData *channel, bool existing) : AbstractBox(),
-_channel(channel),
-_existing(existing),
-_public(this, qsl("channel_privacy"), 0, lang(lng_create_public_channel_title), true),
-_private(this, qsl("channel_privacy"), 1, lang(lng_create_private_channel_title)),
-_comments(this, lang(lng_create_channel_comments), false),
-_aboutPublicWidth(width() - st::boxPadding.left() - st::boxButtonPadding.right() - st::newGroupPadding.left() - st::defaultRadiobutton.textPosition.x()),
-_aboutPublic(st::normalFont, lang(lng_create_public_channel_about), _defaultOptions, _aboutPublicWidth),
-_aboutPrivate(st::normalFont, lang(lng_create_private_channel_about), _defaultOptions, _aboutPublicWidth),
-_aboutComments(st::normalFont, lang(lng_create_channel_comments_about), _defaultOptions, _aboutPublicWidth),
-_link(this, st::defaultInputField, QString(), channel->username, true),
-_linkOver(false),
-_save(this, lang(lng_settings_save), st::defaultBoxButton),
-_skip(this, lang(existing ? lng_cancel : lng_create_group_skip), st::cancelBoxButton),
-_tooMuchUsernames(false),
-_saveRequestId(0), _checkRequestId(0),
-a_goodOpacity(0, 0), _a_goodFade(animFunc(this, &SetupChannelBox::animStep_goodFade)) {
+SetupChannelBox::SetupChannelBox(ChannelData *channel, bool existing) : AbstractBox()
+, _channel(channel)
+, _existing(existing)
+, _public(this, qsl("channel_privacy"), 0, lang(lng_create_public_channel_title), true)
+, _private(this, qsl("channel_privacy"), 1, lang(lng_create_private_channel_title))
+, _comments(this, lang(lng_create_channel_comments), false)
+, _aboutPublicWidth(width() - st::boxPadding.left() - st::boxButtonPadding.right() - st::newGroupPadding.left() - st::defaultRadiobutton.textPosition.x())
+, _aboutPublic(st::normalFont, lang(lng_create_public_channel_about), _defaultOptions, _aboutPublicWidth)
+, _aboutPrivate(st::normalFont, lang(lng_create_private_channel_about), _defaultOptions, _aboutPublicWidth)
+, _aboutComments(st::normalFont, lang(lng_create_channel_comments_about), _defaultOptions, _aboutPublicWidth)
+, _link(this, st::defaultInputField, QString(), channel->username, true)
+, _linkOver(false)
+, _save(this, lang(lng_settings_save), st::defaultBoxButton)
+, _skip(this, lang(existing ? lng_cancel : lng_create_group_skip), st::cancelBoxButton)
+, _tooMuchUsernames(false)
+, _saveRequestId(0)
+, _checkRequestId(0)
+, a_goodOpacity(0, 0)
+, _a_goodFade(animation(this, &SetupChannelBox::step_goodFade)) {
setMouseTracking(true);
_checkRequestId = MTP::send(MTPchannels_CheckUsername(_channel->inputChannel, MTP_string("preston")), RPCDoneHandlerPtr(), rpcFail(&SetupChannelBox::onFirstCheckFail));
@@ -772,22 +772,20 @@ void SetupChannelBox::updateSelected(const QPoint &cursorGlobalPosition) {
}
}
-bool SetupChannelBox::animStep_goodFade(float64 ms) {
+void SetupChannelBox::step_goodFade(float64 ms, bool timer) {
float dt = ms / st::newGroupLinkFadeDuration;
- bool res = true;
if (dt >= 1) {
- res = false;
+ _a_goodFade.stop();
a_goodOpacity.finish();
} else {
a_goodOpacity.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
void SetupChannelBox::closePressed() {
if (!_existing) {
- App::wnd()->showLayer(new ContactsBox(_channel));
+ Ui::showLayer(new ContactsBox(_channel));
}
}
@@ -872,7 +870,7 @@ void SetupChannelBox::onPrivacyChange() {
if (_public.checked()) {
if (_tooMuchUsernames) {
_private.setChecked(true);
- App::wnd()->replaceLayer(new InformBox(lang(lng_channels_too_much_public)));
+ Ui::showLayer(new InformBox(lang(lng_channels_too_much_public)), KeepOtherLayers);
return;
}
_link.show();
@@ -933,7 +931,7 @@ bool SetupChannelBox::onCheckFail(const RPCError &error) {
QString err(error.type());
if (err == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH") {
if (_existing) {
- App::wnd()->showLayer(new InformBox(lang(lng_channels_too_much_public_existing)));
+ Ui::showLayer(new InformBox(lang(lng_channels_too_much_public_existing)));
} else {
_tooMuchUsernames = true;
_private.setChecked(true);
@@ -961,7 +959,7 @@ bool SetupChannelBox::onFirstCheckFail(const RPCError &error) {
QString err(error.type());
if (err == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH") {
if (_existing) {
- App::wnd()->showLayer(new InformBox(lang(lng_channels_too_much_public_existing)));
+ Ui::showLayer(new InformBox(lang(lng_channels_too_much_public_existing)));
} else {
_tooMuchUsernames = true;
_private.setChecked(true);
@@ -1269,7 +1267,7 @@ void EditChannelBox::onSave() {
}
void EditChannelBox::onPublicLink() {
- App::wnd()->replaceLayer(new SetupChannelBox(_channel, true));
+ Ui::showLayer(new SetupChannelBox(_channel, true), KeepOtherLayers);
}
void EditChannelBox::saveDescription() {
diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h
index 00c600c304..b50c715deb 100644
--- a/Telegram/SourceFiles/boxes/addcontactbox.h
+++ b/Telegram/SourceFiles/boxes/addcontactbox.h
@@ -113,8 +113,6 @@ public:
void mousePressEvent(QMouseEvent *e);
void leaveEvent(QEvent *e);
- bool animStep_photoOver(float64 ms);
-
void setInnerFocus() {
_title.setFocus();
}
@@ -136,6 +134,8 @@ protected:
private:
+ void step_photoOver(float64 ms, bool timer);
+
QRect photoRect() const;
void updateMaxHeight();
@@ -202,7 +202,7 @@ protected:
private:
void updateSelected(const QPoint &cursorGlobalPosition);
- bool animStep_goodFade(float64 ms);
+ void step_goodFade(float64 ms, bool timer);
void onUpdateDone(const MTPBool &result);
bool onUpdateFail(const RPCError &error);
diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp
index 69fbe86b08..e6dd0269a9 100644
--- a/Telegram/SourceFiles/boxes/confirmbox.cpp
+++ b/Telegram/SourceFiles/boxes/confirmbox.cpp
@@ -94,7 +94,7 @@ void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos();
updateHover();
if (textlnkOver() && textlnkOver() == textlnkDown()) {
- App::wnd()->hideLayer();
+ Ui::hideLayer();
textlnkOver()->onClick(e->button());
}
textlnkDown(TextLinkPtr());
@@ -184,14 +184,16 @@ void ConfirmLinkBox::onOpenLink() {
} else {
TextLink(_url).onClick(Qt::LeftButton);
}
- App::wnd()->hideLayer();
+ Ui::hideLayer();
}
-MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth),
-_close(this, lang(lng_box_ok), st::defaultBoxButton),
-_text(st::boxTextFont, lng_participant_invite_sorry(lt_count, cMaxGroupCount()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()),
-_link(link), _linkOver(false),
-a_goodOpacity(0, 0), a_good(animFunc(this, &MaxInviteBox::goodAnimStep)) {
+MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth)
+, _close(this, lang(lng_box_ok), st::defaultBoxButton)
+, _text(st::boxTextFont, lng_participant_invite_sorry(lt_count, cMaxGroupCount()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right())
+, _link(link)
+, _linkOver(false)
+, a_goodOpacity(0, 0)
+, _a_good(animation(this, &MaxInviteBox::step_good)) {
setMouseTracking(true);
_textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right();
@@ -213,7 +215,7 @@ void MaxInviteBox::mousePressEvent(QMouseEvent *e) {
App::app()->clipboard()->setText(_link);
_goodTextLink = lang(lng_create_channel_link_copied);
a_goodOpacity = anim::fvalue(1, 0);
- a_good.start();
+ _a_good.start();
}
}
@@ -232,17 +234,15 @@ void MaxInviteBox::updateSelected(const QPoint &cursorGlobalPosition) {
}
}
-bool MaxInviteBox::goodAnimStep(float64 ms) {
+void MaxInviteBox::step_good(float64 ms, bool timer) {
float dt = ms / st::newGroupLinkFadeDuration;
- bool res = true;
if (dt >= 1) {
- res = false;
+ _a_good.stop();
a_goodOpacity.finish();
} else {
a_goodOpacity.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
void MaxInviteBox::hideAll() {
diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h
index f2a23bf407..ba439eb957 100644
--- a/Telegram/SourceFiles/boxes/confirmbox.h
+++ b/Telegram/SourceFiles/boxes/confirmbox.h
@@ -108,8 +108,7 @@ public:
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void leaveEvent(QEvent *e);
- void updateLink();
-
+
protected:
void hideAll();
@@ -118,7 +117,7 @@ protected:
private:
void updateSelected(const QPoint &cursorGlobalPosition);
- bool goodAnimStep(float64 ms);
+ void step_good(float64 ms, bool timer);
BoxButton _close;
Text _text;
@@ -132,5 +131,5 @@ private:
QString _goodTextLink;
anim::fvalue a_goodOpacity;
- Animation a_good;
+ Animation _a_good;
};
diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp
index 23ac63af9a..f2c24a9e38 100644
--- a/Telegram/SourceFiles/boxes/connectionbox.cpp
+++ b/Telegram/SourceFiles/boxes/connectionbox.cpp
@@ -27,17 +27,17 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "window.h"
-ConnectionBox::ConnectionBox() : AbstractBox(st::boxWidth),
-_hostInput(this, st::connectionHostInputField, lang(lng_connection_host_ph), cConnectionProxy().host),
-_portInput(this, st::connectionPortInputField, lang(lng_connection_port_ph), QString::number(cConnectionProxy().port)),
-_userInput(this, st::connectionUserInputField, lang(lng_connection_user_ph), cConnectionProxy().user),
-_passwordInput(this, st::connectionPasswordInputField, lang(lng_connection_password_ph), cConnectionProxy().password),
-_autoRadio(this, qsl("conn_type"), dbictAuto, lang(lng_connection_auto_rb), (cConnectionType() == dbictAuto)),
-_httpProxyRadio(this, qsl("conn_type"), dbictHttpProxy, lang(lng_connection_http_proxy_rb), (cConnectionType() == dbictHttpProxy)),
-_tcpProxyRadio(this, qsl("conn_type"), dbictTcpProxy, lang(lng_connection_tcp_proxy_rb), (cConnectionType() == dbictTcpProxy)),
-_tryIPv6(this, lang(lng_connection_try_ipv6), cTryIPv6()),
-_save(this, lang(lng_connection_save), st::defaultBoxButton),
-_cancel(this, lang(lng_cancel), st::cancelBoxButton) {
+ConnectionBox::ConnectionBox() : AbstractBox(st::boxWidth)
+, _hostInput(this, st::connectionHostInputField, lang(lng_connection_host_ph), cConnectionProxy().host)
+, _portInput(this, st::connectionPortInputField, lang(lng_connection_port_ph), QString::number(cConnectionProxy().port))
+, _userInput(this, st::connectionUserInputField, lang(lng_connection_user_ph), cConnectionProxy().user)
+, _passwordInput(this, st::connectionPasswordInputField, lang(lng_connection_password_ph), cConnectionProxy().password)
+, _autoRadio(this, qsl("conn_type"), dbictAuto, lang(lng_connection_auto_rb), (cConnectionType() == dbictAuto))
+, _httpProxyRadio(this, qsl("conn_type"), dbictHttpProxy, lang(lng_connection_http_proxy_rb), (cConnectionType() == dbictHttpProxy))
+, _tcpProxyRadio(this, qsl("conn_type"), dbictTcpProxy, lang(lng_connection_tcp_proxy_rb), (cConnectionType() == dbictTcpProxy))
+, _tryIPv6(this, lang(lng_connection_try_ipv6), cTryIPv6())
+, _save(this, lang(lng_connection_save), st::defaultBoxButton)
+, _cancel(this, lang(lng_cancel), st::cancelBoxButton) {
connect(&_save, SIGNAL(clicked()), this, SLOT(onSave()));
connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
@@ -215,6 +215,132 @@ void ConnectionBox::onSave() {
Local::writeSettings();
MTP::restart();
reinitImageLinkManager();
+ reinitWebLoadManager();
emit closed();
}
}
+
+AutoDownloadBox::AutoDownloadBox() : AbstractBox(st::boxWidth)
+, _photoPrivate(this, lang(lng_media_auto_private_chats), !(cAutoDownloadPhoto() & dbiadNoPrivate))
+, _photoGroups(this, lang(lng_media_auto_groups), !(cAutoDownloadPhoto() & dbiadNoGroups))
+, _audioPrivate(this, lang(lng_media_auto_private_chats), !(cAutoDownloadAudio() & dbiadNoPrivate))
+, _audioGroups(this, lang(lng_media_auto_groups), !(cAutoDownloadAudio() & dbiadNoGroups))
+, _gifPrivate(this, lang(lng_media_auto_private_chats), !(cAutoDownloadGif() & dbiadNoPrivate))
+, _gifGroups(this, lang(lng_media_auto_groups), !(cAutoDownloadGif() & dbiadNoGroups))
+, _gifPlay(this, lang(lng_media_auto_play), cAutoPlayGif())
+, _sectionHeight(st::boxTitleHeight + 2 * (st::defaultCheckbox.height + st::setLittleSkip))
+, _save(this, lang(lng_connection_save), st::defaultBoxButton)
+, _cancel(this, lang(lng_cancel), st::cancelBoxButton) {
+
+ setMaxHeight(3 * _sectionHeight + st::setLittleSkip + _gifPlay.height() + st::setLittleSkip + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom());
+
+ connect(&_save, SIGNAL(clicked()), this, SLOT(onSave()));
+ connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
+
+ prepare();
+}
+
+void AutoDownloadBox::hideAll() {
+ _photoPrivate.hide();
+ _photoGroups.hide();
+ _audioPrivate.hide();
+ _audioGroups.hide();
+ _gifPrivate.hide();
+ _gifGroups.hide();
+ _gifPlay.hide();
+
+ _save.hide();
+ _cancel.hide();
+}
+
+void AutoDownloadBox::showAll() {
+ _photoPrivate.show();
+ _photoGroups.show();
+ _audioPrivate.show();
+ _audioGroups.show();
+ _gifPrivate.show();
+ _gifGroups.show();
+ _gifPlay.show();
+
+ _save.show();
+ _cancel.show();
+}
+
+void AutoDownloadBox::paintEvent(QPaintEvent *e) {
+ Painter p(this);
+ if (paint(p)) return;
+
+ p.setPen(st::black);
+ p.setFont(st::semiboldFont);
+ p.drawTextLeft(st::boxTitlePosition.x(), st::boxTitlePosition.y(), width(), lang(lng_media_auto_photo));
+ p.drawTextLeft(st::boxTitlePosition.x(), _sectionHeight + st::boxTitlePosition.y(), width(), lang(lng_media_auto_audio));
+ p.drawTextLeft(st::boxTitlePosition.x(), 2 * _sectionHeight + st::boxTitlePosition.y(), width(), lang(lng_media_auto_gif));
+}
+
+void AutoDownloadBox::resizeEvent(QResizeEvent *e) {
+ _photoPrivate.moveToLeft(st::boxTitlePosition.x(), st::boxTitleHeight + st::setLittleSkip);
+ _photoGroups.moveToLeft(st::boxTitlePosition.x(), _photoPrivate.y() + _photoPrivate.height() + st::setLittleSkip);
+
+ _audioPrivate.moveToLeft(st::boxTitlePosition.x(), _sectionHeight + st::boxTitleHeight + st::setLittleSkip);
+ _audioGroups.moveToLeft(st::boxTitlePosition.x(), _audioPrivate.y() + _audioPrivate.height() + st::setLittleSkip);
+
+ _gifPrivate.moveToLeft(st::boxTitlePosition.x(), 2 * _sectionHeight + st::boxTitleHeight + st::setLittleSkip);
+ _gifGroups.moveToLeft(st::boxTitlePosition.x(), _gifPrivate.y() + _gifPrivate.height() + st::setLittleSkip);
+ _gifPlay.moveToLeft(st::boxTitlePosition.x(), _gifGroups.y() + _gifGroups.height() + st::setLittleSkip);
+
+ _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height());
+ _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y());
+}
+
+void AutoDownloadBox::onSave() {
+ bool changed = false;
+ int32 autoDownloadPhoto = (_photoPrivate.checked() ? 0 : dbiadNoPrivate) | (_photoGroups.checked() ? 0 : dbiadNoGroups);
+ if (cAutoDownloadPhoto() != autoDownloadPhoto) {
+ bool enabledPrivate = ((cAutoDownloadPhoto() & dbiadNoPrivate) && !(autoDownloadPhoto & dbiadNoPrivate));
+ bool enabledGroups = ((cAutoDownloadPhoto() & dbiadNoGroups) && !(autoDownloadPhoto & dbiadNoGroups));
+ cSetAutoDownloadPhoto(autoDownloadPhoto);
+ if (enabledPrivate || enabledGroups) {
+ const PhotosData &data(App::photosData());
+ for (PhotosData::const_iterator i = data.cbegin(), e = data.cend(); i != e; ++i) {
+ i.value()->automaticLoadSettingsChanged();
+ }
+ }
+ changed = true;
+ }
+ int32 autoDownloadAudio = (_audioPrivate.checked() ? 0 : dbiadNoPrivate) | (_audioGroups.checked() ? 0 : dbiadNoGroups);
+ if (cAutoDownloadAudio() != autoDownloadAudio) {
+ bool enabledPrivate = ((cAutoDownloadAudio() & dbiadNoPrivate) && !(autoDownloadAudio & dbiadNoPrivate));
+ bool enabledGroups = ((cAutoDownloadAudio() & dbiadNoGroups) && !(autoDownloadAudio & dbiadNoGroups));
+ cSetAutoDownloadAudio(autoDownloadAudio);
+ if (enabledPrivate || enabledGroups) {
+ const AudiosData &data(App::audiosData());
+ for (AudiosData::const_iterator i = data.cbegin(), e = data.cend(); i != e; ++i) {
+ i.value()->automaticLoadSettingsChanged();
+ }
+ }
+ changed = true;
+ }
+ int32 autoDownloadGif = (_gifPrivate.checked() ? 0 : dbiadNoPrivate) | (_gifGroups.checked() ? 0 : dbiadNoGroups);
+ if (cAutoDownloadGif() != autoDownloadGif) {
+ bool enabledPrivate = ((cAutoDownloadGif() & dbiadNoPrivate) && !(autoDownloadGif & dbiadNoPrivate));
+ bool enabledGroups = ((cAutoDownloadGif() & dbiadNoGroups) && !(autoDownloadGif & dbiadNoGroups));
+ cSetAutoDownloadGif(autoDownloadGif);
+ if (enabledPrivate || enabledGroups) {
+ const DocumentsData &data(App::documentsData());
+ for (DocumentsData::const_iterator i = data.cbegin(), e = data.cend(); i != e; ++i) {
+ i.value()->automaticLoadSettingsChanged();
+ }
+ Notify::automaticLoadSettingsChangedGif();
+ }
+ changed = true;
+ }
+ if (cAutoPlayGif() != _gifPlay.checked()) {
+ cSetAutoPlayGif(_gifPlay.checked());
+ if (!cAutoPlayGif()) {
+ App::stopGifItems();
+ }
+ changed = true;
+ }
+ if (changed) Local::writeUserSettings();
+ onClose();
+}
diff --git a/Telegram/SourceFiles/boxes/connectionbox.h b/Telegram/SourceFiles/boxes/connectionbox.h
index f8bbce5ab4..79a43f378f 100644
--- a/Telegram/SourceFiles/boxes/connectionbox.h
+++ b/Telegram/SourceFiles/boxes/connectionbox.h
@@ -54,3 +54,32 @@ private:
BoxButton _save, _cancel;
};
+
+class AutoDownloadBox : public AbstractBox {
+ Q_OBJECT
+
+public:
+
+ AutoDownloadBox();
+ void paintEvent(QPaintEvent *e);
+ void resizeEvent(QResizeEvent *e);
+
+public slots:
+
+ void onSave();
+
+protected:
+
+ void hideAll();
+ void showAll();
+
+private:
+
+ Checkbox _photoPrivate, _photoGroups;
+ Checkbox _audioPrivate, _audioGroups;
+ Checkbox _gifPrivate, _gifGroups, _gifPlay;
+
+ int32 _sectionHeight;
+
+ BoxButton _save, _cancel;
+};
diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp
index a7827d193b..4ac079ff9f 100644
--- a/Telegram/SourceFiles/boxes/contactsbox.cpp
+++ b/Telegram/SourceFiles/boxes/contactsbox.cpp
@@ -228,8 +228,8 @@ void ContactsInner::onAddBot() {
} else {
App::main()->addParticipants(_addToPeer, QVector(1, _bot));
}
- App::wnd()->hideLayer();
- App::main()->showPeerHistory(_addToPeer->id, ShowAtUnreadMsgId);
+ Ui::hideLayer();
+ Ui::showPeerHistory(_addToPeer, ShowAtUnreadMsgId);
}
void ContactsInner::onAddAdmin() {
@@ -269,9 +269,9 @@ bool ContactsInner::addAdminFail(const RPCError &error, mtpRequestId req) {
_addAdminRequestId = 0;
if (_addAdminBox) _addAdminBox->onClose();
if (error.type() == "USERS_TOO_MUCH") {
- App::wnd()->replaceLayer(new MaxInviteBox(_channel->invitationUrl));
+ Ui::showLayer(new MaxInviteBox(_channel->invitationUrl), KeepOtherLayers);
} else if (error.type() == "ADMINS_TOO_MUCH") {
- App::wnd()->replaceLayer(new InformBox(lang(lng_channel_admins_too_much)));
+ Ui::showLayer(new InformBox(lang(lng_channel_admins_too_much)), KeepOtherLayers);
} else {
emit adminAdded();
}
@@ -292,7 +292,7 @@ void ContactsInner::peerUpdated(PeerData *peer) {
inited = true;
}
if (!_chat->canEdit()) {
- App::wnd()->hideLayer();
+ Ui::hideLayer();
} else if (!_chat->participants.isEmpty()) {
for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) {
delete i.value();
@@ -741,16 +741,16 @@ void ContactsInner::chooseParticipant() {
_addAdminBox = new ConfirmBox(lng_channel_admin_sure(lt_user, _addAdmin->firstName));
connect(_addAdminBox, SIGNAL(confirmed()), this, SLOT(onAddAdmin()));
connect(_addAdminBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoAddAdminBox(QObject*)));
- App::wnd()->replaceLayer(_addAdminBox);
+ Ui::showLayer(_addAdminBox, KeepOtherLayers);
} else if (bot() && (peer->isChat() || peer->isMegagroup())) {
_addToPeer = peer;
ConfirmBox *box = new ConfirmBox(lng_bot_sure_invite(lt_group, peer->name));
connect(box, SIGNAL(confirmed()), this, SLOT(onAddBot()));
- App::wnd()->replaceLayer(box);
+ Ui::showLayer(box, KeepOtherLayers);
} else {
App::wnd()->hideSettings(true);
App::main()->choosePeer(peer->id, ShowAtUnreadMsgId);
- App::wnd()->hideLayer();
+ Ui::hideLayer();
}
}
}
@@ -1558,7 +1558,7 @@ void ContactsBox::resizeEvent(QResizeEvent *e) {
void ContactsBox::closePressed() {
if (_inner.channel() && !_inner.hasAlreadyMembersInChannel()) {
- App::main()->showPeerHistory(_inner.channel()->id, ShowAtTheEndMsgId);
+ Ui::showPeerHistory(_inner.channel(), ShowAtTheEndMsgId);
}
}
@@ -1590,8 +1590,8 @@ void ContactsBox::onInvite() {
App::main()->addParticipants(_inner.chat() ? (PeerData*)_inner.chat() : _inner.channel(), users);
if (_inner.chat()) {
- App::wnd()->hideLayer();
- App::main()->showPeerHistory(_inner.chat()->id, ShowAtTheEndMsgId);
+ Ui::hideLayer();
+ Ui::showPeerHistory(_inner.chat(), ShowAtTheEndMsgId);
} else {
onClose();
}
@@ -1713,7 +1713,7 @@ void ContactsBox::onScroll() {
}
void ContactsBox::creationDone(const MTPUpdates &updates) {
- App::wnd()->hideLayer();
+ Ui::hideLayer();
App::main()->sentUpdatesReceived(updates);
const QVector *v = 0;
@@ -1730,7 +1730,7 @@ void ContactsBox::creationDone(const MTPUpdates &updates) {
if (!_creationPhoto.isNull()) {
App::app()->uploadProfilePhoto(_creationPhoto, peer->id);
}
- App::main()->showPeerHistory(peer->id, ShowAtUnreadMsgId);
+ Ui::showPeerHistory(peer, ShowAtUnreadMsgId);
}
} else {
LOG(("API Error: chat not found in updates (ContactsBox::creationDone)"));
@@ -1749,7 +1749,7 @@ bool ContactsBox::creationFail(const RPCError &error) {
_filter.showError();
return true;
} else if (error.type() == "PEER_FLOOD") {
- App::wnd()->replaceLayer(new InformBox(lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info)))));
+ Ui::showLayer(new InformBox(lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info)))), KeepOtherLayers);
return true;
}
return false;
@@ -1872,7 +1872,7 @@ void MembersInner::mouseReleaseEvent(QMouseEvent *e) {
_kickBox = new ConfirmBox((_filter == MembersFilterRecent ? (_channel->isMegagroup() ? lng_profile_sure_kick : lng_profile_sure_kick_channel) : lng_profile_sure_kick_admin)(lt_user, _kickConfirm->firstName));
connect(_kickBox, SIGNAL(confirmed()), this, SLOT(onKickConfirm()));
connect(_kickBox, SIGNAL(destroyed(QObject*)), this, SLOT(onKickBoxDestroyed(QObject*)));
- App::wnd()->replaceLayer(_kickBox);
+ Ui::showLayer(_kickBox, KeepOtherLayers);
}
_kickDown = -1;
}
@@ -1993,7 +1993,7 @@ void MembersInner::chooseParticipant() {
}
if (_sel < 0 || _sel >= _rows.size()) return;
if (PeerData *peer = _rows[_sel]) {
- App::wnd()->hideLayer();
+ Ui::hideLayer();
App::main()->showPeerProfile(peer, ShowAtUnreadMsgId);
}
}
@@ -2199,7 +2199,7 @@ void MembersInner::membersReceived(const MTPchannels_ChannelParticipants &result
bool MembersInner::membersFailed(const RPCError &error, mtpRequestId req) {
if (mtpIsFlood(error)) return false;
- App::wnd()->hideLayer();
+ Ui::hideLayer();
return true;
}
@@ -2298,16 +2298,16 @@ void MembersBox::onScroll() {
void MembersBox::onAdd() {
if (_inner.filter() == MembersFilterRecent && _inner.channel()->count >= (_inner.channel()->isMegagroup() ? cMaxMegaGroupCount() : cMaxGroupCount())) {
- App::wnd()->replaceLayer(new MaxInviteBox(_inner.channel()->invitationUrl));
+ Ui::showLayer(new MaxInviteBox(_inner.channel()->invitationUrl), KeepOtherLayers);
return;
}
ContactsBox *box = new ContactsBox(_inner.channel(), _inner.filter(), _inner.already());
if (_inner.filter() == MembersFilterRecent) {
- App::wnd()->showLayer(box);
+ Ui::showLayer(box);
} else {
_addBox = box;
connect(_addBox, SIGNAL(adminAdded()), this, SLOT(onAdminAdded()));
- App::wnd()->replaceLayer(_addBox);
+ Ui::showLayer(_addBox, KeepOtherLayers);
}
}
diff --git a/Telegram/SourceFiles/boxes/languagebox.cpp b/Telegram/SourceFiles/boxes/languagebox.cpp
index c1aae95e6f..3ec7ea7eac 100644
--- a/Telegram/SourceFiles/boxes/languagebox.cpp
+++ b/Telegram/SourceFiles/boxes/languagebox.cpp
@@ -84,16 +84,16 @@ void LanguageBox::mousePressEvent(QMouseEvent *e) {
for (int32 i = 1; i < languageCount; ++i) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i] + qsl(".strings"), LangLoaderRequest(lngkeys_cnt));
if (!loader.errors().isEmpty()) {
- App::wnd()->showLayer(new InformBox(qsl("Lang \"") + LanguageCodes[i] + qsl("\" error :(\n\nError: ") + loader.errors()));
+ Ui::showLayer(new InformBox(qsl("Lang \"") + LanguageCodes[i] + qsl("\" error :(\n\nError: ") + loader.errors()));
return;
} else if (!loader.warnings().isEmpty()) {
QString warn = loader.warnings();
if (warn.size() > 256) warn = warn.mid(0, 254) + qsl("..");
- App::wnd()->showLayer(new InformBox(qsl("Lang \"") + LanguageCodes[i] + qsl("\" warnings :(\n\nWarnings: ") + warn));
+ Ui::showLayer(new InformBox(qsl("Lang \"") + LanguageCodes[i] + qsl("\" warnings :(\n\nWarnings: ") + warn));
return;
}
}
- App::wnd()->showLayer(new InformBox(qsl("Everything seems great in all %1 languages!").arg(languageCount - 1)));
+ Ui::showLayer(new InformBox(qsl("Everything seems great in all %1 languages!").arg(languageCount - 1)));
}
}
@@ -124,7 +124,7 @@ void LanguageBox::onChange() {
ConfirmBox *box = new ConfirmBox(text, save, st::defaultBoxButton, cancel);
connect(box, SIGNAL(confirmed()), this, SLOT(onSave()));
connect(box, SIGNAL(closed()), this, SLOT(onRestore()));
- App::wnd()->replaceLayer(box);
+ Ui::showLayer(box, KeepOtherLayers);
}
}
}
diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp
index b98f8957d1..45055b2cf4 100644
--- a/Telegram/SourceFiles/boxes/passcodebox.cpp
+++ b/Telegram/SourceFiles/boxes/passcodebox.cpp
@@ -280,7 +280,7 @@ void PasscodeBox::setPasswordDone(const MTPBool &result) {
_setRequest = 0;
emit reloadPassword();
ConfirmBox *box = new InformBox(lang(_reenterPasscode.isHidden() ? lng_cloud_password_removed : (_oldPasscode.isHidden() ? lng_cloud_password_was_set : lng_cloud_password_updated)));
- App::wnd()->showLayer(box);
+ Ui::showLayer(box);
}
bool PasscodeBox::setPasswordFail(const RPCError &error) {
@@ -308,7 +308,7 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
_recoverEmail.showError();
update();
} else if (err == "EMAIL_UNCONFIRMED") {
- App::wnd()->showLayer(new InformBox(lang(lng_cloud_password_almost)));
+ Ui::showLayer(new InformBox(lang(lng_cloud_password_almost)));
emit reloadPassword();
} else if (mtpIsFlood(error)) {
if (_oldPasscode.isHidden()) return false;
@@ -385,7 +385,7 @@ void PasscodeBox::onSave(bool force) {
_replacedBy = new ConfirmBox(lang(lng_cloud_password_about_recover), lang(lng_cloud_password_skip_email), st::attentionBoxButton);
connect(_replacedBy, SIGNAL(confirmed()), this, SLOT(onForceNoMail()));
connect(_replacedBy, SIGNAL(destroyed(QObject*)), this, SLOT(onBoxDestroyed(QObject*)));
- App::wnd()->replaceLayer(_replacedBy);
+ Ui::showLayer(_replacedBy, KeepOtherLayers);
} else {
QByteArray newPasswordData = pwd.isEmpty() ? QByteArray() : (_newSalt + pwd.toUtf8() + _newSalt);
QByteArray newPasswordHash = pwd.isEmpty() ? QByteArray() : QByteArray(32, Qt::Uninitialized);
@@ -481,7 +481,7 @@ void PasscodeBox::recover() {
connect(_replacedBy, SIGNAL(reloadPassword()), this, SIGNAL(reloadPassword()));
connect(_replacedBy, SIGNAL(recoveryExpired()), this, SLOT(onRecoverExpired()));
connect(_replacedBy, SIGNAL(destroyed(QObject*)), this, SLOT(onBoxDestroyed(QObject*)));
- App::wnd()->replaceLayer(_replacedBy);
+ Ui::showLayer(_replacedBy, KeepOtherLayers);
}
void PasscodeBox::recoverStarted(const MTPauth_PasswordRecovery &result) {
@@ -583,7 +583,7 @@ void RecoverBox::codeSubmitDone(bool recover, const MTPauth_Authorization &resul
_submitRequest = 0;
emit reloadPassword();
- App::wnd()->showLayer(new InformBox(lang(lng_cloud_password_removed)));
+ Ui::showLayer(new InformBox(lang(lng_cloud_password_removed)));
}
bool RecoverBox::codeSubmitFail(const RPCError &error) {
@@ -592,7 +592,7 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) {
const QString &err = error.type();
if (err == "PASSWORD_EMPTY") {
emit reloadPassword();
- App::wnd()->showLayer(new InformBox(lang(lng_cloud_password_removed)));
+ Ui::showLayer(new InformBox(lang(lng_cloud_password_removed)));
return true;
} else if (err == "PASSWORD_RECOVERY_NA") {
onClose();
diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp
index 1775207011..d9d933e30b 100644
--- a/Telegram/SourceFiles/boxes/photosendbox.cpp
+++ b/Telegram/SourceFiles/boxes/photosendbox.cpp
@@ -29,31 +29,67 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxWideWidth)
, _file(file)
-, _thumbx(0)
-, _thumby(0)
-, _thumbw(0)
-, _thumbh(0)
-, _namew(0)
-, _textw(0)
+, _animated(false)
, _caption(this, st::confirmCaptionArea, lang(lng_photo_caption))
, _compressedFromSettings(_file->type == PrepareAuto)
, _compressed(this, lang(lng_send_image_compressed), _compressedFromSettings ? cCompressPastedImage() : true)
, _send(this, lang(lng_send_button), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
+, _thumbx(0)
+, _thumby(0)
+, _thumbw(0)
+, _thumbh(0)
+, _statusw(0)
+, _isImage(false)
, _replyTo(_file->to.replyTo)
, _confirmed(false) {
connect(&_send, SIGNAL(clicked()), this, SLOT(onSend()));
connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
+
+ _animated = false;
+ QSize dimensions;
if (_file->photo.type() != mtpc_photoEmpty) {
_file->type = PreparePhoto;
+ } else if (_file->document.type() == mtpc_document) {
+ const MTPDdocument &document(_file->document.c_document());
+ const QVector &attributes(document.vattributes.c_vector().v);
+ for (int32 i = 0, l = attributes.size(); i < l; ++i) {
+ if (attributes.at(i).type() == mtpc_documentAttributeAnimated) {
+ _animated = true;
+ } else if (attributes.at(i).type() == mtpc_documentAttributeImageSize) {
+ dimensions = QSize(attributes.at(i).c_documentAttributeImageSize().vw.v, attributes.at(i).c_documentAttributeImageSize().vh.v);
+ } else if (attributes.at(i).type() == mtpc_documentAttributeVideo) {
+ dimensions = QSize(attributes.at(i).c_documentAttributeVideo().vw.v, attributes.at(i).c_documentAttributeVideo().vh.v);
+ }
+ }
+ if (dimensions.isEmpty()) _animated = false;
}
- if (_file->type == PreparePhoto) {
+ if (_file->type == PreparePhoto || _animated) {
int32 maxW = 0, maxH = 0;
- for (PreparedPhotoThumbs::const_iterator i = _file->photoThumbs.cbegin(), e = _file->photoThumbs.cend(); i != e; ++i) {
- if (i->width() >= maxW && i->height() >= maxH) {
- _thumb = *i;
- maxW = _thumb.width();
- maxH = _thumb.height();
+ if (_animated) {
+ int32 limitW = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right();
+ int32 limitH = st::confirmMaxHeight;
+ maxW = dimensions.width();
+ maxH = dimensions.height();
+ if (maxW * limitH > maxH * limitW) {
+ if (maxW < limitW) {
+ maxH = maxH * limitW / maxW;
+ maxW = limitW;
+ }
+ } else {
+ if (maxH < limitH) {
+ maxW = maxW * limitH / maxH;
+ maxH = limitH;
+ }
+ }
+ _thumb = imagePix(_file->thumb.toImage(), maxW * cIntRetinaFactor(), maxH * cIntRetinaFactor(), true, true, false, maxW, maxH);
+ } else {
+ for (PreparedPhotoThumbs::const_iterator i = _file->photoThumbs.cbegin(), e = _file->photoThumbs.cend(); i != e; ++i) {
+ if (i->width() >= maxW && i->height() >= maxH) {
+ _thumb = *i;
+ maxW = _thumb.width();
+ maxH = _thumb.height();
+ }
}
}
int32 tw = _thumb.width(), th = _thumb.height();
@@ -78,32 +114,28 @@ PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxW
_thumb = QPixmap::fromImage(_thumb.toImage().scaled(_thumbw * cIntRetinaFactor(), _thumbh * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly);
_thumb.setDevicePixelRatio(cRetinaFactor());
} else {
- _compressed.hide();
- if (!_file->thumb.isNull()) {
+ if (_file->thumb.isNull()) {
+ _thumbw = 0;
+ } else {
_thumb = _file->thumb;
int32 tw = _thumb.width(), th = _thumb.height();
- if (_thumb.isNull() || !tw || !th) {
- _thumbw = _thumbx = _thumby = 0;
- } else if (tw > th) {
- _thumbw = (tw * st::mediaThumbSize) / th;
- _thumbx = (_thumbw - st::mediaThumbSize) / 2;
- _thumby = 0;
+ if (tw > th) {
+ _thumbw = (tw * st::msgFileThumbSize) / th;
} else {
- _thumbw = st::mediaThumbSize;
- _thumbx = 0;
- _thumby = ((th * _thumbw) / tw - st::mediaThumbSize) / 2;
+ _thumbw = st::msgFileThumbSize;
}
- }
- if (_thumbw) {
- _thumb = QPixmap::fromImage(_thumb.toImage().scaledToWidth(_thumbw * cIntRetinaFactor(), Qt::SmoothTransformation), Qt::ColorOnly);
- _thumb.setDevicePixelRatio(cRetinaFactor());
+ _thumb = imagePix(_thumb.toImage(), _thumbw * cIntRetinaFactor(), 0, true, false, true, st::msgFileThumbSize, st::msgFileThumbSize);
}
- _name = _file->filename;
- _namew = st::mediaFont->width(_name);
- _size = formatSizeText(_file->filesize);
- _textw = qMax(_namew, st::mediaFont->width(_size));
+ _name.setText(st::semiboldFont, _file->filename, _textNameOptions);
+ _status = formatSizeText(_file->filesize);
+ _statusw = qMax(_name.maxWidth(), st::normalFont->width(_status));
+ _isImage = fileIsImage(_file->filename, _file->filemime);
}
+ if (_file->type != PreparePhoto) {
+ _compressed.hide();
+ }
+
updateBoxSize();
_caption.setMaxLength(MaxPhotoCaption);
_caption.setCtrlEnterSubmit(CtrlEnterSubmitBoth);
@@ -111,20 +143,21 @@ PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxW
connect(&_caption, SIGNAL(resized()), this, SLOT(onCaptionResized()));
connect(&_caption, SIGNAL(submitted(bool)), this, SLOT(onSend(bool)));
connect(&_caption, SIGNAL(cancelled()), this, SLOT(onClose()));
+
prepare();
}
PhotoSendBox::PhotoSendBox(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo) : AbstractBox(st::boxWideWidth)
-, _thumbx(0)
-, _thumby(0)
-, _thumbw(0)
-, _thumbh(0)
-, _namew(0)
-, _textw(0)
, _caption(this, st::confirmCaptionArea, lang(lng_photo_caption))
, _compressed(this, lang(lng_send_image_compressed), true)
, _send(this, lang(lng_send_button), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
+, _thumbx(0)
+, _thumby(0)
+, _thumbw(0)
+, _thumbh(0)
+, _statusw(0)
+, _isImage(false)
, _phone(phone)
, _fname(fname)
, _lname(lname)
@@ -135,10 +168,9 @@ PhotoSendBox::PhotoSendBox(const QString &phone, const QString &fname, const QSt
_compressed.hide();
- _name = lng_full_name(lt_first_name, _fname, lt_last_name, _lname);
- _namew = st::mediaFont->width(_name);
- _size = _phone;
- _textw = qMax(_namew, st::mediaFont->width(_size));
+ _name.setText(st::semiboldFont, lng_full_name(lt_first_name, _fname, lt_last_name, _lname), _textNameOptions);
+ _status = _phone;
+ _statusw = qMax(_name.maxWidth(), st::normalFont->width(_status));
updateBoxSize();
prepare();
@@ -163,10 +195,12 @@ void PhotoSendBox::onCaptionResized() {
}
void PhotoSendBox::updateBoxSize() {
- if (_file && _file->type == PreparePhoto) {
- setMaxHeight(st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + st::boxPhotoCompressedPadding.top() + _compressed.height() + (_compressed.checked() ? (st::boxPhotoCompressedPadding.bottom() + _caption.height()) : 0) + st::boxButtonPadding.top() + _send.height() + st::boxButtonPadding.bottom());
+ if (_file && (_file->type == PreparePhoto || _animated)) {
+ setMaxHeight(st::boxPhotoPadding.top() + _thumbh + st::boxPhotoPadding.bottom() + (_animated ? 0 : (st::boxPhotoCompressedPadding.top() + _compressed.height())) + st::boxPhotoCompressedPadding.bottom() + _caption.height() + st::boxButtonPadding.top() + _send.height() + st::boxButtonPadding.bottom());
+ } else if (_thumbw) {
+ setMaxHeight(st::boxPhotoPadding.top() + st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom() + (_file ? (st::boxPhotoCompressedPadding.bottom() + _caption.height()) : 0) + st::boxPhotoPadding.bottom() + st::boxButtonPadding.top() + _send.height() + st::boxButtonPadding.bottom());
} else {
- setMaxHeight(st::boxPhotoPadding.top() + st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom() + st::boxPhotoPadding.bottom() + st::boxButtonPadding.top() + _send.height() + st::boxButtonPadding.bottom());
+ setMaxHeight(st::boxPhotoPadding.top() + st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom() + (_file ? (st::boxPhotoCompressedPadding.bottom() + _caption.height()) : 0) + st::boxPhotoPadding.bottom() + st::boxButtonPadding.top() + _send.height() + st::boxButtonPadding.bottom());
}
}
@@ -182,7 +216,7 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) {
Painter p(this);
if (paint(p)) return;
- if (_file && _file->type == PreparePhoto) {
+ if (_file && (_file->type == PreparePhoto || _animated)) {
if (_thumbx > st::boxPhotoPadding.left()) {
p.fillRect(st::boxPhotoPadding.left(), st::boxPhotoPadding.top(), _thumbx - st::boxPhotoPadding.left(), _thumbh, st::confirmBg->b);
}
@@ -190,36 +224,66 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) {
p.fillRect(_thumbx + _thumbw, st::boxPhotoPadding.top(), width() - st::boxPhotoPadding.right() - _thumbx - _thumbw, _thumbh, st::confirmBg->b);
}
p.drawPixmap(_thumbx, st::boxPhotoPadding.top(), _thumb);
+ if (_animated) {
+ QRect inner(_thumbx + (_thumbw - st::msgFileSize) / 2, st::boxPhotoPadding.top() + (_thumbh - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
+ p.setPen(Qt::NoPen);
+ p.setBrush(st::msgDateImgBg);
+
+ p.setRenderHint(QPainter::HighQualityAntialiasing);
+ p.drawEllipse(inner);
+ p.setRenderHint(QPainter::HighQualityAntialiasing, false);
+
+ p.drawSpriteCenter(inner, st::msgFileInPlay);
+ }
} else {
- int32 w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(), h = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- int32 twidth = w - tleft - st::mediaPadding.right();
- if (twidth > _textw) {
- w -= (twidth - _textw);
- twidth = _textw;
+ int32 w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right();
+ int32 h = _thumbw ? (st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom()) : (st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom());
+ int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0;
+ if (_thumbw) {
+ nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right();
+ nametop = st::msgFileThumbNameTop;
+ nameright = st::msgFileThumbPadding.left();
+ statustop = st::msgFileThumbStatusTop;
+ linktop = st::msgFileThumbLinkTop;
+ } else {
+ nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right();
+ nametop = st::msgFileNameTop;
+ nameright = st::msgFilePadding.left();
+ statustop = st::msgFileStatusTop;
+ }
+ int32 namewidth = w - nameleft - (_thumbw ? st::msgFileThumbPadding.left() : st::msgFilePadding.left());
+ if (namewidth > _statusw) {
+ w -= (namewidth - _statusw);
+ namewidth = _statusw;
}
int32 x = (width() - w) / 2, y = st::boxPhotoPadding.top();
App::roundRect(p, x, y, w, h, st::msgOutBg, MessageOutCorners, &st::msgOutShadow);
+
if (_thumbw) {
- int32 rf(cIntRetinaFactor());
- p.drawPixmap(QPoint(x + st::mediaPadding.left(), y + st::mediaPadding.top()), _thumb, QRect(_thumbx * rf, _thumby * rf, st::mediaThumbSize * rf, st::mediaThumbSize * rf));
+ QRect rthumb(rtlrect(x + st::msgFileThumbPadding.left(), y + st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width()));
+ p.drawPixmap(rthumb.topLeft(), _thumb);
} else if (_file) {
- p.drawPixmap(QPoint(x + st::mediaPadding.left(), y + st::mediaPadding.top()), App::sprite(), st::mediaDocOutImg);
- } else {
- p.drawPixmap(x + st::mediaPadding.left(), y + st::mediaPadding.top(), userDefPhoto(1)->pix(st::mediaThumbSize));
- }
+ QRect inner(rtlrect(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width()));
+ p.setPen(Qt::NoPen);
+ p.setBrush(st::msgFileOutBg);
- p.setFont(st::mediaFont->f);
- p.setPen(st::black->c);
- if (twidth < _namew) {
- p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, st::mediaFont->elided(_name, twidth));
- } else {
- p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, _name);
- }
+ p.setRenderHint(QPainter::HighQualityAntialiasing);
+ p.drawEllipse(inner);
+ p.setRenderHint(QPainter::HighQualityAntialiasing, false);
- p.setPen(st::mediaOutColor->p);
- p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->descent, _size);
+ p.drawSpriteCenter(inner, _isImage ? st::msgFileOutImage : st::msgFileOutFile);
+ } else {
+ p.drawPixmapLeft(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), width(), userDefPhoto(1)->pixRounded(st::msgFileSize));
+ }
+ p.setFont(st::semiboldFont);
+ p.setPen(st::black);
+ _name.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width());
+
+ style::color status(st::mediaOutFg);
+ p.setFont(st::normalFont);
+ p.setPen(status);
+ p.drawTextLeft(x + nameleft, y + statustop, width(), _status);
}
}
@@ -251,13 +315,11 @@ void PhotoSendBox::hideAll() {
void PhotoSendBox::showAll() {
_send.show();
_cancel.show();
- if (_file && _file->type == PreparePhoto) {
- _compressed.show();
- if (_compressed.checked()) {
- _caption.show();
- } else {
- _caption.hide();
+ if (_file) {
+ if (_file->type == PreparePhoto) {
+ _compressed.show();
}
+ _caption.show();
} else {
_caption.hide();
_compressed.hide();
@@ -287,7 +349,7 @@ void PhotoSendBox::onSend(bool ctrlShiftEnter) {
}
}
if (!_caption.isHidden()) {
- _file->photoCaption = prepareText(_caption.getLastText(), true);
+ _file->caption = prepareText(_caption.getLastText(), true);
}
App::main()->onSendFileConfirm(_file, ctrlShiftEnter);
} else {
diff --git a/Telegram/SourceFiles/boxes/photosendbox.h b/Telegram/SourceFiles/boxes/photosendbox.h
index 597c4496ac..2c955dfd00 100644
--- a/Telegram/SourceFiles/boxes/photosendbox.h
+++ b/Telegram/SourceFiles/boxes/photosendbox.h
@@ -65,16 +65,23 @@ private:
void updateBoxSize();
FileLoadResultPtr _file;
- int32 _thumbx, _thumby, _thumbw, _thumbh;
- QString _name, _size;
- int32 _namew, _textw;
+ bool _animated;
+
+ QPixmap _thumb;
+
InputArea _caption;
bool _compressedFromSettings;
Checkbox _compressed;
BoxButton _send, _cancel;
- QPixmap _thumb;
+
+ int32 _thumbx, _thumby, _thumbw, _thumbh;
+ Text _name;
+ QString _status;
+ int32 _statusw;
+ bool _isImage;
QString _phone, _fname, _lname;
+
MsgId _replyTo;
bool _confirmed;
diff --git a/Telegram/SourceFiles/boxes/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp
index 8d58681ff5..6cc62071db 100644
--- a/Telegram/SourceFiles/boxes/sessionsbox.cpp
+++ b/Telegram/SourceFiles/boxes/sessionsbox.cpp
@@ -115,7 +115,7 @@ void SessionsInner::onTerminate() {
_terminateBox = new ConfirmBox(lang(lng_settings_reset_one_sure), lang(lng_settings_reset_button), st::attentionBoxButton);
connect(_terminateBox, SIGNAL(confirmed()), this, SLOT(onTerminateSure()));
connect(_terminateBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoTerminateBox(QObject*)));
- App::wnd()->replaceLayer(_terminateBox);
+ Ui::showLayer(_terminateBox, KeepOtherLayers);
}
}
}
@@ -138,7 +138,7 @@ void SessionsInner::onTerminateAll() {
_terminateBox = new ConfirmBox(lang(lng_settings_reset_sure), lang(lng_settings_reset_button), st::attentionBoxButton);
connect(_terminateBox, SIGNAL(confirmed()), this, SLOT(onTerminateAllSure()));
connect(_terminateBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoTerminateBox(QObject*)));
- App::wnd()->replaceLayer(_terminateBox);
+ Ui::showLayer(_terminateBox, KeepOtherLayers);
}
void SessionsInner::onTerminateAllSure() {
diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp
index cfa0790d31..4424755959 100644
--- a/Telegram/SourceFiles/boxes/stickersetbox.cpp
+++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp
@@ -43,6 +43,7 @@ _input(set), _installRequest(0) {
void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
_pack.clear();
+ _emoji.clear();
if (set.type() == mtpc_messages_stickerSet) {
const MTPDmessages_stickerSet &d(set.c_messages_stickerSet());
const QVector &v(d.vdocuments.c_vector().v);
@@ -50,9 +51,26 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
for (int32 i = 0, l = v.size(); i < l; ++i) {
DocumentData *doc = App::feedDocument(v.at(i));
if (!doc || !doc->sticker()) continue;
-
+
_pack.push_back(doc);
}
+ const QVector &packs(d.vpacks.c_vector().v);
+ for (int32 i = 0, l = packs.size(); i < l; ++i) {
+ if (packs.at(i).type() != mtpc_stickerPack) continue;
+ const MTPDstickerPack &pack(packs.at(i).c_stickerPack());
+ if (EmojiPtr e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) {
+ const QVector &stickers(pack.vdocuments.c_vector().v);
+ StickerPack p;
+ p.reserve(stickers.size());
+ for (int32 j = 0, c = stickers.size(); j < c; ++j) {
+ DocumentData *doc = App::document(stickers.at(j).v);
+ if (!doc || !doc->sticker()) continue;
+
+ p.push_back(doc);
+ }
+ _emoji.insert(e, p);
+ }
+ }
if (d.vset.type() == mtpc_stickerSet) {
const MTPDstickerSet &s(d.vset.c_stickerSet());
_setTitle = stickerSetTitle(s);
@@ -67,7 +85,7 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
}
if (_pack.isEmpty()) {
- App::wnd()->showLayer(new InformBox(lang(lng_stickers_not_found)));
+ Ui::showLayer(new InformBox(lang(lng_stickers_not_found)));
} else {
int32 rows = _pack.size() / StickerPanPerRow + ((_pack.size() % StickerPanPerRow) ? 1 : 0);
resize(st::stickersPadding.left() + StickerPanPerRow * st::stickersSize.width(), st::stickersPadding.top() + rows * st::stickersSize.height() + st::stickersPadding.bottom());
@@ -82,7 +100,7 @@ bool StickerSetInner::failedSet(const RPCError &error) {
_loaded = true;
- App::wnd()->showLayer(new InformBox(lang(lng_stickers_not_found)));
+ Ui::showLayer(new InformBox(lang(lng_stickers_not_found)));
return true;
}
@@ -91,7 +109,12 @@ void StickerSetInner::installDone(const MTPBool &result) {
StickerSets &sets(cRefStickerSets());
_setFlags &= ~MTPDstickerSet::flag_disabled;
- sets.insert(_setId, StickerSet(_setId, _setAccess, _setTitle, _setShortName, _setCount, _setHash, _setFlags)).value().stickers = _pack;
+ StickerSets::iterator it = sets.find(_setId);
+ if (it == sets.cend()) {
+ it = sets.insert(_setId, StickerSet(_setId, _setAccess, _setTitle, _setShortName, _setCount, _setHash, _setFlags));
+ }
+ it.value().stickers = _pack;
+ it.value().emoji = _emoji;
StickerSetsOrder &order(cRefStickerSetsOrder());
int32 insertAtIndex = 0, currentIndex = order.indexOf(_setId);
@@ -112,16 +135,15 @@ void StickerSetInner::installDone(const MTPBool &result) {
sets.erase(custom);
}
}
- cSetStickersHash(stickersCountHash());
Local::writeStickers();
emit installed(_setId);
- App::wnd()->hideLayer();
+ Ui::hideLayer();
}
bool StickerSetInner::installFailed(const RPCError &error) {
if (mtpIsFlood(error)) return false;
- App::wnd()->showLayer(new InformBox(lang(lng_stickers_not_found)));
+ Ui::showLayer(new InformBox(lang(lng_stickers_not_found)));
return true;
}
@@ -147,15 +169,14 @@ void StickerSetInner::paintEvent(QPaintEvent *e) {
if (goodThumb) {
doc->thumb->load();
} else {
- bool already = !doc->already().isEmpty(), hasdata = !doc->data.isEmpty();
- if (!doc->loader && doc->status != FileFailed && !already && !hasdata) {
- doc->save(QString());
+ if (doc->status == FileReady) {
+ doc->automaticLoad(0);
}
- if (doc->sticker()->img->isNull() && (already || hasdata)) {
- if (already) {
+ if (doc->sticker()->img->isNull() && doc->loaded() && doc->loaded(true)) {
+ if (doc->data().isEmpty()) {
doc->sticker()->img = ImagePtr(doc->already());
} else {
- doc->sticker()->img = ImagePtr(doc->data);
+ doc->sticker()->img = ImagePtr(doc->data());
}
}
}
@@ -252,7 +273,7 @@ void StickerSetBox::onAddStickers() {
void StickerSetBox::onShareStickers() {
QString url = qsl("https://telegram.me/addstickers/") + _inner.shortName();
QApplication::clipboard()->setText(url);
- App::wnd()->showLayer(new InformBox(lang(lng_stickers_copied)));
+ Ui::showLayer(new InformBox(lang(lng_stickers_copied)));
}
void StickerSetBox::onUpdateButtons() {
@@ -333,7 +354,7 @@ StickersInner::StickersInner() : TWidget()
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _aboveShadowFadeStart(0)
, _aboveShadowFadeOpacity(0, 0)
-, _a_shifting(animFunc(this, &StickersInner::animStep_shifting))
+, _a_shifting(animation(this, &StickersInner::step_shifting))
, _itemsTop(st::membersPadding.top())
, _saving(false)
, _removeSel(-1)
@@ -355,7 +376,7 @@ void StickersInner::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
Painter p(this);
- updateAnimatedValues();
+ _a_shifting.step();
p.fillRect(r, st::white);
p.setClipRect(r);
@@ -489,7 +510,7 @@ void StickersInner::onUpdateSelected() {
}
_rows.at(_dragging)->yadd = anim::ivalue(local.y() - _dragStart.y(), local.y() - _dragStart.y());
_animStartTimes[_dragging] = 0;
- updateAnimatedRegions();
+ _a_shifting.step(getms(), true);
emit checkDraggingScroll(local.y());
} else {
@@ -509,7 +530,7 @@ void StickersInner::onUpdateSelected() {
float64 StickersInner::aboveShadowOpacity() const {
if (_above < 0) return 0;
-
+
int32 dx = 0;
int32 dy = qAbs(_above * _rowHeight + _rows.at(_above)->yadd.current() - _started * _rowHeight);
return qMin((dx + dy) * 2. / _rowHeight, 1.);
@@ -538,33 +559,14 @@ void StickersInner::mouseReleaseEvent(QMouseEvent *e) {
}
}
-void StickersInner::updateAnimatedRegions() {
- int32 updateMin = -1, updateMax = 0;
- for (int32 i = 0, l = _animStartTimes.size(); i < l; ++i) {
- if (_animStartTimes.at(i)) {
- if (updateMin < 0) updateMin = i;
- updateMax = i;
- }
- }
- if (_aboveShadowFadeStart) {
- if (updateMin < 0 || updateMin > _above) updateMin = _above;
- if (updateMax < _above) updateMin = _above;
- }
- if (_dragging >= 0) {
- if (updateMin < 0 || updateMin > _dragging) updateMin = _dragging;
- if (updateMax < _dragging) updateMax = _dragging;
- }
- if (updateMin >= 0) {
- update(0, _itemsTop + _rowHeight * (updateMin - 1), width(), _rowHeight * (updateMax - updateMin + 3));
- }
-}
-
-bool StickersInner::updateAnimatedValues() {
+void StickersInner::step_shifting(uint64 ms, bool timer) {
bool animating = false;
- uint64 ms = getms();
+ int32 updateMin = -1, updateMax = 0;
for (int32 i = 0, l = _animStartTimes.size(); i < l; ++i) {
uint64 start = _animStartTimes.at(i);
if (start) {
+ if (updateMin < 0) updateMin = i;
+ updateMax = i;
if (start + st::stickersRowDuration > ms && ms >= start) {
_rows.at(i)->yadd.update((ms - start) / st::stickersRowDuration, anim::sineInOut);
animating = true;
@@ -575,6 +577,8 @@ bool StickersInner::updateAnimatedValues() {
}
}
if (_aboveShadowFadeStart) {
+ if (updateMin < 0 || updateMin > _above) updateMin = _above;
+ if (updateMax < _above) updateMin = _above;
if (_aboveShadowFadeStart + st::stickersRowDuration > ms && ms > _aboveShadowFadeStart) {
_aboveShadowFadeOpacity.update((ms - _aboveShadowFadeStart) / st::stickersRowDuration, anim::sineInOut);
animating = true;
@@ -583,17 +587,19 @@ bool StickersInner::updateAnimatedValues() {
_aboveShadowFadeStart = 0;
}
}
- return animating;
-}
-
-bool StickersInner::animStep_shifting(float64) {
- updateAnimatedRegions();
-
- bool animating = updateAnimatedValues();
+ if (timer) {
+ if (_dragging >= 0) {
+ if (updateMin < 0 || updateMin > _dragging) updateMin = _dragging;
+ if (updateMax < _dragging) updateMax = _dragging;
+ }
+ if (updateMin >= 0) {
+ update(0, _itemsTop + _rowHeight * (updateMin - 1), width(), _rowHeight * (updateMax - updateMin + 3));
+ }
+ }
if (!animating) {
_above = _dragging;
+ _a_shifting.stop();
}
- return animating;
}
void StickersInner::clear() {
@@ -630,7 +636,7 @@ void StickersInner::rebuild() {
clear();
const StickerSetsOrder &order(cStickerSetsOrder());
_animStartTimes.reserve(order.size());
-
+
const StickerSets &sets(cStickerSets());
for (int32 i = 0, l = order.size(); i < l; ++i) {
StickerSets::const_iterator it = sets.constFind(order.at(i));
@@ -714,13 +720,14 @@ StickersBox::StickersBox() : ItemListBox(st::boxScroll)
, _topShadow(this, st::contactsAboutShadow)
, _bottomShadow(this)
, _scrollDelta(0)
-, _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPhotoSize - st::contactsPadding.left() - st::contactsPadding.right())
+, _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.left())
, _about(st::boxTextFont, lang(lng_stickers_reorder), _defaultOptions, _aboutWidth)
, _aboutHeight(st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom()) {
ItemListBox::init(&_inner, st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom(), st::boxTitleHeight + _aboutHeight);
setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight)));
connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated()));
+ App::main()->updateStickers();
connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_save, SIGNAL(clicked()), this, SLOT(onSave()));
@@ -793,7 +800,7 @@ void StickersBox::paintEvent(QPaintEvent *e) {
p.fillRect(0, 0, width(), _aboutHeight, st::contactsAboutBg);
p.setPen(st::stickersReorderFg);
- _about.drawLeft(p, st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(), st::stickersReorderPadding.top(), _aboutWidth, width());
+ _about.draw(p, st::contactsPadding.left(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center);
}
void StickersBox::closePressed() {
@@ -908,7 +915,6 @@ void StickersBox::onSave() {
}
}
- cSetStickersHash(stickersCountHash());
Local::writeStickers();
if (writeRecent) Local::writeUserSettings();
emit App::main()->stickersUpdated();
diff --git a/Telegram/SourceFiles/boxes/stickersetbox.h b/Telegram/SourceFiles/boxes/stickersetbox.h
index b896c8af4b..7fbd2b9720 100644
--- a/Telegram/SourceFiles/boxes/stickersetbox.h
+++ b/Telegram/SourceFiles/boxes/stickersetbox.h
@@ -32,7 +32,7 @@ public:
void init();
void paintEvent(QPaintEvent *e);
-
+
bool loaded() const;
int32 notInstalled() const;
bool official() const;
@@ -60,6 +60,7 @@ private:
bool installFailed(const RPCError &error);
StickerPack _pack;
+ StickersByEmojiMap _emoji;
bool _loaded;
uint64 _setId, _setAccess;
QString _title, _setTitle, _setShortName;
@@ -118,7 +119,7 @@ public:
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
-
+
void rebuild();
bool savingStart() {
if (_saving) return false;
@@ -144,13 +145,11 @@ public slots:
private:
- bool animStep_shifting(float64 ms);
+ void step_shifting(uint64 ms, bool timer);
void paintRow(Painter &p, int32 index);
void clear();
void setRemoveSel(int32 removeSel);
float64 aboveShadowOpacity() const;
- void updateAnimatedRegions();
- bool updateAnimatedValues();
int32 _rowHeight;
struct StickerSetRow {
@@ -203,7 +202,7 @@ public:
StickersBox();
void resizeEvent(QResizeEvent *e);
void paintEvent(QPaintEvent *e);
-
+
void closePressed();
public slots:
diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h
index 8a54574136..4a1637ba04 100644
--- a/Telegram/SourceFiles/config.h
+++ b/Telegram/SourceFiles/config.h
@@ -20,10 +20,10 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
*/
#pragma once
-static const int32 AppVersion = 9015;
-static const wchar_t *AppVersionStr = L"0.9.15";
-static const bool DevVersion = false;
-//#define BETA_VERSION (9014003ULL) // just comment this line to build public version
+static const int32 AppVersion = 9019;
+static const wchar_t *AppVersionStr = L"0.9.19";
+static const bool DevVersion = true;
+//#define BETA_VERSION (9015008ULL) // just comment this line to build public version
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
static const wchar_t *AppName = L"Telegram Desktop";
@@ -83,6 +83,13 @@ enum {
LocalEncryptKeySize = 256, // 2048 bit
AnimationTimerDelta = 7,
+ ClipThreadsCount = 8,
+ AverageGifSize = 320 * 240,
+ WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it
+ InlineBotRequestDelay = 400, // wait 400ms before context bot realtime request
+ RecentInlineBotsLimit = 10,
+
+ AVBlockSize = 4096, // 4Kb for ffmpeg blocksize
SaveRecentEmojisTimeout = 3000, // 3 secs
SaveWindowPositionTimeout = 1000, // 1 sec
@@ -108,12 +115,14 @@ enum {
AudioVoiceMsgUpdateView = 100, // 100ms
AudioVoiceMsgChannels = 2, // stereo
AudioVoiceMsgBufferSize = 1024 * 1024, // 1 Mb buffers
- AudioVoiceMsgInMemory = 1024 * 1024, // 1 Mb audio is hold in memory and auto loaded
+ AudioVoiceMsgInMemory = 2 * 1024 * 1024, // 2 Mb audio is hold in memory and auto loaded
AudioPauseDeviceTimeout = 3000, // pause in 3 secs after playing is over
- StickerInMemory = 1024 * 1024, // 1024 Kb stickers hold in memory, auto loaded and displayed inline
+ StickerInMemory = 2 * 1024 * 1024, // 2 Mb stickers hold in memory, auto loaded and displayed inline
StickerMaxSize = 2048, // 2048x2048 is a max image size for sticker
+ AnimationInMemory = 10 * 1024 * 1024, // 10 Mb gif and mp4 animations held in memory while playing
+
MediaViewImageSizeLimit = 100 * 1024 * 1024, // show up to 100mb jpg/png/gif docs in app
MaxZoomLevel = 7, // x8
ZoomToScreenLevel = 1024, // just constant
@@ -123,6 +132,7 @@ enum {
EmojiPanRowsPerPage = 6,
StickerPanPerRow = 5,
StickerPanRowsPerPage = 4,
+ SavedGifsMaxPerRow = 4,
StickersUpdateTimeout = 3600000, // update not more than once in an hour
SearchPeopleLimit = 5,
@@ -335,6 +345,7 @@ enum {
MaxUploadDocumentSize = 1500 * 1024 * 1024, // 1500mb documents max
UseBigFilesFrom = 10 * 1024 * 1024, // mtp big files methods used for files greater than 10mb
MaxFileQueries = 16, // max 16 file parts downloaded at the same time
+ MaxWebFileQueries = 8, // max 8 http[s] files downloaded at the same time
UploadPartSize = 32 * 1024, // 32kb for photo
DocumentMaxPartsCount = 3000, // no more than 3000 parts
diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp
index 56d96241d1..ef5247c88e 100644
--- a/Telegram/SourceFiles/dialogswidget.cpp
+++ b/Telegram/SourceFiles/dialogswidget.cpp
@@ -163,7 +163,7 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO
PeerData *act = App::main()->activePeer();
MsgId actId = App::main()->activeMsgId();
for (; from < to; ++from) {
- bool active = ((_filterResults[from]->history->peer == act) || (_filterResults[from]->history->peer->migrateTo() && _filterResults[from]->history->peer->migrateTo() == act)) && !actId;
+ bool active = ((_filterResults[from]->history->peer == act) || (_filterResults[from]->history->peer->migrateTo() && _filterResults[from]->history->peer->migrateTo() == act)) && !actId;
bool selected = (from == _filteredSel) || (_filterResults[from]->history->peer == _menuPeer);
_filterResults[from]->paint(p, w, active, selected, paintingOther);
p.translate(0, st::dlgHeight);
@@ -488,16 +488,6 @@ void DialogsInner::removeDialog(History *history) {
refresh();
}
-void DialogsInner::removeContact(UserData *user) {
- if (sel && sel->history->peer == user) {
- sel = 0;
- }
- contactsNoDialogs.del(user);
- contacts.del(user);
-
- refresh();
-}
-
void DialogsInner::dlgUpdated(DialogRow *row) {
if (_state == DefaultState) {
update(0, row->pos * st::dlgHeight, fullWidth(), st::dlgHeight);
@@ -675,12 +665,12 @@ void DialogsInner::onContextClearHistory() {
_menuActionPeer = _menuPeer;
ConfirmBox *box = new ConfirmBox(_menuPeer->isUser() ? lng_sure_delete_history(lt_contact, _menuPeer->name) : lng_sure_delete_group_history(lt_group, _menuPeer->name), lang(lng_box_delete), st::attentionBoxButton);
connect(box, SIGNAL(confirmed()), this, SLOT(onContextClearHistorySure()));
- App::showLayer(box);
+ Ui::showLayer(box);
}
void DialogsInner::onContextClearHistorySure() {
if (!_menuActionPeer || _menuActionPeer->isChannel()) return;
- App::wnd()->hideLayer();
+ Ui::hideLayer();
App::main()->clearHistory(_menuActionPeer);
}
@@ -690,14 +680,14 @@ void DialogsInner::onContextDeleteAndLeave() {
_menuActionPeer = _menuPeer;
ConfirmBox *box = new ConfirmBox(_menuPeer->isUser() ? lng_sure_delete_history(lt_contact, _menuPeer->name) : (_menuPeer->isChat() ? lng_sure_delete_and_exit(lt_group, _menuPeer->name) : lang(_menuPeer->isMegagroup() ? lng_sure_leave_group : lng_sure_leave_channel)), lang(_menuPeer->isUser() ? lng_box_delete : lng_box_leave), _menuPeer->isChannel() ? st::defaultBoxButton : st::attentionBoxButton);
connect(box, SIGNAL(confirmed()), this, SLOT(onContextDeleteAndLeaveSure()));
- App::wnd()->showLayer(box);
+ Ui::showLayer(box);
}
void DialogsInner::onContextDeleteAndLeaveSure() {
if (!_menuActionPeer) return;
- App::wnd()->hideLayer();
- App::main()->showDialogs();
+ Ui::hideLayer();
+ Ui::showChatsList();
if (_menuActionPeer->isUser()) {
App::main()->deleteConversation(_menuActionPeer);
} else if (_menuActionPeer->isChat()) {
@@ -882,7 +872,7 @@ void DialogsInner::onHashtagFilterUpdate(QStringRef newFilter) {
}
_hashtagFilter = newFilter.toString();
if (cRecentSearchHashtags().isEmpty() && cRecentWriteHashtags().isEmpty()) {
- Local::readRecentHashtags();
+ Local::readRecentHashtagsAndBots();
}
const RecentHashtagPack &recent(cRecentSearchHashtags());
_hashtagResults.clear();
@@ -917,14 +907,6 @@ void DialogsInner::clearSearchResults(bool clearPeople) {
_lastSearchId = _lastSearchMigratedId = 0;
}
-void DialogsInner::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
- for (int i = 0; i < _searchResults.size(); ++i) {
- if (_searchResults[i]->_item == oldItem) {
- _searchResults[i]->_item = newItem;
- }
- }
-}
-
void DialogsInner::updateNotifySettings(PeerData *peer) {
if (_menu && _menuPeer == peer && _menu->actions().size() > 1) {
_menu->actions().at(1)->setText(lang(menuPeerMuted() ? lng_enable_notifications_from_tray : lng_disable_notifications_from_tray));
@@ -1096,9 +1078,11 @@ void DialogsInner::peopleReceived(const QString &query, const QVector &
void DialogsInner::contactsReceived(const QVector &contacts) {
for (QVector::const_iterator i = contacts.cbegin(), e = contacts.cend(); i != e; ++i) {
int32 uid = i->c_contact().vuser_id.v;
- addNewContact(uid);
if (uid == MTP::authedId() && App::self()) {
- App::self()->contact = 1;
+ if (App::self()->contact < 1) {
+ App::self()->contact = 1;
+ Notify::userIsContactChanged(App::self());
+ }
}
}
if (!sel && contactsNoDialogs.list.count && false) {
@@ -1108,23 +1092,25 @@ void DialogsInner::contactsReceived(const QVector &contacts) {
refresh();
}
-int32 DialogsInner::addNewContact(int32 uid, bool select) { // -2 - err, -1 - don't scroll, >= 0 - scroll
- PeerId peer = peerFromUser(uid);
- if (!App::peerLoaded(peer)) return -2;
-
- History *history = App::history(peer);
- contacts.addByName(history);
- DialogsList::RowByPeer::const_iterator i = dialogs.list.rowByPeer.constFind(peer);
- if (i == dialogs.list.rowByPeer.cend()) {
- DialogRow *added = contactsNoDialogs.addByName(history);
- if (!added) return -2;
- return -1;
+void DialogsInner::notify_userIsContactChanged(UserData *user, bool fromThisApp) {
+ if (user->contact > 0) {
+ History *history = App::history(user->id);
+ contacts.addByName(history);
+ DialogsList::RowByPeer::const_iterator i = dialogs.list.rowByPeer.constFind(user->id);
+ if (i == dialogs.list.rowByPeer.cend()) {
+ contactsNoDialogs.addByName(history);
+ } else if (fromThisApp) {
+ sel = i.value();
+ contactSel = false;
+ }
+ } else {
+ if (sel && sel->history->peer == user) {
+ sel = 0;
+ }
+ contactsNoDialogs.del(user);
+ contacts.del(user);
}
- if (select) {
- sel = i.value();
- contactSel = false;
- }
- return i.value()->pos * st::dlgHeight;
+ refresh();
}
void DialogsInner::refresh(bool toTop) {
@@ -1399,7 +1385,7 @@ void DialogsInner::loadPeerPhotos(int32 yFrom) {
if (from < _filterResults.size()) {
int32 to = (yTo / int32(st::dlgHeight)) + 1, w = width();
if (to > _filterResults.size()) to = _filterResults.size();
-
+
for (; from < to; ++from) {
_filterResults[from]->history->peer->photo->load();
}
@@ -1448,7 +1434,7 @@ bool DialogsInner::choosePeer() {
}
}
cSetRecentSearchHashtags(recent);
- Local::writeRecentHashtags();
+ Local::writeRecentHashtagsAndBots();
emit refreshHashtags();
selByMouse = true;
@@ -1501,7 +1487,7 @@ void DialogsInner::saveRecentHashtags(const QString &text) {
}
}
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
- Local::readRecentHashtags();
+ Local::readRecentHashtagsAndBots();
recent = cRecentSearchHashtags();
}
found = true;
@@ -1509,7 +1495,7 @@ void DialogsInner::saveRecentHashtags(const QString &text) {
}
if (found) {
cSetRecentSearchHashtags(recent);
- Local::writeRecentHashtags();
+ Local::writeRecentHashtagsAndBots();
}
}
@@ -1737,7 +1723,7 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : TWidget(parent)
, _cancelSearch(this, st::btnCancelSearch)
, _scroll(this, st::dlgScroll)
, _inner(&_scroll, parent)
-, _a_show(animFunc(this, &DialogsWidget::animStep_show))
+, _a_show(animation(this, &DialogsWidget::step_show))
, _searchInPeer(0)
, _searchInMigrated(0)
, _searchFull(false)
@@ -1839,13 +1825,11 @@ void DialogsWidget::animShow(const QPixmap &bgAnimCache) {
show();
}
-bool DialogsWidget::animStep_show(float64 ms) {
+void DialogsWidget::step_show(float64 ms, bool timer) {
float64 dt = ms / st::slideDuration;
- bool res = true;
if (dt >= 1) {
_a_show.stop();
- res = false;
a_coordUnder.finish();
a_coordOver.finish();
a_shadow.finish();
@@ -1865,8 +1849,7 @@ bool DialogsWidget::animStep_show(float64 ms) {
a_coordOver.update(dt, st::slideFunction);
a_shadow.update(dt, st::slideFunction);
}
- update();
- return res;
+ if (timer) update();
}
void DialogsWidget::onCancel() {
@@ -1879,14 +1862,19 @@ void DialogsWidget::itemRemoved(HistoryItem *item) {
_inner.itemRemoved(item);
}
-void DialogsWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
- _inner.itemReplaced(oldItem, newItem);
-}
-
void DialogsWidget::updateNotifySettings(PeerData *peer) {
_inner.updateNotifySettings(peer);
}
+void DialogsWidget::notify_userIsContactChanged(UserData *user, bool fromThisApp) {
+ if (fromThisApp) {
+ _filter.setText(QString());
+ _filter.updatePlaceholder();
+ onFilterUpdate();
+ }
+ _inner.notify_userIsContactChanged(user, fromThisApp);
+}
+
void DialogsWidget::unreadCountsReceived(const QVector &dialogs) {
for (QVector::const_iterator i = dialogs.cbegin(), e = dialogs.cend(); i != e; ++i) {
switch (i->type()) {
@@ -2273,17 +2261,6 @@ bool DialogsWidget::peopleFailed(const RPCError &error, mtpRequestId req) {
return true;
}
-bool DialogsWidget::addNewContact(int32 uid, bool show) {
- _filter.setText(QString());
- _filter.updatePlaceholder();
- onFilterUpdate();
- int32 to = _inner.addNewContact(uid, true);
- if (to < -1 || !show) return false;
- _inner.refresh();
- if (to >= 0) _scroll.scrollToY(to);
- return true;
-}
-
void DialogsWidget::dragEnterEvent(QDragEnterEvent *e) {
if (App::main()->selectingPeer()) return;
@@ -2540,13 +2517,6 @@ void DialogsWidget::removeDialog(History *history) {
onFilterUpdate();
}
-void DialogsWidget::removeContact(UserData *user) {
- _filter.setText(QString());
- _filter.updatePlaceholder();
- onFilterUpdate();
- _inner.removeContact(user);
-}
-
DialogsIndexed &DialogsWidget::contactsList() {
return _inner.contactsList();
}
@@ -2556,11 +2526,11 @@ DialogsIndexed &DialogsWidget::dialogsList() {
}
void DialogsWidget::onAddContact() {
- App::wnd()->replaceLayer(new AddContactBox());
+ Ui::showLayer(new AddContactBox(), KeepOtherLayers);
}
void DialogsWidget::onNewGroup() {
- App::wnd()->showLayer(new NewGroupBox());
+ Ui::showLayer(new NewGroupBox());
}
bool DialogsWidget::onCancelSearch() {
@@ -2571,7 +2541,7 @@ bool DialogsWidget::onCancelSearch() {
}
if (_searchInPeer && !clearing) {
if (!cWideMode()) {
- App::main()->showPeerHistory(_searchInPeer->id, ShowAtUnreadMsgId);
+ Ui::showPeerHistory(_searchInPeer, ShowAtUnreadMsgId);
}
_searchInPeer = _searchInMigrated = 0;
_inner.searchInPeer(0);
@@ -2591,7 +2561,7 @@ void DialogsWidget::onCancelSearchInPeer() {
}
if (_searchInPeer) {
if (!cWideMode() && !App::main()->selectingPeer()) {
- App::main()->showPeerHistory(_searchInPeer->id, ShowAtUnreadMsgId);
+ Ui::showPeerHistory(_searchInPeer, ShowAtUnreadMsgId);
}
_searchInPeer = _searchInMigrated = 0;
_inner.searchInPeer(0);
diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h
index 602774d231..60cb877b93 100644
--- a/Telegram/SourceFiles/dialogswidget.h
+++ b/Telegram/SourceFiles/dialogswidget.h
@@ -48,7 +48,6 @@ public:
void activate();
void contactsReceived(const QVector &contacts);
- int32 addNewContact(int32 uid, bool sel = false); // -2 - err, -1 - don't scroll, >= 0 - scroll
int32 filteredOffset() const;
int32 peopleOffset() const;
@@ -72,7 +71,6 @@ public:
void dlgUpdated(DialogRow *row);
void dlgUpdated(History *row, MsgId msgId);
void removeDialog(History *history);
- void removeContact(UserData *user);
void loadPeerPhotos(int32 yFrom);
void clearFilter();
@@ -117,12 +115,13 @@ public:
void onFilterUpdate(QString newFilter, bool force = false);
void onHashtagFilterUpdate(QStringRef newFilter);
void itemRemoved(HistoryItem *item);
- void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
PeerData *updateFromParentDrag(QPoint globalPos);
void updateNotifySettings(PeerData *peer);
+ void notify_userIsContactChanged(UserData *user, bool fromThisApp);
+
~DialogsInner();
public slots:
@@ -220,7 +219,6 @@ public:
void contactsReceived(const MTPcontacts_Contacts &contacts);
void searchReceived(DialogsSearchRequestType type, const MTPmessages_Messages &result, mtpRequestId req);
void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req);
- bool addNewContact(int32 uid, bool show = true);
void dragEnterEvent(QDragEnterEvent *e);
void dragMoveEvent(QDragMoveEvent *e);
@@ -242,7 +240,7 @@ public:
void dialogsToUp();
void animShow(const QPixmap &bgAnimCache);
- bool animStep_show(float64 ms);
+ void step_show(float64 ms, bool timer);
void destroyData();
@@ -251,7 +249,6 @@ public:
void scrollToPeer(const PeerId &peer, MsgId msgId);
void removeDialog(History *history);
- void removeContact(UserData *user);
DialogsIndexed &contactsList();
DialogsIndexed &dialogsList();
@@ -260,10 +257,11 @@ public:
void onSearchMore();
void itemRemoved(HistoryItem *item);
- void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void updateNotifySettings(PeerData *peer);
+ void notify_userIsContactChanged(UserData *user, bool fromThisApp);
+
signals:
void cancelled();
diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp
index 20071440c3..2ee10cd55f 100644
--- a/Telegram/SourceFiles/dropdown.cpp
+++ b/Telegram/SourceFiles/dropdown.cpp
@@ -28,12 +28,20 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "window.h"
#include "apiwrap.h"
+#include "mainwidget.h"
#include "boxes/confirmbox.h"
#include "boxes/stickersetbox.h"
-Dropdown::Dropdown(QWidget *parent, const style::dropdown &st) : TWidget(parent),
-_ignore(false), _selected(-1), _st(st), _width(_st.width), _hiding(false), a_opacity(0), _shadow(_st.shadow) {
+Dropdown::Dropdown(QWidget *parent, const style::dropdown &st) : TWidget(parent)
+, _ignore(false)
+, _selected(-1)
+, _st(st)
+, _width(_st.width)
+, _hiding(false)
+, a_opacity(0)
+, _a_appearance(animation(this, &Dropdown::step_appearance))
+, _shadow(_st.shadow) {
resetButtons();
_hideTimer.setSingleShot(true);
@@ -118,7 +126,7 @@ void Dropdown::resizeEvent(QResizeEvent *e) {
void Dropdown::paintEvent(QPaintEvent *e) {
QPainter p(this);
- if (animating()) {
+ if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
}
@@ -151,7 +159,7 @@ void Dropdown::enterEvent(QEvent *e) {
}
void Dropdown::leaveEvent(QEvent *e) {
- if (animating()) {
+ if (_a_appearance.animating()) {
hideStart();
} else {
_hideTimer.start(300);
@@ -231,7 +239,7 @@ void Dropdown::otherEnter() {
}
void Dropdown::otherLeave() {
- if (animating()) {
+ if (_a_appearance.animating()) {
hideStart();
} else {
_hideTimer.start(0);
@@ -239,8 +247,8 @@ void Dropdown::otherLeave() {
}
void Dropdown::fastHide() {
- if (animating()) {
- anim::stop(this);
+ if (_a_appearance.animating()) {
+ _a_appearance.stop();
}
a_opacity = anim::fvalue(0, 0);
_hideTimer.stop();
@@ -256,7 +264,7 @@ void Dropdown::adjustButtons() {
void Dropdown::hideStart() {
_hiding = true;
a_opacity.start(0);
- anim::start(this);
+ _a_appearance.start();
}
void Dropdown::hideFinish() {
@@ -276,24 +284,22 @@ void Dropdown::showStart() {
_hiding = false;
show();
a_opacity.start(1);
- anim::start(this);
+ _a_appearance.start();
}
-bool Dropdown::animStep(float64 ms) {
+void Dropdown::step_appearance(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
- bool res = true;
if (dt >= 1) {
+ _a_appearance.stop();
a_opacity.finish();
if (_hiding) {
hideFinish();
}
- res = false;
} else {
a_opacity.update(dt, anim::linear);
}
adjustButtons();
- update();
- return res;
+ if (timer) update();
}
bool Dropdown::eventFilter(QObject *obj, QEvent *e) {
@@ -311,8 +317,13 @@ bool Dropdown::eventFilter(QObject *obj, QEvent *e) {
return false;
}
-DragArea::DragArea(QWidget *parent) : TWidget(parent),
-_hiding(false), _in(false), a_opacity(0), a_color(st::dragColor->c), _shadow(st::boxShadow) {
+DragArea::DragArea(QWidget *parent) : TWidget(parent)
+, _hiding(false)
+, _in(false)
+, a_opacity(0)
+, a_color(st::dragColor->c)
+, _a_appearance(animation(this, &DragArea::step_appearance))
+, _shadow(st::boxShadow) {
setMouseTracking(true);
setAcceptDrops(true);
}
@@ -325,7 +336,7 @@ void DragArea::mouseMoveEvent(QMouseEvent *e) {
_in = newIn;
a_opacity.start(1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
- anim::start(this);
+ _a_appearance.start();
}
}
@@ -336,7 +347,7 @@ void DragArea::dragMoveEvent(QDragMoveEvent *e) {
_in = newIn;
a_opacity.start(1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
- anim::start(this);
+ _a_appearance.start();
}
e->setDropAction(_in ? Qt::CopyAction : Qt::IgnoreAction);
e->accept();
@@ -351,7 +362,7 @@ void DragArea::setText(const QString &text, const QString &subtext) {
void DragArea::paintEvent(QPaintEvent *e) {
QPainter p(this);
- if (animating()) {
+ if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
}
@@ -382,7 +393,7 @@ void DragArea::dragLeaveEvent(QDragLeaveEvent *e) {
_in = false;
a_opacity.start(_hiding ? 0 : 1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
- anim::start(this);
+ _a_appearance.start();
}
void DragArea::dropEvent(QDropEvent *e) {
@@ -401,8 +412,8 @@ void DragArea::otherLeave() {
}
void DragArea::fastHide() {
- if (animating()) {
- anim::stop(this);
+ if (_a_appearance.animating()) {
+ _a_appearance.stop();
}
a_opacity = anim::fvalue(0, 0);
hide();
@@ -413,7 +424,7 @@ void DragArea::hideStart() {
_in = false;
a_opacity.start(0);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
- anim::start(this);
+ _a_appearance.start();
}
void DragArea::hideFinish() {
@@ -427,29 +438,34 @@ void DragArea::showStart() {
show();
a_opacity.start(1);
a_color.start((_in ? st::dragDropColor : st::dragColor)->c);
- anim::start(this);
+ _a_appearance.start();
}
-bool DragArea::animStep(float64 ms) {
+void DragArea::step_appearance(float64 ms, bool timer) {
float64 dt = ms / st::dropdownDef.duration;
- bool res = true;
if (dt >= 1) {
a_opacity.finish();
a_color.finish();
if (_hiding) {
hideFinish();
}
- res = false;
+ _a_appearance.stop();
} else {
a_opacity.update(dt, anim::linear);
a_color.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
-EmojiColorPicker::EmojiColorPicker() :
-_ignoreShow(false), _selected(-1), _pressedSel(-1), _hiding(false), a_opacity(0), _shadow(st::dropdownDef.shadow) {
+EmojiColorPicker::EmojiColorPicker() : TWidget()
+, _ignoreShow(false)
+, _a_selected(animation(this, &EmojiColorPicker::step_selected))
+, _selected(-1)
+, _pressedSel(-1)
+, _hiding(false)
+, a_opacity(0)
+, _a_appearance(animation(this, &EmojiColorPicker::step_appearance))
+, _shadow(st::dropdownDef.shadow) {
memset(_variants, 0, sizeof(_variants));
memset(_hovers, 0, sizeof(_hovers));
@@ -547,51 +563,52 @@ void EmojiColorPicker::mouseMoveEvent(QMouseEvent *e) {
updateSelected();
}
-bool EmojiColorPicker::animStep(float64 ms) {
- bool res1 = false, res2 = false;
- if (!_cache.isNull()) {
- float64 dt = ms / st::dropdownDef.duration;
- if (dt >= 1) {
- a_opacity.finish();
- _cache = QPixmap();
- if (_hiding) {
- hide();
- emit hidden();
- } else {
- _lastMousePos = QCursor::pos();
- updateSelected();
- }
+void EmojiColorPicker::step_appearance(float64 ms, bool timer) {
+ if (_cache.isNull()) {
+ _a_appearance.stop();
+ return;
+ }
+ float64 dt = ms / st::dropdownDef.duration;
+ if (dt >= 1) {
+ a_opacity.finish();
+ _cache = QPixmap();
+ if (_hiding) {
+ hide();
+ emit hidden();
} else {
- a_opacity.update(dt, anim::linear);
- res1 = true;
+ _lastMousePos = QCursor::pos();
+ updateSelected();
}
- update();
+ _a_appearance.stop();
+ } else {
+ a_opacity.update(dt, anim::linear);
}
- if (!_emojiAnimations.isEmpty()) {
- uint64 now = getms();
- QRegion toUpdate;
- for (EmojiAnimations::iterator i = _emojiAnimations.begin(); i != _emojiAnimations.end();) {
- int index = qAbs(i.key()) - 1;
- float64 dt = float64(now - i.value()) / st::emojiPanDuration;
- if (dt >= 1) {
- _hovers[index] = (i.key() > 0) ? 1 : 0;
- i = _emojiAnimations.erase(i);
- } else {
- _hovers[index] = (i.key() > 0) ? dt : (1 - dt);
- ++i;
- }
- toUpdate += QRect(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding, st::emojiPanSize.width(), st::emojiPanSize.height());
+ if (timer) update();
+}
+
+void EmojiColorPicker::step_selected(uint64 ms, bool timer) {
+ QRegion toUpdate;
+ for (EmojiAnimations::iterator i = _emojiAnimations.begin(); i != _emojiAnimations.end();) {
+ int index = qAbs(i.key()) - 1;
+ float64 dt = float64(ms - i.value()) / st::emojiPanDuration;
+ if (dt >= 1) {
+ _hovers[index] = (i.key() > 0) ? 1 : 0;
+ i = _emojiAnimations.erase(i);
+ } else {
+ _hovers[index] = (i.key() > 0) ? dt : (1 - dt);
+ ++i;
}
- res2 = !_emojiAnimations.isEmpty();
- rtlupdate(toUpdate.boundingRect());
+ toUpdate += QRect(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + index * st::emojiPanSize.width() + (index ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding, st::emojiPanSize.width(), st::emojiPanSize.height());
}
- return res1 || res2;
+ if (timer) rtlupdate(toUpdate.boundingRect());
+ if (_emojiAnimations.isEmpty()) _a_selected.stop();
}
void EmojiColorPicker::hideStart(bool fast) {
if (fast) {
clearSelection(true);
- if (animating()) anim::stop(this);
+ if (_a_appearance.animating()) _a_appearance.stop();
+ if (_a_selected.animating()) _a_selected.stop();
a_opacity = anim::fvalue(0);
_cache = QPixmap();
hide();
@@ -604,7 +621,7 @@ void EmojiColorPicker::hideStart(bool fast) {
}
_hiding = true;
a_opacity.start(0);
- anim::start(this);
+ _a_appearance.start();
}
}
@@ -613,8 +630,8 @@ void EmojiColorPicker::showStart() {
_hiding = false;
if (!isHidden() && a_opacity.current() == 1) {
- if (animating()) {
- anim::stop(this);
+ if (_a_appearance.animating()) {
+ _a_appearance.stop();
_cache = QPixmap();
}
return;
@@ -626,7 +643,7 @@ void EmojiColorPicker::showStart() {
}
show();
a_opacity.start(1);
- anim::start(this);
+ _a_appearance.start();
}
void EmojiColorPicker::clearSelection(bool fast) {
@@ -676,7 +693,7 @@ void EmojiColorPicker::updateSelected() {
}
setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default);
}
- if (startanim && !animating()) anim::start(this);
+ if (startanim && !_a_selected.animating()) _a_selected.start();
}
void EmojiColorPicker::drawVariant(Painter &p, int variant) {
@@ -694,8 +711,13 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) {
p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_variants[variant]->x * esize, _variants[variant]->y * esize, esize, esize));
}
-EmojiPanInner::EmojiPanInner() : _maxHeight(int(st::emojiPanMaxHeight)),
-_top(0), _selected(-1), _pressedSel(-1), _pickerSel(-1) {
+EmojiPanInner::EmojiPanInner() : TWidget()
+, _maxHeight(int(st::emojiPanMaxHeight))
+, _a_selected(animation(this, &EmojiPanInner::step_selected))
+, _top(0)
+, _selected(-1)
+, _pressedSel(-1)
+, _pickerSel(-1) {
resize(st::emojiPanWidth - st::emojiScroll.width, countHeight());
setMouseTracking(true);
@@ -711,9 +733,6 @@ _top(0), _selected(-1), _pressedSel(-1), _pickerSel(-1) {
_hovers[i] = QVector(_counts[i], 0);
}
- _saveConfigTimer.setSingleShot(true);
- connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig()));
-
_showPickerTimer.setSingleShot(true);
connect(&_showPickerTimer, SIGNAL(timeout()), this, SLOT(onShowPicker()));
connect(&_picker, SIGNAL(emojiSelected(EmojiPtr)), this, SLOT(onColorSelected(EmojiPtr)));
@@ -916,15 +935,11 @@ void EmojiPanInner::selectEmoji(EmojiPtr emoji) {
qSwap(*i, *(i - 1));
}
}
- _saveConfigTimer.start(SaveRecentEmojisTimeout);
+ emit saveConfigDelayed(SaveRecentEmojisTimeout);
emit selected(emoji);
}
-void EmojiPanInner::onSaveConfig() {
- Local::writeUserSettings();
-}
-
void EmojiPanInner::onShowPicker() {
int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift;
if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) {
@@ -1030,7 +1045,7 @@ void EmojiPanInner::clearSelection(bool fast) {
_hovers[tab][sel] = 0;
}
_selected = _pressedSel = -1;
- anim::stop(this);
+ _a_selected.stop();
} else {
updateSelected();
}
@@ -1128,7 +1143,7 @@ void EmojiPanInner::updateSelected() {
bool startanim = false;
int oldSel = _selected, newSel = selIndex;
-
+
if (newSel != oldSel) {
if (oldSel >= 0) {
_animations.remove(oldSel + 1);
@@ -1155,15 +1170,14 @@ void EmojiPanInner::updateSelected() {
}
_selected = selIndex;
- if (startanim) anim::start(this);
+ if (startanim && !_a_selected.animating()) _a_selected.start();
}
-bool EmojiPanInner::animStep(float64 ms) {
- uint64 now = getms();
+void EmojiPanInner::step_selected(uint64 ms, bool timer) {
QRegion toUpdate;
for (Animations::iterator i = _animations.begin(); i != _animations.end();) {
int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift;
- float64 dt = float64(now - i.value()) / st::emojiPanDuration;
+ float64 dt = float64(ms - i.value()) / st::emojiPanDuration;
if (dt >= 1) {
_hovers[tab][sel] = (i.key() > 0) ? 1 : 0;
i = _animations.erase(i);
@@ -1173,8 +1187,8 @@ bool EmojiPanInner::animStep(float64 ms) {
}
toUpdate += emojiRect(tab, sel);
}
- rtlupdate(toUpdate.boundingRect());
- return !_animations.isEmpty();
+ if (timer) rtlupdate(toUpdate.boundingRect());
+ if (_animations.isEmpty()) _a_selected.stop();
}
void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) {
@@ -1197,7 +1211,13 @@ void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) {
}
StickerPanInner::StickerPanInner() : TWidget()
+, _a_selected(animation(this, &StickerPanInner::step_selected))
, _top(0)
+, _showingSavedGifs(cShowingSavedGifs())
+, _showingInlineItems(_showingSavedGifs)
+, _setGifCommand(false)
+, _lastScrolled(0)
+, _inlineWithThumb(false)
, _selected(-1)
, _pressedSel(-1)
, _settings(this, lang(lng_stickers_you_have))
@@ -1210,11 +1230,12 @@ StickerPanInner::StickerPanInner() : TWidget()
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings()));
-
+
_previewTimer.setSingleShot(true);
connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview()));
- refreshStickers();
+ _updateInlineItems.setSingleShot(true);
+ connect(&_updateInlineItems, SIGNAL(timeout()), this, SLOT(onUpdateInlineItems()));
}
void StickerPanInner::setMaxHeight(int32 h) {
@@ -1226,17 +1247,25 @@ void StickerPanInner::setMaxHeight(int32 h) {
void StickerPanInner::setScrollTop(int top) {
if (top == _top) return;
+ _lastScrolled = getms();
_top = top;
updateSelected();
}
-int StickerPanInner::countHeight() {
- int result = 0, minLastH = _maxHeight - st::rbEmoji.height - st::stickerPanPadding;
- for (int i = 0; i < _sets.size(); ++i) {
- int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0);
- int h = st::emojiPanHeader + rows * st::stickerPanSize.height();
- if (i == _sets.size() - 1 && h < minLastH) h = minLastH;
- result += h;
+int32 StickerPanInner::countHeight(bool plain) {
+ int result = 0, minLastH = plain ? 0 : (_maxHeight - st::stickerPanPadding);
+ if (_showingInlineItems) {
+ result = st::emojiPanHeader;
+ for (int i = 0, l = _inlineRows.count(); i < l; ++i) {
+ result += _inlineRows.at(i).height;
+ }
+ } else {
+ for (int i = 0; i < _sets.size(); ++i) {
+ int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0);
+ int h = st::emojiPanHeader + rows * st::stickerPanSize.height();
+ if (i == _sets.size() - 1 && h < minLastH) h = minLastH;
+ result += h;
+ }
}
return qMax(minLastH, result) + st::stickerPanPadding;
}
@@ -1264,8 +1293,49 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
if (r != rect()) {
p.setClipRect(r);
}
- p.fillRect(r, st::white->b);
+ p.fillRect(r, st::white);
+ if (_showingInlineItems) {
+ paintInlineItems(p, r);
+ } else {
+ paintStickers(p, r);
+ }
+}
+
+void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) {
+ if (_inlineRows.isEmpty()) {
+ p.setFont(st::normalFont);
+ p.setPen(st::noContactsColor);
+ p.drawText(QRect(0, 0, width(), (height() / 3) * 2 + st::normalFont->height), lang(lng_inline_bot_no_results), style::al_center);
+ return;
+ }
+ InlinePaintContext context(getms(), false, Ui::isLayerShown() || Ui::isMediaViewShown() || _previewShown, false);
+
+ int32 top = st::emojiPanHeader;
+ int32 fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width());
+ for (int32 row = 0, rows = _inlineRows.size(); row < rows; ++row) {
+ const InlineRow &inlineRow(_inlineRows.at(row));
+ if (top >= r.top() + r.height()) break;
+ if (top + inlineRow.height > r.top()) {
+ int32 left = st::inlineResultsLeft;
+ if (row == rows - 1) context.lastRow = true;
+ for (int32 col = 0, cols = inlineRow.items.size(); col < cols; ++col) {
+ if (left >= tox) break;
+
+ int32 w = inlineRow.items.at(col)->width();
+ if (left + w > fromx) {
+ p.translate(left, top);
+ inlineRow.items.at(col)->paint(p, r.translated(-left, -top), 0, &context);
+ p.translate(-left, -top);
+ }
+ left += w + st::inlineResultsSkip;
+ }
+ }
+ top += inlineRow.height;
+ }
+}
+
+void StickerPanInner::paintStickers(Painter &p, const QRect &r) {
int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow);
int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow);
if (rtl()) {
@@ -1310,17 +1380,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) {
if (goodThumb) {
sticker->thumb->load();
} else {
- bool already = !sticker->already().isEmpty(), hasdata = !sticker->data.isEmpty();
- if (!sticker->loader && sticker->status != FileFailed && !already && !hasdata) {
- sticker->save(QString());
- }
- if (sticker->sticker()->img->isNull() && (already || hasdata)) {
- if (already) {
- sticker->sticker()->img = ImagePtr(sticker->already());
- } else {
- sticker->sticker()->img = ImagePtr(sticker->data);
- }
- }
+ sticker->checkSticker();
}
float64 coef = qMin((st::stickerPanSize.width() - st::msgRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::msgRadius * 2) / float64(sticker->dimensions.height()));
@@ -1353,6 +1413,8 @@ void StickerPanInner::mousePressEvent(QMouseEvent *e) {
updateSelected();
_pressedSel = _selected;
+ textlnkDown(textlnkOver());
+ _linkDown = _linkOver;
_previewTimer.start(QApplication::startDragTime());
}
@@ -1360,7 +1422,10 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
_previewTimer.stop();
int32 pressed = _pressedSel;
+ TextLinkPtr down(_linkDown);
_pressedSel = -1;
+ _linkDown.reset();
+ textlnkDown(TextLinkPtr());
_lastMousePos = e->globalPos();
updateSelected();
@@ -1370,7 +1435,75 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
return;
}
- if (_selected < 0 || _selected != pressed) return;
+ if (_selected < 0 || _selected != pressed || _linkOver != down) return;
+ if (_showingInlineItems) {
+ int32 row = _selected / MatrixRowShift, col = _selected % MatrixRowShift;
+ if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) {
+ if (down) {
+ if (down->type() == qstr("SendInlineItemLink") && e->button() == Qt::LeftButton) {
+ LayoutInlineItem *item = _inlineRows.at(row).items.at(col);
+ PhotoData *photo = item->photo();
+ DocumentData *doc = item->document();
+ InlineResult *result = item->result();
+ if (doc) {
+ if (doc->loaded()) {
+ emit selected(doc);
+ } else if (doc->loading()) {
+ doc->cancel();
+ } else {
+ DocumentOpenLink::doOpen(doc, ActionOnLoadNone);
+ }
+ } else if (photo) {
+ if (photo->medium->loaded() || photo->thumb->loaded()) {
+ emit selected(photo);
+ } else if (!photo->medium->loading()) {
+ photo->thumb->loadEvenCancelled();
+ photo->medium->loadEvenCancelled();
+ }
+ } else if (result) {
+ if (result->type == qstr("gif")) {
+ if (result->doc) {
+ if (result->doc->loaded()) {
+ emit selected(result, _inlineBot);
+ } else if (result->doc->loading()) {
+ result->doc->cancel();
+ } else {
+ DocumentOpenLink::doOpen(result->doc, ActionOnLoadNone);
+ }
+ } else if (result->loaded()) {
+ emit selected(result, _inlineBot);
+ } else if (result->loading()) {
+ result->cancelFile();
+ Ui::repaintInlineItem(item);
+ } else {
+ result->saveFile(QString(), LoadFromCloudOrLocal, false);
+ Ui::repaintInlineItem(item);
+ }
+ } else if (result->type == qstr("photo")) {
+ if (result->photo) {
+ if (result->photo->medium->loaded() || result->photo->thumb->loaded()) {
+ emit selected(result, _inlineBot);
+ } else if (!result->photo->medium->loading()) {
+ result->photo->thumb->loadEvenCancelled();
+ result->photo->medium->loadEvenCancelled();
+ }
+ } else if (result->thumb->loaded()) {
+ emit selected(result, _inlineBot);
+ } else if (!result->thumb->loading()) {
+ result->thumb->loadEvenCancelled();
+ Ui::repaintInlineItem(item);
+ }
+ } else {
+ emit selected(result, _inlineBot);
+ }
+ }
+ } else {
+ down->onClick(e->button());
+ }
+ }
+ }
+ return;
+ }
if (_selected >= MatrixRowShift * _sets.size()) {
return;
}
@@ -1405,7 +1538,7 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
}
}
if (refresh) {
- refreshRecent();
+ refreshRecentStickers();
updateSelected();
update();
}
@@ -1434,9 +1567,27 @@ void StickerPanInner::enterFromChildEvent(QEvent *e) {
updateSelected();
}
+bool StickerPanInner::showSectionIcons() const {
+ return !inlineResultsShown();
+}
+
void StickerPanInner::clearSelection(bool fast) {
_lastMousePos = mapToGlobal(QPoint(-10, -10));
if (fast) {
+ if (_showingInlineItems) {
+ if (_selected >= 0) {
+ int32 srow = _selected / MatrixRowShift, scol = _selected % MatrixRowShift;
+ t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size());
+ if (_linkOver) {
+ _inlineRows.at(srow).items.at(scol)->linkOut(_linkOver);
+ _linkOver = TextLinkPtr();
+ textlnkOver(_linkOver);
+ }
+ setCursor(style::cur_default);
+ }
+ _selected = _pressedSel = -1;
+ return;
+ }
for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) {
int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift;
_sets[tab].hovers[sel] = 0;
@@ -1459,35 +1610,247 @@ void StickerPanInner::clearSelection(bool fast) {
_sets[tab].hovers[sel] = 0;
}
_selected = _pressedSel = -1;
- anim::stop(this);
+ _a_selected.stop();
} else {
updateSelected();
}
}
+void StickerPanInner::hideFinish(bool completely) {
+ if (completely) {
+ clearInlineRows(false);
+ for (GifLayouts::const_iterator i = _gifLayouts.cbegin(), e = _gifLayouts.cend(); i != e; ++i) {
+ i.value()->document()->forget();
+ }
+ for (InlineLayouts::const_iterator i = _inlineLayouts.cbegin(), e = _inlineLayouts.cend(); i != e; ++i) {
+ if (i.value()->result()->doc) {
+ i.value()->result()->doc->forget();
+ }
+ if (i.value()->result()->photo) {
+ i.value()->result()->photo->forget();
+ }
+ }
+ }
+ if (_setGifCommand && _showingSavedGifs) {
+ App::insertBotCommand(qsl(""), true);
+ }
+ _setGifCommand = false;
+}
+
void StickerPanInner::refreshStickers() {
clearSelection(true);
const StickerSets &sets(cStickerSets());
_sets.clear(); _sets.reserve(sets.size() + 1);
- refreshRecent(false);
+ refreshRecentStickers(false);
for (StickerSetsOrder::const_iterator i = cStickerSetsOrder().cbegin(), e = cStickerSetsOrder().cend(); i != e; ++i) {
appendSet(*i);
}
- int32 h = countHeight();
- if (h != height()) resize(width(), h);
+ if (_showingInlineItems) {
+ _settings.hide();
+ } else {
+ int32 h = countHeight();
+ if (h != height()) resize(width(), h);
+
+ _settings.setVisible(_sets.isEmpty());
+ }
- _settings.setVisible(_sets.isEmpty());
emit refreshIcons();
updateSelected();
}
+bool StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth) {
+ LayoutInlineItem *layout = 0;
+ if (savedGif) {
+ layout = layoutPrepareSavedGif(savedGif, (_inlineRows.size() * MatrixRowShift) + row.items.size());
+ } else if (result) {
+ layout = layoutPrepareInlineResult(result, (_inlineRows.size() * MatrixRowShift) + row.items.size());
+ }
+ if (!layout) return false;
+
+ layout->preload();
+ if (inlineRowFinalize(row, sumWidth, layout->fullLine())) {
+ layout->setPosition(_inlineRows.size() * MatrixRowShift);
+ }
+ row.items.push_back(layout);
+ sumWidth += layout->maxWidth();
+ return true;
+}
+
+bool StickerPanInner::inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force) {
+ if (row.items.isEmpty()) return false;
+
+ bool full = (row.items.size() >= SavedGifsMaxPerRow);
+ bool big = (sumWidth >= st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - (row.items.size() - 1) * st::inlineResultsSkip);
+ if (full || big || force) {
+ _inlineRows.push_back(layoutInlineRow(row, (full || big) ? sumWidth : 0));
+ row = InlineRow();
+ row.items.reserve(SavedGifsMaxPerRow);
+ sumWidth = 0;
+ return true;
+ }
+ return false;
+}
+
+void StickerPanInner::refreshSavedGifs() {
+ if (_showingSavedGifs) {
+ clearInlineRows(false);
+ if (_showingInlineItems) {
+ const SavedGifs &saved(cSavedGifs());
+ if (saved.isEmpty()) {
+ showStickerSet(RecentStickerSetId);
+ return;
+ } else {
+ _inlineRows.reserve(saved.size());
+ InlineRow row;
+ row.items.reserve(SavedGifsMaxPerRow);
+ int32 sumWidth = 0;
+ for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) {
+ inlineRowsAddItem(*i, 0, row, sumWidth);
+ }
+ inlineRowFinalize(row, sumWidth, true);
+ }
+ deleteUnusedGifLayouts();
+
+ int32 h = countHeight();
+ if (h != height()) resize(width(), h);
+
+ update();
+ }
+ }
+ emit refreshIcons();
+
+ updateSelected();
+}
+
+void StickerPanInner::inlineBotChanged() {
+ _setGifCommand = false;
+ refreshInlineRows(0, InlineResults(), true);
+ deleteUnusedInlineLayouts();
+}
+
+void StickerPanInner::clearInlineRows(bool resultsDeleted) {
+ if (resultsDeleted) {
+ if (_showingInlineItems) {
+ _selected = _pressedSel = -1;
+ }
+ } else {
+ if (_showingInlineItems) {
+ clearSelection(true);
+ }
+ for (InlineRows::const_iterator i = _inlineRows.cbegin(), e = _inlineRows.cend(); i != e; ++i) {
+ for (InlineItems::const_iterator j = i->items.cbegin(), end = i->items.cend(); j != end; ++j) {
+ (*j)->setPosition(-1);
+ }
+ }
+ }
+ _inlineRows.clear();
+}
+
+LayoutInlineGif *StickerPanInner::layoutPrepareSavedGif(DocumentData *doc, int32 position) {
+ GifLayouts::const_iterator i = _gifLayouts.constFind(doc);
+ if (i == _gifLayouts.cend()) {
+ i = _gifLayouts.insert(doc, new LayoutInlineGif(0, doc, true));
+ i.value()->initDimensions();
+ }
+ if (!i.value()->maxWidth()) return 0;
+
+ i.value()->setPosition(position);
+ return i.value();
+}
+
+LayoutInlineItem *StickerPanInner::layoutPrepareInlineResult(InlineResult *result, int32 position) {
+ InlineLayouts::const_iterator i = _inlineLayouts.constFind(result);
+ if (i == _inlineLayouts.cend()) {
+ LayoutInlineItem *layout = 0;
+ if (result->type == qstr("gif")) {
+ layout = new LayoutInlineGif(result, 0, false);
+ } else if (result->type == qstr("photo")) {
+ layout = new LayoutInlinePhoto(result, 0);
+ } else if (result->type == qstr("video")) {
+ layout = new LayoutInlineWebVideo(result);
+ } else if (result->type == qstr("article")) {
+ layout = new LayoutInlineArticle(result, _inlineWithThumb);
+ }
+ if (!layout) return 0;
+
+ i = _inlineLayouts.insert(result, layout);
+ layout->initDimensions();
+ }
+ if (!i.value()->maxWidth()) return 0;
+
+ i.value()->setPosition(position);
+ return i.value();
+}
+
+void StickerPanInner::deleteUnusedGifLayouts() {
+ if (_inlineRows.isEmpty() || !_showingSavedGifs) { // delete all
+ for (GifLayouts::const_iterator i = _gifLayouts.cbegin(), e = _gifLayouts.cend(); i != e; ++i) {
+ delete i.value();
+ }
+ _gifLayouts.clear();
+ } else {
+ for (GifLayouts::iterator i = _gifLayouts.begin(); i != _gifLayouts.cend();) {
+ if (i.value()->position() < 0) {
+ delete i.value();
+ i = _gifLayouts.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ }
+}
+
+void StickerPanInner::deleteUnusedInlineLayouts() {
+ if (_inlineRows.isEmpty() || _showingSavedGifs) { // delete all
+ for (InlineLayouts::const_iterator i = _inlineLayouts.cbegin(), e = _inlineLayouts.cend(); i != e; ++i) {
+ delete i.value();
+ }
+ _inlineLayouts.clear();
+ } else {
+ for (InlineLayouts::iterator i = _inlineLayouts.begin(); i != _inlineLayouts.cend();) {
+ if (i.value()->position() < 0) {
+ delete i.value();
+ i = _inlineLayouts.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ }
+}
+
+StickerPanInner::InlineRow &StickerPanInner::layoutInlineRow(InlineRow &row, int32 sumWidth) {
+ int32 count = row.items.size();
+ t_assert(count <= SavedGifsMaxPerRow);
+
+ row.height = 0;
+ int32 availw = width() - st::inlineResultsLeft - st::inlineResultsSkip * (count - 1);
+ for (int32 i = 0; i < count; ++i) {
+ int32 w = sumWidth ? (row.items.at(i)->maxWidth() * availw / sumWidth) : row.items.at(i)->maxWidth();
+ int32 actualw = qMax(w, int32(st::inlineResultsMinWidth));
+ row.height = qMax(row.height, row.items.at(i)->resizeGetHeight(actualw));
+ if (sumWidth) {
+ availw -= actualw;
+ sumWidth -= row.items.at(i)->maxWidth();
+ }
+ }
+ return row;
+}
+
void StickerPanInner::preloadImages() {
- uint64 ms = getms();
+ if (_showingInlineItems) {
+ for (int32 row = 0, rows = _inlineRows.size(); row < rows; ++row) {
+ for (int32 col = 0, cols = _inlineRows.at(row).items.size(); col < cols; ++col) {
+ _inlineRows.at(row).items.at(col)->preload();
+ }
+ }
+ return;
+ }
+
for (int32 i = 0, l = _sets.size(), k = 0; i < l; ++i) {
for (int32 j = 0, n = _sets.at(i).pack.size(); j < n; ++j) {
if (++k > StickerPanPerRow * (StickerPanPerRow + 1)) break;
@@ -1499,17 +1862,7 @@ void StickerPanInner::preloadImages() {
if (goodThumb) {
sticker->thumb->load();
} else {
- bool already = !sticker->already().isEmpty(), hasdata = !sticker->data.isEmpty();
- if (!sticker->loader && sticker->status != FileFailed && !already && !hasdata) {
- sticker->save(QString());
- }
- //if (sticker->sticker->img->isNull() && (already || hasdata)) {
- // if (already) {
- // sticker->sticker->img = ImagePtr(sticker->already());
- // } else {
- // sticker->sticker->img = ImagePtr(sticker->data);
- // }
- //}
+ sticker->automaticLoad(0);
}
}
if (k > StickerPanPerRow * (StickerPanPerRow + 1)) break;
@@ -1517,6 +1870,8 @@ void StickerPanInner::preloadImages() {
}
uint64 StickerPanInner::currentSet(int yOffset) const {
+ if (_showingInlineItems) return NoneStickerSetId;
+
int y, ytill = 0;
for (int i = 0, l = _sets.size(); i < l; ++i) {
int cnt = _sets.at(i).pack.size();
@@ -1529,6 +1884,158 @@ uint64 StickerPanInner::currentSet(int yOffset) const {
return _sets.isEmpty() ? RecentStickerSetId : _sets.back().id;
}
+void StickerPanInner::hideInlineRowsPanel() {
+ clearInlineRows(false);
+ if (_showingInlineItems) {
+ _showingSavedGifs = cShowingSavedGifs();
+ if (_showingSavedGifs) {
+ refreshSavedGifs();
+ emit scrollToY(0);
+ emit scrollUpdated();
+ } else {
+ showStickerSet(RecentStickerSetId);
+ }
+ }
+}
+
+void StickerPanInner::clearInlineRowsPanel() {
+ clearInlineRows(false);
+}
+
+int32 StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &results, bool resultsDeleted) {
+ _inlineBot = bot;
+ if (results.isEmpty() && (!_inlineBot || _inlineBot->username != cInlineGifBotUsername())) {
+ if (resultsDeleted) {
+ clearInlineRows(true);
+ }
+ emit emptyInlineRows();
+ return 0;
+ }
+
+ clearSelection(true);
+
+ t_assert(_inlineBot != 0);
+ _inlineBotTitle = lng_inline_bot_results(lt_inline_bot, _inlineBot->username.isEmpty() ? _inlineBot->name : ('@' + _inlineBot->username));
+
+ _showingInlineItems = true;
+ _showingSavedGifs = false;
+
+ int32 count = results.size(), from = validateExistingInlineRows(results), added = 0;
+
+ if (count) {
+ _inlineRows.reserve(count);
+ InlineRow row;
+ row.items.reserve(SavedGifsMaxPerRow);
+ int32 sumWidth = 0;
+ for (int32 i = from; i < count; ++i) {
+ if (inlineRowsAddItem(0, results.at(i), row, sumWidth)) {
+ ++added;
+ }
+ }
+ inlineRowFinalize(row, sumWidth, true);
+ }
+
+ int32 h = countHeight();
+ if (h != height()) resize(width(), h);
+ update();
+
+ emit refreshIcons();
+
+ _lastMousePos = QCursor::pos();
+ updateSelected();
+
+ return added;
+}
+
+int32 StickerPanInner::validateExistingInlineRows(const InlineResults &results) {
+ int32 count = results.size(), until = 0, untilrow = 0, untilcol = 0;
+ for (; until < count;) {
+ if (untilrow >= _inlineRows.size() || _inlineRows.at(untilrow).items.at(untilcol)->result() != results.at(until)) {
+ break;
+ }
+ ++until;
+ if (++untilcol == _inlineRows.at(untilrow).items.size()) {
+ ++untilrow;
+ untilcol = 0;
+ }
+ }
+ if (until == count) { // all items are layed out
+ if (untilrow == _inlineRows.size()) { // nothing changed
+ return until;
+ }
+
+ for (int32 i = untilrow, l = _inlineRows.size(), skip = untilcol; i < l; ++i) {
+ for (int32 j = 0, s = _inlineRows.at(i).items.size(); j < s; ++j) {
+ if (skip) {
+ --skip;
+ } else {
+ _inlineRows.at(i).items.at(j)->setPosition(-1);
+ }
+ }
+ }
+ if (!untilcol) { // all good rows are filled
+ _inlineRows.resize(untilrow);
+ return until;
+ }
+ _inlineRows.resize(untilrow + 1);
+ _inlineRows[untilrow].items.resize(untilcol);
+ _inlineRows[untilrow] = layoutInlineRow(_inlineRows[untilrow]);
+ return until;
+ }
+ if (untilrow && !untilcol) { // remove last row, maybe it is not full
+ --untilrow;
+ untilcol = _inlineRows.at(untilrow).items.size();
+ }
+ until -= untilcol;
+
+ for (int32 i = untilrow, l = _inlineRows.size(); i < l; ++i) {
+ for (int32 j = 0, s = _inlineRows.at(i).items.size(); j < s; ++j) {
+ _inlineRows.at(i).items.at(j)->setPosition(-1);
+ }
+ }
+ _inlineRows.resize(untilrow);
+
+ if (_inlineRows.isEmpty()) {
+ _inlineWithThumb = false;
+ for (int32 i = until; i < count; ++i) {
+ if (!results.at(i)->thumb->isNull()) {
+ _inlineWithThumb = true;
+ break;
+ }
+ }
+ }
+ return until;
+}
+
+void StickerPanInner::ui_repaintInlineItem(const LayoutInlineItem *layout) {
+ uint64 ms = getms();
+ if (_lastScrolled + 100 <= ms) {
+ update();
+ } else {
+ _updateInlineItems.start(_lastScrolled + 100 - ms);
+ }
+}
+
+bool StickerPanInner::ui_isInlineItemVisible(const LayoutInlineItem *layout) {
+ int32 position = layout->position();
+ if (!_showingInlineItems || position < 0) return false;
+
+ int32 row = position / MatrixRowShift, col = position % MatrixRowShift;
+ t_assert((row < _inlineRows.size()) && (col < _inlineRows.at(row).items.size()));
+
+ const InlineItems &inlineItems(_inlineRows.at(row).items);
+ int32 top = st::emojiPanHeader;
+ for (int32 i = 0; i < row; ++i) {
+ top += _inlineRows.at(i).height;
+ }
+
+ return (top < _top + _maxHeight) && (top + _inlineRows.at(row).items.at(col)->height() > _top);
+}
+
+bool StickerPanInner::ui_isInlineItemBeingChosen() {
+ return _showingInlineItems;
+}
+
void StickerPanInner::appendSet(uint64 setId) {
const StickerSets &sets(cStickerSets());
StickerSets::const_iterator it = sets.constFind(setId);
@@ -1542,7 +2049,17 @@ void StickerPanInner::appendSet(uint64 setId) {
_sets.push_back(DisplayedSet(it->id, it->flags, it->title, pack.size() + 1, pack));
}
-void StickerPanInner::refreshRecent(bool performResize) {
+void StickerPanInner::refreshRecent() {
+ if (_showingInlineItems) {
+ if (_showingSavedGifs) {
+ refreshSavedGifs();
+ }
+ } else {
+ refreshRecentStickers();
+ }
+}
+
+void StickerPanInner::refreshRecentStickers(bool performResize) {
_custom.clear();
clearSelection(true);
StickerSets::const_iterator customIt = cStickerSets().constFind(CustomStickerSetId);
@@ -1579,7 +2096,7 @@ void StickerPanInner::refreshRecent(bool performResize) {
}
}
- if (performResize) {
+ if (performResize && !_showingInlineItems) {
int32 h = countHeight();
if (h != height()) {
resize(width(), h);
@@ -1590,14 +2107,15 @@ void StickerPanInner::refreshRecent(bool performResize) {
}
}
-void StickerPanInner::fillIcons(QVector &icons) {
+void StickerPanInner::fillIcons(QList &icons) {
icons.clear();
- if (_sets.isEmpty()) return;
+ icons.reserve(_sets.size() + 1);
+ if (!cSavedGifs().isEmpty()) icons.push_back(StickerIcon(NoneStickerSetId));
- icons.reserve(_sets.size());
+ if (_sets.isEmpty()) return;
int32 i = 0;
if (_sets.at(0).id == RecentStickerSetId) ++i;
- if (i > 0) icons.push_back(StickerIcon());
+ if (i > 0) icons.push_back(StickerIcon(RecentStickerSetId));
for (int32 l = _sets.size(); i < l; ++i) {
DocumentData *s = _sets.at(i).pack.at(0);
int32 availw = st::rbEmoji.width - 2 * st::stickerIconPadding, availh = st::rbEmoji.height - 2 * st::stickerIconPadding;
@@ -1621,6 +2139,13 @@ void StickerPanInner::fillPanels(QVector &panels) {
panels.at(i)->deleteLater();
}
panels.clear();
+
+ if (_showingInlineItems) {
+ panels.push_back(new EmojiPanel(parentWidget(), _showingSavedGifs ? lang(lng_saved_gifs) : _inlineBotTitle, NoneStickerSetId, true, 0));
+ panels.back()->show();
+ return;
+ }
+
if (_sets.isEmpty()) return;
int y = 0;
@@ -1636,8 +2161,9 @@ void StickerPanInner::fillPanels(QVector &panels) {
}
}
-
void StickerPanInner::refreshPanels(QVector &panels) {
+ if (_showingInlineItems) return;
+
if (panels.size() != _sets.size()) return fillPanels(panels);
int32 y = 0;
@@ -1655,6 +2181,79 @@ void StickerPanInner::updateSelected() {
int32 selIndex = -1;
QPoint p(mapFromGlobal(_lastMousePos));
+ if (_showingInlineItems) {
+ int sx = (rtl() ? width() - p.x() : p.x()) - st::inlineResultsLeft, sy = p.y() - st::emojiPanHeader;
+ int32 row = -1, col = -1, sel = -1;
+ TextLinkPtr lnk;
+ HistoryCursorState cursor = HistoryDefaultCursorState;
+ if (sy >= 0) {
+ row = 0;
+ for (int32 rows = _inlineRows.size(); row < rows; ++row) {
+ if (sy < _inlineRows.at(row).height) {
+ break;
+ }
+ sy -= _inlineRows.at(row).height;
+ }
+ }
+ if (sx >= 0 && row >= 0 && row < _inlineRows.size()) {
+ const InlineItems &inlineItems(_inlineRows.at(row).items);
+ col = 0;
+ for (int32 cols = inlineItems.size(); col < cols; ++col) {
+ int32 width = inlineItems.at(col)->width();
+ if (sx < width) {
+ break;
+ }
+ sx -= width + st::inlineResultsSkip;
+ }
+ if (col < inlineItems.size()) {
+ sel = row * MatrixRowShift + col;
+ inlineItems.at(col)->getState(lnk, cursor, sx, sy);
+ } else {
+ row = col = -1;
+ }
+ } else {
+ row = col = -1;
+ }
+ int32 srow = (_selected >= 0) ? (_selected / MatrixRowShift) : -1;
+ int32 scol = (_selected >= 0) ? (_selected % MatrixRowShift) : -1;
+ if (_selected != sel) {
+ if (srow >= 0 && scol >= 0) {
+ t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size());
+ Ui::repaintInlineItem(_inlineRows.at(srow).items.at(scol));
+ }
+ _selected = sel;
+ if (row >= 0 && col >= 0) {
+ t_assert(row >= 0 && row < _inlineRows.size() && col >= 0 && col < _inlineRows.at(row).items.size());
+ Ui::repaintInlineItem(_inlineRows.at(row).items.at(col));
+ }
+ if (_pressedSel >= 0 && _selected >= 0 && _pressedSel != _selected) {
+ _pressedSel = _selected;
+ if (row >= 0 && col >= 0 && _inlineRows.at(row).items.at(col)->document()) {
+ Ui::showStickerPreview(_inlineRows.at(row).items.at(col)->document());
+ }
+ }
+ }
+ if (_linkOver != lnk) {
+ if (_linkOver && srow >= 0 && scol >= 0) {
+ t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size());
+ _inlineRows.at(srow).items.at(scol)->linkOut(_linkOver);
+ Ui::repaintInlineItem(_inlineRows.at(srow).items.at(scol));
+ }
+ if ((_linkOver && !lnk) || (!_linkOver && lnk)) {
+ setCursor(lnk ? style::cur_pointer : style::cur_default);
+ }
+ _linkOver = lnk;
+ textlnkOver(lnk);
+ if (_linkOver && row >= 0 && col >= 0) {
+ t_assert(row >= 0 && row < _inlineRows.size() && col >= 0 && col < _inlineRows.at(row).items.size());
+ _inlineRows.at(row).items.at(col)->linkOver(_linkOver);
+ Ui::repaintInlineItem(_inlineRows.at(row).items.at(col));
+ }
+
+ }
+ return;
+ }
+
int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::stickerPanPadding;
for (int c = 0, l = _sets.size(); c < l; ++c) {
const DisplayedSet &set(_sets.at(c));
@@ -1733,15 +2332,22 @@ void StickerPanInner::updateSelected() {
Ui::showStickerPreview(_sets.at(newSelTab).pack.at(newSel % MatrixRowShift));
}
}
- if (startanim) anim::start(this);
+ if (startanim && !_a_selected.animating()) _a_selected.start();
}
void StickerPanInner::onSettings() {
- App::showLayer(new StickersBox());
+ Ui::showLayer(new StickersBox());
}
void StickerPanInner::onPreview() {
- if (_pressedSel >= 0 && _pressedSel < MatrixRowShift * _sets.size()) {
+ if (_pressedSel < 0) return;
+ if (_showingInlineItems) {
+ int32 row = _pressedSel / MatrixRowShift, col = _pressedSel % MatrixRowShift;
+ if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size() && _inlineRows.at(row).items.at(col)->document() && _inlineRows.at(row).items.at(col)->document()->loaded()) {
+ Ui::showStickerPreview(_inlineRows.at(row).items.at(col)->document());
+ _previewShown = true;
+ }
+ } else if (_pressedSel < MatrixRowShift * _sets.size()) {
int tab = (_pressedSel / MatrixRowShift), sel = _pressedSel % MatrixRowShift;
if (sel < _sets.at(tab).pack.size()) {
Ui::showStickerPreview(_sets.at(tab).pack.at(sel));
@@ -1750,12 +2356,22 @@ void StickerPanInner::onPreview() {
}
}
-bool StickerPanInner::animStep(float64 ms) {
- uint64 now = getms();
+void StickerPanInner::onUpdateInlineItems() {
+ if (!_showingInlineItems) return;
+
+ uint64 ms = getms();
+ if (_lastScrolled + 100 <= ms) {
+ update();
+ } else {
+ _updateInlineItems.start(_lastScrolled + 100 - ms);
+ }
+}
+
+void StickerPanInner::step_selected(uint64 ms, bool timer) {
QRegion toUpdate;
for (Animations::iterator i = _animations.begin(); i != _animations.end();) {
int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift;
- float64 dt = float64(now - i.value()) / st::emojiPanDuration;
+ float64 dt = float64(ms - i.value()) / st::emojiPanDuration;
if (dt >= 1) {
_sets[tab].hovers[sel] = (i.key() > 0) ? 1 : 0;
i = _animations.erase(i);
@@ -1765,13 +2381,44 @@ bool StickerPanInner::animStep(float64 ms) {
}
toUpdate += stickerRect(tab, sel);
}
- rtlupdate(toUpdate.boundingRect());
- return !_animations.isEmpty();
+ if (timer) rtlupdate(toUpdate.boundingRect());
+ if (_animations.isEmpty()) _a_selected.stop();
}
void StickerPanInner::showStickerSet(uint64 setId) {
clearSelection(true);
+ if (setId == NoneStickerSetId) {
+ bool wasNotShowingGifs = !_showingInlineItems;
+ if (wasNotShowingGifs) {
+ _showingInlineItems = true;
+ _showingSavedGifs = true;
+ cSetShowingSavedGifs(true);
+ emit saveConfigDelayed(SaveRecentEmojisTimeout);
+ }
+ refreshSavedGifs();
+ emit scrollToY(0);
+ emit scrollUpdated();
+ showFinish();
+ return;
+ }
+
+ if (_showingInlineItems) {
+ if (_setGifCommand && _showingSavedGifs) {
+ App::insertBotCommand(qsl(""), true);
+ }
+ _setGifCommand = false;
+
+ _showingInlineItems = false;
+ cSetShowingSavedGifs(false);
+ emit saveConfigDelayed(SaveRecentEmojisTimeout);
+
+ Notify::clipStopperHidden(ClipStopperSavedGifsPanel);
+
+ refreshRecentStickers(true);
+ emit refreshIcons();
+ }
+
int32 y = 0;
for (int c = 0; c < _sets.size(); ++c) {
if (_sets.at(c).id == setId) break;
@@ -1780,14 +2427,38 @@ void StickerPanInner::showStickerSet(uint64 setId) {
}
emit scrollToY(y);
+ emit scrollUpdated();
_lastMousePos = QCursor::pos();
update();
}
-EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY) : TWidget(parent),
-_wantedY(wantedY), _setId(setId), _special(special), _deleteVisible(false), _delete(special ? 0 : new IconedButton(this, st::notifyClose)) { // NoneStickerSetId if in emoji
+void StickerPanInner::updateShowingSavedGifs() {
+ if (cShowingSavedGifs()) {
+ if (!_showingInlineItems) {
+ clearSelection(true);
+ _showingSavedGifs = _showingInlineItems = true;
+ if (_inlineRows.isEmpty()) refreshSavedGifs();
+ }
+ } else if (!_showingInlineItems) {
+ clearSelection(true);
+ _showingSavedGifs = false;
+ }
+}
+
+void StickerPanInner::showFinish() {
+ if (_showingInlineItems && _showingSavedGifs) {
+ _setGifCommand = App::insertBotCommand('@' + cInlineGifBotUsername(), true);
+ }
+}
+
+EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY) : TWidget(parent)
+, _wantedY(wantedY)
+, _setId(setId)
+, _special(special)
+, _deleteVisible(false)
+, _delete(special ? 0 : new IconedButton(this, st::notifyClose)) { // NoneStickerSetId if in emoji
resize(st::emojiPanWidth, st::emojiPanHeader);
setMouseTracking(true);
setFocusPolicy(Qt::NoFocus);
@@ -1815,7 +2486,7 @@ void EmojiPanel::updateText() {
availw -= st::notifyClose.icon.pxWidth() + st::emojiPanHeaderLeft;
}
} else {
- QString switchText = lang((_setId != NoneStickerSetId) ? lng_switch_emoji : lng_switch_stickers);
+ QString switchText = lang((_setId != NoneStickerSetId) ? lng_switch_emoji : (cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs));
availw -= st::emojiSwitchSkip + st::emojiPanHeaderFont->width(switchText);
}
_text = st::emojiPanHeaderFont->elided(_fullText, availw);
@@ -1847,11 +2518,36 @@ void EmojiPanel::paintEvent(QPaintEvent *e) {
p.drawTextLeft(st::emojiPanHeaderLeft, st::emojiPanHeaderTop, width(), _text);
}
-EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button(parent),
-_toStickers(toStickers), _text(lang(_toStickers ? lng_switch_stickers : lng_switch_emoji)),
-_textWidth(st::emojiPanHeaderFont->width(_text)) {
- int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip);
+EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button(parent)
+, _toStickers(toStickers) {
setCursor(style::cur_pointer);
+ updateText();
+}
+
+void EmojiSwitchButton::updateText(const QString &inlineBotUsername) {
+ if (_toStickers) {
+ if (inlineBotUsername.isEmpty()) {
+ _text = lang(cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs);
+ } else {
+ _text = '@' + inlineBotUsername;
+ }
+ } else {
+ _text = lang(lng_switch_emoji);
+ }
+ _textWidth = st::emojiPanHeaderFont->width(_text);
+ if (_toStickers && !inlineBotUsername.isEmpty()) {
+ int32 maxw = 0;
+ for (int c = 0; c < emojiTabCount; ++c) {
+ maxw = qMax(maxw, st::emojiPanHeaderFont->width(lang(LangKey(lng_emoji_category0 + c))));
+ }
+ maxw += st::emojiPanHeaderLeft + st::emojiSwitchSkip + (st::emojiSwitchSkip - st::emojiSwitchImgSkip);
+ if (_textWidth > st::emojiPanWidth - maxw) {
+ _text = st::emojiPanHeaderFont->elided(_text, st::emojiPanWidth - maxw);
+ _textWidth = st::emojiPanHeaderFont->width(_text);
+ }
+ }
+
+ int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip);
resize(w, st::emojiPanHeader);
}
@@ -1869,22 +2565,50 @@ void EmojiSwitchButton::paintEvent(QPaintEvent *e) {
}
}
-EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent), _maxHeight(st::emojiPanMaxHeight),
-_horizontal(false), _noTabUpdate(false), _hiding(false), a_opacity(0), _shadow(st::dropdownDef.shadow),
-_recent(this , qsl("emoji_group"), dbietRecent , QString(), true , st::rbEmojiRecent),
-_people(this , qsl("emoji_group"), dbietPeople , QString(), false, st::rbEmojiPeople),
-_nature(this , qsl("emoji_group"), dbietNature , QString(), false, st::rbEmojiNature),
-_food(this , qsl("emoji_group"), dbietFood , QString(), false, st::rbEmojiFood),
-_activity(this, qsl("emoji_group"), dbietActivity, QString(), false, st::rbEmojiActivity),
-_travel(this , qsl("emoji_group"), dbietTravel , QString(), false, st::rbEmojiTravel),
-_objects(this , qsl("emoji_group"), dbietObjects , QString(), false, st::rbEmojiObjects),
-_symbols(this , qsl("emoji_group"), dbietSymbols , QString(), false, st::rbEmojiSymbols),
-_iconOver(-1), _iconSel(0), _iconDown(-1), _iconsDragging(false),
-_iconAnim(animFunc(this, &EmojiPan::iconAnim)),
-_iconsLeft(0), _iconsTop(0), _iconsStartX(0), _iconsMax(0), _iconsX(0, 0), _iconSelX(0, 0), _iconsStartAnim(0),
-_stickersShown(false), _moveStart(0),
-e_scroll(this, st::emojiScroll), e_inner(), e_switch(&e_scroll, true),
-s_scroll(this, st::emojiScroll), s_inner(), s_switch(&s_scroll, false), _removingSetId(0) {
+EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent)
+, _maxHeight(st::emojiPanMaxHeight)
+, _contentMaxHeight(st::emojiPanMaxHeight)
+, _contentHeight(_contentMaxHeight)
+, _contentHeightEmoji(_contentHeight - st::rbEmoji.height)
+, _contentHeightStickers(_contentHeight - st::rbEmoji.height)
+, _horizontal(false)
+, _noTabUpdate(false)
+, _hiding(false)
+, a_opacity(0)
+, _a_appearance(animation(this, &EmojiPan::step_appearance))
+, _shadow(st::dropdownDef.shadow)
+, _recent(this , qsl("emoji_group"), dbietRecent , QString(), true , st::rbEmojiRecent)
+, _people(this , qsl("emoji_group"), dbietPeople , QString(), false, st::rbEmojiPeople)
+, _nature(this , qsl("emoji_group"), dbietNature , QString(), false, st::rbEmojiNature)
+, _food(this , qsl("emoji_group"), dbietFood , QString(), false, st::rbEmojiFood)
+, _activity(this, qsl("emoji_group"), dbietActivity, QString(), false, st::rbEmojiActivity)
+, _travel(this , qsl("emoji_group"), dbietTravel , QString(), false, st::rbEmojiTravel)
+, _objects(this , qsl("emoji_group"), dbietObjects , QString(), false, st::rbEmojiObjects)
+, _symbols(this , qsl("emoji_group"), dbietSymbols , QString(), false, st::rbEmojiSymbols)
+, _iconOver(-1)
+, _iconSel(0)
+, _iconDown(-1)
+, _iconsDragging(false)
+, _a_icons(animation(this, &EmojiPan::step_icons))
+, _iconsLeft(0)
+, _iconsTop(0)
+, _iconsStartX(0)
+, _iconsMax(0)
+, _iconsX(0, 0)
+, _iconSelX(0, 0)
+, _iconsStartAnim(0)
+, _stickersShown(false)
+, _shownFromInlineQuery(false)
+, _a_slide(animation(this, &EmojiPan::step_slide))
+, e_scroll(this, st::emojiScroll)
+, e_inner()
+, e_switch(&e_scroll, true)
+, s_scroll(this, st::emojiScroll)
+, s_inner()
+, s_switch(&s_scroll, false)
+, _removingSetId(0)
+, _inlineBot(0)
+, _inlineRequestId(0) {
setFocusPolicy(Qt::NoFocus);
e_scroll.setFocusPolicy(Qt::NoFocus);
e_scroll.viewport()->setFocusPolicy(Qt::NoFocus);
@@ -1892,11 +2616,12 @@ s_scroll(this, st::emojiScroll), s_inner(), s_switch(&s_scroll, false), _removin
s_scroll.viewport()->setFocusPolicy(Qt::NoFocus);
_width = st::dropdownDef.padding.left() + st::emojiPanWidth + st::dropdownDef.padding.right();
- _height = st::dropdownDef.padding.top() + _maxHeight + st::dropdownDef.padding.bottom();
+ _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom();
+ _bottom = 0;
resize(_width, _height);
- e_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height);
- s_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height);
+ e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji);
+ s_scroll.resize(st::emojiPanWidth, _contentHeightStickers);
e_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top());
e_scroll.setWidget(&e_inner);
@@ -1907,7 +2632,7 @@ s_scroll(this, st::emojiScroll), s_inner(), s_switch(&s_scroll, false), _removin
s_inner.moveToLeft(0, 0, s_scroll.width());
int32 left = _iconsLeft = st::dropdownDef.padding.left() + (st::emojiPanWidth - 8 * st::rbEmoji.width) / 2;
- int32 top = _iconsTop = st::dropdownDef.padding.top() + _maxHeight - st::rbEmoji.height;
+ int32 top = _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height;
prepareTab(left, top, _width, _recent);
prepareTab(left, top, _width, _people);
prepareTab(left, top, _width, _nature);
@@ -1926,12 +2651,17 @@ s_scroll(this, st::emojiScroll), s_inner(), s_switch(&s_scroll, false), _removin
connect(&e_inner, SIGNAL(disableScroll(bool)), &e_scroll, SLOT(disableScroll(bool)));
connect(&s_inner, SIGNAL(scrollToY(int)), &s_scroll, SLOT(scrollToY(int)));
+ connect(&s_inner, SIGNAL(scrollUpdated()), this, SLOT(onScroll()));
connect(&e_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(&s_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(&e_inner, SIGNAL(selected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr)));
connect(&s_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*)));
+ connect(&s_inner, SIGNAL(selected(PhotoData*)), this, SIGNAL(photoSelected(PhotoData*)));
+ connect(&s_inner, SIGNAL(selected(InlineResult*,UserData*)), this, SIGNAL(inlineResultSelected(InlineResult*,UserData*)));
+
+ connect(&s_inner, SIGNAL(emptyInlineRows()), this, SLOT(onEmptyInlineRows()));
connect(&s_switch, SIGNAL(clicked()), this, SLOT(onSwitch()));
connect(&e_switch, SIGNAL(clicked()), this, SLOT(onSwitch()));
@@ -1943,6 +2673,15 @@ s_scroll(this, st::emojiScroll), s_inner(), s_switch(&s_scroll, false), _removin
connect(&e_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels()));
connect(&s_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels()));
+ _saveConfigTimer.setSingleShot(true);
+ connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig()));
+ connect(&e_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32)));
+ connect(&s_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32)));
+
+ // inline bots
+ _inlineRequestTimer.setSingleShot(true);
+ connect(&_inlineRequestTimer, SIGNAL(timeout()), this, SLOT(onInlineRequest()));
+
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
}
@@ -1952,28 +2691,39 @@ s_scroll(this, st::emojiScroll), s_inner(), s_switch(&s_scroll, false), _removin
}
void EmojiPan::setMaxHeight(int32 h) {
- h = qMin(int(st::emojiPanMaxHeight), h);
- if (h == _maxHeight) return;
-
- int32 was = _maxHeight;
_maxHeight = h;
+ updateContentHeight();
+}
+
+void EmojiPan::updateContentHeight() {
+ int32 h = qMin(_contentMaxHeight, _maxHeight);
+ int32 he = h - st::rbEmoji.height;
+ int32 hs = h - (s_inner.showSectionIcons() ? st::rbEmoji.height : 0);
+ if (h == _contentHeight && he == _contentHeightEmoji && hs == _contentHeightStickers) return;
+
+ int32 was = _contentHeight, wase = _contentHeightEmoji, wass = _contentHeightStickers;
+ _contentHeight = h;
+ _contentHeightEmoji = he;
+ _contentHeightStickers = hs;
+
+ _height = st::dropdownDef.padding.top() + _contentHeight + st::dropdownDef.padding.bottom();
- _height = st::dropdownDef.padding.top() + _maxHeight + st::dropdownDef.padding.bottom();
resize(_width, _height);
+ move(x(), _bottom - height());
- if (was > _maxHeight) {
- e_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height);
- s_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height);
- s_inner.setMaxHeight(_maxHeight);
- e_inner.setMaxHeight(_maxHeight);
+ if (was > _contentHeight || (was == _contentHeight && wass > _contentHeightStickers)) {
+ e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji);
+ s_scroll.resize(st::emojiPanWidth, _contentHeightStickers);
+ s_inner.setMaxHeight(_contentHeightStickers);
+ e_inner.setMaxHeight(_contentHeightEmoji);
} else {
- s_inner.setMaxHeight(_maxHeight);
- e_inner.setMaxHeight(_maxHeight);
- e_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height);
- s_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height);
+ s_inner.setMaxHeight(_contentHeightStickers);
+ e_inner.setMaxHeight(_contentHeightEmoji);
+ e_scroll.resize(st::emojiPanWidth, _contentHeightEmoji);
+ s_scroll.resize(st::emojiPanWidth, _contentHeightStickers);
}
- _iconsTop = st::dropdownDef.padding.top() + _maxHeight - st::rbEmoji.height;
+ _iconsTop = st::dropdownDef.padding.top() + _contentHeight - st::rbEmoji.height;
_recent.move(_recent.x(), _iconsTop);
_people.move(_people.x(), _iconsTop);
_nature.move(_nature.x(), _iconsTop);
@@ -1999,6 +2749,14 @@ void EmojiPan::onWndActiveChanged() {
}
}
+void EmojiPan::onSaveConfig() {
+ Local::writeUserSettings();
+}
+
+void EmojiPan::onSaveConfigDelayed(int32 delay) {
+ _saveConfigTimer.start(delay);
+}
+
void EmojiPan::paintEvent(QPaintEvent *e) {
Painter p(this);
@@ -2014,29 +2772,25 @@ void EmojiPan::paintEvent(QPaintEvent *e) {
if (_toCache.isNull()) {
if (_cache.isNull()) {
p.fillRect(myrtlrect(r.x() + r.width() - st::emojiScroll.width, r.y(), st::emojiScroll.width, e_scroll.height()), st::white->b);
- if (_stickersShown) {
- p.fillRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height, st::emojiPanCategories->b);
+ if (_stickersShown && s_inner.showSectionIcons()) {
+ p.fillRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height, st::emojiPanCategories);
p.drawSpriteLeft(_iconsLeft + 7 * st::rbEmoji.width + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::stickersSettings);
if (!_icons.isEmpty()) {
- int32 x = _iconsLeft, i = 0, selxrel = _iconSelX.current(), selx = x + selxrel - _iconsX.current();
- if (!_icons.at(i).sticker) {
- if (selxrel > 0) {
- if (_iconHovers.at(i) < 1) {
- p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::rbEmojiRecent.imageRect);
- }
- if (_iconHovers.at(i) > 0) {
- p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::rbEmojiRecent.overImageRect);
- }
+ int32 x = _iconsLeft, i = 0, selxrel = _iconsLeft + _iconSelX.current(), selx = selxrel - _iconsX.current();
+ for (int32 l = _icons.size(); i < l && !_icons.at(i).sticker; ++i) {
+ bool gifs = (_icons.at(i).setId == NoneStickerSetId);
+ if (selxrel != x) {
+ p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), gifs ? st::savedGifsOver : st::rbEmojiRecent.imageRect);
}
- if (selxrel < st::rbEmoji.width) {
- p.setOpacity(1 - (selxrel / st::rbEmoji.width));
- p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::rbEmojiRecent.chkImageRect);
+ if (selxrel < x + st::rbEmoji.width && selxrel > x - st::rbEmoji.width) {
+ p.setOpacity(1 - (qAbs(selxrel - x) / st::rbEmoji.width));
+ p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), gifs ? st::savedGifsActive : st::rbEmojiRecent.chkImageRect);
p.setOpacity(1);
}
x += st::rbEmoji.width;
- ++i;
}
+ int32 skip = i;
QRect clip(x, _iconsTop, _iconsLeft + 7 * st::rbEmoji.width - x, st::rbEmoji.height);
if (rtl()) clip.moveLeft(width() - x - clip.width());
@@ -2044,28 +2798,23 @@ void EmojiPan::paintEvent(QPaintEvent *e) {
i += _iconsX.current() / int(st::rbEmoji.width);
x -= _iconsX.current() % int(st::rbEmoji.width);
- for (int32 l = qMin(_icons.size(), i + 7 + (_icons.at(0).sticker ? 1 : 0)); i < l; ++i) {
+ for (int32 l = qMin(_icons.size(), i + 8 - skip); i < l; ++i) {
const StickerIcon &s(_icons.at(i));
s.sticker->thumb->load();
QPixmap pix(s.sticker->thumb->pix(s.pixw, s.pixh));
- //if (_iconSel == i) {
- // p.setOpacity(1);
- //} else {
- // p.setOpacity(1. * _iconHovers.at(i) + st::stickerIconOpacity * (1 - _iconHovers.at(i)));
- //}
+
p.drawPixmapLeft(x + (st::rbEmoji.width - s.pixw) / 2, _iconsTop + (st::rbEmoji.height - s.pixh) / 2, width(), pix);
x += st::rbEmoji.width;
- p.setOpacity(1);
}
if (rtl()) selx = width() - selx - st::rbEmoji.width;
- p.setOpacity(_icons.at(0).sticker ? 1. : qMax(1., selx / st::rbEmoji.width));
- p.fillRect(selx, _iconsTop + st::rbEmoji.height - st::stickerIconPadding, st::rbEmoji.width, st::stickerIconSel, st::stickerIconSelColor->b);
+ p.setOpacity(skip ? qMax(1., selx / (skip * st::rbEmoji.width)) : 1.);
+ p.fillRect(selx, _iconsTop + st::rbEmoji.height - st::stickerIconPadding, st::rbEmoji.width, st::stickerIconSel, st::stickerIconSelColor);
float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.pxWidth(), 0., 1.);
if (o_left > 0) {
p.setOpacity(o_left);
- p.drawSpriteLeft(QRect(_iconsLeft + (_icons.at(0).sticker ? 0 : st::rbEmoji.width), _iconsTop, st::stickerIconLeft.pxWidth(), st::rbEmoji.height), width(), st::stickerIconLeft);
+ p.drawSpriteLeft(QRect(_iconsLeft + skip * st::rbEmoji.width, _iconsTop, st::stickerIconLeft.pxWidth(), st::rbEmoji.height), width(), st::stickerIconLeft);
}
float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.pxWidth(), 0., 1.);
if (o_right > 0) {
@@ -2073,10 +2822,13 @@ void EmojiPan::paintEvent(QPaintEvent *e) {
p.drawSpriteRight(QRect(width() - _iconsLeft - 7 * st::rbEmoji.width, _iconsTop, st::stickerIconRight.pxWidth(), st::rbEmoji.height), width(), st::stickerIconRight);
}
}
- } else {
- p.fillRect(r.left(), _recent.y(), (rtl() ? _objects.x() : _recent.x() - r.left()), st::rbEmoji.height, st::emojiPanCategories->b);
+ } else if (_stickersShown) {
int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width());
- p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::emojiPanCategories->b);
+ p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::white);
+ } else {
+ p.fillRect(r.left(), _recent.y(), (rtl() ? _objects.x() : _recent.x() - r.left()), st::rbEmoji.height, st::emojiPanCategories);
+ int32 x = rtl() ? (_recent.x() + _recent.width()) : (_objects.x() + _objects.width());
+ p.fillRect(x, _recent.y(), r.left() + r.width() - x, st::rbEmoji.height, st::emojiPanCategories);
}
} else {
p.fillRect(r, st::white->b);
@@ -2114,14 +2866,27 @@ void EmojiPan::paintEvent(QPaintEvent *e) {
}
}
+void EmojiPan::moveBottom(int32 bottom, bool force) {
+ _bottom = bottom;
+ if (isHidden() && !force) {
+ move(x(), _bottom - height());
+ return;
+ }
+ if (_stickersShown && s_inner.inlineResultsShown()) {
+ moveToLeft(0, _bottom - height());
+ } else {
+ moveToRight(0, _bottom - height());
+ }
+}
+
void EmojiPan::enterEvent(QEvent *e) {
_hideTimer.stop();
if (_hiding) showStart();
}
void EmojiPan::leaveEvent(QEvent *e) {
- if (_removingSetId) return;
- if (animating()) {
+ if (_removingSetId || s_inner.inlineResultsShown()) return;
+ if (_a_appearance.animating()) {
hideStart();
} else {
_hideTimer.start(300);
@@ -2134,7 +2899,8 @@ void EmojiPan::otherEnter() {
}
void EmojiPan::otherLeave() {
- if (animating()) {
+ if (_removingSetId || s_inner.inlineResultsShown()) return;
+ if (_a_appearance.animating()) {
hideStart();
} else {
_hideTimer.start(0);
@@ -2147,7 +2913,7 @@ void EmojiPan::mousePressEvent(QMouseEvent *e) {
updateSelected();
if (_iconOver == _icons.size()) {
- App::showLayer(new StickersBox());
+ Ui::showLayer(new StickersBox());
} else {
_iconDown = _iconOver;
_iconsMouseDown = _iconsMousePos;
@@ -2160,7 +2926,12 @@ void EmojiPan::mouseMoveEvent(QMouseEvent *e) {
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
updateSelected();
- if (!_iconsDragging && !_icons.isEmpty() && _iconDown >= (_icons.at(0).sticker ? 0 : 1)) {
+ int32 skip = 0;
+ for (int32 i = 0, l = _icons.size(); i < l; ++i) {
+ if (_icons.at(i).sticker) break;
+ ++skip;
+ }
+ if (!_iconsDragging && !_icons.isEmpty() && _iconDown >= skip) {
if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) {
_iconsDragging = true;
}
@@ -2170,7 +2941,7 @@ void EmojiPan::mouseMoveEvent(QMouseEvent *e) {
if (newX != _iconsX.current()) {
_iconsX = anim::ivalue(newX, newX);
_iconsStartAnim = 0;
- if (_iconAnimations.isEmpty()) _iconAnim.stop();
+ if (_iconAnimations.isEmpty()) _a_icons.stop();
updateIcons();
}
}
@@ -2188,7 +2959,7 @@ void EmojiPan::mouseReleaseEvent(QMouseEvent *e) {
if (newX != _iconsX.current()) {
_iconsX = anim::ivalue(newX, newX);
_iconsStartAnim = 0;
- if (_iconAnimations.isEmpty()) _iconAnim.stop();
+ if (_iconAnimations.isEmpty()) _a_icons.stop();
updateIcons();
}
_iconsDragging = false;
@@ -2205,32 +2976,39 @@ void EmojiPan::mouseReleaseEvent(QMouseEvent *e) {
bool EmojiPan::event(QEvent *e) {
if (e->type() == QEvent::TouchBegin) {
- int a = 0;
- } else if (e->type() == QEvent::Wheel && !_icons.isEmpty() && _iconOver >= (_icons.at(0).sticker ? 0 : 1) && _iconOver < _icons.size() && _iconDown < 0) {
- QWheelEvent *ev = static_cast(e);
- bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal);
- bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical);
- if (hor) _horizontal = true;
- int32 newX = _iconsX.current();
- if (/*_horizontal && */hor) {
- newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax);
- } else if (/*!_horizontal && */ver) {
- newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax);
+
+ } else if (e->type() == QEvent::Wheel) {
+ int32 skip = 0;
+ for (int32 i = 0, l = _icons.size(); i < l; ++i) {
+ if (_icons.at(i).sticker) break;
+ ++skip;
}
- if (newX != _iconsX.current()) {
- _iconsX = anim::ivalue(newX, newX);
- _iconsStartAnim = 0;
- if (_iconAnimations.isEmpty()) _iconAnim.stop();
- updateSelected();
- updateIcons();
+ if (!_icons.isEmpty() && _iconOver >= skip && _iconOver < _icons.size() && _iconDown < 0) {
+ QWheelEvent *ev = static_cast(e);
+ bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal);
+ bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical);
+ if (hor) _horizontal = true;
+ int32 newX = _iconsX.current();
+ if (/*_horizontal && */hor) {
+ newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax);
+ } else if (/*!_horizontal && */ver) {
+ newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax);
+ }
+ if (newX != _iconsX.current()) {
+ _iconsX = anim::ivalue(newX, newX);
+ _iconsStartAnim = 0;
+ if (_iconAnimations.isEmpty()) _a_icons.stop();
+ updateSelected();
+ updateIcons();
+ }
}
}
return TWidget::event(e);
}
void EmojiPan::fastHide() {
- if (animating()) {
- anim::stop(this);
+ if (_a_appearance.animating()) {
+ _a_appearance.stop();
}
a_opacity = anim::fvalue(0, 0);
_hideTimer.stop();
@@ -2245,6 +3023,15 @@ void EmojiPan::refreshStickers() {
}
}
+void EmojiPan::refreshSavedGifs() {
+ e_switch.updateText();
+ e_switch.moveToRight(0, 0, st::emojiPanWidth);
+ s_inner.refreshSavedGifs();
+ if (!_stickersShown) {
+ s_inner.preloadImages();
+ }
+}
+
void EmojiPan::onRefreshIcons() {
_iconOver = -1;
_iconHovers.clear();
@@ -2254,7 +3041,7 @@ void EmojiPan::onRefreshIcons() {
_iconsX = anim::ivalue(0, 0);
_iconSelX.finish();
_iconsStartAnim = 0;
- _iconAnim.stop();
+ _a_icons.stop();
if (_icons.isEmpty()) {
_iconsMax = 0;
} else {
@@ -2263,6 +3050,10 @@ void EmojiPan::onRefreshIcons() {
}
updatePanelsPositions(s_panels, s_scroll.scrollTop());
updateSelected();
+ if (_stickersShown) {
+ validateSelectedIcon();
+ updateContentHeight();
+ }
updateIcons();
}
@@ -2295,16 +3086,19 @@ void EmojiPan::updateSelected() {
newOver = _icons.size();
} else if (!_icons.isEmpty()) {
if (y >= _iconsTop && y < _iconsTop + st::rbEmoji.height && x >= 0 && x < 7 * st::rbEmoji.width && x < _icons.size() * st::rbEmoji.width) {
- if (!_icons.at(0).sticker) {
+ int32 skip = 0;
+ for (int32 i = 0, l = _icons.size(); i < l; ++i) {
+ if (_icons.at(i).sticker) break;
if (x < st::rbEmoji.width) {
- newOver = 0;
- } else {
- x -= st::rbEmoji.width;
+ newOver = i;
+ break;
}
+ x -= st::rbEmoji.width;
+ ++skip;
}
if (newOver < 0) {
x += _iconsX.current();
- newOver = qFloor(x / st::rbEmoji.width) + (_icons.at(0).sticker ? 0 : 1);
+ newOver = qFloor(x / st::rbEmoji.width) + skip;
}
}
}
@@ -2330,22 +3124,26 @@ void EmojiPan::updateSelected() {
_iconAnimations.insert(_iconOver + 1, getms());
}
}
- if (startanim) _iconAnim.start();
+ if (startanim && !_a_icons.animating()) _a_icons.start();
}
}
void EmojiPan::updateIcons() {
+ if (!_stickersShown || !s_inner.showSectionIcons()) return;
+
QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom());
update(r.left(), _iconsTop, r.width(), st::rbEmoji.height);
}
-bool EmojiPan::iconAnim(float64 ms) {
- if (!_stickersShown) return false;
+void EmojiPan::step_icons(uint64 ms, bool timer) {
+ if (!_stickersShown) {
+ _a_icons.stop();
+ return;
+ }
- uint64 now = getms();
for (Animations::iterator i = _iconAnimations.begin(); i != _iconAnimations.end();) {
int index = qAbs(i.key()) - 1;
- float64 dt = float64(now - i.value()) / st::emojiPanDuration;
+ float64 dt = float64(ms - i.value()) / st::emojiPanDuration;
if (index >= _iconHovers.size()) {
i = _iconAnimations.erase(i);
} else if (dt >= 1) {
@@ -2358,7 +3156,7 @@ bool EmojiPan::iconAnim(float64 ms) {
}
if (_iconsStartAnim) {
- float64 dt = (now - _iconsStartAnim) / st::stickerIconMove;
+ float64 dt = (ms - _iconsStartAnim) / st::stickerIconMove;
if (dt >= 1) {
_iconsStartAnim = 0;
_iconsX.finish();
@@ -2367,58 +3165,65 @@ bool EmojiPan::iconAnim(float64 ms) {
_iconsX.update(dt, anim::linear);
_iconSelX.update(dt, anim::linear);
}
- updateSelected();
+ if (timer) updateSelected();
}
- updateIcons();
+ if (timer) updateIcons();
- return !_iconAnimations.isEmpty() || _iconsStartAnim;
+ if (_iconAnimations.isEmpty() && !_iconsStartAnim) {
+ _a_icons.stop();
+ }
}
-bool EmojiPan::animStep(float64 ms) {
- bool res1 = false;
- if (_moveStart) {
- float64 movems = getms() - _moveStart;
- float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration;
- float64 dt1 = (movems > st::introSlideDuration) ? 1 : (movems / st::introSlideDuration), dt2 = (movems > st::introSlideDelta) ? (movems - st::introSlideDelta) / (st::introSlideDuration) : 0;
- if (dt2 >= 1) {
- a_fromCoord.finish();
- a_fromAlpha.finish();
- a_toCoord.finish();
- a_toAlpha.finish();
- _fromCache = _toCache = QPixmap();
- _moveStart = 0;
- if (_cache.isNull()) showAll();
- } else {
- a_fromCoord.update(dt1, st::introHideFunc);
- a_fromAlpha.update(dt1, st::introAlphaHideFunc);
- a_toCoord.update(dt2, st::introShowFunc);
- a_toAlpha.update(dt2, st::introAlphaShowFunc);
- res1 = true;
- }
+void EmojiPan::step_slide(float64 ms, bool timer) {
+ float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration;
+ float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0;
+ if (dt2 >= 1) {
+ _a_slide.stop();
+ a_fromCoord.finish();
+ a_fromAlpha.finish();
+ a_toCoord.finish();
+ a_toAlpha.finish();
+ _fromCache = _toCache = QPixmap();
+ if (_cache.isNull()) showAll();
+ } else {
+ a_fromCoord.update(dt1, st::introHideFunc);
+ a_fromAlpha.update(dt1, st::introAlphaHideFunc);
+ a_toCoord.update(dt2, st::introShowFunc);
+ a_toAlpha.update(dt2, st::introAlphaShowFunc);
}
- bool res2 = false;
- if (!_cache.isNull()) {
- float64 dt = ms / st::dropdownDef.duration;
- if (dt >= 1) {
- a_opacity.finish();
- if (_hiding) {
- res1 = false;
- hideFinish();
- } else {
- _cache = QPixmap();
- if (_toCache.isNull()) showAll();
- }
- } else {
- a_opacity.update(dt, anim::linear);
- res2 = true;
- }
+ if (timer) update();
+}
+
+void EmojiPan::step_appearance(float64 ms, bool timer) {
+ if (_cache.isNull()) {
+ _a_appearance.stop();
+ return;
}
- update();
- return res1 || res2;
+
+ float64 dt = ms / st::dropdownDef.duration;
+ if (dt >= 1) {
+ _a_appearance.stop();
+ a_opacity.finish();
+ if (_hiding) {
+ hideFinish();
+ } else {
+ _cache = QPixmap();
+ if (_toCache.isNull()) showAll();
+ }
+ } else {
+ a_opacity.update(dt, anim::linear);
+ }
+ if (timer) update();
}
void EmojiPan::hideStart() {
+ if (_removingSetId || s_inner.inlineResultsShown()) return;
+
+ hideAnimated();
+}
+
+void EmojiPan::prepareShowHideCache() {
if (_cache.isNull()) {
QPixmap from = _fromCache, to = _toCache;
_fromCache = _toCache = QPixmap();
@@ -2426,18 +3231,26 @@ void EmojiPan::hideStart() {
_cache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding));
_fromCache = from; _toCache = to;
}
+}
+
+void EmojiPan::hideAnimated() {
+ if (_hiding) return;
+
+ prepareShowHideCache();
hideAll();
_hiding = true;
a_opacity.start(0);
- anim::start(this);
+ _a_appearance.start();
}
void EmojiPan::hideFinish() {
hide();
e_inner.hideFinish();
+ s_inner.hideFinish(true);
_cache = _toCache = _fromCache = QPixmap();
- _moveStart = 0;
+ _a_slide.stop();
_horizontal = false;
+ _hiding = false;
e_scroll.scrollToY(0);
if (!_recent.checked()) {
@@ -2451,35 +3264,44 @@ void EmojiPan::hideFinish() {
_iconsX = anim::ivalue(0, 0);
_iconSelX = anim::ivalue(0, 0);
_iconsStartAnim = 0;
- _iconAnim.stop();
+ _a_icons.stop();
_iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0);
_iconAnimations.clear();
+
+ Notify::clipStopperHidden(ClipStopperSavedGifsPanel);
}
void EmojiPan::showStart() {
- if (!isHidden() && a_opacity.current() == 1) {
+ if (!isHidden() && !_hiding) {
return;
}
if (isHidden()) {
e_inner.refreshRecent();
- s_inner.refreshRecent();
+ if (s_inner.inlineResultsShown() && refreshInlineRows()) {
+ _stickersShown = true;
+ _shownFromInlineQuery = true;
+ } else {
+ s_inner.refreshRecent();
+ _stickersShown = false;
+ _shownFromInlineQuery = false;
+ _cache = QPixmap(); // clear after refreshInlineRows()
+ }
+ recountContentMaxHeight();
s_inner.preloadImages();
- _stickersShown = false;
_fromCache = _toCache = QPixmap();
- _moveStart = 0;
- }
- if (_cache.isNull()) {
- QPixmap from = _fromCache, to = _toCache;
- _fromCache = _toCache = QPixmap();
- showAll();
- _cache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding));
- _fromCache = from; _toCache = to;
+ _a_slide.stop();
+ moveBottom(y() + height(), true);
+ } else if (_hiding) {
+ if (s_inner.inlineResultsShown() && refreshInlineRows()) {
+ onSwitch();
+ }
}
+ prepareShowHideCache();
hideAll();
_hiding = false;
show();
a_opacity.start(1);
- anim::start(this);
+ _a_appearance.start();
emit updateStickers();
}
@@ -2498,9 +3320,10 @@ bool EmojiPan::eventFilter(QObject *obj, QEvent *e) {
//}
} else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton/* && !dynamic_cast(obj)*/) {
if (isHidden() || _hiding) {
- otherEnter();
+ _hideTimer.stop();
+ showStart();
} else {
- otherLeave();
+ hideAnimated();
}
}
return false;
@@ -2509,6 +3332,7 @@ bool EmojiPan::eventFilter(QObject *obj, QEvent *e) {
void EmojiPan::stickersInstalled(uint64 setId) {
_stickersShown = true;
if (isHidden()) {
+ moveBottom(y() + height(), true);
show();
a_opacity = anim::fvalue(0, 1);
a_opacity.update(0, anim::linear);
@@ -2516,9 +3340,38 @@ void EmojiPan::stickersInstalled(uint64 setId) {
}
showAll();
s_inner.showStickerSet(setId);
+ updateContentHeight();
showStart();
}
+void EmojiPan::ui_repaintInlineItem(const LayoutInlineItem *layout) {
+ if (_stickersShown && !isHidden()) {
+ s_inner.ui_repaintInlineItem(layout);
+ }
+}
+
+bool EmojiPan::ui_isInlineItemVisible(const LayoutInlineItem *layout) {
+ if (_stickersShown && !isHidden()) {
+ return s_inner.ui_isInlineItemVisible(layout);
+ }
+ return false;
+}
+
+bool EmojiPan::ui_isInlineItemBeingChosen() {
+ if (_stickersShown && !isHidden()) {
+ return s_inner.ui_isInlineItemBeingChosen();
+ }
+ return false;
+}
+
+void EmojiPan::notify_automaticLoadSettingsChangedGif() {
+ for (InlineCache::const_iterator i = _inlineCache.cbegin(), ei = _inlineCache.cend(); i != ei; ++i) {
+ for (InlineResults::const_iterator j = i.value()->results.cbegin(), ej = i.value()->results.cend(); j != ej; ++j) {
+ (*j)->automaticLoadSettingsChangedGif();
+ }
+ }
+}
+
void EmojiPan::showAll() {
if (_stickersShown) {
s_scroll.show();
@@ -2612,37 +3465,67 @@ void EmojiPan::onScroll() {
st = s_scroll.scrollTop();
if (_stickersShown) {
updatePanelsPositions(s_panels, st);
-
- uint64 setId = s_inner.currentSet(st);
- int32 newSel = 0;
- for (int32 i = 0, l = _icons.size(); i < l; ++i) {
- if (_icons.at(i).setId == setId) {
- newSel = i;
- break;
- }
- }
- if (newSel != _iconSel) {
- _iconSel = newSel;
- _iconSelX.start(newSel * st::rbEmoji.width);
- _iconsX.start(snap((2 * newSel - 7 - ((_icons.isEmpty() || _icons.at(0).sticker) ? 0 : 1)) * int(st::rbEmoji.width) / 2, 0, _iconsMax));
- _iconsStartAnim = getms();
- _iconAnim.start();
- updateSelected();
- updateIcons();
+ validateSelectedIcon(true);
+ if (st + s_scroll.height() > s_scroll.scrollTopMax()) {
+ onInlineRequest();
}
}
s_inner.setScrollTop(st);
}
+void EmojiPan::validateSelectedIcon(bool animated) {
+ uint64 setId = s_inner.currentSet(s_scroll.scrollTop());
+ int32 newSel = 0;
+ for (int32 i = 0, l = _icons.size(); i < l; ++i) {
+ if (_icons.at(i).setId == setId) {
+ newSel = i;
+ break;
+ }
+ }
+ if (newSel != _iconSel) {
+ _iconSel = newSel;
+ int32 skip = 0;
+ for (int32 i = 0, l = _icons.size(); i < l; ++i) {
+ if (_icons.at(i).sticker) break;
+ ++skip;
+ }
+ if (animated) {
+ _iconSelX.start(newSel * st::rbEmoji.width);
+ } else {
+ _iconSelX = anim::ivalue(newSel * st::rbEmoji.width, newSel * st::rbEmoji.width);
+ }
+ _iconsX.start(snap((2 * newSel - 7 - skip) * int(st::rbEmoji.width) / 2, 0, _iconsMax));
+ _iconsStartAnim = getms();
+ _a_icons.start();
+ updateSelected();
+ updateIcons();
+ }
+}
+
void EmojiPan::onSwitch() {
QPixmap cache = _cache;
_fromCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding));
_stickersShown = !_stickersShown;
-
+ if (!_stickersShown) {
+ Notify::clipStopperHidden(ClipStopperSavedGifsPanel);
+ } else {
+ if (cShowingSavedGifs() && cSavedGifs().isEmpty()) {
+ s_inner.showStickerSet(DefaultStickerSetId);
+ } else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && cStickerSets().isEmpty()) {
+ s_inner.showStickerSet(NoneStickerSetId);
+ } else {
+ s_inner.updateShowingSavedGifs();
+ }
+ if (cShowingSavedGifs()) {
+ s_inner.showFinish();
+ }
+ validateSelectedIcon();
+ updateContentHeight();
+ }
_iconOver = -1;
_iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0);
_iconAnimations.clear();
- _iconAnim.stop();
+ _a_icons.stop();
_cache = QPixmap();
showAll();
@@ -2650,10 +3533,11 @@ void EmojiPan::onSwitch() {
_cache = cache;
hideAll();
- _moveStart = getms();
if (_stickersShown) {
e_inner.hideFinish();
+ } else {
+ s_inner.hideFinish(false);
}
a_toCoord = (_stickersShown != rtl()) ? anim::ivalue(st::emojiPanWidth, 0) : anim::ivalue(-st::emojiPanWidth, 0);
@@ -2661,7 +3545,7 @@ void EmojiPan::onSwitch() {
a_fromCoord = (_stickersShown != rtl()) ? anim::ivalue(0, -st::emojiPanWidth) : anim::ivalue(0, st::emojiPanWidth);
a_fromAlpha = anim::fvalue(1, 0);
- if (!animating()) anim::start(this);
+ _a_slide.start();
update();
}
@@ -2672,12 +3556,12 @@ void EmojiPan::onRemoveSet(quint64 setId) {
ConfirmBox *box = new ConfirmBox(lng_stickers_remove_pack(lt_sticker_pack, it->title), lang(lng_box_remove));
connect(box, SIGNAL(confirmed()), this, SLOT(onRemoveSetSure()));
connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onDelayedHide()));
- App::wnd()->showLayer(box);
+ Ui::showLayer(box);
}
}
void EmojiPan::onRemoveSetSure() {
- App::wnd()->hideLayer();
+ Ui::hideLayer();
StickerSets::iterator it = cRefStickerSets().find(_removingSetId);
if (it != cRefStickerSets().cend() && !(it->flags & MTPDstickerSet::flag_official)) {
if (it->id && it->access) {
@@ -2698,7 +3582,6 @@ void EmojiPan::onRemoveSetSure() {
cRefStickerSets().erase(it);
int32 removeIndex = cStickerSetsOrder().indexOf(_removingSetId);
if (removeIndex >= 0) cRefStickerSetsOrder().removeAt(removeIndex);
- cSetStickersHash(stickersCountHash());
refreshStickers();
Local::writeStickers();
if (writeRecent) Local::writeUserSettings();
@@ -2713,126 +3596,430 @@ void EmojiPan::onDelayedHide() {
_removingSetId = 0;
}
-MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *rows, HashtagRows *hrows, BotCommandRows *crows) : _parent(parent), _rows(rows), _hrows(hrows), _crows(crows), _sel(-1), _mouseSel(false), _overDelete(false) {
+void EmojiPan::clearInlineBot() {
+ inlineBotChanged();
+ e_switch.updateText();
+ e_switch.moveToRight(0, 0, st::emojiPanWidth);
+}
+
+bool EmojiPan::hideOnNoInlineResults() {
+ return _inlineBot && _stickersShown && s_inner.inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername());
+}
+
+void EmojiPan::inlineBotChanged() {
+ if (!_inlineBot) return;
+
+ if (!isHidden() && !_hiding) {
+ if (hideOnNoInlineResults() || !rect().contains(mapFromGlobal(QCursor::pos()))) {
+ hideAnimated();
+ }
+ }
+
+ if (_inlineRequestId) MTP::cancel(_inlineRequestId);
+ _inlineRequestId = 0;
+ _inlineQuery = _inlineNextQuery = _inlineNextOffset = QString();
+ _inlineBot = 0;
+ for (InlineCache::const_iterator i = _inlineCache.cbegin(), e = _inlineCache.cend(); i != e; ++i) {
+ delete i.value();
+ }
+ _inlineCache.clear();
+ s_inner.inlineBotChanged();
+ s_inner.hideInlineRowsPanel();
+
+ Notify::inlineBotRequesting(false);
+}
+
+void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) {
+ _inlineRequestId = 0;
+ Notify::inlineBotRequesting(false);
+
+ InlineCache::iterator it = _inlineCache.find(_inlineQuery);
+
+ bool adding = (it != _inlineCache.cend());
+ if (result.type() == mtpc_messages_botResults) {
+ const MTPDmessages_botResults &d(result.c_messages_botResults());
+ const QVector &v(d.vresults.c_vector().v);
+ uint64 queryId(d.vquery_id.v);
+
+ if (!adding) {
+ it = _inlineCache.insert(_inlineQuery, new InlineCacheEntry());
+ }
+ it.value()->nextOffset = qs(d.vnext_offset);
+
+ int32 count = v.size(), added = 0;
+ if (count) {
+ it.value()->results.reserve(it.value()->results.size() + count);
+ }
+ for (int32 i = 0; i < count; ++i) {
+ InlineResult *result = new InlineResult(queryId);
+ const MTPBotInlineMessage *message = 0;
+ switch (v.at(i).type()) {
+ case mtpc_botInlineMediaResultPhoto: {
+ const MTPDbotInlineMediaResultPhoto &r(v.at(i).c_botInlineMediaResultPhoto());
+ result->id = qs(r.vid);
+ result->type = qs(r.vtype);
+ result->photo = App::feedPhoto(r.vphoto);
+ message = &r.vsend_message;
+ } break;
+ case mtpc_botInlineMediaResultDocument: {
+ const MTPDbotInlineMediaResultDocument &r(v.at(i).c_botInlineMediaResultDocument());
+ result->id = qs(r.vid);
+ result->type = qs(r.vtype);
+ result->doc = App::feedDocument(r.vdocument);
+ message = &r.vsend_message;
+ } break;
+ case mtpc_botInlineResult: {
+ const MTPDbotInlineResult &r(v.at(i).c_botInlineResult());
+ result->id = qs(r.vid);
+ result->type = qs(r.vtype);
+ result->title = r.has_title() ? qs(r.vtitle) : QString();
+ result->description = r.has_description() ? qs(r.vdescription) : QString();
+ result->url = r.has_url() ? qs(r.vurl) : QString();
+ result->thumb_url = r.has_thumb_url() ? qs(r.vthumb_url) : QString();
+ result->content_type = r.has_content_type() ? qs(r.vcontent_type) : QString();
+ result->content_url = r.has_content_url() ? qs(r.vcontent_url) : QString();
+ result->width = r.has_w() ? r.vw.v : 0;
+ result->height = r.has_h() ? r.vh.v : 0;
+ result->duration = r.has_duration() ? r.vduration.v : 0;
+ if (!result->thumb_url.isEmpty() && (result->thumb_url.startsWith(qstr("http://"), Qt::CaseInsensitive) || result->thumb_url.startsWith(qstr("https://"), Qt::CaseInsensitive))) {
+ result->thumb = ImagePtr(result->thumb_url);
+ }
+ message = &r.vsend_message;
+ } break;
+ }
+ bool badAttachment = (result->photo && !result->photo->access) || (result->doc && !result->doc->access);
+
+ if (!message) {
+ delete result;
+ continue;
+ }
+ switch (message->type()) {
+ case mtpc_botInlineMessageMediaAuto: {
+ const MTPDbotInlineMessageMediaAuto &r(message->c_botInlineMessageMediaAuto());
+ result->caption = qs(r.vcaption);
+ } break;
+
+ case mtpc_botInlineMessageText: {
+ const MTPDbotInlineMessageText &r(message->c_botInlineMessageText());
+ result->message = qs(r.vmessage);
+ if (r.has_entities()) result->entities = entitiesFromMTP(r.ventities.c_vector().v);
+ result->noWebPage = r.is_no_webpage();
+ } break;
+
+ default: {
+ badAttachment = true;
+ } break;
+ }
+
+ bool canSend = (result->photo || result->doc || !result->message.isEmpty() || (!result->content_url.isEmpty() && (result->type == qstr("gif") || result->type == qstr("photo"))));
+ if (result->type.isEmpty() || badAttachment || !canSend) {
+ delete result;
+ } else {
+ ++added;
+ it.value()->results.push_back(result);
+ }
+ }
+
+ if (!added) {
+ it.value()->nextOffset = QString();
+ }
+ } else if (adding) {
+ it.value()->nextOffset = QString();
+ }
+
+ if (!showInlineRows(!adding)) {
+ it.value()->nextOffset = QString();
+ }
+ onScroll();
+}
+
+bool EmojiPan::inlineResultsFail(const RPCError &error) {
+ if (mtpIsFlood(error)) return false;
+
+ Notify::inlineBotRequesting(false);
+ _inlineRequestId = 0;
+ return true;
+}
+
+void EmojiPan::queryInlineBot(UserData *bot, QString query) {
+ bool force = false;
+ if (bot != _inlineBot) {
+ inlineBotChanged();
+ _inlineBot = bot;
+ force = true;
+ }
+ if (_inlineQuery != query || force) {
+ if (_inlineRequestId) {
+ MTP::cancel(_inlineRequestId);
+ _inlineRequestId = 0;
+ Notify::inlineBotRequesting(false);
+ }
+ if (_inlineCache.contains(query)) {
+ _inlineRequestTimer.stop();
+ _inlineQuery = query;
+ showInlineRows(true);
+ } else {
+ _inlineNextQuery = query;
+ _inlineRequestTimer.start(InlineBotRequestDelay);
+ }
+ }
+}
+
+void EmojiPan::onInlineRequest() {
+ if (_inlineRequestId || !_inlineBot) return;
+ _inlineQuery = _inlineNextQuery;
+
+ QString nextOffset;
+ InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery);
+ if (i != _inlineCache.cend()) {
+ nextOffset = i.value()->nextOffset;
+ if (nextOffset.isEmpty()) return;
+ }
+ Notify::inlineBotRequesting(true);
+ _inlineRequestId = MTP::send(MTPmessages_GetInlineBotResults(_inlineBot->inputUser, MTP_string(_inlineQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::inlineResultsDone), rpcFail(&EmojiPan::inlineResultsFail));
+}
+
+void EmojiPan::onEmptyInlineRows() {
+ if (_shownFromInlineQuery || hideOnNoInlineResults()) {
+ hideAnimated();
+ } else if (!_inlineBot) {
+ s_inner.hideInlineRowsPanel();
+ } else {
+ s_inner.clearInlineRowsPanel();
+ }
+}
+
+bool EmojiPan::refreshInlineRows(int32 *added) {
+ bool clear = true;
+ InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery);
+ if (i != _inlineCache.cend()) {
+ clear = i.value()->results.isEmpty();
+ _inlineNextOffset = i.value()->nextOffset;
+ }
+ if (clear) prepareShowHideCache();
+ int32 result = s_inner.refreshInlineRows(_inlineBot, clear ? InlineResults() : i.value()->results, false);
+ if (added) *added = result;
+ return !clear;
+}
+
+int32 EmojiPan::showInlineRows(bool newResults) {
+ int32 added = 0;
+ bool clear = !refreshInlineRows(&added);
+ if (newResults) s_scroll.scrollToY(0);
+
+ e_switch.updateText(clear ? QString() : _inlineBot->username);
+ e_switch.moveToRight(0, 0, st::emojiPanWidth);
+
+ bool hidden = isHidden();
+ if (!hidden && !clear) {
+ recountContentMaxHeight();
+ }
+ if (clear) {
+ if (!hidden && hideOnNoInlineResults()) {
+ hideAnimated();
+ } else {
+ _cache = QPixmap(); // clear after refreshInlineRows()
+ }
+ } else {
+ _hideTimer.stop();
+ if (hidden || _hiding) {
+ showStart();
+ } else if (!_stickersShown) {
+ onSwitch();
+ }
+ }
+
+ return added;
+}
+
+void EmojiPan::recountContentMaxHeight() {
+ if (_shownFromInlineQuery) {
+ _contentMaxHeight = qMin(s_inner.countHeight(true), int(st::emojiPanMaxHeight));
+ } else {
+ _contentMaxHeight = st::emojiPanMaxHeight;
+ }
+ updateContentHeight();
+}
+
+MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerPack *srows)
+: _parent(parent)
+, _mrows(mrows)
+, _hrows(hrows)
+, _brows(brows)
+, _srows(srows)
+, _stickersPerRow(1)
+, _recentInlineBotsInRows(0)
+, _sel(-1)
+, _mouseSel(false)
+, _overDelete(false) {
}
void MentionsInner::paintEvent(QPaintEvent *e) {
- QPainter p(this);
+ Painter p(this);
+
+ QRect r(e->rect());
+ if (r != rect()) p.setClipRect(r);
int32 atwidth = st::mentionFont->width('@'), hashwidth = st::mentionFont->width('#');
int32 mentionleft = 2 * st::mentionPadding.left() + st::mentionPhotoSize;
int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right();
int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width;
- int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1;
- int32 last = _rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size();
- bool hasUsername = _parent->filter().indexOf('@') > 1;
- for (int32 i = from; i < to; ++i) {
- if (i >= last) break;
+ if (!_srows->isEmpty()) {
+ int32 rows = rowscount(_srows->size(), _stickersPerRow);
+ int32 fromrow = floorclamp(r.y() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
+ int32 torow = ceilclamp(r.y() + r.height() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
+ int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, _stickersPerRow);
+ int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, _stickersPerRow);
+ for (int32 row = fromrow; row < torow; ++row) {
+ for (int32 col = fromcol; col < tocol; ++col) {
+ int32 index = row * _stickersPerRow + col;
+ if (index >= _srows->size()) break;
- bool selected = (i == _sel);
- if (selected) {
- p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b);
- int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
- if (!_hrows->isEmpty()) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
+ DocumentData *sticker = _srows->at(index);
+ if (!sticker->sticker()) continue;
+
+ QPoint pos(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height());
+ if (_sel == index) {
+ QPoint tl(pos);
+ if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width());
+ App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners);
+ }
+
+ bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128));
+ if (goodThumb) {
+ sticker->thumb->load();
+ } else {
+ sticker->checkSticker();
+ }
+
+ float64 coef = qMin((st::stickerPanSize.width() - st::msgRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::msgRadius * 2) / float64(sticker->dimensions.height()));
+ if (coef > 1) coef = 1;
+ int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height());
+ if (w < 1) w = 1;
+ if (h < 1) h = 1;
+ QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
+ if (goodThumb) {
+ p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h));
+ } else if (!sticker->sticker()->img->isNull()) {
+ p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h));
+ }
+ }
}
- p.setPen(st::black->p);
- if (!_rows->isEmpty()) {
- UserData *user = _rows->at(i);
- QString first = (_parent->filter().size() < 2) ? QString() : ('@' + user->username.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('@' + user->username) : user->username.mid(_parent->filter().size() - 1);
- int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth();
- if (mentionwidth < unamewidth + namewidth) {
- namewidth = (mentionwidth * namewidth) / (namewidth + unamewidth);
- unamewidth = mentionwidth - namewidth;
- if (firstwidth < unamewidth + st::mentionFont->elidew) {
- if (firstwidth < unamewidth) {
- first = st::mentionFont->elided(first, unamewidth);
- } else if (!second.isEmpty()) {
- first = st::mentionFont->elided(first + second, unamewidth);
- second = QString();
+ } else {
+ int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1;
+ int32 last = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
+ bool hasUsername = _parent->filter().indexOf('@') > 1;
+ for (int32 i = from; i < to; ++i) {
+ if (i >= last) break;
+
+ bool selected = (i == _sel);
+ if (selected) {
+ p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b);
+ int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
+ if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
+ }
+ p.setPen(st::black->p);
+ if (!_mrows->isEmpty()) {
+ UserData *user = _mrows->at(i);
+ QString first = (_parent->filter().size() < 2) ? QString() : ('@' + user->username.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('@' + user->username) : user->username.mid(_parent->filter().size() - 1);
+ int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth();
+ if (mentionwidth < unamewidth + namewidth) {
+ namewidth = (mentionwidth * namewidth) / (namewidth + unamewidth);
+ unamewidth = mentionwidth - namewidth;
+ if (firstwidth < unamewidth + st::mentionFont->elidew) {
+ if (firstwidth < unamewidth) {
+ first = st::mentionFont->elided(first, unamewidth);
+ } else if (!second.isEmpty()) {
+ first = st::mentionFont->elided(first + second, unamewidth);
+ second = QString();
+ }
+ } else {
+ second = st::mentionFont->elided(second, unamewidth - firstwidth);
}
- } else {
- second = st::mentionFont->elided(second, unamewidth - firstwidth);
}
- }
- user->photo->load();
- p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize));
- user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
-
- p.setFont(st::mentionFont->f);
- p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
- p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
- if (!second.isEmpty()) {
- p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
- p.drawText(mentionleft + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
- }
- } else if (!_hrows->isEmpty()) {
- QString hrow = _hrows->at(i);
- QString first = (_parent->filter().size() < 2) ? QString() : ('#' + hrow.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('#' + hrow) : hrow.mid(_parent->filter().size() - 1);
- int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
- if (htagwidth < firstwidth + secondwidth) {
- if (htagwidth < firstwidth + st::mentionFont->elidew) {
- first = st::mentionFont->elided(first + second, htagwidth);
- second = QString();
- } else {
- second = st::mentionFont->elided(second, htagwidth - firstwidth);
- }
- }
-
- p.setFont(st::mentionFont->f);
- if (!first.isEmpty()) {
- p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
- p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
- }
- if (!second.isEmpty()) {
- p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
- p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
- }
- } else {
- UserData *user = _crows->at(i).first;
-
- const BotCommand *command = _crows->at(i).second;
- QString toHighlight = command->command;
- int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
- if (hasUsername || botStatus == 0 || botStatus == 2) {
- toHighlight += '@' + user->username;
- }
- if (true || _parent->chat() || botStatus == 0 || botStatus == 2) {
user->photo->load();
p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize));
- }
+ user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
- int32 addleft = 0, widthleft = mentionwidth;
- QString first = (_parent->filter().size() < 2) ? QString() : ('/' + toHighlight.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('/' + toHighlight) : toHighlight.mid(_parent->filter().size() - 1);
- int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
- if (widthleft < firstwidth + secondwidth) {
- if (widthleft < firstwidth + st::mentionFont->elidew) {
- first = st::mentionFont->elided(first + second, widthleft);
- second = QString();
- } else {
- second = st::mentionFont->elided(second, widthleft - firstwidth);
+ p.setFont(st::mentionFont->f);
+ p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
+ p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
+ if (!second.isEmpty()) {
+ p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
+ p.drawText(mentionleft + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
+ }
+ } else if (!_hrows->isEmpty()) {
+ QString hrow = _hrows->at(i);
+ QString first = (_parent->filter().size() < 2) ? QString() : ('#' + hrow.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('#' + hrow) : hrow.mid(_parent->filter().size() - 1);
+ int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
+ if (htagwidth < firstwidth + secondwidth) {
+ if (htagwidth < firstwidth + st::mentionFont->elidew) {
+ first = st::mentionFont->elided(first + second, htagwidth);
+ second = QString();
+ } else {
+ second = st::mentionFont->elided(second, htagwidth - firstwidth);
+ }
+ }
+
+ p.setFont(st::mentionFont->f);
+ if (!first.isEmpty()) {
+ p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
+ p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
+ }
+ if (!second.isEmpty()) {
+ p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
+ p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
+ }
+ } else {
+ UserData *user = _brows->at(i).first;
+
+ const BotCommand *command = _brows->at(i).second;
+ QString toHighlight = command->command;
+ int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
+ if (hasUsername || botStatus == 0 || botStatus == 2) {
+ toHighlight += '@' + user->username;
+ }
+ if (true || _parent->chat() || botStatus == 0 || botStatus == 2) {
+ user->photo->load();
+ p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize));
+ }
+
+ int32 addleft = 0, widthleft = mentionwidth;
+ QString first = (_parent->filter().size() < 2) ? QString() : ('/' + toHighlight.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('/' + toHighlight) : toHighlight.mid(_parent->filter().size() - 1);
+ int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second);
+ if (widthleft < firstwidth + secondwidth) {
+ if (widthleft < firstwidth + st::mentionFont->elidew) {
+ first = st::mentionFont->elided(first + second, widthleft);
+ second = QString();
+ } else {
+ second = st::mentionFont->elided(second, widthleft - firstwidth);
+ }
+ }
+ p.setFont(st::mentionFont->f);
+ if (!first.isEmpty()) {
+ p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
+ p.drawText(mentionleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
+ }
+ if (!second.isEmpty()) {
+ p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
+ p.drawText(mentionleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
+ }
+ addleft += firstwidth + secondwidth + st::mentionPadding.left();
+ widthleft -= firstwidth + secondwidth + st::mentionPadding.left();
+ if (widthleft > st::mentionFont->elidew && !command->descriptionText().isEmpty()) {
+ p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
+ command->descriptionText().drawElided(p, mentionleft + addleft, i * st::mentionHeight + st::mentionTop, widthleft, 1, style::al_right);
}
}
- p.setFont(st::mentionFont->f);
- if (!first.isEmpty()) {
- p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p);
- p.drawText(mentionleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first);
- }
- if (!second.isEmpty()) {
- p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
- p.drawText(mentionleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
- }
- addleft += firstwidth + secondwidth + st::mentionPadding.left();
- widthleft -= firstwidth + secondwidth + st::mentionPadding.left();
- if (widthleft > st::mentionFont->elidew && !command->descriptionText().isEmpty()) {
- p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p);
- command->descriptionText().drawElided(p, mentionleft + addleft, i * st::mentionHeight + st::mentionTop, widthleft, 1, style::al_right);
- }
}
+ p.fillRect(cWideMode() ? st::lineWidth : 0, _parent->innerBottom() - st::lineWidth, width() - (cWideMode() ? st::lineWidth : 0), st::lineWidth, st::shadowColor->b);
}
-
p.fillRect(cWideMode() ? st::lineWidth : 0, _parent->innerTop(), width() - (cWideMode() ? st::lineWidth : 0), st::lineWidth, st::shadowColor->b);
- p.fillRect(cWideMode() ? st::lineWidth : 0, _parent->innerBottom() - st::lineWidth, width() - (cWideMode() ? st::lineWidth : 0), st::lineWidth, st::shadowColor->b);
+}
+
+void MentionsInner::resizeEvent(QResizeEvent *e) {
+ _stickersPerRow = qMax(1, int32(width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()));
}
void MentionsInner::mouseMoveEvent(QMouseEvent *e) {
@@ -2843,44 +4030,66 @@ void MentionsInner::mouseMoveEvent(QMouseEvent *e) {
void MentionsInner::clearSel() {
_mouseSel = _overDelete = false;
- setSel((_rows->isEmpty() && _crows->isEmpty() && _hrows->isEmpty()) ? -1 : 0);
+ setSel((_mrows->isEmpty() && _brows->isEmpty() && _hrows->isEmpty()) ? -1 : 0);
}
-bool MentionsInner::moveSel(int direction) {
+bool MentionsInner::moveSel(int key) {
_mouseSel = false;
- int32 maxSel = (_rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size());
+ int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? (_brows->isEmpty() ? _srows->size() : _brows->size()) : _hrows->size()) : _mrows->size());
+ int32 direction = (key == Qt::Key_Up) ? -1 : (key == Qt::Key_Down ? 1 : 0);
+ if (!_srows->isEmpty()) {
+ if (key == Qt::Key_Left) {
+ direction = -1;
+ } else if (key == Qt::Key_Right) {
+ direction = 1;
+ } else {
+ direction *= _stickersPerRow;
+ }
+ }
if (_sel >= maxSel || _sel < 0) {
- if (direction < 0) {
+ if (direction < -1) {
+ setSel(((maxSel - 1) / _stickersPerRow) * _stickersPerRow, true);
+ } else if (direction < 0) {
setSel(maxSel - 1, true);
} else {
setSel(0, true);
}
return (_sel >= 0 && _sel < maxSel);
}
- setSel((_sel + direction >= maxSel) ? -1 : (_sel + direction), true);
+ setSel((_sel + direction >= maxSel || _sel + direction < 0) ? -1 : (_sel + direction), true);
return true;
}
bool MentionsInner::select() {
- QString sel = getSelected();
- if (!sel.isEmpty()) {
- emit chosen(sel);
- return true;
+ if (!_srows->isEmpty()) {
+ if (_sel >= 0 && _sel < _srows->size()) {
+ emit selected(_srows->at(_sel));
+ }
+ } else {
+ QString sel = getSelected();
+ if (!sel.isEmpty()) {
+ emit chosen(sel);
+ return true;
+ }
}
return false;
}
+void MentionsInner::setRecentInlineBotsInRows(int32 bots) {
+ _recentInlineBotsInRows = bots;
+}
+
QString MentionsInner::getSelected() const {
- int32 maxSel = (_rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size());
+ int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size());
if (_sel >= 0 && _sel < maxSel) {
QString result;
- if (!_rows->isEmpty()) {
- result = '@' + _rows->at(_sel)->username;
+ if (!_mrows->isEmpty()) {
+ result = '@' + _mrows->at(_sel)->username;
} else if (!_hrows->isEmpty()) {
result = '#' + _hrows->at(_sel);
} else {
- UserData *user = _crows->at(_sel).first;
- const BotCommand *command(_crows->at(_sel).second);
+ UserData *user = _brows->at(_sel).first;
+ const BotCommand *command(_brows->at(_sel).second);
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (botStatus == 0 || botStatus == 2 || _parent->filter().indexOf('@') > 1) {
result = '/' + command->command + '@' + user->username;
@@ -2898,20 +4107,32 @@ void MentionsInner::mousePressEvent(QMouseEvent *e) {
_mouseSel = true;
onUpdateSelected(true);
if (e->button() == Qt::LeftButton) {
- if (_overDelete && _sel >= 0 && _sel < _hrows->size()) {
+ if (_overDelete && _sel >= 0 && _sel < (_mrows->isEmpty() ? _hrows->size() : _recentInlineBotsInRows)) {
_mousePos = mapToGlobal(e->pos());
-
- QString toRemove = _hrows->at(_sel);
- RecentHashtagPack recent(cRecentWriteHashtags());
- for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
- if (i->first == toRemove) {
- i = recent.erase(i);
- } else {
- ++i;
+ bool removed = false;
+ if (_mrows->isEmpty()) {
+ QString toRemove = _hrows->at(_sel);
+ RecentHashtagPack &recent(cRefRecentWriteHashtags());
+ for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
+ if (i->first == toRemove) {
+ i = recent.erase(i);
+ removed = true;
+ } else {
+ ++i;
+ }
+ }
+ } else {
+ UserData *toRemove = _mrows->at(_sel);
+ RecentInlineBots &recent(cRefRecentInlineBots());
+ int32 index = recent.indexOf(toRemove);
+ if (index >= 0) {
+ recent.remove(index);
+ removed = true;
}
}
- cSetRecentWriteHashtags(recent);
- Local::writeRecentHashtags();
+ if (removed) {
+ Local::writeRecentHashtagsAndBots();
+ }
_parent->updateFiltered();
_mouseSel = true;
@@ -2935,21 +4156,51 @@ void MentionsInner::leaveEvent(QEvent *e) {
}
}
+void MentionsInner::updateSelectedRow() {
+ if (_sel >= 0) {
+ if (_srows->isEmpty()) {
+ update(0, _sel * st::mentionHeight, width(), st::mentionHeight);
+ } else {
+ int32 row = _sel / _stickersPerRow, col = _sel % _stickersPerRow;
+ update(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height(), st::stickerPanSize.width(), st::stickerPanSize.height());
+ }
+ }
+}
+
void MentionsInner::setSel(int sel, bool scroll) {
- if (_sel >= 0) update(0, _sel * st::mentionHeight, width(), st::mentionHeight);
+ updateSelectedRow();
_sel = sel;
- if (_sel >= 0) update(0, _sel * st::mentionHeight, width(), st::mentionHeight);
- int32 maxSel = _rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size();
- if (scroll && _sel >= 0 && _sel < maxSel) emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
+ updateSelectedRow();
+
+ if (scroll && _sel >= 0) {
+ if (_srows->isEmpty()) {
+ emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
+ } else {
+ int32 row = _sel / _stickersPerRow;
+ emit mustScrollTo(st::stickerPanPadding + row * st::stickerPanSize.height(), st::stickerPanPadding + (row + 1) * st::stickerPanSize.height());
+ }
+ }
}
void MentionsInner::onUpdateSelected(bool force) {
QPoint mouse(mapFromGlobal(_mousePos));
if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
- int w = width(), mouseY = mouse.y();
- _overDelete = _rows->isEmpty() && (mouse.x() >= w - st::mentionHeight);
- int32 sel = mouseY / int32(st::mentionHeight), maxSel = _rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size();
+ int32 sel = -1, maxSel = 0;
+ if (!_srows->isEmpty()) {
+ int32 rows = rowscount(_srows->size(), _stickersPerRow);
+ int32 row = (mouse.y() >= st::stickerPanPadding) ? ((mouse.y() - st::stickerPanPadding) / st::stickerPanSize.height()) : -1;
+ int32 col = (mouse.x() >= st::stickerPanPadding) ? ((mouse.x() - st::stickerPanPadding) / st::stickerPanSize.width()) : -1;
+ if (row >= 0 && col >= 0) {
+ sel = row * _stickersPerRow + col;
+ }
+ maxSel = _srows->size();
+ _overDelete = false;
+ } else {
+ sel = mouse.y() / int32(st::mentionHeight);
+ maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
+ _overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= width() - st::mentionHeight) : false;
+ }
if (sel < 0 || sel >= maxSel) {
sel = -1;
}
@@ -2966,11 +4217,20 @@ void MentionsInner::onParentGeometryChanged() {
}
}
-MentionsDropdown::MentionsDropdown(QWidget *parent) : TWidget(parent),
-_scroll(this, st::mentionScroll), _inner(this, &_rows, &_hrows, &_crows), _chat(0), _user(0), _channel(0), _hiding(false), a_opacity(0), _shadow(st::dropdownDef.shadow) {
+MentionsDropdown::MentionsDropdown(QWidget *parent) : TWidget(parent)
+, _scroll(this, st::mentionScroll)
+, _inner(this, &_mrows, &_hrows, &_brows, &_srows)
+, _chat(0)
+, _user(0)
+, _channel(0)
+, _hiding(false)
+, a_opacity(0)
+, _a_appearance(animation(this, &MentionsDropdown::step_appearance))
+, _shadow(st::dropdownDef.shadow) {
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
connect(&_inner, SIGNAL(chosen(QString)), this, SIGNAL(chosen(QString)));
+ connect(&_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*)));
connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int)));
connect(App::wnd(), SIGNAL(imageLoaded()), &_inner, SLOT(update()));
@@ -2988,88 +4248,152 @@ _scroll(this, st::mentionScroll), _inner(this, &_rows, &_hrows, &_crows), _chat(
connect(&_scroll, SIGNAL(geometryChanged()), &_inner, SLOT(onParentGeometryChanged()));
connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(onUpdateSelected()));
-
- if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
- connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
- }
}
void MentionsDropdown::paintEvent(QPaintEvent *e) {
- QPainter p(this);
+ Painter p(this);
- if (animating()) {
+ if (_a_appearance.animating()) {
p.setOpacity(a_opacity.current());
p.drawPixmap(0, 0, _cache);
return;
}
- p.fillRect(rect(), st::white->b);
-
+ p.fillRect(rect(), st::white);
}
-void MentionsDropdown::showFiltered(PeerData *peer, QString start) {
+void MentionsDropdown::showFiltered(PeerData *peer, QString query, bool start) {
_chat = peer->asChat();
_user = peer->asUser();
_channel = peer->asChannel();
- start = start.toLower();
- bool toDown = (_filter != start);
- if (toDown) {
- _filter = start;
+ if (query.isEmpty()) {
+ rowsUpdated(MentionRows(), HashtagRows(), BotCommandRows(), _srows, false);
+ return;
}
- updateFiltered(toDown);
+ _emoji = EmojiPtr();
+
+ query = query.toLower();
+ bool resetScroll = (_filter != query);
+ if (resetScroll) {
+ _filter = query;
+ }
+ _addInlineBots = start;
+
+ updateFiltered(resetScroll);
}
-bool MentionsDropdown::clearFilteredCommands() {
- if (_crows.isEmpty()) return false;
- _crows.clear();
+void MentionsDropdown::showStickers(EmojiPtr emoji) {
+ bool resetScroll = (_emoji != emoji);
+ _emoji = emoji;
+ if (!emoji) {
+ rowsUpdated(_mrows, _hrows, _brows, StickerPack(), false);
+ return;
+ }
+
+ _chat = 0;
+ _user = 0;
+ _channel = 0;
+
+ updateFiltered(resetScroll);
+}
+
+bool MentionsDropdown::clearFilteredBotCommands() {
+ if (_brows.isEmpty()) return false;
+ _brows.clear();
return true;
}
-void MentionsDropdown::updateFiltered(bool toDown) {
- int32 now = unixtime();
- MentionRows rows;
+void MentionsDropdown::updateFiltered(bool resetScroll) {
+ int32 now = unixtime(), recentInlineBots = 0;
+ MentionRows mrows;
HashtagRows hrows;
- BotCommandRows crows;
- if (_filter.at(0) == '@' && _chat) {
- QMultiMap ordered;
- rows.reserve(_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size());
- if (_chat->noParticipantInfo()) {
- if (App::api()) App::api()->requestFullPeer(_chat);
- } else if (!_chat->participants.isEmpty()) {
- for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
- UserData *user = i.key();
- if (user->username.isEmpty()) continue;
- if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
- ordered.insertMulti(App::onlineForSort(user, now), user);
+ BotCommandRows brows;
+ StickerPack srows;
+ if (_emoji) {
+ QMap setsToRequest;
+ StickerSets &sets(cRefStickerSets());
+ const StickerSetsOrder &order(cStickerSetsOrder());
+ for (int32 i = 0, l = order.size(); i < l; ++i) {
+ StickerSets::iterator it = sets.find(order.at(i));
+ if (it != sets.cend()) {
+ if (it->emoji.isEmpty()) {
+ setsToRequest.insert(it->id, it->access);
+ it->flags |= MTPDstickerSet_flag_NOT_LOADED;
+ } else {
+ StickersByEmojiMap::const_iterator i = it->emoji.constFind(emojiGetNoColor(_emoji));
+ if (i != it->emoji.cend()) {
+ srows += *i;
+ }
+ }
}
}
- for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
- UserData *user = *i;
- if (user->username.isEmpty()) continue;
- if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
- rows.push_back(user);
- if (!ordered.isEmpty()) {
- ordered.remove(App::onlineForSort(user, now), user);
+ if (!setsToRequest.isEmpty() && App::api()) {
+ for (QMap::const_iterator i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) {
+ App::api()->scheduleStickerSetRequest(i.key(), i.value());
}
+ App::api()->requestStickerSets();
}
- if (!ordered.isEmpty()) {
- for (QMultiMap::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
- --i;
- rows.push_back(i.value());
+ } else if (_filter.at(0) == '@') {
+ if (_chat) {
+ mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
+ } else if (_channel && _channel->isMegagroup()) {
+ if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
+ } else {
+ mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + _channel->mgInfo->lastParticipants.size());
}
+ } else if (_addInlineBots) {
+ mrows.reserve(cRecentInlineBots().size());
}
- } else if (_filter.at(0) == '@' && _channel && _channel->isMegagroup()) {
- QMultiMap ordered;
- if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
- if (App::api()) App::api()->requestLastParticipants(_channel);
- } else {
- rows.reserve(_channel->mgInfo->lastParticipants.size());
- for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) {
+ if (_addInlineBots) {
+ for (RecentInlineBots::const_iterator i = cRecentInlineBots().cbegin(), e = cRecentInlineBots().cend(); i != e; ++i) {
UserData *user = *i;
if (user->username.isEmpty()) continue;
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
- rows.push_back(user);
+ mrows.push_back(user);
+ ++recentInlineBots;
+ }
+ }
+ if (_chat) {
+ QMultiMap ordered;
+ mrows.reserve(mrows.size() + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
+ if (_chat->noParticipantInfo()) {
+ if (App::api()) App::api()->requestFullPeer(_chat);
+ } else if (!_chat->participants.isEmpty()) {
+ for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
+ UserData *user = i.key();
+ if (user->username.isEmpty()) continue;
+ if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
+ ordered.insertMulti(App::onlineForSort(user, now), user);
+ }
+ }
+ for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
+ UserData *user = *i;
+ if (user->username.isEmpty()) continue;
+ if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
+ mrows.push_back(user);
+ if (!ordered.isEmpty()) {
+ ordered.remove(App::onlineForSort(user, now), user);
+ }
+ }
+ if (!ordered.isEmpty()) {
+ for (QMultiMap::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
+ --i;
+ mrows.push_back(i.value());
+ }
+ }
+ } else if (_channel && _channel->isMegagroup()) {
+ QMultiMap ordered;
+ if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
+ if (App::api()) App::api()->requestLastParticipants(_channel);
+ } else {
+ mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size());
+ for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) {
+ UserData *user = *i;
+ if (user->username.isEmpty()) continue;
+ if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
+ mrows.push_back(user);
+ }
}
}
} else if (_filter.at(0) == '#') {
@@ -3115,7 +4439,7 @@ void MentionsDropdown::updateFiltered(bool toDown) {
}
}
if (cnt) {
- crows.reserve(cnt);
+ brows.reserve(cnt);
int32 botStatus = _chat ? _chat->botStatus : ((_channel && _channel->isMegagroup()) ? _channel->mgInfo->botStatus : -1);
if (_chat) {
for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
@@ -3130,7 +4454,7 @@ void MentionsDropdown::updateFiltered(bool toDown) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
if (!toFilter.startsWith(_filter.midRef(1), Qt::CaseInsensitive)/* || toFilter.size() + 1 == _filter.size()*/) continue;
}
- crows.push_back(qMakePair(user, &user->botInfo->commands.at(j)));
+ brows.push_back(qMakePair(user, &user->botInfo->commands.at(j)));
}
}
}
@@ -3142,29 +4466,37 @@ void MentionsDropdown::updateFiltered(bool toDown) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
if (!toFilter.startsWith(_filter.midRef(1), Qt::CaseInsensitive)/* || toFilter.size() + 1 == _filter.size()*/) continue;
}
- crows.push_back(qMakePair(user, &user->botInfo->commands.at(j)));
+ brows.push_back(qMakePair(user, &user->botInfo->commands.at(j)));
}
}
}
}
}
- if (rows.isEmpty() && hrows.isEmpty() && crows.isEmpty()) {
+ rowsUpdated(mrows, hrows, brows, srows, resetScroll);
+ _inner.setRecentInlineBotsInRows(recentInlineBots);
+}
+
+void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, const StickerPack &srows, bool resetScroll) {
+ if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.isEmpty()) {
if (!isHidden()) {
hideStart();
}
- _rows.clear();
+ _mrows.clear();
_hrows.clear();
- _crows.clear();
+ _brows.clear();
+ _srows.clear();
} else {
- _rows = rows;
+ _mrows = mrows;
_hrows = hrows;
- _crows = crows;
+ _brows = brows;
+ _srows = srows;
+
bool hidden = _hiding || isHidden();
if (hidden) {
show();
_scroll.show();
}
- recount(toDown);
+ recount(resetScroll);
if (hidden) {
hide();
showStart();
@@ -3174,36 +4506,42 @@ void MentionsDropdown::updateFiltered(bool toDown) {
void MentionsDropdown::setBoundings(QRect boundings) {
_boundings = boundings;
- resize(_boundings.width(), height());
- _scroll.resize(size());
- _inner.resize(width(), _inner.height());
recount();
}
-void MentionsDropdown::recount(bool toDown) {
- int32 h = (_rows.isEmpty() ? (_hrows.isEmpty() ? _crows.size() : _hrows.size()) : _rows.size()) * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst;
-
- if (_inner.height() != h) {
-// st += h - _inner.height();
- _inner.resize(width(), h);
+void MentionsDropdown::recount(bool resetScroll) {
+ int32 h = 0, oldst = _scroll.scrollTop(), st = oldst, maxh = 4.5 * st::mentionHeight;
+ if (!_srows.isEmpty()) {
+ int32 stickersPerRow = qMax(1, int32(_boundings.width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()));
+ int32 rows = rowscount(_srows.size(), stickersPerRow);
+ h = st::stickerPanPadding + rows * st::stickerPanSize.height();
+ } else if (!_mrows.isEmpty()) {
+ h = _mrows.size() * st::mentionHeight;
+ } else if (!_hrows.isEmpty()) {
+ h = _hrows.size() * st::mentionHeight;
+ } else if (!_brows.isEmpty()) {
+ h = _brows.size() * st::mentionHeight;
+ }
+
+ if (_inner.width() != _boundings.width() || _inner.height() != h) {
+ _inner.resize(_boundings.width(), h);
}
if (h > _boundings.height()) h = _boundings.height();
- if (h > 4.5 * st::mentionHeight) h = 4.5 * st::mentionHeight;
- if (height() != h) {
-// st += _scroll.height() - h;
- setGeometry(0, _boundings.height() - h, width(), h);
- _scroll.resize(width(), h);
+ if (h > maxh) h = maxh;
+ if (width() != _boundings.width() || height() != h) {
+ setGeometry(0, _boundings.height() - h, _boundings.width(), h);
+ _scroll.resize(_boundings.width(), h);
} else if (y() != _boundings.height() - h) {
move(0, _boundings.height() - h);
}
- if (toDown) st = 0;// _scroll.scrollTopMax();
+ if (resetScroll) st = 0;
if (st != oldst) _scroll.scrollToY(st);
- if (toDown) _inner.clearSel();
+ if (resetScroll) _inner.clearSel();
}
void MentionsDropdown::fastHide() {
- if (animating()) {
- anim::stop(this);
+ if (_a_appearance.animating()) {
+ _a_appearance.stop();
}
a_opacity = anim::fvalue(0, 0);
_hideTimer.stop();
@@ -3220,7 +4558,7 @@ void MentionsDropdown::hideStart() {
_hiding = true;
a_opacity.start(0);
setAttribute(Qt::WA_OpaquePaintEvent, false);
- anim::start(this);
+ _a_appearance.start();
}
}
@@ -3244,13 +4582,13 @@ void MentionsDropdown::showStart() {
show();
a_opacity.start(1);
setAttribute(Qt::WA_OpaquePaintEvent, false);
- anim::start(this);
+ _a_appearance.start();
}
-bool MentionsDropdown::animStep(float64 ms) {
+void MentionsDropdown::step_appearance(float64 ms, bool timer) {
float64 dt = ms / st::dropdownDef.duration;
- bool res = true;
if (dt >= 1) {
+ _a_appearance.stop();
a_opacity.finish();
_cache = QPixmap();
setAttribute(Qt::WA_OpaquePaintEvent);
@@ -3260,12 +4598,10 @@ bool MentionsDropdown::animStep(float64 ms) {
_scroll.show();
_inner.clearSel();
}
- res = false;
} else {
a_opacity.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
const QString &MentionsDropdown::filter() const {
@@ -3300,11 +4636,8 @@ bool MentionsDropdown::eventFilter(QObject *obj, QEvent *e) {
if (isHidden()) return QWidget::eventFilter(obj, e);
if (e->type() == QEvent::KeyPress) {
QKeyEvent *ev = static_cast(e);
- if (ev->key() == Qt::Key_Up) {
- _inner.moveSel(-1);
- return true;
- } else if (ev->key() == Qt::Key_Down) {
- return _inner.moveSel(1);
+ if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down || (!_srows.isEmpty() && (ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right))) {
+ return _inner.moveSel(ev->key());
} else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
return _inner.select();
}
diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h
index 9a64fce257..36146697b4 100644
--- a/Telegram/SourceFiles/dropdown.h
+++ b/Telegram/SourceFiles/dropdown.h
@@ -23,7 +23,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "gui/twidget.h"
#include "gui/boxshadow.h"
-class Dropdown : public TWidget, public Animated {
+class Dropdown : public TWidget {
Q_OBJECT
public:
@@ -46,12 +46,12 @@ public:
void fastHide();
void ignoreShow(bool ignore = true);
- bool animStep(float64 ms);
+ void step_appearance(float64 ms, bool timer);
bool eventFilter(QObject *obj, QEvent *e);
bool overlaps(const QRect &globalRect) {
- if (isHidden() || animating()) return false;
+ if (isHidden() || _a_appearance.animating()) return false;
return QRect(_st.padding.left(),
_st.padding.top(),
@@ -91,6 +91,7 @@ private:
bool _hiding;
anim::fvalue a_opacity;
+ Animation _a_appearance;
QTimer _hideTimer;
@@ -98,7 +99,7 @@ private:
};
-class DragArea : public TWidget, public Animated {
+class DragArea : public TWidget {
Q_OBJECT
public:
@@ -119,10 +120,10 @@ public:
void fastHide();
- bool animStep(float64 ms);
+ void step_appearance(float64 ms, bool timer);
bool overlaps(const QRect &globalRect) {
- if (isHidden() || animating()) return false;
+ if (isHidden() || _a_appearance.animating()) return false;
return QRect(st::dragPadding.left(),
st::dragPadding.top(),
@@ -148,6 +149,7 @@ private:
anim::fvalue a_opacity;
anim::cvalue a_color;
+ Animation _a_appearance;
BoxShadow _shadow;
@@ -158,7 +160,7 @@ private:
class EmojiPanel;
static const int EmojiColorsCount = 5;
-class EmojiColorPicker : public TWidget, public Animated {
+class EmojiColorPicker : public TWidget {
Q_OBJECT
public:
@@ -174,7 +176,8 @@ public:
void mouseReleaseEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
- bool animStep(float64 ms);
+ void step_appearance(float64 ms, bool timer);
+ void step_selected(uint64 ms, bool timer);
void showStart();
void clearSelection(bool fast = false);
@@ -200,6 +203,7 @@ private:
typedef QMap EmojiAnimations; // index - showing, -index - hiding
EmojiAnimations _emojiAnimations;
+ Animation _a_selected;
float64 _hovers[EmojiColorsCount + 1];
@@ -210,6 +214,7 @@ private:
QPixmap _cache;
anim::fvalue a_opacity;
+ Animation _a_appearance;
QTimer _hideTimer;
@@ -217,7 +222,7 @@ private:
};
-class EmojiPanInner : public TWidget, public Animated {
+class EmojiPanInner : public TWidget {
Q_OBJECT
public:
@@ -234,7 +239,7 @@ public:
void leaveToChildEvent(QEvent *e);
void enterFromChildEvent(QEvent *e);
- bool animStep(float64 ms);
+ void step_selected(uint64 ms, bool timer);
void hideFinish();
void showEmojiPack(DBIEmojiTab packIndex);
@@ -249,11 +254,10 @@ public:
void fillPanels(QVector &panels);
void refreshPanels(QVector &panels);
-
+
public slots:
void updateSelected();
- void onSaveConfig();
void onShowPicker();
void onPickerHidden();
@@ -271,6 +275,7 @@ signals:
void disableScroll(bool dis);
void needRefreshPanels();
+ void saveConfigDelayed(int32 delay);
private:
@@ -283,6 +288,7 @@ private:
typedef QMap Animations; // index - showing, -index - hiding
Animations _animations;
+ Animation _a_selected;
int32 _top, _counts[emojiTabCount];
@@ -294,14 +300,12 @@ private:
int32 _selected, _pressedSel, _pickerSel;
QPoint _lastMousePos;
- QTimer _saveConfigTimer;
-
EmojiColorPicker _picker;
QTimer _showPickerTimer;
};
struct StickerIcon {
- StickerIcon() : setId(RecentStickerSetId), sticker(0), pixw(0), pixh(0) {
+ StickerIcon(uint64 setId) : setId(setId), sticker(0), pixw(0), pixh(0) {
}
StickerIcon(uint64 setId, DocumentData *sticker, int32 pixw, int32 pixh) : setId(setId), sticker(sticker), pixw(pixw), pixh(pixh) {
}
@@ -310,7 +314,7 @@ struct StickerIcon {
int32 pixw, pixh;
};
-class StickerPanInner : public TWidget, public Animated {
+class StickerPanInner : public TWidget {
Q_OBJECT
public:
@@ -327,16 +331,26 @@ public:
void leaveToChildEvent(QEvent *e);
void enterFromChildEvent(QEvent *e);
- bool animStep(float64 ms);
+ void step_selected(uint64 ms, bool timer);
+ void hideFinish(bool completely);
+ void showFinish();
void showStickerSet(uint64 setId);
+ void updateShowingSavedGifs();
+ bool showSectionIcons() const;
void clearSelection(bool fast = false);
void refreshStickers();
- void refreshRecent(bool resize = true);
+ void refreshRecentStickers(bool resize = true);
+ void refreshSavedGifs();
+ int32 refreshInlineRows(UserData *bot, const InlineResults &results, bool resultsDeleted);
+ void refreshRecent();
+ void inlineBotChanged();
+ void hideInlineRowsPanel();
+ void clearInlineRowsPanel();
- void fillIcons(QVector &icons);
+ void fillIcons(QList &icons);
void fillPanels(QVector &panels);
void refreshPanels(QVector &panels);
@@ -345,37 +359,63 @@ public:
uint64 currentSet(int yOffset) const;
+ void ui_repaintInlineItem(const LayoutInlineItem *layout);
+ bool ui_isInlineItemVisible(const LayoutInlineItem *layout);
+ bool ui_isInlineItemBeingChosen();
+
+ bool inlineResultsShown() const {
+ return _showingInlineItems && !_showingSavedGifs;
+ }
+ int32 countHeight(bool plain = false);
+
+ ~StickerPanInner() {
+ clearInlineRows(true);
+ deleteUnusedGifLayouts();
+ deleteUnusedInlineLayouts();
+ }
+
public slots:
void updateSelected();
void onSettings();
void onPreview();
+ void onUpdateInlineItems();
signals:
void selected(DocumentData *sticker);
+ void selected(PhotoData *photo);
+ void selected(InlineResult *result, UserData *bot);
+
void removing(quint64 setId);
void refreshIcons();
+ void emptyInlineRows();
void switchToEmoji();
void scrollToY(int y);
+ void scrollUpdated();
void disableScroll(bool dis);
void needRefreshPanels();
+ void saveConfigDelayed(int32 delay);
+
private:
+ void paintInlineItems(Painter &p, const QRect &r);
+ void paintStickers(Painter &p, const QRect &r);
+
int32 _maxHeight;
void appendSet(uint64 setId);
- int32 countHeight();
void selectEmoji(EmojiPtr emoji);
QRect stickerRect(int tab, int sel);
typedef QMap Animations; // index - showing, -index - hiding
Animations _animations;
+ Animation _a_selected;
int32 _top;
@@ -391,8 +431,45 @@ private:
QList _sets;
QList _custom;
+ bool _showingSavedGifs, _showingInlineItems;
+ bool _setGifCommand;
+ UserData *_inlineBot;
+ QString _inlineBotTitle;
+ uint64 _lastScrolled;
+ QTimer _updateInlineItems;
+ bool _inlineWithThumb;
+
+ typedef QVector InlineItems;
+ struct InlineRow {
+ InlineRow() : height(0) {
+ }
+ int32 height;
+ InlineItems items;
+ };
+ typedef QVector InlineRows;
+ InlineRows _inlineRows;
+ void clearInlineRows(bool resultsDeleted);
+
+ typedef QMap GifLayouts;
+ GifLayouts _gifLayouts;
+ LayoutInlineGif *layoutPrepareSavedGif(DocumentData *doc, int32 position);
+
+ typedef QMap InlineLayouts;
+ InlineLayouts _inlineLayouts;
+ LayoutInlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position);
+
+ bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth);
+ bool inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force = false);
+
+ InlineRow &layoutInlineRow(InlineRow &row, int32 sumWidth = 0);
+ void deleteUnusedGifLayouts();
+
+ void deleteUnusedInlineLayouts();
+
+ int32 validateExistingInlineRows(const InlineResults &results);
int32 _selected, _pressedSel;
QPoint _lastMousePos;
+ TextLinkPtr _linkOver, _linkDown;
LinkButton _settings;
@@ -445,6 +522,7 @@ public:
EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji
void paintEvent(QPaintEvent *e);
+ void updateText(const QString &inlineBotUsername = QString());
protected:
@@ -454,7 +532,7 @@ protected:
};
-class EmojiPan : public TWidget, public Animated {
+class EmojiPan : public TWidget, public RPCSender {
Q_OBJECT
public:
@@ -464,6 +542,8 @@ public:
void setMaxHeight(int32 h);
void paintEvent(QPaintEvent *e);
+ void moveBottom(int32 bottom, bool force = false);
+
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void otherEnter();
@@ -480,13 +560,16 @@ public:
return _hiding || _hideTimer.isActive();
}
- bool animStep(float64 ms);
-
- bool iconAnim(float64 ms);
+ void step_appearance(float64 ms, bool timer);
+ void step_slide(float64 ms, bool timer);
+ void step_icons(uint64 ms, bool timer);
bool eventFilter(QObject *obj, QEvent *e);
void stickersInstalled(uint64 setId);
+ void queryInlineBot(UserData *bot, QString query);
+ void clearInlineBot();
+
bool overlaps(const QRect &globalRect) {
if (isHidden() || !_cache.isNull()) return false;
@@ -497,9 +580,19 @@ public:
).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
+ void ui_repaintInlineItem(const LayoutInlineItem *layout);
+ bool ui_isInlineItemVisible(const LayoutInlineItem *layout);
+ bool ui_isInlineItemBeingChosen();
+
+ bool inlineResultsShown() const {
+ return s_inner.inlineResultsShown();
+ }
+ void notify_automaticLoadSettingsChangedGif();
+
public slots:
void refreshStickers();
+ void refreshSavedGifs();
void hideStart();
void hideFinish();
@@ -518,18 +611,32 @@ public slots:
void onRefreshIcons();
void onRefreshPanels();
+ void onSaveConfig();
+ void onSaveConfigDelayed(int32 delay);
+
+ void onInlineRequest();
+ void onEmptyInlineRows();
+
signals:
void emojiSelected(EmojiPtr emoji);
void stickerSelected(DocumentData *sticker);
+ void photoSelected(PhotoData *photo);
+ void inlineResultSelected(InlineResult *result, UserData *bot);
+
void updateStickers();
private:
- int32 _maxHeight;
+ void validateSelectedIcon(bool animated = false);
+
+ int32 _maxHeight, _contentMaxHeight, _contentHeight, _contentHeightEmoji, _contentHeightStickers;
bool _horizontal;
+ void updateContentHeight();
void leaveToChildEvent(QEvent *e);
+ void hideAnimated();
+ void prepareShowHideCache();
void updateSelected();
void updateIcons();
@@ -542,35 +649,36 @@ private:
bool _noTabUpdate;
- int32 _width, _height;
+ int32 _width, _height, _bottom;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
+ Animation _a_appearance;
QTimer _hideTimer;
BoxShadow _shadow;
FlatRadiobutton _recent, _people, _nature, _food, _activity, _travel, _objects, _symbols;
- QVector _icons;
+ QList _icons;
QVector _iconHovers;
int32 _iconOver, _iconSel, _iconDown;
bool _iconsDragging;
typedef QMap Animations; // index - showing, -index - hiding
Animations _iconAnimations;
- Animation _iconAnim;
+ Animation _a_icons;
QPoint _iconsMousePos, _iconsMouseDown;
int32 _iconsLeft, _iconsTop;
int32 _iconsStartX, _iconsMax;
anim::ivalue _iconsX, _iconSelX;
uint64 _iconsStartAnim;
- bool _stickersShown;
+ bool _stickersShown, _shownFromInlineQuery;
QPixmap _fromCache, _toCache;
anim::ivalue a_fromCoord, a_toCoord;
anim::fvalue a_fromAlpha, a_toAlpha;
- uint64 _moveStart;
+ Animation _a_slide;
ScrollArea e_scroll;
EmojiPanInner e_inner;
@@ -583,6 +691,37 @@ private:
uint64 _removingSetId;
+ QTimer _saveConfigTimer;
+
+ // inline bots
+ struct InlineCacheEntry {
+ ~InlineCacheEntry() {
+ clearResults();
+ }
+ QString nextOffset;
+ InlineResults results;
+ void clearResults() {
+ for (int32 i = 0, l = results.size(); i < l; ++i) {
+ delete results.at(i);
+ }
+ results.clear();
+ }
+ };
+ typedef QMap InlineCache;
+ InlineCache _inlineCache;
+ QTimer _inlineRequestTimer;
+
+ void inlineBotChanged();
+ int32 showInlineRows(bool newResults);
+ bool hideOnNoInlineResults();
+ void recountContentMaxHeight();
+ bool refreshInlineRows(int32 *added = 0);
+ UserData *_inlineBot;
+ QString _inlineQuery, _inlineNextQuery, _inlineNextOffset;
+ mtpRequestId _inlineRequestId;
+ void inlineResultsDone(const MTPmessages_BotResults &result);
+ bool inlineResultsFail(const RPCError &error);
+
};
typedef QList MentionRows;
@@ -590,14 +729,15 @@ typedef QList HashtagRows;
typedef QList > BotCommandRows;
class MentionsDropdown;
-class MentionsInner : public QWidget {
+class MentionsInner : public TWidget {
Q_OBJECT
public:
- MentionsInner(MentionsDropdown *parent, MentionRows *rows, HashtagRows *hrows, BotCommandRows *crows);
+ MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerPack *srows);
void paintEvent(QPaintEvent *e);
+ void resizeEvent(QResizeEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
@@ -606,14 +746,17 @@ public:
void mouseMoveEvent(QMouseEvent *e);
void clearSel();
- bool moveSel(int direction);
+ bool moveSel(int key);
bool select();
+ void setRecentInlineBotsInRows(int32 bots);
+
QString getSelected() const;
signals:
void chosen(QString mentionOrHashtag);
+ void selected(DocumentData *sticker);
void mustScrollTo(int scrollToTop, int scrollToBottom);
public slots:
@@ -623,12 +766,15 @@ public slots:
private:
+ void updateSelectedRow();
void setSel(int sel, bool scroll = false);
MentionsDropdown *_parent;
- MentionRows *_rows;
+ MentionRows *_mrows;
HashtagRows *_hrows;
- BotCommandRows *_crows;
+ BotCommandRows *_brows;
+ StickerPack *_srows;
+ int32 _stickersPerRow, _recentInlineBotsInRows;
int32 _sel;
bool _mouseSel;
QPoint _mousePos;
@@ -636,7 +782,7 @@ private:
bool _overDelete;
};
-class MentionsDropdown : public TWidget, public Animated {
+class MentionsDropdown : public TWidget {
Q_OBJECT
public:
@@ -647,12 +793,13 @@ public:
void fastHide();
- bool clearFilteredCommands();
- void showFiltered(PeerData *peer, QString start);
- void updateFiltered(bool toDown = false);
+ bool clearFilteredBotCommands();
+ void showFiltered(PeerData *peer, QString query, bool start);
+ void showStickers(EmojiPtr emoji);
+ void updateFiltered(bool resetScroll = false);
void setBoundings(QRect boundings);
- bool animStep(float64 ms);
+ void step_appearance(float64 ms, bool timer);
const QString &filter() const;
ChatData *chat() const;
@@ -665,6 +812,10 @@ public:
bool eventFilter(QObject *obj, QEvent *e);
QString getSelected() const;
+ bool stickersShown() const {
+ return !_srows.isEmpty();
+ }
+
bool overlaps(const QRect &globalRect) {
if (isHidden() || !testAttribute(Qt::WA_OpaquePaintEvent)) return false;
@@ -676,6 +827,7 @@ public:
signals:
void chosen(QString mentionOrHashtag);
+ void stickerSelected(DocumentData *sticker);
public slots:
@@ -686,12 +838,15 @@ public slots:
private:
- void recount(bool toDown = false);
+ void recount(bool resetScroll = false);
QPixmap _cache;
- MentionRows _rows;
+ MentionRows _mrows;
HashtagRows _hrows;
- BotCommandRows _crows;
+ BotCommandRows _brows;
+ StickerPack _srows;
+
+ void rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, const StickerPack &srows, bool resetScroll);
ScrollArea _scroll;
MentionsInner _inner;
@@ -699,13 +854,16 @@ private:
ChatData *_chat;
UserData *_user;
ChannelData *_channel;
+ EmojiPtr _emoji;
QString _filter;
QRect _boundings;
+ bool _addInlineBots;
int32 _width, _height;
bool _hiding;
anim::fvalue a_opacity;
+ Animation _a_appearance;
QTimer _hideTimer;
diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp
index caa7fb9de1..683b3cd277 100644
--- a/Telegram/SourceFiles/facades.cpp
+++ b/Telegram/SourceFiles/facades.cpp
@@ -23,14 +23,17 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "window.h"
#include "mainwidget.h"
+#include "layerwidget.h"
+
namespace App {
void sendBotCommand(const QString &cmd, MsgId replyTo) {
if (MainWidget *m = main()) m->sendBotCommand(cmd, replyTo);
}
- void insertBotCommand(const QString &cmd) {
- if (MainWidget *m = main()) m->insertBotCommand(cmd);
+ bool insertBotCommand(const QString &cmd, bool specialGif) {
+ if (MainWidget *m = main()) return m->insertBotCommand(cmd, specialGif);
+ return false;
}
void searchByHashtag(const QString &tag, PeerData *inPeer) {
@@ -66,18 +69,6 @@ namespace App {
if (Window *win = wnd()) win->showSettings();
}
- void showLayer(LayeredWidget *widget, bool forceFast) {
- if (Window *w = wnd()) w->showLayer(widget, forceFast);
- }
-
- void replaceLayer(LayeredWidget *widget) {
- if (Window *w = wnd()) w->replaceLayer(widget);
- }
-
- void showLayerLast(LayeredWidget *widget) {
- if (Window *w = wnd()) w->showLayerLast(widget);
- }
-
}
namespace Ui {
@@ -90,20 +81,133 @@ namespace Ui {
if (MainWidget *m = App::main()) m->ui_hideStickerPreview();
}
+ void showLayer(LayeredWidget *box, ShowLayerOptions options) {
+ if (Window *w = App::wnd()) {
+ w->ui_showLayer(box, options);
+ } else {
+ delete box;
+ }
+ }
+
+ void hideLayer(bool fast) {
+ if (Window *w = App::wnd()) w->ui_showLayer(0, ShowLayerOptions(CloseOtherLayers) | (fast ? ForceFastShowLayer : AnimatedShowLayer));
+ }
+
+ bool isLayerShown() {
+ if (Window *w = App::wnd()) return w->ui_isLayerShown();
+ return false;
+ }
+
+ bool isMediaViewShown() {
+ if (Window *w = App::wnd()) return w->ui_isMediaViewShown();
+ return false;
+ }
+
+ bool isInlineItemBeingChosen() {
+ if (MainWidget *m = App::main()) return m->ui_isInlineItemBeingChosen();
+ return false;
+ }
+
+ void repaintHistoryItem(const HistoryItem *item) {
+ if (!item) return;
+ if (MainWidget *m = App::main()) m->ui_repaintHistoryItem(item);
+ }
+
+ void repaintInlineItem(const LayoutInlineItem *layout) {
+ if (!layout) return;
+ if (MainWidget *m = App::main()) m->ui_repaintInlineItem(layout);
+ }
+
+ bool isInlineItemVisible(const LayoutInlineItem *layout) {
+ if (MainWidget *m = App::main()) return m->ui_isInlineItemVisible(layout);
+ return false;
+ }
+
+ void showPeerHistory(const PeerId &peer, MsgId msgId, bool back) {
+ if (MainWidget *m = App::main()) m->ui_showPeerHistory(peer, msgId, back);
+ }
+
+ void showPeerHistoryAsync(const PeerId &peer, MsgId msgId) {
+ if (MainWidget *m = App::main()) {
+ QMetaObject::invokeMethod(m, "ui_showPeerHistoryAsync", Qt::QueuedConnection, Q_ARG(quint64, peer), Q_ARG(qint32, msgId));
+ }
+ }
+
}
namespace Notify {
void userIsBotChanged(UserData *user) {
- if (MainWidget *m = App::main()) m->notifyUserIsBotChanged(user);
+ if (MainWidget *m = App::main()) m->notify_userIsBotChanged(user);
+ }
+
+ void userIsContactChanged(UserData *user, bool fromThisApp) {
+ if (MainWidget *m = App::main()) m->notify_userIsContactChanged(user, fromThisApp);
}
void botCommandsChanged(UserData *user) {
- if (MainWidget *m = App::main()) m->notifyBotCommandsChanged(user);
+ if (MainWidget *m = App::main()) m->notify_botCommandsChanged(user);
+ }
+
+ void inlineBotRequesting(bool requesting) {
+ if (MainWidget *m = App::main()) m->notify_inlineBotRequesting(requesting);
}
void migrateUpdated(PeerData *peer) {
- if (MainWidget *m = App::main()) m->notifyMigrateUpdated(peer);
+ if (MainWidget *m = App::main()) m->notify_migrateUpdated(peer);
+ }
+
+ void clipStopperHidden(ClipStopperType type) {
+ if (MainWidget *m = App::main()) m->notify_clipStopperHidden(type);
+ }
+
+ void historyItemResized(const HistoryItem *item, bool scrollToIt) {
+ if (MainWidget *m = App::main()) m->notify_historyItemResized(item, scrollToIt);
+ }
+
+ void historyItemLayoutChanged(const HistoryItem *item) {
+ if (MainWidget *m = App::main()) m->notify_historyItemLayoutChanged(item);
+ }
+
+ void automaticLoadSettingsChangedGif() {
+ if (MainWidget *m = App::main()) m->notify_automaticLoadSettingsChangedGif();
}
}
+
+namespace Global {
+
+ struct Data {
+ uint64 LaunchId = 0;
+ };
+
+ Data *_data = 0;
+
+ Initializer::Initializer() {
+ initThirdParty();
+ _data = new Data();
+
+ memset_rand(&_data->LaunchId, sizeof(_data->LaunchId));
+ }
+
+ Initializer::~Initializer() {
+ deinitThirdParty();
+ }
+
+#define DefineGlobalReadOnly(Type, Name) const Type &Name() { \
+ t_assert_full(_data != 0, "_data is null in Global::" #Name, __FILE__, __LINE__); \
+ return _data->Name; \
+}
+#define DefineGlobal(Type, Name) DefineGlobalReadOnly(Type, Name) \
+void Set##Name(const Type &Name) { \
+ t_assert_full(_data != 0, "_data is null in Global::Set" #Name, __FILE__, __LINE__); \
+ _data->Name = Name; \
+} \
+Type &Ref##Name() { \
+ t_assert_full(_data != 0, "_data is null in Global::Ref" #Name, __FILE__, __LINE__); \
+ return _data->Name; \
+}
+
+ DefineGlobalReadOnly(uint64, LaunchId);
+
+};
diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h
index afbbb8e079..1a80278c75 100644
--- a/Telegram/SourceFiles/facades.h
+++ b/Telegram/SourceFiles/facades.h
@@ -25,7 +25,7 @@ class LayeredWidget;
namespace App {
void sendBotCommand(const QString &cmd, MsgId replyTo = 0);
- void insertBotCommand(const QString &cmd);
+ bool insertBotCommand(const QString &cmd, bool specialGif = false);
void searchByHashtag(const QString &tag, PeerData *inPeer);
void openPeerByName(const QString &username, bool toProfile = false, const QString &startToken = QString());
void joinGroupByHash(const QString &hash);
@@ -34,23 +34,81 @@ namespace App {
bool forward(const PeerId &peer, ForwardWhatMessages what);
void removeDialog(History *history);
void showSettings();
- void showLayer(LayeredWidget *w, bool forceFast = false);
- void replaceLayer(LayeredWidget *w);
- void showLayerLast(LayeredWidget *w);
};
-namespace Ui { // it doesn't allow me to use UI :(
+namespace Ui { // openssl doesn't allow me to use UI :(
void showStickerPreview(DocumentData *sticker);
void hideStickerPreview();
+ void showLayer(LayeredWidget *box, ShowLayerOptions options = CloseOtherLayers);
+ void hideLayer(bool fast = false);
+ bool isLayerShown();
+ bool isMediaViewShown();
+ bool isInlineItemBeingChosen();
+
+ void repaintHistoryItem(const HistoryItem *item);
+ void repaintInlineItem(const LayoutInlineItem *layout);
+ bool isInlineItemVisible(const LayoutInlineItem *reader);
+
+ void showPeerHistory(const PeerId &peer, MsgId msgId, bool back = false);
+ inline void showPeerHistory(const PeerData *peer, MsgId msgId, bool back = false) {
+ showPeerHistory(peer->id, msgId, back);
+ }
+ inline void showPeerHistory(const History *history, MsgId msgId, bool back = false) {
+ showPeerHistory(history->peer->id, msgId, back);
+ }
+ inline void showPeerHistoryAtItem(const HistoryItem *item) {
+ showPeerHistory(item->history()->peer->id, item->id);
+ }
+ void showPeerHistoryAsync(const PeerId &peer, MsgId msgId);
+ inline void showChatsList() {
+ showPeerHistory(PeerId(0), 0);
+ }
+
+};
+
+enum ClipStopperType {
+ ClipStopperMediaview,
+ ClipStopperSavedGifsPanel,
};
namespace Notify {
void userIsBotChanged(UserData *user);
+ void userIsContactChanged(UserData *user, bool fromThisApp = false);
void botCommandsChanged(UserData *user);
+
+ void inlineBotRequesting(bool requesting);
+
void migrateUpdated(PeerData *peer);
+ void clipStopperHidden(ClipStopperType type);
+
+ void historyItemResized(const HistoryItem *item, bool scrollToIt = false);
+ inline void historyItemsResized() {
+ historyItemResized(0);
+ }
+ void historyItemLayoutChanged(const HistoryItem *item);
+
+ void automaticLoadSettingsChangedGif();
+
+};
+
+namespace Global {
+
+ class Initializer {
+ public:
+ Initializer();
+ ~Initializer();
+ };
+
+#define DeclareGlobalReadOnly(Type, Name) const Type &Name();
+#define DeclareGlobal(Type, Name) DeclareGlobalReadOnly(Type, Name) \
+ void Set##Name(const Type &Name); \
+ Type &Ref##Name();
+
+ DeclareGlobalReadOnly(uint64, LaunchId);
+
};
diff --git a/Telegram/SourceFiles/fileuploader.cpp b/Telegram/SourceFiles/fileuploader.cpp
index 3a2c7eae2d..2c5f30adc7 100644
--- a/Telegram/SourceFiles/fileuploader.cpp
+++ b/Telegram/SourceFiles/fileuploader.cpp
@@ -46,7 +46,7 @@ void FileUploader::uploadMedia(const FullMsgId &msgId, const ReadyLocalMedia &me
} else if (media.type == PrepareAudio) {
AudioData *audio = App::feedAudio(media.audio);
audio->status = FileUploading;
- audio->data = media.data;
+ audio->setData(media.data);
}
queue.insert(msgId, File(media));
sendNext();
@@ -54,7 +54,8 @@ void FileUploader::uploadMedia(const FullMsgId &msgId, const ReadyLocalMedia &me
void FileUploader::upload(const FullMsgId &msgId, const FileLoadResultPtr &file) {
if (file->type == PreparePhoto) {
- App::feedPhoto(file->photo, file->photoThumbs);
+ PhotoData *photo = App::feedPhoto(file->photo, file->photoThumbs);
+ photo->uploadingData = new PhotoData::UploadingData(file->partssize);
} else if (file->type == PrepareDocument) {
DocumentData *document;
if (file->thumb.isNull()) {
@@ -69,7 +70,7 @@ void FileUploader::upload(const FullMsgId &msgId, const FileLoadResultPtr &file)
} else if (file->type == PrepareAudio) {
AudioData *audio = App::feedAudio(file->audio);
audio->status = FileUploading;
- audio->data = file->content;
+ audio->setData(file->content);
}
queue.insert(msgId, File(file));
sendNext();
@@ -83,13 +84,13 @@ void FileUploader::currentFailed() {
} else if (j->type() == PrepareDocument) {
DocumentData *doc = App::document(j->id());
if (doc->status == FileUploading) {
- doc->status = FileFailed;
+ doc->status = FileUploadFailed;
}
emit documentFailed(j.key());
} else if (j->type() == PrepareAudio) {
AudioData *audio = App::audio(j->id());
if (audio->status == FileUploading) {
- audio->status = FileFailed;
+ audio->status = FileUploadFailed;
}
emit audioFailed(j.key());
}
@@ -115,7 +116,7 @@ void FileUploader::killSessions() {
}
void FileUploader::sendNext() {
- if (sentSize >= MaxUploadFileParallelSize) return;
+ if (sentSize >= MaxUploadFileParallelSize || _paused.msg) return;
bool killing = killSessionsTimer.isActive();
if (queue.isEmpty()) {
@@ -232,6 +233,15 @@ void FileUploader::cancel(const FullMsgId &msgId) {
}
}
+void FileUploader::pause(const FullMsgId &msgId) {
+ _paused = msgId;
+}
+
+void FileUploader::unpause() {
+ _paused = FullMsgId();
+ sendNext();
+}
+
void FileUploader::confirm(const FullMsgId &msgId) {
}
@@ -274,21 +284,28 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
int32 dc = dcIt.value();
dcMap.erase(dcIt);
+ int32 sentPartSize = 0;
Queue::const_iterator k = queue.constFind(uploading);
if (i != requestsSent.cend()) {
- sentSize -= i.value().size();
- sentSizes[dc] -= i.value().size();
+ sentPartSize = i.value().size();
requestsSent.erase(i);
} else {
- sentSize -= k->docPartSize;
- sentSizes[dc] -= k->docPartSize;
+ sentPartSize = k->docPartSize;
docRequestsSent.erase(j);
}
+ sentSize -= sentPartSize;
+ sentSizes[dc] -= sentPartSize;
if (k->type() == PreparePhoto) {
+ k->fileSentSize += sentPartSize;
+ PhotoData *photo = App::photo(k->id());
+ if (photo->uploading() && k->file) {
+ photo->uploadingData->size = k->file->partssize;
+ photo->uploadingData->offset = k->fileSentSize;
+ }
emit photoProgress(k.key());
} else if (k->type() == PrepareDocument) {
DocumentData *doc = App::document(k->id());
- if (doc->status == FileUploading) {
+ if (doc->uploading()) {
doc->uploadOffset = (k->docSentParts - docRequestsSent.size()) * k->docPartSize;
if (doc->uploadOffset > doc->size) {
doc->uploadOffset = doc->size;
@@ -297,7 +314,7 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
emit documentProgress(k.key());
} else if (k->type() == PrepareAudio) {
AudioData *audio = App::audio(k->id());
- if (audio->status == FileUploading) {
+ if (audio->uploading()) {
audio->uploadOffset = (k->docSentParts - docRequestsSent.size()) * k->docPartSize;
if (audio->uploadOffset > audio->size) {
audio->uploadOffset = audio->size;
diff --git a/Telegram/SourceFiles/fileuploader.h b/Telegram/SourceFiles/fileuploader.h
index 26b542bdb5..460f8e1702 100644
--- a/Telegram/SourceFiles/fileuploader.h
+++ b/Telegram/SourceFiles/fileuploader.h
@@ -35,12 +35,14 @@ public:
int32 fullSize(const FullMsgId &msgId) const;
void cancel(const FullMsgId &msgId);
+ void pause(const FullMsgId &msgId);
void confirm(const FullMsgId &msgId);
void clear();
public slots:
+ void unpause();
void sendNext();
void killSessions();
@@ -101,6 +103,7 @@ private:
FileLoadResultPtr file;
ReadyLocalMedia media;
int32 partsCount;
+ mutable int32 fileSentSize;
uint64 id() const {
return file ? file->id : media.id;
@@ -136,7 +139,7 @@ private:
uint32 sentSize;
uint32 sentSizes[MTPUploadSessionsCount];
- FullMsgId uploading;
+ FullMsgId uploading, _paused;
Queue queue;
Queue uploaded;
QTimer nextTimer, killSessionsTimer;
diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp
index 72a2a8d275..f5d59a9381 100644
--- a/Telegram/SourceFiles/gui/animation.cpp
+++ b/Telegram/SourceFiles/gui/animation.cpp
@@ -26,7 +26,9 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "window.h"
namespace {
- AnimationManager *manager = 0;
+ AnimationManager *_manager = 0;
+ QVector _clipThreads;
+ QVector _clipManagers;
};
namespace anim {
@@ -78,177 +80,1237 @@ namespace anim {
return delta * (t2 * t2 * t + 1);
}
- void start(Animated *obj) {
- if (!manager) return;
- manager->start(obj);
- }
-
- void step(Animated *obj) {
- if (!manager) return;
- manager->step(obj);
- }
-
- void stop(Animated *obj) {
- if (!manager) return;
- manager->stop(obj);
- }
-
void startManager() {
- delete manager;
- manager = new AnimationManager();
+ stopManager();
+
+ _manager = new AnimationManager();
+
}
void stopManager() {
- delete manager;
- manager = 0;
+ delete _manager;
+ _manager = 0;
+ if (!_clipThreads.isEmpty()) {
+ for (int32 i = 0, l = _clipThreads.size(); i < l; ++i) {
+ _clipThreads.at(i)->quit();
+ _clipThreads.at(i)->wait();
+ delete _clipManagers.at(i);
+ delete _clipThreads.at(i);
+ }
+ _clipThreads.clear();
+ _clipManagers.clear();
+ }
}
}
-bool AnimatedGif::animStep(float64 ms) {
- int32 f = frame;
- while (f < images.size() && ms > delays[f]) {
- ++f;
- if (f == images.size() && images.size() < framesCount) {
- if (reader->read(&img)) {
- int64 d = reader->nextImageDelay(), delay = delays[f - 1];
- if (!d) d = 1;
- delay += d;
- if (img.size() != QSize(w, h)) img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
- images.push_back(img);
- frames.push_back(QPixmap());
- delays.push_back(delay);
- for (int32 i = 0; i < images.size(); ++i) {
- if (!images[i].isNull() || !frames[i].isNull()) {
- images[i] = QImage();
- frames[i] = QPixmap();
- break;
- }
- }
- } else {
- framesCount = images.size();
- }
- }
- if (f == images.size()) {
- if (!duration) {
- duration = delays.isEmpty() ? 1 : delays.back();
- }
+void Animation::start() {
+ if (!_manager) return;
- f = 0;
- for (int32 i = 0, s = delays.size() - 1; i <= s; ++i) {
- delays[i] += duration;
- }
- if (images[f].isNull()) {
- QString fname = reader->fileName();
- delete reader;
- reader = new QImageReader(fname);
- }
+ _cb.start();
+ _manager->start(this);
+ _animating = true;
+}
+
+void Animation::stop() {
+ if (!_manager) return;
+
+ _animating = false;
+ _manager->stop(this);
+}
+
+AnimationManager::AnimationManager() : _timer(this), _iterating(false) {
+ _timer.setSingleShot(false);
+ connect(&_timer, SIGNAL(timeout()), this, SLOT(timeout()));
+}
+
+void AnimationManager::start(Animation *obj) {
+ if (_iterating) {
+ _starting.insert(obj, NullType());
+ if (!_stopping.isEmpty()) {
+ _stopping.remove(obj);
}
- if (images[f].isNull() && reader->read(&img)) {
- if (img.size() != QSize(w, h)) img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
- images[f] = img;
- frames[f] = QPixmap();
+ } else {
+ if (_objects.isEmpty()) {
+ _timer.start(AnimationTimerDelta);
+ }
+ _objects.insert(obj, NullType());
+ }
+}
+
+void AnimationManager::stop(Animation *obj) {
+ if (_iterating) {
+ _stopping.insert(obj, NullType());
+ if (!_starting.isEmpty()) {
+ _starting.insert(obj, NullType());
+ }
+ } else {
+ AnimatingObjects::iterator i = _objects.find(obj);
+ if (i != _objects.cend()) {
+ _objects.erase(i);
+ if (_objects.isEmpty()) {
+ _timer.stop();
+ }
}
}
- if (frame != f) {
- frame = f;
- if (msg && App::main()) {
- App::main()->msgUpdated(msg);
- } else {
- emit updated();
+}
+
+void AnimationManager::timeout() {
+ _iterating = true;
+ uint64 ms = getms();
+ for (AnimatingObjects::const_iterator i = _objects.begin(), e = _objects.end(); i != e; ++i) {
+ i.key()->step(ms, true);
+ }
+ _iterating = false;
+
+ if (!_starting.isEmpty()) {
+ for (AnimatingObjects::iterator i = _starting.begin(), e = _starting.end(); i != e; ++i) {
+ _objects.insert(i.key(), NullType());
}
+ _starting.clear();
+ }
+ if (!_stopping.isEmpty()) {
+ for (AnimatingObjects::iterator i = _stopping.begin(), e = _stopping.end(); i != e; ++i) {
+ _objects.remove(i.key());
+ }
+ _stopping.clear();
+ }
+ if (!_objects.size()) {
+ _timer.stop();
+ }
+}
+
+void AnimationManager::clipCallback(ClipReader *reader, qint32 threadIndex, qint32 notification) {
+ ClipReader::callback(reader, threadIndex, ClipReaderNotification(notification));
+}
+
+QPixmap _prepareFrame(const ClipFrameRequest &request, const QImage &original, bool hasAlpha, QImage &cache) {
+ bool badSize = (original.width() != request.framew) || (original.height() != request.frameh);
+ bool needOuter = (request.outerw != request.framew) || (request.outerh != request.frameh);
+ if (badSize || needOuter || hasAlpha || request.rounded) {
+ int32 factor(request.factor);
+ bool newcache = (cache.width() != request.outerw || cache.height() != request.outerh);
+ if (newcache) {
+ cache = QImage(request.outerw, request.outerh, QImage::Format_ARGB32_Premultiplied);
+ cache.setDevicePixelRatio(factor);
+ }
+ {
+ Painter p(&cache);
+ if (newcache) {
+ if (request.framew < request.outerw) {
+ p.fillRect(0, 0, (request.outerw - request.framew) / (2 * factor), cache.height() / factor, st::black);
+ p.fillRect((request.outerw - request.framew) / (2 * factor) + (request.framew / factor), 0, (cache.width() / factor) - ((request.outerw - request.framew) / (2 * factor) + (request.framew / factor)), cache.height() / factor, st::black);
+ }
+ if (request.frameh < request.outerh) {
+ p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), 0, qMin(cache.width(), request.framew) / factor, (request.outerh - request.frameh) / (2 * factor), st::black);
+ p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), (request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor), qMin(cache.width(), request.framew) / factor, (cache.height() / factor) - ((request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor)), st::black);
+ }
+ }
+ if (hasAlpha) {
+ p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), qMax(0, (request.outerh - request.frameh) / (2 * factor)), qMin(cache.width(), request.framew) / factor, qMin(cache.height(), request.frameh) / factor, st::white);
+ }
+ QPoint position((request.outerw - request.framew) / (2 * factor), (request.outerh - request.frameh) / (2 * factor));
+ if (badSize) {
+ p.setRenderHint(QPainter::SmoothPixmapTransform);
+ QRect to(position, QSize(request.framew / factor, request.frameh / factor));
+ QRect from(0, 0, original.width(), original.height());
+ p.drawImage(to, original, from, Qt::ColorOnly);
+ } else {
+ p.drawImage(position, original);
+ }
+ }
+ if (request.rounded) {
+ imageRound(cache);
+ }
+ return QPixmap::fromImage(cache, Qt::ColorOnly);
+ }
+ return QPixmap::fromImage(original, Qt::ColorOnly);
+}
+
+ClipReader::ClipReader(const FileLocation &location, const QByteArray &data, Callback::Creator cb)
+: _cb(cb)
+, _state(ClipReading)
+, _width(0)
+, _height(0)
+, _step(WaitingForDimensionsStep)
+, _paused(0)
+, _autoplay(false)
+, _private(0) {
+ if (_clipThreads.size() < ClipThreadsCount) {
+ _threadIndex = _clipThreads.size();
+ _clipThreads.push_back(new QThread());
+ _clipManagers.push_back(new ClipReadManager(_clipThreads.back()));
+ _clipThreads.back()->start();
+ } else {
+ _threadIndex = int32(MTP::nonce() % _clipThreads.size());
+ int32 loadLevel = 0x7FFFFFFF;
+ for (int32 i = 0, l = _clipThreads.size(); i < l; ++i) {
+ int32 level = _clipManagers.at(i)->loadLevel();
+ if (level < loadLevel) {
+ _threadIndex = i;
+ loadLevel = level;
+ }
+ }
+ }
+ _clipManagers.at(_threadIndex)->append(this, location, data);
+}
+
+ClipReader::Frame *ClipReader::frameToShow(int32 *index) const { // 0 means not ready
+ int32 step = _step.loadAcquire(), i;
+ if (step == WaitingForDimensionsStep) {
+ if (index) *index = 0;
+ return 0;
+ } else if (step == WaitingForRequestStep) {
+ i = 0;
+ } else if (step == WaitingForFirstFrameStep) {
+ i = 0;
+ } else {
+ i = (step / 2) % 3;
+ }
+ if (index) *index = i;
+ return _frames + i;
+}
+
+ClipReader::Frame *ClipReader::frameToWrite(int32 *index) const { // 0 means not ready
+ int32 step = _step.loadAcquire(), i;
+ if (step == WaitingForDimensionsStep) {
+ i = 0;
+ } else if (step == WaitingForRequestStep) {
+ if (index) *index = 0;
+ return 0;
+ } else if (step == WaitingForFirstFrameStep) {
+ i = 0;
+ } else {
+ i = ((step + 2) / 2) % 3;
+ }
+ if (index) *index = i;
+ return _frames + i;
+}
+
+ClipReader::Frame *ClipReader::frameToWriteNext(bool checkNotWriting, int32 *index) const {
+ int32 step = _step.loadAcquire(), i;
+ if (step == WaitingForDimensionsStep || step == WaitingForRequestStep || (checkNotWriting && (step % 2))) {
+ if (index) *index = 0;
+ return 0;
+ }
+ i = ((step + 4) / 2) % 3;
+ if (index) *index = i;
+ return _frames + i;
+}
+
+void ClipReader::moveToNextShow() const {
+ int32 step = _step.loadAcquire();
+ if (step == WaitingForDimensionsStep) {
+ } else if (step == WaitingForRequestStep) {
+ _step.storeRelease(WaitingForFirstFrameStep);
+ } else if (step == WaitingForFirstFrameStep) {
+ } else if (!(step % 2)) {
+ _step.storeRelease(step + 1);
+ }
+}
+
+void ClipReader::moveToNextWrite() const {
+ int32 step = _step.loadAcquire();
+ if (step == WaitingForDimensionsStep) {
+ _step.storeRelease(WaitingForRequestStep);
+ } else if (step == WaitingForRequestStep) {
+ } else if (step == WaitingForFirstFrameStep) {
+ _step.storeRelease(0);
+ } else if (step % 2) {
+ _step.storeRelease((step + 1) % 6);
+ }
+}
+
+void ClipReader::callback(ClipReader *reader, int32 threadIndex, ClipReaderNotification notification) {
+ // check if reader is not deleted already
+ if (_clipManagers.size() > threadIndex && _clipManagers.at(threadIndex)->carries(reader)) {
+ reader->_cb.call(notification);
+ }
+}
+
+void ClipReader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded) {
+ if (_clipManagers.size() <= _threadIndex) error();
+ if (_state == ClipError) return;
+
+ if (_step.loadAcquire() == WaitingForRequestStep) {
+ int32 factor(cIntRetinaFactor());
+ ClipFrameRequest request;
+ request.factor = factor;
+ request.framew = framew * factor;
+ request.frameh = frameh * factor;
+ request.outerw = outerw * factor;
+ request.outerh = outerh * factor;
+ request.rounded = rounded;
+ _frames[0].request = _frames[1].request = _frames[2].request = request;
+ moveToNextShow();
+ _clipManagers.at(_threadIndex)->start(this);
+ }
+}
+
+QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms) {
+ Frame *frame = frameToShow();
+ t_assert(frame != 0);
+
+ if (ms) {
+ frame->displayed.storeRelease(1);
+ if (_paused.loadAcquire()) {
+ _paused.storeRelease(0);
+ if (_clipManagers.size() <= _threadIndex) error();
+ if (_state != ClipError) {
+ _clipManagers.at(_threadIndex)->update(this);
+ }
+ }
+ } else {
+ frame->displayed.storeRelease(-1); // displayed, but should be paused
+ }
+
+ int32 factor(cIntRetinaFactor());
+ if (frame->pix.width() == outerw * factor && frame->pix.height() == outerh * factor) {
+ moveToNextShow();
+ return frame->pix;
+ }
+
+ frame->request.framew = framew * factor;
+ frame->request.frameh = frameh * factor;
+ frame->request.outerw = outerw * factor;
+ frame->request.outerh = outerh * factor;
+
+ QImage cacheForResize;
+ frame->pix = QPixmap();
+ frame->pix = _prepareFrame(frame->request, frame->original, true, cacheForResize);
+
+ Frame *other = frameToWriteNext(true);
+ if (other) other->request = frame->request;
+
+ moveToNextShow();
+
+ if (_clipManagers.size() <= _threadIndex) error();
+ if (_state != ClipError) {
+ _clipManagers.at(_threadIndex)->update(this);
+ }
+
+ return frame->pix;
+}
+
+bool ClipReader::ready() const {
+ if (_width && _height) return true;
+
+ Frame *frame = frameToShow();
+ if (frame) {
+ _width = frame->original.width();
+ _height = frame->original.height();
+ return true;
+ }
+ return false;
+}
+
+int32 ClipReader::width() const {
+ return _width;
+}
+
+int32 ClipReader::height() const {
+ return _height;
+}
+
+ClipState ClipReader::state() const {
+ return _state;
+}
+
+void ClipReader::stop() {
+ if (_clipManagers.size() <= _threadIndex) error();
+ if (_state != ClipError) {
+ _clipManagers.at(_threadIndex)->stop(this);
+ _width = _height = 0;
+ }
+}
+
+void ClipReader::error() {
+ _private = 0;
+ _state = ClipError;
+}
+
+ClipReader::~ClipReader() {
+ stop();
+}
+
+class ClipReaderImplementation {
+public:
+
+ ClipReaderImplementation(FileLocation *location, QByteArray *data)
+ : _location(location)
+ , _data(data)
+ , _device(0)
+ , _dataSize(0) {
+ }
+ virtual bool readNextFrame() = 0;
+ virtual bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) = 0;
+ virtual int32 nextFrameDelay() = 0;
+ virtual bool start(bool onlyGifv) = 0;
+ virtual ~ClipReaderImplementation() {
+ }
+ int64 dataSize() const {
+ return _dataSize;
+ }
+
+protected:
+ FileLocation *_location;
+ QByteArray *_data;
+ QFile _file;
+ QBuffer _buffer;
+ QIODevice *_device;
+ int64 _dataSize;
+
+ void initDevice() {
+ if (_data->isEmpty()) {
+ if (_file.isOpen()) _file.close();
+ _file.setFileName(_location->name());
+ _dataSize = _file.size();
+ } else {
+ if (_buffer.isOpen()) _buffer.close();
+ _buffer.setBuffer(_data);
+ _dataSize = _data->size();
+ }
+ _device = _data->isEmpty() ? static_cast(&_file) : static_cast(&_buffer);
+ }
+
+};
+
+class QtGifReaderImplementation : public ClipReaderImplementation{
+public:
+
+ QtGifReaderImplementation(FileLocation *location, QByteArray *data) : ClipReaderImplementation(location, data)
+ , _reader(0)
+ , _framesLeft(0)
+ , _frameDelay(0) {
+ }
+
+ bool readNextFrame() {
+ if (_reader) _frameDelay = _reader->nextImageDelay();
+ if (_framesLeft < 1 && !jumpToStart()) {
+ return false;
+ }
+
+ _frame = QImage(); // QGifHandler always reads first to internal QImage and returns it
+ if (!_reader->read(&_frame) || _frame.isNull()) {
+ return false;
+ }
+ --_framesLeft;
+ return true;
+ }
+
+ bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) {
+ t_assert(!_frame.isNull());
+ if (size.isEmpty() || size == _frame.size()) {
+ int32 w = _frame.width(), h = _frame.height();
+ if (to.width() == w && to.height() == h && to.format() == _frame.format()) {
+ if (to.byteCount() != _frame.byteCount()) {
+ int bpl = qMin(to.bytesPerLine(), _frame.bytesPerLine());
+ for (int i = 0; i < h; ++i) {
+ memcpy(to.scanLine(i), _frame.constScanLine(i), bpl);
+ }
+ } else {
+ memcpy(to.bits(), _frame.constBits(), _frame.byteCount());
+ }
+ } else {
+ to = _frame.copy();
+ }
+ } else {
+ to = _frame.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ }
+ hasAlpha = _frame.hasAlphaChannel();
+ _frame = QImage();
+ return true;
+ }
+
+ int32 nextFrameDelay() {
+ return _frameDelay;
+ }
+
+ bool start(bool onlyGifv) {
+ if (onlyGifv) return false;
+ return jumpToStart();
+ }
+
+ ~QtGifReaderImplementation() {
+ deleteAndMark(_reader);
+ }
+
+private:
+ QImageReader *_reader;
+ int32 _framesLeft, _frameDelay;
+ QImage _frame;
+
+ bool jumpToStart() {
+ if (_reader && _reader->jumpToImage(0)) {
+ _framesLeft = _reader->imageCount();
+ return true;
+ }
+
+ delete _reader;
+ initDevice();
+ _reader = new QImageReader(_device);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
+ _reader->setAutoTransform(true);
+#endif
+ if (!_reader->canRead() || !_reader->supportsAnimation()) {
+ return false;
+ }
+ _framesLeft = _reader->imageCount();
+ if (_framesLeft < 1) {
+ return false;
+ }
+ return true;
+ }
+
+};
+
+class FFMpegReaderImplementation : public ClipReaderImplementation {
+public:
+
+ FFMpegReaderImplementation(FileLocation *location, QByteArray *data) : ClipReaderImplementation(location, data)
+ , _ioBuffer(0)
+ , _ioContext(0)
+ , _fmtContext(0)
+ , _codec(0)
+ , _codecContext(0)
+ , _streamId(0)
+ , _frame(0)
+ , _opened(false)
+ , _hadFrame(false)
+ , _frameRead(false)
+ , _packetSize(0)
+ , _packetData(0)
+ , _packetWas(false)
+ , _width(0)
+ , _height(0)
+ , _swsContext(0)
+ , _frameMs(0)
+ , _nextFrameDelay(0)
+ , _currentFrameDelay(0) {
+ _frame = av_frame_alloc();
+ av_init_packet(&_avpkt);
+ _avpkt.data = NULL;
+ _avpkt.size = 0;
+ }
+
+ bool readNextFrame() {
+ if (_frameRead) {
+ av_frame_unref(_frame);
+ _frameRead = false;
+ }
+
+ int res;
+ while (true) {
+ if (_avpkt.size > 0) { // previous packet not finished
+ res = 0;
+ } else if ((res = av_read_frame(_fmtContext, &_avpkt)) < 0) {
+ if (res != AVERROR_EOF || !_hadFrame) {
+ char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+ LOG(("Gif Error: Unable to av_read_frame() %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
+ return false;
+ }
+ }
+
+ bool finished = (res < 0);
+ if (finished) {
+ _avpkt.data = NULL;
+ _avpkt.size = 0;
+ } else {
+ rememberPacket();
+ }
+
+ int32 got_frame = 0;
+ int32 decoded = _avpkt.size;
+ if (_avpkt.stream_index == _streamId) {
+ if ((res = avcodec_decode_video2(_codecContext, _frame, &got_frame, &_avpkt)) < 0) {
+ char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+ LOG(("Gif Error: Unable to avcodec_decode_video2() %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
+
+ if (res == AVERROR_INVALIDDATA) { // try to skip bad packet
+ freePacket();
+ _avpkt.data = NULL;
+ _avpkt.size = 0;
+ continue;
+ }
+
+ if (res != AVERROR_EOF || !_hadFrame) { // try to skip end of file
+ return false;
+ }
+ freePacket();
+ _avpkt.data = NULL;
+ _avpkt.size = 0;
+ continue;
+ }
+ if (res > 0) decoded = res;
+ }
+ if (!finished) {
+ _avpkt.data += decoded;
+ _avpkt.size -= decoded;
+ if (_avpkt.size <= 0) freePacket();
+ }
+
+ if (got_frame) {
+ int64 duration = av_frame_get_pkt_duration(_frame);
+ int64 framePts = (_frame->pkt_pts == AV_NOPTS_VALUE) ? _frame->pkt_dts : _frame->pkt_pts;
+ int64 frameMs = (framePts * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den;
+ _currentFrameDelay = _nextFrameDelay;
+ if (_frameMs + _currentFrameDelay < frameMs) {
+ _currentFrameDelay = int32(frameMs - _frameMs);
+ }
+ if (duration == AV_NOPTS_VALUE) {
+ _nextFrameDelay = 0;
+ } else {
+ _nextFrameDelay = (duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den;
+ }
+ _frameMs = frameMs;
+
+ _hadFrame = _frameRead = true;
+ return true;
+ }
+
+ if (finished) {
+ if ((res = avformat_seek_file(_fmtContext, _streamId, std::numeric_limits::min(), 0, std::numeric_limits::max(), 0)) < 0) {
+ if ((res = av_seek_frame(_fmtContext, _streamId, 0, AVSEEK_FLAG_BYTE)) < 0) {
+ if ((res = av_seek_frame(_fmtContext, _streamId, 0, AVSEEK_FLAG_FRAME)) < 0) {
+ if ((res = av_seek_frame(_fmtContext, _streamId, 0, 0)) < 0) {
+ char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+ LOG(("Gif Error: Unable to av_seek_frame() to the start %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
+ return false;
+ }
+ }
+ }
+ }
+ avcodec_flush_buffers(_codecContext);
+ _hadFrame = false;
+ _frameMs = 0;
+ }
+ }
+
+ return false;
+ }
+
+ bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) {
+ t_assert(_frameRead);
+ _frameRead = false;
+
+ if (!_width || !_height) {
+ _width = _frame->width;
+ _height = _frame->height;
+ if (!_width || !_height) {
+ LOG(("Gif Error: Bad frame size %1").arg(logData()));
+ return false;
+ }
+ }
+
+ QSize toSize(size.isEmpty() ? QSize(_width, _height) : size);
+ if (to.isNull() || to.size() != toSize) {
+ to = QImage(toSize, QImage::Format_ARGB32);
+ }
+ hasAlpha = (_frame->format == AV_PIX_FMT_BGRA || (_frame->format == -1 && _codecContext->pix_fmt == AV_PIX_FMT_BGRA));
+ if (_frame->width == toSize.width() && _frame->height == toSize.height() && hasAlpha) {
+ int32 sbpl = _frame->linesize[0], dbpl = to.bytesPerLine(), bpl = qMin(sbpl, dbpl);
+ uchar *s = _frame->data[0], *d = to.bits();
+ for (int32 i = 0, l = _frame->height; i < l; ++i) {
+ memcpy(d + i * dbpl, s + i * sbpl, bpl);
+ }
+ } else {
+ if ((_swsSize != toSize) || (_frame->format != -1 && _frame->format != _codecContext->pix_fmt) || !_swsContext) {
+ _swsSize = toSize;
+ _swsContext = sws_getCachedContext(_swsContext, _frame->width, _frame->height, AVPixelFormat(_frame->format), toSize.width(), toSize.height(), AV_PIX_FMT_BGRA, 0, 0, 0, 0);
+ }
+ uint8_t * toData[1] = { to.bits() };
+ int toLinesize[1] = { to.bytesPerLine() }, res;
+ if ((res = sws_scale(_swsContext, _frame->data, _frame->linesize, 0, _frame->height, toData, toLinesize)) != _swsSize.height()) {
+ LOG(("Gif Error: Unable to sws_scale to good size %1, height %2, should be %3").arg(logData()).arg(res).arg(_swsSize.height()));
+ return false;
+ }
+ }
+
+ av_frame_unref(_frame);
+ return true;
+ }
+
+ int32 nextFrameDelay() {
+ return _currentFrameDelay;
+ }
+
+ QString logData() const {
+ return qsl("for file '%1', data size '%2'").arg(_location ? _location->name() : QString()).arg(_data->size());
+ }
+
+ bool start(bool onlyGifv) {
+ initDevice();
+ if (!_device->open(QIODevice::ReadOnly)) {
+ LOG(("Gif Error: Unable to open device %1").arg(logData()));
+ return false;
+ }
+ _ioBuffer = (uchar*)av_malloc(AVBlockSize);
+ _ioContext = avio_alloc_context(_ioBuffer, AVBlockSize, 0, static_cast(this), &FFMpegReaderImplementation::_read, 0, &FFMpegReaderImplementation::_seek);
+ _fmtContext = avformat_alloc_context();
+ if (!_fmtContext) {
+ LOG(("Gif Error: Unable to avformat_alloc_context %1").arg(logData()));
+ return false;
+ }
+ _fmtContext->pb = _ioContext;
+
+ int res = 0;
+ char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+ if ((res = avformat_open_input(&_fmtContext, 0, 0, 0)) < 0) {
+ _ioBuffer = 0;
+
+ LOG(("Gif Error: Unable to avformat_open_input %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
+ return false;
+ }
+ _opened = true;
+
+ if ((res = avformat_find_stream_info(_fmtContext, 0)) < 0) {
+ LOG(("Gif Error: Unable to avformat_find_stream_info %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
+ return false;
+ }
+
+ _streamId = av_find_best_stream(_fmtContext, AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);
+ if (_streamId < 0) {
+ LOG(("Gif Error: Unable to av_find_best_stream %1, error %2, %3").arg(logData()).arg(_streamId).arg(av_make_error_string(err, sizeof(err), _streamId)));
+ return false;
+ }
+
+ // Get a pointer to the codec context for the audio stream
+ _codecContext = _fmtContext->streams[_streamId]->codec;
+ _codec = avcodec_find_decoder(_codecContext->codec_id);
+
+ if (onlyGifv) {
+ if (av_find_best_stream(_fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0) >= 0) { // should be no audio stream
+ return false;
+ }
+ if (dataSize() > AnimationInMemory) {
+ return false;
+ }
+ if (_codecContext->codec_id != AV_CODEC_ID_H264) {
+ return false;
+ }
+ }
+ av_opt_set_int(_codecContext, "refcounted_frames", 1, 0);
+ if ((res = avcodec_open2(_codecContext, _codec, 0)) < 0) {
+ LOG(("Gif Error: Unable to avcodec_open2 %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
+ return false;
+ }
+
+ return true;
+ }
+
+ int32 duration() const {
+ if (_fmtContext->streams[_streamId]->duration == AV_NOPTS_VALUE) return 0;
+ return (_fmtContext->streams[_streamId]->duration * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den;
+ }
+
+ ~FFMpegReaderImplementation() {
+ if (_frameRead) {
+ av_frame_unref(_frame);
+ _frameRead = false;
+ }
+ if (_ioContext) av_free(_ioContext);
+ if (_codecContext) avcodec_close(_codecContext);
+ if (_swsContext) sws_freeContext(_swsContext);
+ if (_opened) {
+ avformat_close_input(&_fmtContext);
+ } else if (_ioBuffer) {
+ av_free(_ioBuffer);
+ }
+ if (_fmtContext) avformat_free_context(_fmtContext);
+ av_frame_free(&_frame);
+ freePacket();
+ }
+
+private:
+ uchar *_ioBuffer;
+ AVIOContext *_ioContext;
+ AVFormatContext *_fmtContext;
+ AVCodec *_codec;
+ AVCodecContext *_codecContext;
+ int32 _streamId;
+ AVFrame *_frame;
+ bool _opened, _hadFrame, _frameRead;
+
+ AVPacket _avpkt;
+ int _packetSize;
+ uint8_t *_packetData;
+ bool _packetWas;
+ void rememberPacket() {
+ if (!_packetWas) {
+ _packetSize = _avpkt.size;
+ _packetData = _avpkt.data;
+ _packetWas = true;
+ }
+ }
+ void freePacket() {
+ if (_packetWas) {
+ _avpkt.size = _packetSize;
+ _avpkt.data = _packetData;
+ _packetWas = false;
+ av_packet_unref(&_avpkt);
+ }
+ }
+
+ int32 _width, _height;
+ SwsContext *_swsContext;
+ QSize _swsSize;
+
+ int64 _frameMs;
+ int32 _nextFrameDelay, _currentFrameDelay;
+
+ static int _read(void *opaque, uint8_t *buf, int buf_size) {
+ FFMpegReaderImplementation *l = reinterpret_cast(opaque);
+ return int(l->_device->read((char*)(buf), buf_size));
+ }
+
+ static int64_t _seek(void *opaque, int64_t offset, int whence) {
+ FFMpegReaderImplementation *l = reinterpret_cast(opaque);
+
+ switch (whence) {
+ case SEEK_SET: return l->_device->seek(offset) ? l->_device->pos() : -1;
+ case SEEK_CUR: return l->_device->seek(l->_device->pos() + offset) ? l->_device->pos() : -1;
+ case SEEK_END: return l->_device->seek(l->_device->size() + offset) ? l->_device->pos() : -1;
+ }
+ return -1;
+ }
+
+};
+
+class ClipReaderPrivate {
+public:
+
+ ClipReaderPrivate(ClipReader *reader, const FileLocation &location, const QByteArray &data) : _interface(reader)
+ , _state(ClipReading)
+ , _data(data)
+ , _location(_data.isEmpty() ? new FileLocation(location) : 0)
+ , _accessed(false)
+ , _implementation(0)
+ , _frame(0)
+ , _width(0)
+ , _height(0)
+ , _nextFrameWhen(0)
+ , _paused(false) {
+ if (_data.isEmpty() && !_location->accessEnable()) {
+ error();
+ return;
+ }
+ _accessed = true;
+ }
+
+ ClipProcessResult start(uint64 ms) {
+ if (!_implementation && !init()) {
+ return error();
+ }
+ if (frame() && frame()->original.isNull()) {
+ if (!_implementation->readNextFrame()) {
+ return error();
+ }
+ if (!_implementation->renderFrame(frame()->original, frame()->alpha, QSize())) {
+ return error();
+ }
+ _width = frame()->original.width();
+ _height = frame()->original.height();
+ return ClipProcessStarted;
+ }
+ return ClipProcessWait;
+ }
+
+ ClipProcessResult process(uint64 ms) { // -1 - do nothing, 0 - update, 1 - reinit
+ if (_state == ClipError) return ClipProcessError;
+
+ if (!_request.valid()) {
+ return start(ms);
+ }
+
+ if (!_paused && ms >= _nextFrameWhen) {
+ return ClipProcessRepaint;
+ }
+ return ClipProcessWait;
+ }
+
+ ClipProcessResult finishProcess(uint64 ms) {
+ if (!readNextFrame()) {
+ return error();
+ }
+ if (ms >= _nextFrameWhen && !readNextFrame(true)) {
+ return error();
+ }
+ if (!renderFrame()) {
+ return error();
+ }
+ return ClipProcessCopyFrame;
+ }
+
+ uint64 nextFrameDelay() {
+ int32 delay = _implementation->nextFrameDelay();
+ return qMax(delay, 5);
+ }
+
+ bool readNextFrame(bool keepup = false) {
+ if (!_implementation->readNextFrame()) {
+ return false;
+ }
+ _nextFrameWhen += nextFrameDelay();
+ if (keepup) {
+ _nextFrameWhen = qMax(_nextFrameWhen, getms());
+ }
+ return true;
+ }
+
+ bool renderFrame() {
+ t_assert(frame() != 0 && _request.valid());
+ if (!_implementation->renderFrame(frame()->original, frame()->alpha, QSize(_request.framew, _request.frameh))) {
+ return false;
+ }
+ frame()->original.setDevicePixelRatio(_request.factor);
+ frame()->pix = QPixmap();
+ frame()->pix = _prepareFrame(_request, frame()->original, frame()->alpha, frame()->cache);
+ frame()->when = _nextFrameWhen;
+ return true;
+ }
+
+ bool init() {
+ if (_data.isEmpty() && QFileInfo(_location->name()).size() <= AnimationInMemory) {
+ QFile f(_location->name());
+ if (f.open(QIODevice::ReadOnly)) {
+ _data = f.readAll();
+ if (f.error() != QFile::NoError) {
+ _data = QByteArray();
+ }
+ }
+ }
+
+ _implementation = new FFMpegReaderImplementation(_location, &_data);
+// _implementation = new QtGifReaderImplementation(_location, &_data);
+ return _implementation->start(false);
+ }
+
+ ClipProcessResult error() {
+ stop();
+ _state = ClipError;
+ return ClipProcessError;
+ }
+
+ void stop() {
+ delete _implementation;
+ _implementation = 0;
+
+ if (_location) {
+ if (_accessed) {
+ _location->accessDisable();
+ }
+ delete _location;
+ _location = 0;
+ }
+ _accessed = false;
+ }
+
+ ~ClipReaderPrivate() {
+ stop();
+ deleteAndMark(_location);
+ deleteAndMark(_implementation);
+ _data.clear();
+ }
+
+private:
+
+ ClipReader *_interface;
+ ClipState _state;
+
+ QByteArray _data;
+ FileLocation *_location;
+ bool _accessed;
+
+ QBuffer _buffer;
+ ClipReaderImplementation *_implementation;
+
+ ClipFrameRequest _request;
+ struct Frame {
+ Frame() : alpha(true), when(0) {
+ }
+ QPixmap pix;
+ QImage original, cache;
+ bool alpha;
+ uint64 when;
+ };
+ Frame _frames[3];
+ int32 _frame;
+ Frame *frame() {
+ return _frames + _frame;
+ }
+
+ int32 _width, _height;
+
+ uint64 _nextFrameWhen;
+
+ bool _paused;
+
+ friend class ClipReadManager;
+
+};
+
+ClipReadManager::ClipReadManager(QThread *thread) : _processingInThread(0), _needReProcess(false) {
+ moveToThread(thread);
+ connect(thread, SIGNAL(started()), this, SLOT(process()));
+ connect(thread, SIGNAL(finished()), this, SLOT(finish()));
+ connect(this, SIGNAL(processDelayed()), this, SLOT(process()), Qt::QueuedConnection);
+
+ _timer.setSingleShot(true);
+ _timer.moveToThread(thread);
+ connect(&_timer, SIGNAL(timeout()), this, SLOT(process()));
+
+ connect(this, SIGNAL(callback(ClipReader*,qint32,qint32)), _manager, SLOT(clipCallback(ClipReader*,qint32,qint32)));
+}
+
+void ClipReadManager::append(ClipReader *reader, const FileLocation &location, const QByteArray &data) {
+ reader->_private = new ClipReaderPrivate(reader, location, data);
+ _loadLevel.fetchAndAddRelaxed(AverageGifSize);
+ update(reader);
+}
+
+void ClipReadManager::start(ClipReader *reader) {
+ update(reader);
+}
+
+void ClipReadManager::update(ClipReader *reader) {
+ QReadLocker lock(&_readerPointersMutex);
+ ReaderPointers::const_iterator i = _readerPointers.constFind(reader);
+ if (i == _readerPointers.cend()) {
+ lock.unlock();
+
+ QWriteLocker lock(&_readerPointersMutex);
+ _readerPointers.insert(reader, MutableAtomicInt(1));
+ } else {
+ i->v.storeRelease(1);
+ }
+ emit processDelayed();
+}
+
+void ClipReadManager::stop(ClipReader *reader) {
+ if (!carries(reader)) return;
+
+ QWriteLocker lock(&_readerPointersMutex);
+ _readerPointers.remove(reader);
+ emit processDelayed();
+}
+
+bool ClipReadManager::carries(ClipReader *reader) const {
+ QReadLocker lock(&_readerPointersMutex);
+ return _readerPointers.contains(reader);
+}
+
+ClipReadManager::ReaderPointers::iterator ClipReadManager::unsafeFindReaderPointer(ClipReaderPrivate *reader) {
+ ReaderPointers::iterator it = _readerPointers.find(reader->_interface);
+
+ // could be a new reader which was realloced in the same address
+ return (it == _readerPointers.cend() || it.key()->_private == reader) ? it : _readerPointers.end();
+}
+
+ClipReadManager::ReaderPointers::const_iterator ClipReadManager::constUnsafeFindReaderPointer(ClipReaderPrivate *reader) const {
+ ReaderPointers::const_iterator it = _readerPointers.constFind(reader->_interface);
+
+ // could be a new reader which was realloced in the same address
+ return (it == _readerPointers.cend() || it.key()->_private == reader) ? it : _readerPointers.cend();
+}
+
+bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) {
+ QReadLocker lock(&_readerPointersMutex);
+ ReaderPointers::const_iterator it = constUnsafeFindReaderPointer(reader);
+ if (result == ClipProcessError) {
+ if (it != _readerPointers.cend()) {
+ it.key()->error();
+ emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit);
+
+ lock.unlock();
+ QWriteLocker lock(&_readerPointersMutex);
+ ReaderPointers::iterator i = unsafeFindReaderPointer(reader);
+ if (i != _readerPointers.cend()) _readerPointers.erase(i);
+ }
+ return false;
+ }
+ if (it == _readerPointers.cend()) {
+ return false;
+ }
+
+ if (result == ClipProcessStarted) {
+ _loadLevel.fetchAndAddRelaxed(reader->_width * reader->_height - AverageGifSize);
+ }
+ if (!reader->_paused && result == ClipProcessRepaint) {
+ int32 ishowing, iprevious;
+ ClipReader::Frame *showing = it.key()->frameToShow(&ishowing), *previous = it.key()->frameToWriteNext(false, &iprevious);
+ t_assert(previous != 0 && showing != 0 && ishowing >= 0 && iprevious >= 0);
+ if (reader->_frames[ishowing].when > 0 && showing->displayed.loadAcquire() <= 0) { // current frame was not shown
+ if (reader->_frames[ishowing].when + WaitBeforeGifPause < ms || (reader->_frames[iprevious].when && previous->displayed.loadAcquire() <= 0)) {
+ reader->_paused = true;
+ it.key()->_paused.storeRelease(1);
+ result = ClipProcessPaused;
+ }
+ }
+ }
+ if (result == ClipProcessStarted || result == ClipProcessCopyFrame) {
+ t_assert(reader->_frame >= 0);
+ ClipReader::Frame *frame = it.key()->_frames + reader->_frame;
+ frame->clear();
+ frame->pix = reader->frame()->pix;
+ frame->original = reader->frame()->original;
+ frame->displayed.storeRelease(0);
+ if (result == ClipProcessStarted) {
+ reader->_nextFrameWhen = ms;
+ it.key()->moveToNextWrite();
+ emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit);
+ }
+ } else if (result == ClipProcessPaused) {
+ it.key()->moveToNextWrite();
+ emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit);
+ } else if (result == ClipProcessRepaint) {
+ it.key()->moveToNextWrite();
+ emit callback(it.key(), it.key()->threadIndex(), ClipReaderRepaint);
}
return true;
}
-void AnimatedGif::start(HistoryItem *row, const FileLocation &f) {
- stop();
-
- file = new FileLocation(f);
- if (!file->accessEnable()) {
- stop();
- return;
- }
- access = true;
-
- reader = new QImageReader(file->name());
- if (!reader->canRead() || !reader->supportsAnimation()) {
- stop();
- return;
+ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) {
+ if (!handleProcessResult(reader, result, ms)) {
+ _loadLevel.fetchAndAddRelaxed(-1 * (reader->_width > 0 ? reader->_width * reader->_height : AverageGifSize));
+ delete reader;
+ return ResultHandleRemove;
}
- QSize s = reader->size();
- w = s.width();
- h = s.height();
- framesCount = reader->imageCount();
- if (!w || !h || !framesCount) {
- stop();
- return;
+ _processingInThread->eventDispatcher()->processEvents(QEventLoop::AllEvents);
+ if (_processingInThread->isInterruptionRequested()) {
+ return ResultHandleStop;
}
- frames.reserve(framesCount);
- images.reserve(framesCount);
- delays.reserve(framesCount);
-
- int32 sizeLeft = MediaViewImageSizeLimit, delay = 0;
- for (bool read = reader->read(&img); read; read = reader->read(&img)) {
- sizeLeft -= w * h * 4;
- if (img.size() != s) img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
- images.push_back(img);
- frames.push_back(QPixmap());
- int32 d = reader->nextImageDelay();
- if (!d) d = 1;
- delay += d;
- delays.push_back(delay);
- if (sizeLeft < 0) break;
- }
-
- msg = row;
-
- anim::start(this);
- if (msg) {
- msg->initDimensions();
- if (App::main()) App::main()->itemResized(msg, true);
- }
-}
-
-void AnimatedGif::stop(bool onItemRemoved) {
- if (file) {
- if (access) {
- file->accessDisable();
+ if (result == ClipProcessRepaint) {
+ {
+ QReadLocker lock(&_readerPointersMutex);
+ ReaderPointers::const_iterator it = constUnsafeFindReaderPointer(reader);
+ if (it != _readerPointers.cend()) {
+ int32 index = 0;
+ ClipReader *r = it.key();
+ ClipReader::Frame *frame = it.key()->frameToWrite(&index);
+ if (frame) {
+ frame->clear();
+ } else {
+ t_assert(!reader->_request.valid());
+ }
+ reader->_frame = index;
+ }
}
- delete file;
- file = 0;
+ return handleResult(reader, reader->finishProcess(ms), ms);
}
- access = false;
- if (isNull()) return;
+ return ResultHandleContinue;
+}
+void ClipReadManager::process() {
+ if (_processingInThread) {
+ _needReProcess = true;
+ return;
+ }
+
+ _timer.stop();
+ _processingInThread = thread();
+
+ uint64 ms = getms(), minms = ms + 86400 * 1000ULL;
+ {
+ QReadLocker lock(&_readerPointersMutex);
+ for (ReaderPointers::iterator it = _readerPointers.begin(), e = _readerPointers.end(); it != e; ++it) {
+ if (it->v.loadAcquire()) {
+ Readers::iterator i = _readers.find(it.key()->_private);
+ if (i == _readers.cend()) {
+ _readers.insert(it.key()->_private, 0);
+ } else {
+ i.value() = ms;
+ if (i.key()->_paused && !it.key()->_paused.loadAcquire()) {
+ i.key()->_paused = false;
+ }
+ }
+ ClipReader::Frame *frame = it.key()->frameToWrite();
+ if (frame) it.key()->_private->_request = frame->request;
+ it->v.storeRelease(0);
+ }
+ }
+ }
+
+ for (Readers::iterator i = _readers.begin(), e = _readers.end(); i != e;) {
+ ClipReaderPrivate *reader = i.key();
+ if (i.value() <= ms) {
+ ResultHandleState state = handleResult(reader, reader->process(ms), ms);
+ if (state == ResultHandleRemove) {
+ i = _readers.erase(i);
+ continue;
+ } else if (state == ResultHandleStop) {
+ _processingInThread = 0;
+ return;
+ }
+ ms = getms();
+ i.value() = reader->_nextFrameWhen ? reader->_nextFrameWhen : (ms + 86400 * 1000ULL);
+ }
+ if (!reader->_paused && i.value() < minms) {
+ minms = i.value();
+ }
+ ++i;
+ }
+
+ ms = getms();
+ if (_needReProcess || minms <= ms) {
+ _needReProcess = false;
+ _timer.start(1);
+ } else {
+ _timer.start(minms - ms);
+ }
+
+ _processingInThread = 0;
+}
+
+void ClipReadManager::finish() {
+ _timer.stop();
+ clear();
+}
+
+void ClipReadManager::clear() {
+ {
+ QWriteLocker lock(&_readerPointersMutex);
+ for (ReaderPointers::iterator it = _readerPointers.begin(), e = _readerPointers.end(); it != e; ++it) {
+ it.key()->_private = 0;
+ }
+ _readerPointers.clear();
+ }
+
+ for (Readers::iterator i = _readers.begin(), e = _readers.end(); i != e; ++i) {
+ delete i.key();
+ }
+ _readers.clear();
+}
+
+ClipReadManager::~ClipReadManager() {
+ clear();
+}
+
+MTPDocumentAttribute clipReadAnimatedAttributes(const QString &fname, const QByteArray &data, QImage &cover) {
+ FileLocation localloc(StorageFilePartial, fname);
+ QByteArray localdata(data);
+
+ FFMpegReaderImplementation *reader = new FFMpegReaderImplementation(&localloc, &localdata);
+ if (reader->start(true)) {
+ bool hasAlpha = false;
+ if (reader->readNextFrame() && reader->renderFrame(cover, hasAlpha, QSize())) {
+ if (cover.width() > 0 && cover.height() > 0 && cover.width() < cover.height() * 10 && cover.height() < cover.width() * 10) {
+ if (hasAlpha) {
+ QImage cacheForResize;
+ ClipFrameRequest request;
+ request.framew = request.outerw = cover.width();
+ request.frameh = request.outerh = cover.height();
+ request.factor = 1;
+ cover = _prepareFrame(request, cover, hasAlpha, cacheForResize).toImage();
+ }
+ int32 duration = reader->duration();
+ delete reader;
+ return MTP_documentAttributeVideo(MTP_int(duration), MTP_int(cover.width()), MTP_int(cover.height()));
+ }
+ }
+ }
delete reader;
- reader = 0;
- HistoryItem *row = msg;
- msg = 0;
- frames.clear();
- images.clear();
- delays.clear();
- w = h = frame = framesCount = duration = 0;
-
- anim::stop(this);
- if (row && !onItemRemoved) {
- row->initDimensions();
- if (App::main()) App::main()->itemResized(row, true);
- }
-}
-
-const QPixmap &AnimatedGif::current(int32 width, int32 height, bool rounded) {
- if (!width) width = w;
- if (!height) height = h;
- if ((frames[frame].isNull() || frames[frame].width() != width || frames[frame].height() != height) && !images[frame].isNull()) {
- QImage img = images[frame];
- if (img.width() != width || img.height() != height) img = img.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
- if (rounded) imageRound(img);
- frames[frame] = QPixmap::fromImage(img, Qt::ColorOnly);
- frames[frame].setDevicePixelRatio(cRetinaFactor());
- }
- return frames[frame];
+ return MTP_documentAttributeFilename(MTP_string(fname));
}
diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h
index 4f92671edc..b3aa52afa0 100644
--- a/Telegram/SourceFiles/gui/animation.h
+++ b/Telegram/SourceFiles/gui/animation.h
@@ -24,12 +24,10 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include
#include
-class Animated;
-
namespace anim {
typedef float64 (*transition)(const float64 &delta, const float64 &dt);
-
+
float64 linear(const float64 &delta, const float64 &dt);
float64 sineInOut(const float64 &delta, const float64 &dt);
float64 halfSine(const float64 &delta, const float64 &dt);
@@ -74,6 +72,8 @@ namespace anim {
_delta = 0;
}
+ typedef float64 Type;
+
private:
float64 _cur, _from, _delta;
@@ -112,6 +112,8 @@ namespace anim {
_delta = 0;
}
+ typedef int32 Type;
+
private:
int32 _cur;
@@ -181,253 +183,486 @@ namespace anim {
_delta_r = _delta_g = _delta_b = _delta_a = 0;
}
+ typedef QColor Type;
+
private:
QColor _cur;
float64 _from_r, _from_g, _from_b, _from_a, _delta_r, _delta_g, _delta_b, _delta_a;
};
- void start(Animated *obj);
- void step(Animated *obj);
- void stop(Animated *obj);
-
void startManager();
void stopManager();
};
-class Animated {
+class Animation;
+
+class AnimationImplementation {
public:
+ virtual void start() {}
+ virtual void step(Animation *a, uint64 ms, bool timer) = 0;
+ virtual ~AnimationImplementation() {}
+};
+class AnimationCreator {
+public:
+ AnimationCreator(AnimationImplementation *ptr) : _ptr(ptr) {}
+ AnimationCreator(const AnimationCreator &other) : _ptr(other.create()) {}
+ AnimationImplementation *create() const { return exchange(_ptr); }
+ ~AnimationCreator() { deleteAndMark(_ptr); }
+private:
+ AnimationCreator &operator=(const AnimationCreator &other);
+ mutable AnimationImplementation *_ptr;
+};
+class AnimationCallbacks {
+public:
+ AnimationCallbacks(const AnimationCreator &creator) : _implementation(creator.create()) {}
+ void start() { _implementation->start(); }
+ void step(Animation *a, uint64 ms, bool timer) { _implementation->step(a, ms, timer); }
+ ~AnimationCallbacks() { deleteAndMark(_implementation); }
+private:
+ AnimationCallbacks(const AnimationCallbacks &other);
+ AnimationCallbacks &operator=(const AnimationCallbacks &other);
+ AnimationImplementation *_implementation;
+};
- Animated() : animStarted(0), animInProcess(false) {
+class Animation {
+public:
+ Animation(AnimationCreator cb) : _cb(cb), _animating(false) {
}
- virtual bool animStep(float64 ms) = 0;
+ void start();
+ void stop();
- void animReset() {
- animStarted = float64(getms());
+ void step(uint64 ms, bool timer = false) {
+ _cb.step(this, ms, timer);
}
- virtual ~Animated() {
- if (animating()) {
- anim::stop(this);
- }
+ void step() {
+ step(getms(), false);
}
bool animating() const {
- return animInProcess;
- }
-
-private:
-
- float64 animStarted;
- bool animInProcess;
- friend class AnimationManager;
-
-};
-
-class AnimationFunc {
-public:
- virtual bool animStep(float64 ms) = 0;
- virtual ~AnimationFunc() {
- }
-};
-
-template
-class AnimationFuncOwned : public AnimationFunc {
-public:
- typedef bool (Type::*Method)(float64);
-
- AnimationFuncOwned(Type *obj, Method method) : _obj(obj), _method(method) {
- }
-
- bool animStep(float64 ms) {
- return (_obj->*_method)(ms);
- }
-
-private:
- Type *_obj;
- Method _method;
-
-};
-
-template
-AnimationFunc *animFunc(Type *obj, typename AnimationFuncOwned::Method method) {
- return new AnimationFuncOwned(obj, method);
-}
-
-class Animation : public Animated {
-public:
-
- Animation(AnimationFunc *func) : _func(func) {
- }
-
- void start() {
- anim::start(this);
- }
- void stop() {
- anim::stop(this);
- }
-
- //Animation
- bool animStep(float64 ms) {
- return _func->animStep(ms);
+ return _animating;
}
~Animation() {
- delete _func;
+ if (_animating) stop();
}
private:
- AnimationFunc *_func;
+ AnimationCallbacks _cb;
+ bool _animating;
};
+template
+class AnimationCallbacksRelative : public AnimationImplementation {
+public:
+ typedef void (Type::*Method)(float64, bool);
+
+ AnimationCallbacksRelative(Type *obj, Method method) : _started(0), _obj(obj), _method(method) {
+ }
+
+ void start() {
+ _started = float64(getms());
+ }
+
+ void step(Animation *a, uint64 ms, bool timer) {
+ (_obj->*_method)(ms - _started, timer);
+ }
+
+private:
+ float64 _started;
+ Type *_obj;
+ Method _method;
+
+};
+template
+AnimationCreator animation(Type *obj, typename AnimationCallbacksRelative::Method method) {
+ return AnimationCreator(new AnimationCallbacksRelative(obj, method));
+}
+
+template
+class AnimationCallbacksAbsolute : public AnimationImplementation {
+public:
+ typedef void (Type::*Method)(uint64, bool);
+
+ AnimationCallbacksAbsolute(Type *obj, Method method) : _obj(obj), _method(method) {
+ }
+
+ void step(Animation *a, uint64 ms, bool timer) {
+ (_obj->*_method)(ms, timer);
+ }
+
+private:
+ Type *_obj;
+ Method _method;
+
+};
+template
+AnimationCreator animation(Type *obj, typename AnimationCallbacksAbsolute::Method method) {
+ return AnimationCreator(new AnimationCallbacksAbsolute(obj, method));
+}
+
+template
+class AnimationCallbacksRelativeWithParam : public AnimationImplementation {
+public:
+ typedef void (Type::*Method)(Param, float64, bool);
+
+ AnimationCallbacksRelativeWithParam(Param param, Type *obj, Method method) : _started(0), _param(param), _obj(obj), _method(method) {
+ }
+
+ void start() {
+ _started = float64(getms());
+ }
+
+ void step(Animation *a, uint64 ms, bool timer) {
+ (_obj->*_method)(_param, ms - _started, timer);
+ }
+
+private:
+ float64 _started;
+ Param _param;
+ Type *_obj;
+ Method _method;
+
+};
+template
+AnimationCreator animation(Param param, Type *obj, typename AnimationCallbacksRelativeWithParam::Method method) {
+ return AnimationCreator(new AnimationCallbacksRelativeWithParam(param, obj, method));
+}
+
+template
+class AnimationCallbacksAbsoluteWithParam : public AnimationImplementation {
+public:
+ typedef void (Type::*Method)(Param, uint64, bool);
+
+ AnimationCallbacksAbsoluteWithParam(Param param, Type *obj, Method method) : _param(param), _obj(obj), _method(method) {
+ }
+
+ void step(Animation *a, uint64 ms, bool timer) {
+ (_obj->*_method)(_param, ms, timer);
+ }
+
+private:
+ Param _param;
+ Type *_obj;
+ Method _method;
+
+};
+template
+AnimationCreator animation(Param param, Type *obj, typename AnimationCallbacksAbsoluteWithParam::Method method) {
+ return AnimationCreator(new AnimationCallbacksAbsoluteWithParam(param, obj, method));
+}
+
+template
+class SimpleAnimation {
+public:
+
+ typedef Function Callback;
+
+ SimpleAnimation() : _data(0) {
+ }
+
+ bool animating(uint64 ms) {
+ if (_data && _data->_a.animating()) {
+ _data->_a.step(ms);
+ return _data && _data->_a.animating();
+ }
+ return false;
+ }
+
+ bool isNull() {
+ return !_data;
+ }
+
+ typename AnimType::Type current() {
+ return _data ? _data->a.current() : typename AnimType::Type();
+ }
+
+ typename AnimType::Type current(uint64 ms, const typename AnimType::Type &def) {
+ return animating(ms) ? current() : def;
+ }
+
+ void setup(const typename AnimType::Type &from, Callback::Creator update) {
+ if (!_data) {
+ _data = new Data(from, update, animation(this, &SimpleAnimation::step));
+ } else {
+ _data->a = AnimType(from, from);
+ }
+ }
+
+ void start(const typename AnimType::Type &to, float64 duration, anim::transition transition = anim::linear) {
+ if (_data) {
+ _data->a.start(to);
+ _data->_a.start();
+ _data->duration = duration;
+ _data->transition = transition;
+ }
+ }
+
+ ~SimpleAnimation() {
+ deleteAndMark(_data);
+ }
+
+private:
+ typedef struct _Data {
+ _Data(const typename AnimType::Type &from, Callback::Creator update, AnimationCreator acb)
+ : a(from, from)
+ , _a(acb)
+ , update(update)
+ , duration(0)
+ , transition(anim::linear) {
+ }
+ AnimType a;
+ Animation _a;
+ Callback update;
+ float64 duration;
+ anim::transition transition;
+ } Data;
+ Data *_data;
+
+ void step(float64 ms, bool timer) {
+ float64 dt = (ms >= _data->duration) ? 1 : (ms / _data->duration);
+ if (dt >= 1) {
+ _data->a.finish();
+ _data->_a.stop();
+ } else {
+ _data->a.update(dt, _data->transition);
+ }
+ if (timer) {
+ _data->update.call();
+ }
+ if (!_data->_a.animating()) {
+ delete _data;
+ _data = 0;
+ }
+ }
+
+};
+
+typedef SimpleAnimation FloatAnimation;
+typedef SimpleAnimation IntAnimation;
+typedef SimpleAnimation ColorAnimation;
+
+#define EnsureAnimation(animation, from, callback) if ((animation).isNull()) { (animation).setup((from), (callback)); }
+
+class ClipReader;
+
class AnimationManager : public QObject {
Q_OBJECT
public:
+ AnimationManager();
- AnimationManager() : timer(this), iterating(false) {
- timer.setSingleShot(false);
- connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
- }
-
- void start(Animated *obj) {
- obj->animReset();
- if (iterating) {
- toStart.insert(obj);
- if (!toStop.isEmpty()) {
- toStop.remove(obj);
- }
- } else {
- if (!objs.size()) {
- timer.start(AnimationTimerDelta);
- }
- objs.insert(obj);
- }
- obj->animInProcess = true;
- }
-
- void step(Animated *obj) {
- if (iterating) return;
-
- float64 ms = float64(getms());
- AnimObjs::iterator i = objs.find(obj);
- if (i != objs.cend()) {
- Animated *obj = *i;
- if (!obj->animStep(ms - obj->animStarted)) {
- objs.erase(i);
- if (!objs.size()) {
- timer.stop();
- }
- obj->animInProcess = false;
- }
- }
- }
-
- void stop(Animated *obj) {
- if (iterating) {
- toStop.insert(obj);
- if (!toStart.isEmpty()) {
- toStart.insert(obj);
- }
- } else {
- AnimObjs::iterator i = objs.find(obj);
- if (i != objs.cend()) {
- objs.erase(i);
- if (!objs.size()) {
- timer.stop();
- }
- }
- }
- obj->animInProcess = false;
- }
+ void start(Animation *obj);
+ void stop(Animation *obj);
public slots:
- void timeout() {
- iterating = true;
- float64 ms = float64(getms());
- for (AnimObjs::iterator i = objs.begin(), e = objs.end(); i != e; ) {
- Animated *obj = *i;
- if (!obj->animStep(ms - obj->animStarted)) {
- i = objs.erase(i);
- obj->animInProcess = false;
- } else {
- ++i;
- }
- }
- iterating = false;
- if (!toStart.isEmpty()) {
- for (AnimObjs::iterator i = toStart.begin(), e = toStart.end(); i != e; ++i) {
- objs.insert(*i);
- }
- toStart.clear();
- }
- if (!toStop.isEmpty()) {
- for (AnimObjs::iterator i = toStop.begin(), e = toStop.end(); i != e; ++i) {
- objs.remove(*i);
- }
- toStop.clear();
- }
- if (!objs.size()) {
- timer.stop();
- }
- }
+ void timeout();
+
+ void clipCallback(ClipReader *reader, qint32 threadIndex, qint32 notification);
private:
-
- typedef QSet AnimObjs;
- AnimObjs objs;
- AnimObjs toStart;
- AnimObjs toStop;
- QTimer timer;
- bool iterating;
+ typedef QMap AnimatingObjects;
+ AnimatingObjects _objects, _starting, _stopping;
+ QTimer _timer;
+ bool _iterating;
};
-class HistoryItem;
class FileLocation;
-class AnimatedGif : public QObject, public Animated {
+
+enum ClipState {
+ ClipReading,
+ ClipError,
+};
+
+struct ClipFrameRequest {
+ ClipFrameRequest() : factor(0), framew(0), frameh(0), outerw(0), outerh(0), rounded(false) {
+ }
+ bool valid() const {
+ return factor > 0;
+ }
+ int32 factor;
+ int32 framew, frameh;
+ int32 outerw, outerh;
+ bool rounded;
+};
+
+enum ClipReaderNotification {
+ ClipReaderReinit,
+ ClipReaderRepaint,
+};
+
+enum ClipReaderSteps {
+ WaitingForDimensionsStep = -3, // before ClipReaderPrivate read the first image and got the original frame size
+ WaitingForRequestStep = -2, // before ClipReader got the original frame size and prepared the frame request
+ WaitingForFirstFrameStep = -1, // before ClipReaderPrivate got the frame request and started waiting for the 1-2 delay
+};
+
+class ClipReaderPrivate;
+class ClipReader {
+public:
+
+ typedef Function1 Callback;
+
+ ClipReader(const FileLocation &location, const QByteArray &data, Callback::Creator cb);
+ static void callback(ClipReader *reader, int32 threadIndex, ClipReaderNotification notification); // reader can be deleted
+
+ void setAutoplay() {
+ _autoplay = true;
+ }
+ bool autoplay() const {
+ return _autoplay;
+ }
+
+ void start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded);
+ QPixmap current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms);
+ QPixmap frameOriginal() const {
+ Frame *frame = frameToShow();
+ if (!frame) return QPixmap();
+ QPixmap result(frame ? QPixmap::fromImage(frame->original) : QPixmap());
+ result.detach();
+ return result;
+ }
+ bool currentDisplayed() const {
+ Frame *frame = frameToShow();
+ return frame ? (frame->displayed.loadAcquire() != 0) : true;
+ }
+ bool paused() const {
+ return _paused.loadAcquire();
+ }
+ int32 threadIndex() const {
+ return _threadIndex;
+ }
+
+ int32 width() const;
+ int32 height() const;
+
+ ClipState state() const;
+ bool started() const {
+ int32 step = _step.loadAcquire();
+ return (step == WaitingForFirstFrameStep) || (step >= 0);
+ }
+ bool ready() const;
+
+ void stop();
+ void error();
+
+ ~ClipReader();
+
+private:
+
+ Callback _cb;
+
+ ClipState _state;
+
+ mutable int32 _width, _height;
+
+ mutable QAtomicInt _step; // -2, -1 - init, 0-5 - work, show ((state + 1) / 2) % 3 state, write ((state + 3) / 2) % 3
+ struct Frame {
+ Frame() : displayed(false) {
+ }
+ void clear() {
+ pix = QPixmap();
+ original = QImage();
+ }
+ QPixmap pix;
+ QImage original;
+ ClipFrameRequest request;
+ QAtomicInt displayed;
+ };
+ mutable Frame _frames[3];
+ Frame *frameToShow(int32 *index = 0) const; // 0 means not ready
+ Frame *frameToWrite(int32 *index = 0) const; // 0 means not ready
+ Frame *frameToWriteNext(bool check, int32 *index = 0) const;
+ void moveToNextShow() const;
+ void moveToNextWrite() const;
+
+ QAtomicInt _paused;
+ int32 _threadIndex;
+
+ bool _autoplay;
+
+ friend class ClipReadManager;
+
+ ClipReaderPrivate *_private;
+
+};
+
+static ClipReader * const BadClipReader = SharedMemoryLocation();
+
+enum ClipProcessResult {
+ ClipProcessError,
+ ClipProcessStarted,
+ ClipProcessPaused,
+ ClipProcessRepaint,
+ ClipProcessCopyFrame,
+ ClipProcessWait,
+};
+
+class ClipReadManager : public QObject {
Q_OBJECT
public:
- AnimatedGif() : msg(0), file(0), access(false), reader(0), w(0), h(0), frame(0), framesCount(0), duration(0) {
+ ClipReadManager(QThread *thread);
+ int32 loadLevel() const {
+ return _loadLevel.load();
}
-
- bool animStep(float64 ms);
-
- void start(HistoryItem *row, const FileLocation &file);
- void stop(bool onItemRemoved = false);
-
- bool isNull() const {
- return !reader;
- }
-
- ~AnimatedGif() {
- stop(true);
- }
-
- const QPixmap ¤t(int32 width = 0, int32 height = 0, bool rounded = false);
+ void append(ClipReader *reader, const FileLocation &location, const QByteArray &data);
+ void start(ClipReader *reader);
+ void update(ClipReader *reader);
+ void stop(ClipReader *reader);
+ bool carries(ClipReader *reader) const;
+ ~ClipReadManager();
signals:
- void updated();
+ void processDelayed();
-public:
+ void callback(ClipReader *reader, qint32 threadIndex, qint32 notification);
- HistoryItem *msg;
- QImage img;
- FileLocation *file;
- bool access;
- QImageReader *reader;
- int32 w, h, frame;
+public slots:
+
+ void process();
+ void finish();
private:
- QVector frames;
- QVector images;
- QVector delays;
- int32 framesCount, duration;
+ void clear();
+
+ QAtomicInt _loadLevel;
+ struct MutableAtomicInt {
+ MutableAtomicInt(int value) : v(value) {
+ }
+ mutable QAtomicInt v;
+ };
+ typedef QMap ReaderPointers;
+ ReaderPointers _readerPointers;
+ mutable QReadWriteLock _readerPointersMutex;
+
+ ReaderPointers::const_iterator constUnsafeFindReaderPointer(ClipReaderPrivate *reader) const;
+ ReaderPointers::iterator unsafeFindReaderPointer(ClipReaderPrivate *reader);
+
+ bool handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms);
+
+ enum ResultHandleState {
+ ResultHandleRemove,
+ ResultHandleStop,
+ ResultHandleContinue,
+ };
+ ResultHandleState handleResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms);
+
+ typedef QMap Readers;
+ Readers _readers;
+
+ QTimer _timer;
+ QThread *_processingInThread;
+ bool _needReProcess;
+
};
+
+MTPDocumentAttribute clipReadAnimatedAttributes(const QString &fname, const QByteArray &data, QImage &cover);
diff --git a/Telegram/SourceFiles/gui/countryinput.cpp b/Telegram/SourceFiles/gui/countryinput.cpp
index 53719fa5c9..5405737503 100644
--- a/Telegram/SourceFiles/gui/countryinput.cpp
+++ b/Telegram/SourceFiles/gui/countryinput.cpp
@@ -137,7 +137,7 @@ void CountryInput::mousePressEvent(QMouseEvent *e) {
if (_active) {
CountrySelectBox *box = new CountrySelectBox();
connect(box, SIGNAL(countryChosen(const QString&)), this, SLOT(onChooseCountry(const QString&)));
- App::wnd()->showLayer(box);
+ Ui::showLayer(box);
}
}
@@ -152,7 +152,7 @@ void CountryInput::leaveEvent(QEvent *e) {
}
void CountryInput::onChooseCode(const QString &code) {
- App::wnd()->hideLayer();
+ Ui::hideLayer();
if (code.length()) {
CountriesByCode::const_iterator i = _countriesByCode.constFind(code);
if (i != _countriesByCode.cend()) {
@@ -169,7 +169,7 @@ void CountryInput::onChooseCode(const QString &code) {
}
bool CountryInput::onChooseCountry(const QString &iso) {
- App::wnd()->hideLayer();
+ Ui::hideLayer();
CountriesByISO2::const_iterator i = _countriesByISO2.constFind(iso);
const CountryInfo *info = (i == _countriesByISO2.cend()) ? 0 : (*i);
diff --git a/Telegram/SourceFiles/gui/emoji_config.h b/Telegram/SourceFiles/gui/emoji_config.h
index e377374ba1..1734d3d827 100644
--- a/Telegram/SourceFiles/gui/emoji_config.h
+++ b/Telegram/SourceFiles/gui/emoji_config.h
@@ -83,7 +83,7 @@ inline EmojiPtr emojiFromUrl(const QString &url) {
return emojiFromKey(url.midRef(10).toULongLong(0, 16)); // skip emoji://e.
}
-inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) {
+inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int *plen = 0) {
EmojiPtr emoji = 0;
if (ch + 1 < e && ((ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) || (((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0x20E3))) {
uint32 code = (ch->unicode() << 16) | (ch + 1)->unicode();
@@ -108,15 +108,15 @@ inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) {
} else if (ch + 2 < e && ((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0xFE0F && (ch + 2)->unicode() == 0x20E3) {
uint32 code = (ch->unicode() << 16) | (ch + 2)->unicode();
emoji = emojiGet(code);
- len = emoji->len + 1;
+ if (plen) *plen = emoji->len + 1;
return emoji;
} else if (ch < e) {
emoji = emojiGet(ch->unicode());
- Q_ASSERT(emoji != TwoSymbolEmoji);
+ t_assert(emoji != TwoSymbolEmoji);
}
if (emoji) {
- len = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
+ int32 len = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
if (emoji->color && (ch + len + 1 < e && (ch + len)->isHighSurrogate() && (ch + len + 1)->isLowSurrogate())) { // color
uint32 color = ((uint32((ch + len)->unicode()) << 16) | uint32((ch + len + 1)->unicode()));
EmojiPtr col = emojiGet(emoji, color);
@@ -128,8 +128,21 @@ inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) {
}
}
}
+ if (plen) *plen = len;
+ }
+
+ return emoji;
+}
+
+inline EmojiPtr emojiFromText(const QString &text, int32 *plen = 0) {
+ return text.isEmpty() ? EmojiPtr(0) : emojiFromText(text.constBegin(), text.constEnd(), plen);
+}
+
+inline EmojiPtr emojiGetNoColor(EmojiPtr emoji) {
+ if (emoji && emoji->color && (emoji->color & 0xFFFF0000U) != 0xFFFF0000U) {
+ EmojiPtr result = emojiGet(emoji->code);
+ return (result == TwoSymbolEmoji) ? emojiGet(emoji->code, emoji->code2) : result;
}
-
return emoji;
}
@@ -180,7 +193,7 @@ inline QString replaceEmojis(const QString &text, EntitiesInText &entities) {
if (canFindEmoji) {
emojiFind(ch, e, newEmojiEnd, emojiCode);
}
-
+
while (currentEntity < entitiesCount && ch >= emojiStart + entities[currentEntity].offset + entities[currentEntity].length) {
++currentEntity;
}
diff --git a/Telegram/SourceFiles/gui/flatbutton.cpp b/Telegram/SourceFiles/gui/flatbutton.cpp
index e881a5af11..1df0ac6642 100644
--- a/Telegram/SourceFiles/gui/flatbutton.cpp
+++ b/Telegram/SourceFiles/gui/flatbutton.cpp
@@ -21,10 +21,14 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "gui/flatbutton.h"
-FlatButton::FlatButton(QWidget *parent, const QString &text, const style::flatButton &st) : Button(parent),
- _text(text),
- _st(st), _autoFontPadding(0),
- a_bg(st.bgColor->c), a_text(st.color->c), _opacity(1) {
+FlatButton::FlatButton(QWidget *parent, const QString &text, const style::flatButton &st) : Button(parent)
+, _text(text)
+, _st(st)
+, _autoFontPadding(0)
+, a_bg(st.bgColor->c)
+, a_text(st.color->c)
+, _a_appearance(animation(this, &FlatButton::step_appearance))
+, _opacity(1) {
if (_st.width < 0) {
_st.width = textWidth() - _st.width;
} else if (!_st.width) {
@@ -88,19 +92,17 @@ void FlatButton::resizeEvent(QResizeEvent *e) {
return Button::resizeEvent(e);
}
-bool FlatButton::animStep(float64 ms) {
+void FlatButton::step_appearance(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
- bool res = true;
if (dt >= 1) {
+ _a_appearance.stop();
a_bg.finish();
a_text.finish();
- res = false;
} else {
a_bg.update(dt, anim::linear);
a_text.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
void FlatButton::onStateChange(int oldState, ButtonStateChangeSource source) {
@@ -110,12 +112,12 @@ void FlatButton::onStateChange(int oldState, ButtonStateChangeSource source) {
a_bg.start(bgColorTo->c);
a_text.start(colorTo->c);
if (source == ButtonByUser || source == ButtonByPress) {
- anim::stop(this);
+ _a_appearance.stop();
a_bg.finish();
a_text.finish();
update();
} else {
- anim::start(this);
+ _a_appearance.start();
}
}
@@ -164,8 +166,14 @@ void LinkButton::onStateChange(int oldState, ButtonStateChangeSource source) {
LinkButton::~LinkButton() {
}
-IconedButton::IconedButton(QWidget *parent, const style::iconedButton &st, const QString &text) : Button(parent),
- _text(text), _st(st), _width(_st.width), a_opacity(_st.opacity), a_bg(_st.bgColor->c), _opacity(1) {
+IconedButton::IconedButton(QWidget *parent, const style::iconedButton &st, const QString &text) : Button(parent)
+, _text(text)
+, _st(st)
+, _width(_st.width)
+, a_opacity(_st.opacity)
+, a_bg(_st.bgColor->c)
+, _a_appearance(animation(this, &IconedButton::step_appearance))
+, _opacity(1) {
if (_width < 0) {
_width = _st.font->width(text) - _width;
@@ -199,25 +207,23 @@ QString IconedButton::getText() const {
return _text;
}
-bool IconedButton::animStep(float64 ms) {
- bool res = true;
+void IconedButton::step_appearance(float64 ms, bool timer) {
if (_st.duration <= 1) {
+ _a_appearance.stop();
a_opacity.finish();
a_bg.finish();
- res = false;
} else {
float64 dt = ms / _st.duration;
if (dt >= 1) {
+ _a_appearance.stop();
a_opacity.finish();
a_bg.finish();
- res = false;
} else {
a_opacity.update(dt, anim::linear);
a_bg.update(dt, anim::linear);
}
}
- update();
- return res;
+ if (timer) update();
}
void IconedButton::onStateChange(int oldState, ButtonStateChangeSource source) {
@@ -225,12 +231,12 @@ void IconedButton::onStateChange(int oldState, ButtonStateChangeSource source) {
a_bg.start(((_state & (StateOver | StateDown)) ? _st.overBgColor : _st.bgColor)->c);
if (source == ButtonByUser || source == ButtonByPress) {
- anim::stop(this);
+ _a_appearance.stop();
a_opacity.finish();
a_bg.finish();
update();
} else {
- anim::start(this);
+ _a_appearance.start();
}
}
@@ -283,10 +289,65 @@ void MaskedButton::paintEvent(QPaintEvent *e) {
}
}
-BoxButton::BoxButton(QWidget *parent, const QString &text, const style::BoxButton &st) : Button(parent),
-_text(text.toUpper()), _fullText(text.toUpper()), _textWidth(st.font->width(_text)),
-_st(st),
-a_textBgOverOpacity(0), a_textFg(st.textFg->c), _a_over(animFunc(this, &BoxButton::animStep_over)) {
+EmojiButton::EmojiButton(QWidget *parent, const style::iconedButton &st) : IconedButton(parent, st)
+, _loading(false)
+, _a_loading(animation(this, &EmojiButton::step_loading)) {
+}
+
+void EmojiButton::paintEvent(QPaintEvent *e) {
+ QPainter p(this);
+
+ uint64 ms = getms();
+ float64 loading = a_loading.current(ms, _loading ? 1 : 0);
+ p.setOpacity(_opacity * (1 - loading));
+
+ p.fillRect(e->rect(), a_bg.current());
+
+ p.setOpacity(a_opacity.current() * _opacity * (1 - loading));
+
+ const QRect &i((_state & StateDown) ? _st.downIcon : _st.icon);
+ if (i.width()) {
+ const QPoint &t((_state & StateDown) ? _st.downIconPos : _st.iconPos);
+ p.drawPixmap(t, App::sprite(), i);
+ }
+
+ p.setOpacity(a_opacity.current() * _opacity);
+ p.setPen(QPen(st::emojiCircleFg, st::emojiCircleLine));
+ p.setBrush(Qt::NoBrush);
+
+ p.setRenderHint(QPainter::HighQualityAntialiasing);
+ QRect inner(QPoint((width() - st::emojiCircle.width()) / 2, st::emojiCircleTop), st::emojiCircle);
+ if (loading > 0) {
+ int32 full = 5760;
+ int32 start = qRound(full * float64(ms % uint64(st::emojiCirclePeriod)) / st::emojiCirclePeriod), part = qRound(loading * full / st::emojiCirclePart);
+ p.drawArc(inner, start, full - part);
+ } else {
+ p.drawEllipse(inner);
+ }
+ p.setRenderHint(QPainter::HighQualityAntialiasing, false);
+}
+
+void EmojiButton::setLoading(bool loading) {
+ if (_loading != loading) {
+ EnsureAnimation(a_loading, _loading ? 1. : 0., func(this, &EmojiButton::update));
+ a_loading.start(loading ? 1. : 0., st::emojiCircleDuration);
+ _loading = loading;
+ if (_loading) {
+ _a_loading.start();
+ } else {
+ _a_loading.stop();
+ }
+ }
+}
+
+BoxButton::BoxButton(QWidget *parent, const QString &text, const style::BoxButton &st) : Button(parent)
+, _text(text.toUpper())
+, _fullText(text.toUpper())
+, _textWidth(st.font->width(_text))
+, _st(st)
+, a_textBgOverOpacity(0)
+, a_textFg(st.textFg->c)
+, _a_over(animation(this, &BoxButton::step_over)) {
if (_st.width <= 0) {
resize(_textWidth - _st.width, _st.height);
} else {
@@ -322,19 +383,17 @@ void BoxButton::paintEvent(QPaintEvent *e) {
p.drawText((width() - _textWidth) / 2, _st.textTop + _st.font->ascent, _text);
}
-bool BoxButton::animStep_over(float64 ms) {
+void BoxButton::step_over(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
- bool res = true;
if (dt >= 1) {
+ _a_over.stop();
a_textFg.finish();
a_textBgOverOpacity.finish();
- res = false;
} else {
a_textFg.update(dt, anim::linear);
a_textBgOverOpacity.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
void BoxButton::onStateChange(int oldState, ButtonStateChangeSource source) {
diff --git a/Telegram/SourceFiles/gui/flatbutton.h b/Telegram/SourceFiles/gui/flatbutton.h
index c6f0dc54bc..f07b0c4a25 100644
--- a/Telegram/SourceFiles/gui/flatbutton.h
+++ b/Telegram/SourceFiles/gui/flatbutton.h
@@ -25,7 +25,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "gui/animation.h"
#include "style.h"
-class FlatButton : public Button, public Animated {
+class FlatButton : public Button {
Q_OBJECT
public:
@@ -34,7 +34,7 @@ public:
void resizeEvent(QResizeEvent *e);
- bool animStep(float64 ms);
+ void step_appearance(float64 ms, bool timer);
void paintEvent(QPaintEvent *e);
void setOpacity(float64 o);
float64 opacity() const;
@@ -63,7 +63,10 @@ private:
style::font _autoFont;
anim::cvalue a_bg, a_text;
+ Animation _a_appearance;
+
float64 _opacity;
+
};
class LinkButton : public Button {
@@ -89,21 +92,21 @@ private:
style::linkButton _st;
};
-class IconedButton : public Button, public Animated {
+class IconedButton : public Button {
Q_OBJECT
public:
IconedButton(QWidget *parent, const style::iconedButton &st, const QString &text = QString());
- bool animStep(float64 ms);
+ void step_appearance(float64 ms, bool timer);
void paintEvent(QPaintEvent *e);
void setOpacity(float64 o);
void setText(const QString &text);
QString getText() const;
-
+
public slots:
void onStateChange(int oldState, ButtonStateChangeSource source);
@@ -117,6 +120,7 @@ protected:
anim::fvalue a_opacity;
anim::cvalue a_bg;
+ Animation _a_appearance;
float64 _opacity;
};
@@ -132,6 +136,28 @@ public:
};
+class EmojiButton : public IconedButton {
+ Q_OBJECT
+
+public:
+ EmojiButton(QWidget *parent, const style::iconedButton &st);
+
+ void paintEvent(QPaintEvent *e);
+ void setLoading(bool loading);
+
+private:
+ bool _loading;
+ FloatAnimation a_loading;
+ Animation _a_loading;
+
+ void step_loading(uint64 ms, bool timer) {
+ if (timer) {
+ update();
+ }
+ }
+
+};
+
class BoxButton : public Button {
Q_OBJECT
@@ -141,7 +167,7 @@ public:
void paintEvent(QPaintEvent *e);
- bool animStep_over(float64 ms);
+ void step_over(float64 ms, bool timer);
public slots:
diff --git a/Telegram/SourceFiles/gui/flatcheckbox.cpp b/Telegram/SourceFiles/gui/flatcheckbox.cpp
index 7e4b8225ee..bace80fc7c 100644
--- a/Telegram/SourceFiles/gui/flatcheckbox.cpp
+++ b/Telegram/SourceFiles/gui/flatcheckbox.cpp
@@ -24,8 +24,13 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "flatcheckbox.h"
-FlatCheckbox::FlatCheckbox(QWidget *parent, const QString &text, bool checked, const style::flatCheckbox &st) : Button(parent),
- _st(st), a_over(0, 0), _text(text), _opacity(1), _checked(checked) {
+FlatCheckbox::FlatCheckbox(QWidget *parent, const QString &text, bool checked, const style::flatCheckbox &st) : Button(parent)
+, _st(st)
+, a_over(0, 0)
+, _a_appearance(animation(this, &FlatCheckbox::step_appearance))
+, _text(text)
+, _opacity(1)
+, _checked(checked) {
connect(this, SIGNAL(clicked()), this, SLOT(onClicked()));
connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource)));
setCursor(_st.cursor);
@@ -60,17 +65,17 @@ void FlatCheckbox::onClicked() {
void FlatCheckbox::onStateChange(int oldState, ButtonStateChangeSource source) {
if ((_state & StateOver) && !(oldState & StateOver)) {
a_over.start(1);
- anim::start(this);
+ _a_appearance.start();
} else if (!(_state & StateOver) && (oldState & StateOver)) {
a_over.start(0);
- anim::start(this);
+ _a_appearance.start();
}
if ((_state & StateDisabled) && !(oldState & StateDisabled)) {
setCursor(_st.disabledCursor);
- anim::start(this);
+ _a_appearance.start();
} else if (!(_state & StateDisabled) && (oldState & StateDisabled)) {
setCursor(_st.cursor);
- anim::start(this);
+ _a_appearance.start();
}
}
@@ -114,17 +119,15 @@ void FlatCheckbox::paintEvent(QPaintEvent *e) {
}
}
-bool FlatCheckbox::animStep(float64 ms) {
+void FlatCheckbox::step_appearance(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
- bool res = true;
if (dt >= 1) {
+ _a_appearance.stop();
a_over.finish();
- res = false;
} else {
a_over.update(dt, _st.bgFunc);
}
- update();
- return res;
+ if (timer) update();
}
template
@@ -135,7 +138,8 @@ public:
TemplateRadiobuttonsGroup(const QString &name) : _name(name), _val(0) {
}
- void remove(Type * const &radio);
+ void remove(Type * const &radio) {
+ }
int32 val() const {
return _val;
}
@@ -232,12 +236,16 @@ FlatRadiobutton::~FlatRadiobutton() {
reinterpret_cast(_group)->remove(this);
}
-Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const style::Checkbox &st) : Button(parent),
-_st(st),
-a_over(0), a_checked(checked ? 1 : 0),
-_a_over(animFunc(this, &Checkbox::animStep_over)), _a_checked(animFunc(this, &Checkbox::animStep_checked)),
-_text(text), _fullText(text), _textWidth(st.font->width(text)),
-_checked(checked) {
+Checkbox::Checkbox(QWidget *parent, const QString &text, bool checked, const style::Checkbox &st) : Button(parent)
+, _st(st)
+, a_over(0)
+, a_checked(checked ? 1 : 0)
+, _a_over(animation(this, &Checkbox::step_over))
+, _a_checked(animation(this, &Checkbox::step_checked))
+, _text(text)
+, _fullText(text)
+, _textWidth(st.font->width(text))
+, _checked(checked) {
if (_st.width <= 0) {
resize(_textWidth - _st.width, _st.height);
} else {
@@ -275,30 +283,26 @@ void Checkbox::setChecked(bool checked) {
}
}
-bool Checkbox::animStep_over(float64 ms) {
+void Checkbox::step_over(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
- bool res = true;
if (dt >= 1) {
+ _a_over.stop();
a_over.finish();
- res = false;
} else {
a_over.update(dt, anim::linear);
}
- update(_checkRect);
- return res;
+ if (timer) update(_checkRect);
}
-bool Checkbox::animStep_checked(float64 ms) {
+void Checkbox::step_checked(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
- bool res = true;
if (dt >= 1) {
a_checked.finish();
- res = false;
+ _a_checked.stop();
} else {
a_checked.update(dt, anim::linear);
}
- update(_checkRect);
- return res;
+ if (timer) update(_checkRect);
}
void Checkbox::paintEvent(QPaintEvent *e) {
@@ -372,12 +376,18 @@ void Checkbox::onStateChange(int oldState, ButtonStateChangeSource source) {
}
}
-Radiobutton::Radiobutton(QWidget *parent, const QString &group, int32 value, const QString &text, bool checked, const style::Radiobutton &st) : Button(parent),
-_st(st),
-a_over(0), a_checked(checked ? 1 : 0),
-_a_over(animFunc(this, &Radiobutton::animStep_over)), _a_checked(animFunc(this, &Radiobutton::animStep_checked)),
-_text(text), _fullText(text), _textWidth(st.font->width(text)),
-_checked(checked), _group(radiobuttons.reg(group)), _value(value) {
+Radiobutton::Radiobutton(QWidget *parent, const QString &group, int32 value, const QString &text, bool checked, const style::Radiobutton &st) : Button(parent)
+, _st(st)
+, a_over(0)
+, a_checked(checked ? 1 : 0)
+, _a_over(animation(this, &Radiobutton::step_over))
+, _a_checked(animation(this, &Radiobutton::step_checked))
+, _text(text)
+, _fullText(text)
+, _textWidth(st.font->width(text))
+, _checked(checked)
+, _group(radiobuttons.reg(group))
+, _value(value) {
if (_st.width <= 0) {
resize(_textWidth - _st.width, _st.height);
} else {
@@ -419,30 +429,26 @@ void Radiobutton::setChecked(bool checked) {
}
}
-bool Radiobutton::animStep_over(float64 ms) {
+void Radiobutton::step_over(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
- bool res = true;
if (dt >= 1) {
+ _a_over.stop();
a_over.finish();
- res = false;
} else {
a_over.update(dt, anim::linear);
}
- update(_checkRect);
- return res;
+ if (timer) update(_checkRect);
}
-bool Radiobutton::animStep_checked(float64 ms) {
+void Radiobutton::step_checked(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
- bool res = true;
if (dt >= 1) {
a_checked.finish();
- res = false;
+ _a_checked.stop();
} else {
a_checked.update(dt, anim::linear);
}
- update(_checkRect);
- return res;
+ if (timer) update(_checkRect);
}
void Radiobutton::paintEvent(QPaintEvent *e) {
diff --git a/Telegram/SourceFiles/gui/flatcheckbox.h b/Telegram/SourceFiles/gui/flatcheckbox.h
index c0296e405e..560045fdfe 100644
--- a/Telegram/SourceFiles/gui/flatcheckbox.h
+++ b/Telegram/SourceFiles/gui/flatcheckbox.h
@@ -22,7 +22,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "gui/button.h"
-class FlatCheckbox : public Button, public Animated {
+class FlatCheckbox : public Button {
Q_OBJECT
public:
@@ -32,7 +32,7 @@ public:
bool checked() const;
void setChecked(bool checked);
- bool animStep(float64 ms);
+ void step_appearance(float64 ms, bool timer);
void paintEvent(QPaintEvent *e);
void setOpacity(float64 o);
@@ -50,6 +50,8 @@ private:
style::flatCheckbox _st;
anim::fvalue a_over;
+ Animation _a_appearance;
+
QString _text;
style::font _font;
@@ -91,8 +93,8 @@ public:
bool checked() const;
void setChecked(bool checked);
- bool animStep_over(float64 ms);
- bool animStep_checked(float64 ms);
+ void step_over(float64 ms, bool timer);
+ void step_checked(float64 ms, bool timer);
void paintEvent(QPaintEvent *e);
@@ -133,8 +135,8 @@ public:
return _value;
}
- bool animStep_over(float64 ms);
- bool animStep_checked(float64 ms);
+ void step_over(float64 ms, bool timer);
+ void step_checked(float64 ms, bool timer);
void paintEvent(QPaintEvent *e);
diff --git a/Telegram/SourceFiles/gui/flatinput.cpp b/Telegram/SourceFiles/gui/flatinput.cpp
index f217b16f73..a7f4241a02 100644
--- a/Telegram/SourceFiles/gui/flatinput.cpp
+++ b/Telegram/SourceFiles/gui/flatinput.cpp
@@ -51,12 +51,22 @@ namespace {
InputStyle _inputFieldStyle;
}
-FlatInput::FlatInput(QWidget *parent, const style::flatInput &st, const QString &pholder, const QString &v) : QLineEdit(v, parent),
-_oldtext(v), _fullph(pholder), _fastph(false), _customUpDown(false), _phVisible(!v.length()),
-a_phLeft(_phVisible ? 0 : st.phShift), a_phAlpha(_phVisible ? 1 : 0), a_phColor(st.phColor->c),
-a_borderColor(st.borderColor->c), a_bgColor(st.bgColor->c), _notingBene(0), _st(st) {
+FlatInput::FlatInput(QWidget *parent, const style::flatInput &st, const QString &pholder, const QString &v) : QLineEdit(v, parent)
+, _oldtext(v)
+, _fullph(pholder)
+, _fastph(false)
+, _customUpDown(false)
+, _phVisible(!v.length())
+, a_phLeft(_phVisible ? 0 : st.phShift)
+, a_phAlpha(_phVisible ? 1 : 0)
+, a_phColor(st.phColor->c)
+, a_borderColor(st.borderColor->c)
+, a_bgColor(st.bgColor->c)
+, _a_appearance(animation(this, &FlatInput::step_appearance))
+, _notingBene(0)
+, _st(st) {
resize(_st.width, _st.height);
-
+
setFont(_st.font->f);
setAlignment(_st.align);
@@ -158,7 +168,7 @@ void FlatInput::paintEvent(QPaintEvent *e) {
}
bool phDraw = _phVisible;
- if (animating()) {
+ if (_a_appearance.animating()) {
p.setOpacity(a_phAlpha.current());
phDraw = true;
}
@@ -180,7 +190,7 @@ void FlatInput::focusInEvent(QFocusEvent *e) {
a_borderColor.start(_st.borderActive->c);
}
a_bgColor.start(_st.bgActive->c);
- anim::start(this);
+ _a_appearance.start();
QLineEdit::focusInEvent(e);
emit focused();
}
@@ -191,7 +201,7 @@ void FlatInput::focusOutEvent(QFocusEvent *e) {
a_borderColor.start(_st.borderColor->c);
}
a_bgColor.start(_st.bgColor->c);
- anim::start(this);
+ _a_appearance.start();
QLineEdit::focusOutEvent(e);
emit blurred();
}
@@ -224,11 +234,10 @@ QSize FlatInput::minimumSizeHint() const {
return geometry().size();
}
-bool FlatInput::animStep(float64 ms) {
+void FlatInput::step_appearance(float64 ms, bool timer) {
float dt = ms / _st.phDuration;
- bool res = true;
if (dt >= 1) {
- res = false;
+ _a_appearance.stop();
a_phLeft.finish();
a_phAlpha.finish();
a_phColor.finish();
@@ -236,8 +245,8 @@ bool FlatInput::animStep(float64 ms) {
if (_notingBene > 0) {
_notingBene = -1;
a_borderColor.start((hasFocus() ? _st.borderActive : _st.borderColor)->c);
- anim::start(this);
- return true;
+ _a_appearance.start();
+ return;
} else if (_notingBene) {
_notingBene = 0;
}
@@ -249,8 +258,7 @@ bool FlatInput::animStep(float64 ms) {
a_bgColor.update(dt, _st.phColorFunc);
a_borderColor.update(dt, _st.phColorFunc);
}
- update();
- return res;
+ if (timer) update();
}
void FlatInput::setPlaceholder(const QString &ph) {
@@ -279,7 +287,7 @@ void FlatInput::updatePlaceholder() {
} else {
a_phLeft.start(vis ? 0 : _st.phShift);
a_phAlpha.start(vis ? 1 : 0);
- anim::start(this);
+ _a_appearance.start();
}
_phVisible = vis;
}
@@ -345,11 +353,11 @@ void FlatInput::notaBene() {
_notingBene = 1;
setFocus();
a_borderColor.start(_st.borderError->c);
- anim::start(this);
+ _a_appearance.start();
}
-CountryCodeInput::CountryCodeInput(QWidget *parent, const style::flatInput &st) : FlatInput(parent, st), _nosignal(false) {
-
+CountryCodeInput::CountryCodeInput(QWidget *parent, const style::flatInput &st) : FlatInput(parent, st)
+, _nosignal(false) {
}
void CountryCodeInput::startErasing(QKeyEvent *e) {
@@ -541,38 +549,39 @@ void PhonePartInput::onChooseCode(const QString &code) {
updatePlaceholder();
}
-InputArea::InputArea(QWidget *parent, const style::InputArea &st, const QString &ph, const QString &val) : TWidget(parent),
-_maxLength(-1),
-_inner(this),
-_oldtext(val),
+InputArea::InputArea(QWidget *parent, const style::InputArea &st, const QString &ph, const QString &val) : TWidget(parent)
+, _maxLength(-1)
+, _inner(this)
+, _oldtext(val)
-_ctrlEnterSubmit(CtrlEnterSubmitCtrlEnter),
-_undoAvailable(false),
-_redoAvailable(false),
-_inHeightCheck(false),
+, _ctrlEnterSubmit(CtrlEnterSubmitCtrlEnter)
+, _undoAvailable(false)
+, _redoAvailable(false)
+, _inHeightCheck(false)
-_customUpDown(false),
+, _customUpDown(false)
-_placeholderFull(ph),
-_placeholderVisible(val.isEmpty()),
-a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift),
-a_placeholderOpacity(_placeholderVisible ? 1 : 0),
-a_placeholderFg(st.placeholderFg->c),
-_a_placeholderFg(animFunc(this, &InputArea::animStep_placeholderFg)),
-_a_placeholderShift(animFunc(this, &InputArea::animStep_placeholderShift)),
+, _placeholderFull(ph)
+, _placeholderVisible(val.isEmpty())
+, a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift)
+, a_placeholderOpacity(_placeholderVisible ? 1 : 0)
+, a_placeholderFg(st.placeholderFg->c)
+, _a_placeholderFg(animation(this, &InputArea::step_placeholderFg))
+, _a_placeholderShift(animation(this, &InputArea::step_placeholderShift))
-a_borderOpacityActive(0),
-a_borderFg(st.borderFg->c),
-_a_border(animFunc(this, &InputArea::animStep_border)),
+, a_borderOpacityActive(0)
+, a_borderFg(st.borderFg->c)
+, _a_border(animation(this, &InputArea::step_border))
-_focused(false), _error(false),
+, _focused(false)
+, _error(false)
-_st(st),
+, _st(st)
-_touchPress(false),
-_touchRightButton(false),
-_touchMove(false),
-_correcting(false) {
+, _touchPress(false)
+, _touchRightButton(false)
+, _touchMove(false)
+, _correcting(false) {
_inner.setAcceptRichText(false);
resize(_st.width, _st.heightMin);
@@ -950,7 +959,7 @@ void InputArea::processDocumentContentsChange(int position, int charsAdded) {
const QChar *ch = t.constData(), *e = ch + t.size();
for (; ch != e; ++ch, ++fp) {
int32 emojiLen = 0;
- emoji = emojiFromText(ch, e, emojiLen);
+ emoji = emojiFromText(ch, e, &emojiLen);
if (emoji) {
if (replacePosition >= 0) {
emoji = 0; // replace tilde char format first
@@ -1106,47 +1115,42 @@ void InputArea::onRedoAvailable(bool avail) {
if (App::wnd()) App::wnd()->updateGlobalMenu();
}
-bool InputArea::animStep_placeholderFg(float64 ms) {
- float dt = ms / _st.duration;
- bool res = true;
+void InputArea::step_placeholderFg(float64 ms, bool timer) {
+ float64 dt = ms / _st.duration;
if (dt >= 1) {
- res = false;
+ _a_placeholderFg.stop();
a_placeholderFg.finish();
} else {
a_placeholderFg.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
-bool InputArea::animStep_placeholderShift(float64 ms) {
- float dt = ms / _st.duration;
- bool res = true;
+void InputArea::step_placeholderShift(float64 ms, bool timer) {
+ float64 dt = ms / _st.duration;
if (dt >= 1) {
- res = false;
+ _a_placeholderShift.stop();
a_placeholderLeft.finish();
a_placeholderOpacity.finish();
} else {
a_placeholderLeft.update(dt, anim::linear);
a_placeholderOpacity.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
-bool InputArea::animStep_border(float64 ms) {
- float dt = ms / _st.duration;
+void InputArea::step_border(float64 ms, bool timer) {
+ float64 dt = ms / _st.duration;
bool res = true;
if (dt >= 1) {
- res = false;
+ _a_border.stop();
a_borderFg.finish();
a_borderOpacityActive.finish();
} else {
a_borderFg.update(dt, anim::linear);
a_borderOpacityActive.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
void InputArea::updatePlaceholder() {
@@ -1261,41 +1265,41 @@ void InputArea::showError() {
}
}
-InputField::InputField(QWidget *parent, const style::InputField &st, const QString &ph, const QString &val) : TWidget(parent),
-_maxLength(-1),
-_inner(this),
-_oldtext(val),
+InputField::InputField(QWidget *parent, const style::InputField &st, const QString &ph, const QString &val) : TWidget(parent)
+, _maxLength(-1)
+, _inner(this)
+, _oldtext(val)
-_undoAvailable(false),
-_redoAvailable(false),
+, _undoAvailable(false)
+, _redoAvailable(false)
-_customUpDown(true),
+, _customUpDown(true)
-_placeholderFull(ph),
-_placeholderVisible(val.isEmpty()),
-a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift),
-a_placeholderOpacity(_placeholderVisible ? 1 : 0),
-a_placeholderFg(st.placeholderFg->c),
-_a_placeholderFg(animFunc(this, &InputField::animStep_placeholderFg)),
-_a_placeholderShift(animFunc(this, &InputField::animStep_placeholderShift)),
+, _placeholderFull(ph)
+, _placeholderVisible(val.isEmpty())
+, a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift)
+, a_placeholderOpacity(_placeholderVisible ? 1 : 0)
+, a_placeholderFg(st.placeholderFg->c)
+, _a_placeholderFg(animation(this, &InputField::step_placeholderFg))
+, _a_placeholderShift(animation(this, &InputField::step_placeholderShift))
-a_borderOpacityActive(0),
-a_borderFg(st.borderFg->c),
-_a_border(animFunc(this, &InputField::animStep_border)),
+, a_borderOpacityActive(0)
+, a_borderFg(st.borderFg->c)
+, _a_border(animation(this, &InputField::step_border))
-_focused(false), _error(false),
+, _focused(false)
+, _error(false)
-_st(st),
+, _st(st)
-_touchPress(false),
-_touchRightButton(false),
-_touchMove(false),
-_correcting(false) {
+, _touchPress(false)
+, _touchRightButton(false)
+, _touchMove(false)
+, _correcting(false) {
_inner.setAcceptRichText(false);
resize(_st.width, _st.height);
_inner.setWordWrapMode(QTextOption::NoWrap);
- _inner.setLineWrapMode(QTextEdit::NoWrap);
setAttribute(Qt::WA_OpaquePaintEvent);
@@ -1327,7 +1331,7 @@ _correcting(false) {
connect(&_inner, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool)));
connect(&_inner, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool)));
if (App::wnd()) connect(&_inner, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu()));
-
+
setCursor(style::cur_text);
if (!val.isEmpty()) {
_inner.setPlainText(val);
@@ -1408,7 +1412,7 @@ void InputField::paintEvent(QPaintEvent *e) {
if (_st.iconSprite.pxWidth()) {
p.drawSpriteLeft(_st.iconPosition, width(), _st.iconSprite);
}
-
+
bool drawPlaceholder = _placeholderVisible;
if (_a_placeholderShift.animating()) {
p.setOpacity(a_placeholderOpacity.current());
@@ -1421,11 +1425,11 @@ void InputField::paintEvent(QPaintEvent *e) {
QRect r(rect().marginsRemoved(_st.textMargins + _st.placeholderMargins));
r.moveLeft(r.left() + a_placeholderLeft.current());
if (rtl()) r.moveLeft(width() - r.left() - r.width());
-
+
p.setFont(_st.font);
p.setPen(a_placeholderFg.current());
p.drawText(r, _placeholder, _st.placeholderAlign);
-
+
p.restore();
}
TWidget::paintEvent(e);
@@ -1659,7 +1663,7 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) {
}
int32 emojiLen = 0;
- emoji = emojiFromText(ch, e, emojiLen);
+ emoji = emojiFromText(ch, e, &emojiLen);
if (emoji) {
if (replacePosition >= 0) {
emoji = 0; // replace tilde char format first
@@ -1834,47 +1838,41 @@ void InputField::selectAll() {
_inner.setTextCursor(c);
}
-bool InputField::animStep_placeholderFg(float64 ms) {
- float dt = ms / _st.duration;
- bool res = true;
+void InputField::step_placeholderFg(float64 ms, bool timer) {
+ float64 dt = ms / _st.duration;
if (dt >= 1) {
- res = false;
+ _a_placeholderFg.stop();
a_placeholderFg.finish();
} else {
a_placeholderFg.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
-bool InputField::animStep_placeholderShift(float64 ms) {
- float dt = ms / _st.duration;
- bool res = true;
+void InputField::step_placeholderShift(float64 ms, bool timer) {
+ float64 dt = ms / _st.duration;
if (dt >= 1) {
- res = false;
+ _a_placeholderShift.stop();
a_placeholderLeft.finish();
a_placeholderOpacity.finish();
} else {
a_placeholderLeft.update(dt, anim::linear);
a_placeholderOpacity.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
-bool InputField::animStep_border(float64 ms) {
- float dt = ms / _st.duration;
- bool res = true;
+void InputField::step_border(float64 ms, bool timer) {
+ float64 dt = ms / _st.duration;
if (dt >= 1) {
- res = false;
+ _a_border.stop();
a_borderFg.finish();
a_borderOpacityActive.finish();
} else {
a_borderFg.update(dt, anim::linear);
a_borderOpacityActive.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
void InputField::updatePlaceholder() {
@@ -1981,34 +1979,35 @@ void InputField::showError() {
}
}
-MaskedInputField::MaskedInputField(QWidget *parent, const style::InputField &st, const QString &placeholder, const QString &val) : QLineEdit(val, parent),
-_st(st),
-_maxLength(-1),
-_oldtext(val),
+MaskedInputField::MaskedInputField(QWidget *parent, const style::InputField &st, const QString &placeholder, const QString &val) : QLineEdit(val, parent)
+, _st(st)
+, _maxLength(-1)
+, _oldtext(val)
-_undoAvailable(false),
-_redoAvailable(false),
+, _undoAvailable(false)
+, _redoAvailable(false)
-_customUpDown(false),
+, _customUpDown(false)
-_placeholderFull(placeholder),
-_placeholderVisible(val.isEmpty()),
-_placeholderFast(false),
-a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift),
-a_placeholderOpacity(_placeholderVisible ? 1 : 0),
-a_placeholderFg(st.placeholderFg->c),
-_a_placeholderFg(animFunc(this, &MaskedInputField::animStep_placeholderFg)),
-_a_placeholderShift(animFunc(this, &MaskedInputField::animStep_placeholderShift)),
+, _placeholderFull(placeholder)
+, _placeholderVisible(val.isEmpty())
+, _placeholderFast(false)
+, a_placeholderLeft(_placeholderVisible ? 0 : st.placeholderShift)
+, a_placeholderOpacity(_placeholderVisible ? 1 : 0)
+, a_placeholderFg(st.placeholderFg->c)
+, _a_placeholderFg(animation(this, &MaskedInputField::step_placeholderFg))
+, _a_placeholderShift(animation(this, &MaskedInputField::step_placeholderShift))
-a_borderOpacityActive(0),
-a_borderFg(st.borderFg->c),
-_a_border(animFunc(this, &MaskedInputField::animStep_border)),
+, a_borderOpacityActive(0)
+, a_borderFg(st.borderFg->c)
+, _a_border(animation(this, &MaskedInputField::step_border))
-_focused(false), _error(false),
+, _focused(false)
+, _error(false)
-_touchPress(false),
-_touchRightButton(false),
-_touchMove(false) {
+, _touchPress(false)
+, _touchRightButton(false)
+, _touchMove(false) {
resize(_st.width, _st.height);
setFont(_st.font->f);
@@ -2029,7 +2028,7 @@ _touchMove(false) {
setStyle(&_inputFieldStyle);
QLineEdit::setTextMargins(0, 0, 0, 0);
setContentsMargins(0, 0, 0, 0);
-
+
setAttribute(Qt::WA_AcceptTouchEvents);
_touchTimer.setSingleShot(true);
connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer()));
@@ -2188,47 +2187,41 @@ QSize MaskedInputField::minimumSizeHint() const {
return geometry().size();
}
-bool MaskedInputField::animStep_placeholderFg(float64 ms) {
- float dt = ms / _st.duration;
- bool res = true;
+void MaskedInputField::step_placeholderFg(float64 ms, bool timer) {
+ float64 dt = ms / _st.duration;
if (dt >= 1) {
- res = false;
+ _a_placeholderFg.stop();
a_placeholderFg.finish();
} else {
a_placeholderFg.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
-bool MaskedInputField::animStep_placeholderShift(float64 ms) {
- float dt = ms / _st.duration;
- bool res = true;
+void MaskedInputField::step_placeholderShift(float64 ms, bool timer) {
+ float64 dt = ms / _st.duration;
if (dt >= 1) {
- res = false;
+ _a_placeholderShift.stop();
a_placeholderLeft.finish();
a_placeholderOpacity.finish();
} else {
a_placeholderLeft.update(dt, anim::linear);
a_placeholderOpacity.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
-bool MaskedInputField::animStep_border(float64 ms) {
- float dt = ms / _st.duration;
- bool res = true;
+void MaskedInputField::step_border(float64 ms, bool timer) {
+ float64 dt = ms / _st.duration;
if (dt >= 1) {
- res = false;
+ _a_border.stop();
a_borderFg.finish();
a_borderOpacityActive.finish();
} else {
a_borderFg.update(dt, anim::linear);
a_borderOpacityActive.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
bool MaskedInputField::setPlaceholder(const QString &placeholder) {
diff --git a/Telegram/SourceFiles/gui/flatinput.h b/Telegram/SourceFiles/gui/flatinput.h
index 95aa9b8cec..98b8d3be19 100644
--- a/Telegram/SourceFiles/gui/flatinput.h
+++ b/Telegram/SourceFiles/gui/flatinput.h
@@ -23,7 +23,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "style.h"
#include "animation.h"
-class FlatInput : public QLineEdit, public Animated {
+class FlatInput : public QLineEdit {
Q_OBJECT
T_WIDGET
@@ -50,7 +50,7 @@ public:
QRect getTextRect() const;
- bool animStep(float64 ms);
+ void step_appearance(float64 ms, bool timer);
QSize sizeHint() const;
QSize minimumSizeHint() const;
@@ -98,6 +98,7 @@ private:
anim::ivalue a_phLeft;
anim::fvalue a_phAlpha;
anim::cvalue a_phColor, a_borderColor, a_bgColor;
+ Animation _a_appearance;
int _notingBene;
style::flatInput _st;
@@ -196,9 +197,9 @@ public:
}
void updatePlaceholder();
- bool animStep_placeholderFg(float64 ms);
- bool animStep_placeholderShift(float64 ms);
- bool animStep_border(float64 ms);
+ void step_placeholderFg(float64 ms, bool timer);
+ void step_placeholderShift(float64 ms, bool timer);
+ void step_border(float64 ms, bool timer);
QSize sizeHint() const;
QSize minimumSizeHint() const;
@@ -354,9 +355,9 @@ public:
}
void updatePlaceholder();
- bool animStep_placeholderFg(float64 ms);
- bool animStep_placeholderShift(float64 ms);
- bool animStep_border(float64 ms);
+ void step_placeholderFg(float64 ms, bool timer);
+ void step_placeholderShift(float64 ms, bool timer);
+ void step_border(float64 ms, bool timer);
QSize sizeHint() const;
QSize minimumSizeHint() const;
@@ -523,9 +524,9 @@ public:
QRect getTextRect() const;
- bool animStep_placeholderFg(float64 ms);
- bool animStep_placeholderShift(float64 ms);
- bool animStep_border(float64 ms);
+ void step_placeholderFg(float64 ms, bool timer);
+ void step_placeholderShift(float64 ms, bool timer);
+ void step_border(float64 ms, bool timer);
QSize sizeHint() const;
QSize minimumSizeHint() const;
diff --git a/Telegram/SourceFiles/gui/flattextarea.cpp b/Telegram/SourceFiles/gui/flattextarea.cpp
index 7dccc46b13..3340bb1ab5 100644
--- a/Telegram/SourceFiles/gui/flattextarea.cpp
+++ b/Telegram/SourceFiles/gui/flattextarea.cpp
@@ -24,18 +24,34 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "flattextarea.h"
#include "window.h"
-FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &pholder, const QString &v) : QTextEdit(parent),
-_minHeight(-1), _maxHeight(-1), _maxLength(-1), _ctrlEnterSubmit(true),
-_oldtext(v), _phVisible(!v.length()),
-a_phLeft(_phVisible ? 0 : st.phShift), a_phAlpha(_phVisible ? 1 : 0), a_phColor(st.phColor->c),
-_st(st), _undoAvailable(false), _redoAvailable(false), _inDrop(false), _inHeightCheck(false), _fakeMargin(0),
-_touchPress(false), _touchRightButton(false), _touchMove(false), _correcting(false) {
+FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &pholder, const QString &v) : QTextEdit(parent)
+, _minHeight(-1)
+, _maxHeight(-1)
+, _maxLength(-1)
+, _ctrlEnterSubmit(true)
+, _oldtext(v)
+, _phAfter(0)
+, _phVisible(!v.length())
+, a_phLeft(_phVisible ? 0 : st.phShift)
+, a_phAlpha(_phVisible ? 1 : 0)
+, a_phColor(st.phColor->c)
+, _a_appearance(animation(this, &FlatTextarea::step_appearance))
+, _st(st)
+, _undoAvailable(false)
+, _redoAvailable(false)
+, _inDrop(false)
+, _inHeightCheck(false)
+, _fakeMargin(0)
+, _touchPress(false)
+, _touchRightButton(false)
+, _touchMove(false)
+, _correcting(false) {
setAcceptRichText(false);
resize(_st.width, _st.font->height);
-
+
setFont(_st.font->f);
setAlignment(_st.align);
-
+
setPlaceholder(pholder);
QPalette p(palette());
@@ -72,12 +88,25 @@ _touchPress(false), _touchRightButton(false), _touchMove(false), _correcting(fal
}
}
-void FlatTextarea::setTextFast(const QString &text) {
- setPlainText(text);
- if (animating()) {
+void FlatTextarea::setTextFast(const QString &text, bool clearUndoHistory) {
+ if (clearUndoHistory) {
+ setPlainText(text);
+ } else {
+ QTextCursor c(document()->docHandle(), 0);
+ c.joinPreviousEditBlock();
+ c.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
+ c.insertText(text);
+ c.movePosition(QTextCursor::End);
+ c.endEditBlock();
+ }
+ finishPlaceholder();
+}
+
+void FlatTextarea::finishPlaceholder() {
+ if (_a_appearance.animating()) {
a_phLeft.finish();
a_phAlpha.finish();
- anim::stop(this);
+ _a_appearance.stop();
update();
}
}
@@ -184,17 +213,21 @@ void FlatTextarea::paintEvent(QPaintEvent *e) {
QRect r(rect().intersected(e->rect()));
p.fillRect(r, _st.bgColor->b);
bool phDraw = _phVisible;
- if (animating()) {
+ if (_a_appearance.animating()) {
p.setOpacity(a_phAlpha.current());
phDraw = true;
}
if (phDraw) {
p.save();
p.setClipRect(r);
- QRect phRect(_st.textMrg.left() - _fakeMargin + _st.phPos.x() + a_phLeft.current(), _st.textMrg.top() - _fakeMargin + _st.phPos.y(), width() - _st.textMrg.left() - _st.textMrg.right(), height() - _st.textMrg.top() - _st.textMrg.bottom());
- p.setFont(_st.font->f);
+ p.setFont(_st.font);
p.setPen(a_phColor.current());
- p.drawText(phRect, _ph, QTextOption(_st.phAlign));
+ if (_st.phAlign == style::al_topleft && _phAfter > 0) {
+ p.drawText(_st.textMrg.left() - _fakeMargin + a_phLeft.current() + _st.font->width(getLastText().mid(0, _phAfter)), _st.textMrg.top() - _fakeMargin - st::lineWidth + _st.font->ascent, _ph);
+ } else {
+ QRect phRect(_st.textMrg.left() - _fakeMargin + _st.phPos.x() + a_phLeft.current(), _st.textMrg.top() - _fakeMargin + _st.phPos.y(), width() - _st.textMrg.left() - _st.textMrg.right(), height() - _st.textMrg.top() - _st.textMrg.bottom());
+ p.drawText(phRect, _ph, QTextOption(_st.phAlign));
+ }
p.restore();
p.setOpacity(1);
}
@@ -203,13 +236,13 @@ void FlatTextarea::paintEvent(QPaintEvent *e) {
void FlatTextarea::focusInEvent(QFocusEvent *e) {
a_phColor.start(_st.phFocusColor->c);
- anim::start(this);
+ _a_appearance.start();
QTextEdit::focusInEvent(e);
}
void FlatTextarea::focusOutEvent(QFocusEvent *e) {
a_phColor.start(_st.phColor->c);
- anim::start(this);
+ _a_appearance.start();
QTextEdit::focusOutEvent(e);
}
@@ -226,7 +259,7 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
QTextFragment fragment;
getSingleEmojiFragment(text, fragment);
-
+
if (!text.isEmpty()) {
QTextCharFormat format = fragment.charFormat();
QString imageName = static_cast(&format)->name();
@@ -237,10 +270,62 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
return 0;
}
-void FlatTextarea::getMentionHashtagBotCommandStart(QString &start) const {
- int32 pos = textCursor().position();
- if (textCursor().anchor() != pos) return;
+QString FlatTextarea::getInlineBotQuery(UserData *&inlineBot, QString &inlineBotUsername) const {
+ const QString &text(getLastText());
+ int32 inlineUsernameStart = 1, inlineUsernameLength = 0, size = text.size();
+ if (size > 2 && text.at(0) == '@' && text.at(1).isLetter()) {
+ inlineUsernameLength = 1;
+ for (int32 i = inlineUsernameStart + 1, l = text.size(); i < l; ++i) {
+ if (text.at(i).isLetterOrNumber() || text.at(i).unicode() == '_') {
+ ++inlineUsernameLength;
+ continue;
+ }
+ if (!text.at(i).isSpace()) {
+ inlineUsernameLength = 0;
+ }
+ break;
+ }
+ if (inlineUsernameLength && inlineUsernameStart + inlineUsernameLength < text.size() && text.at(inlineUsernameStart + inlineUsernameLength).isSpace()) {
+ QStringRef username = text.midRef(inlineUsernameStart, inlineUsernameLength);
+ if (username != inlineBotUsername) {
+ inlineBotUsername = username.toString();
+ PeerData *peer = App::peerByName(inlineBotUsername);
+ if (peer) {
+ if (peer->isUser()) {
+ inlineBot = peer->asUser();
+ } else {
+ inlineBot = 0;
+ }
+ } else {
+ inlineBot = InlineBotLookingUpData;
+ }
+ }
+ if (inlineBot == InlineBotLookingUpData) return QString();
+
+ if (inlineBot && (!inlineBot->botInfo || inlineBot->botInfo->inlinePlaceholder.isEmpty())) {
+ inlineBot = 0;
+ } else {
+ return text.mid(inlineUsernameStart + inlineUsernameLength + 1);
+ }
+ } else {
+ inlineUsernameLength = 0;
+ }
+ }
+ if (inlineUsernameLength < 3) {
+ inlineBot = 0;
+ inlineBotUsername = QString();
+ }
+ return QString();
+}
+
+QString FlatTextarea::getMentionHashtagBotCommandPart(bool &start) const {
+ start = false;
+
+ int32 pos = textCursor().position();
+ if (textCursor().anchor() != pos) return QString();
+
+ // check mention / hashtag / bot command
QTextDocument *doc(document());
QTextBlock block = doc->findBlock(pos);
for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) {
@@ -258,29 +343,33 @@ void FlatTextarea::getMentionHashtagBotCommandStart(QString &start) const {
for (int i = pos - p; i > 0; --i) {
if (t.at(i - 1) == '@') {
if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
- start = t.mid(i - 1, pos - p - i + 1);
+ start = (i == 1) && (p == 0);
+ return t.mid(i - 1, pos - p - i + 1);
} else if ((pos - p - i < 1 || t.at(i).isLetter()) && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) {
mentionInCommand = true;
--i;
continue;
}
- return;
+ return QString();
} else if (t.at(i - 1) == '#') {
if (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_')) {
- start = t.mid(i - 1, pos - p - i + 1);
+ start = (i == 1) && (p == 0);
+ return t.mid(i - 1, pos - p - i + 1);
}
- return;
+ return QString();
} else if (t.at(i - 1) == '/') {
if (i < 2) {
- start = t.mid(i - 1, pos - p - i + 1);
+ start = (i == 1) && (p == 0);
+ return t.mid(i - 1, pos - p - i + 1);
}
- return;
+ return QString();
}
if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break;
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
}
- return;
+ break;
}
+ return QString();
}
void FlatTextarea::onMentionHashtagOrBotCommandInsert(QString str) {
@@ -649,7 +738,7 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
const QChar *ch = t.constData(), *e = ch + t.size();
for (; ch != e; ++ch, ++fp) {
int32 emojiLen = 0;
- emoji = emojiFromText(ch, e, emojiLen);
+ emoji = emojiFromText(ch, e, &emojiLen);
if (emoji) {
if (replacePosition >= 0) {
emoji = 0; // replace tilde char format first
@@ -701,7 +790,7 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
emoji = 0;
replacePosition = -1;
- } else {
+ } else {
break;
}
}
@@ -807,11 +896,10 @@ void FlatTextarea::onRedoAvailable(bool avail) {
if (App::wnd()) App::wnd()->updateGlobalMenu();
}
-bool FlatTextarea::animStep(float64 ms) {
+void FlatTextarea::step_appearance(float64 ms, bool timer) {
float dt = ms / _st.phDuration;
- bool res = true;
if (dt >= 1) {
- res = false;
+ _a_appearance.stop();
a_phLeft.finish();
a_phAlpha.finish();
a_phColor.finish();
@@ -823,23 +911,26 @@ bool FlatTextarea::animStep(float64 ms) {
a_phAlpha.update(dt, _st.phAlphaFunc);
a_phColor.update(dt, _st.phColorFunc);
}
- update();
- return res;
+ if (timer) update();
}
-void FlatTextarea::setPlaceholder(const QString &ph) {
+void FlatTextarea::setPlaceholder(const QString &ph, int32 afterSymbols) {
_ph = ph;
- _phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1);
+ if (_phAfter != afterSymbols) {
+ _phAfter = afterSymbols;
+ updatePlaceholder();
+ }
+ _phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1 - (_phAfter ? _st.font->width(getLastText().mid(0, _phAfter)) : 0));
if (_phVisible) update();
}
void FlatTextarea::updatePlaceholder() {
- bool vis = getLastText().isEmpty();
+ bool vis = (getLastText().size() <= _phAfter);
if (vis == _phVisible) return;
a_phLeft.start(vis ? 0 : _st.phShift);
a_phAlpha.start(vis ? 1 : 0);
- anim::start(this);
+ _a_appearance.start();
_phVisible = vis;
}
diff --git a/Telegram/SourceFiles/gui/flattextarea.h b/Telegram/SourceFiles/gui/flattextarea.h
index 524838216e..14b9fa10dc 100644
--- a/Telegram/SourceFiles/gui/flattextarea.h
+++ b/Telegram/SourceFiles/gui/flattextarea.h
@@ -24,7 +24,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "style.h"
#include "animation.h"
-class FlatTextarea : public QTextEdit, public Animated {
+class UserData;
+class FlatTextarea : public QTextEdit {
Q_OBJECT
T_WIDGET
@@ -50,19 +51,21 @@ public:
const QString &getLastText() const {
return _oldtext;
}
- void setPlaceholder(const QString &ph);
+ void setPlaceholder(const QString &ph, int32 afterSymbols = 0);
void updatePlaceholder();
+ void finishPlaceholder();
QRect getTextRect() const;
int32 fakeMargin() const;
- bool animStep(float64 ms);
+ void step_appearance(float64 ms, bool timer);
QSize sizeHint() const;
QSize minimumSizeHint() const;
EmojiPtr getSingleEmoji() const;
- void getMentionHashtagBotCommandStart(QString &start) const;
+ QString getMentionHashtagBotCommandPart(bool &start) const;
+ QString getInlineBotQuery(UserData *&contextBot, QString &contextBotUsername) const;
void removeSingleEmoji();
bool hasText() const;
@@ -77,7 +80,7 @@ public:
QMimeData *createMimeDataFromSelection() const;
void setCtrlEnterSubmit(bool ctrlEnterSubmit);
- void setTextFast(const QString &text);
+ void setTextFast(const QString &text, bool clearUndoHistory = true);
public slots:
@@ -123,10 +126,13 @@ private:
bool _ctrlEnterSubmit;
QString _ph, _phelided, _oldtext;
+ int32 _phAfter;
bool _phVisible;
anim::ivalue a_phLeft;
anim::fvalue a_phAlpha;
anim::cvalue a_phColor;
+ Animation _a_appearance;
+
style::flatTextarea _st;
bool _undoAvailable, _redoAvailable, _inDrop, _inHeightCheck;
diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp
index 6b9b74756e..6437abf3f2 100644
--- a/Telegram/SourceFiles/gui/images.cpp
+++ b/Telegram/SourceFiles/gui/images.cpp
@@ -27,9 +27,12 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "pspecific.h"
namespace {
- typedef QMap LocalImages;
+ typedef QMap LocalImages;
LocalImages localImages;
+ typedef QMap WebImages;
+ WebImages webImages;
+
Image *blank() {
static Image *img = getImage(qsl(":/gui/art/blank.gif"), "GIF");
return img;
@@ -38,7 +41,7 @@ namespace {
typedef QMap StorageImages;
StorageImages storageImages;
- int64 globalAquiredSize = 0;
+ int64 globalAcquiredSize = 0;
static const uint64 BlurredCacheSkip = 0x1000000000000000LLU;
static const uint64 ColoredCacheSkip = 0x2000000000000000LLU;
@@ -46,6 +49,8 @@ namespace {
static const uint64 RoundedCacheSkip = 0x4000000000000000LLU;
}
+StorageImageLocation StorageImageLocation::Null;
+
bool Image::isNull() const {
return (this == blank());
}
@@ -57,8 +62,39 @@ ImagePtr::ImagePtr(int32 width, int32 height, const MTPFileLocation &location, I
Parent((location.type() == mtpc_fileLocation) ? (Image*)(getImage(StorageImageLocation(width, height, location.c_fileLocation()))) : def.v()) {
}
+Image::Image(const QString &file, QByteArray fmt) : _forgot(false) {
+ _data = QPixmap::fromImage(App::readImage(file, &fmt, false, 0, &_saved), Qt::ColorOnly);
+ _format = fmt;
+ if (!_data.isNull()) {
+ globalAcquiredSize += int64(_data.width()) * _data.height() * 4;
+ }
+}
+
+Image::Image(const QByteArray &filecontent, QByteArray fmt) : _forgot(false) {
+ _data = QPixmap::fromImage(App::readImage(filecontent, &fmt, false), Qt::ColorOnly);
+ _format = fmt;
+ _saved = filecontent;
+ if (!_data.isNull()) {
+ globalAcquiredSize += int64(_data.width()) * _data.height() * 4;
+ }
+}
+
+Image::Image(const QPixmap &pixmap, QByteArray format) : _format(format), _forgot(false), _data(pixmap) {
+ if (!_data.isNull()) {
+ globalAcquiredSize += int64(_data.width()) * _data.height() * 4;
+ }
+}
+
+Image::Image(const QByteArray &filecontent, QByteArray fmt, const QPixmap &pixmap) : _saved(filecontent), _format(fmt), _forgot(false), _data(pixmap) {
+ _data = pixmap;
+ _format = fmt;
+ _saved = filecontent;
+ if (!_data.isNull()) {
+ globalAcquiredSize += int64(_data.width()) * _data.height() * 4;
+ }
+}
+
const QPixmap &Image::pix(int32 w, int32 h) const {
- restore();
checkload();
if (w <= 0 || !width() || !height()) {
@@ -74,14 +110,13 @@ const QPixmap &Image::pix(int32 w, int32 h) const {
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
i = _sizesCache.insert(k, p);
if (!p.isNull()) {
- globalAquiredSize += int64(p.width()) * p.height() * 4;
+ globalAcquiredSize += int64(p.width()) * p.height() * 4;
}
}
return i.value();
}
const QPixmap &Image::pixRounded(int32 w, int32 h) const {
- restore();
checkload();
if (w <= 0 || !width() || !height()) {
@@ -97,14 +132,13 @@ const QPixmap &Image::pixRounded(int32 w, int32 h) const {
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
i = _sizesCache.insert(k, p);
if (!p.isNull()) {
- globalAquiredSize += int64(p.width()) * p.height() * 4;
+ globalAcquiredSize += int64(p.width()) * p.height() * 4;
}
}
return i.value();
}
const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
- restore();
checkload();
if (w <= 0 || !width() || !height()) {
@@ -120,14 +154,13 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
i = _sizesCache.insert(k, p);
if (!p.isNull()) {
- globalAquiredSize += int64(p.width()) * p.height() * 4;
+ globalAcquiredSize += int64(p.width()) * p.height() * 4;
}
}
return i.value();
}
const QPixmap &Image::pixColored(const style::color &add, int32 w, int32 h) const {
- restore();
checkload();
if (w <= 0 || !width() || !height()) {
@@ -143,14 +176,13 @@ const QPixmap &Image::pixColored(const style::color &add, int32 w, int32 h) cons
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
i = _sizesCache.insert(k, p);
if (!p.isNull()) {
- globalAquiredSize += int64(p.width()) * p.height() * 4;
+ globalAcquiredSize += int64(p.width()) * p.height() * 4;
}
}
return i.value();
}
const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32 h) const {
- restore();
checkload();
if (w <= 0 || !width() || !height()) {
@@ -166,14 +198,13 @@ const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
i = _sizesCache.insert(k, p);
if (!p.isNull()) {
- globalAquiredSize += int64(p.width()) * p.height() * 4;
+ globalAcquiredSize += int64(p.width()) * p.height() * 4;
}
}
return i.value();
}
const QPixmap &Image::pixSingle(int32 w, int32 h, int32 outerw, int32 outerh) const {
- restore();
checkload();
if (w <= 0 || !width() || !height()) {
@@ -184,22 +215,21 @@ const QPixmap &Image::pixSingle(int32 w, int32 h, int32 outerw, int32 outerh) co
}
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() || i->width() != (outerw * cIntRetinaFactor()) || i->height() != (outerh * cIntRetinaFactor())) {
if (i != _sizesCache.cend()) {
- globalAquiredSize -= int64(i->width()) * i->height() * 4;
+ globalAcquiredSize -= int64(i->width()) * i->height() * 4;
}
QPixmap p(pixNoCache(w, h, true, false, true, outerw, outerh));
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
i = _sizesCache.insert(k, p);
if (!p.isNull()) {
- globalAquiredSize += int64(p.width()) * p.height() * 4;
+ globalAcquiredSize += int64(p.width()) * p.height() * 4;
}
}
return i.value();
}
const QPixmap &Image::pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh) const {
- restore();
checkload();
if (w <= 0 || !width() || !height()) {
@@ -210,15 +240,15 @@ const QPixmap &Image::pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 out
}
uint64 k = BlurredCacheSkip | 0LL;
Sizes::const_iterator i = _sizesCache.constFind(k);
- if (i == _sizesCache.cend() || i->width() != w || (h && i->height() != h)) {
+ if (i == _sizesCache.cend() || i->width() != (outerw * cIntRetinaFactor()) || i->height() != (outerh * cIntRetinaFactor())) {
if (i != _sizesCache.cend()) {
- globalAquiredSize -= int64(i->width()) * i->height() * 4;
+ globalAcquiredSize -= int64(i->width()) * i->height() * 4;
}
QPixmap p(pixNoCache(w, h, true, true, true, outerw, outerh));
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
i = _sizesCache.insert(k, p);
if (!p.isNull()) {
- globalAquiredSize += int64(p.width()) * p.height() * 4;
+ globalAcquiredSize += int64(p.width()) * p.height() * 4;
}
}
return i.value();
@@ -335,7 +365,7 @@ yi += stride;
#undef update
}
-
+
delete[] rgb;
}
}
@@ -396,16 +426,9 @@ QImage imageColored(const style::color &add, QImage img) {
return img;
}
-QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh) const {
- restore();
- loaded();
-
- const QPixmap &p(pixData());
- if (p.isNull()) return blank()->pix();
-
- QImage img = p.toImage();
+QPixmap imagePix(QImage img, int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh) {
if (blurred) img = imageBlur(img);
- if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) {
+ if (w <= 0 || (w == img.width() && (h <= 0 || h == img.height()))) {
} else if (h <= 0) {
img = img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation);
} else {
@@ -421,7 +444,7 @@ QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool roun
{
QPainter p(&result);
if (w < outerw || h < outerh) {
- p.fillRect(0, 0, result.width(), result.height(), st::black->b);
+ p.fillRect(0, 0, result.width(), result.height(), st::black);
}
p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img);
}
@@ -429,32 +452,65 @@ QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool roun
}
}
if (rounded) imageRound(img);
+ img.setDevicePixelRatio(cRetinaFactor());
return QPixmap::fromImage(img, Qt::ColorOnly);
}
-QPixmap Image::pixColoredNoCache(const style::color &add, int32 w, int32 h, bool smooth) const {
+QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh) const {
+ if (!loading()) const_cast(this)->load();
restore();
- loaded();
+ if (_data.isNull()) {
+ if (h <= 0 && height() > 0) {
+ h = qRound(width() * w / float64(height()));
+ }
+ return blank()->pixNoCache(w, h, smooth, blurred, rounded, outerw, outerh);
+ }
- const QPixmap &p(pixData());
- if (p.isNull()) {
- return blank()->pix();
+ if (isNull() && outerw > 0 && outerh > 0) {
+ outerw *= cIntRetinaFactor();
+ outerh *= cIntRetinaFactor();
+
+ QImage result(outerw, outerh, QImage::Format_ARGB32_Premultiplied);
+ result.setDevicePixelRatio(cRetinaFactor());
+
+ {
+ QPainter p(&result);
+ if (w < outerw) {
+ p.fillRect(0, 0, (outerw - w) / 2, result.height(), st::black);
+ p.fillRect(((outerw - w) / 2) + w, 0, result.width() - (((outerw - w) / 2) + w), result.height(), st::black);
+ }
+ if (h < outerh) {
+ p.fillRect(qMax(0, (outerw - w) / 2), 0, qMin(result.width(), w), (outerh - h) / 2, st::black);
+ p.fillRect(qMax(0, (outerw - w) / 2), ((outerh - h) / 2) + h, qMin(result.width(), w), result.height() - (((outerh - h) / 2) + h), st::black);
+ }
+ p.fillRect(qMax(0, (outerw - w) / 2), qMax(0, (outerh - h) / 2), qMin(result.width(), w), qMin(result.height(), h), st::white);
+ }
+
+ if (rounded) imageRound(result);
+ return QPixmap::fromImage(result, Qt::ColorOnly);
}
- if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) return QPixmap::fromImage(imageColored(add, p.toImage()));
+ return imagePix(_data.toImage(), w, h, smooth, blurred, rounded, outerw, outerh);
+}
+
+QPixmap Image::pixColoredNoCache(const style::color &add, int32 w, int32 h, bool smooth) const {
+ const_cast(this)->load();
+ restore();
+ if (_data.isNull()) return blank()->pix();
+
+ QImage img = _data.toImage();
+ if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) return QPixmap::fromImage(imageColored(add, img));
if (h <= 0) {
- return QPixmap::fromImage(imageColored(add, p.toImage().scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation)), Qt::ColorOnly);
+ return QPixmap::fromImage(imageColored(add, img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation)), Qt::ColorOnly);
}
- return QPixmap::fromImage(imageColored(add, p.toImage().scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation)), Qt::ColorOnly);
+ return QPixmap::fromImage(imageColored(add, img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation)), Qt::ColorOnly);
}
QPixmap Image::pixBlurredColoredNoCache(const style::color &add, int32 w, int32 h) const {
+ const_cast(this)->load();
restore();
- loaded();
+ if (_data.isNull()) return blank()->pix();
- const QPixmap &p(pixData());
- if (p.isNull()) return blank()->pix();
-
- QImage img = imageBlur(p.toImage());
+ QImage img = imageBlur(_data.toImage());
if (h <= 0) {
img = img.scaledToWidth(w, Qt::SmoothTransformation);
} else {
@@ -465,121 +521,91 @@ QPixmap Image::pixBlurredColoredNoCache(const style::color &add, int32 w, int32
}
void Image::forget() const {
- if (forgot) return;
+ if (_forgot) return;
- const QPixmap &p(pixData());
- if (p.isNull()) return;
+ if (_data.isNull()) return;
invalidateSizeCache();
- if (saved.isEmpty()) {
- QBuffer buffer(&saved);
- if (format.toLower() == "webp") {
- int a = 0;
- }
- if (!p.save(&buffer, format)) {
- if (p.save(&buffer, "PNG")) {
- format = "PNG";
+ if (_saved.isEmpty()) {
+ QBuffer buffer(&_saved);
+ if (!_data.save(&buffer, _format)) {
+ if (_data.save(&buffer, "PNG")) {
+ _format = "PNG";
} else {
return;
}
}
}
- globalAquiredSize -= int64(p.width()) * p.height() * 4;
- doForget();
- forgot = true;
+ globalAcquiredSize -= int64(_data.width()) * _data.height() * 4;
+ _data = QPixmap();
+ _forgot = true;
}
void Image::restore() const {
- if (!forgot) return;
- doRestore();
- const QPixmap &p(pixData());
- if (!p.isNull()) {
- globalAquiredSize += int64(p.width()) * p.height() * 4;
+ if (!_forgot) return;
+
+ QBuffer buffer(&_saved);
+ QImageReader reader(&buffer, _format);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
+ reader.setAutoTransform(true);
+#endif
+ _data = QPixmap::fromImageReader(&reader, Qt::ColorOnly);
+
+ if (!_data.isNull()) {
+ globalAcquiredSize += int64(_data.width()) * _data.height() * 4;
}
- forgot = false;
+ _forgot = false;
}
void Image::invalidateSizeCache() const {
for (Sizes::const_iterator i = _sizesCache.cbegin(), e = _sizesCache.cend(); i != e; ++i) {
if (!i->isNull()) {
- globalAquiredSize -= int64(i->width()) * i->height() * 4;
+ globalAcquiredSize -= int64(i->width()) * i->height() * 4;
}
}
_sizesCache.clear();
}
-LocalImage::LocalImage(const QString &file, QByteArray fmt) {
- data = QPixmap::fromImage(App::readImage(file, &fmt, false, 0, &saved), Qt::ColorOnly);
- format = fmt;
- if (!data.isNull()) {
- globalAquiredSize += int64(data.width()) * data.height() * 4;
+Image::~Image() {
+ invalidateSizeCache();
+ if (!_data.isNull()) {
+ globalAcquiredSize -= int64(_data.width()) * _data.height() * 4;
}
}
-LocalImage::LocalImage(const QByteArray &filecontent, QByteArray fmt) {
- data = QPixmap::fromImage(App::readImage(filecontent, &fmt, false), Qt::ColorOnly);
- format = fmt;
- saved = filecontent;
- if (!data.isNull()) {
- globalAquiredSize += int64(data.width()) * data.height() * 4;
+Image *getImage(const QString &file, QByteArray format) {
+ if (file.startsWith(qstr("http://"), Qt::CaseInsensitive) || file.startsWith(qstr("https://"), Qt::CaseInsensitive)) {
+ QString key = file;
+ WebImages::const_iterator i = webImages.constFind(key);
+ if (i == webImages.cend()) {
+ i = webImages.insert(key, new WebImage(file));
+ }
+ return i.value();
+ } else {
+ QFileInfo f(file);
+ QString key = qsl("//:%1//:%2//:").arg(f.size()).arg(f.lastModified().toTime_t()) + file;
+ LocalImages::const_iterator i = localImages.constFind(key);
+ if (i == localImages.cend()) {
+ i = localImages.insert(key, new Image(file, format));
+ }
+ return i.value();
}
}
-LocalImage::LocalImage(const QPixmap &pixmap, QByteArray format) : Image(format), data(pixmap) {
- if (!data.isNull()) {
- globalAquiredSize += int64(data.width()) * data.height() * 4;
- }
+Image *getImage(const QByteArray &filecontent, QByteArray format) {
+ return new Image(filecontent, format);
}
-LocalImage::LocalImage(const QByteArray &filecontent, QByteArray fmt, const QPixmap &pixmap) {
- data = pixmap;
- format = fmt;
- saved = filecontent;
- if (!data.isNull()) {
- globalAquiredSize += int64(data.width()) * data.height() * 4;
- }
+Image *getImage(const QPixmap &pixmap, QByteArray format) {
+ return new Image(pixmap, format);
}
-const QPixmap &LocalImage::pixData() const {
- return data;
+Image *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap) {
+ return new Image(filecontent, format, pixmap);
}
-int32 LocalImage::width() const {
- restore();
- return data.width();
-}
-
-int32 LocalImage::height() const {
- restore();
- return data.height();
-}
-
-LocalImage::~LocalImage() {
- if (!data.isNull()) {
- globalAquiredSize -= int64(data.width()) * data.height() * 4;
- }
-}
-
-LocalImage *getImage(const QString &file, QByteArray format) {
- QFileInfo f(file);
- QString key = qsl("//:%1//:%2//:").arg(f.size()).arg(f.lastModified().toTime_t()) + file;
- LocalImages::const_iterator i = localImages.constFind(key);
- if (i == localImages.cend()) {
- i = localImages.insert(key, new LocalImage(file, format));
- }
- return i.value();
-}
-
-LocalImage *getImage(const QByteArray &filecontent, QByteArray format) {
- return new LocalImage(filecontent, format);
-}
-
-LocalImage *getImage(const QPixmap &pixmap, QByteArray format) {
- return new LocalImage(pixmap, format);
-}
-
-LocalImage *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap) {
- return new LocalImage(filecontent, format, pixmap);
+Image *getImage(int32 width, int32 height) {
+ return new DelayedStorageImage(width, height);
}
void clearStorageImages() {
@@ -587,6 +613,10 @@ void clearStorageImages() {
delete i.value();
}
storageImages.clear();
+ for (WebImages::const_iterator i = webImages.cbegin(), e = webImages.cend(); i != e; ++i) {
+ delete i.value();
+ }
+ webImages.clear();
}
void clearAllImages() {
@@ -598,100 +628,268 @@ void clearAllImages() {
}
int64 imageCacheSize() {
- return globalAquiredSize;
+ return globalAcquiredSize;
}
-StorageImage::StorageImage(const StorageImageLocation &location, int32 size) : w(location.width), h(location.height), loader(new mtpFileLoader(location.dc, location.volume, location.local, location.secret, size)) {
+void RemoteImage::doCheckload() const {
+ if (!amLoading() || !_loader->done()) return;
+
+ QPixmap data = _loader->imagePixmap();
+ if (data.isNull()) {
+ _loader->deleteLater();
+ _loader->stop();
+ _loader = CancelledFileLoader;
+ return;
+ }
+
+ if (!_data.isNull()) {
+ globalAcquiredSize -= int64(_data.width()) * _data.height() * 4;
+ }
+
+ _format = _loader->imageFormat();
+ _data = data;
+ _saved = _loader->bytes();
+ const_cast(this)->setInformation(_saved.size(), _data.width(), _data.height());
+ globalAcquiredSize += int64(_data.width()) * _data.height() * 4;
+
+ invalidateSizeCache();
+
+ _loader->deleteLater();
+ _loader->stop();
+ _loader = 0;
+
+ _forgot = false;
}
-StorageImage::StorageImage(const StorageImageLocation &location, QByteArray &bytes) : w(location.width), h(location.height), loader(0) {
- setData(bytes);
- if (location.dc) {
- Local::writeImage(storageKey(location.dc, location.volume, location.local), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes));
+void RemoteImage::loadLocal() {
+ if (loaded() || amLoading()) return;
+
+ _loader = createLoader(LoadFromLocalOnly, true);
+ if (_loader) _loader->start();
+}
+
+void RemoteImage::setData(QByteArray &bytes, const QByteArray &bytesFormat) {
+ QBuffer buffer(&bytes);
+
+ if (!_data.isNull()) {
+ globalAcquiredSize -= int64(_data.width()) * _data.height() * 4;
+ }
+ QByteArray fmt(bytesFormat);
+ _data = QPixmap::fromImage(App::readImage(bytes, &fmt, false), Qt::ColorOnly);
+ if (!_data.isNull()) {
+ globalAcquiredSize += int64(_data.width()) * _data.height() * 4;
+ setInformation(bytes.size(), _data.width(), _data.height());
+ }
+
+ invalidateSizeCache();
+ if (amLoading()) {
+ _loader->deleteLater();
+ _loader->stop();
+ _loader = 0;
+ }
+ _saved = bytes;
+ _format = fmt;
+ _forgot = false;
+}
+
+void RemoteImage::automaticLoad(const HistoryItem *item) {
+ if (loaded()) return;
+
+ if (_loader != CancelledFileLoader && item) {
+ bool loadFromCloud = false;
+ if (item->history()->peer->isUser()) {
+ loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate);
+ } else {
+ loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups);
+ }
+
+ if (_loader) {
+ if (loadFromCloud) _loader->permitLoadFromCloud();
+ } else {
+ _loader = createLoader(loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, true);
+ if (_loader) _loader->start();
+ }
}
}
-const QPixmap &StorageImage::pixData() const {
- return data;
+void RemoteImage::automaticLoadSettingsChanged() {
+ if (loaded() || _loader != CancelledFileLoader) return;
+ _loader = 0;
+}
+
+void RemoteImage::load(bool loadFirst, bool prior) {
+ if (loaded()) return;
+
+ if (!_loader) {
+ _loader = createLoader(LoadFromCloudOrLocal, false);
+ }
+ if (amLoading()) {
+ _loader->start(loadFirst, prior);
+ }
+}
+
+void RemoteImage::loadEvenCancelled(bool loadFirst, bool prior) {
+ if (_loader == CancelledFileLoader) _loader = 0;
+ return load(loadFirst, prior);
+}
+
+RemoteImage::~RemoteImage() {
+ if (!_data.isNull()) {
+ globalAcquiredSize -= int64(_data.width()) * _data.height() * 4;
+ }
+ if (amLoading()) {
+ _loader->deleteLater();
+ _loader->stop();
+ _loader = 0;
+ }
+}
+
+bool RemoteImage::loaded() const {
+ doCheckload();
+ return (!_data.isNull() || !_saved.isNull());
+}
+
+bool RemoteImage::displayLoading() const {
+ return amLoading() && (!_loader->loadingLocal() || !_loader->autoLoading());
+}
+
+void RemoteImage::cancel() {
+ if (!amLoading()) return;
+
+ FileLoader *l = _loader;
+ _loader = CancelledFileLoader;
+ if (l) {
+ l->cancel();
+ l->deleteLater();
+ l->stop();
+ }
+}
+
+float64 RemoteImage::progress() const {
+ return amLoading() ? _loader->currentProgress() : (loaded() ? 1 : 0);
+}
+
+int32 RemoteImage::loadOffset() const {
+ return amLoading() ? _loader->currentOffset() : 0;
+}
+
+StorageImage::StorageImage(const StorageImageLocation &location, int32 size)
+ : _location(location)
+ , _size(size) {
+}
+
+StorageImage::StorageImage(const StorageImageLocation &location, QByteArray &bytes)
+ : _location(location)
+ , _size(bytes.size()) {
+ setData(bytes);
+ if (!_location.isNull()) {
+ Local::writeImage(storageKey(_location), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes));
+ }
}
int32 StorageImage::width() const {
- return w;
+ return _location.width();
}
int32 StorageImage::height() const {
- return h;
+ return _location.height();
}
-bool StorageImage::check() const {
- if (loader->done()) {
- if (!data.isNull()) {
- globalAquiredSize -= int64(data.width()) * data.height() * 4;
+void StorageImage::setInformation(int32 size, int32 width, int32 height) {
+ _size = size;
+ _location.setSize(width, height);
+}
+
+FileLoader *StorageImage::createLoader(LoadFromCloudSetting fromCloud, bool autoLoading) {
+ if (_location.isNull()) return 0;
+ return new mtpFileLoader(&_location, _size, fromCloud, autoLoading);
+}
+
+DelayedStorageImage::DelayedStorageImage() : StorageImage(StorageImageLocation())
+, _loadRequested(false)
+, _loadCancelled(false)
+, _loadFromCloud(false) {
+}
+
+DelayedStorageImage::DelayedStorageImage(int32 w, int32 h) : StorageImage(StorageImageLocation(w, h, 0, 0, 0, 0))
+, _loadRequested(false)
+, _loadCancelled(false)
+, _loadFromCloud(false) {
+}
+
+DelayedStorageImage::DelayedStorageImage(QByteArray &bytes) : StorageImage(StorageImageLocation(), bytes)
+, _loadRequested(false)
+, _loadCancelled(false)
+, _loadFromCloud(false) {
+}
+
+void DelayedStorageImage::setStorageLocation(const StorageImageLocation location) {
+ _location = location;
+ if (_loadRequested) {
+ if (!_loadCancelled) {
+ if (_loadFromCloud) {
+ load();
+ } else {
+ loadLocal();
+ }
}
- format = loader->imageFormat();
- data = loader->imagePixmap();
- QByteArray bytes = loader->bytes();
- if (!data.isNull()) {
- globalAquiredSize += int64(data.width()) * data.height() * 4;
+ _loadRequested = false;
+ }
+}
+
+void DelayedStorageImage::automaticLoad(const HistoryItem *item) {
+ if (_location.isNull()) {
+ if (!_loadCancelled && item) {
+ bool loadFromCloud = false;
+ if (item->history()->peer->isUser()) {
+ loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate);
+ } else {
+ loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups);
+ }
+
+ if (_loadRequested) {
+ if (loadFromCloud) _loadFromCloud = loadFromCloud;
+ } else {
+ _loadFromCloud = loadFromCloud;
+ _loadRequested = true;
+ }
}
-
- w = data.width();
- h = data.height();
- invalidateSizeCache();
- loader->deleteLater();
- loader->rpcInvalidate();
- loader = 0;
-
- saved = bytes;
- forgot = false;
- return true;
- }
- return false;
-}
-
-void StorageImage::setData(QByteArray &bytes, const QByteArray &format) {
- QBuffer buffer(&bytes);
-
- if (!data.isNull()) {
- globalAquiredSize -= int64(data.width()) * data.height() * 4;
- }
- QByteArray fmt(format);
- data = QPixmap::fromImage(App::readImage(bytes, &fmt, false), Qt::ColorOnly);
- if (!data.isNull()) {
- globalAquiredSize += int64(data.width()) * data.height() * 4;
- }
-
- w = data.width();
- h = data.height();
- invalidateSizeCache();
- if (loader) {
- loader->deleteLater();
- loader->rpcInvalidate();
- loader = 0;
- }
- this->saved = bytes;
- this->format = fmt;
- forgot = false;
-}
-
-StorageImage::~StorageImage() {
- if (!data.isNull()) {
- globalAquiredSize -= int64(data.width()) * data.height() * 4;
- }
- if (loader) {
- loader->deleteLater();
- loader->rpcInvalidate();
- loader = 0;
+ } else {
+ StorageImage::automaticLoad(item);
}
}
-bool StorageImage::loaded() const {
- if (!loader) return true;
- return check();
+void DelayedStorageImage::automaticLoadSettingsChanged() {
+ if (_loadCancelled) _loadCancelled = false;
+ StorageImage::automaticLoadSettingsChanged();
+}
+
+void DelayedStorageImage::load(bool loadFirst, bool prior) {
+ if (_location.isNull()) {
+ _loadRequested = _loadFromCloud = true;
+ } else {
+ StorageImage::load(loadFirst, prior);
+ }
+}
+
+void DelayedStorageImage::loadEvenCancelled(bool loadFirst, bool prior) {
+ _loadCancelled = false;
+ StorageImage::loadEvenCancelled(loadFirst, prior);
+}
+
+bool DelayedStorageImage::displayLoading() const {
+ return _location.isNull() ? true : StorageImage::displayLoading();
+}
+
+void DelayedStorageImage::cancel() {
+ if (_loadRequested) {
+ _loadRequested = false;
+ }
+ StorageImage::cancel();
}
StorageImage *getImage(const StorageImageLocation &location, int32 size) {
- StorageKey key(storageKey(location.dc, location.volume, location.local));
+ StorageKey key(storageKey(location));
StorageImages::const_iterator i = storageImages.constFind(key);
if (i == storageImages.cend()) {
i = storageImages.insert(key, new StorageImage(location, size));
@@ -700,7 +898,7 @@ StorageImage *getImage(const StorageImageLocation &location, int32 size) {
}
StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes) {
- StorageKey key(storageKey(location.dc, location.volume, location.local));
+ StorageKey key(storageKey(location));
StorageImages::const_iterator i = storageImages.constFind(key);
if (i == storageImages.cend()) {
QByteArray bytesArr(bytes);
@@ -708,13 +906,35 @@ StorageImage *getImage(const StorageImageLocation &location, const QByteArray &b
} else if (!i.value()->loaded()) {
QByteArray bytesArr(bytes);
i.value()->setData(bytesArr);
- if (location.dc) {
- Local::writeImage(storageKey(location.dc, location.volume, location.local), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes));
+ if (!location.isNull()) {
+ Local::writeImage(key, StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes));
}
}
return i.value();
}
+
+WebImage::WebImage(const QString &url) : _url(url), _size(0), _width(0), _height(0) {
+}
+
+int32 WebImage::width() const {
+ return _width;
+}
+
+int32 WebImage::height() const {
+ return _height;
+}
+
+void WebImage::setInformation(int32 size, int32 width, int32 height) {
+ _size = size;
+ _width = width;
+ _height = height;
+}
+
+FileLoader *WebImage::createLoader(LoadFromCloudSetting fromCloud, bool autoLoading) {
+ return new webFileLoader(_url, QString(), fromCloud, autoLoading);
+}
+
ReadAccessEnabler::ReadAccessEnabler(const PsFileBookmark *bookmark) : _bookmark(bookmark), _failed(_bookmark ? !_bookmark->enable() : false) {
}
diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h
index 384da3da14..5ef9d93282 100644
--- a/Telegram/SourceFiles/gui/images.h
+++ b/Telegram/SourceFiles/gui/images.h
@@ -25,87 +25,187 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
QImage imageBlur(QImage img);
void imageRound(QImage &img);
-struct StorageImageLocation {
- StorageImageLocation() : width(0), height(0), dc(0), volume(0), local(0), secret(0) {
+inline uint32 packInt(int32 a) {
+ return (a < 0) ? uint32(int64(a) + 0x100000000LL) : uint32(a);
+}
+inline int32 unpackInt(uint32 a) {
+ return (a > 0x7FFFFFFFU) ? int32(int64(a) - 0x100000000LL) : int32(a);
+}
+inline uint64 packUIntUInt(uint32 a, uint32 b) {
+ return (uint64(a) << 32) | uint64(b);
+}
+inline uint64 packUIntInt(uint32 a, int32 b) {
+ return packUIntUInt(a, packInt(b));
+}
+inline uint64 packIntUInt(int32 a, uint32 b) {
+ return packUIntUInt(packInt(a), b);
+}
+inline uint64 packIntInt(int32 a, int32 b) {
+ return packUIntUInt(packInt(a), packInt(b));
+}
+inline uint32 unpackUIntFirst(uint64 v) {
+ return uint32(v >> 32);
+}
+inline int32 unpackIntFirst(uint64 v) {
+ return unpackInt(unpackUIntFirst(v));
+}
+inline uint32 unpackUIntSecond(uint64 v) {
+ return uint32(v & 0xFFFFFFFFULL);
+}
+inline int32 unpackIntSecond(uint64 v) {
+ return unpackInt(unpackUIntSecond(v));
+}
+
+class StorageImageLocation {
+public:
+ StorageImageLocation() : _widthheight(0), _dclocal(0), _volume(0), _secret(0) {
}
- StorageImageLocation(int32 width, int32 height, int32 dc, const uint64 &volume, int32 local, const uint64 &secret) : width(width), height(height), dc(dc), volume(volume), local(local), secret(secret) {
+ StorageImageLocation(int32 width, int32 height, int32 dc, const uint64 &volume, int32 local, const uint64 &secret) : _widthheight(packIntInt(width, height)), _dclocal(packIntInt(dc, local)), _volume(volume), _secret(secret) {
}
- StorageImageLocation(int32 width, int32 height, const MTPDfileLocation &location) : width(width), height(height), dc(location.vdc_id.v), volume(location.vvolume_id.v), local(location.vlocal_id.v), secret(location.vsecret.v) {
+ StorageImageLocation(int32 width, int32 height, const MTPDfileLocation &location) : _widthheight(packIntInt(width, height)), _dclocal(packIntInt(location.vdc_id.v, location.vlocal_id.v)), _volume(location.vvolume_id.v), _secret(location.vsecret.v) {
}
bool isNull() const {
- return !dc;
+ return !_dclocal;
}
- int32 width, height;
- int32 dc;
- uint64 volume;
- int32 local;
- uint64 secret;
+ int32 width() const {
+ return unpackIntFirst(_widthheight);
+ }
+ int32 height() const {
+ return unpackIntSecond(_widthheight);
+ }
+ void setSize(int32 width, int32 height) {
+ _widthheight = packIntInt(width, height);
+ }
+ int32 dc() const {
+ return unpackIntFirst(_dclocal);
+ }
+ uint64 volume() const {
+ return _volume;
+ }
+ int32 local() const {
+ return unpackIntSecond(_dclocal);
+ }
+ uint64 secret() const {
+ return _secret;
+ }
+
+ static StorageImageLocation Null;
+
+private:
+ uint64 _widthheight;
+ uint64 _dclocal;
+ uint64 _volume;
+ uint64 _secret;
+
+ friend inline bool operator==(const StorageImageLocation &a, const StorageImageLocation &b) {
+ return (a._dclocal == b._dclocal) && (a._volume == b._volume) && (a._secret == b._secret);
+ }
+
};
-inline bool operator==(const StorageImageLocation &a, const StorageImageLocation &b) {
- return (a.width == b.width) && (a.height == b.height) && (a.dc == b.dc) && (a.volume == b.volume) && (a.local == b.local) && (a.secret == b.secret);
-}
inline bool operator!=(const StorageImageLocation &a, const StorageImageLocation &b) {
return !(a == b);
}
+QPixmap imagePix(QImage img, int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh);
+
+class DelayedStorageImage;
+
+class HistoryItem;
class Image {
public:
- Image(QByteArray format = "PNG") : format(format), forgot(false) {
+ Image(const QString &file, QByteArray format = QByteArray());
+ Image(const QByteArray &filecontent, QByteArray format = QByteArray());
+ Image(const QPixmap &pixmap, QByteArray format = QByteArray());
+ Image(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap);
+
+ virtual void automaticLoad(const HistoryItem *item) { // auto load photo
}
+ virtual void automaticLoadSettingsChanged() {
+ }
+
virtual bool loaded() const {
return true;
}
virtual bool loading() const {
return false;
}
+ virtual bool displayLoading() const {
+ return false;
+ }
+ virtual void cancel() {
+ }
+ virtual float64 progress() const {
+ return 1;
+ }
+ virtual int32 loadOffset() const {
+ return 0;
+ }
+
const QPixmap &pix(int32 w = 0, int32 h = 0) const;
const QPixmap &pixRounded(int32 w = 0, int32 h = 0) const;
const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const;
const QPixmap &pixColored(const style::color &add, int32 w = 0, int32 h = 0) const;
const QPixmap &pixBlurredColored(const style::color &add, int32 w = 0, int32 h = 0) const;
- const QPixmap &pixSingle(int32 w, int32 y, int32 outerw, int32 outerh) const;
+ const QPixmap &pixSingle(int32 w, int32 h, int32 outerw, int32 outerh) const;
const QPixmap &pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh) const;
QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false, bool blurred = false, bool rounded = false, int32 outerw = -1, int32 outerh = -1) const;
QPixmap pixColoredNoCache(const style::color &add, int32 w = 0, int32 h = 0, bool smooth = false) const;
QPixmap pixBlurredColoredNoCache(const style::color &add, int32 w, int32 h = 0) const;
- virtual int32 width() const = 0;
- virtual int32 height() const = 0;
-
- virtual void load(bool /*loadFirst*/ = false, bool /*prior*/ = true) {
+ virtual int32 width() const {
+ restore();
+ return _data.width();
}
- virtual void checkload() const {
+ virtual int32 height() const {
+ restore();
+ return _data.height();
+ }
+
+ virtual void load(bool loadFirst = false, bool prior = true) {
+ }
+
+ virtual void loadEvenCancelled(bool loadFirst = false, bool prior = true) {
+ }
+
+ virtual const StorageImageLocation &location() const {
+ return StorageImageLocation::Null;
}
bool isNull() const;
-
+
void forget() const;
- void restore() const;
QByteArray savedFormat() const {
- return format;
+ return _format;
}
QByteArray savedData() const {
- return saved;
+ return _saved;
}
- virtual ~Image() {
- invalidateSizeCache();
+ virtual DelayedStorageImage *toDelayedStorageImage() {
+ return 0;
}
+ virtual const DelayedStorageImage *toDelayedStorageImage() const {
+ return 0;
+ }
+
+ virtual ~Image();
protected:
+ Image(QByteArray format = "PNG") : _format(format), _forgot(false) {
+ }
- virtual const QPixmap &pixData() const = 0;
- virtual void doForget() const = 0;
- virtual void doRestore() const = 0;
-
+ void restore() const;
+ virtual void checkload() const {
+ }
void invalidateSizeCache() const;
- mutable QByteArray saved, format;
- mutable bool forgot;
+ mutable QByteArray _saved, _format;
+ mutable bool _forgot;
+ mutable QPixmap _data;
private:
@@ -114,40 +214,11 @@ private:
};
-class LocalImage : public Image {
-public:
-
- LocalImage(const QString &file, QByteArray format = QByteArray());
- LocalImage(const QByteArray &filecontent, QByteArray format = QByteArray());
- LocalImage(const QPixmap &pixmap, QByteArray format = QByteArray());
- LocalImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap);
-
- int32 width() const;
- int32 height() const;
-
- ~LocalImage();
-
-protected:
-
- const QPixmap &pixData() const;
- void doForget() const {
- data = QPixmap();
- }
- void doRestore() const {
- QBuffer buffer(&saved);
- QImageReader reader(&buffer, format);
- data = QPixmap::fromImageReader(&reader, Qt::ColorOnly);
- }
-
-private:
-
- mutable QPixmap data;
-};
-
-LocalImage *getImage(const QString &file, QByteArray format);
-LocalImage *getImage(const QByteArray &filecontent, QByteArray format);
-LocalImage *getImage(const QPixmap &pixmap, QByteArray format);
-LocalImage *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap);
+Image *getImage(const QString &file, QByteArray format);
+Image *getImage(const QByteArray &filecontent, QByteArray format);
+Image *getImage(const QPixmap &pixmap, QByteArray format);
+Image *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap);
+Image *getImage(int32 width, int32 height);
typedef QPair StorageKey;
inline uint64 storageMix32To64(int32 a, int32 b) {
@@ -160,64 +231,130 @@ inline StorageKey storageKey(const MTPDfileLocation &location) {
return storageKey(location.vdc_id.v, location.vvolume_id.v, location.vlocal_id.v);
}
inline StorageKey storageKey(const StorageImageLocation &location) {
- return storageKey(location.dc, location.volume, location.local);
+ return storageKey(location.dc(), location.volume(), location.local());
}
-class StorageImage : public Image {
+class RemoteImage : public Image {
+public:
+
+ RemoteImage() : _loader(0) {
+ }
+
+ void automaticLoad(const HistoryItem *item); // auto load photo
+ void automaticLoadSettingsChanged();
+
+ bool loaded() const;
+ bool loading() const {
+ return amLoading();
+ }
+ bool displayLoading() const;
+ void cancel();
+ float64 progress() const;
+ int32 loadOffset() const;
+
+ void setData(QByteArray &bytes, const QByteArray &format = QByteArray());
+
+ void load(bool loadFirst = false, bool prior = true);
+ void loadEvenCancelled(bool loadFirst = false, bool prior = true);
+
+ virtual void setInformation(int32 size, int32 width, int32 height) = 0;
+ virtual FileLoader *createLoader(LoadFromCloudSetting fromCloud, bool autoLoading) = 0;
+
+ ~RemoteImage();
+
+protected:
+ void checkload() const {
+ doCheckload();
+ }
+ void loadLocal();
+
+private:
+ mutable FileLoader *_loader;
+ bool amLoading() const {
+ return _loader && _loader != CancelledFileLoader;
+ }
+ void doCheckload() const;
+
+};
+
+class StorageImage : public RemoteImage {
public:
StorageImage(const StorageImageLocation &location, int32 size = 0);
StorageImage(const StorageImageLocation &location, QByteArray &bytes);
-
+
int32 width() const;
int32 height() const;
- bool loaded() const;
- bool loading() const {
- return loader ? loader->loading() : false;
- }
- void setData(QByteArray &bytes, const QByteArray &format = QByteArray());
- void load(bool loadFirst = false, bool prior = true) {
- if (loader) {
- loader->start(loadFirst, prior);
- if (loader) check();
- }
- }
- void checkload() const {
- if (loader) {
- if (!loader->loading()) {
- loader->start(true);
- }
- if (loader) check();
- }
- }
+ virtual void setInformation(int32 size, int32 width, int32 height);
+ virtual FileLoader *createLoader(LoadFromCloudSetting fromCloud, bool autoLoading);
- ~StorageImage();
+ virtual const StorageImageLocation &location() const {
+ return _location;
+ }
protected:
+ StorageImageLocation _location;
+ int32 _size;
- const QPixmap &pixData() const;
- bool check() const;
- void doForget() const {
- data = QPixmap();
+};
+
+class DelayedStorageImage : public StorageImage {
+public:
+
+ DelayedStorageImage();
+ DelayedStorageImage(int32 w, int32 h);
+ DelayedStorageImage(QByteArray &bytes);
+
+ void setStorageLocation(const StorageImageLocation location);
+
+ virtual DelayedStorageImage *toDelayedStorageImage() {
+ return this;
}
- void doRestore() const {
- QBuffer buffer(&saved);
- QImageReader reader(&buffer, format);
- data = QPixmap::fromImageReader(&reader, Qt::ColorOnly);
+ virtual const DelayedStorageImage *toDelayedStorageImage() const {
+ return this;
}
+ void automaticLoad(const HistoryItem *item); // auto load photo
+ void automaticLoadSettingsChanged();
+
+ bool loading() const {
+ return _location.isNull() ? _loadRequested : StorageImage::loading();
+ }
+ bool displayLoading() const;
+ void cancel();
+
+ void load(bool loadFirst = false, bool prior = true);
+ void loadEvenCancelled(bool loadFirst = false, bool prior = true);
+
private:
+ bool _loadRequested, _loadCancelled, _loadFromCloud;
- mutable QPixmap data;
- mutable int32 w, h;
- mutable mtpFileLoader *loader;
};
StorageImage *getImage(const StorageImageLocation &location, int32 size = 0);
StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes);
Image *getImage(int32 width, int32 height, const MTPFileLocation &location);
+class WebImage : public RemoteImage {
+public:
+
+ WebImage(const QString &url);
+
+ int32 width() const;
+ int32 height() const;
+
+ virtual void setInformation(int32 size, int32 width, int32 height);
+ virtual FileLoader *createLoader(LoadFromCloudSetting fromCloud, bool autoLoading);
+
+private:
+ QString _url;
+ int32 _size, _width, _height;
+
+};
+
+WebImage *getImage(const QUrl &url);
+
class ImagePtr : public ManagedPtr {
public:
ImagePtr();
@@ -234,8 +371,22 @@ public:
ImagePtr(const StorageImageLocation &location, const QByteArray &bytes) : Parent(getImage(location, bytes)) {
}
ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr());
+ ImagePtr(int32 width, int32 height) : Parent(getImage(width, height)) {
+ }
};
+inline QSize resizeKeepAspect(int32 width, int32 height, int32 towidth, int32 toheight) {
+ int32 w = qMax(width, 1), h = qMax(height, 1);
+ if (w * toheight > h * towidth) {
+ h = qRound(h * towidth / float64(w));
+ w = towidth;
+ } else {
+ w = qRound(w * toheight / float64(h));
+ h = toheight;
+ }
+ return QSize(qMax(w, 1), qMax(h, 1));
+}
+
void clearStorageImages();
void clearAllImages();
int64 imageCacheSize();
diff --git a/Telegram/SourceFiles/gui/popupmenu.cpp b/Telegram/SourceFiles/gui/popupmenu.cpp
index 17f96535b2..21fefa3e1d 100644
--- a/Telegram/SourceFiles/gui/popupmenu.cpp
+++ b/Telegram/SourceFiles/gui/popupmenu.cpp
@@ -36,7 +36,7 @@ PopupMenu::PopupMenu(const style::PopupMenu &st) : TWidget(0)
, _selected(-1)
, _childMenuIndex(-1)
, a_opacity(1)
-, _a_hide(animFunc(this, &PopupMenu::animStep_hide))
+, _a_hide(animation(this, &PopupMenu::step_hide))
, _deleteOnHide(true)
, _triggering(false)
, _deleteLater(false) {
@@ -54,7 +54,7 @@ PopupMenu::PopupMenu(QMenu *menu, const style::PopupMenu &st) : TWidget(0)
, _selected(-1)
, _childMenuIndex(-1)
, a_opacity(1)
-, _a_hide(animFunc(this, &PopupMenu::animStep_hide))
+, _a_hide(animation(this, &PopupMenu::step_hide))
, _deleteOnHide(true)
, _triggering(false)
, _deleteLater(false) {
@@ -440,18 +440,16 @@ void PopupMenu::hideFinish() {
hide();
}
-bool PopupMenu::animStep_hide(float64 ms) {
+void PopupMenu::step_hide(float64 ms, bool timer) {
float64 dt = ms / _st.duration;
- bool res = true;
if (dt >= 1) {
+ _a_hide.stop();
a_opacity.finish();
hideFinish();
- res = false;
} else {
a_opacity.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
void PopupMenu::deleteOnHide(bool del) {
diff --git a/Telegram/SourceFiles/gui/popupmenu.h b/Telegram/SourceFiles/gui/popupmenu.h
index 887f15bd6c..121984e830 100644
--- a/Telegram/SourceFiles/gui/popupmenu.h
+++ b/Telegram/SourceFiles/gui/popupmenu.h
@@ -59,7 +59,7 @@ private:
void childHiding(PopupMenu *child);
- bool animStep_hide(float64 ms);
+ void step_hide(float64 ms, bool timer);
void init();
void hideFinish();
diff --git a/Telegram/SourceFiles/gui/scrollarea.cpp b/Telegram/SourceFiles/gui/scrollarea.cpp
index 0f76852b68..9fdc86a76f 100644
--- a/Telegram/SourceFiles/gui/scrollarea.cpp
+++ b/Telegram/SourceFiles/gui/scrollarea.cpp
@@ -38,12 +38,20 @@ void ScrollShadow::changeVisibility(bool shown) {
setVisible(shown);
}
-ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::flatScroll *st) : QWidget(parent), _st(st), _vertical(vert),
- _over(false), _overbar(false), _moving(false), _topSh(false), _bottomSh(false),
- _connected(vert ? parent->verticalScrollBar() : parent->horizontalScrollBar()),
- _scrollMax(_connected->maximum()), _hideIn(-1),
- a_bg((_st->hiding ? st::transparent : _st->bgColor)->c),
- a_bar((_st->hiding ? st::transparent : _st->barColor)->c) {
+ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::flatScroll *st) : QWidget(parent)
+, _st(st)
+, _vertical(vert)
+, _over(false)
+, _overbar(false)
+, _moving(false)
+, _topSh(false)
+, _bottomSh(false)
+, _connected(vert ? parent->verticalScrollBar() : parent->horizontalScrollBar())
+, _scrollMax(_connected->maximum())
+, _hideIn(-1)
+, a_bg((_st->hiding ? st::transparent : _st->bgColor)->c)
+, a_bar((_st->hiding ? st::transparent : _st->barColor)->c)
+, _a_appearance(animation(this, &ScrollBar::step_appearance)) {
recountSize();
_hideTimer.setSingleShot(true);
@@ -115,7 +123,7 @@ void ScrollBar::onHideTimer() {
_hideIn = -1;
a_bg.start(QColor(a_bg.current().red(), a_bg.current().green(), a_bg.current().blue(), 0));
a_bar.start(QColor(a_bar.current().red(), a_bar.current().green(), a_bar.current().blue(), 0));
- anim::start(this);
+ _a_appearance.start();
}
ScrollArea *ScrollBar::area() {
@@ -144,26 +152,24 @@ void ScrollBar::paintEvent(QPaintEvent *e) {
}
}
-bool ScrollBar::animStep(float64 ms) {
+void ScrollBar::step_appearance(float64 ms, bool timer) {
float64 dt = ms / _st->duration;
- bool res = true;
if (dt >= 1) {
+ _a_appearance.stop();
a_bg.finish();
a_bar.finish();
- res = false;
} else {
a_bg.update(dt, anim::linear);
a_bar.update(dt, anim::linear);
}
- update();
- return res;
+ if (timer) update();
}
void ScrollBar::hideTimeout(int64 dt) {
if (_hideIn < 0) {
a_bg.start((_over ? _st->bgOverColor : _st->bgColor)->c);
a_bar.start((_overbar ? _st->barOverColor : _st->barColor)->c);
- anim::start(this);
+ _a_appearance.start();
}
_hideIn = dt;
if (!_moving && _hideIn >= 0) {
@@ -177,7 +183,7 @@ void ScrollBar::enterEvent(QEvent *e) {
_over = true;
a_bg.start(_st->bgOverColor->c);
a_bar.start(_st->barColor->c);
- anim::start(this);
+ _a_appearance.start();
}
void ScrollBar::leaveEvent(QEvent *e) {
@@ -185,7 +191,7 @@ void ScrollBar::leaveEvent(QEvent *e) {
setMouseTracking(false);
a_bg.start(_st->bgColor->c);
a_bar.start(_st->barColor->c);
- anim::start(this);
+ _a_appearance.start();
if (_hideIn >= 0) {
_hideTimer.start(_hideIn);
} else if (_st->hiding) {
@@ -202,7 +208,7 @@ void ScrollBar::mouseMoveEvent(QMouseEvent *e) {
if (!_moving) {
a_bar.start((newOverBar ? _st->barOverColor : _st->barColor)->c);
a_bg.start(_st->bgOverColor->c);
- anim::start(this);
+ _a_appearance.start();
}
}
if (_moving) {
@@ -232,7 +238,7 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) {
_overbar = true;
a_bar.start(_st->barOverColor->c);
a_bg.start(_st->bgOverColor->c);
- anim::start(this);
+ _a_appearance.start();
}
}
emit area()->scrollStarted();
@@ -257,7 +263,7 @@ void ScrollBar::mouseReleaseEvent(QMouseEvent *e) {
_hideTimer.start(_hideIn);
}
}
- if (a) anim::start(this);
+ if (a) _a_appearance.start();
emit area()->scrollFinished();
}
if (!_over) {
diff --git a/Telegram/SourceFiles/gui/scrollarea.h b/Telegram/SourceFiles/gui/scrollarea.h
index 4029ffc25c..76add3f2ba 100644
--- a/Telegram/SourceFiles/gui/scrollarea.h
+++ b/Telegram/SourceFiles/gui/scrollarea.h
@@ -50,7 +50,7 @@ private:
};
-class ScrollBar : public QWidget, public Animated {
+class ScrollBar : public QWidget {
Q_OBJECT
public:
@@ -67,7 +67,7 @@ public:
void mouseReleaseEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
- bool animStep(float64 ms);
+ void step_appearance(float64 ms, bool timer);
void hideTimeout(int64 dt);
@@ -100,6 +100,8 @@ private:
QTimer _hideTimer;
anim::cvalue a_bg, a_bar;
+ Animation _a_appearance;
+
QRect _bar;
};
diff --git a/Telegram/SourceFiles/gui/style_core.h b/Telegram/SourceFiles/gui/style_core.h
index 8939118bc8..8642a701ac 100644
--- a/Telegram/SourceFiles/gui/style_core.h
+++ b/Telegram/SourceFiles/gui/style_core.h
@@ -304,6 +304,14 @@ namespace style {
typedef Font font;
typedef Color color;
+ inline QColor interpolate(const style::color &a, const style::color &b, float64 opacity_b) {
+ QColor result;
+ result.setRedF((a->c.redF() * (1. - opacity_b)) + (b->c.redF() * opacity_b));
+ result.setGreenF((a->c.greenF() * (1. - opacity_b)) + (b->c.greenF() * opacity_b));
+ result.setBlueF((a->c.blueF() * (1. - opacity_b)) + (b->c.blueF() * opacity_b));
+ return result;
+ }
+
void startManager();
void stopManager();
diff --git a/Telegram/SourceFiles/gui/switcher.cpp b/Telegram/SourceFiles/gui/switcher.cpp
deleted file mode 100644
index e0e9173c3e..0000000000
--- a/Telegram/SourceFiles/gui/switcher.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
-This file is part of Telegram Desktop,
-the official desktop version of Telegram messaging app, see https://telegram.org
-
-Telegram Desktop is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-It is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-In addition, as a special exception, the copyright holders give permission
-to link the code of portions of this program with the OpenSSL library.
-
-Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
-Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
-*/
-#include "stdafx.h"
-#include "switcher.h"
-
-Switcher::Switcher(QWidget *parent, const style::switcher &st) : TWidget(parent)
-, _selected(0)
-, _over(-1)
-, _wasOver(-1)
-, _pressed(-1)
-, _st(st)
-, a_bgOver(_st.bgColor->c)
-, a_bgWasOver(_st.bgHovered->c) {
- resize(width(), _st.height);
-}
-
-void Switcher::leaveEvent(QEvent *e) {
- setOver(-1);
- if (_pressed >= 0) return;
-
- setMouseTracking(false);
- return TWidget::leaveEvent(e);
-}
-
-void Switcher::enterEvent(QEvent *e) {
- setMouseTracking(true);
- return TWidget::enterEvent(e);
-}
-
-void Switcher::mousePressEvent(QMouseEvent *e) {
- if (e->buttons() & Qt::LeftButton) {
- mouseMoveEvent(e);
- if (_over != _pressed) {
- _pressed = _over;
- e->accept();
- }
- }
-}
-
-void Switcher::mouseMoveEvent(QMouseEvent *e) {
- if (rect().contains(e->pos())) {
- if (width()) {
- setOver((e->pos().x() * _buttons.size()) / width());
- }
- } else {
- setOver(-1);
- }
-}
-
-void Switcher::mouseReleaseEvent(QMouseEvent *e) {
- if (_pressed >= 0) {
- if (_pressed == _over && _pressed != _selected) {
- setSelected(_pressed);
- } else {
- setSelected(_selected);
- }
- } else {
- leaveEvent(e);
- }
-}
-
-void Switcher::addButton(const QString &btn) {
- _buttons.push_back(btn);
- update();
-}
-
-bool Switcher::animStep(float64 ms) {
- float64 dt = ms / _st.duration;
- bool res = true;
- if (dt >= 1) {
- res = false;
- a_bgOver.finish();
- a_bgWasOver.finish();
- } else {
- a_bgOver.update(dt, anim::linear);
- a_bgWasOver.update(dt, anim::linear);
- }
- update();
- return res;
-}
-
-void Switcher::paintEvent(QPaintEvent *e) {
- QPainter p(this);
-
- p.fillRect(rect(), _st.bgColor->b);
- if (!_buttons.isEmpty()) {
- p.setFont(_st.font->f);
- float64 btnWidth = float64(width()) / _buttons.size();
- for (int i = 0; i < _buttons.size(); ++i) {
- QRect btnRect(qRound(i * btnWidth), 0, qRound((i + 1) * btnWidth) - qRound(i * btnWidth), height());
- if (i == _selected) {
- p.fillRect(btnRect, _st.bgActive->b);
- } else if (i == _over) {
- p.fillRect(btnRect, a_bgOver.current());
- } else if (i == _wasOver) {
- p.fillRect(btnRect, a_bgWasOver.current());
- }
- p.setPen((i == _selected ? _st.activeColor : _st.textColor)->p);
- p.drawText(btnRect, _buttons[i], style::al_center);
- }
- }
- if (_st.border) {
- p.fillRect(0, 0, width() - _st.border, _st.border, _st.borderColor->b);
- p.fillRect(width() - _st.border, 0, _st.border, height() - _st.border, _st.borderColor->b);
- p.fillRect(_st.border, height() - _st.border, width() - _st.border, _st.border, _st.borderColor->b);
- p.fillRect(0, _st.border, _st.border, height() - _st.border, _st.borderColor->b);
- }
-}
-
-int Switcher::selected() const {
- return _selected;
-}
-
-void Switcher::setSelected(int selected) {
- if (selected != _selected) {
- _selected = selected;
- emit changed();
- }
- _pressed = _over = _wasOver = -1;
- anim::stop(this);
- setCursor(style::cur_default);
- update();
-}
-
-void Switcher::setOver(int over) {
- if (over != _over) {
- QColor c(a_bgOver.current());
- if (_wasOver == over) {
- a_bgOver = anim::cvalue(a_bgWasOver.current(), _st.bgHovered->c);
- } else {
- a_bgOver = anim::cvalue(_st.bgColor->c, _st.bgHovered->c);
- }
- a_bgWasOver = anim::cvalue(c, _st.bgColor->c);
-
- _wasOver = _over;
- _over = over;
-
- anim::start(this);
-
- setCursor((_over >= 0 && _over != _selected) ? style::cur_pointer : style::cur_default);
- }
-}
diff --git a/Telegram/SourceFiles/gui/switcher.h b/Telegram/SourceFiles/gui/switcher.h
deleted file mode 100644
index 5b633514ab..0000000000
--- a/Telegram/SourceFiles/gui/switcher.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-This file is part of Telegram Desktop,
-the official desktop version of Telegram messaging app, see https://telegram.org
-
-Telegram Desktop is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-It is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-In addition, as a special exception, the copyright holders give permission
-to link the code of portions of this program with the OpenSSL library.
-
-Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
-Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
-*/
-#pragma once
-
-#include
-#include "gui/twidget.h"
-
-class Switcher : public TWidget, public Animated {
- Q_OBJECT
-
-public:
- Switcher(QWidget *parent, const style::switcher &st);
-
- void mousePressEvent(QMouseEvent *e);
- void mouseMoveEvent(QMouseEvent *e);
- void mouseReleaseEvent(QMouseEvent *e);
-
- void paintEvent(QPaintEvent *e);
-
- void enterEvent(QEvent *e);
- void leaveEvent(QEvent *e);
-
- void addButton(const QString &btn);
-
- bool animStep(float64 ms);
-
- int selected() const;
- void setSelected(int selected);
-
-signals:
-
- void changed();
-
-private:
-
- void setOver(int over);
-
- int _selected;
- int _over, _wasOver, _pressed;
-
- typedef QVector Buttons;
- Buttons _buttons;
-
- style::switcher _st;
- anim::cvalue a_bgOver, a_bgWasOver;
-
-};
diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp
index 5621122078..0dad2f9f4c 100644
--- a/Telegram/SourceFiles/gui/text.cpp
+++ b/Telegram/SourceFiles/gui/text.cpp
@@ -36,10 +36,10 @@ namespace {
const QRegularExpression _reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$"));
const QRegularExpression _reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
const QRegularExpression _reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[\\w]{2,64}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
- const QRegularExpression _reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{5,32}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
+ const QRegularExpression _reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{3,32}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
const QRegularExpression _reBotCommand(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])/[A-Za-z_0-9]{1,64}(@[A-Za-z_0-9]{5,32})?([\\W]|$)"));
- const QRegularExpression _rePre(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])(````?)[\\s\\S]+?(````?)([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"), QRegularExpression::UseUnicodePropertiesOption);
- const QRegularExpression _reCode(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])(`)[^\\n]+?(`)([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"), QRegularExpression::UseUnicodePropertiesOption);
+ const QRegularExpression _rePre(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10])(````?)[\\s\\S]+?(````?)([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"), QRegularExpression::UseUnicodePropertiesOption);
+ const QRegularExpression _reCode(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10])(`)[^\\n]+?(`)([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"), QRegularExpression::UseUnicodePropertiesOption);
QSet _validProtocols, _validTopDomains;
const style::textStyle *_textStyle = 0;
@@ -103,6 +103,10 @@ const TextLinkPtr &textlnkDown() {
return _downLnk;
}
+bool textlnkDrawOver(const TextLinkPtr &lnk) {
+ return (_overLnk == lnk) && (!_downLnk || _downLnk == lnk);
+}
+
QString textOneLine(const QString &text, bool trim, bool rich) {
QString result(text);
const QChar *s = text.unicode(), *ch = s, *e = text.unicode() + text.size();
@@ -259,7 +263,7 @@ const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink)
class TextParser {
public:
-
+
static Qt::LayoutDirection stringDirection(const QString &str, int32 from, int32 to) {
const ushort *p = reinterpret_cast(str.unicode()) + from;
const ushort *end = p + (to - from);
@@ -336,7 +340,7 @@ public:
void getLinkData(const QString &original, QString &result, int32 &fullDisplayed) {
if (!original.isEmpty() && original.at(0) == '/') {
result = original;
- fullDisplayed = -4; // bot command
+ fullDisplayed = -4; // bot command
} else if (!original.isEmpty() && original.at(0) == '@') {
result = original;
fullDisplayed = -3; // mention
@@ -450,7 +454,7 @@ public:
while (waitingEntity != entitiesEnd && waitingEntity->length <= 0) ++waitingEntity;
}
}
-
+
bool readCommand() {
const QChar *afterCmd = textSkipCommand(ptr, end, links.size() < 0x7FFF);
if (afterCmd == ptr) {
@@ -594,7 +598,7 @@ public:
void parseEmojiFromCurrent() {
int len = 0;
- EmojiPtr e = emojiFromText(ptr - emojiLookback, end, len);
+ EmojiPtr e = emojiFromText(ptr - emojiLookback, end, &len);
if (!e) return;
for (int l = len - emojiLookback - 1; l > 0; --l) {
@@ -916,7 +920,7 @@ void EmailLink::onClick(Qt::MouseButton button) const {
}
void CustomTextLink::onClick(Qt::MouseButton button) const {
- App::wnd()->showLayer(new ConfirmLinkBox(text()));
+ Ui::showLayer(new ConfirmLinkBox(text()));
}
void MentionLink::onClick(Qt::MouseButton button) const {
@@ -971,7 +975,7 @@ public:
void initParagraphBidi() {
if (!_parLength || !_parAnalysis.isEmpty()) return;
-
+
Text::TextBlocks::const_iterator i = _parStartBlock, e = _t->_blocks.cend(), n = i + 1;
bool ignore = false;
@@ -1027,6 +1031,9 @@ public:
_y = top;
_yFrom = yFrom + top;
_yTo = (yTo < 0) ? -1 : (yTo + top);
+ if (_elideLast) {
+ _yToElide = _yTo;
+ }
_selectedFrom = selectedFrom;
_selectedTo = selectedTo;
_wLeft = _w = w;
@@ -1077,7 +1084,7 @@ public:
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
_wLeft = _w - (b->f_width() - last_rBearing);
- if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yTo)) {
+ if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yToElide)) {
_wLeft -= _elideRemoveFromEnd;
}
@@ -1104,6 +1111,15 @@ public:
if (_btype == TextBlockTText) {
TextBlock *t = static_cast(b);
+ if (t->_words.isEmpty()) { // no words in this block, spaces only => layout this block in the same line
+ last_rPadding += lpadding;
+
+ _lineHeight = qMax(_lineHeight, blockHeight);
+
+ longWordLine = false;
+ continue;
+ }
+
QFixed f_wLeft = _wLeft; // vars for saving state of the last word start
int32 f_lineHeight = _lineHeight; // f points to the last word-start element of t->_words
for (TextBlock::TextWords::const_iterator j = t->_words.cbegin(), en = t->_words.cend(), f = j; j != en; ++j) {
@@ -1131,7 +1147,7 @@ public:
}
int32 elidedLineHeight = qMax(_lineHeight, blockHeight);
- bool elidedLine = _elideLast && (_y + elidedLineHeight >= _yTo);
+ bool elidedLine = _elideLast && (_y + elidedLineHeight >= _yToElide);
if (elidedLine) {
_lineHeight = elidedLineHeight;
} else if (f != j) {
@@ -1150,7 +1166,7 @@ public:
last_rBearing = j->f_rbearing();
last_rPadding = j->rpadding;
_wLeft = _w - (j_width - last_rBearing);
- if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yTo)) {
+ if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yToElide)) {
_wLeft -= _elideRemoveFromEnd;
}
@@ -1159,33 +1175,11 @@ public:
f_wLeft = _wLeft;
f_lineHeight = _lineHeight;
}
- if (lpadding > 0) { // no words in this block, spaces only
- int32 elidedLineHeight = qMax(_lineHeight, blockHeight);
- bool elidedLine = _elideLast && (_y + elidedLineHeight >= _yTo);
- if (elidedLine) {
- _lineHeight = elidedLineHeight;
- }
- ushort nextStart = _blockEnd(_t, i, e);
- if (!drawLine(nextStart, i + 1, e)) return;
- _y += _lineHeight;
- _lineHeight = qMax(0, blockHeight);
- _lineStart = nextStart;
- _lineStartBlock = blockIndex + 1;
-
- last_rBearing = _rb;
- last_rPadding = b->rpadding();
- _wLeft = _w;
- if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yTo)) {
- _wLeft -= _elideRemoveFromEnd;
- }
-
- longWordLine = true;
- }
continue;
}
int32 elidedLineHeight = qMax(_lineHeight, blockHeight);
- bool elidedLine = _elideLast && (_y + elidedLineHeight >= _yTo);
+ bool elidedLine = _elideLast && (_y + elidedLineHeight >= _yToElide);
if (elidedLine) {
_lineHeight = elidedLineHeight;
}
@@ -1198,7 +1192,7 @@ public:
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
_wLeft = _w - (b->f_width() - last_rBearing);
- if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yTo)) {
+ if (_elideLast && _elideRemoveFromEnd > 0 && (_y + blockHeight >= _yToElide)) {
_wLeft -= _elideRemoveFromEnd;
}
@@ -1297,7 +1291,7 @@ public:
}
ITextBlock *_endBlock = (_endBlockIter == _end) ? 0 : (*_endBlockIter);
- bool elidedLine = _elideLast && _endBlock && (_y + _lineHeight >= _yTo);
+ bool elidedLine = _elideLast && _endBlock && (_y + _lineHeight >= _yToElide);
int blockIndex = _lineStartBlock;
ITextBlock *currentBlock = _t->_blocks[blockIndex];
@@ -1333,7 +1327,7 @@ public:
*_getSymbolAfter = false;
*_getSymbolUpon = ((_lnkX >= _x) && (_lineStart > 0)) ? true : false;
}
- return false;
+ return false;
} else if (_lnkX >= x + (_w - _wLeft)) {
if (_parDirection == Qt::RightToLeft) {
*_getSymbol = _lineStart;
@@ -1385,6 +1379,7 @@ public:
return true;
}
+ int skipIndex = -1;
QVarLengthArray visualOrder(nItems);
QVarLengthArray levels(nItems);
for (int i = 0; i < nItems; ++i) {
@@ -1396,6 +1391,7 @@ public:
TextBlockType _type = currentBlock->type();
if (_type == TextBlockTSkip) {
levels[i] = si.analysis.bidiLevel = 0;
+ skipIndex = i;
} else {
levels[i] = si.analysis.bidiLevel;
}
@@ -1406,6 +1402,13 @@ public:
}
}
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+ if (rtl() && skipIndex == nItems - 1) {
+ for (int32 i = nItems; i > 1;) {
+ --i;
+ visualOrder[i] = visualOrder[i - 1];
+ }
+ visualOrder[0] = skipIndex;
+ }
blockIndex = _lineStartBlock;
currentBlock = _t->_blocks[blockIndex];
@@ -2396,7 +2399,7 @@ private:
int32 _elideRemoveFromEnd;
style::align _align;
QPen _originalPen;
- int32 _yFrom, _yTo;
+ int32 _yFrom, _yTo, _yToElide;
uint16 _selectedFrom, _selectedTo;
const QChar *_str;
@@ -2412,7 +2415,7 @@ private:
style::font _f;
QFixed _x, _w, _wLeft;
int32 _y, _yDelta, _lineHeight, _fontHeight;
-
+
// elided hack support
int32 _blocksSize;
int32 _elideSavedIndex;
@@ -2746,8 +2749,8 @@ int32 Text::countHeight(int32 w) const {
longWordLine = true;
continue;
}
- widthLeft -= b->f_lpadding();
- QFixed newWidthLeft = widthLeft - last_rBearing - (last_rPadding + b->f_width() - _rb);
+ QFixed lpadding = b->f_lpadding();
+ QFixed newWidthLeft = widthLeft - lpadding - last_rBearing - (last_rPadding + b->f_width() - _rb);
if (newWidthLeft >= 0) {
last_rBearing = _rb;
last_rPadding = b->f_rpadding();
@@ -2761,13 +2764,23 @@ int32 Text::countHeight(int32 w) const {
if (_btype == TextBlockTText) {
TextBlock *t = static_cast(b);
+ if (t->_words.isEmpty()) { // no words in this block, spaces only => layout this block in the same line
+ last_rPadding += lpadding;
+
+ lineHeight = qMax(lineHeight, blockHeight);
+
+ longWordLine = false;
+ continue;
+ }
+
QFixed f_wLeft = widthLeft;
int32 f_lineHeight = lineHeight;
for (TextBlock::TextWords::const_iterator j = t->_words.cbegin(), e = t->_words.cend(), f = j; j != e; ++j) {
bool wordEndsHere = (j->width >= 0);
QFixed j_width = wordEndsHere ? j->width : -j->width;
- QFixed newWidthLeft = widthLeft - last_rBearing - (last_rPadding + j_width - j->f_rbearing());
+ QFixed newWidthLeft = widthLeft - lpadding - last_rBearing - (last_rPadding + j_width - j->f_rbearing());
+ lpadding = 0;
if (newWidthLeft >= 0) {
last_rBearing = j->f_rbearing();
last_rPadding = j->rpadding;
@@ -2897,7 +2910,7 @@ QString Text::original(uint16 selectedFrom, uint16 selectedTo, ExpandLinksMode m
result.reserve(_text.size());
int32 lnkFrom = 0, lnkIndex = 0;
- for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) {
+ for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) {
int32 blockLnkIndex = (i == e) ? 0 : (*i)->lnkIndex();
int32 blockFrom = (i == e) ? _text.size() : (*i)->from();
if (blockLnkIndex != lnkIndex) {
@@ -3140,7 +3153,8 @@ namespace {
class BlockParser {
public:
- BlockParser(QTextEngine *e, TextBlock *b, QFixed minResizeWidth, int32 blockFrom) : block(b), eng(e) {
+ BlockParser(QTextEngine *e, TextBlock *b, QFixed minResizeWidth, int32 blockFrom, const QString &str)
+ : block(b), eng(e), str(str) {
parseWords(minResizeWidth, blockFrom);
}
@@ -3218,7 +3232,7 @@ public:
if (lbh.currentPosition >= eng->layoutData->string.length()
|| attributes[lbh.currentPosition].whiteSpace
- || attributes[lbh.currentPosition].lineBreak) {
+ || isLineBreak(attributes, lbh.currentPosition)) {
lbh.adjustRightBearing();
block->_words.push_back(TextWord(wordStart + blockFrom, lbh.tmpData.textWidth, qMin(QFixed(), lbh.rightBearing)));
block->_width += lbh.tmpData.textWidth;
@@ -3265,10 +3279,19 @@ public:
}
}
+ bool isLineBreak(const QCharAttributes *attributes, int32 index) {
+ bool lineBreak = attributes[index].lineBreak;
+ if (lineBreak && block->lnkIndex() > 0 && index > 0 && str.at(index - 1) == '/') {
+ return false; // don't break after / in links
+ }
+ return lineBreak;
+ }
+
private:
TextBlock *block;
QTextEngine *eng;
+ const QString &str;
};
@@ -3302,14 +3325,15 @@ TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResi
}
}
- QStackTextEngine engine(str.mid(_from, length), blockFont->f);
+ QString part = str.mid(_from, length);
+ QStackTextEngine engine(part, blockFont->f);
engine.itemize();
QTextLayout layout(&engine);
layout.beginLayout();
layout.createLine();
- BlockParser parser(&engine, this, minResizeWidth, _from);
+ BlockParser parser(&engine, this, minResizeWidth, _from, part);
layout.endLayout();
}
@@ -3671,7 +3695,7 @@ void initLinkSets() {
namespace {
// accent char list taken from https://github.com/aristus/accent-folding
- inline QChar chNoAccent(int32 code) {
+ inline QChar chNoAccent(int32 code) {
switch (code) {
case 7834: return QChar(97);
case 193: return QChar(97);
@@ -4395,7 +4419,7 @@ QString textAccentFold(const QString &text) {
result[i] = noAccent;
} else {
if (copying) result[i] = *ch;
- ++ch, ++i;
+ ++ch, ++i;
if (copying) result[i] = *ch;
}
} else {
@@ -4478,8 +4502,7 @@ goodCanBreakEntity = canBreakEntity;\
#undef MARK_GOOD_AS_LEVEL
int elen = 0;
- EmojiPtr e = emojiFromText(ch, end, elen);
- if (e) {
+ if (EmojiPtr e = emojiFromText(ch, end, &elen)) {
for (int i = 0; i < elen; ++i, ++ch, ++s) {
if (ch->isHighSurrogate() && i + 1 < elen && (ch + 1)->isLowSurrogate()) {
++ch;
diff --git a/Telegram/SourceFiles/gui/text.h b/Telegram/SourceFiles/gui/text.h
index c1ff5a911c..bd95be3163 100644
--- a/Telegram/SourceFiles/gui/text.h
+++ b/Telegram/SourceFiles/gui/text.h
@@ -323,6 +323,8 @@ public:
virtual bool fullDisplayed() const {
return true;
}
+ virtual void setFullDisplayed(bool full) {
+ }
virtual QString encoded() const {
return QString();
}
@@ -364,6 +366,10 @@ public:
return _fullDisplayed;
}
+ void setFullDisplayed(bool full) {
+ _fullDisplayed = full;
+ }
+
QString encoded() const {
QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString());
QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _url);
@@ -680,6 +686,8 @@ const TextLinkPtr &textlnkOver();
void textlnkDown(const TextLinkPtr &lnk);
const TextLinkPtr &textlnkDown();
+bool textlnkDrawOver(const TextLinkPtr &lnk);
+
// textcmd
QString textcmdSkipBlock(ushort w, ushort h);
QString textcmdStartLink(ushort lnkIndex);
diff --git a/Telegram/SourceFiles/gui/twidget.h b/Telegram/SourceFiles/gui/twidget.h
index 411762e1b7..d01504c2b7 100644
--- a/Telegram/SourceFiles/gui/twidget.h
+++ b/Telegram/SourceFiles/gui/twidget.h
@@ -148,6 +148,9 @@ QRect myrtlrect(const QRect &r) { \
void rtlupdate(const QRect &r) { \
update(myrtlrect(r)); \
} \
+void rtlupdate(int x, int y, int w, int h) { \
+ update(myrtlrect(x, y, w, h)); \
+} \
protected: \
void enterEvent(QEvent *e) { \
TWidget *p(tparent()); \
diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp
index 28854b3973..ef73e71ac3 100644
--- a/Telegram/SourceFiles/history.cpp
+++ b/Telegram/SourceFiles/history.cpp
@@ -22,53 +22,18 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
#include "style.h"
#include "lang.h"
-#include "history.h"
#include "mainwidget.h"
#include "application.h"
#include "fileuploader.h"
#include "window.h"
#include "gui/filedialog.h"
+#include "boxes/addcontactbox.h"
+#include "boxes/confirmbox.h"
+
#include "audio.h"
#include "localstorage.h"
-TextParseOptions _textNameOptions = {
- 0, // flags
- 4096, // maxw
- 1, // maxh
- Qt::LayoutDirectionAuto, // lang-dependent
-};
-TextParseOptions _textDlgOptions = {
- 0, // flags
- 0, // maxw is style-dependent
- 1, // maxh
- Qt::LayoutDirectionAuto, // lang-dependent
-};
-TextParseOptions _historyTextOptions = {
- TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText | TextParseMono, // flags
- 0, // maxw
- 0, // maxh
- Qt::LayoutDirectionAuto, // dir
-};
-TextParseOptions _historyBotOptions = {
- TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands | TextParseMultiline | TextParseRichText | TextParseMono, // flags
- 0, // maxw
- 0, // maxh
- Qt::LayoutDirectionAuto, // dir
-};
-TextParseOptions _historyTextNoMonoOptions = {
- TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
- 0, // maxw
- 0, // maxh
- Qt::LayoutDirectionAuto, // dir
-};
-TextParseOptions _historyBotNoMonoOptions = {
- TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands | TextParseMultiline | TextParseRichText, // flags
- 0, // maxw
- 0, // maxh
- Qt::LayoutDirectionAuto, // dir
-};
-
namespace {
TextParseOptions _historySrvOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
@@ -110,8 +75,6 @@ namespace {
_webpageDescriptionOptions.maxh = st::webPageDescriptionFont->height * 3;
}
- AnimatedGif animated;
-
inline HistoryReply *toHistoryReply(HistoryItem *item) {
return item ? item->toHistoryReply() : 0;
}
@@ -127,53 +90,15 @@ namespace {
inline const TextParseOptions &itemTextOptions(HistoryItem *item) {
return itemTextOptions(item->history(), item->from());
}
- inline const TextParseOptions &itemTextNoMonoOptions(HistoryItem *item) {
+ inline const TextParseOptions &itemTextNoMonoOptions(const HistoryItem *item) {
return itemTextNoMonoOptions(item->history(), item->from());
}
}
-const TextParseOptions &itemTextOptions(History *h, PeerData *f) {
- if ((h->peer->isUser() && h->peer->asUser()->botInfo) || (f->isUser() && f->asUser()->botInfo) || (h->peer->isChat() && h->peer->asChat()->botStatus >= 0) || (h->peer->isMegagroup() && h->peer->asChannel()->mgInfo->botStatus >= 0)) {
- return _historyBotOptions;
- }
- return _historyTextOptions;
-}
-
-const TextParseOptions &itemTextNoMonoOptions(History *h, PeerData *f) {
- if ((h->peer->isUser() && h->peer->asUser()->botInfo) || (f->isUser() && f->asUser()->botInfo) || (h->peer->isChat() && h->peer->asChat()->botStatus >= 0) || (h->peer->isMegagroup() && h->peer->asChannel()->mgInfo->botStatus >= 0)) {
- return _historyBotNoMonoOptions;
- }
- return _historyTextNoMonoOptions;
-}
-
void historyInit() {
_initTextOptions();
}
-void startGif(HistoryItem *row, const FileLocation &file) {
- if (row == animated.msg) {
- stopGif();
- } else {
- animated.start(row, file);
- }
-}
-
-void itemReplacedGif(HistoryItem *oldItem, HistoryItem *newItem) {
- if (oldItem == animated.msg) {
- animated.msg = newItem;
- }
-}
-
-void itemRemovedGif(HistoryItem *item) {
- if (item == animated.msg) {
- animated.stop(true);
- }
-}
-
-void stopGif() {
- animated.stop();
-}
-
void DialogRow::paint(Painter &p, int32 w, bool act, bool sel, bool onlyBackground) const {
QRect fullRect(0, 0, w, st::dlgHeight);
p.fillRect(fullRect, (act ? st::dlgActiveBG : (sel ? st::dlgHoverBG : st::dlgBG))->b);
@@ -284,7 +209,7 @@ void FakeDialogRow::paint(Painter &p, int32 w, bool act, bool sel, bool onlyBack
QRect fullRect(0, 0, w, st::dlgHeight);
p.fillRect(fullRect, (act ? st::dlgActiveBG : (sel ? st::dlgHoverBG : st::dlgBG))->b);
if (onlyBackground) return;
-
+
History *history = _item->history();
if (history->peer->migrateTo()) {
p.drawPixmap(st::dlgPaddingHor, st::dlgPaddingVer, history->peer->migrateTo()->photo->pix(st::dlgPhotoSize));
@@ -396,8 +321,7 @@ void History::clearLastKeyboard() {
lastKeyboardFrom = 0;
}
-bool History::updateTyping(uint64 ms, uint32 dots, bool force) {
- if (!ms) ms = getms(true);
+bool History::updateTyping(uint64 ms, bool force) {
bool changed = force;
for (TypingUsers::iterator i = typing.begin(), e = typing.end(); i != e;) {
if (ms >= i.value()) {
@@ -444,7 +368,7 @@ bool History::updateTyping(uint64 ms, uint32 dots, bool force) {
}
}
if (!typingStr.isEmpty()) {
- if (typingText.lastDots(dots % 4)) {
+ if (typingText.lastDots(typingDots % 4)) {
changed = true;
}
}
@@ -457,25 +381,6 @@ bool History::updateTyping(uint64 ms, uint32 dots, bool force) {
return changed;
}
-void History::eraseFromOverview(MediaOverviewType type, MsgId msgId) {
- if (overviewIds[type].isEmpty()) return;
-
- History::MediaOverviewIds::iterator i = overviewIds[type].find(msgId);
- if (i == overviewIds[type].cend()) return;
-
- overviewIds[type].erase(i);
- for (History::MediaOverview::iterator i = overview[type].begin(), e = overview[type].end(); i != e; ++i) {
- if ((*i) == msgId) {
- overview[type].erase(i);
- if (overviewCountData[type] > 0) {
- --overviewCountData[type];
- }
- break;
- }
- }
- if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer, type);
-}
-
ChannelHistory::ChannelHistory(const PeerId &peer) : History(peer),
unreadCountAll(0),
_onlyImportant(!isMegagroup()),
@@ -601,7 +506,7 @@ void ChannelHistory::insertCollapseItem(MsgId wasMinId) {
HistoryItem *item = block->items.at(itemIndex);
if (insertAfter || item->id > wasMinId || (item->id == wasMinId && !item->isImportant())) {
_collapseMessage = new HistoryCollapse(this, block, wasMinId, item->date);
- if (!addNewInTheMiddle(_collapseMessage, blockIndex, itemIndex)) {
+ if (!addNewInTheMiddle(regItem(_collapseMessage), blockIndex, itemIndex)) {
_collapseMessage = 0;
}
return;
@@ -699,7 +604,7 @@ void ChannelHistory::addNewGroup(const MTPMessageGroup &group) {
for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) {
(*i)->y += dh;
}
- blocks.push_front(dateBlock); // date block
+ blocks.push_front(dateBlock); // date block CHECK
height += dh;
}
}
@@ -745,12 +650,12 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) {
++itemIndex;
if (item->date.date() != inviteDate.date()) {
HistoryDateMsg *joinedDateItem = new HistoryDateMsg(this, block, inviteDate.date());
- if (addNewInTheMiddle(joinedDateItem, blockIndex, itemIndex)) {
+ if (addNewInTheMiddle(regItem(joinedDateItem), blockIndex, itemIndex)) {
++itemIndex;
}
}
_joinedMessage = new HistoryJoined(this, block, inviteDate, inviter, flags);
- if (!addNewInTheMiddle(_joinedMessage, blockIndex, itemIndex)) {
+ if (!addNewInTheMiddle(regItem(_joinedMessage), blockIndex, itemIndex)) {
_joinedMessage = 0;
}
if (lastSeenDateItem && lastSeenDateItem->date.date() == inviteDate.date()) {
@@ -784,11 +689,7 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) {
HistoryBlock *block = new HistoryBlock(this);
_joinedMessage = new HistoryJoined(this, block, inviteDate, inviter, flags);
- if (regItem(_joinedMessage)) {
- addItemAfterPrevToBlock(_joinedMessage, 0, block);
- } else {
- _joinedMessage = 0;
- }
+ addItemAfterPrevToBlock(regItem(_joinedMessage), 0, block);
if (till && _joinedMessage && inviteDate.date() != till->date.date()) {
HistoryItem *dayItem = createDayServiceMsg(this, block, till->date);
block->items.push_back(dayItem);
@@ -798,7 +699,7 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) {
}
}
if (!block->items.isEmpty()) {
- blocks.push_front(block);
+ blocks.push_front(block); // CHECK
if (width) {
addToH += block->height;
++skip;
@@ -819,7 +720,7 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) {
addToH += dh;
++skip;
}
- blocks.push_front(dateBlock); // date block
+ blocks.push_front(dateBlock); // date block CHECK
}
if (width && addToH) {
for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) {
@@ -963,7 +864,14 @@ HistoryItem *ChannelHistory::addNewToBlocks(const MTPMessage &msg, NewMessageTyp
} else {
to = blocks.back();
}
- return addNewItem(to, newBlock, createItem(to, msg, (type == NewMessageUnread)), (type == NewMessageUnread));
+ HistoryItem *item = createItem((type == NewMessageLast) ? 0 : to, msg, (type == NewMessageUnread));
+ if (type == NewMessageLast) {
+ if (!item->detached()) {
+ return item;
+ }
+ item->attach(to);
+ }
+ return addNewItem(to, newBlock, item, (type == NewMessageUnread));
}
void ChannelHistory::addNewToOther(HistoryItem *item, NewMessageType type) {
@@ -1024,9 +932,11 @@ void ChannelHistory::switchMode() {
item->attach(block);
prev = addItemAfterPrevToBlock(item, prev, block);
}
- block->y = height;
- blocks.push_back(block);
- height += block->height;
+ blocks.push_back(block); // CHECK
+ if (width) {
+ block->y = height;
+ height += block->height;
+ }
}
}
@@ -1334,7 +1244,7 @@ void Histories::regSendAction(History *history, UserData *user, const MTPSendMes
return;
}
- uint64 ms = getms(true);
+ uint64 ms = getms();
switch (action.type()) {
case mtpc_sendMessageTypingAction: history->typing[user] = ms + 6000; break;
case mtpc_sendMessageRecordVideoAction: history->sendActions.insert(user, SendAction(SendActionRecordVideo, ms + 6000)); break;
@@ -1353,25 +1263,25 @@ void Histories::regSendAction(History *history, UserData *user, const MTPSendMes
TypingHistories::const_iterator i = typing.find(history);
if (i == typing.cend()) {
typing.insert(history, ms);
- history->typingFrame = 0;
+ history->typingDots = 0;
+ _a_typings.start();
}
-
- history->updateTyping(ms, history->typingFrame, true);
- anim::start(this);
+ history->updateTyping(ms, true);
}
-bool Histories::animStep(float64) {
- uint64 ms = getms(true);
+void Histories::step_typings(uint64 ms, bool timer) {
for (TypingHistories::iterator i = typing.begin(), e = typing.end(); i != e;) {
- uint32 typingFrame = (ms - i.value()) / 150;
- i.key()->updateTyping(ms, typingFrame);
+ i.key()->typingDots = (ms - i.value()) / 150;
+ i.key()->updateTyping(ms);
if (i.key()->typing.isEmpty() && i.key()->sendActions.isEmpty()) {
i = typing.erase(i);
} else {
++i;
}
}
- return !typing.isEmpty();
+ if (typing.isEmpty()) {
+ _a_typings.stop();
+ }
}
void Histories::remove(const PeerId &peer) {
@@ -1390,28 +1300,34 @@ HistoryItem *Histories::addNewMessage(const MTPMessage &msg, NewMessageType type
return findOrInsert(peer, 0, 0)->addNewMessage(msg, type);
}
-HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, bool applyServiceAction, bool returnExisting) {
- HistoryItem *result = 0;
-
+HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, bool applyServiceAction) {
MsgId msgId = 0;
switch (msg.type()) {
case mtpc_messageEmpty: msgId = msg.c_messageEmpty().vid.v; break;
case mtpc_message: msgId = msg.c_message().vid.v; break;
case mtpc_messageService: msgId = msg.c_messageService().vid.v; break;
}
+ if (!msgId) return 0;
- HistoryItem *existing = App::histItemById(channelId(), msgId);
- if (existing) {
- bool regged = false;
- if (existing->detached() && block) {
- existing->attach(block);
- regged = true;
+ HistoryItem *result = App::histItemById(channelId(), msgId);
+ if (result) {
+ if (block) {
+ if (!result->detached()) {
+ result->detach();
+ }
+ result->attach(block);
}
-
if (msg.type() == mtpc_message) {
- existing->updateMedia(msg.c_message().has_media() ? (&msg.c_message().vmedia) : 0, (block ? false : true));
+ result->updateMedia(msg.c_message().has_media() ? (&msg.c_message().vmedia) : 0);
+ result->initDimensions();
+ if (!block) {
+ Notify::historyItemResized(result);
+ }
+ if (applyServiceAction) {
+ App::checkSavedGif(result);
+ }
}
- return (returnExisting || regged) ? existing : 0;
+ return result;
}
switch (msg.type()) {
@@ -1478,8 +1394,13 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo
case mtpc_messageMediaUnsupported:
default: badMedia = 1; break;
}
- if (badMedia) {
- result = new HistoryServiceMsg(this, block, m.vid.v, date(m.vdate), lang((badMedia == 2) ? lng_message_empty : lng_media_unsupported), m.vflags.v, 0, m.has_from_id() ? m.vfrom_id.v : 0);
+ if (badMedia == 1) {
+ QString text(lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org")));
+ EntitiesInText entities = textParseEntities(text, _historyTextNoMonoOptions.flags);
+ entities.push_front(EntityInText(EntityInTextItalic, 0, text.size()));
+ result = new HistoryMessage(this, block, m.vid.v, m.vflags.v, m.vvia_bot_id.v, date(m.vdate), m.vfrom_id.v, text, entities, 0);
+ } else if (badMedia) {
+ result = new HistoryServiceMsg(this, block, m.vid.v, date(m.vdate), lang(lng_message_empty), m.vflags.v, 0, m.has_from_id() ? m.vfrom_id.v : 0);
} else {
if ((m.has_fwd_date() && m.vfwd_date.v > 0) || (m.has_fwd_from_id() && peerFromMTP(m.vfwd_from_id) != 0)) {
result = new HistoryForwarded(this, block, m);
@@ -1617,24 +1538,36 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo
} break;
}
- return regItem(result, returnExisting);
-}
-
-HistoryItem *History::createItemForwarded(HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg) {
- HistoryItem *result = 0;
-
- result = new HistoryForwarded(this, block, id, date, from, msg);
+ if (applyServiceAction) {
+ App::checkSavedGif(result);
+ }
return regItem(result);
}
-HistoryItem *History::createItemDocument(HistoryBlock *block, MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc) {
+HistoryItem *History::createItemForwarded(HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg) {
+ return regItem(new HistoryForwarded(this, block, id, date, from, msg));
+}
+
+HistoryItem *History::createItemDocument(HistoryBlock *block, MsgId id, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption) {
+ HistoryItem *result = 0;
+
+ if ((flags & MTPDmessage::flag_reply_to_msg_id) && replyTo > 0) {
+ result = new HistoryReply(this, block, id, flags, viaBotId, replyTo, date, from, doc, caption);
+ } else {
+ result = new HistoryMessage(this, block, id, flags, viaBotId, date, from, doc, caption);
+ }
+
+ return regItem(result);
+}
+
+HistoryItem *History::createItemPhoto(HistoryBlock *block, MsgId id, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption) {
HistoryItem *result = 0;
if (flags & MTPDmessage::flag_reply_to_msg_id && replyTo > 0) {
- result = new HistoryReply(this, block, id, flags, replyTo, date, from, doc);
+ result = new HistoryReply(this, block, id, flags, viaBotId, replyTo, date, from, photo, caption);
} else {
- result = new HistoryMessage(this, block, id, flags, date, from, doc);
+ result = new HistoryMessage(this, block, id, flags, viaBotId, date, from, photo, caption);
}
return regItem(result);
@@ -1649,7 +1582,8 @@ HistoryItem *History::addNewService(MsgId msgId, QDateTime date, const QString &
to = blocks.back();
}
- return addNewItem(to, newBlock, regItem(new HistoryServiceMsg(this, to, msgId, date, text, flags, media)), newMsg);
+ HistoryItem *result = new HistoryServiceMsg(this, to, msgId, date, text, flags, media);
+ return addNewItem(to, newBlock, regItem(result), newMsg);
}
HistoryItem *History::addNewMessage(const MTPMessage &msg, NewMessageType type) {
@@ -1674,11 +1608,18 @@ HistoryItem *History::addNewMessage(const MTPMessage &msg, NewMessageType type)
} else {
to = blocks.back();
}
- return addNewItem(to, newBlock, createItem(to, msg, (type == NewMessageUnread)), (type == NewMessageUnread));
+ HistoryItem *item = createItem((type == NewMessageLast) ? 0 : to, msg, (type == NewMessageUnread));
+ if (type == NewMessageLast) {
+ if (!item->detached()) {
+ return item;
+ }
+ item->attach(to);
+ }
+ return addNewItem(to, newBlock, item, (type == NewMessageUnread));
}
HistoryItem *History::addToHistory(const MTPMessage &msg) {
- return createItem(0, msg, false, true);
+ return createItem(0, msg, false);
}
HistoryItem *History::addNewForwarded(MsgId id, QDateTime date, int32 from, HistoryMessage *item) {
@@ -1692,7 +1633,7 @@ HistoryItem *History::addNewForwarded(MsgId id, QDateTime date, int32 from, Hist
return addNewItem(to, newBlock, createItemForwarded(to, id, date, from, item), true);
}
-HistoryItem *History::addNewDocument(MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc) {
+HistoryItem *History::addNewDocument(MsgId id, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption) {
HistoryBlock *to = 0;
bool newBlock = blocks.isEmpty();
if (newBlock) {
@@ -1700,7 +1641,18 @@ HistoryItem *History::addNewDocument(MsgId id, int32 flags, MsgId replyTo, QDate
} else {
to = blocks.back();
}
- return addNewItem(to, newBlock, createItemDocument(to, id, flags, replyTo, date, from, doc), true);
+ return addNewItem(to, newBlock, createItemDocument(to, id, flags, viaBotId, replyTo, date, from, doc, caption), true);
+}
+
+HistoryItem *History::addNewPhoto(MsgId id, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption) {
+ HistoryBlock *to = 0;
+ bool newBlock = blocks.isEmpty();
+ if (newBlock) {
+ to = new HistoryBlock(this);
+ } else {
+ to = blocks.back();
+ }
+ return addNewItem(to, newBlock, createItemPhoto(to, id, flags, viaBotId, replyTo, date, from, photo, caption), true);
}
void History::createInitialDateBlock(const QDateTime &date) {
@@ -1708,34 +1660,59 @@ void History::createInitialDateBlock(const QDateTime &date) {
HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, date);
dateBlock->items.push_back(dayItem);
if (width) {
- int32 dh = dayItem->resize(width);
- dateBlock->height = dh;
- height += dh;
- for (int32 i = 0, l = blocks.size(); i < l; ++i) {
- blocks[i]->y += dh;
+ dateBlock->height += dayItem->resize(width);
+ }
+
+ blocks.push_front(dateBlock);
+ if (width) {
+ height += dateBlock->height;
+ for (int32 i = 1, l = blocks.size(); i < l; ++i) {
+ blocks.at(i)->y += dateBlock->height;
}
}
- blocks.push_front(dateBlock);
}
-void History::addToOverview(HistoryItem *item, MediaOverviewType type) {
- if (overviewIds[type].constFind(item->id) == overviewIds[type].cend()) {
- overview[type].push_back(item->id);
- overviewIds[type].insert(item->id, NullType());
+bool History::addToOverview(MediaOverviewType type, MsgId msgId, AddToOverviewMethod method) {
+ bool adding = false;
+ switch (method) {
+ case AddToOverviewNew:
+ case AddToOverviewFront: adding = (overviewIds[type].constFind(msgId) == overviewIds[type].cend()); break;
+ case AddToOverviewBack: adding = (overviewCountData[type] != 0); break;
+ }
+ if (!adding) return false;
+
+ overviewIds[type].insert(msgId, NullType());
+ switch (method) {
+ case AddToOverviewNew:
+ case AddToOverviewBack: overview[type].push_back(msgId); break;
+ case AddToOverviewFront: overview[type].push_front(msgId); break;
+ }
+ if (method == AddToOverviewNew) {
if (overviewCountData[type] > 0) {
++overviewCountData[type];
}
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer, type);
}
+ return true;
}
-bool History::addToOverviewFront(HistoryItem *item, MediaOverviewType type) {
- if (overviewIds[type].constFind(item->id) == overviewIds[type].cend()) {
- overview[type].push_front(item->id);
- overviewIds[type].insert(item->id, NullType());
- return true;
+void History::eraseFromOverview(MediaOverviewType type, MsgId msgId) {
+ if (overviewIds[type].isEmpty()) return;
+
+ History::MediaOverviewIds::iterator i = overviewIds[type].find(msgId);
+ if (i == overviewIds[type].cend()) return;
+
+ overviewIds[type].erase(i);
+ for (History::MediaOverview::iterator i = overview[type].begin(), e = overview[type].end(); i != e; ++i) {
+ if ((*i) == msgId) {
+ overview[type].erase(i);
+ if (overviewCountData[type] > 0) {
+ --overviewCountData[type];
+ }
+ break;
+ }
}
- return false;
+ if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer, type);
}
HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *adding, bool newMsg) {
@@ -1752,8 +1729,9 @@ HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *a
} else if (to->items.back()->date.date() != adding->date.date()) {
HistoryItem *dayItem = createDayServiceMsg(this, to, adding->date);
to->items.push_back(dayItem);
- dayItem->y = to->height;
if (width) {
+ dayItem->y = to->height;
+
int32 dh = dayItem->resize(width);
to->height += dh;
height += dh;
@@ -1772,23 +1750,7 @@ HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *a
newItemAdded(adding);
}
- if (adding->indexInOverview()) {
- HistoryMedia *media = adding->getMedia(true);
- if (media) {
- HistoryMediaType mt = media->type();
- MediaOverviewType t = mediaToOverviewType(mt);
- if (t != OverviewCount) {
- if (mt == MediaTypeDocument && static_cast(media)->document()->song()) {
- addToOverview(adding, OverviewAudioDocuments);
- } else {
- addToOverview(adding, t);
- }
- }
- }
- if (adding->hasTextLinks()) {
- addToOverview(adding, OverviewLinks);
- }
- }
+ adding->addToOverview(AddToOverviewNew);
if (adding->from()->id) {
if (adding->from()->isUser()) {
QList *lastAuthors = 0;
@@ -1858,16 +1820,16 @@ void History::unregTyping(UserData *from) {
uint64 updateAtMs = 0;
TypingUsers::iterator i = typing.find(from);
if (i != typing.end()) {
- updateAtMs = getms(true);
+ updateAtMs = getms();
i.value() = updateAtMs;
}
SendActionUsers::iterator j = sendActions.find(from);
if (j != sendActions.end()) {
- if (!updateAtMs) updateAtMs = getms(true);
+ if (!updateAtMs) updateAtMs = getms();
j.value().until = updateAtMs;
}
if (updateAtMs) {
- updateTyping(updateAtMs, 0, true);
+ updateTyping(updateAtMs, true);
}
}
@@ -1933,8 +1895,10 @@ HistoryItem *History::addMessageGroupAfterPrev(HistoryItem *newItem, HistoryItem
createInitialDateBlock(date);
block = new HistoryBlock(this);
- block->y = height;
- blocks.push_back(block);
+ blocks.push_back(block); // CHECK
+ if (width) {
+ block->y = height;
+ }
}
return addItemAfterPrevToBlock(regItem(new HistoryGroup(this, block, newItem, date)), prev, block);
}
@@ -1953,13 +1917,11 @@ void History::addOlderSlice(const QVector &slice, const QVectorconstData() : 0, *groupsIt = groupsBegin, *groupsEnd = (isChannel() && collapsed) ? (groupsBegin + collapsed->size()) : 0;
- int32 addToH = 0, skip = 0;
- if (!blocks.isEmpty()) { // remove date block
- if (width) addToH = -blocks.front()->height;
- delete blocks.front();
- blocks.pop_front();
+ HistoryItem *oldFirst = 0, *last = 0;
+ if (!blocks.isEmpty()) {
+ t_assert(blocks.size() > 1);
+ oldFirst = blocks.at(1)->items.front();
}
- HistoryItem *till = blocks.isEmpty() ? 0 : blocks.front()->items.front(), *prev = 0;
HistoryBlock *block = new HistoryBlock(this);
block->items.reserve(slice.size() + (collapsed ? collapsed->size() : 0));
@@ -1973,41 +1935,58 @@ void History::addOlderSlice(const QVector &slice, const QVectorc_messageGroup());
if (group.vmin_id.v >= adding->id) break;
- prev = addMessageGroupAfterPrevToBlock(group, prev, block);
+ last = addMessageGroupAfterPrevToBlock(group, last, block);
}
- prev = addItemAfterPrevToBlock(adding, prev, block);
+ last = addItemAfterPrevToBlock(adding, last, block);
}
for (; groupsIt != groupsEnd; ++groupsIt) {
if (groupsIt->type() != mtpc_messageGroup) continue;
const MTPDmessageGroup &group(groupsIt->c_messageGroup());
- prev = addMessageGroupAfterPrevToBlock(group, prev, block);
+ last = addMessageGroupAfterPrevToBlock(group, last, block);
}
- while (till && prev && till->type() == HistoryItemGroup && prev->type() == HistoryItemGroup) {
- static_cast(prev)->uniteWith(static_cast(till));
- till->detach();
- delete till;
- if (blocks.front()->items.isEmpty()) {
- delete blocks.front();
- blocks.pop_front();
+ while (oldFirst && last && oldFirst->type() == HistoryItemGroup && last->type() == HistoryItemGroup) {
+ static_cast(last)->uniteWith(static_cast(oldFirst));
+ oldFirst->destroy();
+ if (blocks.isEmpty()) {
+ oldFirst = 0;
+ } else {
+ t_assert(blocks.size() > 1);
+ oldFirst = blocks.at(1)->items.front();
}
- till = blocks.isEmpty() ? 0 : blocks.front()->items.front();
}
- if (till && prev && prev->date.date() != till->date.date()) {
- HistoryItem *dayItem = createDayServiceMsg(this, block, till->date);
+ if (oldFirst && last && last->date.date() != oldFirst->date.date()) {
+ HistoryItem *dayItem = createDayServiceMsg(this, block, oldFirst->date);
block->items.push_back(dayItem);
if (width) {
dayItem->y = block->height;
block->height += dayItem->resize(width);
}
}
- if (!block->items.isEmpty()) {
- blocks.push_front(block);
- if (width) {
- addToH += block->height;
- ++skip;
+ if (block->items.isEmpty()) {
+ oldLoaded = true;
+ delete block;
+ } else {
+ if (oldFirst) {
+ HistoryBlock *initial = blocks.at(0);
+ blocks[0] = block;
+ blocks.push_front(initial);
+ if (width) {
+ block->y = initial->height;
+ for (int32 i = 2, l = blocks.size(); i < l; ++i) {
+ blocks.at(i)->y += block->height;
+ }
+ height += block->height;
+ }
+ initial->items.at(0)->setDate(block->items.at(0)->date);
+ } else {
+ blocks.push_front(block);
+ if (width) {
+ height = block->height;
+ }
+ createInitialDateBlock(block->items.at(0)->date);
}
if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors / lastParticipants
@@ -2024,23 +2003,7 @@ void History::addOlderSlice(const QVector &slice, const QVectoritems.size(); i > 0; --i) {
HistoryItem *item = block->items[i - 1];
- if (item->indexInOverview()) {
- HistoryMedia *media = item->getMedia(true);
- if (media) {
- HistoryMediaType mt = media->type();
- MediaOverviewType t = mediaToOverviewType(mt);
- if (t != OverviewCount) {
- if (mt == MediaTypeDocument && static_cast(media)->document()->song()) {
- if (addToOverviewFront(item, OverviewAudioDocuments)) mask |= (1 << OverviewAudioDocuments);
- } else {
- if (addToOverviewFront(item, t)) mask |= (1 << t);
- }
- }
- }
- if (item->hasTextLinks()) {
- if (addToOverviewFront(item, OverviewLinks)) mask |= (1 << OverviewLinks);
- }
- }
+ mask |= item->addToOverview(AddToOverviewFront);
if (item->from()->id) {
if (lastAuthors) { // chats
if (item->from()->isUser()) {
@@ -2099,33 +2062,6 @@ void History::addOlderSlice(const QVector &slice, const QVectormediaOverviewUpdated(peer, MediaOverviewType(t));
}
}
- } else {
- delete block;
- }
- if (!blocks.isEmpty()) {
- HistoryBlock *dateBlock = new HistoryBlock(this);
- HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, blocks.front()->items.front()->date);
- dateBlock->items.push_back(dayItem);
- if (width) {
- int32 dh = dayItem->resize(width);
- dateBlock->height = dh;
- if (skip) {
- blocks.front()->y += dh;
- }
- addToH += dh;
- ++skip;
- }
- blocks.push_front(dateBlock); // date block
- }
- if (width && addToH) {
- for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) {
- if (skip) {
- --skip;
- } else {
- (*i)->y += addToH;
- }
- }
- height += addToH;
}
if (isChannel()) {
@@ -2172,16 +2108,22 @@ void History::addNewerSlice(const QVector &slice, const QVectoritems.size()) {
- block->y = height;
- blocks.push_back(block);
- height += block->height;
- } else {
+ if (block->items.isEmpty()) {
newLoaded = true;
setLastMessage(lastImportantMessage());
delete block;
+ } else {
+ blocks.push_back(block);
+ if (width) {
+ block->y = height;
+ height += block->height;
+ }
+ if (blocks.size() == 1) {
+ createInitialDateBlock(block->items.at(0)->date);
+ }
}
}
+
if (!wasLoadedAtBottom && loadedAtBottom()) { // add all loaded photos to overview
int32 mask = 0;
for (int32 i = 0; i < OverviewCount; ++i) {
@@ -2196,56 +2138,13 @@ void History::addNewerSlice(const QVector &slice, const QVectoritems.size(); ++j) {
- HistoryItem *item = b->items[j];
- if (!item->indexInOverview()) continue;
-
- HistoryMedia *media = item->getMedia(true);
- if (media) {
- HistoryMediaType mt = media->type();
- MediaOverviewType t = mediaToOverviewType(mt);
- if (t != OverviewCount) {
- if (mt == MediaTypeDocument && static_cast(media)->document()->song()) {
- t = OverviewAudioDocuments;
- if (overviewCountData[t] != 0) {
- overview[t].push_back(item->id);
- overviewIds[t].insert(item->id, NullType());
- mask |= (1 << t);
- }
- } else {
- if (overviewCountData[t] != 0) {
- overview[t].push_back(item->id);
- overviewIds[t].insert(item->id, NullType());
- mask |= (1 << t);
- }
- }
- }
- }
- if (item->hasTextLinks()) {
- MediaOverviewType t = OverviewLinks;
- if (overviewCountData[t] != 0) {
- overview[t].push_back(item->id);
- overviewIds[t].insert(item->id, NullType());
- mask |= (1 << t);
- }
- }
+ mask |= b->items[j]->addToOverview(AddToOverviewBack);
}
}
for (int32 t = 0; t < OverviewCount; ++t) {
if ((mask & (1 << t)) && App::wnd()) App::wnd()->mediaOverviewUpdated(peer, MediaOverviewType(t));
}
}
- if (wasEmpty && !isEmpty()) {
- HistoryBlock *dateBlock = new HistoryBlock(this);
- HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, blocks.front()->items.front()->date);
- dateBlock->items.push_back(dayItem);
- int32 dh = dayItem->resize(width);
- dateBlock->height = dh;
- for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) {
- (*i)->y += dh;
- }
- blocks.push_front(dateBlock); // date block
- height += dh;
- }
if (isChannel()) asChannelHistory()->checkJoinedMessage();
}
@@ -2357,7 +2256,7 @@ void History::setUnreadCount(int32 newUnreadCount, bool psUpdate) {
App::histories().unreadFull += newUnreadCount - unreadCount;
if (mute) App::histories().unreadMuted += newUnreadCount - unreadCount;
unreadCount = newUnreadCount;
- if (psUpdate && (!mute || cIncludeMuted())) App::wnd()->updateCounter();
+ if (psUpdate && (!mute || cIncludeMuted()) && App::wnd()) App::wnd()->updateCounter();
if (unreadBar) {
int32 count = unreadCount;
if (peer->migrateTo()) {
@@ -2407,7 +2306,7 @@ void History::getNextShowFrom(HistoryBlock *block, int32 i) {
void History::addUnreadBar() {
if (unreadBar || !showFrom || showFrom->detached() || !unreadCount) return;
-
+
int32 count = unreadCount;
if (peer->migrateTo()) {
if (History *h = App::historyLoaded(peer->migrateTo()->id)) {
@@ -2416,15 +2315,12 @@ void History::addUnreadBar() {
}
HistoryBlock *block = showFrom->block();
unreadBar = new HistoryUnreadBar(this, block, count, showFrom->date);
- if (!addNewInTheMiddle(unreadBar, blocks.indexOf(block), block->items.indexOf(showFrom))) {
+ if (!addNewInTheMiddle(regItem(unreadBar), blocks.indexOf(block), block->items.indexOf(showFrom))) {
unreadBar = 0;
}
}
HistoryItem *History::addNewInTheMiddle(HistoryItem *newItem, int32 blockIndex, int32 itemIndex) {
- if (!regItem(newItem)) {
- return 0;
- }
if (blockIndex < 0 || itemIndex < 0 || blockIndex >= blocks.size() || itemIndex > blocks.at(blockIndex)->items.size()) {
delete newItem;
return 0;
@@ -2600,9 +2496,10 @@ MsgId History::msgIdForRead() const {
return result;
}
-int32 History::geomResize(int32 newWidth, int32 *ytransform, HistoryItem *resizedItem) {
+int32 History::geomResize(int32 newWidth, int32 *ytransform, const HistoryItem *resizedItem) {
if (width != newWidth) resizedItem = 0; // recount all items
if (width != newWidth || resizedItem) {
+ width = newWidth;
int32 y = 0;
for (Blocks::iterator i = blocks.begin(), e = blocks.end(); i != e; ++i) {
HistoryBlock *block = *i;
@@ -2617,7 +2514,6 @@ int32 History::geomResize(int32 newWidth, int32 *ytransform, HistoryItem *resize
ytransform = 0;
}
}
- width = newWidth;
height = y;
}
return height;
@@ -2795,7 +2691,7 @@ void History::removeBlock(HistoryBlock *block) {
delete block;
}
-int32 HistoryBlock::geomResize(int32 newWidth, int32 *ytransform, HistoryItem *resizedItem) {
+int32 HistoryBlock::geomResize(int32 newWidth, int32 *ytransform, const HistoryItem *resizedItem) {
int32 y = 0;
for (Items::iterator i = items.begin(), e = items.end(); i != e; ++i) {
HistoryItem *item = *i;
@@ -2894,7 +2790,7 @@ void HistoryBlock::removeItem(HistoryItem *item) {
nextCollapse->destroy();
}
}
-
+
// fix date items
HistoryItem *nextItem = (i < items.size() - 1) ? items[i + 1] : ((myIndex < history->blocks.size() - 1) ? history->blocks[myIndex + 1]->items[0] : 0);
if (nextItem && nextItem == history->unreadBar) { // skip unread bar
@@ -2968,42 +2864,6 @@ void HistoryBlock::removeItem(HistoryItem *item) {
}
}
-bool ItemAnimations::animStep(float64 ms) {
- for (Animations::iterator i = _animations.begin(); i != _animations.end();) {
- const HistoryItem *item = i.key();
- if (item->animating()) {
- App::main()->msgUpdated(item);
- ++i;
- } else {
- i = _animations.erase(i);
- }
- }
- return !_animations.isEmpty();
-}
-
-uint64 ItemAnimations::animate(const HistoryItem *item, uint64 ms) {
- if (_animations.isEmpty()) {
- _animations.insert(item, ms);
- anim::start(this);
- return 0;
- }
- Animations::const_iterator i = _animations.constFind(item);
- if (i == _animations.cend()) i = _animations.insert(item, ms);
- return ms - i.value();
-}
-
-void ItemAnimations::remove(const HistoryItem *item) {
- _animations.remove(item);
-}
-
-namespace {
- ItemAnimations _itemAnimations;
-}
-
-ItemAnimations &itemAnimations() {
- return _itemAnimations;
-}
-
HistoryItem::HistoryItem(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime msgDate, int32 from) : y(0)
, id(msgId)
, date(msgDate)
@@ -3029,18 +2889,6 @@ void HistoryItem::destroy() {
history()->clearLastKeyboard();
if (App::main()) App::main()->updateBotKeyboard(history());
}
- HistoryMedia *m = getMedia(true);
- MediaOverviewType t = m ? mediaToOverviewType(m->type()) : OverviewCount;
- if (t != OverviewCount) {
- if (m->type() == MediaTypeDocument && static_cast(m)->document()->song()) {
- history()->eraseFromOverview(OverviewAudioDocuments, id);
- } else {
- history()->eraseFromOverview(t, id);
- }
- }
- if (hasTextLinks()) {
- history()->eraseFromOverview(OverviewLinks, id);
- }
delete this;
}
@@ -3076,99 +2924,296 @@ void HistoryItem::setId(MsgId newId) {
id = newId;
}
-HistoryItem::~HistoryItem() {
- itemAnimations().remove(this);
- App::historyUnregItem(this);
- if (id < 0) {
- App::app()->uploader()->cancel(fullId());
+void HistoryItem::clipCallback(ClipReaderNotification notification) {
+ HistoryMedia *media = getMedia();
+ if (!media) return;
+
+ ClipReader *reader = media ? media->getClipReader() : 0;
+ if (!reader) return;
+
+ switch (notification) {
+ case ClipReaderReinit: {
+ bool stopped = false;
+ if (reader->paused()) {
+ if (MainWidget *m = App::main()) {
+ if (!m->isItemVisible(this)) { // stop animation if it is not visible
+ media->stopInline(this);
+ if (DocumentData *document = media->getDocument()) { // forget data from memory
+ document->forget();
+ }
+ stopped = true;
+ }
+ }
+ }
+ if (!stopped) {
+ initDimensions();
+ Notify::historyItemResized(this);
+ Notify::historyItemLayoutChanged(this);
+ }
+ } break;
+
+ case ClipReaderRepaint: {
+ if (!reader->currentDisplayed()) {
+ Ui::repaintHistoryItem(this);
+ }
+ } break;
}
}
-HistoryItem *regItem(HistoryItem *item, bool returnExisting) {
- if (!item) return 0;
-
- HistoryItem *existing = App::historyRegItem(item);
- if (existing) {
- delete item;
- return returnExisting ? existing : 0;
+HistoryItem::~HistoryItem() {
+ App::historyUnregItem(this);
+ if (id < 0 && App::uploader()) {
+ App::uploader()->cancel(fullId());
}
+}
- item->initDimensions();
+HistoryItem *regItem(HistoryItem *item) {
+ if (item) {
+ App::historyRegItem(item);
+ item->initDimensions();
+ }
return item;
}
-HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent) : HistoryMedia()
-, pixw(1), pixh(1)
-, data(App::feedPhoto(photo))
-, _caption(st::minPhotoSize)
-, openl(new PhotoLink(data)) {
+RadialAnimation::RadialAnimation(AnimationCreator creator)
+: _firstStart(0)
+, _lastStart(0)
+, _lastTime(0)
+, _opacity(0)
+, a_arcEnd(0, 0)
+, a_arcStart(0, FullArcLength)
+, _animation(creator) {
+
+}
+
+void RadialAnimation::start(float64 prg) {
+ _firstStart = _lastStart = _lastTime = getms();
+ int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength), iprgstrict = qRound(prg * AlmostFullArcLength);
+ a_arcEnd = anim::ivalue(iprgstrict, iprg);
+ _animation.start();
+}
+
+void RadialAnimation::update(float64 prg, bool finished, uint64 ms) {
+ int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength);
+ if (iprg != a_arcEnd.to()) {
+ a_arcEnd.start(iprg);
+ _lastStart = _lastTime;
+ }
+ _lastTime = ms;
+
+ float64 dt = float64(ms - _lastStart), fulldt = float64(ms - _firstStart);
+ _opacity = qMin(fulldt / st::radialDuration, 1.);
+ if (!finished) {
+ a_arcEnd.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear);
+ } else if (dt >= st::radialDuration) {
+ a_arcEnd.update(1, anim::linear);
+ stop();
+ } else {
+ float64 r = dt / st::radialDuration;
+ a_arcEnd.update(r, anim::linear);
+ _opacity *= 1 - r;
+ }
+ float64 fromstart = fulldt / st::radialPeriod;
+ a_arcStart.update(fromstart - qFloor(fromstart), anim::linear);
+}
+
+void RadialAnimation::stop() {
+ _firstStart = _lastStart = _lastTime = 0;
+ a_arcEnd = anim::ivalue(0, 0);
+ _animation.stop();
+}
+
+void RadialAnimation::step(uint64 ms) {
+ _animation.step(ms);
+}
+
+void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color) {
+ float64 o = p.opacity();
+ p.setOpacity(o * _opacity);
+
+ QPen pen(color->p), was(p.pen());
+ pen.setWidth(thickness);
+ p.setPen(pen);
+
+ int32 len = MinArcLength + a_arcEnd.current();
+ int32 from = QuarterArcLength - a_arcStart.current() - len;
+ if (rtl()) {
+ from = QuarterArcLength - (from - QuarterArcLength) - len;
+ if (from < 0) from += FullArcLength;
+ }
+
+ p.setRenderHint(QPainter::HighQualityAntialiasing);
+ p.drawArc(inner, from, len);
+ p.setRenderHint(QPainter::HighQualityAntialiasing, false);
+
+ p.setPen(was);
+ p.setOpacity(o);
+}
+
+namespace {
+ int32 videoMaxStatusWidth(VideoData *video) {
+ int32 result = st::normalFont->width(formatDownloadText(video->size, video->size));
+ result = qMax(result, st::normalFont->width(formatDurationAndSizeText(video->duration, video->size)));
+ return result;
+ }
+
+ int32 audioMaxStatusWidth(AudioData *audio) {
+ int32 result = st::normalFont->width(formatDownloadText(audio->size, audio->size));
+ result = qMax(result, st::normalFont->width(formatPlayedText(audio->duration, audio->duration)));
+ result = qMax(result, st::normalFont->width(formatDurationAndSizeText(audio->duration, audio->size)));
+ return result;
+ }
+
+ int32 documentMaxStatusWidth(DocumentData *document) {
+ int32 result = st::normalFont->width(formatDownloadText(document->size, document->size));
+ if (SongData *song = document->song()) {
+ result = qMax(result, st::normalFont->width(formatPlayedText(song->duration, song->duration)));
+ result = qMax(result, st::normalFont->width(formatDurationAndSizeText(song->duration, document->size)));
+ } else {
+ result = qMax(result, st::normalFont->width(formatSizeText(document->size)));
+ }
+ return result;
+ }
+
+ int32 gifMaxStatusWidth(DocumentData *document) {
+ int32 result = st::normalFont->width(formatDownloadText(document->size, document->size));
+ result = qMax(result, st::normalFont->width(formatGifAndSizeText(document->size)));
+ return result;
+ }
+}
+
+HistoryFileMedia::HistoryFileMedia() : HistoryMedia()
+, _animation(0) {
+}
+
+void HistoryFileMedia::linkOver(HistoryItem *parent, const TextLinkPtr &lnk) {
+ if ((lnk == _savel || lnk == _cancell) && !dataLoaded()) {
+ ensureAnimation(parent);
+ _animation->a_thumbOver.start(1);
+ _animation->_a_thumbOver.start();
+ }
+}
+
+void HistoryFileMedia::linkOut(HistoryItem *parent, const TextLinkPtr &lnk) {
+ if (_animation && (lnk == _savel || lnk == _cancell)) {
+ _animation->a_thumbOver.start(0);
+ _animation->_a_thumbOver.start();
+ }
+}
+
+void HistoryFileMedia::setLinks(ITextLink *openl, ITextLink *savel, ITextLink *cancell) {
+ _openl.reset(openl);
+ _savel.reset(savel);
+ _cancell.reset(cancell);
+}
+
+void HistoryFileMedia::setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const {
+ _statusSize = newSize;
+ if (_statusSize == FileStatusSizeReady) {
+ _statusText = (duration >= 0) ? formatDurationAndSizeText(duration, fullSize) : (duration < -1 ? formatGifAndSizeText(fullSize) : formatSizeText(fullSize));
+ } else if (_statusSize == FileStatusSizeLoaded) {
+ _statusText = (duration >= 0) ? formatDurationText(duration) : (duration < -1 ? qsl("GIF") : formatSizeText(fullSize));
+ } else if (_statusSize == FileStatusSizeFailed) {
+ _statusText = lang(lng_attach_failed);
+ } else if (_statusSize >= 0) {
+ _statusText = formatDownloadText(_statusSize, fullSize);
+ } else {
+ _statusText = formatPlayedText(-_statusSize - 1, realDuration);
+ }
+}
+
+void HistoryFileMedia::step_thumbOver(const HistoryItem *parent, float64 ms, bool timer) {
+ float64 dt = ms / st::msgFileOverDuration;
+ if (dt >= 1) {
+ _animation->a_thumbOver.finish();
+ _animation->_a_thumbOver.stop();
+ checkAnimationFinished();
+ } else if (!timer) {
+ _animation->a_thumbOver.update(dt, anim::linear);
+ }
+ if (timer) {
+ Ui::repaintHistoryItem(parent);
+ }
+}
+
+void HistoryFileMedia::step_radial(const HistoryItem *parent, uint64 ms, bool timer) {
+ if (timer) {
+ Ui::repaintHistoryItem(parent);
+ } else {
+ _animation->radial.update(dataProgress(), dataFinished(), ms);
+ if (!_animation->radial.animating()) {
+ checkAnimationFinished();
+ }
+ }
+}
+
+void HistoryFileMedia::ensureAnimation(const HistoryItem *parent) const {
+ if (!_animation) {
+ _animation = new AnimationData(
+ animation(parent, const_cast(this), &HistoryFileMedia::step_thumbOver),
+ animation(parent, const_cast(this), &HistoryFileMedia::step_radial));
+ }
+}
+
+void HistoryFileMedia::checkAnimationFinished() {
+ if (_animation && !_animation->_a_thumbOver.animating() && !_animation->radial.animating()) {
+ if (dataLoaded()) {
+ delete _animation;
+ _animation = 0;
+ }
+ }
+}
+
+HistoryFileMedia::~HistoryFileMedia() {
+ deleteAndMark(_animation);
+}
+
+HistoryPhoto::HistoryPhoto(PhotoData *photo, const QString &caption, const HistoryItem *parent) : HistoryFileMedia()
+, _data(photo)
+, _pixw(1)
+, _pixh(1)
+, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
+ setLinks(new PhotoLink(_data), new PhotoSaveLink(_data), new PhotoCancelLink(_data));
+
if (!caption.isEmpty()) {
_caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent));
}
init();
}
-HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryMedia(width)
-, pixw(1), pixh(1)
-, data(App::feedPhoto(photo))
-, openl(new PhotoLink(data, chat)) {
+HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryFileMedia()
+, _data(App::feedPhoto(photo))
+, _pixw(1)
+, _pixh(1) {
+ setLinks(new PhotoLink(_data, chat), new PhotoSaveLink(_data, chat), new PhotoCancelLink(_data));
+
+ _width = width;
+ init();
+}
+
+HistoryPhoto::HistoryPhoto(const HistoryPhoto &other) : HistoryFileMedia()
+, _data(other._data)
+, _pixw(other._pixw)
+, _pixh(other._pixh)
+, _caption(other._caption) {
+ setLinks(new PhotoLink(_data), new PhotoSaveLink(_data), new PhotoCancelLink(_data));
+
init();
}
void HistoryPhoto::init() {
- data->thumb->load();
+ _data->thumb->load();
}
void HistoryPhoto::initDimensions(const HistoryItem *parent) {
- if (_caption.hasSkipBlock()) _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight());
+ if (_caption.hasSkipBlock()) {
+ _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight());
+ }
- int32 tw = convertScale(data->full->width()), th = convertScale(data->full->height());
+ int32 tw = convertScale(_data->full->width()), th = convertScale(_data->full->height());
if (!tw || !th) {
tw = th = 1;
}
- int32 thumbw = tw;
- int32 thumbh = th;
- if (!w) {
- w = thumbw;
- } else {
- thumbh = w; // square chat photo updates
- }
- _maxw = qMax(w, int32(st::minPhotoSize));
- _minh = qMax(thumbh, int32(st::minPhotoSize));
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = toHistoryForwarded(parent);
- if (reply || (fwd && fwd->fromForwarded()->isChannel()) || !_caption.isEmpty()) {
- _maxw += st::mediaPadding.left() + st::mediaPadding.right();
- if (reply) {
- _minh += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else {
- if (!parent->displayFromName() || !fwd) {
- _minh += st::msgPadding.top();
- }
- if (fwd) {
- _minh += st::msgServiceNameFont->height + st::msgPadding.top();
- }
- }
- if (parent->displayFromName()) {
- _minh += st::msgPadding.top() + st::msgNameFont->height;
- }
- if (!_caption.isEmpty()) {
- _minh += st::webPagePhotoSkip + _caption.minHeight();
- }
- _minh += st::mediaPadding.bottom();
- }
-}
-
-int32 HistoryPhoto::resize(int32 width, const HistoryItem *parent) {
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
-
- pixw = qMin(width, _maxw);
- if (reply || (fwd && fwd->fromForwarded()->isChannel()) || !_caption.isEmpty()) {
- pixw -= st::mediaPadding.left() + st::mediaPadding.right();
- }
-
- int32 tw = convertScale(data->full->width()), th = convertScale(data->full->height());
if (tw > st::maxMediaSize) {
th = (st::maxMediaSize * th) / tw;
tw = st::maxMediaSize;
@@ -3177,43 +3222,274 @@ int32 HistoryPhoto::resize(int32 width, const HistoryItem *parent) {
tw = (st::maxMediaSize * tw) / th;
th = st::maxMediaSize;
}
- pixh = th;
- if (tw > pixw) {
- pixh = (pixw * pixh / tw);
+
+ if (parent->toHistoryMessage()) {
+ bool bubble = parent->hasBubble();
+
+ int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ int32 maxActualWidth = qMax(tw, minWidth);
+ _maxw = qMax(maxActualWidth, th);
+ _minh = qMax(th, int32(st::minPhotoSize));
+ if (bubble) {
+ maxActualWidth += st::mediaPadding.left() + st::mediaPadding.right();
+ _maxw += st::mediaPadding.left() + st::mediaPadding.right();
+ _minh += st::mediaPadding.top() + st::mediaPadding.bottom();
+ if (!_caption.isEmpty()) {
+ _minh += st::mediaCaptionSkip + _caption.countHeight(maxActualWidth - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
+ }
+ }
} else {
- pixw = tw;
+ _maxw = _minh = _width;
}
- if (pixh > width) {
- pixw = (pixw * width) / pixh;
- pixh = width;
+}
+
+int32 HistoryPhoto::resize(int32 width, const HistoryItem *parent) {
+ bool bubble = parent->hasBubble();
+
+ int32 tw = convertScale(_data->full->width()), th = convertScale(_data->full->height());
+ if (tw > st::maxMediaSize) {
+ th = (st::maxMediaSize * th) / tw;
+ tw = st::maxMediaSize;
}
- if (pixw < 1) pixw = 1;
- if (pixh < 1) pixh = 1;
- w = qMax(pixw, int16(st::minPhotoSize));
- _height = qMax(pixh, int16(st::minPhotoSize));
- if (reply || (fwd && fwd->fromForwarded()->isChannel()) || !_caption.isEmpty()) {
- if (reply) {
- _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else {
- if (!parent->displayFromName() || !fwd) {
- _height += st::msgPadding.top();
- }
- if (fwd) {
- _height += st::msgServiceNameFont->height + st::msgPadding.top();
- }
- }
- if (parent->displayFromName()) {
- _height += st::msgPadding.top() + st::msgNameFont->height;
- }
+ if (th > st::maxMediaSize) {
+ tw = (st::maxMediaSize * tw) / th;
+ th = st::maxMediaSize;
+ }
+
+ _pixw = qMin(width, _maxw);
+ if (bubble) {
+ _pixw -= st::mediaPadding.left() + st::mediaPadding.right();
+ }
+ _pixh = th;
+ if (tw > _pixw) {
+ _pixh = (_pixw * _pixh / tw);
+ } else {
+ _pixw = tw;
+ }
+ if (_pixh > width) {
+ _pixw = (_pixw * width) / _pixh;
+ _pixh = width;
+ }
+ if (_pixw < 1) _pixw = 1;
+ if (_pixh < 1) _pixh = 1;
+
+ int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ _width = qMax(_pixw, int16(minWidth));
+ _height = qMax(_pixh, int16(st::minPhotoSize));
+ if (bubble) {
+ _width += st::mediaPadding.left() + st::mediaPadding.right();
+ _height += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
- _height += st::webPagePhotoSkip + _caption.countHeight(w);
+ int32 captionw = _width - st::msgPadding.left() - st::msgPadding.right();
+ _height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
}
- _height += st::mediaPadding.bottom();
- w += st::mediaPadding.left() + st::mediaPadding.right();
}
return _height;
}
+void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+
+ _data->automaticLoad(parent);
+ bool loaded = _data->loaded(), displayLoading = _data->displayLoading();
+
+ bool notChild = (parent->getMedia() == this);
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
+ bool bubble = parent->hasBubble();
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
+
+ int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
+
+ if (displayLoading) {
+ ensureAnimation(parent);
+ if (!_animation->radial.animating()) {
+ _animation->radial.start(_data->progress());
+ }
+ }
+ bool radial = isRadialAnimation(ms);
+
+ if (bubble) {
+ skipx = st::mediaPadding.left();
+ skipy = st::mediaPadding.top();
+
+ width -= st::mediaPadding.left() + st::mediaPadding.right();
+ height -= skipy + st::mediaPadding.bottom();
+ if (!_caption.isEmpty()) {
+ height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
+ }
+ } else {
+ App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
+ }
+
+ QPixmap pix;
+ if (loaded) {
+ pix = _data->full->pixSingle(_pixw, _pixh, width, height);
+ } else {
+ pix = _data->thumb->pixBlurredSingle(_pixw, _pixh, width, height);
+ }
+ QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
+ p.drawPixmap(rthumb.topLeft(), pix);
+ if (selected) {
+ App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
+ }
+
+ if (notChild && (radial || (!loaded && !_data->loading()))) {
+ float64 radialOpacity = (radial && loaded && !_data->uploading()) ? _animation->radial.opacity() : 1;
+ QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
+ p.setPen(Qt::NoPen);
+ if (selected) {
+ p.setBrush(st::msgDateImgBgSelected);
+ } else if (isThumbAnimation(ms)) {
+ float64 over = _animation->a_thumbOver.current();
+ p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over));
+ p.setBrush(st::black);
+ } else {
+ bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel);
+ p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
+ }
+
+ p.setOpacity(radialOpacity * p.opacity());
+
+ p.setRenderHint(QPainter::HighQualityAntialiasing);
+ p.drawEllipse(inner);
+ p.setRenderHint(QPainter::HighQualityAntialiasing, false);
+
+ p.setOpacity(radial ? _animation->radial.opacity() : 1);
+
+ p.setOpacity(radialOpacity);
+ style::sprite icon;
+ if (radial || _data->loading()) {
+ DelayedStorageImage *delayed = _data->full->toDelayedStorageImage();
+ if (!delayed || !delayed->location().isNull()) {
+ icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel);
+ }
+ } else {
+ icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload);
+ }
+ if (!icon.isEmpty()) {
+ p.drawSpriteCenter(inner, icon);
+ }
+ if (radial) {
+ p.setOpacity(1);
+ QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
+ _animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg);
+ }
+ }
+
+ // date
+ if (_caption.isEmpty()) {
+ if (notChild) {
+ int32 fullRight = skipx + width, fullBottom = skipy + height;
+ parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
+ }
+ } else {
+ p.setPen(st::black);
+ _caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw);
+ }
+}
+
+void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
+ bool bubble = parent->hasBubble();
+
+ if (bubble) {
+ skipx = st::mediaPadding.left();
+ skipy = st::mediaPadding.top();
+ if (!_caption.isEmpty()) {
+ int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
+ height -= _caption.countHeight(captionw) + st::msgPadding.bottom();
+ if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
+ bool inText = false;
+ _caption.getState(lnk, inText, x - st::msgPadding.left(), y - height, captionw);
+ state = inText ? HistoryInTextCursorState : HistoryDefaultCursorState;
+ return;
+ }
+ height -= st::mediaCaptionSkip;
+ }
+ width -= st::mediaPadding.left() + st::mediaPadding.right();
+ height -= skipy + st::mediaPadding.bottom();
+ }
+ if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) {
+ if (_data->uploading()) {
+ lnk = _cancell;
+ } else if (_data->loaded()) {
+ lnk = _openl;
+ } else if (_data->loading()) {
+ DelayedStorageImage *delayed = _data->full->toDelayedStorageImage();
+ if (!delayed || !delayed->location().isNull()) {
+ lnk = _cancell;
+ }
+ } else {
+ lnk = _savel;
+ }
+ if (_caption.isEmpty() && parent->getMedia() == this) {
+ int32 fullRight = skipx + width, fullBottom = skipy + height;
+ bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage);
+ if (inDate) {
+ state = HistoryInDateCursorState;
+ }
+ }
+ return;
+ }
+}
+
+void HistoryPhoto::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
+ if (media.type() == mtpc_messageMediaPhoto) {
+ const MTPPhoto &photo(media.c_messageMediaPhoto().vphoto);
+ App::feedPhoto(photo, _data);
+
+ if (photo.type() == mtpc_photo) {
+ const QVector &sizes(photo.c_photo().vsizes.c_vector().v);
+ int32 max = 0;
+ const MTPDfileLocation *maxLocation = 0;
+ for (int32 i = 0, l = sizes.size(); i < l; ++i) {
+ char size = 0;
+ const MTPFileLocation *loc = 0;
+ switch (sizes.at(i).type()) {
+ case mtpc_photoSize: {
+ const string &s(sizes.at(i).c_photoSize().vtype.c_string().v);
+ loc = &sizes.at(i).c_photoSize().vlocation;
+ if (s.size()) size = s[0];
+ } break;
+
+ case mtpc_photoCachedSize: {
+ const string &s(sizes.at(i).c_photoCachedSize().vtype.c_string().v);
+ loc = &sizes.at(i).c_photoCachedSize().vlocation;
+ if (s.size()) size = s[0];
+ } break;
+ }
+ if (!loc || loc->type() != mtpc_fileLocation) continue;
+ if (size == 's') {
+ Local::writeImage(storageKey(loc->c_fileLocation()), _data->thumb);
+ } else if (size == 'm') {
+ Local::writeImage(storageKey(loc->c_fileLocation()), _data->medium);
+ } else if (size == 'x' && max < 1) {
+ max = 1;
+ maxLocation = &loc->c_fileLocation();
+ } else if (size == 'y' && max < 2) {
+ max = 2;
+ maxLocation = &loc->c_fileLocation();
+ //} else if (size == 'w' && max < 3) {
+ // max = 3;
+ // maxLocation = &loc->c_fileLocation();
+ }
+ }
+ if (maxLocation) {
+ Local::writeImage(storageKey(*maxLocation), _data->full);
+ }
+ }
+ }
+}
+
+void HistoryPhoto::regItem(HistoryItem *item) {
+ App::regPhotoItem(_data, item);
+}
+
+void HistoryPhoto::unregItem(HistoryItem *item) {
+ App::unregPhotoItem(_data, item);
+}
+
const QString HistoryPhoto::inDialogsText() const {
return _caption.isEmpty() ? lang(lng_in_dlg_photo) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone);
}
@@ -3222,70 +3498,228 @@ const QString HistoryPhoto::inHistoryText() const {
return qsl("[ ") + lang(lng_in_dlg_photo) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]");
}
-bool HistoryPhoto::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- return (x >= 0 && y >= 0 && x < width && y < _height);
+ImagePtr HistoryPhoto::replyPreview() {
+ return _data->makeReplyPreview();
}
-void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- if (width < 1) return;
+HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent) : HistoryFileMedia()
+, _data(App::feedVideo(video))
+, _thumbw(1)
+, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
+ if (!caption.isEmpty()) {
+ _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent));
+ }
- int skipx = 0, skipy = 0, height = _height;
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
- int replyFrom = 0, fwdFrom = 0;
- if (reply || (fwd && fwd->fromForwarded()->isChannel()) || !_caption.isEmpty()) {
- skipx = st::mediaPadding.left();
- if (reply) {
- skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (fwd) {
- skipy = st::msgServiceNameFont->height + st::msgPadding.top();
- }
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- if (x >= st::mediaPadding.left() && y >= st::msgPadding.top() && x < width - st::mediaPadding.left() - st::mediaPadding.right() && x < st::mediaPadding.left() + parent->from()->nameText.maxWidth() && y < replyFrom) {
- lnk = parent->from()->lnk;
- return;
- }
- if (!reply && !fwd) skipy += st::msgPadding.top();
- } else if (!reply) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
- if (reply) {
- if (x >= 0 && y >= replyFrom + st::msgReplyPadding.top() && x < width && y < skipy - st::msgReplyPadding.bottom()) {
- lnk = reply->replyToLink();
- return;
- }
- } else if (fwd) {
- if (y >= fwdFrom && y < fwdFrom + st::msgServiceNameFont->height) {
- return fwd->getForwardedState(lnk, state, x - st::mediaPadding.left(), width - st::mediaPadding.left() - st::mediaPadding.right());
- }
- }
- height -= skipy + st::mediaPadding.bottom();
- width -= st::mediaPadding.left() + st::mediaPadding.right();
+ setLinks(new VideoOpenLink(_data), new VideoSaveLink(_data), new VideoCancelLink(_data));
+
+ setStatusSize(FileStatusSizeReady);
+
+ _data->thumb->load();
+}
+
+HistoryVideo::HistoryVideo(const HistoryVideo &other) : HistoryFileMedia()
+, _data(other._data)
+, _thumbw(other._thumbw)
+, _caption(other._caption) {
+ setLinks(new VideoOpenLink(_data), new VideoSaveLink(_data), new VideoCancelLink(_data));
+
+ setStatusSize(other._statusSize);
+}
+
+void HistoryVideo::initDimensions(const HistoryItem *parent) {
+ bool bubble = parent->hasBubble();
+
+ if (_caption.hasSkipBlock()) {
+ _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight());
+ }
+
+ int32 tw = convertScale(_data->thumb->width()), th = convertScale(_data->thumb->height());
+ if (!tw || !th) {
+ tw = th = 1;
+ }
+ if (tw * st::msgVideoSize.height() > th * st::msgVideoSize.width()) {
+ th = qRound((st::msgVideoSize.width() / float64(tw)) * th);
+ tw = st::msgVideoSize.width();
+ } else {
+ tw = qRound((st::msgVideoSize.height() / float64(th)) * tw);
+ th = st::msgVideoSize.height();
+ }
+
+ _thumbw = qMax(tw, 1);
+ int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ minWidth = qMax(minWidth, videoMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ _maxw = qMax(_thumbw, int16(minWidth));
+ _minh = qMax(th, int32(st::minPhotoSize));
+ if (bubble) {
+ _maxw += st::mediaPadding.left() + st::mediaPadding.right();
+ _minh += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
- int32 fullRight = skipx + width + st::mediaPadding.right(), fullBottom = _height;
- bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayDefault);
- if (inDate) {
- state = HistoryInDateCursorState;
- }
-
- height -= _caption.countHeight(width) + st::webPagePhotoSkip;
- if (x >= skipx && y >= skipy + height + st::webPagePhotoSkip && x < skipx + width && y < _height) {
- bool inText = false;
- _caption.getState(lnk, inText, x - skipx, y - skipy - height - st::webPagePhotoSkip, width);
- state = inDate ? HistoryInDateCursorState : (inText ? HistoryInTextCursorState : HistoryDefaultCursorState);
- }
+ _minh += st::mediaCaptionSkip + _caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
}
}
+}
+
+int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) {
+ bool bubble = parent->hasBubble();
+
+ int32 tw = convertScale(_data->thumb->width()), th = convertScale(_data->thumb->height());
+ if (!tw || !th) {
+ tw = th = 1;
+ }
+ if (tw * st::msgVideoSize.height() > th * st::msgVideoSize.width()) {
+ th = qRound((st::msgVideoSize.width() / float64(tw)) * th);
+ tw = st::msgVideoSize.width();
+ } else {
+ tw = qRound((st::msgVideoSize.height() / float64(th)) * tw);
+ th = st::msgVideoSize.height();
+ }
+
+ if (bubble) {
+ width -= st::mediaPadding.left() + st::mediaPadding.right();
+ }
+ if (width < tw) {
+ th = qRound((width / float64(tw)) * th);
+ tw = width;
+ }
+
+ int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ minWidth = qMax(minWidth, videoMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ _width = qMax(_thumbw, int16(minWidth));
+ _height = qMax(th, int32(st::minPhotoSize));
+ if (bubble) {
+ _width += st::mediaPadding.left() + st::mediaPadding.right();
+ _height += st::mediaPadding.top() + st::mediaPadding.bottom();
+ if (!_caption.isEmpty()) {
+ int32 captionw = _width - st::msgPadding.left() - st::msgPadding.right();
+ _height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
+ }
+ }
+ return _height;
+}
+
+void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+
+ _data->automaticLoad(parent);
+ bool loaded = _data->loaded(), displayLoading = _data->displayLoading();
+
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
+ bool bubble = parent->hasBubble();
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
+
+ int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
+
+ if (displayLoading) {
+ ensureAnimation(parent);
+ if (!_animation->radial.animating()) {
+ _animation->radial.start(_data->progress());
+ }
+ }
+ updateStatusText(parent);
+ bool radial = isRadialAnimation(ms);
+
+ if (bubble) {
+ skipx = st::mediaPadding.left();
+ skipy = st::mediaPadding.top();
+
+ width -= st::mediaPadding.left() + st::mediaPadding.right();
+ height -= skipy + st::mediaPadding.bottom();
+ if (!_caption.isEmpty()) {
+ height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
+ }
+ } else {
+ App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
+ }
+
+ QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
+ p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_thumbw, 0, width, height));
+ if (selected) {
+ App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
+ }
+
+ QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
+ p.setPen(Qt::NoPen);
+ if (selected) {
+ p.setBrush(st::msgDateImgBgSelected);
+ } else if (isThumbAnimation(ms)) {
+ float64 over = _animation->a_thumbOver.current();
+ p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over));
+ p.setBrush(st::black);
+ } else {
+ bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel);
+ p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
+ }
+
+ p.setRenderHint(QPainter::HighQualityAntialiasing);
+ p.drawEllipse(inner);
+ p.setRenderHint(QPainter::HighQualityAntialiasing, false);
+
+ if (!selected && _animation) {
+ p.setOpacity(1);
+ }
+
+ style::sprite icon;
+ if (loaded) {
+ icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay);
+ } else if (radial || _data->loading()) {
+ icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel);
+ } else {
+ icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload);
+ }
+ p.drawSpriteCenter(inner, icon);
+ if (radial) {
+ QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
+ _animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg);
+ }
+
+ int32 statusX = skipx + st::msgDateImgDelta + st::msgDateImgPadding.x(), statusY = skipy + st::msgDateImgDelta + st::msgDateImgPadding.y();
+ int32 statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
+ int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
+ App::roundRect(p, rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, _width), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
+ p.setFont(st::normalFont);
+ p.setPen(st::white);
+ p.drawTextLeft(statusX, statusY, _width, _statusText, statusW - 2 * st::msgDateImgPadding.x());
+
+ // date
+ if (_caption.isEmpty()) {
+ if (parent->getMedia() == this) {
+ int32 fullRight = skipx + width, fullBottom = skipy + height;
+ parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
+ }
+ } else {
+ p.setPen(st::black);
+ _caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw);
+ }
+}
+
+void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+
+ bool loaded = _data->loaded();
+
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
+ bool bubble = parent->hasBubble();
+
+ if (bubble) {
+ skipx = st::mediaPadding.left();
+ skipy = st::mediaPadding.top();
+ if (!_caption.isEmpty()) {
+ int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
+ height -= _caption.countHeight(captionw) + st::msgPadding.bottom();
+ if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
+ bool inText = false;
+ _caption.getState(lnk, inText, x - st::msgPadding.left(), y - height, captionw);
+ state = inText ? HistoryInTextCursorState : HistoryDefaultCursorState;
+ }
+ height -= st::mediaCaptionSkip;
+ }
+ width -= st::mediaPadding.left() + st::mediaPadding.right();
+ height -= skipy + st::mediaPadding.bottom();
+ }
if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) {
- lnk = openl;
- if (_caption.isEmpty()) {
- int32 fullRight = skipx + width, fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0);
+ lnk = loaded ? _openl : (_data->loading() ? _cancell : _savel);
+ if (_caption.isEmpty() && parent->getMedia() == this) {
+ int32 fullRight = skipx + width, fullBottom = skipy + height;
bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage);
if (inDate) {
state = HistoryInDateCursorState;
@@ -3295,253 +3729,8 @@ void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x
}
}
-HistoryMedia *HistoryPhoto::clone() const {
- return new HistoryPhoto(*this);
-}
-
-void HistoryPhoto::updateFrom(const MTPMessageMedia &media) {
- if (media.type() == mtpc_messageMediaPhoto) {
- const MTPPhoto &photo(media.c_messageMediaPhoto().vphoto);
- if (photo.type() == mtpc_photo) {
- const QVector &sizes(photo.c_photo().vsizes.c_vector().v);
- for (QVector::const_iterator i = sizes.cbegin(), e = sizes.cend(); i != e; ++i) {
- char size = 0;
- const MTPFileLocation *loc = 0;
- switch (i->type()) {
- case mtpc_photoSize: {
- const string &s(i->c_photoSize().vtype.c_string().v);
- loc = &i->c_photoSize().vlocation;
- if (s.size()) size = s[0];
- } break;
-
- case mtpc_photoCachedSize: {
- const string &s(i->c_photoCachedSize().vtype.c_string().v);
- loc = &i->c_photoCachedSize().vlocation;
- if (s.size()) size = s[0];
- } break;
- }
- if (!loc || loc->type() != mtpc_fileLocation) continue;
- if (size == 's') {
- Local::writeImage(storageKey(loc->c_fileLocation()), data->thumb);
- } else if (size == 'm') {
- Local::writeImage(storageKey(loc->c_fileLocation()), data->medium);
- } else if (size == 'x') {
- Local::writeImage(storageKey(loc->c_fileLocation()), data->full);
- }
- }
- }
- }
-}
-
-void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const {
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
- bool fromChannel = parent->fromChannel(), out = parent->out(), outbg = out && !fromChannel;
-
- if (width < 0) width = w;
- int skipx = 0, skipy = 0, height = _height;
- if (reply || (fwd && fwd->fromForwarded()->isChannel()) || !_caption.isEmpty()) {
- skipx = st::mediaPadding.left();
-
- style::color bg(selected ? (outbg ? st::msgOutSelectBg : st::msgInSelectBg) : (outbg ? st::msgOutBg : st::msgInBg));
- style::color sh(selected ? (outbg ? st::msgOutSelectShadow : st::msgInSelectShadow) : (outbg ? st::msgOutShadow : st::msgInShadow));
- RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners));
- App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
-
- int replyFrom = 0, fwdFrom = 0;
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- p.setFont(st::msgNameFont->f);
- if (fromChannel) {
- p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor);
- } else {
- p.setPen(parent->from()->color);
- }
- parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right());
- if (!fwd && !reply) skipy += st::msgPadding.top();
- } else if (!reply) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
- if (reply) {
- skipy += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- reply->drawReplyTo(p, st::msgReplyPadding.left(), replyFrom, width - st::msgReplyPadding.left() - st::msgReplyPadding.right(), selected);
- } else if (fwd) {
- skipy += st::msgServiceNameFont->height + st::msgPadding.top();
- fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
- }
- width -= st::mediaPadding.left() + st::mediaPadding.right();
- height -= skipy + st::mediaPadding.bottom();
- if (!_caption.isEmpty()) {
- height -= st::webPagePhotoSkip + _caption.countHeight(width);
- }
- } else {
- App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInSelectShadow : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
- }
- data->full->load(false, false);
- bool full = data->full->loaded();
- QPixmap pix;
- if (full) {
- pix = data->full->pixSingle(pixw, pixh, width, height);
- } else {
- pix = data->thumb->pixBlurredSingle(pixw, pixh, width, height);
- }
- p.drawPixmap(skipx, skipy, pix);
- if (!full) {
- uint64 dt = itemAnimations().animate(parent, getms());
- int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta);
-
- int32 x = (width - st::photoLoader.width()) / 2, y = (height - st::photoLoader.height()) / 2;
- p.fillRect(skipx + x, skipy + y, st::photoLoader.width(), st::photoLoader.height(), st::photoLoaderBg->b);
- x += (st::photoLoader.width() - cnt * st::photoLoaderPoint.width() - (cnt - 1) * st::photoLoaderSkip) / 2;
- y += (st::photoLoader.height() - st::photoLoaderPoint.height()) / 2;
- QColor c(st::white->c);
- QBrush b(c);
- for (int32 i = 0; i < cnt; ++i) {
- t -= delta;
- while (t < 0) t += period;
-
- float64 alpha = (t >= st::photoLoaderDuration1 + st::photoLoaderDuration2) ? 0 : ((t > st::photoLoaderDuration1 ? ((st::photoLoaderDuration1 + st::photoLoaderDuration2 - t) / st::photoLoaderDuration2) : (t / st::photoLoaderDuration1)));
- c.setAlphaF(st::photoLoaderAlphaMin + alpha * (1 - st::photoLoaderAlphaMin));
- b.setColor(c);
- p.fillRect(skipx + x + i * (st::photoLoaderPoint.width() + st::photoLoaderSkip), skipy + y, st::photoLoaderPoint.width(), st::photoLoaderPoint.height(), b);
- }
- }
-
- if (selected) {
- App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
- }
-
- // date
- QString time(parent->timeText());
- if (_caption.isEmpty()) {
- int32 fullRight = skipx + width, fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0);
- parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayOverImage);
- } else {
- p.setPen(st::black->p);
- _caption.draw(p, skipx, skipy + height + st::webPagePhotoSkip, width);
-
- int32 fullRight = skipx + width + st::mediaPadding.right(), fullBottom = _height;
- parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayDefault);
- }
-}
-
-ImagePtr HistoryPhoto::replyPreview() {
- return data->makeReplyPreview();
-}
-
-QString formatSizeText(qint64 size) {
- if (size >= 1024 * 1024) { // more than 1 mb
- qint64 sizeTenthMb = (size * 10 / (1024 * 1024));
- return QString::number(sizeTenthMb / 10) + '.' + QString::number(sizeTenthMb % 10) + qsl(" MB");
- }
- if (size >= 1024) {
- qint64 sizeTenthKb = (size * 10 / 1024);
- return QString::number(sizeTenthKb / 10) + '.' + QString::number(sizeTenthKb % 10) + qsl(" KB");
- }
- return QString::number(size) + qsl(" B");
-}
-
-QString formatDownloadText(qint64 ready, qint64 total) {
- QString readyStr, totalStr, mb;
- if (total >= 1024 * 1024) { // more than 1 mb
- qint64 readyTenthMb = (ready * 10 / (1024 * 1024)), totalTenthMb = (total * 10 / (1024 * 1024));
- readyStr = QString::number(readyTenthMb / 10) + '.' + QString::number(readyTenthMb % 10);
- totalStr = QString::number(totalTenthMb / 10) + '.' + QString::number(totalTenthMb % 10);
- mb = qsl("MB");
- } else if (total >= 1024) {
- qint64 readyKb = (ready / 1024), totalKb = (total / 1024);
- readyStr = QString::number(readyKb);
- totalStr = QString::number(totalKb);
- mb = qsl("KB");
- } else {
- readyStr = QString::number(ready);
- totalStr = QString::number(total);
- mb = qsl("B");
- }
- return lng_save_downloaded(lt_ready, readyStr, lt_total, totalStr, lt_mb, mb);
-}
-
-QString formatDurationText(qint64 duration) {
- qint64 hours = (duration / 3600), minutes = (duration % 3600) / 60, seconds = duration % 60;
- return (hours ? QString::number(hours) + ':' : QString()) + (minutes >= 10 ? QString() : QString('0')) + QString::number(minutes) + ':' + (seconds >= 10 ? QString() : QString('0')) + QString::number(seconds);
-}
-
-QString formatDurationAndSizeText(qint64 duration, qint64 size) {
- return lng_duration_and_size(lt_duration, formatDurationText(duration), lt_size, formatSizeText(size));
-}
-
-int32 _downloadWidth = 0, _openWithWidth = 0, _cancelWidth = 0, _buttonWidth = 0;
-
-HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent) : HistoryMedia()
-, data(App::feedVideo(video))
-, _openl(new VideoOpenLink(data))
-, _savel(new VideoSaveLink(data))
-, _cancell(new VideoCancelLink(data))
-, _caption(st::minPhotoSize)
-, _dldDone(0)
-, _uplDone(0)
-{
- if (!caption.isEmpty()) {
- _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent));
- }
-
- _size = formatDurationAndSizeText(data->duration, data->size);
-
- if (!_openWithWidth) {
- _downloadWidth = st::mediaSaveButton.font->width(lang(lng_media_download));
- _openWithWidth = st::mediaSaveButton.font->width(lang(lng_media_open_with));
- _cancelWidth = st::mediaSaveButton.font->width(lang(lng_media_cancel));
- _buttonWidth = (st::mediaSaveButton.width > 0) ? st::mediaSaveButton.width : ((_downloadWidth > _openWithWidth ? (_downloadWidth > _cancelWidth ? _downloadWidth : _cancelWidth) : _openWithWidth) - st::mediaSaveButton.width);
- }
-
- data->thumb->load();
-
- int32 tw = data->thumb->width(), th = data->thumb->height();
- if (data->thumb->isNull() || !tw || !th) {
- _thumbw = 0;
- } else if (tw > th) {
- _thumbw = (tw * st::mediaThumbSize) / th;
- } else {
- _thumbw = st::mediaThumbSize;
- }
-}
-
-void HistoryVideo::initDimensions(const HistoryItem *parent) {
- if (_caption.hasSkipBlock()) _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight());
-
- _maxw = st::mediaMaxWidth;
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- if (!parent->out() || parent->fromChannel()) { // add Download / Save As button
- _maxw += st::mediaSaveDelta + _buttonWidth;
- }
- _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
- if (parent->displayFromName()) {
- _minh += st::msgPadding.top() + st::msgNameFont->height;
- }
- if (const HistoryReply *reply = toHistoryReply(parent)) {
- _minh += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (const HistoryForwarded *fwd = toHistoryForwarded(parent)) {
- if (!parent->displayFromName()) {
- _minh += st::msgPadding.top();
- }
- _minh += st::msgServiceNameFont->height;
- }
- if (_caption.isEmpty()) {
- _height = _minh;
- } else {
- _minh += st::webPagePhotoSkip + _caption.minHeight();
- }
-}
-
-void HistoryVideo::regItem(HistoryItem *item) {
- App::regVideoItem(data, item);
-}
-
-void HistoryVideo::unregItem(HistoryItem *item) {
- App::unregVideoItem(data, item);
+void HistoryVideo::setStatusSize(int32 newSize) const {
+ HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration, 0);
}
const QString HistoryVideo::inDialogsText() const {
@@ -3552,519 +3741,177 @@ const QString HistoryVideo::inHistoryText() const {
return qsl("[ ") + lang(lng_in_dlg_video) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]");
}
-bool HistoryVideo::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- int32 height = _height;
- if (width < 0) {
- width = w;
- } else if (!_caption.isEmpty()) {
- height = countHeight(parent, width);
- }
- if (width >= _maxw) {
- width = _maxw;
- }
- return (x >= 0 && y >= 0 && x < width && y < height);
-}
-
-int32 HistoryVideo::countHeight(const HistoryItem *parent, int32 width) const {
- if (_caption.isEmpty()) return _height;
-
- if (width < 0) width = w;
- if (width >= _maxw) {
- width = _maxw;
- }
-
- int32 h = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
- if (parent->displayFromName()) {
- h += st::msgPadding.top() + st::msgNameFont->height;
- }
- if (const HistoryReply *reply = toHistoryReply(parent)) {
- h += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (const HistoryForwarded *fwd = toHistoryForwarded(parent)) {
- if (!parent->displayFromName()) {
- h += st::msgPadding.top();
- }
- h += st::msgServiceNameFont->height;
- }
- if (!_caption.isEmpty()) {
- int32 textw = width - st::mediaPadding.left() - st::mediaPadding.right();
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
- if (!outbg) { // substract Download / Save As button
- textw -= st::mediaSaveDelta + _buttonWidth;
- }
- h += st::webPagePhotoSkip + _caption.countHeight(textw);
- }
- return h;
-}
-
-void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- int32 height = _height;
- if (width < 0) {
- width = w;
- } else if (!_caption.isEmpty()) {
- height = countHeight(parent, width);
- }
- if (width < 1) return;
-
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
- int skipy = 0, replyFrom = 0, fwdFrom = 0;
- if (reply) {
- skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (fwd) {
- skipy = st::msgServiceNameFont->height;
- }
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- } else if (fwd) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
-
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed;
- if (width >= _maxw) {
- width = _maxw;
- }
-
- if (!outbg) { // draw Download / Save As button
- int32 h = height;
- if (!_caption.isEmpty()) {
- h -= st::webPagePhotoSkip + _caption.countHeight(width - _buttonWidth - st::mediaSaveDelta - st::mediaPadding.left() - st::mediaPadding.right());
- }
- int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (h - skipy - btnh) / 2;
- if (x >= btnx && y >= btny && x < btnx + btnw && y < btny + btnh) {
- lnk = data->loader ? _cancell : _savel;
- return;
- }
- width -= btnw + st::mediaSaveDelta;
- }
-
- if (parent->displayFromName()) {
- if (x >= st::mediaPadding.left() && y >= st::msgPadding.top() && x < width - st::mediaPadding.left() - st::mediaPadding.right() && x < st::mediaPadding.left() + parent->from()->nameText.maxWidth() && y < replyFrom) {
- lnk = parent->from()->lnk;
- return;
- }
- }
- if (reply) {
- if (x >= 0 && y >= replyFrom + st::msgReplyPadding.top() && x < width && y < skipy - st::msgReplyPadding.bottom()) {
- lnk = reply->replyToLink();
- return;
- }
- } else if (fwd) {
- if (y >= fwdFrom && y < skipy) {
- return fwd->getForwardedState(lnk, state, x - st::mediaPadding.left(), width - st::mediaPadding.left() - st::mediaPadding.right());
- }
- }
-
- bool inDate = parent->pointInTime(width, height, x, y, InfoDisplayDefault);
- if (inDate) {
- state = HistoryInDateCursorState;
- }
-
- int32 tw = width - st::mediaPadding.left() - st::mediaPadding.right();
- if (x >= st::mediaPadding.left() && y >= skipy + st::mediaPadding.top() && x < st::mediaPadding.left() + tw && y < skipy + st::mediaPadding.top() + st::mediaThumbSize && !data->loader && data->access) {
- lnk = _openl;
- return;
- }
- if (!_caption.isEmpty() && x >= st::mediaPadding.left() && x < st::mediaPadding.left() + tw && y >= skipy + st::mediaPadding.top() + st::mediaThumbSize + st::webPagePhotoSkip) {
- bool inText = false;
- _caption.getState(lnk, inText, x - st::mediaPadding.left(), y - skipy - st::mediaPadding.top() - st::mediaThumbSize - st::webPagePhotoSkip, tw);
- state = inDate ? HistoryInDateCursorState : (inText ? HistoryInTextCursorState : HistoryDefaultCursorState);
- }
-}
-
-HistoryMedia *HistoryVideo::clone() const {
- return new HistoryVideo(*this);
-}
-
-void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const {
- int32 height = _height;
- if (width < 0) {
- width = w;
- } else if (!_caption.isEmpty()) {
- height = countHeight(parent, width);
- }
- if (width < 1) return;
-
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
- int skipy = 0, replyFrom = 0, fwdFrom = 0;
- if (reply) {
- skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (fwd) {
- skipy = st::msgServiceNameFont->height;
- }
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- } else if (fwd) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
-
- data->thumb->checkload();
-
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed;
- if (width >= _maxw) {
- width = _maxw;
- }
-
- if (!outbg) { // draw Download / Save As button
- hovered = ((data->loader ? _cancell : _savel) == textlnkOver());
- pressed = hovered && ((data->loader ? _cancell : _savel) == textlnkDown());
- if (hovered && !pressed && textlnkDown()) hovered = false;
-
- int32 h = height;
- if (!_caption.isEmpty()) {
- h -= st::webPagePhotoSkip + _caption.countHeight(width - _buttonWidth - st::mediaSaveDelta - st::mediaPadding.left() - st::mediaPadding.right());
- }
-
- int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (h - skipy - btnh) / 2;
- style::color bg(selected ? st::msgInSelectBg : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor));
- style::color sh(selected ? st::msgInSelectShadow : st::msgInShadow);
- RoundCorners cors(selected ? MessageInSelectedCorners : (hovered ? ButtonHoverCorners : MessageInCorners));
- App::roundRect(p, btnx, btny, btnw, btnh, bg, cors, &sh);
-
- p.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p);
- p.setFont(st::mediaSaveButton.font->f);
- QString btnText(lang(data->loader ? lng_media_cancel : (data->already().isEmpty() ? lng_media_download : lng_media_open_with)));
- int32 btnTextWidth = data->loader ? _cancelWidth : (data->already().isEmpty() ? _downloadWidth : _openWithWidth);
- p.drawText(btnx + (btnw - btnTextWidth) / 2, btny + (pressed ? st::mediaSaveButton.downTextTop : st::mediaSaveButton.textTop) + st::mediaSaveButton.font->ascent, btnText);
- width -= btnw + st::mediaSaveDelta;
- }
-
- style::color bg(selected ? (outbg ? st::msgOutSelectBg : st::msgInSelectBg) : (outbg ? st::msgOutBg : st::msgInBg));
- style::color sh(selected ? (outbg ? st::msgOutSelectShadow : st::msgInSelectShadow) : (outbg ? st::msgOutShadow : st::msgInShadow));
- RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners));
- App::roundRect(p, 0, 0, width, height, bg, cors, &sh);
-
- if (parent->displayFromName()) {
- p.setFont(st::msgNameFont->f);
- if (fromChannel) {
- p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor);
- } else {
- p.setPen(parent->from()->color);
- }
- parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right());
- }
- if (reply) {
- reply->drawReplyTo(p, st::msgReplyPadding.left(), replyFrom, width - st::msgReplyPadding.left() - st::msgReplyPadding.right(), selected);
- } else if (fwd) {
- fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
- }
-
- if (_thumbw) {
- p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize));
+void HistoryVideo::updateStatusText(const HistoryItem *parent) const {
+ bool showPause = false;
+ int32 statusSize = 0, realDuration = 0;
+ if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
+ statusSize = FileStatusSizeFailed;
+ } else if (_data->status == FileUploading) {
+ statusSize = _data->uploadOffset;
+ } else if (_data->loading()) {
+ statusSize = _data->loadOffset();
+ } else if (!_data->already().isEmpty()) {
+ statusSize = FileStatusSizeLoaded;
} else {
- p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), (outbg ? st::mediaDocOutImg : st::mediaDocInImg));
+ statusSize = FileStatusSizeReady;
}
- if (selected) {
- App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
+ if (statusSize != _statusSize) {
+ setStatusSize(statusSize);
}
-
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- int32 twidth = width - tleft - st::mediaPadding.right();
- int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth();
-
- p.setFont(st::mediaFont->f);
- p.setPen(st::black->c);
- p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, lang(lng_media_video));
-
- QString statusText;
-
- style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor));
- p.setPen(status->p);
-
- if (data->loader) {
- int32 offset = data->loader->currentOffset();
- if (_dldTextCache.isEmpty() || _dldDone != offset) {
- _dldDone = offset;
- _dldTextCache = formatDownloadText(_dldDone, data->size);
- }
- statusText = _dldTextCache;
- } else {
- if (data->status == FileFailed) {
- statusText = lang(lng_attach_failed);
- } else if (data->status == FileUploading) {
- if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
- _uplDone = data->uploadOffset;
- _uplTextCache = formatDownloadText(_uplDone, data->size);
- }
- statusText = _uplTextCache;
- } else {
- statusText = _size;
- }
- }
- int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->height;
- p.drawText(tleft, texty + st::mediaFont->ascent, statusText);
- if (parent->isMediaUnread()) {
- int32 w = st::mediaFont->width(statusText);
- if (w + st::mediaUnreadSkip + st::mediaUnreadSize <= twidth) {
- p.setRenderHint(QPainter::HighQualityAntialiasing, true);
- p.setPen(Qt::NoPen);
- p.setBrush((outbg ? (selected ? st::mediaOutUnreadSelectColor : st::mediaOutUnreadColor) : (selected ? st::mediaInUnreadSelectColor : st::mediaInUnreadColor))->b);
- p.drawEllipse(QRect(tleft + w + st::mediaUnreadSkip, texty + ((st::mediaFont->height - st::mediaUnreadSize) / 2), st::mediaUnreadSize, st::mediaUnreadSize));
- p.setRenderHint(QPainter::HighQualityAntialiasing, false);
- }
- }
- p.setFont(st::msgDateFont->f);
-
- if (!_caption.isEmpty()) {
- p.setPen(st::black->p);
- _caption.draw(p, st::mediaPadding.left(), skipy + st::mediaPadding.top() + st::mediaThumbSize + st::webPagePhotoSkip, width - st::mediaPadding.left() - st::mediaPadding.right());
- }
-
- int32 fullRight = width, fullBottom = height;
- parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayDefault);
}
-int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) {
- w = qMin(width, _maxw);
- if (_caption.isEmpty()) return _height;
+void HistoryVideo::regItem(HistoryItem *item) {
+ App::regVideoItem(_data, item);
+}
- _height = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
- if (parent->displayFromName()) {
- _height += st::msgPadding.top() + st::msgNameFont->height;
- }
- if (const HistoryReply *reply = toHistoryReply(parent)) {
- _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (const HistoryForwarded *fwd = toHistoryForwarded(parent)) {
- if (!parent->displayFromName()) {
- _height += st::msgPadding.top();
- }
- _height += st::msgServiceNameFont->height;
- }
- if (!_caption.isEmpty()) {
- int32 textw = w - st::mediaPadding.left() - st::mediaPadding.right();
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
- if (!outbg) { // substract Download / Save As button
- textw -= st::mediaSaveDelta + _buttonWidth;
- }
- _height += st::webPagePhotoSkip + _caption.countHeight(textw);
- }
- return _height;
+void HistoryVideo::unregItem(HistoryItem *item) {
+ App::unregVideoItem(_data, item);
}
ImagePtr HistoryVideo::replyPreview() {
- if (data->replyPreview->isNull() && !data->thumb->isNull()) {
- if (data->thumb->loaded()) {
- int w = data->thumb->width(), h = data->thumb->height();
+ if (_data->replyPreview->isNull() && !_data->thumb->isNull()) {
+ if (_data->thumb->loaded()) {
+ int w = _data->thumb->width(), h = _data->thumb->height();
if (w <= 0) w = 1;
if (h <= 0) h = 1;
- data->replyPreview = ImagePtr(w > h ? data->thumb->pix(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : data->thumb->pix(st::msgReplyBarSize.height()), "PNG");
+ _data->replyPreview = ImagePtr(w > h ? _data->thumb->pix(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : _data->thumb->pix(st::msgReplyBarSize.height()), "PNG");
} else {
- data->thumb->load();
+ _data->thumb->load();
}
}
- return data->replyPreview;
+ return _data->replyPreview;
}
-HistoryAudio::HistoryAudio(const MTPDaudio &audio) : HistoryMedia()
-, data(App::feedAudio(audio))
-, _openl(new AudioOpenLink(data))
-, _savel(new AudioSaveLink(data))
-, _cancell(new AudioCancelLink(data))
-, _dldDone(0)
-, _uplDone(0)
-{
- _size = formatDurationAndSizeText(data->duration, data->size);
+HistoryAudio::HistoryAudio(const MTPDaudio &audio) : HistoryFileMedia()
+, _data(App::feedAudio(audio)) {
+ setLinks(new AudioOpenLink(_data), new AudioOpenLink(_data), new AudioCancelLink(_data));
- if (!_openWithWidth) {
- _downloadWidth = st::mediaSaveButton.font->width(lang(lng_media_download));
- _openWithWidth = st::mediaSaveButton.font->width(lang(lng_media_open_with));
- _cancelWidth = st::mediaSaveButton.font->width(lang(lng_media_cancel));
- _buttonWidth = (st::mediaSaveButton.width > 0) ? st::mediaSaveButton.width : ((_downloadWidth > _openWithWidth ? (_downloadWidth > _cancelWidth ? _downloadWidth : _cancelWidth) : _openWithWidth) - st::mediaSaveButton.width);
- }
+ setStatusSize(FileStatusSizeReady);
+}
+
+HistoryAudio::HistoryAudio(const HistoryAudio &other) : HistoryFileMedia()
+, _data(other._data) {
+ setLinks(new AudioOpenLink(_data), new AudioOpenLink(_data), new AudioCancelLink(_data));
+
+ setStatusSize(other._statusSize);
}
void HistoryAudio::initDimensions(const HistoryItem *parent) {
- _maxw = st::mediaMaxWidth;
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
- if (!outbg) { // add Download / Save As button
- _maxw += st::mediaSaveDelta + _buttonWidth;
- }
+ _maxw = st::msgFileMinWidth;
- _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
- if (parent->displayFromName()) {
- _minh += st::msgPadding.top() + st::msgNameFont->height;
- }
- if (const HistoryReply *reply = toHistoryReply(parent)) {
- _minh += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (const HistoryForwarded *fwd = toHistoryForwarded(parent)) {
- if (!parent->displayFromName()) {
- _minh += st::msgPadding.top();
- }
- _minh += st::msgServiceNameFont->height;
- }
- _height = _minh;
+ int32 tleft = 0, tright = 0;
+
+ tleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right();
+ tright = st::msgFileThumbPadding.left();
+ _maxw = qMax(_maxw, tleft + audioMaxStatusWidth(_data) + int(st::mediaUnreadSkip + st::mediaUnreadSize) + parent->skipBlockWidth() + st::msgPadding.right());
+
+ _maxw = qMax(tleft + st::semiboldFont->width(lang(lng_media_audio)) + tright, _maxw);
+ _maxw = qMin(_maxw, int(st::msgMaxWidth));
+
+ _height = _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom();
}
-void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const {
- if (width < 0) width = w;
- if (width < 1) return;
+void HistoryAudio::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
- int skipy = 0, replyFrom = 0, fwdFrom = 0;
- if (reply) {
- skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (fwd) {
- skipy = st::msgServiceNameFont->height;
- }
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- } else if (fwd) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
+ _data->automaticLoad(parent);
+ bool loaded = _data->loaded(), displayLoading = _data->displayLoading();
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed;
- bool already = !data->already().isEmpty(), hasdata = !data->data.isEmpty();
- if (width >= _maxw) {
- width = _maxw;
- }
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
- if (!data->loader && data->status != FileFailed && !already && !hasdata && data->size < AudioVoiceMsgInMemory) {
- data->save(QString());
- }
-
- if (!outbg) { // draw Download / Save As button
- hovered = ((data->loader ? _cancell : _savel) == textlnkOver());
- pressed = hovered && ((data->loader ? _cancell : _savel) == textlnkDown());
- if (hovered && !pressed && textlnkDown()) hovered = false;
-
- int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2;
- style::color bg(selected ? st::msgInSelectBg : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor));
- style::color sh(selected ? st::msgInSelectShadow : st::msgInShadow);
- RoundCorners cors(selected ? MessageInSelectedCorners : (hovered ? ButtonHoverCorners : MessageInCorners));
- App::roundRect(p, btnx, btny, btnw, btnh, bg, cors, &sh);
-
- p.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p);
- p.setFont(st::mediaSaveButton.font->f);
- QString btnText(lang(data->loader ? lng_media_cancel : (already ? lng_media_open_with : lng_media_download)));
- int32 btnTextWidth = data->loader ? _cancelWidth : (already ? _openWithWidth : _downloadWidth);
- p.drawText(btnx + (btnw - btnTextWidth) / 2, btny + (pressed ? st::mediaSaveButton.downTextTop : st::mediaSaveButton.textTop) + st::mediaSaveButton.font->ascent, btnText);
- width -= btnw + st::mediaSaveDelta;
- }
-
- style::color bg(selected ? (outbg ? st::msgOutSelectBg : st::msgInSelectBg) : (outbg ? st::msgOutBg : st::msgInBg));
- style::color sh(selected ? (outbg ? st::msgOutSelectShadow : st::msgInSelectShadow) : (outbg ? st::msgOutShadow : st::msgInShadow));
- RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners));
- App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
-
- if (parent->displayFromName()) {
- p.setFont(st::msgNameFont->f);
- if (fromChannel) {
- p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor);
- } else {
- p.setPen(parent->from()->color);
+ if (displayLoading) {
+ ensureAnimation(parent);
+ if (!_animation->radial.animating()) {
+ _animation->radial.start(_data->progress());
}
- parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right());
- }
- if (reply) {
- reply->drawReplyTo(p, st::msgReplyPadding.left(), replyFrom, width - st::msgReplyPadding.left() - st::msgReplyPadding.right(), selected);
- } else if (fwd) {
- fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
}
+ bool showPause = updateStatusText(parent);
+ bool radial = isRadialAnimation(ms);
- AudioMsgId playing;
- AudioPlayerState playingState = AudioPlayerStopped;
- int64 playingPosition = 0, playingDuration = 0;
- int32 playingFrequency = 0;
- if (audioPlayer()) {
- audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
- }
+ int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0;
- QRect img;
- QString statusText;
- if (data->status == FileFailed) {
- statusText = lang(lng_attach_failed);
- img = outbg ? st::mediaAudioOutImg : st::mediaAudioInImg;
- } else if (data->status == FileUploading) {
- if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
- _uplDone = data->uploadOffset;
- _uplTextCache = formatDownloadText(_uplDone, data->size);
- }
- statusText = _uplTextCache;
- img = outbg ? st::mediaAudioOutImg : st::mediaAudioInImg;
- } else if (already || hasdata) {
- bool showPause = false;
- if (playing.msgId == parent->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
- statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
- showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
- } else {
- statusText = formatDurationText(data->duration);
- }
- img = outbg ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg);
- } else {
- if (data->loader) {
- int32 offset = data->loader->currentOffset();
- if (_dldTextCache.isEmpty() || _dldDone != offset) {
- _dldDone = offset;
- _dldTextCache = formatDownloadText(_dldDone, data->size);
- }
- statusText = _dldTextCache;
- } else {
- statusText = _size;
- }
- img = outbg ? st::mediaAudioOutImg : st::mediaAudioInImg;
- }
+ nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right();
+ nametop = st::msgFileNameTop;
+ nameright = st::msgFilePadding.left();
+ statustop = st::msgFileStatusTop;
- p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), img);
+ QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width));
+ p.setPen(Qt::NoPen);
if (selected) {
- App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
+ p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected);
+ } else if (isThumbAnimation(ms)) {
+ float64 over = _animation->a_thumbOver.current();
+ p.setBrush(style::interpolate(outbg ? st::msgFileOutBg : st::msgFileInBg, outbg ? st::msgFileOutBgOver : st::msgFileInBgOver, over));
+ } else {
+ bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel);
+ p.setBrush(outbg ? (over ? st::msgFileOutBgOver : st::msgFileOutBg) : (over ? st::msgFileInBgOver : st::msgFileInBg));
}
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- int32 twidth = width - tleft - st::mediaPadding.right();
- int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth();
+ p.setRenderHint(QPainter::HighQualityAntialiasing);
+ p.drawEllipse(inner);
+ p.setRenderHint(QPainter::HighQualityAntialiasing, false);
- p.setFont(st::mediaFont->f);
- p.setPen(st::black->c);
- p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, lang(lng_media_audio));
+ if (radial) {
+ QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
+ style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg));
+ _animation->radial.draw(p, rinner, st::msgFileRadialLine, bg);
+ }
+
+ style::sprite icon;
+ if (showPause) {
+ icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause);
+ } else if (radial || _data->loading()) {
+ icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel);
+ } else if (loaded) {
+ icon = outbg ? (selected ? st::msgFileOutPlaySelected : st::msgFileOutPlay) : (selected ? st::msgFileInPlaySelected : st::msgFileInPlay);
+ } else {
+ icon = outbg ? (selected ? st::msgFileOutDownloadSelected : st::msgFileOutDownload) : (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload);
+ }
+ p.drawSpriteCenter(inner, icon);
+
+ int32 namewidth = _width - nameleft - nameright;
+
+ p.setFont(st::semiboldFont);
+ p.setPen(st::black);
+ p.drawTextLeft(nameleft, nametop, _width, lang(lng_media_audio));
+
+ style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg));
+ p.setFont(st::normalFont);
+ p.setPen(status);
+ p.drawTextLeft(nameleft, statustop, _width, _statusText);
- style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor));
- p.setPen(status->p);
- int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->height;
- p.drawText(tleft, texty + st::mediaFont->ascent, statusText);
if (parent->isMediaUnread()) {
- int32 w = st::mediaFont->width(statusText);
- if (w + st::mediaUnreadSkip + st::mediaUnreadSize <= twidth) {
- p.setRenderHint(QPainter::HighQualityAntialiasing, true);
+ int32 w = st::normalFont->width(_statusText);
+ if (w + st::mediaUnreadSkip + st::mediaUnreadSize <= namewidth) {
p.setPen(Qt::NoPen);
- p.setBrush((outbg ? (selected ? st::mediaOutUnreadSelectColor : st::mediaOutUnreadColor) : (selected ? st::mediaInUnreadSelectColor : st::mediaInUnreadColor))->b);
- p.drawEllipse(QRect(tleft + w + st::mediaUnreadSkip, texty + ((st::mediaFont->height - st::mediaUnreadSize) / 2), st::mediaUnreadSize, st::mediaUnreadSize));
+ p.setBrush(outbg ? (selected ? st::msgFileOutBgSelected : st::msgFileOutBg) : (selected ? st::msgFileInBgSelected : st::msgFileInBg));
+
+ p.setRenderHint(QPainter::HighQualityAntialiasing, true);
+ p.drawEllipse(rtlrect(nameleft + w + st::mediaUnreadSkip, statustop + st::mediaUnreadTop, st::mediaUnreadSize, st::mediaUnreadSize, _width));
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
}
}
- p.setFont(st::msgDateFont->f);
-
- style::color date(selected ? (outbg ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (outbg ? st::msgOutDateColor : st::msgInDateColor));
- p.setPen(date->p);
-
- int32 fullRight = width, fullBottom = _height;
- parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayDefault);
}
-void HistoryAudio::regItem(HistoryItem *item) {
- App::regAudioItem(data, item);
-}
+void HistoryAudio::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
-void HistoryAudio::unregItem(HistoryItem *item) {
- App::unregAudioItem(data, item);
-}
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
+ bool loaded = _data->loaded();
-void HistoryAudio::updateFrom(const MTPMessageMedia &media) {
- if (media.type() == mtpc_messageMediaAudio) {
- App::feedAudio(media.c_messageMediaAudio().vaudio, data);
- if (!data->data.isEmpty()) {
- Local::writeAudio(mediaKey(mtpToLocationType(mtpc_inputAudioFileLocation), data->dc, data->id), data->data);
- }
+ bool showPause = updateStatusText(parent);
+
+ int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0;
+
+ QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width));
+ if ((_data->loading() || _data->status == FileUploading || !loaded) && inner.contains(x, y)) {
+ lnk = (_data->loading() || _data->status == FileUploading) ? _cancell : _savel;
+ return;
+ }
+
+ if (x >= 0 && y >= 0 && x < _width && y < _height && _data->access && !_data->loading()) {
+ lnk = _openl;
+ return;
}
}
@@ -4076,638 +3923,873 @@ const QString HistoryAudio::inHistoryText() const {
return qsl("[ ") + lang(lng_in_dlg_audio) + qsl(" ]");
}
-bool HistoryAudio::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- if (width >= _maxw) {
- width = _maxw;
- }
- return (x >= 0 && y >= 0 && x < width && y < _height);
+void HistoryAudio::regItem(HistoryItem *item) {
+ App::regAudioItem(_data, item);
}
-void HistoryAudio::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- if (width < 1) return;
+void HistoryAudio::unregItem(HistoryItem *item) {
+ App::unregAudioItem(_data, item);
+}
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
- int skipy = 0, replyFrom = 0, fwdFrom = 0;
- if (reply) {
- skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (fwd) {
- skipy = st::msgServiceNameFont->height;
- }
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- } else if (fwd) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
-
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed;
- if (width >= _maxw) {
- width = _maxw;
- }
-
- if (!outbg) { // draw Download / Save As button
- int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2;
- if (x >= btnx && y >= btny && x < btnx + btnw && y < btny + btnh) {
- lnk = data->loader ? _cancell : _savel;
- return;
+void HistoryAudio::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
+ if (media.type() == mtpc_messageMediaAudio) {
+ App::feedAudio(media.c_messageMediaAudio().vaudio, _data);
+ if (!_data->data().isEmpty()) {
+ Local::writeAudio(mediaKey(AudioFileLocation, _data->dc, _data->id), _data->data());
}
- width -= btnw + st::mediaSaveDelta;
- }
-
- if (parent->displayFromName()) {
- if (x >= st::mediaPadding.left() && y >= st::msgPadding.top() && x < width - st::mediaPadding.left() - st::mediaPadding.right() && x < st::mediaPadding.left() + parent->from()->nameText.maxWidth() && y < replyFrom) {
- lnk = parent->from()->lnk;
- return;
- }
- }
- if (reply) {
- if (x >= 0 && y >= replyFrom + st::msgReplyPadding.top() && x < width && y < skipy - st::msgReplyPadding.bottom()) {
- lnk = reply->replyToLink();
- return;
- }
- } else if (fwd) {
- if (y >= fwdFrom && y < skipy) {
- return fwd->getForwardedState(lnk, state, x - st::mediaPadding.left(), width - st::mediaPadding.left() - st::mediaPadding.right());
- }
- }
-
- if (x >= 0 && y >= skipy && x < width && y < _height && !data->loader && data->access) {
- lnk = _openl;
-
- bool inDate = parent->pointInTime(width, _height, x, y, InfoDisplayDefault);
- if (inDate) {
- state = HistoryInDateCursorState;
- }
-
- return;
}
}
-HistoryMedia *HistoryAudio::clone() const {
- return new HistoryAudio(*this);
+void HistoryAudio::setStatusSize(int32 newSize, qint64 realDuration) const {
+ HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration, realDuration);
}
-namespace {
- QString documentName(DocumentData *document) {
- SongData *song = document->song();
- if (!song || (song->title.isEmpty() && song->performer.isEmpty())) return document->name;
- if (song->performer.isEmpty()) return song->title;
- return song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title);
- }
-}
+bool HistoryAudio::updateStatusText(const HistoryItem *parent) const {
+ bool showPause = false;
+ int32 statusSize = 0, realDuration = 0;
+ if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
+ statusSize = FileStatusSizeFailed;
+ } else if (_data->status == FileUploading) {
+ statusSize = _data->uploadOffset;
+ } else if (_data->loading()) {
+ statusSize = _data->loadOffset();
+ } else if (_data->loaded()) {
+ AudioMsgId playing;
+ AudioPlayerState playingState = AudioPlayerStopped;
+ int64 playingPosition = 0, playingDuration = 0;
+ int32 playingFrequency = 0;
+ if (audioPlayer()) {
+ audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
+ }
-HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia()
-, data(document)
-, _openl(new DocumentOpenLink(data))
-, _savel(new DocumentSaveLink(data))
-, _cancell(new DocumentCancelLink(data))
-, _name(documentName(data))
-, _dldDone(0)
-, _uplDone(0)
-{
- _namew = st::mediaFont->width(_name.isEmpty() ? qsl("Document") : _name);
- _size = document->song() ? formatDurationAndSizeText(document->song()->duration, data->size) : formatSizeText(data->size);
-
- _height = _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
-
- if (!_openWithWidth) {
- _downloadWidth = st::mediaSaveButton.font->width(lang(lng_media_download));
- _openWithWidth = st::mediaSaveButton.font->width(lang(lng_media_open_with));
- _cancelWidth = st::mediaSaveButton.font->width(lang(lng_media_cancel));
- _buttonWidth = (st::mediaSaveButton.width > 0) ? st::mediaSaveButton.width : ((_downloadWidth > _openWithWidth ? (_downloadWidth > _cancelWidth ? _downloadWidth : _cancelWidth) : _openWithWidth) - st::mediaSaveButton.width);
- }
-
- data->thumb->load();
-
- int32 tw = data->thumb->width(), th = data->thumb->height();
- if (data->thumb->isNull() || !tw || !th) {
- _thumbw = 0;
- } else if (tw > th) {
- _thumbw = (tw * st::mediaThumbSize) / th;
+ if (playing.msgId == parent->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
+ statusSize = -1 - (playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
+ realDuration = playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency);
+ showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
+ } else {
+ statusSize = FileStatusSizeLoaded;
+ }
} else {
- _thumbw = st::mediaThumbSize;
+ statusSize = FileStatusSizeReady;
}
+ if (statusSize != _statusSize) {
+ setStatusSize(statusSize, realDuration);
+ }
+ return showPause;
+}
+
+HistoryDocument::HistoryDocument(DocumentData *document, const QString &caption, const HistoryItem *parent) : HistoryFileMedia()
+, _data(document)
+, _linksavel(new DocumentSaveLink(_data))
+, _linkcancell(new DocumentCancelLink(_data))
+, _name(documentName(_data))
+, _namew(st::semiboldFont->width(_name))
+, _caption(st::msgFileMinWidth - st::msgPadding.left() - st::msgPadding.right()) {
+ setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data));
+
+ setStatusSize(FileStatusSizeReady);
+
+ if (!caption.isEmpty()) {
+ _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent));
+ }
+}
+
+HistoryDocument::HistoryDocument(const HistoryDocument &other) : HistoryFileMedia()
+, _data(other._data)
+, _linksavel(new DocumentSaveLink(_data))
+, _linkcancell(new DocumentCancelLink(_data))
+, _name(other._name)
+, _namew(other._namew)
+, _thumbw(other._thumbw) {
+ setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data));
+
+ setStatusSize(other._statusSize);
}
void HistoryDocument::initDimensions(const HistoryItem *parent) {
- if (parent == animated.msg) {
- _maxw = animated.w / cIntRetinaFactor();
- _minh = animated.h / cIntRetinaFactor();
+ if (_caption.hasSkipBlock()) {
+ _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight());
+ }
+
+ if (withThumb()) {
+ _data->thumb->load();
+ int32 tw = _data->thumb->width(), th = _data->thumb->height();
+ if (tw > th) {
+ _thumbw = (tw * st::msgFileThumbSize) / th;
+ } else {
+ _thumbw = st::msgFileThumbSize;
+ }
} else {
- _maxw = st::mediaMaxWidth;
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- if (_namew + tleft + st::mediaPadding.right() > _maxw) {
- _maxw = _namew + tleft + st::mediaPadding.right();
- }
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
- if (!outbg) { // add Download / Save As button
- _maxw += st::mediaSaveDelta + _buttonWidth;
- }
- _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
-
- if (parent->displayFromName()) {
- _minh += st::msgPadding.top() + st::msgNameFont->height;
- }
- if (const HistoryReply *reply = toHistoryReply(parent)) {
- _minh += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (const HistoryForwarded *fwd = toHistoryForwarded(parent)) {
- if (!data->song()) {
- if (!parent->displayFromName()) {
- _minh += st::msgPadding.top();
- }
- _minh += st::msgServiceNameFont->height;
- }
- }
- }
- _height = _minh;
-}
-
-void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const {
- if (width < 0) width = w;
- if (width < 1) return;
-
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed;
- bool already = !data->already().isEmpty(), hasdata = !data->data.isEmpty();
- if (parent == animated.msg) {
- int32 pw = animated.w / cIntRetinaFactor(), ph = animated.h / cIntRetinaFactor();
- if (width < pw) {
- pw = width;
- ph = (pw == w) ? _height : (pw * animated.h / animated.w);
- if (ph < 1) ph = 1;
- }
-
- App::roundShadow(p, 0, 0, pw, ph, selected ? st::msgInSelectShadow : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
-
- p.drawPixmap(0, 0, animated.current(pw * cIntRetinaFactor(), ph * cIntRetinaFactor(), true));
- if (selected) {
- App::roundRect(p, 0, 0, pw, ph, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
- }
- return;
+ _thumbw = 0;
}
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = (reply || data->song()) ? 0 : toHistoryForwarded(parent);
- int skipy = 0, replyFrom = 0, fwdFrom = 0;
- if (reply) {
- skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (fwd) {
- skipy = st::msgServiceNameFont->height;
- }
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- } else if (fwd) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
+ _maxw = st::msgFileMinWidth;
- if (width >= _maxw) {
- width = _maxw;
- }
-
- if (!outbg) { // draw Download / Save As button
- hovered = ((data->loader ? _cancell : _savel) == textlnkOver());
- pressed = hovered && ((data->loader ? _cancell : _savel) == textlnkDown());
- if (hovered && !pressed && textlnkDown()) hovered = false;
-
- int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2;
- style::color bg(selected ? st::msgInSelectBg : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor));
- style::color sh(selected ? st::msgInSelectShadow : st::msgInShadow);
- RoundCorners cors(selected ? MessageInSelectedCorners : (hovered ? ButtonHoverCorners : MessageInCorners));
- App::roundRect(p, btnx, btny, btnw, btnh, bg, cors, &sh);
-
- p.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p);
- p.setFont(st::mediaSaveButton.font->f);
- QString btnText(lang(data->loader ? lng_media_cancel : (already ? lng_media_open_with : lng_media_download)));
- int32 btnTextWidth = data->loader ? _cancelWidth : (already ? _openWithWidth : _downloadWidth);
- p.drawText(btnx + (btnw - btnTextWidth) / 2, btny + (pressed ? st::mediaSaveButton.downTextTop : st::mediaSaveButton.textTop) + st::mediaSaveButton.font->ascent, btnText);
- width -= btnw + st::mediaSaveDelta;
- }
-
- style::color bg(selected ? (outbg ? st::msgOutSelectBg : st::msgInSelectBg) : (outbg ? st::msgOutBg : st::msgInBg));
- style::color sh(selected ? (outbg ? st::msgOutSelectShadow : st::msgInSelectShadow) : (outbg ? st::msgOutShadow : st::msgInShadow));
- RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners));
- App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
-
- if (parent->displayFromName()) {
- p.setFont(st::msgNameFont->f);
- if (fromChannel) {
- p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor);
- } else {
- p.setPen(parent->from()->color);
- }
- parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right());
- }
- if (reply) {
- reply->drawReplyTo(p, st::msgReplyPadding.left(), replyFrom, width - st::msgReplyPadding.left() - st::msgReplyPadding.right(), selected);
- } else if (fwd) {
- fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
- }
-
- QString statusText;
- if (data->song()) {
- SongMsgId playing;
- AudioPlayerState playingState = AudioPlayerStopped;
- int64 playingPosition = 0, playingDuration = 0;
- int32 playingFrequency = 0;
- if (audioPlayer()) {
- audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
- }
-
- QRect img;
- if (data->status == FileFailed) {
- statusText = lang(lng_attach_failed);
- img = outbg ? st::mediaMusicOutImg : st::mediaMusicInImg;
- } else if (data->status == FileUploading) {
- if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
- _uplDone = data->uploadOffset;
- _uplTextCache = formatDownloadText(_uplDone, data->size);
- }
- statusText = _uplTextCache;
- img = outbg ? st::mediaMusicOutImg : st::mediaMusicInImg;
- } else if (already || hasdata) {
- bool showPause = false;
- if (playing.msgId == parent->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
- statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
- showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
- } else {
- statusText = formatDurationText(data->song()->duration);
- }
- if (!showPause && playing.msgId == parent->fullId() && App::main() && App::main()->player()->seekingSong(playing)) showPause = true;
- img = outbg ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg);
- } else {
- if (data->loader) {
- int32 offset = data->loader->currentOffset();
- if (_dldTextCache.isEmpty() || _dldDone != offset) {
- _dldDone = offset;
- _dldTextCache = formatDownloadText(_dldDone, data->size);
- }
- statusText = _dldTextCache;
- } else {
- statusText = _size;
- }
- img = outbg ? st::mediaMusicOutImg : st::mediaMusicInImg;
- }
-
- p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), img);
+ int32 tleft = 0, tright = 0;
+ bool wthumb = withThumb();
+ if (wthumb) {
+ tleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right();
+ tright = st::msgFileThumbPadding.left();
+ _maxw = qMax(_maxw, tleft + documentMaxStatusWidth(_data) + tright);
} else {
- if (data->status == FileFailed) {
- statusText = lang(lng_attach_failed);
- } else if (data->status == FileUploading) {
- if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
- _uplDone = data->uploadOffset;
- _uplTextCache = formatDownloadText(_uplDone, data->size);
- }
- statusText = _uplTextCache;
- } else if (data->loader) {
- int32 offset = data->loader->currentOffset();
- if (_dldTextCache.isEmpty() || _dldDone != offset) {
- _dldDone = offset;
- _dldTextCache = formatDownloadText(_dldDone, data->size);
- }
- statusText = _dldTextCache;
- } else {
- statusText = _size;
- }
-
- if (_thumbw) {
- data->thumb->checkload();
- p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize));
- } else {
- p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), (outbg ? st::mediaDocOutImg : st::mediaDocInImg));
- }
- }
- if (selected) {
- App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
+ tleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right();
+ tright = st::msgFileThumbPadding.left();
+ _maxw = qMax(_maxw, tleft + documentMaxStatusWidth(_data) + parent->skipBlockWidth() + st::msgPadding.right());
}
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- int32 twidth = width - tleft - st::mediaPadding.right();
- int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth();
+ _maxw = qMax(tleft + _namew + tright, _maxw);
+ _maxw = qMin(_maxw, int(st::msgMaxWidth));
- p.setFont(st::mediaFont->f);
- p.setPen(st::black->c);
- if (twidth < _namew) {
- p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, st::mediaFont->elided(_name, twidth));
+ if (wthumb) {
+ _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom();
} else {
- p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, _name);
+ _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom();
}
- style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor));
- p.setPen(status->p);
-
- p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->descent, statusText);
-
- p.setFont(st::msgDateFont->f);
-
- style::color date(selected ? (outbg ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (outbg ? st::msgOutDateColor : st::msgInDateColor));
- p.setPen(date->p);
-
- int32 fullRight = width, fullBottom = _height;
- parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayDefault);
-}
-
-void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool selected, bool over, int32 width) const {
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
- bool already = !data->already().isEmpty(), hasdata = !data->data.isEmpty();
- int32 height = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
-
- style::color bg(selected ? st::msgInSelectBg : (over ? st::playlistHoverBg : st::msgInBg));
- p.fillRect(0, 0, width, height, bg->b);
-
- QString statusText;
- if (data->song()) {
- SongMsgId playing;
- AudioPlayerState playingState = AudioPlayerStopped;
- int64 playingPosition = 0, playingDuration = 0;
- int32 playingFrequency = 0;
- if (audioPlayer()) {
- audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
- }
-
- QRect img;
- if (data->status == FileFailed) {
- statusText = lang(lng_attach_failed);
- img = st::mediaMusicInImg;
- } else if (data->status == FileUploading) {
- if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
- _uplDone = data->uploadOffset;
- _uplTextCache = formatDownloadText(_uplDone, data->size);
- }
- statusText = _uplTextCache;
- img = st::mediaMusicInImg;
- } else if (already || hasdata) {
- bool isPlaying = (playing.msgId == parent->fullId());
- bool showPause = false;
- if (playing.msgId == parent->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
- statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
- showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
- } else {
- statusText = formatDurationText(data->song()->duration);
- }
- if (!showPause && playing.msgId == parent->fullId() && App::main() && App::main()->player()->seekingSong(playing)) showPause = true;
- img = isPlaying ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg);
- } else {
- if (data->loader) {
- int32 offset = data->loader->currentOffset();
- if (_dldTextCache.isEmpty() || _dldDone != offset) {
- _dldDone = offset;
- _dldTextCache = formatDownloadText(_dldDone, data->size);
- }
- statusText = _dldTextCache;
- } else {
- statusText = _size;
- }
- img = st::mediaMusicInImg;
- }
-
- p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), App::sprite(), img);
+ if (_caption.isEmpty()) {
+ _height = _minh;
} else {
- if (data->status == FileFailed) {
- statusText = lang(lng_attach_failed);
- } else if (data->status == FileUploading) {
- if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) {
- _uplDone = data->uploadOffset;
- _uplTextCache = formatDownloadText(_uplDone, data->size);
- }
- statusText = _uplTextCache;
- } else if (data->loader) {
- int32 offset = data->loader->currentOffset();
- if (_dldTextCache.isEmpty() || _dldDone != offset) {
- _dldDone = offset;
- _dldTextCache = formatDownloadText(_dldDone, data->size);
- }
- statusText = _dldTextCache;
- } else {
- statusText = _size;
- }
-
- if (_thumbw) {
- data->thumb->checkload();
- p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize));
- } else {
- p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), App::sprite(), st::mediaDocInImg);
- }
- }
- if (selected) {
- App::roundRect(p, st::mediaPadding.left(), st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
- }
-
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- int32 twidth = width - tleft - st::mediaPadding.right();
- int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth();
-
- p.setFont(st::mediaFont->f);
- p.setPen(st::black->c);
- if (twidth < _namew) {
- p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, st::mediaFont->elided(_name, twidth));
- } else {
- p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, _name);
- }
-
- style::color status(selected ? st::mediaInSelectColor : st::mediaInColor);
- p.setPen(status->p);
- p.drawText(tleft, st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->descent, statusText);
-}
-
-TextLinkPtr HistoryDocument::linkInPlaylist() {
- if (!data->loader && data->access) {
- return _openl;
- }
- return TextLinkPtr();
-}
-
-void HistoryDocument::regItem(HistoryItem *item) {
- App::regDocumentItem(data, item);
-}
-
-void HistoryDocument::unregItem(HistoryItem *item) {
- App::unregDocumentItem(data, item);
-}
-
-void HistoryDocument::updateFrom(const MTPMessageMedia &media) {
- if (media.type() == mtpc_messageMediaDocument) {
- App::feedDocument(media.c_messageMediaDocument().vdocument, data);
+ _minh += _caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
}
}
int32 HistoryDocument::resize(int32 width, const HistoryItem *parent) {
- w = qMin(width, _maxw);
- if (parent == animated.msg) {
- if (w > st::maxMediaSize) {
- w = st::maxMediaSize;
+ if (_caption.isEmpty()) {
+ return HistoryFileMedia::resize(width, parent);
+ }
+
+ _width = qMin(width, _maxw);
+ bool wthumb = withThumb();
+ if (wthumb) {
+ _height = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom();
+ } else {
+ _height = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom();
+ }
+ _height += _caption.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
+
+ return _height;
+}
+
+void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+
+ _data->automaticLoad(parent);
+ bool loaded = _data->loaded(), displayLoading = _data->displayLoading();
+
+ int32 captionw = _width - st::msgPadding.left() - st::msgPadding.right();
+
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
+
+ if (displayLoading) {
+ ensureAnimation(parent);
+ if (!_animation->radial.animating()) {
+ _animation->radial.start(_data->progress());
}
- _height = animated.h / cIntRetinaFactor();
- if (animated.w / cIntRetinaFactor() > w) {
- _height = (w * _height / (animated.w / cIntRetinaFactor()));
- if (_height <= 0) _height = 1;
+ }
+ bool showPause = updateStatusText(parent);
+ bool radial = isRadialAnimation(ms);
+
+ int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0, bottom = 0;
+ bool wthumb = withThumb();
+ if (wthumb) {
+ nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right();
+ nametop = st::msgFileThumbNameTop;
+ nameright = st::msgFileThumbPadding.left();
+ statustop = st::msgFileThumbStatusTop;
+ linktop = st::msgFileThumbLinkTop;
+ bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom();
+
+ QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width));
+ QPixmap thumb = loaded ? _data->thumb->pixSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize);
+ p.drawPixmap(rthumb.topLeft(), thumb);
+ if (selected) {
+ App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
+ }
+
+ if (radial || (!loaded && !_data->loading())) {
+ float64 radialOpacity = (radial && loaded && !_data->uploading()) ? _animation->radial.opacity() : 1;
+ QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
+ p.setPen(Qt::NoPen);
+ if (selected) {
+ p.setBrush(st::msgDateImgBgSelected);
+ } else if (isThumbAnimation(ms)) {
+ float64 over = _animation->a_thumbOver.current();
+ p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over));
+ p.setBrush(st::black);
+ } else {
+ bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel);
+ p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
+ }
+ p.setOpacity(radialOpacity * p.opacity());
+
+ p.setRenderHint(QPainter::HighQualityAntialiasing);
+ p.drawEllipse(inner);
+ p.setRenderHint(QPainter::HighQualityAntialiasing, false);
+
+ p.setOpacity(radialOpacity);
+ style::sprite icon;
+ if (radial || _data->loading()) {
+ icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel);
+ } else {
+ icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload);
+ }
+ p.setOpacity((radial && loaded) ? _animation->radial.opacity() : 1);
+ p.drawSpriteCenter(inner, icon);
+ if (radial) {
+ p.setOpacity(1);
+
+ QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
+ _animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg);
+ }
+ }
+
+ if (_data->status != FileUploadFailed) {
+ const TextLinkPtr &lnk((_data->loading() || _data->status == FileUploading) ? _linkcancell : _linksavel);
+ bool over = textlnkDrawOver(lnk);
+ p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont);
+ p.setPen(outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg));
+ p.drawTextLeft(nameleft, linktop, _width, _link, _linkw);
}
} else {
- _height = _minh;
+ nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right();
+ nametop = st::msgFileNameTop;
+ nameright = st::msgFilePadding.left();
+ statustop = st::msgFileStatusTop;
+ bottom = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom();
+
+ QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width));
+ p.setPen(Qt::NoPen);
+ if (selected) {
+ p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected);
+ } else if (isThumbAnimation(ms)) {
+ float64 over = _animation->a_thumbOver.current();
+ p.setBrush(style::interpolate(outbg ? st::msgFileOutBg : st::msgFileInBg, outbg ? st::msgFileOutBgOver : st::msgFileInBgOver, over));
+ } else {
+ bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel);
+ p.setBrush(outbg ? (over ? st::msgFileOutBgOver : st::msgFileOutBg) : (over ? st::msgFileInBgOver : st::msgFileInBg));
+ }
+
+ p.setRenderHint(QPainter::HighQualityAntialiasing);
+ p.drawEllipse(inner);
+ p.setRenderHint(QPainter::HighQualityAntialiasing, false);
+
+ if (radial) {
+ QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
+ style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg));
+ _animation->radial.draw(p, rinner, st::msgFileRadialLine, bg);
+ }
+
+ style::sprite icon;
+ if (showPause) {
+ icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause);
+ } else if (radial || _data->loading()) {
+ icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel);
+ } else if (loaded) {
+ if (_data->song()) {
+ icon = outbg ? (selected ? st::msgFileOutPlaySelected : st::msgFileOutPlay) : (selected ? st::msgFileInPlaySelected : st::msgFileInPlay);
+ } else if (_data->isImage()) {
+ icon = outbg ? (selected ? st::msgFileOutImageSelected : st::msgFileOutImage) : (selected ? st::msgFileInImageSelected : st::msgFileInImage);
+ } else {
+ icon = outbg ? (selected ? st::msgFileOutFileSelected : st::msgFileOutFile) : (selected ? st::msgFileInFileSelected : st::msgFileInFile);
+ }
+ } else {
+ icon = outbg ? (selected ? st::msgFileOutDownloadSelected : st::msgFileOutDownload) : (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload);
+ }
+ p.drawSpriteCenter(inner, icon);
+ }
+ int32 namewidth = _width - nameleft - nameright;
+
+ p.setFont(st::semiboldFont);
+ p.setPen(st::black);
+ if (namewidth < _namew) {
+ p.drawTextLeft(nameleft, nametop, _width, st::semiboldFont->elided(_name, namewidth));
+ } else {
+ p.drawTextLeft(nameleft, nametop, _width, _name, _namew);
+ }
+
+ style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg));
+ p.setFont(st::normalFont);
+ p.setPen(status);
+ p.drawTextLeft(nameleft, statustop, _width, _statusText);
+
+ if (!_caption.isEmpty()) {
+ p.setPen(st::black);
+ _caption.draw(p, st::msgPadding.left(), bottom, captionw);
+ }
+}
+
+void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
+ bool loaded = _data->loaded();
+
+ bool showPause = updateStatusText(parent);
+
+ int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0, bottom = 0;
+ bool wthumb = withThumb();
+ if (wthumb) {
+ nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right();
+ linktop = st::msgFileThumbLinkTop;
+ bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom();
+
+ QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width));
+
+ if ((_data->loading() || _data->uploading() || !loaded) && rthumb.contains(x, y)) {
+ lnk = (_data->loading() || _data->uploading()) ? _cancell : _savel;
+ return;
+ }
+
+ if (_data->status != FileUploadFailed) {
+ if (rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, _width).contains(x, y)) {
+ lnk = (_data->loading() || _data->uploading()) ? _linkcancell : _linksavel;
+ return;
+ }
+ }
+ } else {
+ bottom = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom();
+
+ QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width));
+ if ((_data->loading() || _data->uploading() || !loaded) && inner.contains(x, y)) {
+ lnk = (_data->loading() || _data->uploading()) ? _cancell : _savel;
+ return;
+ }
+ }
+
+ int32 height = _height;
+ if (!_caption.isEmpty()) {
+ if (y >= bottom) {
+ bool inText = false;
+ _caption.getState(lnk, inText, x - st::msgPadding.left(), y - bottom, _width - st::msgPadding.left() - st::msgPadding.right());
+ state = inText ? HistoryInTextCursorState : HistoryDefaultCursorState;
+ return;
+ }
+ height -= _caption.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
+ }
+ if (x >= 0 && y >= 0 && x < _width && y < height && !_data->loading() && !_data->uploading() && _data->access) {
+ lnk = _openl;
+ return;
}
- return _height;
}
const QString HistoryDocument::inDialogsText() const {
- return _name.isEmpty() ? lang(lng_in_dlg_file) : _name;
+ return (_name.isEmpty() ? lang(lng_in_dlg_file) : _name) + (_caption.isEmpty() ? QString() : (' ' + _caption.original(0, 0xFFFF, Text::ExpandLinksNone)));
}
const QString HistoryDocument::inHistoryText() const {
- return qsl("[ ") + lang(lng_in_dlg_file) + (_name.isEmpty() ? QString() : (qsl(" : ") + _name)) + qsl(" ]");
+ return qsl("[ ") + lang(lng_in_dlg_file) + (_name.isEmpty() ? QString() : (qsl(" : ") + _name)) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]");
}
-bool HistoryDocument::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- if (width >= _maxw) {
- width = _maxw;
+void HistoryDocument::setStatusSize(int32 newSize, qint64 realDuration) const {
+ HistoryFileMedia::setStatusSize(newSize, _data->size, _data->song() ? _data->song()->duration : -1, realDuration);
+
+ if (_statusSize == FileStatusSizeReady) {
+ _link = lang(lng_media_download).toUpper();
+ } else if (_statusSize == FileStatusSizeLoaded) {
+ _link = lang(lng_media_open_with).toUpper();
+ } else if (_statusSize == FileStatusSizeFailed) {
+ _link = lang(lng_media_download).toUpper();
+ } else if (_statusSize >= 0) {
+ _link = lang(lng_media_cancel).toUpper();
+ } else {
+ _link = lang(lng_media_open_with).toUpper();
}
- if (parent == animated.msg) {
- int32 h = (width == w) ? _height : (width * animated.h / animated.w);
- if (h < 1) h = 1;
- return (x >= 0 && y >= 0 && x < width && y < h);
- }
- return (x >= 0 && y >= 0 && x < width && y < _height);
+ _linkw = st::semiboldFont->width(_link);
}
-int32 HistoryDocument::countHeight(const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- if (width >= _maxw) {
- width = _maxw;
+bool HistoryDocument::updateStatusText(const HistoryItem *parent) const {
+ bool showPause = false;
+ int32 statusSize = 0, realDuration = 0;
+ if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
+ statusSize = FileStatusSizeFailed;
+ } else if (_data->status == FileUploading) {
+ statusSize = _data->uploadOffset;
+ } else if (_data->loading()) {
+ statusSize = _data->loadOffset();
+ } else if (_data->loaded()) {
+ if (_data->song()) {
+ SongMsgId playing;
+ AudioPlayerState playingState = AudioPlayerStopped;
+ int64 playingPosition = 0, playingDuration = 0;
+ int32 playingFrequency = 0;
+ if (audioPlayer()) {
+ audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
+ }
+
+ if (playing.msgId == parent->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
+ statusSize = -1 - (playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
+ realDuration = playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency);
+ showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
+ } else {
+ statusSize = FileStatusSizeLoaded;
+ }
+ if (!showPause && playing.msgId == parent->fullId() && App::main() && App::main()->player()->seekingSong(playing)) {
+ showPause = true;
+ }
+ } else {
+ statusSize = FileStatusSizeLoaded;
+ }
+ } else {
+ statusSize = FileStatusSizeReady;
}
- if (parent == animated.msg) {
- int32 h = (width == w) ? _height : (width * animated.h / animated.w);
- if (h < 1) h = 1;
- return h;
+ if (statusSize != _statusSize) {
+ setStatusSize(statusSize, realDuration);
}
- return _height;
+ return showPause;
}
-void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- if (width < 1) return;
-
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed;
- if (width >= _maxw) {
- width = _maxw;
- }
- if (parent == animated.msg) {
- int32 h = (width == w) ? _height : (width * animated.h / animated.w);
- if (h < 1) h = 1;
- lnk = (x >= 0 && y >= 0 && x < width && y < h) ? _openl : TextLinkPtr();
- return;
- }
-
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = (reply || data->song()) ? 0 : toHistoryForwarded(parent);
- int skipy = 0, replyFrom = 0, fwdFrom = 0;
- if (reply) {
- skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (fwd) {
- skipy = st::msgServiceNameFont->height;
- }
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- } else if (fwd) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
-
- if (!outbg) { // draw Download / Save As button
- int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2;
- if (x >= btnx && y >= btny && x < btnx + btnw && y < btny + btnh) {
- lnk = data->loader ? _cancell : _savel;
- return;
- }
- width -= btnw + st::mediaSaveDelta;
- }
-
- if (parent->displayFromName()) {
- if (x >= st::mediaPadding.left() && y >= st::msgPadding.top() && x < width - st::mediaPadding.left() - st::mediaPadding.right() && x < st::mediaPadding.left() + parent->from()->nameText.maxWidth() && y < replyFrom) {
- lnk = parent->from()->lnk;
- return;
- }
- }
- if (reply) {
- if (x >= 0 && y >= replyFrom + st::msgReplyPadding.top() && x < width && y < skipy - st::msgReplyPadding.bottom()) {
- lnk = reply->replyToLink();
- return;
- }
- } else if (fwd) {
- if (y >= fwdFrom && y < skipy) {
- return fwd->getForwardedState(lnk, state, x - st::mediaPadding.left(), width - st::mediaPadding.left() - st::mediaPadding.right());
- }
- }
-
- if (x >= 0 && y >= skipy && x < width && y < _height && !data->loader && data->access) {
- lnk = _openl;
-
- bool inDate = parent->pointInTime(width, _height, x, y, InfoDisplayDefault);
- if (inDate) {
- state = HistoryInDateCursorState;
- }
-
- return;
- }
+void HistoryDocument::regItem(HistoryItem *item) {
+ App::regDocumentItem(_data, item);
}
-HistoryMedia *HistoryDocument::clone() const {
- return new HistoryDocument(*this);
+void HistoryDocument::unregItem(HistoryItem *item) {
+ App::unregDocumentItem(_data, item);
+}
+
+void HistoryDocument::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
+ if (media.type() == mtpc_messageMediaDocument) {
+ App::feedDocument(media.c_messageMediaDocument().vdocument, _data);
+ }
}
ImagePtr HistoryDocument::replyPreview() {
- return data->makeReplyPreview();
+ return _data->makeReplyPreview();
+}
+
+HistoryGif::HistoryGif(DocumentData *document, const QString &caption, const HistoryItem *parent) : HistoryFileMedia()
+, _data(document)
+, _thumbw(1)
+, _thumbh(1)
+, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
+, _gif(0) {
+ setLinks(new GifOpenLink(_data), new GifOpenLink(_data), new DocumentCancelLink(_data));
+
+ setStatusSize(FileStatusSizeReady);
+
+ if (!caption.isEmpty()) {
+ _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent));
+ }
+
+ _data->thumb->load();
+}
+
+HistoryGif::HistoryGif(const HistoryGif &other) : HistoryFileMedia()
+, _parent(0)
+, _data(other._data)
+, _thumbw(other._thumbw)
+, _thumbh(other._thumbh)
+, _gif(0) {
+ setLinks(new GifOpenLink(_data), new GifOpenLink(_data), new DocumentCancelLink(_data));
+
+ setStatusSize(other._statusSize);
+}
+
+void HistoryGif::initDimensions(const HistoryItem *parent) {
+ _parent = parent;
+ if (_caption.hasSkipBlock()) {
+ _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight());
+ }
+
+ bool bubble = parent->hasBubble();
+ int32 tw = 0, th = 0;
+ if (gif() && _gif->state() == ClipError) {
+ if (!_gif->autoplay()) {
+ Ui::showLayer(new InformBox(lang(lng_gif_error)));
+ }
+ App::unregGifItem(_gif);
+ delete _gif;
+ _gif = BadClipReader;
+ }
+
+ if (gif() && _gif->ready()) {
+ tw = convertScale(_gif->width());
+ th = convertScale(_gif->height());
+ } else {
+ tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height());
+ if (!tw || !th) {
+ tw = convertScale(_data->thumb->width());
+ th = convertScale(_data->thumb->height());
+ }
+ }
+ if (tw > st::maxGifSize) {
+ th = (st::maxGifSize * th) / tw;
+ tw = st::maxGifSize;
+ }
+ if (th > st::maxGifSize) {
+ tw = (st::maxGifSize * tw) / th;
+ th = st::maxGifSize;
+ }
+ if (!tw || !th) {
+ tw = th = 1;
+ }
+ _thumbw = tw;
+ _thumbh = th;
+ _maxw = qMax(tw, int32(st::minPhotoSize));
+ _minh = qMax(th, int32(st::minPhotoSize));
+ if (!gif() || !_gif->ready()) {
+ _maxw = qMax(_maxw, parent->infoWidth() + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ _maxw = qMax(_maxw, gifMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ }
+ if (bubble) {
+ _maxw += st::mediaPadding.left() + st::mediaPadding.right();
+ _minh += st::mediaPadding.top() + st::mediaPadding.bottom();
+ if (!_caption.isEmpty()) {
+ _minh += st::mediaCaptionSkip + _caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
+ }
+ }
+}
+
+int32 HistoryGif::resize(int32 width, const HistoryItem *parent) {
+ bool bubble = parent->hasBubble();
+
+ int32 tw = 0, th = 0;
+ if (gif() && _gif->ready()) {
+ tw = convertScale(_gif->width());
+ th = convertScale(_gif->height());
+ } else {
+ tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height());
+ if (!tw || !th) {
+ tw = convertScale(_data->thumb->width());
+ th = convertScale(_data->thumb->height());
+ }
+ }
+ if (tw > st::maxGifSize) {
+ th = (st::maxGifSize * th) / tw;
+ tw = st::maxGifSize;
+ }
+ if (th > st::maxGifSize) {
+ tw = (st::maxGifSize * tw) / th;
+ th = st::maxGifSize;
+ }
+ if (!tw || !th) {
+ tw = th = 1;
+ }
+
+ if (bubble) {
+ width -= st::mediaPadding.left() + st::mediaPadding.right();
+ }
+ if (width < tw) {
+ th = qRound((width / float64(tw)) * th);
+ tw = width;
+ }
+ _thumbw = tw;
+ _thumbh = th;
+
+ _width = qMax(tw, int32(st::minPhotoSize));
+ _height = qMax(th, int32(st::minPhotoSize));
+ if (gif() && _gif->ready()) {
+ if (!_gif->started()) {
+ _gif->start(_thumbw, _thumbh, _width, _height, true);
+ }
+ } else {
+ _width = qMax(_width, parent->infoWidth() + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ _width = qMax(_width, gifMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ }
+ if (bubble) {
+ _width += st::mediaPadding.left() + st::mediaPadding.right();
+ _height += st::mediaPadding.top() + st::mediaPadding.bottom();
+ if (!_caption.isEmpty()) {
+ _height += st::mediaCaptionSkip + _caption.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
+ }
+ }
+
+ return _height;
+}
+
+void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+
+ _data->automaticLoad(parent);
+ bool loaded = _data->loaded(), displayLoading = (parent->id < 0) || _data->displayLoading();
+ if (loaded && !gif() && _gif != BadClipReader && cAutoPlayGif()) {
+ const_cast(this)->playInline(const_cast(parent));
+ if (gif()) _gif->setAutoplay();
+ }
+
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
+ bool bubble = parent->hasBubble();
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
+
+ int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
+
+ bool animating = (gif() && _gif->started());
+
+ if (!animating || parent->id < 0) {
+ if (displayLoading) {
+ ensureAnimation(parent);
+ if (!_animation->radial.animating()) {
+ _animation->radial.start(dataProgress());
+ }
+ }
+ updateStatusText(parent);
+ }
+ bool radial = isRadialAnimation(ms);
+
+ if (bubble) {
+ skipx = st::mediaPadding.left();
+ skipy = st::mediaPadding.top();
+
+ width -= st::mediaPadding.left() + st::mediaPadding.right();
+ height -= skipy + st::mediaPadding.bottom();
+ if (!_caption.isEmpty()) {
+ height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
+ }
+ } else {
+ App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
+ }
+
+ QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
+
+ if (animating) {
+ p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, width, height, (Ui::isLayerShown() || Ui::isMediaViewShown() || Ui::isInlineItemBeingChosen()) ? 0 : ms));
+ } else {
+ p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_thumbw, _thumbh, width, height));
+ }
+ if (selected) {
+ App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
+ }
+
+ if (radial || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif())) || (_gif == BadClipReader)) {
+ float64 radialOpacity = (radial && loaded && parent->id > 0) ? _animation->radial.opacity() : 1;
+ QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
+ p.setPen(Qt::NoPen);
+ if (selected) {
+ p.setBrush(st::msgDateImgBgSelected);
+ } else if (isThumbAnimation(ms)) {
+ float64 over = _animation->a_thumbOver.current();
+ p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over));
+ p.setBrush(st::black);
+ } else {
+ bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel);
+ p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
+ }
+ p.setOpacity(radialOpacity * p.opacity());
+
+ p.setRenderHint(QPainter::HighQualityAntialiasing);
+ p.drawEllipse(inner);
+ p.setRenderHint(QPainter::HighQualityAntialiasing, false);
+
+ p.setOpacity(radialOpacity);
+ style::sprite icon;
+ if (_data->loaded() && !radial) {
+ icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay);
+ } else if (radial || _data->loading()) {
+ if (parent->id > 0 || _data->uploading()) {
+ icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel);
+ }
+ } else {
+ icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload);
+ }
+ if (!icon.isEmpty()) {
+ p.drawSpriteCenter(inner, icon);
+ }
+ if (radial) {
+ p.setOpacity(1);
+ QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
+ _animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg);
+ }
+
+ if (!animating || parent->id < 0) {
+ int32 statusX = skipx + st::msgDateImgDelta + st::msgDateImgPadding.x(), statusY = skipy + st::msgDateImgDelta + st::msgDateImgPadding.y();
+ int32 statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
+ int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
+ App::roundRect(p, rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, _width), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
+ p.setFont(st::normalFont);
+ p.setPen(st::white);
+ p.drawTextLeft(statusX, statusY, _width, _statusText, statusW - 2 * st::msgDateImgPadding.x());
+ }
+ }
+
+ if (!_caption.isEmpty()) {
+ p.setPen(st::black);
+ _caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw);
+ } else if (parent->getMedia() == this) {
+ int32 fullRight = skipx + width, fullBottom = skipy + height;
+ parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage);
+ }
+}
+
+void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
+ bool bubble = parent->hasBubble();
+
+ if (bubble) {
+ skipx = st::mediaPadding.left();
+ skipy = st::mediaPadding.top();
+ if (!_caption.isEmpty()) {
+ int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
+ height -= _caption.countHeight(captionw) + st::msgPadding.bottom();
+ if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
+ bool inText = false;
+ _caption.getState(lnk, inText, x - st::msgPadding.left(), y - height, captionw);
+ state = inText ? HistoryInTextCursorState : HistoryDefaultCursorState;
+ return;
+ }
+ height -= st::mediaCaptionSkip;
+ }
+ width -= st::mediaPadding.left() + st::mediaPadding.right();
+ height -= skipy + st::mediaPadding.bottom();
+ }
+ if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) {
+ if (_data->uploading()) {
+ lnk = _cancell;
+ } else if (!gif() || !cAutoPlayGif()) {
+ lnk = _data->loaded() ? _openl : (_data->loading() ? _cancell : _savel);
+ }
+ if (parent->getMedia() == this) {
+ int32 fullRight = skipx + width, fullBottom = skipy + height;
+ bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage);
+ if (inDate) {
+ state = HistoryInDateCursorState;
+ }
+ }
+ return;
+ }
+}
+
+const QString HistoryGif::inDialogsText() const {
+ return qsl("GIF") + (_caption.isEmpty() ? QString() : (' ' + _caption.original(0, 0xFFFF, Text::ExpandLinksNone)));
+}
+
+const QString HistoryGif::inHistoryText() const {
+ return qsl("[ GIF ") + (_caption.isEmpty() ? QString() : (_caption.original(0, 0xFFFF, Text::ExpandLinksAll) + ' ')) + qsl(" ]");
+}
+
+void HistoryGif::setStatusSize(int32 newSize) const {
+ HistoryFileMedia::setStatusSize(newSize, _data->size, -2, 0);
+}
+
+void HistoryGif::updateStatusText(const HistoryItem *parent) const {
+ bool showPause = false;
+ int32 statusSize = 0, realDuration = 0;
+ if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
+ statusSize = FileStatusSizeFailed;
+ } else if (_data->status == FileUploading) {
+ statusSize = _data->uploadOffset;
+ } else if (_data->loading()) {
+ statusSize = _data->loadOffset();
+ } else if (_data->loaded()) {
+ statusSize = FileStatusSizeLoaded;
+ } else {
+ statusSize = FileStatusSizeReady;
+ }
+ if (statusSize != _statusSize) {
+ setStatusSize(statusSize);
+ }
+}
+
+void HistoryGif::regItem(HistoryItem *item) {
+ App::regDocumentItem(_data, item);
+}
+
+void HistoryGif::unregItem(HistoryItem *item) {
+ App::unregDocumentItem(_data, item);
+}
+
+void HistoryGif::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
+ if (media.type() == mtpc_messageMediaDocument) {
+ App::feedDocument(media.c_messageMediaDocument().vdocument, _data);
+ }
+}
+
+ImagePtr HistoryGif::replyPreview() {
+ return _data->makeReplyPreview();
+}
+
+bool HistoryGif::playInline(HistoryItem *parent) {
+ if (gif()) {
+ stopInline(parent);
+ } else {
+ if (!cAutoPlayGif()) {
+ App::stopGifItems();
+ }
+ _gif = new ClipReader(_data->location(), _data->data(), func(parent, &HistoryItem::clipCallback));
+ App::regGifItem(_gif, parent);
+ }
+ return true;
+}
+
+void HistoryGif::stopInline(HistoryItem *parent) {
+ if (gif()) {
+ App::unregGifItem(_gif);
+ delete _gif;
+ _gif = 0;
+ }
+
+ parent->initDimensions();
+ Notify::historyItemResized(parent);
+ Notify::historyItemLayoutChanged(parent);
+}
+
+HistoryGif::~HistoryGif() {
+ if (gif()) {
+ App::unregGifItem(_gif);
+ deleteAndMark(_gif);
+ }
+}
+
+float64 HistoryGif::dataProgress() const {
+ return (_data->uploading() || !_parent || _parent->id > 0) ? _data->progress() : 0;
+}
+
+bool HistoryGif::dataFinished() const {
+ return (!_parent || _parent->id > 0) ? (!_data->loading() && !_data->uploading()) : false;
+}
+
+bool HistoryGif::dataLoaded() const {
+ return (!_parent || _parent->id > 0) ? _data->loaded() : false;
}
HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia()
-, pixw(1), pixh(1), data(document), lastw(0)
-{
- data->thumb->load();
- if (!data->sticker()->alt.isEmpty()) {
- _emoji = data->sticker()->alt;
- int32 elen = 0;
- if (EmojiPtr e = emojiFromText(_emoji.constData(), _emoji.constEnd(), elen)) {
- _emoji = emojiString(e);
- }
+, _pixw(1)
+, _pixh(1)
+, _data(document)
+, _emoji(_data->sticker()->alt) {
+ _data->thumb->load();
+ if (EmojiPtr e = emojiFromText(_emoji)) {
+ _emoji = emojiString(e);
}
}
void HistorySticker::initDimensions(const HistoryItem *parent) {
- pixw = data->dimensions.width();
- pixh = data->dimensions.height();
- if (pixw > st::maxStickerSize) {
- pixh = (st::maxStickerSize * pixh) / pixw;
- pixw = st::maxStickerSize;
+ _pixw = _data->dimensions.width();
+ _pixh = _data->dimensions.height();
+ if (_pixw > st::maxStickerSize) {
+ _pixh = (st::maxStickerSize * _pixh) / _pixw;
+ _pixw = st::maxStickerSize;
}
- if (pixh > st::maxStickerSize) {
- pixw = (st::maxStickerSize * pixw) / pixh;
- pixh = st::maxStickerSize;
+ if (_pixh > st::maxStickerSize) {
+ _pixw = (st::maxStickerSize * _pixw) / _pixh;
+ _pixh = st::maxStickerSize;
}
- if (pixw < 1) pixw = 1;
- if (pixh < 1) pixh = 1;
- _maxw = qMax(pixw, int16(st::minPhotoSize));
- _minh = qMax(pixh, int16(st::minPhotoSize));
+ if (_pixw < 1) _pixw = 1;
+ if (_pixh < 1) _pixh = 1;
+ _maxw = qMax(_pixw, int16(st::minPhotoSize));
+ _minh = qMax(_pixh, int16(st::minPhotoSize));
if (const HistoryReply *reply = toHistoryReply(parent)) {
_maxw += st::msgReplyPadding.left() + reply->replyToWidth();
}
_height = _minh;
- w = qMin(lastw, _maxw);
}
-void HistorySticker::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const {
- if (width < 0) width = w;
- if (width < 1) return;
- if (width > _maxw) width = _maxw;
+int32 HistorySticker::resize(int32 width, const HistoryItem *parent) { // return new height
+ _width = qMin(width, _maxw);
+ if (const HistoryReply *reply = toHistoryReply(parent)) {
+ int32 usew = _maxw - st::msgReplyPadding.left() - reply->replyToWidth();
+ int32 rw = _width - usew - st::msgReplyPadding.left() - st::msgReplyPadding.left() - st::msgReplyPadding.right();
+ reply->resizeVia(rw);
+ }
+ return _height;
+}
+
+void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+
+ _data->checkSticker();
+ bool loaded = _data->loaded();
bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed;
- bool already = !data->already().isEmpty(), hasdata = !data->data.isEmpty();
int32 usew = _maxw, usex = 0;
const HistoryReply *reply = toHistoryReply(parent);
@@ -4715,65 +4797,68 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, bool selected,
usew -= st::msgReplyPadding.left() + reply->replyToWidth();
if (fromChannel) {
} else if (out) {
- usex = width - usew;
+ usex = _width - usew;
}
}
+ if (rtl()) usex = _width - usex - usew;
- if (!data->loader && data->status != FileFailed && !already && !hasdata) {
- data->save(QString());
- }
- if (data->sticker()->img->isNull() && (already || hasdata)) {
- if (already) {
- data->sticker()->img = ImagePtr(data->already());
- } else {
- data->sticker()->img = ImagePtr(data->data);
- }
- }
if (selected) {
- if (data->sticker()->img->isNull()) {
- p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->thumb->pixBlurredColored(st::msgStickerOverlay, pixw, pixh));
+ if (_data->sticker()->img->isNull()) {
+ p.drawPixmap(QPoint(usex + (usew - _pixw) / 2, (_minh - _pixh) / 2), _data->thumb->pixBlurredColored(st::msgStickerOverlay, _pixw, _pixh));
} else {
- p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->sticker()->img->pixColored(st::msgStickerOverlay, pixw, pixh));
+ p.drawPixmap(QPoint(usex + (usew - _pixw) / 2, (_minh - _pixh) / 2), _data->sticker()->img->pixColored(st::msgStickerOverlay, _pixw, _pixh));
}
} else {
- if (data->sticker()->img->isNull()) {
- p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->thumb->pixBlurred(pixw, pixh));
+ if (_data->sticker()->img->isNull()) {
+ p.drawPixmap(QPoint(usex + (usew - _pixw) / 2, (_minh - _pixh) / 2), _data->thumb->pixBlurred(_pixw, _pixh));
} else {
- p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->sticker()->img->pix(pixw, pixh));
+ p.drawPixmap(QPoint(usex + (usew - _pixw) / 2, (_minh - _pixh) / 2), _data->sticker()->img->pix(_pixw, _pixh));
}
}
- parent->drawInfo(p, usex + usew, _height, selected, InfoDisplayOverImage);
+ if (parent->getMedia() == this) {
+ parent->drawInfo(p, usex + usew, _height, usex * 2 + usew, selected, InfoDisplayOverImage);
- if (reply) {
- int32 rw = width - usew - st::msgReplyPadding.left(), rh = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- int32 rx = fromChannel ? (usew + st::msgReplyPadding.left()) : (out ? 0 : (usew + st::msgReplyPadding.left())), ry = _height - rh;
-
- App::roundRect(p, rx, ry, rw, rh, selected ? App::msgServiceSelectBg() : App::msgServiceBg(), selected ? ServiceSelectedCorners : ServiceCorners);
+ if (reply) {
+ int32 rw = _width - usew - st::msgReplyPadding.left(), rh = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
+ int32 rx = fromChannel ? (usew + st::msgReplyPadding.left()) : (out ? 0 : (usew + st::msgReplyPadding.left())), ry = _height - rh;
+ if (rtl()) rx = _width - rx - rw;
- reply->drawReplyTo(p, rx + st::msgReplyPadding.left(), ry, rw - st::msgReplyPadding.left() - st::msgReplyPadding.right(), selected, true);
+ App::roundRect(p, rx, ry, rw, rh, selected ? App::msgServiceSelectBg() : App::msgServiceBg(), selected ? ServiceSelectedCorners : ServiceCorners);
+
+ reply->drawReplyTo(p, rx + st::msgReplyPadding.left(), ry, rw - st::msgReplyPadding.left() - st::msgReplyPadding.right(), selected, true);
+ }
}
}
-int32 HistorySticker::resize(int32 width, const HistoryItem *parent) {
- w = qMin(width, _maxw);
- lastw = width;
- return _height;
-}
+void HistorySticker::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
-void HistorySticker::regItem(HistoryItem *item) {
- App::regDocumentItem(data, item);
-}
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
-void HistorySticker::unregItem(HistoryItem *item) {
- App::unregDocumentItem(data, item);
-}
-
-void HistorySticker::updateFrom(const MTPMessageMedia &media) {
- if (media.type() == mtpc_messageMediaDocument) {
- App::feedDocument(media.c_messageMediaDocument().vdocument, data);
- if (!data->data.isEmpty()) {
- Local::writeStickerImage(mediaKey(mtpToLocationType(mtpc_inputDocumentFileLocation), data->dc, data->id), data->data);
+ int32 usew = _maxw, usex = 0;
+ const HistoryReply *reply = toHistoryReply(parent);
+ if (reply) {
+ usew -= reply->replyToWidth();
+ if (fromChannel) {
+ } else if (out) {
+ usex = _width - usew;
+ }
+ }
+ if (rtl()) usex = _width - usex - usew;
+ if (reply) {
+ int32 rw = _width - usew, rh = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
+ int32 rx = fromChannel ? (usew + st::msgReplyPadding.left()) : (out ? 0 : (usew + st::msgReplyPadding.left())), ry = _height - rh;
+ if (rtl()) rx = _width - rx - rw;
+ if (x >= rx && y >= ry && x < rx + rw && y < ry + rh) {
+ lnk = reply->replyToLink();
+ return;
+ }
+ }
+ if (parent->getMedia() == this) {
+ bool inDate = parent->pointInTime(usex + usew, _height, x, y, InfoDisplayOverImage);
+ if (inDate) {
+ state = HistoryInDateCursorState;
}
}
}
@@ -4786,546 +4871,555 @@ const QString HistorySticker::inHistoryText() const {
return qsl("[ ") + inDialogsText() + qsl(" ]");
}
-bool HistorySticker::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- return (x >= 0 && y >= 0 && x < _maxw && y < _minh);
+void HistorySticker::regItem(HistoryItem *item) {
+ App::regDocumentItem(_data, item);
}
-int32 HistorySticker::countHeight(const HistoryItem *parent, int32 width) const {
- return _minh;
+void HistorySticker::unregItem(HistoryItem *item) {
+ App::unregDocumentItem(_data, item);
}
-void HistorySticker::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- if (width < 1) return;
-
- if (width > _maxw) width = _maxw;
-
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
-
- int32 usew = _maxw, usex = 0;
- const HistoryReply *reply = toHistoryReply(parent);
- if (reply) {
- usew -= reply->replyToWidth();
- if (fromChannel) {
- } else if (out) {
- usex = width - usew;
- }
-
- int32 rw = width - usew, rh = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- int32 rx = fromChannel ? (usew + st::msgReplyPadding.left()) : (out ? 0 : (usew + st::msgReplyPadding.left())), ry = _height - rh;
- if (x >= rx && y >= ry && x < rx + rw && y < ry + rh) {
- lnk = reply->replyToLink();
- return;
+void HistorySticker::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
+ if (media.type() == mtpc_messageMediaDocument) {
+ App::feedDocument(media.c_messageMediaDocument().vdocument, _data);
+ if (!_data->data().isEmpty()) {
+ Local::writeStickerImage(mediaKey(DocumentFileLocation, _data->dc, _data->id), _data->data());
}
}
- bool inDate = parent->pointInTime(usex + usew, _height, x, y, InfoDisplayOverImage);
- if (inDate) {
- state = HistoryInDateCursorState;
+}
+
+void SendMessageLink::onClick(Qt::MouseButton button) const {
+ if (button == Qt::LeftButton) {
+ Ui::showPeerHistory(peer()->id, ShowAtUnreadMsgId);
}
}
-HistoryMedia *HistorySticker::clone() const {
- return new HistorySticker(*this);
-}
-
-HistoryContact::HistoryContact(int32 userId, const QString &first, const QString &last, const QString &phone) : HistoryMedia(0)
-, userId(userId)
-, phone(App::formatPhone(phone))
-, contact(App::userLoaded(userId))
-{
- App::regSharedContactPhone(userId, phone);
-
- _maxw = st::mediaMaxWidth;
- name.setText(st::mediaFont, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions);
-
- phonew = st::mediaFont->width(phone);
-
- if (contact) {
- contact->photo->load();
+void AddContactLink::onClick(Qt::MouseButton button) const {
+ if (button == Qt::LeftButton) {
+ if (HistoryItem *item = App::histItemById(peerToChannel(peer()), msgid())) {
+ if (HistoryMedia *media = item->getMedia()) {
+ if (media->type() == MediaTypeContact) {
+ QString fname = static_cast(media)->fname();
+ QString lname = static_cast(media)->lname();
+ QString phone = static_cast(media)->phone();
+ Ui::showLayer(new AddContactBox(fname, lname, phone));
+ }
+ }
+ }
}
}
-HistoryContact::HistoryContact(int32 userId, const QString &fullname, const QString &phone) : HistoryMedia(0)
-, userId(userId)
-, phone(App::formatPhone(phone))
-, contact(App::userLoaded(userId))
-{
- App::regSharedContactPhone(userId, phone);
+HistoryContact::HistoryContact(int32 userId, const QString &first, const QString &last, const QString &phone) : HistoryMedia()
+, _userId(userId)
+, _contact(0)
+, _phonew(0)
+, _fname(first)
+, _lname(last)
+, _phone(App::formatPhone(phone))
+, _linkw(0) {
+ _name.setText(st::semiboldFont, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions);
- _maxw = st::mediaMaxWidth;
- name.setText(st::mediaFont, fullname.trimmed(), _textNameOptions);
-
- phonew = st::mediaFont->width(phone);
-
- if (contact) {
- contact->photo->load();
- }
+ _phonew = st::normalFont->width(_phone);
}
void HistoryContact::initDimensions(const HistoryItem *parent) {
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- int32 fullInfoWidth = parent->skipBlockWidth() + st::msgPadding.right();
- if (name.maxWidth() + tleft + fullInfoWidth > _maxw) {
- _maxw = name.maxWidth() + tleft + fullInfoWidth;
+ _maxw = st::msgFileMinWidth;
+
+ _contact = _userId ? App::userLoaded(_userId) : 0;
+ if (_contact) {
+ _contact->photo->load();
}
- if (phonew + tleft + st::mediaPadding.right() > _maxw) {
- _maxw = phonew + tleft + st::mediaPadding.right();
+ if (_contact && _contact->contact > 0) {
+ _linkl.reset(new SendMessageLink(_contact));
+ _link = lang(lng_profile_send_message).toUpper();
+ } else if (_userId) {
+ _linkl.reset(new AddContactLink(parent->history()->peer->id, parent->id));
+ _link = lang(lng_profile_add_contact).toUpper();
}
- _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom();
- if (parent->displayFromName()) {
- _minh += st::msgPadding.top() + st::msgNameFont->height;
+ _linkw = _link.isEmpty() ? 0 : st::semiboldFont->width(_link);
+
+ int32 tleft = 0, tright = 0;
+ if (_userId) {
+ tleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right();
+ tright = st::msgFileThumbPadding.left();
+ _maxw = qMax(_maxw, tleft + _phonew + tright);
+ } else {
+ tleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right();
+ tright = st::msgFileThumbPadding.left();
+ _maxw = qMax(_maxw, tleft + _phonew + parent->skipBlockWidth() + st::msgPadding.right());
}
- if (const HistoryReply *reply = toHistoryReply(parent)) {
- _minh += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (const HistoryForwarded *fwd = toHistoryForwarded(parent)) {
- if (!parent->displayFromName()) {
- _minh += st::msgPadding.top();
- }
- _minh += st::msgServiceNameFont->height;
+
+ _maxw = qMax(tleft + _name.maxWidth() + tright, _maxw);
+ _maxw = qMin(_maxw, int(st::msgMaxWidth));
+
+ if (_userId) {
+ _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom();
+ } else {
+ _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom();
}
_height = _minh;
}
+void HistoryContact::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
+
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
+
+ if (width >= _maxw) {
+ width = _maxw;
+ }
+
+ int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0;
+ if (_userId) {
+ nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right();
+ nametop = st::msgFileThumbNameTop;
+ nameright = st::msgFileThumbPadding.left();
+ statustop = st::msgFileThumbStatusTop;
+ linktop = st::msgFileThumbLinkTop;
+
+ QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width));
+ if (_contact && _contact->photo->loaded()) {
+ QPixmap thumb = _contact->photo->pixRounded(st::msgFileThumbSize, st::msgFileThumbSize);
+ p.drawPixmap(rthumb.topLeft(), thumb);
+ } else {
+ p.drawPixmap(rthumb.topLeft(), userDefPhoto(_contact ? _contact->colorIndex : (qAbs(_userId) % UserColorsCount))->pixRounded(st::msgFileThumbSize, st::msgFileThumbSize));
+ }
+ if (selected) {
+ App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
+ }
+
+ bool over = textlnkDrawOver(_linkl);
+ p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont);
+ p.setPen(outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg));
+ p.drawTextLeft(nameleft, linktop, width, _link, _linkw);
+ } else {
+ nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right();
+ nametop = st::msgFileNameTop;
+ nameright = st::msgFilePadding.left();
+ statustop = st::msgFileStatusTop;
+
+ QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width));
+ p.drawPixmap(inner.topLeft(), userDefPhoto(qAbs(parent->id) % UserColorsCount)->pixRounded(st::msgFileSize, st::msgFileSize));
+ }
+ int32 namewidth = width - nameleft - nameright;
+
+ p.setFont(st::semiboldFont);
+ p.setPen(st::black);
+ _name.drawLeftElided(p, nameleft, nametop, namewidth, width);
+
+ style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg));
+ p.setFont(st::normalFont);
+ p.setPen(status);
+ p.drawTextLeft(nameleft, statustop, width, _phone);
+}
+
+void HistoryContact::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const {
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
+
+ int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0;
+ if (_userId) {
+ nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right();
+ linktop = st::msgFileThumbLinkTop;
+ if (rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, _width).contains(x, y)) {
+ lnk = _linkl;
+ return;
+ }
+ }
+ if (x >= 0 && y >= 0 && x < _width && y < _height && _contact) {
+ lnk = _contact->lnk;
+ return;
+ }
+}
+
const QString HistoryContact::inDialogsText() const {
return lang(lng_in_dlg_contact);
}
const QString HistoryContact::inHistoryText() const {
- return qsl("[ ") + lang(lng_in_dlg_contact) + qsl(" : ") + name.original() + qsl(", ") + phone + qsl(" ]");
+ return qsl("[ ") + lang(lng_in_dlg_contact) + qsl(" : ") + _name.original() + qsl(", ") + _phone + qsl(" ]");
}
-bool HistoryContact::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- return (x >= 0 && y <= 0 && x < w && y < _height);
-}
-
-void HistoryContact::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
-
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
- int skipy = 0, replyFrom = 0, fwdFrom = 0;
- if (reply) {
- skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (fwd) {
- skipy = st::msgServiceNameFont->height;
- }
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- } else if (fwd) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
-
- if (parent->displayFromName()) {
- if (x >= st::mediaPadding.left() && y >= st::msgPadding.top() && x < width - st::mediaPadding.left() - st::mediaPadding.right() && x < st::mediaPadding.left() + parent->from()->nameText.maxWidth() && y < replyFrom) {
- lnk = parent->from()->lnk;
- return;
- }
- }
- if (reply) {
- if (x >= 0 && y >= replyFrom + st::msgReplyPadding.top() && x < width && y < skipy - st::msgReplyPadding.bottom()) {
- lnk = reply->replyToLink();
- return;
- }
- } else if (fwd) {
- if (y >= fwdFrom && y < skipy) {
- return fwd->getForwardedState(lnk, state, x - st::mediaPadding.left(), width - st::mediaPadding.left() - st::mediaPadding.right());
- }
- }
-
- if (x >= 0 && y >= skipy && x < width && y < _height && contact) {
- lnk = contact->lnk;
-
- bool inDate = parent->pointInTime(width, _height, x, y, InfoDisplayDefault);
- if (inDate) {
- state = HistoryInDateCursorState;
- }
-
- return;
+void HistoryContact::regItem(HistoryItem *item) {
+ if (_userId) {
+ App::regSharedContactItem(_userId, item);
}
}
-HistoryMedia *HistoryContact::clone() const {
- return new HistoryContact(userId, name.original(), phone);
+void HistoryContact::unregItem(HistoryItem *item) {
+ if (_userId) {
+ App::unregSharedContactItem(_userId, item);
+ }
}
-void HistoryContact::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const {
- if (width < 0) width = w;
- if (width < 1) return;
-
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
- int skipy = 0, replyFrom = 0, fwdFrom = 0;
- if (reply) {
- skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (fwd) {
- skipy = st::msgServiceNameFont->height;
- }
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- } else if (fwd) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
-
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
- if (width >= _maxw) {
- width = _maxw;
- }
-
- style::color bg(selected ? (outbg ? st::msgOutSelectBg : st::msgInSelectBg) : (outbg ? st::msgOutBg : st::msgInBg));
- style::color sh(selected ? (outbg ? st::msgOutSelectShadow : st::msgInSelectShadow) : (outbg ? st::msgOutShadow : st::msgInShadow));
- RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners));
- App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
-
- if (parent->displayFromName()) {
- p.setFont(st::msgNameFont->f);
- if (fromChannel) {
- p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor);
- } else {
- p.setPen(parent->from()->color);
- }
- parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right());
- }
- if (reply) {
- reply->drawReplyTo(p, st::msgReplyPadding.left(), replyFrom, width - st::msgReplyPadding.left() - st::msgReplyPadding.right(), selected);
- } else if (fwd) {
- fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
- }
-
- p.drawPixmap(st::mediaPadding.left(), skipy + st::mediaPadding.top(), (contact ? contact->photo : userDefPhoto(1))->pixRounded(st::mediaThumbSize));
- if (selected) {
- App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
- }
-
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- int32 twidth = width - tleft - st::mediaPadding.right();
- int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth();
-
- p.setFont(st::mediaFont->f);
- p.setPen(st::black->c);
- if (twidth < phonew) {
- p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, st::mediaFont->elided(phone, twidth));
- } else {
- p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, phone);
- }
-
- style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor));
- p.setPen(status->p);
-
- name.drawElided(p, tleft, skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->height, secondwidth);
-
- int32 fullRight = width, fullBottom = _height;
- parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayDefault);
-}
-
-void HistoryContact::updateFrom(const MTPMessageMedia &media) {
+void HistoryContact::updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
if (media.type() == mtpc_messageMediaContact) {
- userId = media.c_messageMediaContact().vuser_id.v;
- contact = App::userLoaded(userId);
- if (contact) {
- if (contact->phone.isEmpty()) {
- contact->setPhone(phone);
- }
- if (contact->contact < 0) {
- contact->contact = 0;
- }
- contact->photo->load();
+ if (_userId != media.c_messageMediaContact().vuser_id.v) {
+ unregItem(parent);
+ _userId = media.c_messageMediaContact().vuser_id.v;
+ regItem(parent);
}
}
}
+namespace {
+ QString siteNameFromUrl(const QString &url) {
+ QUrl u(url);
+ QString pretty = u.isValid() ? u.toDisplayString() : url;
+ QRegularExpressionMatch m = QRegularExpression(qsl("^[a-zA-Z0-9]+://")).match(pretty);
+ if (m.hasMatch()) pretty = pretty.mid(m.capturedLength());
+ int32 slash = pretty.indexOf('/');
+ if (slash > 0) pretty = pretty.mid(0, slash);
+ QStringList components = pretty.split('.', QString::SkipEmptyParts);
+ if (components.size() >= 2) {
+ components = components.mid(components.size() - 2);
+ return components.at(0).at(0).toUpper() + components.at(0).mid(1) + '.' + components.at(1);
+ }
+ return QString();
+ }
+
+ int32 articleThumbWidth(PhotoData *thumb, int32 height) {
+ int32 w = thumb->medium->width(), h = thumb->medium->height();
+ return qMax(qMin(height * w / h, height), 1);
+ }
+
+ int32 articleThumbHeight(PhotoData *thumb, int32 width) {
+ return qMax(thumb->medium->height() * width / thumb->medium->width(), 1);
+ }
+
+ int32 _lineHeight = 0;
+}
+
HistoryWebPage::HistoryWebPage(WebPageData *data) : HistoryMedia()
-, data(data)
+, _data(data)
, _openl(0)
-, _attachl(0)
+, _attach(0)
, _asArticle(false)
, _title(st::msgMinWidth - st::webPageLeft)
, _description(st::msgMinWidth - st::webPageLeft)
, _siteNameWidth(0)
, _durationWidth(0)
-, _docNameWidth(0)
-, _docThumbWidth(0)
-, _docDownloadDone(0)
-, _pixw(0), _pixh(0)
-{
+, _pixw(0)
+, _pixh(0) {
+}
+
+HistoryWebPage::HistoryWebPage(const HistoryWebPage &other) : HistoryMedia()
+, _data(other._data)
+, _openl(0)
+, _attach(other._attach ? other._attach->clone() : 0)
+, _asArticle(other._asArticle)
+, _title(other._title)
+, _description(other._description)
+, _siteNameWidth(other._siteNameWidth)
+, _durationWidth(other._durationWidth)
+, _pixw(other._pixw)
+, _pixh(other._pixh) {
}
void HistoryWebPage::initDimensions(const HistoryItem *parent) {
- if (data->pendingTill) {
+ if (_data->pendingTill) {
_maxw = _minh = _height = 0;
- //_maxw = st::webPageLeft + st::linkFont->width(lang((data->pendingTill < 0) ? lng_attach_failed : lng_profile_loading));
- //_minh = st::replyHeight;
- //_height = _minh;
return;
}
- if (!_openl && !data->url.isEmpty()) _openl = TextLinkPtr(new TextLink(data->url));
- if (!_attachl && data->photo && data->type != WebPageVideo) _attachl = TextLinkPtr(new PhotoLink(data->photo));
- if (!_attachl && data->doc) _attachl = TextLinkPtr(new DocumentOpenLink(data->doc));
- if (data->photo && data->type != WebPagePhoto && data->type != WebPageVideo) {
- if (data->type == WebPageProfile) {
+ if (!_lineHeight) _lineHeight = qMax(st::webPageTitleFont->height, st::webPageDescriptionFont->height);
+
+ if (!_openl && !_data->url.isEmpty()) _openl = TextLinkPtr(new TextLink(_data->url));
+
+ // init layout
+ QString title(_data->title.isEmpty() ? _data->author : _data->title);
+ if (!_data->description.isEmpty() && title.isEmpty() && _data->siteName.isEmpty() && !_data->url.isEmpty()) {
+ _data->siteName = siteNameFromUrl(_data->url);
+ }
+ if (!_data->doc && _data->photo && _data->type != WebPagePhoto && _data->type != WebPageVideo) {
+ if (_data->type == WebPageProfile) {
_asArticle = true;
- } else if (data->siteName == qstr("Twitter") || data->siteName == qstr("Facebook")) {
+ } else if (_data->siteName == qstr("Twitter") || _data->siteName == qstr("Facebook")) {
_asArticle = false;
} else {
_asArticle = true;
}
+ if (_asArticle && (_data->description.isEmpty() || (title.isEmpty() && _data->siteName.isEmpty()))) {
+ _asArticle = false;
+ }
} else {
_asArticle = false;
}
- if (_asArticle) {
- w = st::webPagePhotoSize;
- _maxw = st::webPageLeft + st::webPagePhotoSize;
- _minh = st::webPagePhotoSize;
- _minh += st::webPagePhotoSkip + (st::msgDateFont->height - st::msgDateDelta.y());
- } else if (data->photo) {
- int32 tw = convertScale(data->photo->full->width()), th = convertScale(data->photo->full->height());
- if (!tw || !th) {
- tw = th = 1;
- }
- if (tw > st::maxMediaSize) {
- th = (st::maxMediaSize * th) / tw;
- tw = st::maxMediaSize;
- }
- if (th > st::maxMediaSize) {
- tw = (st::maxMediaSize * tw) / th;
- th = st::maxMediaSize;
- }
- int32 thumbw = tw;
- int32 thumbh = th;
-
- w = thumbw;
-
- _maxw = st::webPageLeft + qMax(thumbh, qMax(w, int32(st::minPhotoSize))) + parent->skipBlockWidth();
- _minh = qMax(thumbh, int32(st::minPhotoSize));
- _minh += st::webPagePhotoSkip;
- } else if (data->doc) {
- if (!data->doc->thumb->isNull()) {
- data->doc->thumb->load();
-
- int32 tw = data->doc->thumb->width(), th = data->doc->thumb->height();
- if (data->doc->thumb->isNull() || !tw || !th) {
- _docThumbWidth = 0;
- } else if (tw > th) {
- _docThumbWidth = (tw * st::mediaThumbSize) / th;
+ // init attach
+ if (!_asArticle && !_attach) {
+ if (_data->doc) {
+ if (_data->doc->sticker()) {
+ _attach = new HistorySticker(_data->doc);
+ } else if (_data->doc->isAnimation()) {
+ _attach = new HistoryGif(_data->doc, QString(), parent);
} else {
- _docThumbWidth = st::mediaThumbSize;
+ _attach = new HistoryDocument(_data->doc, QString(), parent);
}
+ } else if (_data->photo) {
+ _attach = new HistoryPhoto(_data->photo, QString(), parent);
}
- _docName = documentName(data->doc);
- _docSize = data->doc->song() ? formatDurationAndSizeText(data->doc->song()->duration, data->doc->size) : formatSizeText(data->doc->size);
- _docNameWidth = st::mediaFont->width(_docName.isEmpty() ? qsl("Document") : _docName);
-
- if (parent == animated.msg) {
- _maxw = st::webPageLeft + (animated.w / cIntRetinaFactor()) + parent->skipBlockWidth();
- _minh = animated.h / cIntRetinaFactor();
- _minh += st::webPagePhotoSkip;
- } else {
- _maxw = qMax(st::webPageLeft + st::mediaThumbSize + st::mediaPadding.right() + _docNameWidth + st::mediaPadding.right(), st::mediaMaxWidth);
- _minh = st::mediaThumbSize;
- }
- } else {
- _maxw = st::webPageLeft;
- _minh = 0;
}
- if (!data->siteName.isEmpty()) {
- _siteNameWidth = st::webPageTitleFont->width(data->siteName);
- if (_asArticle) {
- _maxw = qMax(_maxw, int32(st::webPageLeft + _siteNameWidth + st::webPagePhotoDelta + st::webPagePhotoSize));
+ // init strings
+ if (_description.isEmpty() && !_data->description.isEmpty()) {
+ QString text = textClean(_data->description);
+ if (text.isEmpty()) {
+ _data->description = QString();
} else {
- _maxw = qMax(_maxw, int32(st::webPageLeft + _siteNameWidth + parent->skipBlockWidth()));
- _minh += st::webPageTitleFont->height;
+ if (!_asArticle && !_attach) {
+ text += parent->skipBlock();
+ }
+ const TextParseOptions *opts = &_webpageDescriptionOptions;
+ if (_data->siteName == qstr("Twitter")) {
+ opts = &_twitterDescriptionOptions;
+ } else if (_data->siteName == qstr("Instagram")) {
+ opts = &_instagramDescriptionOptions;
+ }
+ _description.setText(st::webPageDescriptionFont, text, *opts);
}
}
- QString title(data->title.isEmpty() ? data->author : data->title);
- if (!title.isEmpty()) {
- title = textClean(title);
- if (!_asArticle && !data->photo && data->description.isEmpty()) title += parent->skipBlock();
- _title.setText(st::webPageTitleFont, title, _webpageTitleOptions);
- if (_asArticle) {
- _maxw = qMax(_maxw, int32(st::webPageLeft + _title.maxWidth() + st::webPagePhotoDelta + st::webPagePhotoSize));
+ if (_title.isEmpty() && !title.isEmpty()) {
+ title = textOneLine(textClean(title));
+ if (title.isEmpty()) {
+ if (_data->title.isEmpty()) {
+ _data->author = QString();
+ } else {
+ _data->title = QString();
+ }
} else {
- _maxw = qMax(_maxw, int32(st::webPageLeft + _title.maxWidth()));
- _minh += qMin(_title.minHeight(), 2 * st::webPageTitleFont->height);
+ if (!_asArticle && !_attach && _description.isEmpty()) {
+ title += parent->skipBlock();
+ }
+ _title.setText(st::webPageTitleFont, title, _webpageTitleOptions);
}
}
- if (!data->description.isEmpty()) {
- QString text = textClean(data->description);
- if (!_asArticle && !data->photo) text += parent->skipBlock();
- const TextParseOptions *opts = &_webpageDescriptionOptions;
- if (data->siteName == qstr("Twitter")) {
- opts = &_twitterDescriptionOptions;
- } else if (data->siteName == qstr("Instagram")) {
- opts = &_instagramDescriptionOptions;
- }
- _description.setText(st::webPageDescriptionFont, text, *opts);
- if (_asArticle) {
- _maxw = qMax(_maxw, int32(st::webPageLeft + _description.maxWidth() + st::webPagePhotoDelta + st::webPagePhotoSize));
+ if (!_siteNameWidth && !_data->siteName.isEmpty()) {
+ _siteNameWidth = st::webPageTitleFont->width(_data->siteName);
+ }
+
+ // init dimensions
+ int32 l = st::msgPadding.left() + st::webPageLeft, r = st::msgPadding.right();
+ int32 skipBlockWidth = parent->skipBlockWidth();
+ _maxw = skipBlockWidth;
+ _minh = 0;
+
+ int32 siteNameHeight = _data->siteName.isEmpty() ? 0 : _lineHeight;
+ int32 titleMinHeight = _title.isEmpty() ? 0 : _lineHeight;
+ int32 descMaxLines = (3 + (siteNameHeight ? 0 : 1) + (titleMinHeight ? 0 : 1));
+ int32 descriptionMinHeight = _description.isEmpty() ? 0 : qMin(_description.minHeight(), descMaxLines * _lineHeight);
+ int32 articleMinHeight = siteNameHeight + titleMinHeight + descriptionMinHeight;
+ int32 articlePhotoMaxWidth = 0;
+ if (_asArticle) {
+ articlePhotoMaxWidth = st::webPagePhotoDelta + qMax(articleThumbWidth(_data->photo, articleMinHeight), _lineHeight);
+ }
+
+ if (_siteNameWidth) {
+ if (_title.isEmpty() && _description.isEmpty()) {
+ _maxw = qMax(_maxw, int32(_siteNameWidth + parent->skipBlockWidth()));
} else {
- _maxw = qMax(_maxw, int32(st::webPageLeft + _description.maxWidth()));
- _minh += qMin(_description.minHeight(), 3 * st::webPageTitleFont->height);
+ _maxw = qMax(_maxw, int32(_siteNameWidth + articlePhotoMaxWidth));
}
+ _minh += _lineHeight;
}
- if (!_asArticle && (data->photo || data->doc) && (_siteNameWidth || !_title.isEmpty() || !_description.isEmpty())) {
- _minh += st::webPagePhotoSkip;
+ if (!_title.isEmpty()) {
+ _maxw = qMax(_maxw, int32(_title.maxWidth() + articlePhotoMaxWidth));
+ _minh += titleMinHeight;
}
- if (data->type == WebPageVideo && data->duration) {
- _duration = formatDurationText(data->duration);
+ if (!_description.isEmpty()) {
+ _maxw = qMax(_maxw, int32(_description.maxWidth() + articlePhotoMaxWidth));
+ _minh += descriptionMinHeight;
+ }
+ if (_attach) {
+ if (_minh) _minh += st::webPagePhotoSkip;
+ _attach->initDimensions(parent);
+ QMargins bubble(_attach->bubbleMargins());
+ _maxw = qMax(_maxw, int32(_attach->maxWidth() - bubble.left() - bubble.top() + (_attach->customInfoLayout() ? skipBlockWidth : 0)));
+ _minh += _attach->minHeight() - bubble.top() - bubble.bottom();
+ }
+ if (_data->type == WebPageVideo && _data->duration) {
+ _duration = formatDurationText(_data->duration);
_durationWidth = st::msgDateFont->width(_duration);
}
- _height = _minh;
+ _maxw += st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
+ _minh += st::msgPadding.bottom();
+ if (_asArticle) {
+ _minh = resize(_maxw, parent); // hack
+// _minh += st::msgDateFont->height;
+ }
}
-void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const {
- if (width < 0) width = w;
- if (width < 1 || data->pendingTill) return;
-
- int16 animw = 0, animh = 0;
- if (data->doc && animated.msg == parent) {
- animw = animated.w / cIntRetinaFactor();
- animh = animated.h / cIntRetinaFactor();
- if (width - st::webPageLeft < animw) {
- animw = width - st::webPageLeft;
- animh = (animw * animated.h / animated.w);
- if (animh < 1) animh = 1;
- }
+int32 HistoryWebPage::resize(int32 width, const HistoryItem *parent) {
+ if (_data->pendingTill) {
+ _width = width;
+ _height = _minh;
+ return _height;
}
- int32 bottomSkip = 0;
- if (data->photo) {
- bottomSkip += st::webPagePhotoSkip;
- if (_asArticle || (st::webPageLeft + qMax(_pixw, int16(st::minPhotoSize)) + parent->skipBlockWidth() > width)) {
- bottomSkip += (st::msgDateFont->height - st::msgDateDelta.y());
+ _width = qMin(width, _maxw);
+ width -= st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
+
+ int32 linesMax = 5;
+ int32 siteNameLines = _siteNameWidth ? 1 : 0, siteNameHeight = _siteNameWidth ? _lineHeight : 0;
+ if (_asArticle) {
+ _pixh = linesMax * _lineHeight;
+ do {
+ _pixw = articleThumbWidth(_data->photo, _pixh);
+ int32 wleft = width - st::webPagePhotoDelta - qMax(_pixw, int16(_lineHeight));
+
+ _height = siteNameHeight;
+
+ if (_title.isEmpty()) {
+ _titleLines = 0;
+ } else {
+ if (_title.countHeight(wleft) < 2 * st::webPageTitleFont->height) {
+ _titleLines = 1;
+ } else {
+ _titleLines = 2;
+ }
+ _height += _titleLines * _lineHeight;
+ }
+
+ int32 descriptionHeight = _description.countHeight(wleft);
+ if (descriptionHeight < (linesMax - siteNameLines - _titleLines) * st::webPageDescriptionFont->height) {
+ _descriptionLines = (descriptionHeight / st::webPageDescriptionFont->height);
+ } else {
+ _descriptionLines = (linesMax - siteNameLines - _titleLines);
+ }
+ _height += _descriptionLines * _lineHeight;
+
+ if (_height >= _pixh) {
+ break;
+ }
+
+ _pixh -= _lineHeight;
+ } while (_pixh > _lineHeight);
+ _height += st::msgDateFont->height;
+ } else {
+ _height = siteNameHeight;
+
+ if (_title.isEmpty()) {
+ _titleLines = 0;
+ } else {
+ if (_title.countHeight(width) < 2 * st::webPageTitleFont->height) {
+ _titleLines = 1;
+ } else {
+ _titleLines = 2;
+ }
+ _height += _titleLines * _lineHeight;
}
- } else if (data->doc && animated.msg == parent) {
- bottomSkip += st::webPagePhotoSkip;
- if (st::webPageLeft + qMax(animw, int16(st::minPhotoSize)) + parent->skipBlockWidth() > width) {
- bottomSkip += (st::msgDateFont->height - st::msgDateDelta.y());
+
+ if (_description.isEmpty()) {
+ _descriptionLines = 0;
+ } else {
+ int32 descriptionHeight = _description.countHeight(width);
+ if (descriptionHeight < (linesMax - siteNameLines - _titleLines) * st::webPageDescriptionFont->height) {
+ _descriptionLines = (descriptionHeight / st::webPageDescriptionFont->height);
+ } else {
+ _descriptionLines = (linesMax - siteNameLines - _titleLines);
+ }
+ _height += _descriptionLines * _lineHeight;
+ }
+
+ if (_attach) {
+ if (_height) _height += st::webPagePhotoSkip;
+
+ QMargins bubble(_attach->bubbleMargins());
+
+ _attach->resize(width + bubble.left() + bubble.right(), parent);
+ _height += _attach->height() - bubble.top() - bubble.bottom();
+ if (_attach->customInfoLayout() && _attach->currentWidth() + parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
+ _height += st::msgDateFont->height;
+ }
}
}
+ _height += st::msgPadding.bottom();
+
+ return _height;
+}
+
+void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
- style::color bar = (selected ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor));
- style::color semibold = (selected ? (outbg ? st::msgOutServiceSelColor : st::msgInServiceSelColor) : (outbg ? st::msgOutServiceColor : st::msgInServiceColor));
- style::color regular = (selected ? (outbg ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (outbg ? st::msgOutDateColor : st::msgInDateColor));
- p.fillRect(0, 0, st::webPageBar, _height - bottomSkip, bar->b);
+ style::color barfg = (selected ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor));
+ style::color semibold = (selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
+ style::color regular = (selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
- p.save();
- p.translate(st::webPageLeft, 0);
+ int32 lshift = st::msgPadding.left() + st::webPageLeft, rshift = st::msgPadding.right(), bshift = st::msgPadding.bottom();
+ width -= lshift + rshift;
+ QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
+ if (_asArticle || (_attach && _attach->customInfoLayout() && _attach->currentWidth() + parent->skipBlockWidth() > width + bubble.left() + bubble.right())) {
+ bshift += st::msgDateFont->height;
+ }
- width -= st::webPageLeft;
+ QRect bar(rtlrect(st::msgPadding.left(), 0, st::webPageBar, _height - bshift, _width));
+ p.fillRect(bar, barfg);
if (_asArticle) {
- int32 pixwidth = st::webPagePhotoSize, pixheight = st::webPagePhotoSize;
- data->photo->medium->load(false, false);
- bool full = data->photo->medium->loaded();
+ _data->photo->medium->load(false, false);
+ bool full = _data->photo->medium->loaded();
QPixmap pix;
+ int32 pw = qMax(_pixw, int16(_lineHeight)), ph = _pixh;
+ int32 pixw = _pixw, pixh = articleThumbHeight(_data->photo, _pixw);
+ int32 maxw = convertScale(_data->photo->medium->width()), maxh = convertScale(_data->photo->medium->height());
+ if (pixw * ph != pixh * pw) {
+ float64 coef = (pixw * ph > pixh * pw) ? qMin(ph / float64(pixh), maxh / float64(pixh)) : qMin(pw / float64(pixw), maxw / float64(pixw));
+ pixh = qRound(pixh * coef);
+ pixw = qRound(pixw * coef);
+ }
if (full) {
- pix = data->photo->medium->pixSingle(_pixw, _pixh, pixwidth, pixheight);
+ pix = _data->photo->medium->pixSingle(pixw, pixh, pw, ph);
} else {
- pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh, pixwidth, pixheight);
+ pix = _data->photo->thumb->pixBlurredSingle(pixw, pixh, pw, ph);
}
- p.drawPixmap(width - pixwidth, 0, pix);
+ p.drawPixmapLeft(lshift + width - pw, 0, _width, pix);
if (selected) {
- App::roundRect(p, width - pixwidth, 0, pixwidth, pixheight, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
+ App::roundRect(p, rtlrect(lshift + width - pw, 0, pw, _pixh, _width), textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
+ width -= pw + st::webPagePhotoDelta;
}
- int32 articleLines = 5;
+ int32 tshift = 0;
if (_siteNameWidth) {
- int32 availw = width;
- if (_asArticle) {
- availw -= st::webPagePhotoSize + st::webPagePhotoDelta;
- } else if (_title.isEmpty() && _description.isEmpty() && !data->photo) {
- availw -= parent->skipBlockWidth();
- }
- p.setFont(st::webPageTitleFont->f);
- p.setPen(semibold->p);
- p.drawText(0, st::webPageTitleFont->ascent, (availw >= _siteNameWidth) ? data->siteName : st::webPageTitleFont->elided(data->siteName, availw));
- p.translate(0, st::webPageTitleFont->height);
- --articleLines;
+ p.setFont(st::webPageTitleFont);
+ p.setPen(semibold);
+ p.drawTextLeft(lshift, tshift, _width, (width >= _siteNameWidth) ? _data->siteName : st::webPageTitleFont->elided(_data->siteName, width));
+ tshift += _lineHeight;
}
- if (!_title.isEmpty()) {
- p.setPen(st::black->p);
- int32 availw = width, endskip = 0;
- if (_asArticle) {
- availw -= st::webPagePhotoSize + st::webPagePhotoDelta;
- } else if (_description.isEmpty() && !data->photo) {
+ if (_titleLines) {
+ p.setPen(st::black);
+ int32 endskip = 0;
+ if (_title.hasSkipBlock()) {
endskip = parent->skipBlockWidth();
}
- _title.drawElided(p, 0, 0, availw, 2, style::al_left, 0, -1, endskip);
- int32 h = _title.countHeight(availw);
- if (h > st::webPageTitleFont->height) {
- articleLines -= 2;
- p.translate(0, st::webPageTitleFont->height * 2);
- } else {
- --articleLines;
- p.translate(0, h);
- }
+ _title.drawLeftElided(p, lshift, tshift, width, _width, _titleLines, style::al_left, 0, -1, endskip);
+ tshift += _titleLines * _lineHeight;
}
- if (!_description.isEmpty()) {
- p.setPen(st::black->p);
- int32 availw = width, endskip = 0;
- if (_asArticle) {
- availw -= st::webPagePhotoSize + st::webPagePhotoDelta;
- if (articleLines > 3) articleLines = 3;
- } else {
- if (!data->photo) endskip = parent->skipBlockWidth();
- articleLines = 3;
+ if (_descriptionLines) {
+ p.setPen(st::black);
+ int32 endskip = 0;
+ if (_description.hasSkipBlock()) {
+ endskip = parent->skipBlockWidth();
}
- _description.drawElided(p, 0, 0, availw, articleLines, style::al_left, 0, -1, endskip);
- p.translate(0, qMin(_description.countHeight(availw), st::webPageDescriptionFont->height * articleLines));
+ _description.drawLeftElided(p, lshift, tshift, width, _width, _descriptionLines, style::al_left, 0, -1, endskip);
+ tshift += _descriptionLines * _lineHeight;
}
- if (!_asArticle && data->photo) {
- if (_siteNameWidth || !_title.isEmpty() || !_description.isEmpty()) {
- p.translate(0, st::webPagePhotoSkip);
- }
+ if (_attach) {
+ if (tshift) tshift += st::webPagePhotoSkip;
- int32 pixwidth = qMax(_pixw, int16(st::minPhotoSize)), pixheight = qMax(_pixh, int16(st::minPhotoSize));
- data->photo->full->load(false, false);
- bool full = data->photo->full->loaded();
- QPixmap pix;
- if (full) {
- pix = data->photo->full->pixSingle(_pixw, _pixh, pixwidth, pixheight);
- } else {
- pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh, pixwidth, pixheight);
- }
- p.drawPixmap(0, 0, pix);
- if (!full) {
- uint64 dt = itemAnimations().animate(parent, getms());
- int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta);
+ int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top();
+ if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
- int32 x = (pixwidth - st::photoLoader.width()) / 2, y = (pixheight - st::photoLoader.height()) / 2;
- p.fillRect(x, y, st::photoLoader.width(), st::photoLoader.height(), st::photoLoaderBg->b);
- x += (st::photoLoader.width() - cnt * st::photoLoaderPoint.width() - (cnt - 1) * st::photoLoaderSkip) / 2;
- y += (st::photoLoader.height() - st::photoLoaderPoint.height()) / 2;
- QColor c(st::white->c);
- QBrush b(c);
- for (int32 i = 0; i < cnt; ++i) {
- t -= delta;
- while (t < 0) t += period;
+ p.save();
+ p.translate(attachLeft, attachTop);
- float64 alpha = (t >= st::photoLoaderDuration1 + st::photoLoaderDuration2) ? 0 : ((t > st::photoLoaderDuration1 ? ((st::photoLoaderDuration1 + st::photoLoaderDuration2 - t) / st::photoLoaderDuration2) : (t / st::photoLoaderDuration1)));
- c.setAlphaF(st::photoLoaderAlphaMin + alpha * (1 - st::photoLoaderAlphaMin));
- b.setColor(c);
- p.fillRect(x + i * (st::photoLoaderPoint.width() + st::photoLoaderSkip), y, st::photoLoaderPoint.width(), st::photoLoaderPoint.height(), b);
- }
- }
+ _attach->draw(p, parent, r.translated(-attachLeft, -attachTop), selected, ms);
+ int32 pixwidth = _attach->currentWidth(), pixheight = _attach->height();
- if (selected) {
- App::roundRect(p, 0, 0, pixwidth, pixheight, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
- }
-
- if (data->type == WebPageVideo) {
- if (data->siteName == qstr("YouTube")) {
+ if (_data->type == WebPageVideo) {
+ if (_data->siteName == qstr("YouTube")) {
p.drawPixmap(QPoint((pixwidth - st::youtubeIcon.pxWidth()) / 2, (pixheight - st::youtubeIcon.pxHeight()) / 2), App::sprite(), st::youtubeIcon);
} else {
p.drawPixmap(QPoint((pixwidth - st::videoIcon.pxWidth()) / 2, (pixheight - st::videoIcon.pxHeight()) / 2), App::sprite(), st::videoIcon);
@@ -5336,216 +5430,93 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, bool selected,
int32 dateW = pixwidth - dateX - st::msgDateImgDelta;
int32 dateH = pixheight - dateY - st::msgDateImgDelta;
- App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgSelectBg : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
+ App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
- p.setFont(st::msgDateFont->f);
- p.setPen(st::msgDateImgColor->p);
- p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, _duration);
+ p.setFont(st::msgDateFont);
+ p.setPen(st::msgDateImgColor);
+ p.drawTextLeft(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y(), pixwidth, _duration);
}
}
- p.translate(0, pixheight);
- } else if (!_asArticle && data->doc) {
- if (_siteNameWidth || !_title.isEmpty() || !_description.isEmpty()) {
- p.translate(0, st::webPagePhotoSkip);
- }
-
- if (parent == animated.msg) {
- p.drawPixmap(0, 0, animated.current(animw * cIntRetinaFactor(), animh * cIntRetinaFactor(), true));
- if (selected) {
- App::roundRect(p, 0, 0, animw, animh, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
- }
- } else {
- QString statusText;
- if (data->doc->song()) {
- SongMsgId playing;
- AudioPlayerState playingState = AudioPlayerStopped;
- int64 playingPosition = 0, playingDuration = 0;
- int32 playingFrequency = 0;
- if (audioPlayer()) {
- audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
- }
-
- bool already = !data->doc->already().isEmpty(), hasdata = !data->doc->data.isEmpty();
- QRect img;
- if (data->doc->status == FileFailed) {
- statusText = lang(lng_attach_failed);
- img = outbg ? st::mediaMusicOutImg : st::mediaMusicInImg;
- } else if (already || hasdata) {
- bool showPause = false;
- if (playing.msgId == parent->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
- statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
- showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
- } else {
- statusText = formatDurationText(data->doc->song()->duration);
- }
- if (!showPause && playing.msgId == parent->fullId() && App::main() && App::main()->player()->seekingSong(playing)) showPause = true;
- img = outbg ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg);
- } else {
- if (data->doc->loader) {
- int32 offset = data->doc->loader->currentOffset();
- if (_docDownloadTextCache.isEmpty() || _docDownloadDone != offset) {
- _docDownloadDone = offset;
- _docDownloadTextCache = formatDownloadText(_docDownloadDone, data->doc->size);
- }
- statusText = _docDownloadTextCache;
- } else {
- statusText = _docSize;
- }
- img = outbg ? st::mediaMusicOutImg : st::mediaMusicInImg;
- }
-
- p.drawPixmap(QPoint(0, 0), App::sprite(), img);
- } else {
- if (data->doc->status == FileFailed) {
- statusText = lang(lng_attach_failed);
- } else if (data->doc->loader) {
- int32 offset = data->doc->loader->currentOffset();
- if (_docDownloadTextCache.isEmpty() || _docDownloadDone != offset) {
- _docDownloadDone = offset;
- _docDownloadTextCache = formatDownloadText(_docDownloadDone, data->doc->size);
- }
- statusText = _docDownloadTextCache;
- } else {
- statusText = _docSize;
- }
-
- if (_docThumbWidth) {
- data->doc->thumb->checkload();
- p.drawPixmap(QPoint(0, 0), data->doc->thumb->pixSingle(_docThumbWidth, 0, st::mediaThumbSize, st::mediaThumbSize));
- } else {
- p.drawPixmap(QPoint(0, 0), App::sprite(), (outbg ? st::mediaDocOutImg : st::mediaDocInImg));
- }
- }
- if (selected) {
- App::roundRect(p, 0, 0, st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
- }
-
- int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right();
- int32 twidth = width - tleft - st::mediaPadding.right();
- int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth();
-
- p.setFont(st::mediaFont->f);
- p.setPen(st::black->c);
- if (twidth < _docNameWidth) {
- p.drawText(tleft, st::mediaNameTop + st::mediaFont->ascent, st::mediaFont->elided(_docName, twidth));
- } else {
- p.drawText(tleft, st::mediaNameTop + st::mediaFont->ascent, _docName);
- }
-
- style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor));
- p.setPen(status->p);
-
- p.drawText(tleft, st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->descent, statusText);
- }
+ p.restore();
}
-
- p.restore();
}
-int32 HistoryWebPage::resize(int32 width, const HistoryItem *parent) {
- if (data->pendingTill) {
- w = width;
- _height = _minh;
- return _height;
+void HistoryWebPage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
+
+ int32 lshift = st::msgPadding.left() + st::webPageLeft, rshift = st::msgPadding.right(), bshift = st::msgPadding.bottom();
+ width -= lshift + rshift;
+ QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
+ if (_asArticle || (_attach && _attach->customInfoLayout() && _attach->currentWidth() + parent->skipBlockWidth() > width + bubble.left() + bubble.right())) {
+ bshift += st::msgDateFont->height;
}
- w = width;
- width -= st::webPageLeft;
if (_asArticle) {
- int32 tw = convertScale(data->photo->medium->width()), th = convertScale(data->photo->medium->height());
- if (tw > st::webPagePhotoSize) {
- if (th > tw) {
- th = th * st::webPagePhotoSize / tw;
- tw = st::webPagePhotoSize;
- } else if (th > st::webPagePhotoSize) {
- tw = tw * st::webPagePhotoSize / th;
- th = st::webPagePhotoSize;
- }
+ int32 pw = qMax(_pixw, int16(_lineHeight));
+ if (rtlrect(lshift + width - pw, 0, pw, _pixh, _width).contains(x, y)) {
+ lnk = _openl;
+ return;
}
- _pixw = tw;
- _pixh = th;
- if (_pixw < 1) _pixw = 1;
- if (_pixh < 1) _pixh = 1;
- _height = st::webPagePhotoSize;
- _height += st::webPagePhotoSkip + (st::msgDateFont->height - st::msgDateDelta.y());
- } else if (data->photo) {
- _pixw = qMin(width, int32(_maxw - st::webPageLeft - parent->skipBlockWidth()));
-
- int32 tw = convertScale(data->photo->full->width()), th = convertScale(data->photo->full->height());
- if (tw > st::maxMediaSize) {
- th = (st::maxMediaSize * th) / tw;
- tw = st::maxMediaSize;
- }
- if (th > st::maxMediaSize) {
- tw = (st::maxMediaSize * tw) / th;
- th = st::maxMediaSize;
- }
- _pixh = th;
- if (tw > _pixw) {
- _pixh = (_pixw * _pixh / tw);
- } else {
- _pixw = tw;
- }
- if (_pixh > width) {
- _pixw = (_pixw * width) / _pixh;
- _pixh = width;
- }
- if (_pixw < 1) _pixw = 1;
- if (_pixh < 1) _pixh = 1;
- _height = qMax(_pixh, int16(st::minPhotoSize));
- _height += st::webPagePhotoSkip;
- if (qMax(_pixw, int16(st::minPhotoSize)) + parent->skipBlockWidth() > width) {
- _height += (st::msgDateFont->height - st::msgDateDelta.y());
- }
- } else if (data->doc) {
- if (parent == animated.msg) {
- int32 w = qMin(width, int32(animated.w / cIntRetinaFactor()));
- if (w > st::maxMediaSize) {
- w = st::maxMediaSize;
- }
- _height = animated.h / cIntRetinaFactor();
- if (animated.w / cIntRetinaFactor() > w) {
- _height = (w * _height / (animated.w / cIntRetinaFactor()));
- if (_height <= 0) _height = 1;
- }
- _height += st::webPagePhotoSkip;
- if (w + parent->skipBlockWidth() > width) {
- _height += (st::msgDateFont->height - st::msgDateDelta.y());
- }
- } else {
- _height = st::mediaThumbSize;
- }
- } else {
- _height = 0;
+ width -= pw + st::webPagePhotoDelta;
}
+ int32 tshift = 0;
+ if (_siteNameWidth) {
+ tshift += _lineHeight;
+ }
+ if (_titleLines) {
+ tshift += _titleLines * _lineHeight;
+ }
+ if (_descriptionLines) {
+ if (y >= tshift && y < tshift + _descriptionLines * _lineHeight) {
+ bool inText = false;
+ _description.getStateLeft(lnk, inText, x - lshift, y - tshift, width, _width);
+ state = inText ? HistoryInTextCursorState : HistoryDefaultCursorState;
+ return;
+ }
+ tshift += _descriptionLines * _lineHeight;
+ }
+ if (_attach) {
+ if (tshift) tshift += st::webPagePhotoSkip;
- if (!_asArticle) {
- if (!data->siteName.isEmpty()) {
- _height += st::webPageTitleFont->height;
- }
- if (!_title.isEmpty()) {
- _height += qMin(_title.countHeight(width), st::webPageTitleFont->height * 2);
- }
- if (!_description.isEmpty()) {
- _height += qMin(_description.countHeight(width), st::webPageDescriptionFont->height * 3);
- }
- if ((data->photo || data->doc) && (_siteNameWidth || !_title.isEmpty() || !_description.isEmpty())) {
- _height += st::webPagePhotoSkip;
+ if (x >= lshift && x < lshift + width && y >= tshift && y < _height - st::msgPadding.bottom()) {
+ int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top();
+ if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
+ _attach->getState(lnk, state, x - attachLeft, y - attachTop, parent);
+ if (lnk && !_data->doc && _data->photo) {
+ if (_data->type == WebPageProfile || _data->type == WebPageVideo) {
+ lnk = _openl;
+ } else if (_data->type == WebPagePhoto || _data->siteName == qstr("Twitter") || _data->siteName == qstr("Facebook")) {
+ // leave photo link
+ } else {
+ lnk = _openl;
+ }
+ }
}
}
+}
- return _height;
+void HistoryWebPage::linkOver(HistoryItem *parent, const TextLinkPtr &lnk) {
+ if (_attach) {
+ _attach->linkOver(parent, lnk);
+ }
+}
+
+void HistoryWebPage::linkOut(HistoryItem *parent, const TextLinkPtr &lnk) {
+ if (_attach) {
+ _attach->linkOut(parent, lnk);
+ }
}
void HistoryWebPage::regItem(HistoryItem *item) {
- App::regWebPageItem(data, item);
- if (data->doc) App::regDocumentItem(data->doc, item);
+ App::regWebPageItem(_data, item);
+ if (_attach) _attach->regItem(item);
}
void HistoryWebPage::unregItem(HistoryItem *item) {
- App::unregWebPageItem(data, item);
- if (data->doc) App::unregDocumentItem(data->doc, item);
+ App::unregWebPageItem(_data, item);
+ if (_attach) _attach->unregItem(item);
}
const QString HistoryWebPage::inDialogsText() const {
@@ -5556,99 +5527,15 @@ const QString HistoryWebPage::inHistoryText() const {
return QString();
}
-bool HistoryWebPage::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- if (width >= _maxw) width = _maxw;
-
- return (x >= 0 && y >= 0 && x < width && y < _height);
-}
-
-void HistoryWebPage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- if (width < 1) return;
-
- width -= st::webPageLeft;
- x -= st::webPageLeft;
-
- if (_asArticle) {
- int32 pixwidth = st::webPagePhotoSize, pixheight = st::webPagePhotoSize;
- if (x >= width - pixwidth && x < width && y >= 0 && y < pixheight) {
- lnk = _openl;
- return;
- }
- }
- int32 articleLines = 5;
- if (_siteNameWidth) {
- y -= st::webPageTitleFont->height;
- --articleLines;
- }
- if (!_title.isEmpty()) {
- int32 availw = width;
- if (_asArticle) {
- availw -= st::webPagePhotoSize + st::webPagePhotoDelta;
- }
- int32 h = _title.countHeight(availw);
- if (h > st::webPageTitleFont->height) {
- articleLines -= 2;
- y -= st::webPageTitleFont->height * 2;
- } else {
- --articleLines;
- y -= h;
- }
- }
- if (!_description.isEmpty()) {
- int32 availw = width;
- if (_asArticle) {
- availw -= st::webPagePhotoSize + st::webPagePhotoDelta;
- if (articleLines > 3) articleLines = 3;
- } else if (!data->photo) {
- articleLines = 3;
- }
- int32 desch = qMin(_description.countHeight(width), st::webPageDescriptionFont->height * articleLines);
- if (y >= 0 && y < desch) {
- bool inText = false;
- _description.getState(lnk, inText, x, y, availw);
- state = inText ? HistoryInTextCursorState : HistoryDefaultCursorState;
- return;
- }
- y -= desch;
- }
- if (_siteNameWidth || !_title.isEmpty() || !_description.isEmpty()) {
- y -= st::webPagePhotoSkip;
- }
- if (!_asArticle) {
- if (data->doc && parent == animated.msg) {
- int32 h = (width == w) ? _height : (width * animated.h / animated.w);
- if (h < 1) h = 1;
- if (x >= 0 && y >= 0 && x < width && y < h) {
- lnk = _attachl;
- return;
- }
- } else {
- int32 attachwidth = data->doc ? (width - st::mediaPadding.right()) : qMax(_pixw, int16(st::minPhotoSize));
- int32 attachheight = data->doc ? st::mediaThumbSize : qMax(_pixh, int16(st::minPhotoSize));
- if (x >= 0 && y >= 0 && x < attachwidth && y < attachheight) {
- lnk = _attachl ? _attachl : _openl;
- return;
- }
- }
- }
-}
-
-HistoryMedia *HistoryWebPage::clone() const {
- return new HistoryWebPage(*this);
-}
-
ImagePtr HistoryWebPage::replyPreview() {
- return data->photo ? data->photo->makeReplyPreview() : (data->doc ? data->doc->makeReplyPreview() : ImagePtr());
+ return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr());
+}
+
+HistoryWebPage::~HistoryWebPage() {
+ deleteAndMark(_attach);
}
namespace {
- QRegularExpression reYouTube1(qsl("^(https?://)?(www\\.|m\\.)?youtube\\.com/watch\\?([^#]+&)?v=([a-z0-9_-]+)(&[^\\s]*)?$"), QRegularExpression::CaseInsensitiveOption);
- QRegularExpression reYouTube2(qsl("^(https?://)?(www\\.)?youtu\\.be/([a-z0-9_-]+)([/\\?#][^\\s]*)?$"), QRegularExpression::CaseInsensitiveOption);
- QRegularExpression reInstagram(qsl("^(https?://)?(www\\.)?instagram\\.com/p/([a-z0-9_-]+)([/\\?][^\\s]*)?$"), QRegularExpression::CaseInsensitiveOption);
- QRegularExpression reVimeo(qsl("^(https?://)?(www\\.)?vimeo\\.com/([0-9]+)([/\\?][^\\s]*)?$"), QRegularExpression::CaseInsensitiveOption);
-
ImageLinkManager manager;
}
@@ -5708,38 +5595,22 @@ void ImageLinkManager::getData(ImageLinkData *data) {
}
QString url;
switch (data->type) {
- case YouTubeLink: {
- url = qsl("https://gdata.youtube.com/feeds/api/videos/") + data->id.mid(8) + qsl("?v=2&alt=json");
- QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url)));
- dataLoadings[reply] = data;
- } break;
- case VimeoLink: {
- url = qsl("https://vimeo.com/api/v2/video/") + data->id.mid(6) + qsl(".json");
- QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url)));
- dataLoadings[reply] = data;
- } break;
- case InstagramLink: {
- //url = qsl("https://api.instagram.com/oembed?url=http://instagr.am/p/") + data->id.mid(10) + '/';
- url = qsl("https://instagram.com/p/") + data->id.mid(10) + qsl("/media/?size=l");
- QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url)));
- imageLoadings[reply] = data;
- } break;
- case GoogleMapsLink: {
- int32 w = st::locationSize.width(), h = st::locationSize.height();
- int32 zoom = 13, scale = 1;
- if (cScale() == dbisTwo || cRetina()) {
- scale = 2;
- } else {
- w = convertScale(w);
- h = convertScale(h);
- }
- url = qsl("https://maps.googleapis.com/maps/api/staticmap?center=") + data->id.mid(9) + qsl("&zoom=%1&size=%2x%3&maptype=roadmap&scale=%4&markers=color:red|size:big|").arg(zoom).arg(w).arg(h).arg(scale) + data->id.mid(9) + qsl("&sensor=false");
- QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url)));
- imageLoadings[reply] = data;
- } break;
- default: {
- failed(data);
- } break;
+ case GoogleMapsLink: {
+ int32 w = st::locationSize.width(), h = st::locationSize.height();
+ int32 zoom = 13, scale = 1;
+ if (cScale() == dbisTwo || cRetina()) {
+ scale = 2;
+ } else {
+ w = convertScale(w);
+ h = convertScale(h);
+ }
+ url = qsl("https://maps.googleapis.com/maps/api/staticmap?center=") + data->id.mid(9) + qsl("&zoom=%1&size=%2x%3&maptype=roadmap&scale=%4&markers=color:red|size:big|").arg(zoom).arg(w).arg(h).arg(scale) + data->id.mid(9) + qsl("&sensor=false");
+ QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url)));
+ imageLoadings[reply] = data;
+ } break;
+ default: {
+ failed(data);
+ } break;
}
}
@@ -5798,139 +5669,7 @@ void ImageLinkManager::onFinished(QNetworkReply *reply) {
return onFailed(reply);
}
switch (d->type) {
- case YouTubeLink: {
- QJsonObject obj = doc.object();
- QString thumb;
- int32 seconds = 0;
- QJsonObject::const_iterator entryIt = obj.constFind(qsl("entry"));
- if (entryIt != obj.constEnd() && entryIt.value().isObject()) {
- QJsonObject entry = entryIt.value().toObject();
- QJsonObject::const_iterator mediaIt = entry.constFind(qsl("media$group"));
- if (mediaIt != entry.constEnd() && mediaIt.value().isObject()) {
- QJsonObject media = mediaIt.value().toObject();
-
- // title from media
- QJsonObject::const_iterator titleIt = media.constFind(qsl("media$title"));
- if (titleIt != media.constEnd() && titleIt.value().isObject()) {
- QJsonObject title = titleIt.value().toObject();
- QJsonObject::const_iterator tIt = title.constFind(qsl("$t"));
- if (tIt != title.constEnd() && tIt.value().isString()) {
- d->title = tIt.value().toString();
- }
- }
-
- // thumb
- QJsonObject::const_iterator thumbnailsIt = media.constFind(qsl("media$thumbnail"));
- int32 bestLevel = 0;
- if (thumbnailsIt != media.constEnd() && thumbnailsIt.value().isArray()) {
- QJsonArray thumbnails = thumbnailsIt.value().toArray();
- for (int32 i = 0, l = thumbnails.size(); i < l; ++i) {
- QJsonValue thumbnailVal = thumbnails.at(i);
- if (!thumbnailVal.isObject()) continue;
-
- QJsonObject thumbnail = thumbnailVal.toObject();
- QJsonObject::const_iterator urlIt = thumbnail.constFind(qsl("url"));
- if (urlIt == thumbnail.constEnd() || !urlIt.value().isString()) continue;
-
- int32 level = 0;
- if (thumbnail.constFind(qsl("time")) == thumbnail.constEnd()) {
- level += 10;
- }
- QJsonObject::const_iterator wIt = thumbnail.constFind(qsl("width"));
- if (wIt != thumbnail.constEnd()) {
- int32 w = 0;
- if (wIt.value().isDouble()) {
- w = qMax(qRound(wIt.value().toDouble()), 0);
- } else if (wIt.value().isString()) {
- w = qMax(qRound(wIt.value().toString().toDouble()), 0);
- }
- switch (w) {
- case 640: level += 4; break;
- case 480: level += 3; break;
- case 320: level += 2; break;
- case 120: level += 1; break;
- }
- }
- if (level > bestLevel) {
- thumb = urlIt.value().toString();
- bestLevel = level;
- }
- }
- }
-
- // duration
- QJsonObject::const_iterator durationIt = media.constFind(qsl("yt$duration"));
- if (durationIt != media.constEnd() && durationIt.value().isObject()) {
- QJsonObject duration = durationIt.value().toObject();
- QJsonObject::const_iterator secondsIt = duration.constFind(qsl("seconds"));
- if (secondsIt != duration.constEnd()) {
- if (secondsIt.value().isDouble()) {
- seconds = qRound(secondsIt.value().toDouble());
- } else if (secondsIt.value().isString()) {
- seconds = qRound(secondsIt.value().toString().toDouble());
- }
- }
- }
- }
-
- // title field
- if (d->title.isEmpty()) {
- QJsonObject::const_iterator titleIt = entry.constFind(qsl("title"));
- if (titleIt != entry.constEnd() && titleIt.value().isObject()) {
- QJsonObject title = titleIt.value().toObject();
- QJsonObject::const_iterator tIt = title.constFind(qsl("$t"));
- if (tIt != title.constEnd() && tIt.value().isString()) {
- d->title = tIt.value().toString();
- }
- }
- }
- }
-
- if (seconds > 0) {
- d->duration = formatDurationText(seconds);
- }
- if (thumb.isEmpty()) {
- failed(d);
- } else {
- imageLoadings.insert(manager->get(QNetworkRequest(thumb)), d);
- }
- } break;
-
- case VimeoLink: {
- QString thumb;
- int32 seconds = 0;
- QJsonArray arr = doc.array();
- if (!arr.isEmpty()) {
- QJsonObject obj = arr.at(0).toObject();
- QJsonObject::const_iterator titleIt = obj.constFind(qsl("title"));
- if (titleIt != obj.constEnd() && titleIt.value().isString()) {
- d->title = titleIt.value().toString();
- }
- QJsonObject::const_iterator thumbnailsIt = obj.constFind(qsl("thumbnail_large"));
- if (thumbnailsIt != obj.constEnd() && thumbnailsIt.value().isString()) {
- thumb = thumbnailsIt.value().toString();
- }
- QJsonObject::const_iterator secondsIt = obj.constFind(qsl("duration"));
- if (secondsIt != obj.constEnd()) {
- if (secondsIt.value().isDouble()) {
- seconds = qRound(secondsIt.value().toDouble());
- } else if (secondsIt.value().isString()) {
- seconds = qRound(secondsIt.value().toString().toDouble());
- }
- }
- }
- if (seconds > 0) {
- d->duration = formatDurationText(seconds);
- }
- if (thumb.isEmpty()) {
- failed(d);
- } else {
- imageLoadings.insert(manager->get(QNetworkRequest(thumb)), d);
- }
- } break;
-
- case InstagramLink: failed(d); break;
- case GoogleMapsLink: failed(d); break;
+ case GoogleMapsLink: failed(d); break;
}
if (App::main()) App::main()->update();
@@ -5946,6 +5685,9 @@ void ImageLinkManager::onFinished(QNetworkReply *reply) {
{
QBuffer buffer(&data);
QImageReader reader(&buffer);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
+ reader.setAutoTransform(true);
+#endif
thumb = QPixmap::fromImageReader(&reader, Qt::ColorOnly);
format = reader.format();
thumb.setDevicePixelRatio(cRetinaFactor());
@@ -6006,350 +5748,181 @@ _description(st::msgMinWidth) {
if (url.startsWith(qsl("location:"))) {
QString lnk = qsl("https://maps.google.com/maps?q=") + url.mid(9) + qsl("&ll=") + url.mid(9) + qsl("&z=17");
- link.reset(new TextLink(lnk));
+ _link.reset(new TextLink(lnk));
- data = App::imageLinkSet(url, GoogleMapsLink, lnk);
+ _data = App::imageLinkSet(url, GoogleMapsLink, lnk);
} else {
- link.reset(new TextLink(url));
-
- int matchIndex = 4;
- QRegularExpressionMatch m = reYouTube1.match(url);
- if (!m.hasMatch()) {
- m = reYouTube2.match(url);
- matchIndex = 3;
- }
- if (m.hasMatch()) {
- data = App::imageLinkSet(qsl("youtube:") + m.captured(matchIndex), YouTubeLink, url);
- } else {
- m = reVimeo.match(url);
- if (m.hasMatch()) {
- data = App::imageLinkSet(qsl("vimeo:") + m.captured(3), VimeoLink, url);
- } else {
- m = reInstagram.match(url);
- if (m.hasMatch()) {
- data = App::imageLinkSet(qsl("instagram:") + m.captured(3), InstagramLink, url);
- data->title = qsl("instagram.com/p/") + m.captured(3);
- } else {
- data = 0;
- }
- }
- }
+ _link.reset(new TextLink(url));
}
}
-int32 HistoryImageLink::fullWidth() const {
- if (data) {
- switch (data->type) {
- case YouTubeLink: return 640;
- case VimeoLink: return 640;
- case InstagramLink: return 640;
- case GoogleMapsLink: return st::locationSize.width();
- }
- }
- return st::minPhotoSize;
-}
-
-int32 HistoryImageLink::fullHeight() const {
- if (data) {
- switch (data->type) {
- case YouTubeLink: return 480;
- case VimeoLink: return 480;
- case InstagramLink: return 640;
- case GoogleMapsLink: return st::locationSize.height();
- }
- }
- return st::minPhotoSize;
-}
-
void HistoryImageLink::initDimensions(const HistoryItem *parent) {
- int32 tw = convertScale(fullWidth()), th = convertScale(fullHeight());
- int32 thumbw = qMax(tw, int32(st::minPhotoSize)), maxthumbh = thumbw;
- int32 thumbh = qRound(th * float64(thumbw) / tw);
- if (thumbh > maxthumbh) {
- thumbw = qRound(thumbw * float64(maxthumbh) / thumbh);
- thumbh = maxthumbh;
- if (thumbw < st::minPhotoSize) {
- thumbw = st::minPhotoSize;
- }
+ bool bubble = parent->hasBubble();
+
+ int32 tw = fullWidth(), th = fullHeight();
+ if (tw > st::maxMediaSize) {
+ th = (st::maxMediaSize * th) / tw;
+ tw = st::maxMediaSize;
}
- if (thumbh < st::minPhotoSize) {
- thumbh = st::minPhotoSize;
- }
- if (!w) {
- w = thumbw;
- }
- _maxw = w;
- _minh = thumbh;
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = toHistoryForwarded(parent);
- if (reply || !_title.isEmpty() || !_description.isEmpty()) {
+ int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ _maxw = qMax(tw, int32(minWidth));
+ _minh = qMax(th, int32(st::minPhotoSize));
+
+ if (bubble) {
_maxw += st::mediaPadding.left() + st::mediaPadding.right();
- if (reply) {
- _minh += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else {
- if (!parent->displayFromName() || !fwd) {
- _minh += st::msgPadding.top();
- }
- if (fwd) {
- _minh += st::msgServiceNameFont->height + st::msgPadding.top();
- }
- }
- if (parent->displayFromName()) {
- _minh += st::msgPadding.top() + st::msgNameFont->height;
- }
if (!_title.isEmpty()) {
- _maxw = qMax(_maxw, int32(st::webPageLeft + _title.maxWidth()));
- _minh += qMin(_title.minHeight(), 2 * st::webPageTitleFont->height);
+ _minh += qMin(_title.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()), 2 * st::webPageTitleFont->height);
}
if (!_description.isEmpty()) {
- _maxw = qMax(_maxw, int32(st::webPageLeft + _description.maxWidth()));
- _minh += qMin(_description.minHeight(), 3 * st::webPageTitleFont->height);
+ _maxw = qMax(_maxw, int32(st::msgPadding.left() + _description.maxWidth() + st::msgPadding.right()));
+ _minh += qMin(_description.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()), 3 * st::webPageDescriptionFont->height);
}
+ _minh += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_title.isEmpty() || !_description.isEmpty()) {
_minh += st::webPagePhotoSkip;
+ if (!parent->toHistoryForwarded() && !parent->toHistoryReply()) {
+ _minh += st::msgPadding.top();
+ }
}
- _minh += st::mediaPadding.bottom();
}
- _height = _minh;
}
-void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const {
- if (width < 0) width = w;
- int skipx = 0, skipy = 0, height = _height;
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = toHistoryForwarded(parent);
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
+int32 HistoryImageLink::resize(int32 width, const HistoryItem *parent) {
+ bool bubble = parent->hasBubble();
- if (reply || !_title.isEmpty() || !_description.isEmpty()) {
- skipx = st::mediaPadding.left();
-
- style::color bg(selected ? (outbg ? st::msgOutSelectBg : st::msgInSelectBg) : (outbg ? st::msgOutBg : st::msgInBg));
- style::color sh(selected ? (outbg ? st::msgOutSelectShadow : st::msgInSelectShadow) : (outbg ? st::msgOutShadow : st::msgInShadow));
- RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners));
- App::roundRect(p, 0, 0, width, _height, bg, cors, &sh);
-
- int replyFrom = 0, fwdFrom = 0;
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- p.setFont(st::msgNameFont->f);
- if (fromChannel) {
- p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor);
- } else {
- p.setPen(parent->from()->color);
- }
- parent->from()->nameText.drawElided(p, st::mediaPadding.left(), st::msgPadding.top(), width - st::mediaPadding.left() - st::mediaPadding.right());
- if (!fwd && !reply) skipy += st::msgPadding.top();
- } else if (!reply) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
- if (reply) {
- skipy += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- reply->drawReplyTo(p, st::msgReplyPadding.left(), replyFrom, width - st::msgReplyPadding.left() - st::msgReplyPadding.right(), selected);
- } else if (fwd) {
- skipy += st::msgServiceNameFont->height + st::msgPadding.top();
- fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected);
- }
- width -= st::mediaPadding.left() + st::mediaPadding.right();
+ _width = qMin(width, _maxw);
+ if (bubble) {
+ _width -= st::mediaPadding.left() + st::mediaPadding.right();
+ }
+ int32 tw = fullWidth(), th = fullHeight();
+ if (tw > st::maxMediaSize) {
+ th = (st::maxMediaSize * th) / tw;
+ tw = st::maxMediaSize;
+ }
+ _height = th;
+ if (tw > _width) {
+ _height = (_width * _height / tw);
+ } else {
+ _width = tw;
+ }
+ int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
+ _width = qMax(_width, int32(minWidth));
+ _height = qMax(_height, int32(st::minPhotoSize));
+ if (bubble) {
+ _width += st::mediaPadding.left() + st::mediaPadding.right();
+ _height += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_title.isEmpty()) {
- p.setPen(st::black->p);
- _title.drawElided(p, st::mediaPadding.left(), skipy, width, 2);
- skipy += qMin(_title.countHeight(width), 2 * st::webPageTitleFont->height);
+ _height += qMin(_title.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()), st::webPageTitleFont->height * 2);
}
if (!_description.isEmpty()) {
- p.setPen(st::black->p);
- _description.drawElided(p, st::mediaPadding.left(), skipy, width, 3);
- skipy += qMin(_description.countHeight(width), 3 * st::webPageDescriptionFont->height);
+ _height += qMin(_description.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()), st::webPageDescriptionFont->height * 3);
+ }
+ if (!_title.isEmpty() || !_description.isEmpty()) {
+ _height += st::webPagePhotoSkip;
+ if (!parent->toHistoryForwarded() && !parent->toHistoryReply()) {
+ _height += st::msgPadding.top();
+ }
+ }
+ }
+ return _height;
+}
+
+void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
+ bool bubble = parent->hasBubble();
+ bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
+
+ if (bubble) {
+ skipx = st::mediaPadding.left();
+ skipy = st::mediaPadding.top();
+
+ if (!_title.isEmpty() || !_description.isEmpty()) {
+ if (!parent->toHistoryForwarded() && !parent->toHistoryReply()) {
+ skipy += st::msgPadding.top();
+ }
+ }
+
+ width -= st::mediaPadding.left() + st::mediaPadding.right();
+ int32 textw = _width - st::msgPadding.left() - st::msgPadding.right();
+
+ p.setPen(st::black);
+ if (!_title.isEmpty()) {
+ _title.drawLeftElided(p, skipx + st::msgPadding.left(), skipy, textw, _width, 2);
+ skipy += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
+ }
+ if (!_description.isEmpty()) {
+ _description.drawLeftElided(p, skipx + st::msgPadding.left(), skipy, textw, _width, 3);
+ skipy += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
}
if (!_title.isEmpty() || !_description.isEmpty()) {
skipy += st::webPagePhotoSkip;
}
height -= skipy + st::mediaPadding.bottom();
} else {
- App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInSelectShadow : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
+ App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
}
- data->load();
+ _data->load();
QPixmap toDraw;
- if (data && !data->thumb->isNull()) {
- int32 w = data->thumb->width(), h = data->thumb->height();
+ if (_data && !_data->thumb->isNull()) {
+ int32 w = _data->thumb->width(), h = _data->thumb->height();
QPixmap pix;
- if (width * h == height * w || (w == convertScale(fullWidth()) && h == convertScale(fullHeight()))) {
- pix = data->thumb->pixSingle(width, height, width, height);
+ if (width * h == height * w || (w == fullWidth() && h == fullHeight())) {
+ pix = _data->thumb->pixSingle(width, height, width, height);
} else if (width * h > height * w) {
int32 nw = height * w / h;
- pix = data->thumb->pixSingle(nw, height, width, height);
+ pix = _data->thumb->pixSingle(nw, height, width, height);
} else {
int32 nh = width * h / w;
- pix = data->thumb->pixSingle(width, nh, width, height);
+ pix = _data->thumb->pixSingle(width, nh, width, height);
}
p.drawPixmap(QPoint(skipx, skipy), pix);
} else {
- App::roundRect(p, skipx, skipy, width, height, st::black, BlackCorners);
- }
- if (data) {
- switch (data->type) {
- case YouTubeLink: p.drawPixmap(QPoint(skipx + (width - st::youtubeIcon.pxWidth()) / 2, skipy + (height - st::youtubeIcon.pxHeight()) / 2), App::sprite(), st::youtubeIcon); break;
- case VimeoLink: p.drawPixmap(QPoint(skipx + (width - st::vimeoIcon.pxWidth()) / 2, skipy + (height - st::vimeoIcon.pxHeight()) / 2), App::sprite(), st::vimeoIcon); break;
- }
- if (!data->title.isEmpty() || !data->duration.isEmpty()) {
- p.fillRect(skipx, skipy, width, st::msgDateFont->height + 2 * st::msgDateImgPadding.y(), st::msgDateImgBg->b);
- p.setFont(st::msgDateFont->f);
- p.setPen(st::msgDateImgColor->p);
- int32 titleWidth = width - 2 * st::msgDateImgPadding.x();
- if (!data->duration.isEmpty()) {
- int32 durationWidth = st::msgDateFont->width(data->duration);
- p.drawText(skipx + width - st::msgDateImgPadding.x() - durationWidth, skipy + st::msgDateImgPadding.y() + st::msgDateFont->ascent, data->duration);
- titleWidth -= durationWidth + st::msgDateImgPadding.x();
- }
- if (!data->title.isEmpty()) {
- p.drawText(skipx + st::msgDateImgPadding.x(), skipy + st::msgDateImgPadding.y() + st::msgDateFont->ascent, st::msgDateFont->elided(data->title, titleWidth));
- }
- }
+ App::roundRect(p, skipx, skipy, width, height, st::white, MessageInCorners);
}
if (selected) {
App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
}
- int32 fullRight = skipx + width, fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0);
- parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayOverImage);
+ if (parent->getMedia() == this) {
+ int32 fullRight = skipx + width, fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0);
+ parent->drawInfo(p, fullRight, fullBottom, skipx * 2 + width, selected, InfoDisplayOverImage);
+ }
}
-int32 HistoryImageLink::resize(int32 width, const HistoryItem *parent) {
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = toHistoryForwarded(parent);
+void HistoryImageLink::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const {
+ if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
+ int32 skipx = 0, skipy = 0, width = _width, height = _height;
+ bool bubble = parent->hasBubble();
- w = qMin(width, _maxw);
- if (reply || !_title.isEmpty() || !_description.isEmpty()) {
- w -= st::mediaPadding.left() + st::mediaPadding.right();
- }
+ if (bubble) {
+ skipx = st::mediaPadding.left();
+ skipy = st::mediaPadding.top();
- int32 tw = convertScale(fullWidth()), th = convertScale(fullHeight());
- if (tw > st::maxMediaSize) {
- th = (st::maxMediaSize * th) / tw;
- tw = st::maxMediaSize;
- }
- _height = th;
- if (tw > w) {
- _height = (w * _height / tw);
- } else {
- w = tw;
- }
- if (_height > width) {
- w = (w * width) / _height;
- _height = width;
- }
- if (w < st::minPhotoSize) {
- w = st::minPhotoSize;
- }
- if (_height < st::minPhotoSize) {
- _height = st::minPhotoSize;
- }
- if (reply || !_title.isEmpty() || !_description.isEmpty()) {
- if (parent->displayFromName()) {
- _height += st::msgPadding.top() + st::msgNameFont->height;
- }
- if (reply) {
- _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else {
- if (!parent->displayFromName() || !fwd) {
- _height += st::msgPadding.top();
- }
- if (fwd) {
- _height += st::msgServiceNameFont->height + st::msgPadding.top();
+ if (!_title.isEmpty() || !_description.isEmpty()) {
+ if (!parent->toHistoryForwarded() && !parent->toHistoryReply()) {
+ skipy += st::msgPadding.top();
}
}
+
+ width -= st::mediaPadding.left() + st::mediaPadding.right();
+ int32 textw = _width - st::msgPadding.left() - st::msgPadding.right();
+
if (!_title.isEmpty()) {
- _height += qMin(_title.countHeight(w), st::webPageTitleFont->height * 2);
+ skipy += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
}
if (!_description.isEmpty()) {
- _height += qMin(_description.countHeight(w), st::webPageDescriptionFont->height * 3);
+ skipy += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
}
if (!_title.isEmpty() || !_description.isEmpty()) {
- _height += st::webPagePhotoSkip;
- }
- _height += st::mediaPadding.bottom();
- w += st::mediaPadding.left() + st::mediaPadding.right();
- }
- return _height;
-}
-
-const QString HistoryImageLink::inDialogsText() const {
- if (data) {
- switch (data->type) {
- case YouTubeLink: return qsl("YouTube Video");
- case VimeoLink: return qsl("Vimeo Video");
- case InstagramLink: return qsl("Instagram Link");
- case GoogleMapsLink: return lang(lng_maps_point);
- }
- }
- return QString();
-}
-
-const QString HistoryImageLink::inHistoryText() const {
- if (data) {
- switch (data->type) {
- case YouTubeLink: return qsl("[ YouTube Video : ") + link->text() + qsl(" ]");
- case VimeoLink: return qsl("[ Vimeo Video : ") + link->text() + qsl(" ]");
- case InstagramLink: return qsl("[ Instagram Link : ") + link->text() + qsl(" ]");
- case GoogleMapsLink: return qsl("[ ") + lang(lng_maps_point) + qsl(" : ") + link->text() + qsl(" ]");
- }
- }
- return qsl("[ Link : ") + link->text() + qsl(" ]");
-}
-
-bool HistoryImageLink::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
- return (x >= 0 && y >= 0 && x < width && y < _height);
-}
-
-void HistoryImageLink::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const {
- if (width < 0) width = w;
-
- bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel;
-
- int skipx = 0, skipy = 0, height = _height;
- const HistoryReply *reply = toHistoryReply(parent);
- const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent);
- int replyFrom = 0, fwdFrom = 0;
- if (reply || !_title.isEmpty() || !_description.isEmpty()) {
- skipx = st::mediaPadding.left();
- if (reply) {
- skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- } else if (fwd) {
- skipy = st::msgServiceNameFont->height + st::msgPadding.top();
- }
- if (parent->displayFromName()) {
- replyFrom = st::msgPadding.top() + st::msgNameFont->height;
- fwdFrom = st::msgPadding.top() + st::msgNameFont->height;
- skipy += replyFrom;
- if (x >= st::mediaPadding.left() && y >= st::msgPadding.top() && x < width - st::mediaPadding.left() - st::mediaPadding.right() && x < st::mediaPadding.left() + parent->from()->nameText.maxWidth() && y < replyFrom) {
- lnk = parent->from()->lnk;
- return;
- }
- if (!fwd && !reply) skipy += st::msgPadding.top();
- } else if (!reply) {
- fwdFrom = st::msgPadding.top();
- skipy += fwdFrom;
- }
- if (reply) {
- if (x >= 0 && y >= replyFrom + st::msgReplyPadding.top() && x < width && y < skipy - st::msgReplyPadding.bottom()) {
- lnk = reply->replyToLink();
- return;
- }
- } else if (fwd) {
- if (y >= fwdFrom && y < fwdFrom + st::msgServiceNameFont->height) {
- return fwd->getForwardedState(lnk, state, x - st::mediaPadding.left(), width - st::mediaPadding.left() - st::mediaPadding.right());
- }
+ skipy += st::webPagePhotoSkip;
}
height -= skipy + st::mediaPadding.bottom();
- width -= st::mediaPadding.left() + st::mediaPadding.right();
}
- if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height && data) {
- lnk = link;
+ if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height && _data) {
+ lnk = _link;
int32 fullRight = skipx + width, fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0);
bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage);
@@ -6361,8 +5934,70 @@ void HistoryImageLink::getState(TextLinkPtr &lnk, HistoryCursorState &state, int
}
}
-HistoryMedia *HistoryImageLink::clone() const {
- return new HistoryImageLink(*this);
+const QString HistoryImageLink::inDialogsText() const {
+ if (_data) {
+ switch (_data->type) {
+ case GoogleMapsLink: return lang(lng_maps_point);
+ }
+ }
+ return QString();
+}
+
+const QString HistoryImageLink::inHistoryText() const {
+ if (_data) {
+ switch (_data->type) {
+ case GoogleMapsLink: return qsl("[ ") + lang(lng_maps_point) + qsl(" : ") + _link->text() + qsl(" ]");
+ }
+ }
+ return qsl("[ Link : ") + _link->text() + qsl(" ]");
+}
+
+int32 HistoryImageLink::fullWidth() const {
+ if (_data) {
+ switch (_data->type) {
+ case GoogleMapsLink: return st::locationSize.width();
+ }
+ }
+ return st::minPhotoSize;
+}
+
+int32 HistoryImageLink::fullHeight() const {
+ if (_data) {
+ switch (_data->type) {
+ case GoogleMapsLink: return st::locationSize.height();
+ }
+ }
+ return st::minPhotoSize;
+}
+
+void ViaInlineBotLink::onClick(Qt::MouseButton button) const {
+ App::insertBotCommand('@' + _bot->username);
+}
+
+HistoryMessageVia::HistoryMessageVia(int32 userId)
+: bot(App::userLoaded(peerFromUser(userId)))
+, width(0)
+, maxWidth(bot ? st::msgServiceNameFont->width(lng_inline_bot_via(lt_inline_bot, '@' + bot->username)) : 0)
+, lnk(new ViaInlineBotLink(bot)) {
+}
+
+bool HistoryMessageVia::isNull() const {
+ return !bot || bot->username.isEmpty();
+}
+
+void HistoryMessageVia::resize(int32 availw) {
+ if (availw < 0) {
+ text = QString();
+ width = 0;
+ } else {
+ text = lng_inline_bot_via(lt_inline_bot, '@' + bot->username);
+ if (availw < maxWidth) {
+ text = st::msgServiceNameFont->elided(text, availw);
+ width = st::msgServiceNameFont->width(text);
+ } else if (width < maxWidth) {
+ width = maxWidth;
+ }
+ }
}
HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg) :
@@ -6370,23 +6005,23 @@ HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPD
, _text(st::msgMinWidth)
, _textWidth(0)
, _textHeight(0)
+, _via(msg.has_via_bot_id() ? new HistoryMessageVia(msg.vvia_bot_id.v) : 0)
, _media(0)
-, _views(msg.has_views() ? msg.vviews.v : -1)
-{
+, _views(msg.has_views() ? msg.vviews.v : -1) {
QString text(textClean(qs(msg.vmessage)));
initTime();
initMedia(msg.has_media() ? (&msg.vmedia) : 0, text);
setText(text, msg.has_entities() ? entitiesFromMTP(msg.ventities.c_vector().v) : EntitiesInText());
}
-HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities, HistoryMedia *fromMedia) :
-HistoryItem(history, block, msgId, flags, date, from)
+HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities, HistoryMedia *fromMedia) :
+HistoryItem(history, block, msgId, flags, date, (flags & MTPDmessage::flag_from_id) ? from : 0)
, _text(st::msgMinWidth)
, _textWidth(0)
, _textHeight(0)
+, _via((flags & MTPDmessage::flag_via_bot_id) ? new HistoryMessageVia(viaBotId) : 0)
, _media(0)
-, _views(fromChannel() ? 1 : -1)
-{
+, _views(fromChannel() ? 1 : -1) {
initTime();
if (fromMedia) {
_media = fromMedia->clone();
@@ -6395,16 +6030,30 @@ HistoryItem(history, block, msgId, flags, date, from)
setText(msg, entities);
}
-HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, DocumentData *doc) :
-HistoryItem(history, block, msgId, flags, date, from)
+HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption) :
+HistoryItem(history, block, msgId, flags, date, (flags & MTPDmessage::flag_from_id) ? from : 0)
, _text(st::msgMinWidth)
, _textWidth(0)
, _textHeight(0)
+, _via((flags & MTPDmessage::flag_via_bot_id) ? new HistoryMessageVia(viaBotId) : 0)
, _media(0)
-, _views(fromChannel() ? 1 : -1)
-{
+, _views(fromChannel() ? 1 : -1) {
initTime();
- initMediaFromDocument(doc);
+ initMediaFromDocument(doc, caption);
+ setText(QString(), EntitiesInText());
+}
+
+HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption) :
+HistoryItem(history, block, msgId, flags, date, (flags & MTPDmessage::flag_from_id) ? from : 0)
+, _text(st::msgMinWidth)
+, _textWidth(0)
+, _textHeight(0)
+, _via((flags & MTPDmessage::flag_via_bot_id) ? new HistoryMessageVia(viaBotId) : 0)
+, _media(0)
+, _views(fromChannel() ? 1 : -1) {
+ initTime();
+ _media = new HistoryPhoto(photo, caption, this);
+ _media->regItem(this);
setText(QString(), EntitiesInText());
}
@@ -6458,7 +6107,7 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media, QString ¤tTex
case mtpc_messageMediaPhoto: {
const MTPDmessageMediaPhoto &photo(media->c_messageMediaPhoto());
if (photo.vphoto.type() == mtpc_photo) {
- _media = new HistoryPhoto(photo.vphoto.c_photo(), qs(photo.vcaption), this);
+ _media = new HistoryPhoto(App::feedPhoto(photo.vphoto.c_photo()), qs(photo.vcaption), this);
}
} break;
case mtpc_messageMediaVideo: {
@@ -6476,105 +6125,168 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media, QString ¤tTex
case mtpc_messageMediaDocument: {
const MTPDocument &document(media->c_messageMediaDocument().vdocument);
if (document.type() == mtpc_document) {
- DocumentData *doc = App::feedDocument(document);
- return initMediaFromDocument(doc);
+ return initMediaFromDocument(App::feedDocument(document), qs(media->c_messageMediaDocument().vcaption));
}
} break;
case mtpc_messageMediaWebPage: {
const MTPWebPage &d(media->c_messageMediaWebPage().vwebpage);
switch (d.type()) {
- case mtpc_webPageEmpty: initMediaFromText(currentText); break;
+ case mtpc_webPageEmpty: break;
case mtpc_webPagePending: {
- WebPageData *webPage = App::feedWebPage(d.c_webPagePending());
- _media = new HistoryWebPage(webPage);
+ _media = new HistoryWebPage(App::feedWebPage(d.c_webPagePending()));
} break;
case mtpc_webPage: {
_media = new HistoryWebPage(App::feedWebPage(d.c_webPage()));
} break;
}
} break;
- default: initMediaFromText(currentText); break;
};
if (_media) _media->regItem(this);
}
-void HistoryMessage::initMediaFromText(QString ¤tText) {
- QString lnk = currentText.trimmed();
- if (/*reYouTube1.match(currentText).hasMatch() || reYouTube2.match(currentText).hasMatch() || reInstagram.match(currentText).hasMatch() || */reVimeo.match(currentText).hasMatch()) {
- _media = new HistoryImageLink(lnk);
- currentText = QString();
- }
-}
-
-void HistoryMessage::initMediaFromDocument(DocumentData *doc) {
+void HistoryMessage::initMediaFromDocument(DocumentData *doc, const QString &caption) {
if (doc->sticker()) {
_media = new HistorySticker(doc);
+ } else if (doc->isAnimation()) {
+ _media = new HistoryGif(doc, caption, this);
} else {
- _media = new HistoryDocument(doc);
+ _media = new HistoryDocument(doc, caption, this);
}
_media->regItem(this);
}
+int32 HistoryMessage::plainMaxWidth() const {
+ return st::msgPadding.left() + _text.maxWidth() + st::msgPadding.right();
+}
+
void HistoryMessage::initDimensions() {
- if (justMedia()) {
- _media->initDimensions(this);
- _maxw = _media->maxWidth();
- _minh = _media->minHeight();
- } else {
- _maxw = _text.maxWidth();
- _minh = _text.minHeight();
- _maxw += st::msgPadding.left() + st::msgPadding.right();
+ if (drawBubble()) {
if (_media) {
_media->initDimensions(this);
- if (_media->isDisplayed() && _text.hasSkipBlock()) {
- _text.removeSkipBlock();
- _textWidth = 0;
- _textHeight = 0;
- } else if (!_media->isDisplayed() && !_text.hasSkipBlock()) {
+ if (_media->isDisplayed()) {
+ if (_text.hasSkipBlock()) {
+ _text.removeSkipBlock();
+ _textWidth = 0;
+ _textHeight = 0;
+ }
+ } else if (!_text.hasSkipBlock()) {
_text.setSkipBlock(skipBlockWidth(), skipBlockHeight());
_textWidth = 0;
_textHeight = 0;
}
- if (_media->isDisplayed()) {
- int32 maxw = _media->maxWidth() + st::msgPadding.left() + st::msgPadding.right();
- if (maxw > _maxw) _maxw = maxw;
- _minh += st::msgPadding.bottom() + _media->minHeight();
+ }
+
+ _maxw = plainMaxWidth();
+ if (_text.isEmpty()) {
+ _minh = 0;
+ } else {
+ _minh = st::msgPadding.top() + _text.minHeight() + st::msgPadding.bottom();
+ }
+ if (_media && _media->isDisplayed()) {
+ int32 maxw = _media->maxWidth();
+ if (maxw > _maxw) _maxw = maxw;
+ _minh += _media->minHeight();
+ }
+ if (!_media) {
+ if (displayFromName()) {
+ int32 namew = st::msgPadding.left() + _from->nameText.maxWidth() + st::msgPadding.right();
+ if (via() && !toHistoryForwarded()) {
+ namew += st::msgServiceFont->spacew + via()->maxWidth;
+ }
+ if (namew > _maxw) _maxw = namew;
+ } else if (via() && !toHistoryForwarded()) {
+ if (st::msgPadding.left() + via()->maxWidth + st::msgPadding.right() > _maxw) {
+ _maxw = st::msgPadding.left() + via()->maxWidth + st::msgPadding.right();
+ }
+ }
+ }
+ } else {
+ _media->initDimensions(this);
+ _maxw = _media->maxWidth();
+ _minh = _media->minHeight();
+ }
+}
+
+void HistoryMessage::countPositionAndSize(int32 &left, int32 &width) const {
+ int32 mwidth = qMin(int(st::msgMaxWidth), _maxw);
+ if (_media && _media->currentWidth() < mwidth) {
+ mwidth = qMax(_media->currentWidth(), qMin(mwidth, plainMaxWidth()));
+ }
+
+ left = (!fromChannel() && out()) ? st::msgMargin.right() : st::msgMargin.left();
+ if (displayFromPhoto()) {
+ left += st::msgPhotoSkip;
+ }
+
+ width = _history->width - st::msgMargin.left() - st::msgMargin.right();
+ if (width > mwidth) {
+ if (!fromChannel() && out()) {
+ left += width - mwidth;
+ }
+ width = mwidth;
+ }
+}
+
+void HistoryMessage::fromNameUpdated(int32 width) const {
+ _fromVersion = _from->nameVersion;
+ if (drawBubble() && displayFromName()) {
+ if (via() && !toHistoryForwarded()) {
+ via()->resize(width - st::msgPadding.left() - st::msgPadding.right() - _from->nameText.maxWidth() - st::msgServiceFont->spacew);
+ }
+ }
+}
+
+int32 HistoryMessage::addToOverview(AddToOverviewMethod method) {
+ if (!indexInOverview()) return 0;
+
+ int32 result = 0;
+ if (HistoryMedia *media = getMedia(true)) {
+ MediaOverviewType type = mediaToOverviewType(media);
+ if (type != OverviewCount) {
+ if (history()->addToOverview(type, id, method)) {
+ result |= (1 << type);
}
}
}
- fromNameUpdated();
+ if (hasTextLinks()) {
+ if (history()->addToOverview(OverviewLinks, id, method)) {
+ result |= (1 << OverviewLinks);
+ }
+ }
+ return result;
}
-void HistoryMessage::fromNameUpdated() const {
- if (_media) return;
- int32 _namew = (displayFromName() ? _from->nameText.maxWidth() : 0) + st::msgPadding.left() + st::msgPadding.right();
- if (_namew > _maxw) _maxw = _namew;
-}
-
-bool HistoryMessage::uploading() const {
- return _media ? _media->uploading() : false;
+void HistoryMessage::eraseFromOverview() {
+ if (HistoryMedia *media = getMedia(true)) {
+ MediaOverviewType type = mediaToOverviewType(media);
+ if (type != OverviewCount) {
+ history()->eraseFromOverview(type, id);
+ }
+ }
+ if (hasTextLinks()) {
+ history()->eraseFromOverview(OverviewLinks, id);
+ }
}
QString HistoryMessage::selectedText(uint32 selection) const {
- if (_media && selection == FullItemSel) {
+ if (_media && selection == FullSelection) {
QString text = _text.original(0, 0xFFFF, Text::ExpandLinksAll), mediaText = _media->inHistoryText();
return text.isEmpty() ? mediaText : (mediaText.isEmpty() ? text : (text + ' ' + mediaText));
}
- uint16 selectedFrom = (selection == FullItemSel) ? 0 : ((selection >> 16) & 0xFFFF);
- uint16 selectedTo = (selection == FullItemSel) ? 0xFFFF : (selection & 0xFFFF);
+ uint16 selectedFrom = (selection == FullSelection) ? 0 : ((selection >> 16) & 0xFFFF);
+ uint16 selectedTo = (selection == FullSelection) ? 0xFFFF : (selection & 0xFFFF);
return _text.original(selectedFrom, selectedTo, Text::ExpandLinksAll);
}
QString HistoryMessage::inDialogsText() const {
- QString result = _media ? _media->inDialogsText() : QString();
- return result.isEmpty() ? _text.original(0, 0xFFFF, Text::ExpandLinksNone) : result;
+ return emptyText() ? (_media ? _media->inDialogsText() : QString()) : _text.original(0, 0xFFFF, Text::ExpandLinksNone);
}
HistoryMedia *HistoryMessage::getMedia(bool inOverview) const {
return _media;
}
-void HistoryMessage::setMedia(const MTPMessageMedia *media, bool allowEmitResize) {
+void HistoryMessage::setMedia(const MTPMessageMedia *media) {
if ((!_media || _media->isImageLink()) && (!media || media->type() == mtpc_messageMediaEmpty)) return;
bool mediaWasDisplayed = false;
@@ -6594,46 +6306,41 @@ void HistoryMessage::setMedia(const MTPMessageMedia *media, bool allowEmitResize
_textWidth = 0;
_textHeight = 0;
}
- initDimensions();
- if (allowEmitResize && App::main()) App::main()->itemResized(this);
}
void HistoryMessage::setText(const QString &text, const EntitiesInText &entities) {
- if (!_media || !text.isEmpty()) { // !justMedia()
- textstyleSet(&((out() && !fromChannel()) ? st::outTextStyle : st::inTextStyle));
- if (_media && _media->isDisplayed()) {
- _text.setMarkedText(st::msgFont, text, entities, itemTextOptions(this));
- } else {
- _text.setMarkedText(st::msgFont, text + skipBlock(), entities, itemTextOptions(this));
- }
- textstyleRestore();
- if (id > 0) {
- for (int32 i = 0, l = entities.size(); i != l; ++i) {
- if (entities.at(i).type == EntityInTextUrl || entities.at(i).type == EntityInTextCustomUrl || entities.at(i).type == EntityInTextEmail) {
- _flags |= MTPDmessage_flag_HAS_TEXT_LINKS;
- break;
- }
- }
- }
- _textWidth = 0;
- _textHeight = 0;
+ textstyleSet(&((out() && !fromChannel()) ? st::outTextStyle : st::inTextStyle));
+ if (_media && _media->isDisplayed()) {
+ _text.setMarkedText(st::msgFont, text, entities, itemTextOptions(this));
+ } else {
+ _text.setMarkedText(st::msgFont, text + skipBlock(), entities, itemTextOptions(this));
}
+ textstyleRestore();
+
+ for (int32 i = 0, l = entities.size(); i != l; ++i) {
+ if (entities.at(i).type == EntityInTextUrl || entities.at(i).type == EntityInTextCustomUrl || entities.at(i).type == EntityInTextEmail) {
+ _flags |= MTPDmessage_flag_HAS_TEXT_LINKS;
+ break;
+ }
+ }
+ _textWidth = 0;
+ _textHeight = 0;
}
QString HistoryMessage::originalText() const {
- return _text.isEmpty() ? QString() : _text.original();
+ return emptyText() ? QString() : _text.original();
}
EntitiesInText HistoryMessage::originalEntities() const {
- return _text.isEmpty() ? EntitiesInText() : _text.originalEntities();
+ return emptyText() ? EntitiesInText() : _text.originalEntities();
}
bool HistoryMessage::textHasLinks() {
- return _text.hasLinks();
+ return emptyText() ? false : _text.hasLinks();
}
-void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, bool selected, InfoDisplayType type) const {
- p.setFont(st::msgDateFont->f);
+void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const {
+ p.setFont(st::msgDateFont);
bool outbg = out() && !fromChannel(), overimg = (type == InfoDisplayOverImage);
int32 infoRight = right, infoBottom = bottom;
@@ -6641,7 +6348,7 @@ void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, bool select
case InfoDisplayDefault:
infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
- p.setPen((selected ? (outbg ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (outbg ? st::msgOutDateColor : st::msgInDateColor))->p);
+ p.setPen((selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg))->p);
break;
case InfoDisplayOverImage:
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
@@ -6651,11 +6358,13 @@ void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, bool select
}
int32 infoW = HistoryMessage::infoWidth();
+ if (rtl()) infoRight = width - infoRight + infoW;
+
int32 dateX = infoRight - infoW;
int32 dateY = infoBottom - st::msgDateFont->height;
if (type == InfoDisplayOverImage) {
int32 dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y();
- App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgDateImgSelectBg : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
+ App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
}
dateX += HistoryMessage::timeLeft();
@@ -6701,7 +6410,7 @@ void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, bool select
}
}
-void HistoryMessage::setViewsCount(int32 count) {
+void HistoryMessage::setViewsCount(int32 count, bool reinit) {
if (_views == count || (count >= 0 && _views > count)) return;
int32 was = _viewsWidth;
@@ -6709,15 +6418,17 @@ void HistoryMessage::setViewsCount(int32 count) {
_viewsText = (_views >= 0) ? formatViewsCount(_views) : QString();
_viewsWidth = _viewsText.isEmpty() ? 0 : st::msgDateFont->width(_viewsText);
if (was == _viewsWidth) {
- if (App::main()) App::main()->msgUpdated(this);
+ Ui::repaintHistoryItem(this);
} else {
if (_text.hasSkipBlock()) {
_text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight());
_textWidth = 0;
_textHeight = 0;
}
- initDimensions();
- if (App::main()) App::main()->itemResized(this);
+ if (reinit) {
+ initDimensions();
+ Notify::historyItemResized(this);
+ }
}
}
@@ -6725,7 +6436,7 @@ void HistoryMessage::setId(MsgId newId) {
bool wasPositive = (id > 0), positive = (newId > 0);
HistoryItem::setId(newId);
if (wasPositive == positive) {
- if (App::main()) App::main()->msgUpdated(this);
+ Ui::repaintHistoryItem(this);
} else {
if (_text.hasSkipBlock()) {
_text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight());
@@ -6733,21 +6444,22 @@ void HistoryMessage::setId(MsgId newId) {
_textHeight = 0;
}
initDimensions();
- if (App::main()) App::main()->itemResized(this);
+ Notify::historyItemResized(this);
}
}
-void HistoryMessage::draw(Painter &p, uint32 selection) const {
- bool outbg = out() && !fromChannel();
+void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const {
+ bool outbg = out() && !fromChannel(), bubble = drawBubble(), selected = (selection == FullSelection);
textstyleSet(&(outbg ? st::outTextStyle : st::inTextStyle));
- uint64 ms = App::main() ? App::main()->animActiveTime(this) : 0;
- if (ms) {
- if (ms > st::activeFadeInDuration + st::activeFadeOutDuration) {
+ uint64 animms = App::main() ? App::main()->animActiveTimeStart(this) : 0;
+ if (animms > 0 && animms <= ms) {
+ animms = ms - animms;
+ if (animms > st::activeFadeInDuration + st::activeFadeOutDuration) {
App::main()->stopAnimActive();
} else {
- float64 dt = (ms > st::activeFadeInDuration) ? (1 - (ms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (ms / float64(st::activeFadeInDuration));
+ float64 dt = (animms > st::activeFadeInDuration) ? (1 - (animms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (animms / float64(st::activeFadeInDuration));
float64 o = p.opacity();
p.setOpacity(o * dt);
p.fillRect(0, 0, _history->width, _height, textstyleCurrent()->selectOverlay->b);
@@ -6755,154 +6467,154 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const {
}
}
- bool selected = (selection == FullItemSel);
+ int32 left = 0, width = 0;
+ countPositionAndSize(left, width);
if (_from->nameVersion > _fromVersion) {
- fromNameUpdated();
- _fromVersion = _from->nameVersion;
- }
- int32 left = fromChannel() ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out() ? st::msgMargin.right() : st::msgMargin.left()), width = _history->width - st::msgMargin.left() - st::msgMargin.right(), mwidth = st::msgMaxWidth;
- if (justMedia()) {
- if (_media->maxWidth() > mwidth) mwidth = _media->maxWidth();
- if (_media->currentWidth() < mwidth) mwidth = _media->currentWidth();
- }
- if (width > mwidth) {
- if (fromChannel()) {
-// left += (width - mwidth) / 2;
- } else if (out()) {
- left += width - mwidth;
- }
- width = mwidth;
+ fromNameUpdated(width);
}
if (displayFromPhoto()) {
- p.drawPixmap(left, _height - st::msgMargin.bottom() - st::msgPhotoSize, _from->photo->pixRounded(st::msgPhotoSize));
-// width -= st::msgPhotoSkip;
- left += st::msgPhotoSkip;
+ p.drawPixmap(left - st::msgPhotoSkip, _height - st::msgMargin.bottom() - st::msgPhotoSize, _from->photo->pixRounded(st::msgPhotoSize));
}
if (width < 1) return;
- if (width >= _maxw) {
- if (fromChannel()) {
-// left += (width - _maxw) / 2;
- } else if (out()) {
- left += width - _maxw;
- }
- width = _maxw;
- }
- if (justMedia()) {
- p.save();
- p.translate(left, st::msgMargin.top());
- _media->draw(p, this, selected);
- p.restore();
- } else {
+ if (bubble) {
QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
- style::color bg(selected ? (outbg ? st::msgOutSelectBg : st::msgInSelectBg) : (outbg ? st::msgOutBg : st::msgInBg));
- style::color sh(selected ? (outbg ? st::msgOutSelectShadow : st::msgInSelectShadow) : (outbg ? st::msgOutShadow : st::msgInShadow));
+ style::color bg(selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg));
+ style::color sh(selected ? (outbg ? st::msgOutShadowSelected : st::msgInShadowSelected) : (outbg ? st::msgOutShadow : st::msgInShadow));
RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners));
App::roundRect(p, r, bg, cors, &sh);
if (displayFromName()) {
- p.setFont(st::msgNameFont->f);
+ p.setFont(st::msgNameFont);
if (fromChannel()) {
- p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor);
+ p.setPen(selected ? st::msgInServiceFgSelected : st::msgInServiceFg);
} else {
p.setPen(_from->color);
}
_from->nameText.drawElided(p, r.left() + st::msgPadding.left(), r.top() + st::msgPadding.top(), width - st::msgPadding.left() - st::msgPadding.right());
+ if (via() && !toHistoryForwarded() && width > st::msgPadding.left() + st::msgPadding.right() + _from->nameText.maxWidth() + st::msgServiceFont->spacew) {
+ p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
+ p.drawText(r.left() + st::msgPadding.left() + _from->nameText.maxWidth() + st::msgServiceFont->spacew, r.top() + st::msgPadding.top() + st::msgServiceFont->ascent, via()->text);
+ }
r.setTop(r.top() + st::msgNameFont->height);
}
+
QRect trect(r.marginsAdded(-st::msgPadding));
drawMessageText(p, trect, selection);
- if (_media) {
+ if (_media && _media->isDisplayed()) {
p.save();
- p.translate(left + st::msgPadding.left(), _height - st::msgMargin.bottom() - st::msgPadding.bottom() - _media->height());
- _media->draw(p, this, selected);
+ int32 top = _height - st::msgMargin.bottom() - _media->height();
+ p.translate(left, top);
+ _media->draw(p, this, r.translated(-left, -top), selected, ms);
p.restore();
+ if (!_media->customInfoLayout()) {
+ HistoryMessage::drawInfo(p, r.x() + r.width(), r.y() + r.height(), 2 * r.x() + r.width(), selected, InfoDisplayDefault);
+ }
+ } else {
+ HistoryMessage::drawInfo(p, r.x() + r.width(), r.y() + r.height(), 2 * r.x() + r.width(), selected, InfoDisplayDefault);
}
- HistoryMessage::drawInfo(p, r.x() + r.width(), r.y() + r.height(), selected, InfoDisplayDefault);
+ } else {
+ p.save();
+ int32 top = st::msgMargin.top();
+ p.translate(left, top);
+ _media->draw(p, this, r.translated(-left, -top), selected, ms);
+ p.restore();
}
textstyleRestore();
}
-void HistoryMessage::drawMessageText(Painter &p, const QRect &trect, uint32 selection) const {
- p.setPen(st::msgColor->p);
- p.setFont(st::msgFont->f);
- uint16 selectedFrom = (selection == FullItemSel) ? 0 : (selection >> 16) & 0xFFFF;
- uint16 selectedTo = (selection == FullItemSel) ? 0 : selection & 0xFFFF;
- _text.draw(p, trect.x(), trect.y(), trect.width(), Qt::AlignLeft, 0, -1, selectedFrom, selectedTo);
+void HistoryMessage::drawMessageText(Painter &p, QRect trect, uint32 selection) const {
+ bool outbg = out() && !fromChannel(), selected = (selection == FullSelection);
+ if (!displayFromName() && via() && !toHistoryForwarded()) {
+ p.setFont(st::msgServiceNameFont);
+ p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
+ p.drawTextLeft(trect.left(), trect.top(), _history->width, via()->text);
+ trect.setY(trect.y() + st::msgServiceNameFont->height);
+ }
+
+ p.setPen(st::msgColor);
+ p.setFont(st::msgFont);
+ uint16 selectedFrom = (selection == FullSelection) ? 0 : (selection >> 16) & 0xFFFF;
+ uint16 selectedTo = (selection == FullSelection) ? 0 : selection & 0xFFFF;
+ _text.draw(p, trect.x(), trect.y(), trect.width(), style::al_left, 0, -1, selectedFrom, selectedTo);
+}
+
+void HistoryMessage::destroy() {
+ eraseFromOverview();
+ HistoryItem::destroy();
}
int32 HistoryMessage::resize(int32 width) {
if (width < st::msgMinWidth) return _height;
width -= st::msgMargin.left() + st::msgMargin.right();
- if (justMedia()) {
- _height = _media->resize(width, this);
- } else {
- if (width < st::msgPadding.left() + st::msgPadding.right() + 1) {
- width = st::msgPadding.left() + st::msgPadding.right() + 1;
- } else if (width > st::msgMaxWidth) {
- width = st::msgMaxWidth;
- }
- int32 nwidth = qMax(width - st::msgPadding.left() - st::msgPadding.right(), 0);
- if (nwidth != _textWidth) {
- _textWidth = nwidth;
- textstyleSet(&((out() && !fromChannel()) ? st::outTextStyle : st::inTextStyle));
- _textHeight = _text.countHeight(nwidth);
- textstyleRestore();
- }
+ if (width < st::msgPadding.left() + st::msgPadding.right() + 1) {
+ width = st::msgPadding.left() + st::msgPadding.right() + 1;
+ } else if (width > st::msgMaxWidth) {
+ width = st::msgMaxWidth;
+ }
+ if (drawBubble()) {
+ bool media = (_media && _media->isDisplayed());
if (width >= _maxw) {
_height = _minh;
- if (_media) _media->resize(_maxw - st::msgPadding.left() - st::msgPadding.right(), this);
+ if (media) _media->resize(_maxw, this);
} else {
- _height = _textHeight;
- if (_media && _media->isDisplayed()) _height += st::msgPadding.bottom() + _media->resize(nwidth, this);
+ if (_text.isEmpty()) {
+ _height = 0;
+ } else {
+ int32 textWidth = qMax(width - st::msgPadding.left() - st::msgPadding.right(), 1);
+ if (textWidth != _textWidth) {
+ textstyleSet(&((out() && !fromChannel()) ? st::outTextStyle : st::inTextStyle));
+ _textWidth = textWidth;
+ _textHeight = _text.countHeight(textWidth);
+ textstyleRestore();
+ }
+ _height = st::msgPadding.top() + _textHeight + st::msgPadding.bottom();
+ }
+ if (media) _height += _media->resize(width, this);
}
+
if (displayFromName()) {
- _height += st::msgNameFont->height;
+ if (emptyText()) {
+ _height += st::msgPadding.top() + st::msgNameFont->height + st::mediaHeaderSkip;
+ } else {
+ _height += st::msgNameFont->height;
+ }
+ int32 l = 0, w = 0;
+ countPositionAndSize(l, w);
+ fromNameUpdated(w);
+ } else if (via() && !toHistoryForwarded()) {
+ int32 l = 0, w = 0;
+ countPositionAndSize(l, w);
+ via()->resize(w - st::msgPadding.left() - st::msgPadding.right());
+ if (emptyText() && !displayFromName()) {
+ _height += st::msgPadding.top() + st::msgNameFont->height + st::mediaHeaderSkip;
+ } else {
+ _height += st::msgNameFont->height;
+ }
}
- _height += st::msgPadding.top() + st::msgPadding.bottom();
+ } else {
+ _height = _media->resize(width, this);
}
_height += st::msgMargin.top() + st::msgMargin.bottom();
return _height;
}
bool HistoryMessage::hasPoint(int32 x, int32 y) const {
- int32 left = fromChannel() ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out() ? st::msgMargin.right() : st::msgMargin.left()), width = _history->width - st::msgMargin.left() - st::msgMargin.right(), mwidth = st::msgMaxWidth;
- if (justMedia()) {
- if (_media->maxWidth() > mwidth) mwidth = _media->maxWidth();
- if (_media->currentWidth() < mwidth) mwidth = _media->currentWidth();
- }
- if (width > mwidth) {
- if (fromChannel()) {
-// left += (width - mwidth) / 2;
- } else if (out()) {
- left += width - mwidth;
- }
- width = mwidth;
- }
-
- if (displayFromPhoto()) { // from user left photo
- left += st::msgPhotoSkip;
- }
+ int32 left = 0, width = 0;
+ countPositionAndSize(left, width);
if (width < 1) return false;
- if (width >= _maxw) {
- if (fromChannel()) {
-// left += (width - _maxw) / 2;
- } else if (out()) {
- left += width - _maxw;
- }
- width = _maxw;
- }
- if (justMedia()) {
+ if (drawBubble()) {
+ QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
+ return r.contains(x, y);
+ } else {
return _media->hasPoint(x - left, y - st::msgMargin.top(), this);
}
- QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
- return r.contains(x, y);
}
bool HistoryMessage::pointInTime(int32 right, int32 bottom, int32 x, int32 y, InfoDisplayType type) const {
@@ -6926,67 +6638,65 @@ void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32
state = HistoryDefaultCursorState;
lnk = TextLinkPtr();
- int32 left = fromChannel() ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out() ? st::msgMargin.right() : st::msgMargin.left()), width = _history->width - st::msgMargin.left() - st::msgMargin.right(), mwidth = st::msgMaxWidth;
- if (justMedia()) {
- if (_media->maxWidth() > mwidth) mwidth = _media->maxWidth();
- if (_media->currentWidth() < mwidth) mwidth = _media->currentWidth();
- }
- if (width > mwidth) {
- if (fromChannel()) {
-// left += (width - mwidth) / 2;
- } else if (out()) {
- left += width - mwidth;
- }
- width = mwidth;
- }
-
- if (displayFromPhoto()) { // from user left photo
- if (x >= left && x < left + st::msgPhotoSize && y >= _height - st::msgMargin.bottom() - st::msgPhotoSize && y < _height - st::msgMargin.bottom()) {
+ int32 left = 0, width = 0;
+ countPositionAndSize(left, width);
+ if (displayFromPhoto()) {
+ if (x >= left - st::msgPhotoSkip && x < left - st::msgPhotoSkip + st::msgPhotoSize && y >= _height - st::msgMargin.bottom() - st::msgPhotoSize && y < _height - st::msgMargin.bottom()) {
lnk = _from->lnk;
return;
}
-// width -= st::msgPhotoSkip;
- left += st::msgPhotoSkip;
}
if (width < 1) return;
- if (width >= _maxw) {
- if (fromChannel()) {
-// left += (width - _maxw) / 2;
- } else if (out()) {
- left += width - _maxw;
+ if (drawBubble()) {
+ QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
+ if (displayFromName()) { // from user left name
+ if (y >= r.top() + st::msgPadding.top() && y < r.top() + st::msgPadding.top() + st::msgNameFont->height) {
+ if (x >= r.left() + st::msgPadding.left() && x < r.left() + r.width() - st::msgPadding.right() && x < r.left() + st::msgPadding.left() + _from->nameText.maxWidth()) {
+ lnk = _from->lnk;
+ return;
+ }
+ if (via() && !toHistoryForwarded() && x >= r.left() + st::msgPadding.left() + _from->nameText.maxWidth() + st::msgServiceFont->spacew && x < r.left() + st::msgPadding.left() + _from->nameText.maxWidth() + st::msgServiceFont->spacew + via()->width) {
+ lnk = via()->lnk;
+ return;
+ }
+ }
+ r.setTop(r.top() + st::msgNameFont->height);
}
- width = _maxw;
- }
- if (justMedia()) {
+ getStateFromMessageText(lnk, state, x, y, r);
+ } else {
_media->getState(lnk, state, x - left, y - st::msgMargin.top(), this);
- return;
}
- QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
- if (displayFromName()) { // from user left name
- if (x >= r.left() + st::msgPadding.left() && y >= r.top() + st::msgPadding.top() && y < r.top() + st::msgPadding.top() + st::msgNameFont->height && x < r.left() + r.width() - st::msgPadding.right() && x < r.left() + st::msgPadding.left() + _from->nameText.maxWidth()) {
- lnk = _from->lnk;
- return;
- }
- r.setTop(r.top() + st::msgNameFont->height);
- }
-
- getStateFromMessageText(lnk, state, x, y, r);
}
void HistoryMessage::getStateFromMessageText(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const QRect &r) const {
- bool inDate = HistoryMessage::pointInTime(r.x() + r.width(), r.y() + r.height(), x, y, InfoDisplayDefault);
+ bool inDate = false;
QRect trect(r.marginsAdded(-st::msgPadding));
+
+ if (!displayFromName() && via() && !toHistoryForwarded()) {
+ if (x >= trect.left() && y >= trect.top() && y < trect.top() + st::msgNameFont->height && x < trect.left() + via()->width) {
+ lnk = via()->lnk;
+ return;
+ }
+ trect.setTop(trect.top() + st::msgNameFont->height);
+ }
+
TextLinkPtr medialnk;
if (_media && _media->isDisplayed()) {
- if (y >= trect.bottom() - _media->height() && y < trect.bottom()) {
- _media->getState(lnk, state, x - trect.left(), y + _media->height() - trect.bottom(), this);
+ if (!_media->customInfoLayout()) {
+ inDate = HistoryMessage::pointInTime(r.x() + r.width(), r.y() + r.height(), x, y, InfoDisplayDefault);
+ }
+ if (y >= r.bottom() - _media->height() && y < r.bottom()) {
+ _media->getState(lnk, state, x - r.left(), y - (r.bottom() - _media->height()), this);
if (inDate) state = HistoryInDateCursorState;
return;
}
- trect.setBottom(trect.bottom() - _media->height() - st::msgPadding.bottom());
+ trect.setBottom(trect.bottom() - _media->height());
+ } else {
+ inDate = HistoryMessage::pointInTime(r.x() + r.width(), r.y() + r.height(), x, y, InfoDisplayDefault);
}
+
textstyleSet(&((out() && !fromChannel()) ? st::outTextStyle : st::inTextStyle));
bool inText = false;
_text.getState(lnk, inText, x - trect.x(), y - trect.y(), trect.width());
@@ -7005,43 +6715,26 @@ void HistoryMessage::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x,
symbol = 0;
after = false;
upon = false;
- if (justMedia()) return;
+ if (drawBubble()) {
+ int32 left = 0, width = 0;
+ countPositionAndSize(left, width);
+ if (width < 1) return;
- int32 left = fromChannel() ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out() ? st::msgMargin.right() : st::msgMargin.left()), width = _history->width - st::msgMargin.left() - st::msgMargin.right(), mwidth = st::msgMaxWidth;
- if (width > mwidth) {
- if (fromChannel()) {
-// left += (width - mwidth) / 2;
- } else if (out()) {
- left += width - mwidth;
+ QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
+ if (displayFromName()) { // from user left name
+ r.setTop(r.top() + st::msgNameFont->height);
+ } else if (via() && !toHistoryForwarded()) {
+ r.setTop(r.top() + st::msgNameFont->height);
}
- width = mwidth;
- }
-
- if (displayFromPhoto()) { // from user left photo
-// width -= st::msgPhotoSkip;
- left += st::msgPhotoSkip;
- }
- if (width < 1) return;
-
- if (width >= _maxw) {
- if (fromChannel()) {
-// left += (width - _maxw) / 2;
- } else if (out()) {
- left += width - _maxw;
+ QRect trect(r.marginsAdded(-st::msgPadding));
+ if (_media && _media->isDisplayed()) {
+ trect.setBottom(trect.bottom() - _media->height());
}
- width = _maxw;
+
+ textstyleSet(&((out() && !fromChannel()) ? st::outTextStyle : st::inTextStyle));
+ _text.getSymbol(symbol, after, upon, x - trect.x(), y - trect.y(), trect.width());
+ textstyleRestore();
}
- QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
- if (displayFromName()) { // from user left name
- r.setTop(r.top() + st::msgNameFont->height);
- }
- QRect trect(r.marginsAdded(-st::msgPadding));
- if (_media && _media->isDisplayed()) {
- trect.setBottom(trect.bottom() - _media->height() - st::msgPadding.bottom());
- }
- textstyleSet(&((out() && !fromChannel()) ? st::outTextStyle : st::inTextStyle));
- _text.getSymbol(symbol, after, upon, x - trect.x(), y - trect.y(), trect.width());
- textstyleRestore();
}
void HistoryMessage::drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const {
@@ -7060,7 +6753,7 @@ void HistoryMessage::drawInDialog(Painter &p, const QRect &r, bool act, const Hi
if (r.width()) {
textstyleSet(&(act ? st::dlgActiveTextStyle : st::dlgTextStyle));
p.setFont(st::dlgHistFont->f);
- p.setPen((act ? st::dlgActiveColor : (justMedia() ? st::dlgSystemColor : st::dlgTextColor))->p);
+ p.setPen((act ? st::dlgActiveColor : (emptyText() ? st::dlgSystemColor : st::dlgTextColor))->p);
cache.drawElided(p, r.left(), r.top(), r.width(), r.height() / st::dlgHistFont->height);
textstyleRestore();
}
@@ -7079,33 +6772,32 @@ QString HistoryMessage::notificationText() const {
HistoryMessage::~HistoryMessage() {
if (_media) {
_media->unregItem(this);
- delete _media;
+ deleteAndMark(_media);
}
+ deleteAndMark(_via);
if (_flags & MTPDmessage::flag_reply_markup) {
App::clearReplyMarkup(channelId(), id);
}
}
-HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const MTPDmessage &msg) : HistoryMessage(history, block, msg)
+HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const MTPDmessage &msg)
+: HistoryMessage(history, block, msg)
, fwdDate(::date(msg.vfwd_date))
, fwdFrom(App::peer(peerFromMTP(msg.vfwd_from_id)))
, fwdFromVersion(fwdFrom->nameVersion)
-, fromWidth(st::msgServiceFont->width(lang(lng_forwarded_from)) + st::msgServiceFont->spacew)
-{
- fwdNameUpdated();
+, fromWidth(st::msgServiceFont->width(lang(lng_forwarded_from)) + st::msgServiceFont->spacew) {
}
-HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg) : HistoryMessage(history, block, id, newMessageFlags(history->peer) | (!history->peer->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage::flag_media_unread : 0), date, from, msg->HistoryMessage::originalText(), msg->HistoryMessage::originalEntities(), msg->getMedia())
+HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg)
+: HistoryMessage(history, block, id, newForwardedFlags(history->peer, from, msg), msg->via() ? peerToUser(msg->viaBot()->id) : 0, date, from, msg->HistoryMessage::originalText(), msg->HistoryMessage::originalEntities(), msg->getMedia())
, fwdDate(msg->dateForwarded())
, fwdFrom(msg->fromForwarded())
, fwdFromVersion(fwdFrom->nameVersion)
-, fromWidth(st::msgServiceFont->width(lang(lng_forwarded_from)) + st::msgServiceFont->spacew)
-{
- fwdNameUpdated();
+, fromWidth(st::msgServiceFont->width(lang(lng_forwarded_from)) + st::msgServiceFont->spacew) {
}
QString HistoryForwarded::selectedText(uint32 selection) const {
- if (selection != FullItemSel) return HistoryMessage::selectedText(selection);
+ if (selection != FullSelection) return HistoryMessage::selectedText(selection);
QString result, original = HistoryMessage::selectedText(selection);
result.reserve(lang(lng_forwarded_from).size() + fwdFrom->name.size() + 4 + original.size());
result.append('[').append(lang(lng_forwarded_from)).append(' ').append(fwdFrom->name).append(qsl("]\n")).append(original);
@@ -7113,83 +6805,92 @@ QString HistoryForwarded::selectedText(uint32 selection) const {
}
void HistoryForwarded::initDimensions() {
- HistoryMessage::initDimensions();
fwdNameUpdated();
+ HistoryMessage::initDimensions();
+ if (!_media) {
+ int32 _namew = st::msgPadding.left() + fromWidth + fwdFromName.maxWidth() + st::msgPadding.right();
+ if (via()) {
+ _namew += st::msgServiceFont->spacew + via()->maxWidth;
+ }
+ if (_namew > _maxw) _maxw = _namew;
+ }
}
void HistoryForwarded::fwdNameUpdated() const {
- fwdFromName.setText(st::msgServiceNameFont, App::peerName(fwdFrom), _textNameOptions);
- if (justMedia()) return;
- int32 _namew = fromWidth + fwdFromName.maxWidth() + st::msgPadding.left() + st::msgPadding.right();
- if (_namew > _maxw) _maxw = _namew;
+ QString fwdName((via() && fwdFrom->isUser()) ? fwdFrom->asUser()->firstName : App::peerName(fwdFrom));
+ fwdFromName.setText(st::msgServiceNameFont, fwdName, _textNameOptions);
+ if (via()) {
+ int32 l = 0, w = 0;
+ countPositionAndSize(l, w);
+ via()->resize(w - st::msgPadding.left() - st::msgPadding.right() - fromWidth - fwdFromName.maxWidth() - st::msgServiceFont->spacew);
+ }
}
-void HistoryForwarded::draw(Painter &p, uint32 selection) const {
- if (!justMedia() && fwdFrom->nameVersion > fwdFromVersion) {
+void HistoryForwarded::draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const {
+ if (drawBubble() && fwdFrom->nameVersion > fwdFromVersion) {
fwdNameUpdated();
fwdFromVersion = fwdFrom->nameVersion;
}
- HistoryMessage::draw(p, selection);
+ HistoryMessage::draw(p, r, selection, ms);
}
void HistoryForwarded::drawForwardedFrom(Painter &p, int32 x, int32 y, int32 w, bool selected) const {
style::font serviceFont(st::msgServiceFont), serviceName(st::msgServiceNameFont);
bool outbg = out() && !fromChannel();
- p.setPen((selected ? (outbg ? st::msgOutServiceSelColor : st::msgInServiceSelColor) : (outbg ? st::msgOutServiceColor : st::msgInServiceColor))->p);
- p.setFont(serviceFont->f);
+ p.setPen((selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg))->p);
+ p.setFont(serviceFont);
- if (w >= fromWidth) {
+ if (via() && w > fromWidth + fwdFromName.maxWidth() + serviceFont->spacew) {
p.drawText(x, y + serviceFont->ascent, lang(lng_forwarded_from));
- p.setFont(serviceName->f);
+ p.setFont(serviceName);
+ fwdFromName.draw(p, x + fromWidth, y, w - fromWidth);
+
+ p.drawText(x + fromWidth + fwdFromName.maxWidth() + serviceFont->spacew, y + serviceFont->ascent, via()->text);
+ } else if (w > fromWidth) {
+ p.drawText(x, y + serviceFont->ascent, lang(lng_forwarded_from));
+
+ p.setFont(serviceName);
fwdFromName.drawElided(p, x + fromWidth, y, w - fromWidth);
} else {
p.drawText(x, y + serviceFont->ascent, serviceFont->elided(lang(lng_forwarded_from), w));
}
}
-void HistoryForwarded::drawMessageText(Painter &p, const QRect &trect, uint32 selection) const {
- drawForwardedFrom(p, trect.x(), trect.y(), trect.width(), (selection == FullItemSel));
-
- QRect realtrect(trect);
- realtrect.setY(trect.y() + st::msgServiceNameFont->height);
- HistoryMessage::drawMessageText(p, realtrect, selection);
+void HistoryForwarded::drawMessageText(Painter &p, QRect trect, uint32 selection) const {
+ if (displayForwardedFrom()) {
+ drawForwardedFrom(p, trect.x(), trect.y(), trect.width(), (selection == FullSelection));
+ trect.setY(trect.y() + st::msgServiceNameFont->height);
+ }
+ HistoryMessage::drawMessageText(p, trect, selection);
}
int32 HistoryForwarded::resize(int32 width) {
HistoryMessage::resize(width);
-
- if (!justMedia()) _height += st::msgServiceNameFont->height;
+ if (drawBubble()) {
+ if (displayForwardedFrom()) {
+ if (emptyText() && !displayFromName()) {
+ _height += st::msgPadding.top() + st::msgServiceNameFont->height + st::mediaHeaderSkip;
+ } else {
+ _height += st::msgServiceNameFont->height;
+ }
+ if (via()) {
+ int32 l = 0, w = 0;
+ countPositionAndSize(l, w);
+ via()->resize(w - st::msgPadding.left() - st::msgPadding.right() - fromWidth - fwdFromName.maxWidth() - st::msgServiceFont->spacew);
+ }
+ }
+ }
return _height;
}
bool HistoryForwarded::hasPoint(int32 x, int32 y) const {
- if (!justMedia()) {
- int32 left = fromChannel() ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out() ? st::msgMargin.right() : st::msgMargin.left()), width = _history->width - st::msgMargin.left() - st::msgMargin.right(), mwidth = st::msgMaxWidth;
- if (width > mwidth) {
- if (fromChannel()) {
-//f left += (width - mwidth) / 2;
- } else if (out()) {
- left += width - mwidth;
- }
- width = mwidth;
- }
-
- if (displayFromPhoto()) { // from user left photo
-// width -= st::msgPhotoSkip;
- left += st::msgPhotoSkip;
- }
+ if (drawBubble() && displayForwardedFrom()) {
+ int32 left = 0, width = 0;
+ countPositionAndSize(left, width);
if (width < 1) return false;
- if (width >= _maxw) {
- if (fromChannel()) {
-// left += (width - _maxw) / 2;
- } else if (out()) {
- left += width - _maxw;
- }
- width = _maxw;
- }
QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
return r.contains(x, y);
}
@@ -7200,34 +6901,16 @@ void HistoryForwarded::getState(TextLinkPtr &lnk, HistoryCursorState &state, int
lnk = TextLinkPtr();
state = HistoryDefaultCursorState;
- if (!justMedia()) {
- int32 left = fromChannel() ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out() ? st::msgMargin.right() : st::msgMargin.left()), width = _history->width - st::msgMargin.left() - st::msgMargin.right(), mwidth = st::msgMaxWidth;
- if (width > mwidth) {
- if (fromChannel()) {
-// left += (width - mwidth) / 2;
- } else if (out()) {
- left += width - mwidth;
- }
- width = mwidth;
- }
-
- if (displayFromPhoto()) { // from user left photo
- if (x >= left && x < left + st::msgPhotoSize) {
+ if (drawBubble() && displayForwardedFrom()) {
+ int32 left = 0, width = 0;
+ countPositionAndSize(left, width);
+ if (displayFromPhoto()) {
+ if (x >= left - st::msgPhotoSkip && x < left - st::msgPhotoSkip + st::msgPhotoSize) {
return HistoryMessage::getState(lnk, state, x, y);
}
-// width -= st::msgPhotoSkip;
- left += st::msgPhotoSkip;
}
if (width < 1) return;
- if (width >= _maxw) {
- if (fromChannel()) {
-// left += (width - _maxw) / 2;
- } else if (out()) {
- left += width - _maxw;
- }
- width = _maxw;
- }
QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
if (displayFromName()) {
style::font nameFont(st::msgNameFont);
@@ -7248,7 +6931,9 @@ void HistoryForwarded::getState(TextLinkPtr &lnk, HistoryCursorState &state, int
void HistoryForwarded::getStateFromMessageText(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const QRect &r) const {
QRect realr(r);
- realr.setHeight(r.height() - st::msgServiceNameFont->height);
+ if (drawBubble() && displayForwardedFrom()) {
+ realr.setHeight(r.height() - st::msgServiceNameFont->height);
+ }
HistoryMessage::getStateFromMessageText(lnk, state, x, y, realr);
}
@@ -7256,6 +6941,8 @@ void HistoryForwarded::getForwardedState(TextLinkPtr &lnk, HistoryCursorState &s
state = HistoryDefaultCursorState;
if (x >= fromWidth && x < w && x < fromWidth + fwdFromName.maxWidth()) {
lnk = fwdFrom->lnk;
+ } else if (via() && x >= fromWidth + fwdFromName.maxWidth() + st::msgServiceFont->spacew && x < w && x < fromWidth + fwdFromName.maxWidth() + st::msgServiceFont->spacew + via()->maxWidth) {
+ lnk = via()->lnk;
} else {
lnk = TextLinkPtr();
}
@@ -7266,31 +6953,11 @@ void HistoryForwarded::getSymbol(uint16 &symbol, bool &after, bool &upon, int32
after = false;
upon = false;
- if (!justMedia()) {
- int32 left = fromChannel() ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out() ? st::msgMargin.right() : st::msgMargin.left()), width = _history->width - st::msgMargin.left() - st::msgMargin.right(), mwidth = st::msgMaxWidth;
- if (width > mwidth) {
- if (fromChannel()) {
-// left += (width - mwidth) / 2;
- } else if (out()) {
- left += width - mwidth;
- }
- width = mwidth;
- }
-
- if (displayFromPhoto()) { // from user left photo
-// width -= st::msgPhotoSkip;
- left += st::msgPhotoSkip;
- }
+ if (drawBubble() && displayForwardedFrom()) {
+ int32 left = 0, width = 0;
+ countPositionAndSize(left, width);
if (width < 1) return;
- if (width >= _maxw) {
- if (fromChannel()) {
-// left += (width - _maxw) / 2;
- } else if (out()) {
- left += width - _maxw;
- }
- width = _maxw;
- }
QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
if (displayFromName()) {
style::font nameFont(st::msgNameFont);
@@ -7311,25 +6978,39 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, const MTPDmess
, replyToMsg(0)
, replyToVersion(0)
, _maxReplyWidth(0)
-{
+, _replyToVia(0) {
if (!updateReplyTo() && App::api()) {
App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId);
}
}
-HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc) : HistoryMessage(history, block, msgId, flags, date, from, doc)
+HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption)
+: HistoryMessage(history, block, msgId, flags, viaBotId, date, from, doc, caption)
, replyToMsgId(replyTo)
, replyToMsg(0)
, replyToVersion(0)
, _maxReplyWidth(0)
-{
+, _replyToVia(0) {
if (!updateReplyTo() && App::api()) {
App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId);
}
}
+HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption)
+: HistoryMessage(history, block, msgId, flags, viaBotId, date, from, photo, caption)
+, replyToMsgId(replyTo)
+, replyToMsg(0)
+, replyToVersion(0)
+, _maxReplyWidth(0)
+, _replyToVia(0) {
+ if (!updateReplyTo() && App::api()) {
+ App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId);
+ }
+ replyToNameUpdated();
+}
+
QString HistoryReply::selectedText(uint32 selection) const {
- if (selection != FullItemSel || !replyToMsg) return HistoryMessage::selectedText(selection);
+ if (selection != FullSelection || !replyToMsg) return HistoryMessage::selectedText(selection);
QString result, original = HistoryMessage::selectedText(selection);
result.reserve(lang(lng_in_reply_to).size() + replyToMsg->from()->name.size() + 4 + original.size());
result.append('[').append(lang(lng_in_reply_to)).append(' ').append(replyToMsg->from()->name).append(qsl("]\n")).append(original);
@@ -7337,52 +7018,60 @@ QString HistoryReply::selectedText(uint32 selection) const {
}
void HistoryReply::initDimensions() {
- if (!replyToMsg) {
- _maxReplyWidth = st::msgReplyBarSkip + st::msgDateFont->width(lang(replyToMsgId ? lng_profile_loading : lng_deleted_message)) + st::msgPadding.left() + st::msgPadding.right();
- }
+ replyToNameUpdated();
HistoryMessage::initDimensions();
- if (replyToMsg) {
- replyToNameUpdated();
- } else if (!justMedia()) {
- int maxw = _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.left() + st::msgPadding.right();
- if (maxw > _maxw) _maxw = maxw;
+ if (!_media) {
+ int32 replyw = st::msgPadding.left() + _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.right();
+ if (replyToVia()) {
+ replyw += st::msgServiceFont->spacew + replyToVia()->maxWidth;
+ }
+ if (replyw > _maxw) _maxw = replyw;
}
}
bool HistoryReply::updateReplyTo(bool force) {
if (replyToMsg || !replyToMsgId) return true;
replyToMsg = App::histItemById(channelId(), replyToMsgId);
+
if (replyToMsg) {
App::historyRegReply(this, replyToMsg);
replyToText.setText(st::msgFont, replyToMsg->inReplyText(), _textDlgOptions);
replyToNameUpdated();
-
+
replyToLnk = TextLinkPtr(new MessageLink(replyToMsg->history()->peer->id, replyToMsg->id));
+ if (!replyToMsg->toHistoryForwarded()) {
+ if (UserData *bot = replyToMsg->viaBot()) {
+ _replyToVia = new HistoryMessageVia(peerToUser(bot->id));
+ }
+ }
} else if (force) {
replyToMsgId = 0;
}
if (force) {
initDimensions();
- if (App::main()) App::main()->itemResized(this);
+ Notify::historyItemResized(this);
}
return (replyToMsg || !replyToMsgId);
}
void HistoryReply::replyToNameUpdated() const {
- if (!replyToMsg) return;
- replyToName.setText(st::msgServiceNameFont, App::peerName(replyToMsg->from()), _textNameOptions);
- replyToVersion = replyToMsg->from()->nameVersion;
- bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false;
- int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
+ if (replyToMsg) {
+ QString name = (replyToVia() && replyToMsg->from()->isUser()) ? replyToMsg->from()->asUser()->firstName : App::peerName(replyToMsg->from());
+ replyToName.setText(st::msgServiceNameFont, name, _textNameOptions);
+ replyToVersion = replyToMsg->from()->nameVersion;
+ bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false;
+ int32 previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
+ int32 w = replyToName.maxWidth();
+ if (replyToVia()) {
+ w += st::msgServiceFont->spacew + replyToVia()->maxWidth;
+ }
- _maxReplyWidth = st::msgReplyPadding.left() + st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgReplyPadding.right();
- int32 _textw = st::msgReplyPadding.left() + st::msgReplyBarSkip + previewSkip + qMin(replyToText.maxWidth(), 4 * replyToName.maxWidth()) + st::msgReplyPadding.right();
- if (_textw > _maxReplyWidth) _maxReplyWidth = _textw;
- if (!justMedia()) {
- int maxw = _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.left() + st::msgPadding.right();
- if (maxw > _maxw) _maxw = maxw;
+ _maxReplyWidth = previewSkip + qMax(w, qMin(replyToText.maxWidth(), 4 * w));
+ } else {
+ _maxReplyWidth = st::msgDateFont->width(lang(replyToMsgId ? lng_profile_loading : lng_deleted_message));
}
+ _maxReplyWidth = st::msgReplyPadding.left() + st::msgReplyBarSkip + _maxReplyWidth + st::msgReplyPadding.right();
}
int32 HistoryReply::replyToWidth() const {
@@ -7403,19 +7092,25 @@ HistoryItem *HistoryReply::replyToMessage() const {
void HistoryReply::replyToReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
if (replyToMsg == oldItem) {
+ delete _replyToVia;
+ _replyToVia = 0;
replyToMsg = newItem;
if (!newItem) {
replyToMsgId = 0;
initDimensions();
+ } else if (!replyToMsg->toHistoryForwarded()) {
+ if (UserData *bot = replyToMsg->viaBot()) {
+ _replyToVia = new HistoryMessageVia(peerToUser(bot->id));
+ }
}
}
}
-void HistoryReply::draw(Painter &p, uint32 selection) const {
+void HistoryReply::draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const {
if (replyToMsg && replyToMsg->from()->nameVersion > replyToVersion) {
replyToNameUpdated();
}
- HistoryMessage::draw(p, selection);
+ HistoryMessage::draw(p, r, selection, ms);
}
void HistoryReply::drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selected, bool likeService) const {
@@ -7426,7 +7121,8 @@ void HistoryReply::drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selec
} else {
bar = (selected ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor));
}
- p.fillRect(x + st::msgReplyBarPos.x(), y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), bar->b);
+ QRect rbar(rtlrect(x + st::msgReplyBarPos.x(), y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), w + 2 * x));
+ p.fillRect(rbar, bar);
if (w > st::msgReplyBarSkip) {
if (replyToMsg) {
@@ -7436,7 +7132,7 @@ void HistoryReply::drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selec
if (hasPreview) {
ImagePtr replyPreview = replyToMsg->getMedia()->replyPreview();
if (!replyPreview->isNull()) {
- QRect to(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
+ QRect to(rtlrect(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height(), w + 2 * x));
p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height()));
if (selected) {
App::roundRect(p, to, textstyleCurrent()->selectOverlay, SelectedOverlayCorners);
@@ -7445,78 +7141,76 @@ void HistoryReply::drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selec
}
if (w > st::msgReplyBarSkip + previewSkip) {
if (likeService) {
- p.setPen(st::white->p);
+ p.setPen(st::white);
} else {
- p.setPen((selected ? (outbg ? st::msgOutServiceSelColor : st::msgInServiceSelColor) : (outbg ? st::msgOutServiceColor : st::msgInServiceColor))->p);
+ p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
+ }
+ replyToName.drawLeftElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top(), w - st::msgReplyBarSkip - previewSkip, w + 2 * x);
+ if (replyToVia() && w > st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgServiceFont->spacew) {
+ p.setFont(st::msgServiceFont);
+ p.drawText(x + st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgServiceFont->spacew, y + st::msgReplyPadding.top() + st::msgServiceFont->ascent, replyToVia()->text);
}
- replyToName.drawElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top(), w - st::msgReplyBarSkip - previewSkip);
HistoryMessage *replyToAsMsg = replyToMsg->toHistoryMessage();
if (likeService) {
- } else if ((replyToAsMsg && replyToAsMsg->justMedia()) || replyToMsg->serviceMsg()) {
- style::color date(selected ? (outbg ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (outbg ? st::msgOutDateColor : st::msgInDateColor));
- p.setPen(date->p);
+ } else if ((replyToAsMsg && replyToAsMsg->emptyText()) || replyToMsg->serviceMsg()) {
+ style::color date(outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg));
+ p.setPen(date);
} else {
- p.setPen(st::msgColor->p);
+ p.setPen(st::msgColor);
}
- replyToText.drawElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top() + st::msgServiceNameFont->height, w - st::msgReplyBarSkip - previewSkip);
+ replyToText.drawLeftElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top() + st::msgServiceNameFont->height, w - st::msgReplyBarSkip - previewSkip, w + 2 * x);
}
} else {
- p.setFont(st::msgDateFont->f);
- style::color date(selected ? (outbg ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (outbg ? st::msgOutDateColor : st::msgInDateColor));
- if (likeService) {
- p.setPen(st::white->p);
- } else {
- p.setPen(date->p);
- }
- p.drawText(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(replyToMsgId ? lng_profile_loading : lng_deleted_message), w - st::msgReplyBarSkip));
+ p.setFont(st::msgDateFont);
+ style::color date(outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg));
+ p.setPen(likeService ? st::white : date);
+ p.drawTextLeft(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2, w + 2 * x, st::msgDateFont->elided(lang(replyToMsgId ? lng_profile_loading : lng_deleted_message), w - st::msgReplyBarSkip));
}
}
}
-void HistoryReply::drawMessageText(Painter &p, const QRect &trect, uint32 selection) const {
+void HistoryReply::drawMessageText(Painter &p, QRect trect, uint32 selection) const {
int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
- drawReplyTo(p, trect.x(), trect.y(), trect.width(), (selection == FullItemSel));
+ drawReplyTo(p, trect.x(), trect.y(), trect.width(), (selection == FullSelection));
- QRect realtrect(trect);
- realtrect.setY(trect.y() + h);
- HistoryMessage::drawMessageText(p, realtrect, selection);
+ trect.setY(trect.y() + h);
+ HistoryMessage::drawMessageText(p, trect, selection);
}
int32 HistoryReply::resize(int32 width) {
HistoryMessage::resize(width);
- if (!justMedia()) _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
+ if (drawBubble()) {
+ if (emptyText() && !displayFromName() && !via()) {
+ _height += st::msgPadding.top() + st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom() + st::mediaHeaderSkip;
+ } else {
+ _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
+ }
+ if (replyToVia()) {
+ bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false;
+ int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
+ replyToVia()->resize(width - st::msgPadding.left() - st::msgPadding.right() - st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgServiceFont->spacew);
+ }
+ }
return _height;
}
-bool HistoryReply::hasPoint(int32 x, int32 y) const {
- if (!justMedia()) {
- int32 left = fromChannel() ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out() ? st::msgMargin.right() : st::msgMargin.left()), width = _history->width - st::msgMargin.left() - st::msgMargin.right(), mwidth = st::msgMaxWidth;
- if (width > mwidth) {
- if (fromChannel()) {
-// left += (width - mwidth) / 2;
- } else if (out()) {
- left += width - mwidth;
- }
- width = mwidth;
- }
+void HistoryReply::resizeVia(int32 w) const {
+ if (!replyToVia()) return;
- if (displayFromPhoto()) { // from user left photo
-// width -= st::msgPhotoSkip;
- left += st::msgPhotoSkip;
- }
+ bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false;
+ int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
+ replyToVia()->resize(w - st::msgReplyBarSkip - previewSkip - replyToName.maxWidth() - st::msgServiceFont->spacew);
+}
+
+bool HistoryReply::hasPoint(int32 x, int32 y) const {
+ if (drawBubble()) {
+ int32 left = 0, width = 0;
+ countPositionAndSize(left, width);
if (width < 1) return false;
- if (width >= _maxw) {
- if (fromChannel()) {
-// left += (width - _maxw) / 2;
- } else if (out()) {
- left += width - _maxw;
- }
- width = _maxw;
- }
QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
return r.contains(x, y);
}
@@ -7527,34 +7221,16 @@ void HistoryReply::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x
lnk = TextLinkPtr();
state = HistoryDefaultCursorState;
- if (!justMedia()) {
- int32 left = fromChannel() ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out() ? st::msgMargin.right() : st::msgMargin.left()), width = _history->width - st::msgMargin.left() - st::msgMargin.right(), mwidth = st::msgMaxWidth;
- if (width > mwidth) {
- if (fromChannel()) {
-// left += (width - mwidth) / 2;
- } else if (out()) {
- left += width - mwidth;
- }
- width = mwidth;
- }
-
+ if (drawBubble()) {
+ int32 left = 0, width = 0;
+ countPositionAndSize(left, width);
if (displayFromPhoto()) { // from user left photo
- if (x >= left && x < left + st::msgPhotoSize) {
+ if (x >= left - st::msgPhotoSkip && x < left - st::msgPhotoSkip + st::msgPhotoSize) {
return HistoryMessage::getState(lnk, state, x, y);
}
-// width -= st::msgPhotoSkip;
- left += st::msgPhotoSkip;
}
if (width < 1) return;
- if (width >= _maxw) {
- if (fromChannel()) {
-// left += (width - _maxw) / 2;
- } else if (out()) {
- left += width - _maxw;
- }
- width = _maxw;
- }
QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
if (displayFromName()) {
style::font nameFont(st::msgNameFont);
@@ -7590,31 +7266,11 @@ void HistoryReply::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, i
after = false;
upon = false;
- if (!justMedia()) {
- int32 left = fromChannel() ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out() ? st::msgMargin.right() : st::msgMargin.left()), width = _history->width - st::msgMargin.left() - st::msgMargin.right(), mwidth = st::msgMaxWidth;
- if (width > mwidth) {
- if (fromChannel()) {
-// left += (width - mwidth) / 2;
- } else if (out()) {
- left += width - mwidth;
- }
- width = mwidth;
- }
-
- if (displayFromPhoto()) { // from user left photo
-// width -= st::msgPhotoSkip;
- left += st::msgPhotoSkip;
- }
+ if (drawBubble()) {
+ int32 left = 0, width = 0;
+ countPositionAndSize(left, width);
if (width < 1) return;
- if (width >= _maxw) {
- if (fromChannel()) {
-// left += (width - _maxw) / 2;
- } else if (out()) {
- left += width - _maxw;
- }
- width = _maxw;
- }
QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom());
if (displayFromName()) {
style::font nameFont(st::msgNameFont);
@@ -7637,6 +7293,7 @@ HistoryReply::~HistoryReply() {
} else if (replyToMsgId && App::api()) {
App::api()->itemRemoved(this);
}
+ deleteAndMark(_replyToVia);
}
void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) {
@@ -7807,8 +7464,8 @@ void HistoryServiceMsg::initDimensions() {
}
QString HistoryServiceMsg::selectedText(uint32 selection) const {
- uint16 selectedFrom = (selection == FullItemSel) ? 0 : (selection >> 16) & 0xFFFF;
- uint16 selectedTo = (selection == FullItemSel) ? 0xFFFF : (selection & 0xFFFF);
+ uint16 selectedFrom = (selection == FullSelection) ? 0 : (selection >> 16) & 0xFFFF;
+ uint16 selectedTo = (selection == FullSelection) ? 0xFFFF : (selection & 0xFFFF);
return _text.original(selectedFrom, selectedTo);
}
@@ -7828,14 +7485,15 @@ void HistoryServiceMsg::setServiceText(const QString &text) {
initDimensions();
}
-void HistoryServiceMsg::draw(Painter &p, uint32 selection) const {
- uint64 ms = App::main() ? App::main()->animActiveTime(this) : 0;
- if (ms) {
- if (ms > st::activeFadeInDuration + st::activeFadeOutDuration) {
+void HistoryServiceMsg::draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const {
+ uint64 animms = App::main() ? App::main()->animActiveTimeStart(this) : 0;
+ if (animms > 0 && animms <= ms) {
+ animms = ms - animms;
+ if (animms > st::activeFadeInDuration + st::activeFadeOutDuration) {
App::main()->stopAnimActive();
} else {
textstyleSet(&st::inTextStyle);
- float64 dt = (ms > st::activeFadeInDuration) ? (1 - (ms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (ms / float64(st::activeFadeInDuration));
+ float64 dt = (animms > st::activeFadeInDuration) ? (1 - (animms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (animms / float64(st::activeFadeInDuration));
float64 o = p.opacity();
p.setOpacity(o * dt);
p.fillRect(0, 0, _history->width, _height, textstyleCurrent()->selectOverlay->b);
@@ -7851,8 +7509,9 @@ void HistoryServiceMsg::draw(Painter &p, uint32 selection) const {
if (_media) {
height -= st::msgServiceMargin.top() + _media->height();
p.save();
- p.translate(st::msgServiceMargin.left() + (width - _media->maxWidth()) / 2, st::msgServiceMargin.top() + height + st::msgServiceMargin.top());
- _media->draw(p, this, selection == FullItemSel);
+ int32 left = st::msgServiceMargin.left() + (width - _media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top();
+ p.translate(left, top);
+ _media->draw(p, this, r.translated(-left, -top), selection == FullSelection, ms);
p.restore();
}
@@ -7862,13 +7521,13 @@ void HistoryServiceMsg::draw(Painter &p, uint32 selection) const {
left += (width - _maxw) / 2;
width = _maxw;
}
- App::roundRect(p, left, st::msgServiceMargin.top(), width, height, App::msgServiceBg(), (selection == FullItemSel) ? ServiceSelectedCorners : ServiceCorners);
+ App::roundRect(p, left, st::msgServiceMargin.top(), width, height, App::msgServiceBg(), (selection == FullSelection) ? ServiceSelectedCorners : ServiceCorners);
p.setBrush(Qt::NoBrush);
p.setPen(st::msgServiceColor->p);
p.setFont(st::msgServiceFont->f);
- uint16 selectedFrom = (selection == FullItemSel) ? 0 : (selection >> 16) & 0xFFFF;
- uint16 selectedTo = (selection == FullItemSel) ? 0 : selection & 0xFFFF;
+ uint16 selectedFrom = (selection == FullSelection) ? 0 : (selection >> 16) & 0xFFFF;
+ uint16 selectedTo = (selection == FullSelection) ? 0 : selection & 0xFFFF;
_text.draw(p, trect.x(), trect.y(), trect.width(), Qt::AlignCenter, 0, -1, selectedFrom, selectedTo);
textstyleRestore();
}
@@ -7970,7 +7629,14 @@ HistoryServiceMsg::~HistoryServiceMsg() {
}
HistoryDateMsg::HistoryDateMsg(History *history, HistoryBlock *block, const QDate &date) :
-HistoryServiceMsg(history, block, clientMsgId(), QDateTime(date), langDayOfMonth(date)) {
+HistoryServiceMsg(history, block, clientMsgId(), QDateTime(date), langDayOfMonthFull(date)) {
+}
+
+void HistoryDateMsg::setDate(const QDateTime &date) {
+ if (this->date.date() != date.date()) {
+ setServiceText(langDayOfMonthFull(date.date()));
+ }
+ HistoryServiceMsg::setDate(date);
}
HistoryItem *createDayServiceMsg(History *history, HistoryBlock *block, QDateTime date) {
@@ -8053,7 +7719,7 @@ HistoryServiceMsg(history, block, clientMsgId(), date, qsl("-")),
_wasMinId(wasMinId) {
}
-void HistoryCollapse::draw(Painter &p, uint32 selection) const {
+void HistoryCollapse::draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const {
}
void HistoryCollapse::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const {
@@ -8089,7 +7755,7 @@ void HistoryUnreadBar::setCount(int32 count) {
text = lng_unread_bar(lt_count, count);
}
-void HistoryUnreadBar::draw(Painter &p, uint32 selection) const {
+void HistoryUnreadBar::draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const {
p.fillRect(0, st::lineWidth, _history->width, st::unreadBarHeight - 2 * st::lineWidth, st::unreadBarBG->b);
p.fillRect(0, st::unreadBarHeight - st::lineWidth, _history->width, st::lineWidth, st::unreadBarBorder->b);
p.setFont(st::unreadBarFont->f);
diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h
index 8abc9b95b3..360d2cedc7 100644
--- a/Telegram/SourceFiles/history.h
+++ b/Telegram/SourceFiles/history.h
@@ -24,18 +24,8 @@ void historyInit();
class HistoryItem;
-void startGif(HistoryItem *row, const FileLocation &file);
-void itemRemovedGif(HistoryItem *item);
-void itemReplacedGif(HistoryItem *oldItem, HistoryItem *newItem);
-void stopGif();
-
-static const uint32 FullItemSel = 0xFFFFFFFF;
-
typedef QMap SelectedItemSet;
-extern TextParseOptions _textNameOptions, _textDlgOptions;
-extern TextParseOptions _historyTextOptions, _historyBotOptions, _historyTextNoMonoOptions, _historyBotNoMonoOptions;
-
#include "structs.h"
enum NewMessageType {
@@ -45,16 +35,16 @@ enum NewMessageType {
};
class History;
-class Histories : public Animated {
+class Histories {
public:
typedef QHash Map;
Map map;
- Histories() : unreadFull(0), unreadMuted(0) {
+ Histories() : _a_typings(animation(this, &Histories::step_typings)), unreadFull(0), unreadMuted(0) {
}
void regSendAction(History *history, UserData *user, const MTPSendMessageAction &action);
- bool animStep(float64 ms);
+ void step_typings(uint64 ms, bool timer);
History *find(const PeerId &peerId);
History *findOrInsert(const PeerId &peerId, int32 unreadCount, int32 maxInboxRead);
@@ -62,7 +52,6 @@ public:
void clear();
void remove(const PeerId &peer);
~Histories() {
- clear();
unreadFull = unreadMuted = 0;
}
@@ -71,6 +60,7 @@ public:
typedef QMap TypingHistories; // when typing in this history started
TypingHistories typing;
+ Animation _a_typings;
int32 unreadFull, unreadMuted;
};
@@ -107,6 +97,7 @@ enum HistoryMediaType {
MediaTypeContact,
MediaTypeAudio,
MediaTypeDocument,
+ MediaTypeGif,
MediaTypeSticker,
MediaTypeImageLink,
MediaTypeWebPage,
@@ -125,17 +116,6 @@ enum MediaOverviewType {
OverviewCount
};
-inline MediaOverviewType mediaToOverviewType(HistoryMediaType t) {
- switch (t) {
- case MediaTypePhoto: return OverviewPhotos;
- case MediaTypeVideo: return OverviewVideos;
- case MediaTypeDocument: return OverviewDocuments;
-// case MediaTypeSticker: return OverviewDocuments;
- case MediaTypeAudio: return OverviewAudios;
- }
- return OverviewCount;
-}
-
inline MTPMessagesFilter typeToMediaFilter(MediaOverviewType &type) {
switch (type) {
case OverviewPhotos: return MTP_inputMessagesFilterPhotos();
@@ -172,6 +152,12 @@ class HistoryMedia;
class HistoryMessage;
class HistoryUnreadBar;
+enum AddToOverviewMethod {
+ AddToOverviewNew, // when new message is added to history
+ AddToOverviewFront, // when old messages slice was received
+ AddToOverviewBack, // when new messages slice was received and it is the last one, we index all media
+};
+
class ChannelHistory;
class History {
public:
@@ -201,20 +187,22 @@ public:
clear();
}
- HistoryItem *createItem(HistoryBlock *block, const MTPMessage &msg, bool applyServiceAction, bool returnExisting = false);
+ HistoryItem *createItem(HistoryBlock *block, const MTPMessage &msg, bool applyServiceAction);
HistoryItem *createItemForwarded(HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg);
- HistoryItem *createItemDocument(HistoryBlock *block, MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc);
+ HistoryItem *createItemDocument(HistoryBlock *block, MsgId id, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption);
+ HistoryItem *createItemPhoto(HistoryBlock *block, MsgId id, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption);
HistoryItem *addNewService(MsgId msgId, QDateTime date, const QString &text, int32 flags = 0, HistoryMedia *media = 0, bool newMsg = true);
HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type);
HistoryItem *addToHistory(const MTPMessage &msg);
HistoryItem *addNewForwarded(MsgId id, QDateTime date, int32 from, HistoryMessage *item);
- HistoryItem *addNewDocument(MsgId id, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc);
+ HistoryItem *addNewDocument(MsgId id, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption);
+ HistoryItem *addNewPhoto(MsgId id, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption);
void addOlderSlice(const QVector &slice, const QVector *collapsed);
void addNewerSlice(const QVector &slice, const QVector *collapsed);
- void addToOverview(HistoryItem *item, MediaOverviewType type);
- bool addToOverviewFront(HistoryItem *item, MediaOverviewType type);
+ bool addToOverview(MediaOverviewType type, MsgId msgId, AddToOverviewMethod method);
+ void eraseFromOverview(MediaOverviewType type, MsgId msgId);
void newItemAdded(HistoryItem *item);
void unregTyping(UserData *from);
@@ -248,7 +236,7 @@ public:
MsgId maxMsgId() const;
MsgId msgIdForRead() const;
- int32 geomResize(int32 newWidth, int32 *ytransform = 0, HistoryItem *resizedItem = 0); // return new size
+ int32 geomResize(int32 newWidth, int32 *ytransform = 0, const HistoryItem *resizedItem = 0); // return new size
void removeNotification(HistoryItem *item) {
if (!notifies.isEmpty()) {
@@ -275,24 +263,8 @@ public:
if (!notifies.isEmpty() && notifies.back() == item) notifies.pop_back();
}
- void itemReplaced(HistoryItem *old, HistoryItem *item) {
- if (!notifies.isEmpty()) {
- for (NotifyQueue::iterator i = notifies.begin(), e = notifies.end(); i != e; ++i) {
- if ((*i) == old) {
- *i = item;
- break;
- }
- }
- }
- if (lastMsg == old) {
- lastMsg = item;
- }
- // showFrom can't be detached
- }
-
void paintDialog(Painter &p, int32 w, bool sel) const;
- void eraseFromOverview(MediaOverviewType type, MsgId msgId);
- bool updateTyping(uint64 ms = 0, uint32 dots = 0, bool force = false);
+ bool updateTyping(uint64 ms, bool force = false);
void clearLastKeyboard();
typedef QList Blocks;
@@ -338,7 +310,7 @@ public:
SendActionUsers sendActions;
QString typingStr;
Text typingText;
- uint32 typingFrame;
+ uint32 typingDots;
QMap mySendActions;
typedef QList MediaOverview;
@@ -759,7 +731,7 @@ public:
}
void removeItem(HistoryItem *item);
- int32 geomResize(int32 newWidth, int32 *ytransform, HistoryItem *resizedItem); // return new size
+ int32 geomResize(int32 newWidth, int32 *ytransform, const HistoryItem *resizedItem); // return new size
int32 y, height;
History *history;
};
@@ -767,42 +739,29 @@ public:
class HistoryElem {
public:
- HistoryElem() : _height(0), _maxw(0) {
+ HistoryElem() : _maxw(0), _minh(0), _height(0) {
}
- int32 height() const {
- return _height;
- }
int32 maxWidth() const {
return _maxw;
}
int32 minHeight() const {
return _minh;
}
+ int32 height() const {
+ return _height;
+ }
virtual ~HistoryElem() {
}
protected:
- mutable int32 _height, _maxw, _minh;
+ mutable int32 _maxw, _minh, _height;
+ HistoryElem &operator=(const HistoryElem &);
};
-class ItemAnimations : public Animated {
-public:
-
- bool animStep(float64 ms);
- uint64 animate(const HistoryItem *item, uint64 ms);
- void remove(const HistoryItem *item);
-
-private:
- typedef QMap Animations;
- Animations _animations;
-};
-
-ItemAnimations &itemAnimations();
-
class HistoryReply; // dynamic_cast optimize
class HistoryMessage; // dynamic_cast optimize
class HistoryForwarded; // dynamic_cast optimize
@@ -839,12 +798,13 @@ public:
virtual void initDimensions() = 0;
virtual int32 resize(int32 width) = 0; // return new height
- virtual void draw(Painter &p, uint32 selection) const = 0;
+ virtual void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const = 0;
- History *history() {
- return _history;
+ virtual UserData *viaBot() const {
+ return 0;
}
- const History *history() const {
+
+ History *history() const {
return _history;
}
PeerData *from() const {
@@ -856,7 +816,7 @@ public:
const HistoryBlock *block() const {
return _block;
}
- void destroy();
+ virtual void destroy();
void detach();
void detachFast();
bool detached() const {
@@ -906,7 +866,7 @@ public:
return _history->isChannel() && isImportantChannelMessage(id, _flags);
}
bool indexInOverview() const {
- return (!history()->isChannel() || history()->isMegagroup() || fromChannel());
+ return (id > 0) && (!history()->isChannel() || history()->isMegagroup() || fromChannel());
}
virtual bool needCheck() const {
@@ -927,13 +887,23 @@ public:
virtual uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
return (from << 16) | to;
}
+ virtual void linkOver(const TextLinkPtr &lnk) {
+ }
+ virtual void linkOut(const TextLinkPtr &lnk) {
+ }
virtual HistoryItemType type() const {
return HistoryItemMsg;
}
virtual bool serviceMsg() const {
return false;
}
- virtual void updateMedia(const MTPMessageMedia *media, bool allowEmitResize) {
+ virtual void updateMedia(const MTPMessageMedia *media) {
+ }
+ virtual int32 addToOverview(AddToOverviewMethod method) {
+ return 0;
+ }
+ virtual bool hasBubble() const {
+ return false;
}
virtual QString selectedText(uint32 selection) const {
@@ -946,11 +916,14 @@ public:
return inDialogsText();
}
- virtual void drawInfo(Painter &p, int32 right, int32 bottom, bool selected, InfoDisplayType type) const {
+ virtual void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const {
}
- virtual void setViewsCount(int32 count) {
+ virtual void setViewsCount(int32 count, bool reinit = true) {
}
virtual void setId(MsgId newId);
+ virtual void setDate(const QDateTime &date) { // for date items
+ this->date = date;
+ }
virtual void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const = 0;
virtual QString notificationHeader() const {
return QString();
@@ -1002,9 +975,6 @@ public:
virtual int32 timeLeft() const {
return 0;
}
- virtual QString timeText() const {
- return QString();
- }
virtual int32 timeWidth() const {
return 0;
}
@@ -1028,10 +998,6 @@ public:
return textcmdSkipBlock(skipBlockWidth(), skipBlockHeight());
}
- virtual bool animating() const {
- return false;
- }
-
virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize
return 0;
}
@@ -1051,13 +1017,15 @@ public:
return 0;
}
- bool displayFromName() const {
+ bool hasFromName() const {
return (!out() || fromChannel()) && !history()->peer->isUser();
}
bool displayFromPhoto() const {
return !out() && !history()->peer->isUser() && !fromChannel();
}
+ void clipCallback(ClipReaderNotification notification);
+
virtual ~HistoryItem();
protected:
@@ -1076,6 +1044,8 @@ class MessageLink : public ITextLink {
public:
MessageLink(PeerId peer, MsgId msgid) : _peer(peer), _msgid(msgid) {
}
+ MessageLink(HistoryItem *item) : _peer(item->history()->peer->id), _msgid(item->id) {
+ }
void onClick(Qt::MouseButton button) const;
PeerId peer() const {
return _peer;
@@ -1096,50 +1066,102 @@ public:
CommentsLink(HistoryItem *item) : _item(item) {
}
void onClick(Qt::MouseButton button) const;
-
+
private:
HistoryItem *_item;
};
-HistoryItem *regItem(HistoryItem *item, bool returnExisting = false);
+HistoryItem *regItem(HistoryItem *item);
+
+class RadialAnimation {
+public:
+
+ RadialAnimation(AnimationCreator creator);
+
+ float64 opacity() const {
+ return _opacity;
+ }
+ bool animating() const {
+ return _animation.animating();
+ }
+
+ void start(float64 prg);
+ void update(float64 prg, bool finished, uint64 ms);
+ void stop();
+
+ void step(uint64 ms);
+ void step() {
+ step(getms());
+ }
+
+ void draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color);
+
+private:
+
+ uint64 _firstStart, _lastStart, _lastTime;
+ float64 _opacity;
+ anim::ivalue a_arcEnd, a_arcStart;
+ Animation _animation;
+
+};
class HistoryMedia : public HistoryElem {
public:
- HistoryMedia(int32 width = 0) : w(width) {
+ HistoryMedia() : _width(0) {
}
- HistoryMedia(const HistoryMedia &other) : w(0) {
+ HistoryMedia(const HistoryMedia &other) : _width(0) {
}
virtual HistoryMediaType type() const = 0;
virtual const QString inDialogsText() const = 0;
virtual const QString inHistoryText() const = 0;
- virtual bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0;
+
+ bool hasPoint(int32 x, int32 y, const HistoryItem *parent) const {
+ return (x >= 0 && y >= 0 && x < _width && y < _height);
+ }
+
virtual bool isDisplayed() const {
return true;
}
- virtual int32 countHeight(const HistoryItem *parent, int32 width = -1) const {
- return height();
- }
virtual void initDimensions(const HistoryItem *parent) = 0;
virtual int32 resize(int32 width, const HistoryItem *parent) { // return new height
- w = qMin(width, _maxw);
+ _width = qMin(width, _maxw);
return _height;
}
- virtual void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0;
- virtual void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const = 0;
+ virtual void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const = 0;
+ virtual void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const = 0;
+
+ virtual void linkOver(HistoryItem *parent, const TextLinkPtr &lnk) {
+ }
+ virtual void linkOut(HistoryItem *parent, const TextLinkPtr &lnk) {
+ }
+
virtual bool uploading() const {
return false;
}
virtual HistoryMedia *clone() const = 0;
+ virtual DocumentData *getDocument() {
+ return 0;
+ }
+ virtual ClipReader *getClipReader() {
+ return 0;
+ }
+
+ virtual bool playInline(HistoryItem *item) {
+ return false;
+ }
+ virtual void stopInline(HistoryItem *item) {
+ }
+
virtual void regItem(HistoryItem *item) {
}
virtual void unregItem(HistoryItem *item) {
}
- virtual void updateFrom(const MTPMessageMedia &media) {
+ virtual void updateFrom(const MTPMessageMedia &media, HistoryItem *parent) {
}
virtual bool isImageLink() const {
@@ -1159,237 +1181,545 @@ public:
virtual QString getCaption() const {
return QString();
}
+ virtual bool needsBubble(const HistoryItem *parent) const = 0;
+ virtual bool customInfoLayout() const = 0;
+ virtual QMargins bubbleMargins() const {
+ return QMargins();
+ }
+ virtual bool hideFromName() const {
+ return false;
+ }
+ virtual bool hideForwardedFrom() const {
+ return false;
+ }
int32 currentWidth() const {
- return qMin(w, _maxw);
+ return _width;
}
protected:
- int32 w;
+ int32 _width;
};
-class HistoryPhoto : public HistoryMedia {
+inline MediaOverviewType mediaToOverviewType(HistoryMedia *media) {
+ switch (media->type()) {
+ case MediaTypePhoto: return OverviewPhotos;
+ case MediaTypeVideo: return OverviewVideos;
+ case MediaTypeDocument: return media->getDocument()->song() ? OverviewAudioDocuments : OverviewDocuments;
+ case MediaTypeGif: return media->getDocument()->isGifv() ? OverviewCount : OverviewDocuments;
+// case MediaTypeSticker: return OverviewDocuments;
+ case MediaTypeAudio: return OverviewAudios;
+ }
+ return OverviewCount;
+}
+
+class HistoryFileMedia : public HistoryMedia {
public:
- HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent);
+ HistoryFileMedia();
+
+ void linkOver(HistoryItem *parent, const TextLinkPtr &lnk);
+ void linkOut(HistoryItem *parent, const TextLinkPtr &lnk);
+
+ ~HistoryFileMedia();
+
+protected:
+
+ TextLinkPtr _openl, _savel, _cancell;
+ void setLinks(ITextLink *openl, ITextLink *savel, ITextLink *cancell);
+
+ // >= 0 will contain download / upload string, _statusSize = loaded bytes
+ // < 0 will contain played string, _statusSize = -(seconds + 1) played
+ // 0x7FFFFFF0 will contain status for not yet downloaded file
+ // 0x7FFFFFF1 will contain status for already downloaded file
+ // 0x7FFFFFF2 will contain status for failed to download / upload file
+ mutable int32 _statusSize;
+ mutable QString _statusText;
+
+ // duration = -1 - no duration, duration = -2 - "GIF" duration
+ void setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const;
+
+ void step_thumbOver(const HistoryItem *parent, float64 ms, bool timer);
+ void step_radial(const HistoryItem *parent, uint64 ms, bool timer);
+
+ void ensureAnimation(const HistoryItem *parent) const;
+ void checkAnimationFinished();
+
+ bool isRadialAnimation(uint64 ms) const {
+ if (!_animation || !_animation->radial.animating()) return false;
+
+ _animation->radial.step(ms);
+ return _animation && _animation->radial.animating();
+ }
+ bool isThumbAnimation(uint64 ms) const {
+ if (!_animation || !_animation->_a_thumbOver.animating()) return false;
+
+ _animation->_a_thumbOver.step(ms);
+ return _animation && _animation->_a_thumbOver.animating();
+ }
+
+ virtual float64 dataProgress() const = 0;
+ virtual bool dataFinished() const = 0;
+ virtual bool dataLoaded() const = 0;
+
+ struct AnimationData {
+ AnimationData(AnimationCreator thumbOverCallbacks, AnimationCreator radialCallbacks) : a_thumbOver(0, 0)
+ , _a_thumbOver(thumbOverCallbacks)
+ , radial(radialCallbacks) {
+ }
+ anim::fvalue a_thumbOver;
+ Animation _a_thumbOver;
+
+ RadialAnimation radial;
+ };
+ mutable AnimationData *_animation;
+
+private:
+
+ HistoryFileMedia(const HistoryFileMedia &other);
+
+};
+
+class HistoryPhoto : public HistoryFileMedia {
+public:
+
+ HistoryPhoto(PhotoData *photo, const QString &caption, const HistoryItem *parent);
HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0);
-
+ HistoryPhoto(const HistoryPhoto &other);
void init();
- void initDimensions(const HistoryItem *parent);
-
- void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
- int32 resize(int32 width, const HistoryItem *parent);
HistoryMediaType type() const {
return MediaTypePhoto;
}
+ HistoryMedia *clone() const {
+ return new HistoryPhoto(*this);
+ }
+
+ void initDimensions(const HistoryItem *parent);
+ int32 resize(int32 width, const HistoryItem *parent);
+
+ void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const;
+ void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const;
+
const QString inDialogsText() const;
const QString inHistoryText() const;
- bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
- void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
- HistoryMedia *clone() const;
PhotoData *photo() const {
- return data;
+ return _data;
}
- void updateFrom(const MTPMessageMedia &media);
+ void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
- TextLinkPtr lnk() const {
- return openl;
- }
-
- virtual bool animating() const {
- if (data->full->loaded()) return false;
- return data->full->loading() ? true : !data->medium->loaded();
- }
+ void regItem(HistoryItem *item);
+ void unregItem(HistoryItem *item);
bool hasReplyPreview() const {
- return !data->thumb->isNull();
+ return !_data->thumb->isNull();
}
ImagePtr replyPreview();
QString getCaption() const {
return _caption.original();
}
+ bool needsBubble(const HistoryItem *parent) const {
+ return !_caption.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply() || parent->viaBot();
+ }
+ bool customInfoLayout() const {
+ return _caption.isEmpty();
+ }
+ bool hideFromName() const {
+ return true;
+ }
+
+protected:
+
+ float64 dataProgress() const {
+ return _data->progress();
+ }
+ bool dataFinished() const {
+ return !_data->loading() && !_data->uploading();
+ }
+ bool dataLoaded() const {
+ return _data->loaded();
+ }
private:
- int16 pixw, pixh;
- PhotoData *data;
+ PhotoData *_data;
+ int16 _pixw, _pixh;
Text _caption;
- TextLinkPtr openl;
};
-QString formatSizeText(qint64 size);
-QString formatDownloadText(qint64 ready, qint64 total);
-QString formatDurationText(qint64 duration);
-
-class HistoryVideo : public HistoryMedia {
+class HistoryVideo : public HistoryFileMedia {
public:
HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent);
- void initDimensions(const HistoryItem *parent);
-
- void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
- int32 resize(int32 width, const HistoryItem *parent);
+ HistoryVideo(const HistoryVideo &other);
HistoryMediaType type() const {
return MediaTypeVideo;
}
+ HistoryMedia *clone() const {
+ return new HistoryVideo(*this);
+ }
+
+ void initDimensions(const HistoryItem *parent);
+ int32 resize(int32 width, const HistoryItem *parent);
+
+ void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const;
+ void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const;
+
const QString inDialogsText() 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;
- void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
- bool uploading() const {
- return (data->status == FileUploading);
+
+ VideoData *video() const {
+ return _data;
+ }
+
+ bool uploading() const {
+ return _data->uploading();
}
- HistoryMedia *clone() const;
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
bool hasReplyPreview() const {
- return !data->thumb->isNull();
+ return !_data->thumb->isNull();
}
ImagePtr replyPreview();
+ bool needsBubble(const HistoryItem *parent) const {
+ return !_caption.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply() || parent->viaBot();
+ }
+ bool customInfoLayout() const {
+ return _caption.isEmpty();
+ }
+ bool hideFromName() const {
+ return true;
+ }
+
+protected:
+
+ float64 dataProgress() const {
+ return _data->progress();
+ }
+ bool dataFinished() const {
+ return !_data->loading() && !_data->uploading();
+ }
+ bool dataLoaded() const {
+ return _data->loaded();
+ }
+
private:
- VideoData *data;
- TextLinkPtr _openl, _savel, _cancell;
-
+ VideoData *_data;
+ int16 _thumbw;
Text _caption;
- QString _size;
- int32 _thumbw;
+ void setStatusSize(int32 newSize) const;
+ void updateStatusText(const HistoryItem *parent) const;
- mutable QString _dldTextCache, _uplTextCache;
- mutable int32 _dldDone, _uplDone;
};
-class HistoryAudio : public HistoryMedia {
+class HistoryAudio : public HistoryFileMedia {
public:
HistoryAudio(const MTPDaudio &audio);
- void initDimensions(const HistoryItem *parent);
-
- void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
+ HistoryAudio(const HistoryAudio &other);
HistoryMediaType type() const {
return MediaTypeAudio;
}
+ HistoryMedia *clone() const {
+ return new HistoryAudio(*this);
+ }
+
+ void initDimensions(const HistoryItem *parent);
+
+ void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const;
+ void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const;
+
const QString inDialogsText() const;
const QString inHistoryText() const;
- bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
- void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
+
bool uploading() const {
- return (data->status == FileUploading);
+ return _data->uploading();
}
- HistoryMedia *clone() const;
AudioData *audio() {
- return data;
+ return _data;
}
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
- void updateFrom(const MTPMessageMedia &media);
+ void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
+
+ bool needsBubble(const HistoryItem *parent) const {
+ return true;
+ }
+ bool customInfoLayout() const {
+ return false;
+ }
+ QMargins bubbleMargins() const {
+ return st::msgPadding;
+ }
+
+protected:
+
+ float64 dataProgress() const {
+ return _data->progress();
+ }
+ bool dataFinished() const {
+ return !_data->loading() && !_data->uploading();
+ }
+ bool dataLoaded() const {
+ return _data->loaded();
+ }
private:
- AudioData *data;
- TextLinkPtr _openl, _savel, _cancell;
+ AudioData *_data;
- QString _size;
+ void setStatusSize(int32 newSize, qint64 realDuration = 0) const;
+ bool updateStatusText(const HistoryItem *parent) const; // returns showPause
- mutable QString _dldTextCache, _uplTextCache;
- mutable int32 _dldDone, _uplDone;
};
-class HistoryDocument : public HistoryMedia {
+class HistoryDocument : public HistoryFileMedia {
public:
- HistoryDocument(DocumentData *document);
- void initDimensions(const HistoryItem *parent);
-
- void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
- int32 resize(int32 width, const HistoryItem *parent);
+ HistoryDocument(DocumentData *document, const QString &caption, const HistoryItem *parent);
+ HistoryDocument(const HistoryDocument &other);
HistoryMediaType type() const {
return MediaTypeDocument;
}
+ HistoryMedia *clone() const {
+ return new HistoryDocument(*this);
+ }
+
+ void initDimensions(const HistoryItem *parent);
+ int32 resize(int32 width, const HistoryItem *parent);
+
+ void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const;
+ void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const;
+
const QString inDialogsText() 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);
- }
- void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
- HistoryMedia *clone() const;
- DocumentData *document() {
- return data;
+ bool uploading() const {
+ return _data->uploading();
+ }
+
+ bool withThumb() const {
+ return !_data->song() && !_data->thumb->isNull() && _data->thumb->width() && _data->thumb->height();
+ }
+
+ DocumentData *getDocument() {
+ return _data;
}
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
- void updateFrom(const MTPMessageMedia &media);
+ void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
bool hasReplyPreview() const {
- return !data->thumb->isNull();
+ return !_data->thumb->isNull();
}
ImagePtr replyPreview();
- void drawInPlaylist(Painter &p, const HistoryItem *parent, bool selected, bool over, int32 width) const;
- TextLinkPtr linkInPlaylist();
+ QString getCaption() const {
+ return _caption.original();
+ }
+ bool needsBubble(const HistoryItem *parent) const {
+ return true;
+ }
+ bool customInfoLayout() const {
+ return false;
+ }
+ QMargins bubbleMargins() const {
+ return withThumb() ? QMargins(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbPadding.left(), st::msgFileThumbPadding.bottom()) : st::msgPadding;
+ }
+ bool hideForwardedFrom() const {
+ return _data->song();
+ }
+
+protected:
+
+ float64 dataProgress() const {
+ return _data->progress();
+ }
+ bool dataFinished() const {
+ return !_data->loading() && !_data->uploading();
+ }
+ bool dataLoaded() const {
+ return _data->loaded();
+ }
private:
- DocumentData *data;
- TextLinkPtr _openl, _savel, _cancell;
+ DocumentData *_data;
+ TextLinkPtr _linksavel, _linkcancell;
+ QString _name;
int32 _namew;
- QString _name, _size;
- int32 _thumbw, _thumbx, _thumby;
+ int32 _thumbw;
+
+ mutable int32 _linkw;
+ mutable QString _link;
+
+ Text _caption;
+
+ void setStatusSize(int32 newSize, qint64 realDuration = 0) const;
+ bool updateStatusText(const HistoryItem *parent) const; // returns showPause
+
+};
+
+class HistoryGif : public HistoryFileMedia {
+public:
+
+ HistoryGif(DocumentData *document, const QString &caption, const HistoryItem *parent);
+ HistoryGif(const HistoryGif &other);
+ HistoryMediaType type() const {
+ return MediaTypeGif;
+ }
+ HistoryMedia *clone() const {
+ return new HistoryGif(*this);
+ }
+
+ void initDimensions(const HistoryItem *parent);
+ int32 resize(int32 width, const HistoryItem *parent);
+
+ void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const;
+ void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const;
+
+ const QString inDialogsText() const;
+ const QString inHistoryText() const;
+
+ bool uploading() const {
+ return _data->uploading();
+ }
+
+ DocumentData *getDocument() {
+ return _data;
+ }
+ ClipReader *getClipReader() {
+ return gif();
+ }
+
+ bool playInline(HistoryItem *item);
+ void stopInline(HistoryItem *item);
+
+ void regItem(HistoryItem *item);
+ void unregItem(HistoryItem *item);
+
+ void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
+
+ bool hasReplyPreview() const {
+ return !_data->thumb->isNull();
+ }
+ ImagePtr replyPreview();
+
+ QString getCaption() const {
+ return _caption.original();
+ }
+ bool needsBubble(const HistoryItem *parent) const {
+ return !_caption.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply() || parent->viaBot();
+ }
+ bool customInfoLayout() const {
+ return _caption.isEmpty();
+ }
+ bool hideFromName() const {
+ return true;
+ }
+
+ ~HistoryGif();
+
+protected:
+
+ float64 dataProgress() const;
+ bool dataFinished() const;
+ bool dataLoaded() const;
+
+private:
+
+ const HistoryItem *_parent;
+ DocumentData *_data;
+ int32 _thumbw, _thumbh;
+ Text _caption;
+
+ ClipReader *_gif;
+ ClipReader *gif() {
+ return (_gif == BadClipReader) ? 0 : _gif;
+ }
+ const ClipReader *gif() const {
+ return (_gif == BadClipReader) ? 0 : _gif;
+ }
+
+ void setStatusSize(int32 newSize) const;
+ void updateStatusText(const HistoryItem *parent) const;
- mutable QString _dldTextCache, _uplTextCache;
- mutable int32 _dldDone, _uplDone;
};
class HistorySticker : public HistoryMedia {
public:
HistorySticker(DocumentData *document);
- void initDimensions(const HistoryItem *parent);
-
- void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
- int32 resize(int32 width, const HistoryItem *parent);
HistoryMediaType type() const {
return MediaTypeSticker;
}
+ HistoryMedia *clone() const {
+ return new HistorySticker(*this);
+ }
+
+ void initDimensions(const HistoryItem *parent);
+ int32 resize(int32 width, const HistoryItem *parent);
+
+ void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const;
+ void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const;
+
const QString inDialogsText() 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;
- void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
- HistoryMedia *clone() const;
- DocumentData *document() {
- return data;
+ DocumentData *getDocument() {
+ return _data;
}
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
- void updateFrom(const MTPMessageMedia &media);
+ void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
+
+ bool needsBubble(const HistoryItem *parent) const {
+ return false;
+ }
+ bool customInfoLayout() const {
+ return true;
+ }
private:
- int16 pixw, pixh;
- DocumentData *data;
+ int16 _pixw, _pixh;
+ DocumentData *_data;
QString _emoji;
- int32 lastw;
+
+};
+
+class SendMessageLink : public PeerLink {
+ TEXT_LINK_CLASS(SendMessageLink)
+
+public:
+ SendMessageLink(PeerData *peer) : PeerLink(peer) {
+ }
+ void onClick(Qt::MouseButton button) const;
+
+};
+
+class AddContactLink : public MessageLink {
+ TEXT_LINK_CLASS(AddContactLink)
+
+public:
+ AddContactLink(PeerId peer, MsgId msgid) : MessageLink(peer, msgid) {
+ }
+ void onClick(Qt::MouseButton button) const;
};
@@ -1397,78 +1727,135 @@ class HistoryContact : public HistoryMedia {
public:
HistoryContact(int32 userId, const QString &first, const QString &last, const QString &phone);
- HistoryContact(int32 userId, const QString &fullname, const QString &phone);
- void initDimensions(const HistoryItem *parent);
-
- void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const;
HistoryMediaType type() const {
return MediaTypeContact;
}
+ HistoryMedia *clone() const {
+ return new HistoryContact(_userId, _fname, _lname, _phone);
+ }
+
+ void initDimensions(const HistoryItem *parent);
+
+ void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const;
+ void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const;
+
const QString inDialogsText() const;
const QString inHistoryText() const;
- bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const;
- void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const;
- HistoryMedia *clone() const;
- void updateFrom(const MTPMessageMedia &media);
+ void regItem(HistoryItem *item);
+ void unregItem(HistoryItem *item);
+
+ void updateFrom(const MTPMessageMedia &media, HistoryItem *parent);
+
+ bool needsBubble(const HistoryItem *parent) const {
+ return true;
+ }
+ bool customInfoLayout() const {
+ return false;
+ }
+
+ const QString &fname() const {
+ return _fname;
+ }
+ const QString &lname() const {
+ return _lname;
+ }
+ const QString &phone() const {
+ return _phone;
+ }
private:
- int32 userId;
- int32 phonew;
- Text name;
- QString phone;
- UserData *contact;
+
+ int32 _userId;
+ UserData *_contact;
+
+ int32 _phonew;
+ QString _fname, _lname, _phone;
+ Text _name;
+
+ TextLinkPtr _linkl;
+ int32 _linkw;
+ QString _link;
};
class HistoryWebPage : public HistoryMedia {
public:
HistoryWebPage(WebPageData *data);
- void initDimensions(const HistoryItem *parent);
-
- void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
- bool isDisplayed() const {
- return !data->pendingTill;
- }
- int32 resize(int32 width, const HistoryItem *parent);
+ HistoryWebPage(const HistoryWebPage &other);
HistoryMediaType type() const {
return MediaTypeWebPage;
}
+ HistoryMedia *clone() const {
+ return new HistoryWebPage(*this);
+ }
+
+ void initDimensions(const HistoryItem *parent);
+ int32 resize(int32 width, const HistoryItem *parent);
+
+ void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const;
+ void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const;
+
const QString inDialogsText() const;
const QString inHistoryText() const;
- bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
- void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
- HistoryMedia *clone() const;
+
+ void linkOver(HistoryItem *parent, const TextLinkPtr &lnk);
+ void linkOut(HistoryItem *parent, const TextLinkPtr &lnk);
+
+ bool isDisplayed() const {
+ return !_data->pendingTill;
+ }
+ DocumentData *getDocument() {
+ return _attach ? _attach->getDocument() : 0;
+ }
+ ClipReader *getClipReader() {
+ return _attach ? _attach->getClipReader() : 0;
+ }
+ bool playInline(HistoryItem *item) {
+ return _attach ? _attach->playInline(item) : false;
+ }
+ void stopInline(HistoryItem *item) {
+ if (_attach) _attach->stopInline(item);
+ }
void regItem(HistoryItem *item);
void unregItem(HistoryItem *item);
bool hasReplyPreview() const {
- return (data->photo && !data->photo->thumb->isNull()) || (data->doc && !data->doc->thumb->isNull());
+ return (_data->photo && !_data->photo->thumb->isNull()) || (_data->doc && !_data->doc->thumb->isNull());
}
ImagePtr replyPreview();
- virtual bool animating() const {
- if (_asArticle || !data->photo || data->photo->full->loaded()) return false;
- return data->photo->full->loading();
+ WebPageData *webpage() {
+ return _data;
}
- WebPageData *webpage() {
- return data;
+ bool needsBubble(const HistoryItem *parent) const {
+ return true;
}
+ bool customInfoLayout() const {
+ return false;
+ }
+
+ HistoryMedia *attach() const {
+ return _attach;
+ }
+
+ ~HistoryWebPage();
private:
- WebPageData *data;
- TextLinkPtr _openl, _attachl;
+ WebPageData *_data;
+ TextLinkPtr _openl;
+ HistoryMedia *_attach;
+
bool _asArticle;
+ int32 _titleLines, _descriptionLines;
Text _title, _description;
int32 _siteNameWidth;
- QString _duration, _docName, _docSize;
- int32 _durationWidth, _docNameWidth, _docThumbWidth;
- mutable QString _docDownloadTextCache;
- mutable int32 _docDownloadDone;
+ QString _duration;
+ int32 _durationWidth;
int16 _pixw, _pixh;
};
@@ -1479,9 +1866,6 @@ void deinitImageLinkManager();
enum ImageLinkType {
InvalidImageLink = 0,
- YouTubeLink,
- VimeoLink,
- InstagramLink,
GoogleMapsLink
};
struct ImageLinkData {
@@ -1489,7 +1873,6 @@ struct ImageLinkData {
}
QString id;
- QString title, duration;
ImagePtr thumb;
ImageLinkType type;
bool loading;
@@ -1512,7 +1895,7 @@ public:
deinit();
}
-public slots:
+ public slots:
void onFinished(QNetworkReply *reply);
void onFailed(QNetworkReply *reply);
@@ -1529,29 +1912,67 @@ class HistoryImageLink : public HistoryMedia {
public:
HistoryImageLink(const QString &url, const QString &title = QString(), const QString &description = QString());
- int32 fullWidth() const;
- int32 fullHeight() const;
- void initDimensions(const HistoryItem *parent);
-
- void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const;
- int32 resize(int32 width, const HistoryItem *parent);
HistoryMediaType type() const {
return MediaTypeImageLink;
}
+ HistoryMedia *clone() const {
+ return new HistoryImageLink(*this);
+ }
+
+ void initDimensions(const HistoryItem *parent);
+ int32 resize(int32 width, const HistoryItem *parent);
+
+ void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const;
+ void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const;
+
const QString inDialogsText() const;
const QString inHistoryText() const;
- bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
- void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
- HistoryMedia *clone() const;
bool isImageLink() const {
return true;
}
+ bool needsBubble(const HistoryItem *parent) const {
+ return !_title.isEmpty() || !_description.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply() || parent->viaBot();
+ }
+ bool customInfoLayout() const {
+ return true;
+ }
+
private:
- ImageLinkData *data;
+ ImageLinkData *_data;
Text _title, _description;
- TextLinkPtr link;
+ TextLinkPtr _link;
+
+ int32 fullWidth() const;
+ int32 fullHeight() const;
+
+};
+
+class ViaInlineBotLink : public ITextLink {
+ TEXT_LINK_CLASS(ViaInlineBotLink)
+
+public:
+ ViaInlineBotLink(UserData *bot) : _bot(bot) {
+ }
+ void onClick(Qt::MouseButton button) const;
+
+private:
+ UserData *_bot;
+
+};
+
+class HistoryMessageVia {
+public:
+ HistoryMessageVia(int32 userId);
+
+ bool isNull() const;
+ void resize(int32 availw);
+
+ UserData *bot;
+ QString text;
+ int32 width, maxWidth;
+ TextLinkPtr lnk;
};
@@ -1559,27 +1980,50 @@ class HistoryMessage : public HistoryItem {
public:
HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg);
- HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities, HistoryMedia *media); // local forwarded
- HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, DocumentData *doc); // local sticker and reply sticker
+ HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities, HistoryMedia *media); // local forwarded
+ HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption); // local document
+ HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption); // local photo
void initTime();
void initMedia(const MTPMessageMedia *media, QString ¤tText);
- void initMediaFromText(QString ¤tText);
- void initMediaFromDocument(DocumentData *doc);
+ void initMediaFromDocument(DocumentData *doc, const QString &caption);
void initDimensions();
- void fromNameUpdated() const;
+ void fromNameUpdated(int32 width) const;
- bool justMedia() const {
- return _media && _text.isEmpty();
+ virtual HistoryMessageVia *via() const {
+ return (_via && !_via->isNull()) ? _via : 0;
+ }
+ virtual UserData *viaBot() const {
+ return via() ? via()->bot : 0;
}
- bool uploading() const;
+ int32 plainMaxWidth() const;
+ void countPositionAndSize(int32 &left, int32 &width) const;
- void drawInfo(Painter &p, int32 right, int32 bottom, bool selected, InfoDisplayType type) const;
- void setViewsCount(int32 count);
+ bool emptyText() const {
+ return _text.isEmpty();
+ }
+ bool drawBubble() const {
+ return _media ? (!emptyText() || _media->needsBubble(this)) : true;
+ }
+ bool hasBubble() const {
+ return drawBubble();
+ }
+ bool displayFromName() const {
+ return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || viaBot() || !_media->hideFromName());
+ }
+ bool uploading() const {
+ return _media && _media->uploading();
+ }
+
+ void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const;
+ void setViewsCount(int32 count, bool reinit = true);
void setId(MsgId newId);
- void draw(Painter &p, uint32 selection) const;
- virtual void drawMessageText(Painter &p, const QRect &trect, uint32 selection) const;
+ void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const;
+
+ virtual void drawMessageText(Painter &p, QRect trect, uint32 selection) const;
+
+ void destroy();
int32 resize(int32 width);
bool hasPoint(int32 x, int32 y) const;
@@ -1592,23 +2036,31 @@ public:
uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
return _text.adjustSelection(from, to, type);
}
+ void linkOver(const TextLinkPtr &lnk) {
+ if (_media) _media->linkOver(this, lnk);
+ }
+ void linkOut(const TextLinkPtr &lnk) {
+ if (_media) _media->linkOut(this, lnk);
+ }
void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const;
QString notificationHeader() const;
QString notificationText() const;
-
- void updateMedia(const MTPMessageMedia *media, bool allowEmitResize) {
+
+ void updateMedia(const MTPMessageMedia *media) {
if (media && _media && _media->type() != MediaTypeWebPage) {
- _media->updateFrom(*media);
+ _media->updateFrom(*media, this);
} else {
- setMedia(media, allowEmitResize);
+ setMedia(media);
}
}
+ int32 addToOverview(AddToOverviewMethod method);
+ void eraseFromOverview();
QString selectedText(uint32 selection) const;
QString inDialogsText() const;
HistoryMedia *getMedia(bool inOverview = false) const;
- void setMedia(const MTPMessageMedia *media, bool allowEmitResize);
+ void setMedia(const MTPMessageMedia *media);
void setText(const QString &text, const EntitiesInText &entities);
QString originalText() const;
EntitiesInText originalEntities() const;
@@ -1635,9 +2087,6 @@ public:
}
return result;
}
- QString timeText() const {
- return _timeText;
- }
int32 timeWidth() const {
return _timeWidth;
}
@@ -1647,9 +2096,6 @@ public:
int32 viewsWidth() const {
return _viewsWidth;
}
- virtual bool animating() const {
- return _media ? _media->animating() : false;
- }
virtual QDateTime dateForwarded() const { // dynamic_cast optimize
return date;
@@ -1672,11 +2118,12 @@ protected:
Text _text;
int32 _textWidth, _textHeight;
+ HistoryMessageVia *_via;
HistoryMedia *_media;
QString _timeText;
int32 _timeWidth;
-
+
QString _viewsText;
int32 _views, _viewsWidth;
@@ -1691,9 +2138,9 @@ public:
void initDimensions();
void fwdNameUpdated() const;
- void draw(Painter &p, uint32 selection) const;
+ void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const;
void drawForwardedFrom(Painter &p, int32 x, int32 y, int32 w, bool selected) const;
- void drawMessageText(Painter &p, const QRect &trect, uint32 selection) const;
+ void drawMessageText(Painter &p, QRect trect, uint32 selection) const;
int32 resize(int32 width);
bool hasPoint(int32 x, int32 y) const;
void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const;
@@ -1708,6 +2155,9 @@ public:
return fwdFrom;
}
QString selectedText(uint32 selection) const;
+ bool displayForwardedFrom() const {
+ return via() || !_media || !_media->isDisplayed() || (fwdFrom->isChannel() || !_media->hideForwardedFrom());
+ }
HistoryForwarded *toHistoryForwarded() {
return this;
@@ -1730,7 +2180,8 @@ class HistoryReply : public HistoryMessage {
public:
HistoryReply(History *history, HistoryBlock *block, const MTPDmessage &msg);
- HistoryReply(History *history, HistoryBlock *block, MsgId msgId, int32 flags, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc);
+ HistoryReply(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption);
+ HistoryReply(History *history, HistoryBlock *block, MsgId msgId, int32 flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption);
void initDimensions();
@@ -1744,10 +2195,11 @@ public:
HistoryItem *replyToMessage() const;
void replyToReplaced(HistoryItem *oldItem, HistoryItem *newItem);
- void draw(Painter &p, uint32 selection) const;
+ void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const;
void drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selected, bool likeService = false) const;
- void drawMessageText(Painter &p, const QRect &trect, uint32 selection) const;
+ void drawMessageText(Painter &p, QRect trect, uint32 selection) const;
int32 resize(int32 width);
+ void resizeVia(int32 w) const;
bool hasPoint(int32 x, int32 y) const;
void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const;
void getStateFromMessageText(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const QRect &r) const;
@@ -1775,10 +2227,21 @@ protected:
mutable Text replyToName, replyToText;
mutable int32 replyToVersion;
mutable int32 _maxReplyWidth;
+ HistoryMessageVia *_replyToVia;
+ HistoryMessageVia *replyToVia() const {
+ return (_replyToVia && !_replyToVia->isNull()) ? _replyToVia : 0;
+ }
int32 toWidth;
};
+inline int32 newMessageFlags(PeerData *p) {
+ return p->isSelf() ? 0 : (((p->isChat() || (p->isUser() && !p->asUser()->botInfo)) ? MTPDmessage::flag_unread : 0) | MTPDmessage::flag_out);
+}
+inline int32 newForwardedFlags(PeerData *p, int32 from, HistoryMessage *msg) {
+ return newMessageFlags(p) | (from ? MTPDmessage::flag_from_id : 0) | (msg->via() ? MTPDmessage::flag_via_bot_id : 0) | (!p->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage::flag_media_unread : 0);
+}
+
class HistoryServiceMsg : public HistoryItem {
public:
@@ -1787,7 +2250,7 @@ public:
void initDimensions();
- void draw(Painter &p, uint32 selection) const;
+ void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const;
int32 resize(int32 width);
bool hasPoint(int32 x, int32 y) const;
void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const;
@@ -1796,6 +2259,13 @@ public:
return _text.adjustSelection(from, to, type);
}
+ void linkOver(const TextLinkPtr &lnk) {
+ if (_media) _media->linkOver(this, lnk);
+ }
+ void linkOut(const TextLinkPtr &lnk) {
+ if (_media) _media->linkOut(this, lnk);
+ }
+
void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const;
QString notificationText() const;
@@ -1811,10 +2281,6 @@ public:
HistoryMedia *getMedia(bool inOverview = false) const;
- virtual bool animating() const {
- return _media ? _media->animating() : false;
- }
-
void setServiceText(const QString &text);
~HistoryServiceMsg();
@@ -1842,6 +2308,7 @@ public:
after = false;
upon = false;
}
+ void setDate(const QDateTime &date);
QString selectedText(uint32 selection) const {
return QString();
}
@@ -1898,7 +2365,7 @@ class HistoryCollapse : public HistoryServiceMsg {
public:
HistoryCollapse(History *history, HistoryBlock *block, MsgId wasMinId, const QDateTime &date);
- void draw(Painter &p, uint32 selection) const;
+ void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const;
void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const;
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const {
symbol = 0xFFFF;
@@ -1940,7 +2407,7 @@ public:
void setCount(int32 count);
- void draw(Painter &p, uint32 selection) const;
+ void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const;
int32 resize(int32 width);
void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const;
@@ -1958,6 +2425,3 @@ protected:
QString text;
bool freezed;
};
-
-const TextParseOptions &itemTextOptions(History *h, PeerData *f);
-const TextParseOptions &itemTextNoMonoOptions(History *h, PeerData *f);
diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp
index afe6316dfd..e58661c29b 100644
--- a/Telegram/SourceFiles/historywidget.cpp
+++ b/Telegram/SourceFiles/historywidget.cpp
@@ -38,7 +38,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
-HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, History *history) : QWidget(0)
+HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, History *history) : TWidget(0)
, _peer(history->peer)
, _migrated(history->peer->migrateFrom() ? App::history(history->peer->migrateFrom()->id) : 0)
, _history(history)
@@ -116,11 +116,11 @@ void HistoryInner::messagesReceivedDown(PeerData *peer, const QVectordetached() || !_history) return;
- int32 msgy = itemTop(msg);
+void HistoryInner::repaintItem(const HistoryItem *item) {
+ if (!item || item->detached() || !_history) return;
+ int32 msgy = itemTop(item);
if (msgy >= 0) {
- update(0, msgy, width(), msg->height());
+ update(0, msgy, width(), item->height());
}
}
@@ -129,13 +129,13 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
if (!App::main()) return;
+ Painter p(this);
QRect r(e->rect());
bool trivial = (rect() == r);
-
- Painter p(this);
if (!trivial) {
p.setClipRect(r);
}
+ uint64 ms = getms();
if (!_firstLoading && _botInfo && !_botInfo->text.isEmpty() && _botDescHeight > 0) {
if (r.y() < _botDescRect.y() + _botDescRect.height() && r.y() + r.height() > _botDescRect.y()) {
@@ -182,14 +182,14 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
if (r.y() < y + item->height()) while (y < drawToY) {
uint32 sel = 0;
if (y >= selfromy && y < seltoy) {
- sel = (_dragSelecting && !item->serviceMsg() && item->id > 0) ? FullItemSel : 0;
+ sel = (_dragSelecting && !item->serviceMsg() && item->id > 0) ? FullSelection : 0;
} else if (hasSel) {
SelectedItems::const_iterator i = _selected.constFind(item);
if (i != selEnd) {
sel = i.value();
}
}
- item->draw(p, sel);
+ item->draw(p, r.translated(0, -y), sel, ms);
if (item->hasViews()) {
App::main()->scheduleViewIncrement(item);
@@ -226,14 +226,14 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
if (r.y() < y + h && hdrawtop < y + h) {
uint32 sel = 0;
if (y >= selfromy && y < seltoy) {
- sel = (_dragSelecting && !item->serviceMsg() && item->id > 0) ? FullItemSel : 0;
+ sel = (_dragSelecting && !item->serviceMsg() && item->id > 0) ? FullSelection : 0;
} else if (hasSel) {
SelectedItems::const_iterator i = _selected.constFind(item);
if (i != selEnd) {
sel = i.value();
}
}
- item->draw(p, sel);
+ item->draw(p, r.translated(0, -y), sel, ms);
if (item->hasViews()) {
App::main()->scheduleViewIncrement(item);
@@ -357,7 +357,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) {
_touchPrevPos = _touchPos;
_touchPos = e->touchPoints().cbegin()->screenPos().toPoint();
}
-
+
switch (e->type()) {
case QEvent::TouchBegin:
if (_menu) {
@@ -475,16 +475,16 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
if (button != Qt::LeftButton) return;
if (App::pressedItem() != App::hoveredItem()) {
- updateMsg(App::pressedItem());
+ repaintItem(App::pressedItem());
App::pressedItem(App::hoveredItem());
- updateMsg(App::pressedItem());
+ repaintItem(App::pressedItem());
}
if (textlnkDown() != textlnkOver()) {
- updateMsg(App::pressedLinkItem());
+ repaintItem(App::pressedLinkItem());
textlnkDown(textlnkOver());
App::pressedLinkItem(App::hoveredLinkItem());
- updateMsg(App::pressedLinkItem());
- updateMsg(App::pressedItem());
+ repaintItem(App::pressedLinkItem());
+ repaintItem(App::pressedItem());
}
_dragAction = NoDrag;
@@ -495,7 +495,7 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
if (textlnkDown()) {
_dragAction = PrepareDrag;
} else if (!_selected.isEmpty()) {
- if (_selected.cbegin().value() == FullItemSel) {
+ if (_selected.cbegin().value() == FullSelection) {
if (_selected.constFind(_dragItem) != _selected.cend() && App::hoveredItem()) {
_dragAction = PrepareDrag; // start items drag
} else if (!_dragWasInactive) {
@@ -510,9 +510,9 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
_dragItem->getSymbol(symbol, afterDragSymbol, uponSymbol, _dragStartPos.x(), _dragStartPos.y());
if (uponSymbol) {
uint32 selStatus = (symbol << 16) | symbol;
- if (selStatus != FullItemSel && (_selected.isEmpty() || _selected.cbegin().value() != FullItemSel)) {
+ if (selStatus != FullSelection && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection)) {
if (!_selected.isEmpty()) {
- updateMsg(_selected.cbegin().key());
+ repaintItem(_selected.cbegin().key());
_selected.clear();
}
_selected.insert(_dragItem, selStatus);
@@ -532,7 +532,7 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
bool uponSelected = uponSymbol;
if (uponSelected) {
if (_selected.isEmpty() ||
- _selected.cbegin().value() == FullItemSel ||
+ _selected.cbegin().value() == FullSelection ||
_selected.cbegin().key() != _dragItem
) {
uponSelected = false;
@@ -551,14 +551,14 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
} else {
if (afterDragSymbol) ++_dragSymbol;
uint32 selStatus = (_dragSymbol << 16) | _dragSymbol;
- if (selStatus != FullItemSel && (_selected.isEmpty() || _selected.cbegin().value() != FullItemSel)) {
+ if (selStatus != FullSelection && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection)) {
if (!_selected.isEmpty()) {
- updateMsg(_selected.cbegin().key());
+ repaintItem(_selected.cbegin().key());
_selected.clear();
}
_selected.insert(_dragItem, selStatus);
_dragAction = Selecting;
- updateMsg(_dragItem);
+ repaintItem(_dragItem);
} else {
_dragAction = PrepareSelect;
}
@@ -593,13 +593,13 @@ void HistoryInner::onDragExec() {
if (_dragItem) {
bool afterDragSymbol;
uint16 symbol;
- if (!_selected.isEmpty() && _selected.cbegin().value() == FullItemSel) {
+ if (!_selected.isEmpty() && _selected.cbegin().value() == FullSelection) {
uponSelected = _selected.contains(_dragItem);
} else {
_dragItem->getSymbol(symbol, afterDragSymbol, uponSelected, _dragStartPos.x(), _dragStartPos.y());
if (uponSelected) {
if (_selected.isEmpty() ||
- _selected.cbegin().value() == FullItemSel ||
+ _selected.cbegin().value() == FullSelection ||
_selected.cbegin().key() != _dragItem
) {
uponSelected = false;
@@ -631,7 +631,7 @@ void HistoryInner::onDragExec() {
mimeData->setText(sel);
if (!urls.isEmpty()) mimeData->setUrls(urls);
- if (uponSelected && !_selected.isEmpty() && _selected.cbegin().value() == FullItemSel && cWideMode()) {
+ if (uponSelected && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && cWideMode()) {
mimeData->setData(qsl("application/x-td-forward-selected"), "1");
}
drag->setMimeData(mimeData);
@@ -644,7 +644,7 @@ void HistoryInner::onDragExec() {
bool lnkPhoto = (lnkType == qstr("PhotoLink")),
lnkVideo = (lnkType == qstr("VideoOpenLink")),
lnkAudio = (lnkType == qstr("AudioOpenLink")),
- lnkDocument = (lnkType == qstr("DocumentOpenLink")),
+ lnkDocument = (lnkType == qstr("DocumentOpenLink") || lnkType == qstr("GifOpenLink")),
lnkContact = (lnkType == qstr("PeerLink") && dynamic_cast(pressedLnkItem->getMedia())),
dragSticker = dynamic_cast(pressedItem ? pressedItem->getMedia() : 0),
dragByDate = (_dragCursorState == HistoryInDateCursorState);
@@ -682,7 +682,7 @@ void HistoryInner::itemRemoved(HistoryItem *item) {
}
if (_dragAction == NoDrag) return;
-
+
if (_dragItem == item) {
dragActionCancel();
}
@@ -694,20 +694,6 @@ void HistoryInner::itemRemoved(HistoryItem *item) {
updateDragSelection(_dragSelFrom, _dragSelTo, _dragSelecting, true);
}
-void HistoryInner::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 HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton button) {
TextLinkPtr needClick;
@@ -721,9 +707,9 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but
bool lnkPhoto = (lnkType == qstr("PhotoLink")),
lnkVideo = (lnkType == qstr("VideoOpenLink")),
lnkAudio = (lnkType == qstr("AudioOpenLink")),
- lnkDocument = (lnkType == qstr("DocumentOpenLink")),
+ lnkDocument = (lnkType == qstr("DocumentOpenLink") || lnkType == qstr("GifOpenLink")),
lnkContact = (lnkType == qstr("PeerLink") && dynamic_cast(App::pressedLinkItem() ? App::pressedLinkItem()->getMedia() : 0));
- if (_dragAction == PrepareDrag && !_dragWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullItemSel && button != Qt::RightButton) {
+ if (_dragAction == PrepareDrag && !_dragWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && button != Qt::RightButton) {
if (lnkPhoto || lnkVideo || lnkAudio || lnkDocument || lnkContact) {
needClick = TextLinkPtr();
}
@@ -731,7 +717,7 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but
}
}
if (textlnkDown()) {
- updateMsg(App::pressedLinkItem());
+ repaintItem(App::pressedLinkItem());
textlnkDown(TextLinkPtr());
App::pressedLinkItem(0);
if (!textlnkOver() && _cursor != style::cur_default) {
@@ -740,7 +726,7 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but
}
}
if (App::pressedItem()) {
- updateMsg(App::pressedItem());
+ repaintItem(App::pressedItem());
App::pressedItem(0);
}
@@ -752,28 +738,28 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but
dragActionCancel();
return;
}
- if (_dragAction == PrepareSelect && !_dragWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullItemSel) {
+ if (_dragAction == PrepareSelect && !_dragWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection) {
SelectedItems::iterator i = _selected.find(_dragItem);
if (i == _selected.cend() && !_dragItem->serviceMsg() && _dragItem->id > 0) {
if (_selected.size() < MaxSelectedItems) {
- if (!_selected.isEmpty() && _selected.cbegin().value() != FullItemSel) {
+ if (!_selected.isEmpty() && _selected.cbegin().value() != FullSelection) {
_selected.clear();
}
- _selected.insert(_dragItem, FullItemSel);
+ _selected.insert(_dragItem, FullSelection);
}
} else {
_selected.erase(i);
}
- updateMsg(_dragItem);
+ repaintItem(_dragItem);
} else if (_dragAction == PrepareDrag && !_dragWasInactive && button != Qt::RightButton) {
SelectedItems::iterator i = _selected.find(_dragItem);
- if (i != _selected.cend() && i.value() == FullItemSel) {
+ if (i != _selected.cend() && i.value() == FullSelection) {
_selected.erase(i);
- updateMsg(_dragItem);
- } else if (i == _selected.cend() && !_dragItem->serviceMsg() && _dragItem->id > 0 && !_selected.isEmpty() && _selected.cbegin().value() == FullItemSel) {
+ repaintItem(_dragItem);
+ } else if (i == _selected.cend() && !_dragItem->serviceMsg() && _dragItem->id > 0 && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection) {
if (_selected.size() < MaxSelectedItems) {
- _selected.insert(_dragItem, FullItemSel);
- updateMsg(_dragItem);
+ _selected.insert(_dragItem, FullSelection);
+ repaintItem(_dragItem);
}
} else {
_selected.clear();
@@ -785,7 +771,7 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but
_dragSelFrom = _dragSelTo = 0;
} else if (!_selected.isEmpty() && !_dragWasInactive) {
uint32 sel = _selected.cbegin().value();
- if (sel != FullItemSel && (sel & 0xFFFF) == ((sel >> 16) & 0xFFFF)) {
+ if (sel != FullSelection && (sel & 0xFFFF) == ((sel >> 16) & 0xFFFF)) {
_selected.clear();
if (App::wnd()) App::wnd()->setInnerFocus();
}
@@ -807,7 +793,7 @@ void HistoryInner::mouseReleaseEvent(QMouseEvent *e) {
void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) {
if (!_history) return;
- if (((_dragAction == Selecting && !_selected.isEmpty() && _selected.cbegin().value() != FullItemSel) || (_dragAction == NoDrag && (_selected.isEmpty() || _selected.cbegin().value() != FullItemSel))) && _dragSelType == TextSelectLetters && _dragItem) {
+ if (((_dragAction == Selecting && !_selected.isEmpty() && _selected.cbegin().value() != FullSelection) || (_dragAction == NoDrag && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection))) && _dragSelType == TextSelectLetters && _dragItem) {
bool afterDragSymbol, uponSelected;
uint16 symbol;
_dragItem->getSymbol(symbol, afterDragSymbol, uponSelected, _dragStartPos.x(), _dragStartPos.y());
@@ -818,7 +804,7 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) {
_dragAction = Selecting;
uint32 selStatus = (symbol << 16) | symbol;
if (!_selected.isEmpty()) {
- updateMsg(_selected.cbegin().key());
+ repaintItem(_selected.cbegin().key());
_selected.clear();
}
_selected.insert(_dragItem, selStatus);
@@ -850,7 +836,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
int32 isUponSelected = 0, hasSelected = 0;;
if (!_selected.isEmpty()) {
isUponSelected = -1;
- if (_selected.cbegin().value() == FullItemSel) {
+ if (_selected.cbegin().value() == FullSelection) {
hasSelected = 2;
if (App::hoveredItem() && _selected.constFind(App::hoveredItem()) != _selected.cend()) {
isUponSelected = 2;
@@ -894,9 +880,12 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
_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)) {
+ if ((lnkVideo && lnkVideo->video()->loading()) || (lnkAudio && lnkAudio->audio()->loading()) || (lnkDocument && lnkDocument->document()->loading())) {
_menu->addAction(lang(lng_context_cancel_download), this, SLOT(cancelContextDownload()))->setEnabled(true);
} else {
+ if (lnkDocument && lnkDocument->document()->loaded() && lnkDocument->document()->isGifv()) {
+ _menu->addAction(lang(lng_context_save_gif), this, SLOT(saveContextGif()))->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 || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), this, SLOT(showContextInFolder()))->setEnabled(true);
}
@@ -941,14 +930,36 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
_menu->addAction(lang(lng_context_reply_msg), _widget, SLOT(onReplyToMessage()));
}
if (item && !isUponSelected && !_contextMenuLnk) {
- if (HistorySticker *sticker = dynamic_cast(msg ? msg->getMedia() : 0)) {
- DocumentData *doc = sticker->document();
- if (doc && doc->sticker() && doc->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
- _menu->addAction(lang(doc->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), _widget, SLOT(onStickerPackInfo()));
+ if (HistoryMedia *media = (msg ? msg->getMedia() : 0)) {
+ if (media->type() == MediaTypeWebPage && static_cast(media)->attach()) {
+ media = static_cast(media)->attach();
+ }
+ if (media->type() == MediaTypeSticker) {
+ DocumentData *doc = media->getDocument();
+ if (doc && doc->sticker() && doc->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
+ _menu->addAction(lang(doc->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), _widget, SLOT(onStickerPackInfo()));
+ }
+
+ _menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextFile()))->setEnabled(true);
+ } else if (media->type() == MediaTypeGif) {
+ DocumentData *doc = media->getDocument();
+ if (doc) {
+ if (doc->loading()) {
+ _menu->addAction(lang(lng_context_cancel_download), this, SLOT(cancelContextDownload()))->setEnabled(true);
+ } else {
+ if (doc->isGifv()) {
+ _menu->addAction(lang(lng_context_save_gif), this, SLOT(saveContextGif()))->setEnabled(true);
+ }
+ if (!doc->already(true).isEmpty()) {
+ _menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), this, SLOT(showContextInFolder()))->setEnabled(true);
+ }
+ _menu->addAction(lang(lng_context_save_file), this, SLOT(saveContextFile()))->setEnabled(true);
+ }
+ }
}
}
- QString contextMenuText = item->selectedText(FullItemSel);
- if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || msg->getMedia()->type() != MediaTypeSticker)) {
+ QString contextMenuText = item->selectedText(FullSelection);
+ if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || (msg->getMedia()->type() != MediaTypeSticker && msg->getMedia()->type() != MediaTypeGif))) {
_menu->addAction(lang(lng_context_copy_text), this, SLOT(copyContextText()))->setEnabled(true);
}
}
@@ -1036,9 +1047,9 @@ void HistoryInner::copyContextUrl() {
void HistoryInner::saveContextImage() {
PhotoLink *lnk = dynamic_cast(_contextMenuLnk.data());
if (!lnk) return;
-
+
PhotoData *photo = lnk->photo();
- if (!photo || !photo->date || !photo->full->loaded()) return;
+ if (!photo || !photo->date || !photo->loaded()) return;
QString file;
if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg")))) {
@@ -1051,45 +1062,83 @@ void HistoryInner::saveContextImage() {
void HistoryInner::copyContextImage() {
PhotoLink *lnk = dynamic_cast(_contextMenuLnk.data());
if (!lnk) return;
-
+
PhotoData *photo = lnk->photo();
- if (!photo || !photo->date || !photo->full->loaded()) return;
+ if (!photo || !photo->date || !photo->loaded()) return;
QApplication::clipboard()->setPixmap(photo->full->pix());
}
void HistoryInner::cancelContextDownload() {
- VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data());
- AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data());
- DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data());
- mtpFileLoader *loader = lnkVideo ? lnkVideo->video()->loader : (lnkAudio ? lnkAudio->audio()->loader : (lnkDocument ? lnkDocument->document()->loader : 0));
- if (loader) loader->cancel();
+ if (VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data())) {
+ lnkVideo->video()->cancel();
+ } else if (AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data())) {
+ lnkAudio->audio()->cancel();
+ } else if (DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data())) {
+ lnkDocument->document()->cancel();
+ } else if (HistoryItem *item = App::contextItem()) {
+ if (HistoryMedia *media = item->getMedia()) {
+ if (DocumentData *doc = media->getDocument()) {
+ doc->cancel();
+ }
+ }
+ }
}
void HistoryInner::showContextInFolder() {
- VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data());
- AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data());
- DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data());
- QString already = lnkVideo ? lnkVideo->video()->already(true) : (lnkAudio ? lnkAudio->audio()->already(true) : (lnkDocument ? lnkDocument->document()->already(true) : QString()));
+ QString already;
+ if (VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data())) {
+ already = lnkVideo->video()->already(true);
+ } else if (AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data())) {
+ already = lnkAudio->audio()->already(true);
+ } else if (DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data())) {
+ already = lnkDocument->document()->already(true);
+ } else if (HistoryItem *item = App::contextItem()) {
+ if (HistoryMedia *media = item->getMedia()) {
+ if (DocumentData *doc = media->getDocument()) {
+ already = doc->already(true);
+ }
+ }
+ }
if (!already.isEmpty()) psShowInFolder(already);
}
void HistoryInner::openContextFile() {
- VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data());
+ HistoryItem *was = App::hoveredLinkItem();
+ App::hoveredLinkItem(App::contextItem());
+ VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data());
AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data());
DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data());
if (lnkVideo) VideoOpenLink(lnkVideo->video()).onClick(Qt::LeftButton);
if (lnkAudio) AudioOpenLink(lnkAudio->audio()).onClick(Qt::LeftButton);
if (lnkDocument) DocumentOpenLink(lnkDocument->document()).onClick(Qt::LeftButton);
+ App::hoveredLinkItem(was);
}
void HistoryInner::saveContextFile() {
- VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data());
- AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data());
- DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data());
- if (lnkVideo) VideoSaveLink::doSave(lnkVideo->video(), true);
- if (lnkAudio) AudioSaveLink::doSave(lnkAudio->audio(), true);
- if (lnkDocument) DocumentSaveLink::doSave(lnkDocument->document(), true);
+ if (VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data())) {
+ VideoSaveLink::doSave(lnkVideo->video(), true);
+ } else if (AudioLink *lnkAudio = dynamic_cast