From d56e8ec9eaf4fa20c16d41768e0f83c9673e9e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=97=D0=BE=D0=BD=D0=BE=D0=B2?= Date: Mon, 30 Nov 2015 16:16:06 +0300 Subject: [PATCH 001/145] =?UTF-8?q?Do=20not=20depend=20on=20MFC=20Signed-o?= =?UTF-8?q?ff-by:=20=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=97=D0=BE=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=20=20(github:=20OZ1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Telegram/Telegram.rc | Bin 5540 -> 2769 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc index 58e43fa2420be95863224316d36942992d4d8f6e..1ecd730361c67eb534408458a8d3c6630db9b696 100644 GIT binary patch literal 2769 zcmcImUvJtl5PwJFJKVCDRznb=o7BgU7#1~65J^xgAw?M&uofI-n{}1?;dc%p3vI_H z4g3<{`R;swes@l%1NUq$a+xm`OqtxJ8Q7mb!HRM!QbiXasLXd_PC?EE+p2c6(djfB z9~qx#y9EW~aNb@U%?2JU=#p^?);Nq}#|@GRv7Es79+U9#eh_$})<5IgyVL2wsPcFz z@(rjpg@`;vu5Wv>L!eg$@+N{O#xaP}|1-K)7xtQUHZhi0Q&XF{_vAS2grHUL)*vNIDwOMG;!+320n z_zicXLW?mIGmh$o$-1Hb2}6I<`AGQ!%YqBTsd}eMoA&R_$kgb>yWDxs4>>DIcw%Y| zS^h+&0@r~bX||)V&C)rJgC!HlRXI>DcY;E?-DYf_DwcC#r7eWpCS_a#(vnF+ zvkc@jk{gLV=sIrV+JWEILD!b%)yQgVgvA&dvW&{vEEQ@t)A#j0e`(_v>u{99Qyp;T zL|&veK=>+Sat&88XL<~W3a7FB|4t@;{HBwMKl0r0>+eo38A*)O`clA~QXvsE2kcG5 zWJ)HX8~BOq4+AI(y~h?Cx*jPxV76{s-G0kN_IMIFu^qj0q7{aoH45*o@D9wM{iUZL zt)-qyi9-hQsI*!Lp+Hxk(@fG=e6!%|<}43_5T$QS)4aix!m;RaOpvD~3mWHPja3^w z123@ezzCIKe5HZx@})+u#&}JvAorr?jd^R{eA83v&>ycXk literal 5540 zcmd6r+in{-5QgVEK;MCpZxST1WhW_+OIwmvq4ELN0S#claBRylYRiIF$^po;xA}fj z^zKT#ZYmeWVzDBZ91j0EaAv5#|Jt%Gb9P`ic41Syw}f|OGscFDB{sKnyXC84uXvnY z+m(%Zr;N{NFBzNCYTcaP1%1btxZT(Unoaw~p0Uf8Z#NhnGkXj5rKhc}tlzXxR`b+a zx#8(Xew}#l+OhSlZv$&u$ByjGPOQaflm4M~?E@n{eh+MqkuLqXzc5tqiRfbMtc_ll zbv!~#_jZT;GqfW;^pW}xemh9rKu+nS#rO{IvPJ&c2IpA)>tbDA|JBo2L=?PZj;`cM zBjjGsH=^bv-)glHox{)PoUDYCaE{E*UgSnz7T7(>oT3}Aq35~NbP11L+sBW>De7@z z25@cQNoV*|mq&cR^SIKLeR!PUwc;`0yuI|hb{RcnbU@qoo*73XT@T+C&1-n~pxa}& zuts~gz_rV07ax}ng0&?@xwUl`hRER!Z%spMu&RFTZhE-W8OF1YfJhxC?(4Wx?jq~4GTp_F7 zL8A&Hfg<2mJQNb;OyLQuRkf&YN$lHV_Kr5>K_TuCtvbXx)eJgOk3Es@F`DdLjcAru zL;K*n1zm!_pMri0O7$eKuq zmVIsSdAIEizjeQl;~sMY$0mAgkZtpP%htpl_I<6?2H^o!Qjdy9E55>i#Ej4=7pJvS zsHpbXtG(ELl~drb{jmNDFHg`_Ubb}SKwr<$s}k3)`4vat3$$;+sORlsO{%dzH@0Im zm*<(zhx)LXs<`4&HF<<)r5Q!!88%nuxW-B$B6s*2;RE>^q?lWb?|H70q>g3Pe&jdL zDP2=kk;U2A4zZ(daneBz+`8>eh$Le&*dgs5mZw#k)q$=!T_R=~$%CtI?_rW0^Fny9w=RL0v zH+fxxtH=J-{jM5#IIIg(r4cgmmmAj zV)b58Es3&dB$>Wr`_zB0bkAF^nDYN#MO|i5RI4w9VH+Mb{D4~6={ Date: Sat, 5 Dec 2015 14:07:40 -0500 Subject: [PATCH 002/145] Fix some format, add warning on file hierarchy, fix typo and create list for readability.Signed-off-by: Fabrice Edon (github: Bl4ck4t) --- XCODE.md | 105 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/XCODE.md b/XCODE.md index 9c78f70fd..a9121f3ba 100644 --- a/XCODE.md +++ b/XCODE.md @@ -2,35 +2,59 @@ ###Prepare folder -Choose a folder for the future build, for example **/Users/user/TBuild** There you will have two folders, **Libraries** for third-party libs and **tdesktop** (or **tdesktop-master**) for the app. +Choose a folder for the future build, for example **/Users/user/TBuild** + +There you will have two folders, **Libraries** for third-party libs and **tdesktop** (or **tdesktop-master**) for the app. + +**You will need this hierarchy to be able to follow this README !** ###Clone source code -By git – in Terminal go to **/Users/user/TBuild** and run +By git – in Terminal go to **/Users/user/TBuild** and run: git clone https://github.com/telegramdesktop/tdesktop.git -or download in ZIP and extract to **/Users/user/TBuild** rename **tdesktop-master** to **tdesktop** to have **/Users/user/TBuild/tdesktop/Telegram/Telegram.xcodeproj** project +or: +* download in ZIP and extract to **/Users/user/TBuild** +* rename **tdesktop-master** to **tdesktop**. + +The path to Telegram.xcodeproj should now be: **/Users/user/TBuild/tdesktop/Telegram/Telegram.xcodeproj** ###Prepare libraries -In your build Terminal run +In your build Terminal run: - MACOSX_DEPLOYMENT_TARGET=10.8 + MACOSX_DEPLOYMENT_TARGET=10.8 to set minimal supported OS version to 10.8 for future console builds. ####OpenSSL 1.0.1g -Get sources from https://github.com/telegramdesktop/openssl-xcode, by git – in Terminal go to **/Users/user/TBuild/Libraries** and run +#####Get openssl-xcode project file - git clone https://github.com/telegramdesktop/openssl-xcode.git +From https://github.com/telegramdesktop/openssl-xcode with git in Terminal: -or download in ZIP and extract to **/Users/user/TBuild/Libraries**, rename **openssl-xcode-master** to **openssl-xcode** to have **/Users/user/TBuild/Libraries/openssl-xcode/openssl.xcodeproj** project +* go to **/Users/user/TBuild/Libraries +* run: + + git clone https://github.com/telegramdesktop/openssl-xcode.git -http://www.openssl.org/source/ > Download [**openssl-1.0.1h.tar.gz**](http://www.openssl.org/source/openssl-1.0.1h.tar.gz) (4.3 Mb) +or: -Extract openssl-1.0.1h.tar.gz and copy everything from **openssl-1.0.1h** to **/Users/user/TBuild/Libraries/openssl-xcode** to have **/Users/user/TBuild/Libraries/openssl-xcode/include** +* download in ZIP and extract to **/Users/user/TBuild/Libraries**, +* rename **openssl-xcode-master** to **openssl-xcode** + +The path to openssl.xcodeproj should now be: **/Users/user/TBuild/Libraries/openssl-xcode/openssl.xcodeproj** + +#####Get the source code: + +Download [**openssl-1.0.1h.tar.gz**](http://www.openssl.org/source/openssl-1.0.1h.tar.gz) (4.3 Mb) + +* Extract openssl-1.0.1h.tar.gz +* Copy everything from **openssl-1.0.1h** to **/Users/user/TBuild/Libraries/openssl-xcode** + +The folder include of openssl should be: +**/Users/user/TBuild/Libraries/openssl-xcode/include** #####Building library @@ -38,14 +62,15 @@ Extract openssl-1.0.1h.tar.gz and copy everything from **openssl-1.0.1h** to **/ * Product > Build ####liblzma +#####Get the source code -http://tukaani.org/xz/ > Download [**xz-5.0.5.tar.gz**](http://tukaani.org/xz/xz-5.0.5.tar.gz) +Download [**xz-5.0.5.tar.gz**](http://tukaani.org/xz/xz-5.0.5.tar.gz) Extract to **/Users/user/TBuild/Libraries** #####Building library -In Terminal go to **/Users/user/TBuild/Libraries/xz-5.0.5** and there run +In Terminal go to **/Users/user/TBuild/Libraries/xz-5.0.5** and there run: ./configure make @@ -56,12 +81,23 @@ In Terminal go to **/Users/user/TBuild/Libraries/xz-5.0.5** and there run Using se system lib ####libexif 0.6.20 +#####Get the source code -Get sources from https://github.com/telegramdesktop/libexif-0.6.20, by git – in Terminal go to **/Users/user/TBuild/Libraries** and run +From https://github.com/telegramdesktop/libexif-0.6.20 with git in Terminal: - git clone https://github.com/telegramdesktop/libexif-0.6.20.git +* go to **/Users/user/TBuild/Libraries** +* run: -or download in ZIP and extract to **/Users/user/TBuild/Libraries**, rename **libexif-0.6.20-master** to **libexif-0.6.20** to have **/Users/user/TBuild/Libraries/libexif-0.6.20/configure** script + git clone https://github.com/telegramdesktop/libexif-0.6.20.git + +or: + +* download in ZIP +* extract to **/Users/user/TBuild/Libraries** +* rename **libexif-0.6.20-master** to **libexif-0.6.20** + +The folder configure should have this path: +**/Users/user/TBuild/Libraries/libexif-0.6.20/configure** #####Building library @@ -88,40 +124,46 @@ In Terminal go to **/Users/user/TBuild/Libraries/openal-soft/build** and there r sudo make install ####Opus codec +#####Get the source code -Download sources [opus-1.1.tar.gz](http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz) from http://www.opus-codec.org/downloads/, extract to **/Users/user/TBuild/Libraries** and rename to have **/Users/user/TBuild/Libraries/opus/configure** +* Download sources [opus-1.1.tar.gz](http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz) from http://www.opus-codec.org/downloads/ +* Extract them to **/Users/user/TBuild/Libraries** +* Rename opus-1.1 to opus to have **/Users/user/TBuild/Libraries/opus/configure** -#####Building libraries +#####Building library -Download [pkg-config 0.28](http://pkgconfig.freedesktop.org/releases/pkg-config-0.28.tar.gz) from http://pkg-config.freedesktop.org, extract it to **/Users/user/TBuild/Libraries** +* Download [pkg-config 0.28](http://pkgconfig.freedesktop.org/releases/pkg-config-0.28.tar.gz) from http://pkg-config.freedesktop.org +* Extract it to **/Users/user/TBuild/Libraries** -In Terminal go to **/Users/user/TBuild/Libraries/pkg-config-0.28** and run +In Terminal go to **/Users/user/TBuild/Libraries/pkg-config-0.28** and run: ./configure --with-internal-glib make sudo make install -then go to **/Users/user/TBuild/Libraries/opus** and there run +then go to **/Users/user/TBuild/Libraries/opus** and run: ./configure make sudo make install -####FFmpeg +####FFmpeg and Libiconv +#####Get the source code -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 **/Users/user/TBuild/Libraries** to have **/Users/user/TBuild/Libraries/ffmpeg-2.6.3** +* 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 **/Users/user/TBuild/Libraries** to have **/Users/user/TBuild/Libraries/ffmpeg-2.6.3** -#####Building libraries +* Download [libiconv-1.14](http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz) from http://www.gnu.org/software/libiconv/#downloading +* Extract to **/Users/user/TBuild/Libraries** to have **/Users/user/TBuild/Libraries/ibiconv-1.14 ** -Download [libiconv-1.14](http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz) from http://www.gnu.org/software/libiconv/#downloading, extract it to **/Users/user/TBuild/Libraries** - -In Termianl go to **/Users/user/TBuild/Libraries/libiconv-1.14** and run +#####Building library +In Terminal go to **/Users/user/TBuild/Libraries/libiconv-1.14** and run: ./configure --enable-static make sudo make install -Then in Terminal go to **/Users/user/TBuild/Libraries/ffmpeg-2.6.3** and run +Then in Terminal go to **/Users/user/TBuild/Libraries/ffmpeg-2.6.3** and run: ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" @@ -137,8 +179,9 @@ Then in Terminal go to **/Users/user/TBuild/Libraries/ffmpeg-2.6.3** and run sudo make install ####Qt 5.5.1, slightly patched +#####Get the source code -In Terminal go to **/Users/user/TBuild/Libraries** and run +In Terminal go to **/Users/user/TBuild/Libraries** and run: git clone git://code.qt.io/qt/qt5.git QtStatic cd QtStatic @@ -149,16 +192,18 @@ In Terminal go to **/Users/user/TBuild/Libraries** and run cd qtbase && git checkout v5.5.1 && cd .. #####Apply the patch +From **/Users/user/TBuild/Libraries/QtStatic/qtbase**, run: - cd qtbase && git apply ../../../tdesktop/Telegram/_qtbase_5_5_1_patch.diff && cd .. + git apply ../../../tdesktop/Telegram/_qtbase_5_5_1_patch.diff #####Building library +Go to **/Users/user/TBuild/Libraries/QtStatic** and run: ./configure -debug-and-release -opensource -confirm-license -static -opengl desktop -no-openssl -securetransport -nomake examples -nomake tests -platform macx-clang make -j4 sudo make -j4 install -building (**make** command) will take really long time. +Building (**make** command) will take a really long time. ###Building Telegram Desktop From 6222876ac4908014b44fa0a8e5f962130c6bc165 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 7 Dec 2015 21:09:05 +0300 Subject: [PATCH 003/145] refactored layer methods --- Telegram/SourceFiles/application.cpp | 2 +- Telegram/SourceFiles/boxes/aboutbox.cpp | 2 +- Telegram/SourceFiles/boxes/addcontactbox.cpp | 20 ++-- Telegram/SourceFiles/boxes/confirmbox.cpp | 4 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 32 +++--- Telegram/SourceFiles/boxes/languagebox.cpp | 8 +- Telegram/SourceFiles/boxes/passcodebox.cpp | 12 +-- Telegram/SourceFiles/boxes/sessionsbox.cpp | 4 +- Telegram/SourceFiles/boxes/stickersetbox.cpp | 10 +- Telegram/SourceFiles/dialogswidget.cpp | 12 +-- Telegram/SourceFiles/dropdown.cpp | 8 +- Telegram/SourceFiles/facades.cpp | 25 ++--- Telegram/SourceFiles/facades.h | 7 +- Telegram/SourceFiles/gui/countryinput.cpp | 6 +- Telegram/SourceFiles/gui/text.cpp | 2 +- Telegram/SourceFiles/historywidget.cpp | 18 ++-- Telegram/SourceFiles/intro/intropwdcheck.cpp | 8 +- Telegram/SourceFiles/intro/introsignup.cpp | 2 +- Telegram/SourceFiles/localimageloader.cpp | 8 +- Telegram/SourceFiles/mainwidget.cpp | 74 +++++++------- Telegram/SourceFiles/mediaview.cpp | 2 +- Telegram/SourceFiles/overviewwidget.cpp | 6 +- Telegram/SourceFiles/profilewidget.cpp | 47 +++++---- Telegram/SourceFiles/settingswidget.cpp | 46 ++++----- Telegram/SourceFiles/structs.cpp | 2 +- Telegram/SourceFiles/title.cpp | 6 +- Telegram/SourceFiles/types.h | 10 ++ Telegram/SourceFiles/window.cpp | 101 +++++++++---------- Telegram/SourceFiles/window.h | 10 +- Telegram/Telegram.pro | 3 + 30 files changed, 248 insertions(+), 249 deletions(-) diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index d9df42f09..f57ab30df 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -445,7 +445,7 @@ void Application::onSwitchDebugMode() { f.write("1"); f.close(); } - App::wnd()->hideLayer(); + Ui::hideLayer(); } } diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp index ee2db425d..a6bad679e 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/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 05fe432e5..732997d75 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -236,7 +236,7 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) { } if (uid) { App::main()->addNewContact(uid); - App::wnd()->hideLayer(); + Ui::hideLayer(); } else { _save.hide(); _first.hide(); @@ -336,7 +336,7 @@ 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(), @@ -498,7 +498,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 +551,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 +595,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) { @@ -787,7 +787,7 @@ bool SetupChannelBox::animStep_goodFade(float64 ms) { void SetupChannelBox::closePressed() { if (!_existing) { - App::wnd()->showLayer(new ContactsBox(_channel)); + Ui::showLayer(new ContactsBox(_channel)); } } @@ -872,7 +872,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 +933,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 +961,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 +1269,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/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 69fbe86b0..8fad31da6 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,7 +184,7 @@ void ConfirmLinkBox::onOpenLink() { } else { TextLink(_url).onClick(Qt::LeftButton); } - App::wnd()->hideLayer(); + Ui::hideLayer(); } MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth), diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index a7827d193..8a7c16c53 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -228,7 +228,7 @@ void ContactsInner::onAddBot() { } else { App::main()->addParticipants(_addToPeer, QVector(1, _bot)); } - App::wnd()->hideLayer(); + Ui::hideLayer(); App::main()->showPeerHistory(_addToPeer->id, ShowAtUnreadMsgId); } @@ -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(); } } } @@ -1590,7 +1590,7 @@ void ContactsBox::onInvite() { App::main()->addParticipants(_inner.chat() ? (PeerData*)_inner.chat() : _inner.channel(), users); if (_inner.chat()) { - App::wnd()->hideLayer(); + Ui::hideLayer(); App::main()->showPeerHistory(_inner.chat()->id, 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; @@ -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 c1aae95e6..3ec7ea7ea 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 b98f8957d..45055b2cf 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/sessionsbox.cpp b/Telegram/SourceFiles/boxes/sessionsbox.cpp index 8d58681ff..6cc62071d 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 cfa0790d3..f4954e258 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -67,7 +67,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 +82,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; } @@ -115,13 +115,13 @@ void StickerSetInner::installDone(const MTPBool &result) { 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; } @@ -252,7 +252,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() { diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 56d96241d..15531628e 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -675,12 +675,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,13 +690,13 @@ 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(); + Ui::hideLayer(); App::main()->showDialogs(); if (_menuActionPeer->isUser()) { App::main()->deleteConversation(_menuActionPeer); @@ -2556,11 +2556,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() { diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 20071440c..2ff383332 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -1737,7 +1737,7 @@ void StickerPanInner::updateSelected() { } void StickerPanInner::onSettings() { - App::showLayer(new StickersBox()); + Ui::showLayer(new StickersBox()); } void StickerPanInner::onPreview() { @@ -2147,7 +2147,7 @@ void EmojiPan::mousePressEvent(QMouseEvent *e) { updateSelected(); if (_iconOver == _icons.size()) { - App::showLayer(new StickersBox()); + Ui::showLayer(new StickersBox()); } else { _iconDown = _iconOver; _iconsMouseDown = _iconsMousePos; @@ -2672,12 +2672,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) { diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index caa7fb9de..afd723eea 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -66,18 +66,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,6 +78,19 @@ 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); + } + + 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; + } + } namespace Notify { diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index afbbb8e07..cc6cdb606 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -34,9 +34,6 @@ 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); }; @@ -45,6 +42,10 @@ namespace Ui { // it 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(); + }; namespace Notify { diff --git a/Telegram/SourceFiles/gui/countryinput.cpp b/Telegram/SourceFiles/gui/countryinput.cpp index 53719fa5c..540573750 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/text.cpp b/Telegram/SourceFiles/gui/text.cpp index 562112207..cddee2412 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -916,7 +916,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 { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index dddec35fd..702314ced 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3772,7 +3772,7 @@ bool HistoryWidget::messagesFailed(const RPCError &error, mtpRequestId requestId if (error.type() == qstr("CHANNEL_PRIVATE")) { PeerData *was = _peer; App::main()->showDialogs(); - App::wnd()->showLayer(new InformBox(lang((was && was->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); + Ui::showLayer(new InformBox(lang((was && was->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); return true; } @@ -4289,7 +4289,7 @@ bool HistoryWidget::joinFail(const RPCError &error, mtpRequestId req) { if (_unblockRequest == req) _unblockRequest = 0; if (error.type() == qstr("CHANNEL_PRIVATE")) { - App::wnd()->showLayer(new InformBox(lang((_peer && _peer->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); + Ui::showLayer(new InformBox(lang((_peer && _peer->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); return true; } return false; @@ -5223,7 +5223,7 @@ void HistoryWidget::shareContactWithConfirm(const QString &phone, const QString App::wnd()->activateWindow(); _confirmWithTextId = 0xFFFFFFFFFFFFFFFFL; - App::wnd()->showLayer(new PhotoSendBox(phone, fname, lname, replyTo)); + Ui::showLayer(new PhotoSendBox(phone, fname, lname, replyTo)); } void HistoryWidget::confirmSendFile(const FileLoadResultPtr &file, bool ctrlShiftEnter) { @@ -5498,14 +5498,14 @@ void HistoryWidget::onAudioFailed(const FullMsgId &newId) { void HistoryWidget::onReportSpamClicked() { ConfirmBox *box = new ConfirmBox(lang(_peer->isUser() ? lng_report_spam_sure : ((_peer->isChat() || _peer->isMegagroup()) ? lng_report_spam_sure_group : lng_report_spam_sure_channel)), lang(lng_report_spam_ok), st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onReportSpamSure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); _clearPeer = _peer; } void HistoryWidget::onReportSpamSure() { if (_reportSpamRequest) return; - App::wnd()->hideLayer(); + Ui::hideLayer(); if (_clearPeer->isUser()) MTP::send(MTPcontacts_Block(_clearPeer->asUser()->inputUser), rpcDone(&HistoryWidget::blockDone, _clearPeer), RPCFailHandlerPtr(), 0, 5); _reportSpamRequest = MTP::send(MTPmessages_ReportSpam(_clearPeer->input), rpcDone(&HistoryWidget::reportSpamDone, _clearPeer), rpcFail(&HistoryWidget::reportSpamFail)); } @@ -6144,7 +6144,7 @@ void HistoryWidget::onReplyToMessage() { box = new ConfirmBox(lang(lng_reply_cant_forward), lang(lng_selected_forward)); connect(box, SIGNAL(confirmed()), this, SLOT(onForwardHere())); } - App::showLayer(box); + Ui::showLayer(box); } return; } @@ -6457,7 +6457,7 @@ void HistoryWidget::onDeleteSelectedSure() { if (App::main() && App::main()->peer() == peer()) { App::main()->itemResized(0); } - App::wnd()->hideLayer(); + Ui::hideLayer(); for (QMap >::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) { App::main()->deleteMessages(i.key(), i.value()); @@ -6481,7 +6481,7 @@ void HistoryWidget::onDeleteContextSure() { if (App::main() && (App::main()->peer() == h->peer || (App::main()->peer() && h->peer->migrateTo() == App::main()->peer()))) { App::main()->itemResized(0); } - App::wnd()->hideLayer(); + Ui::hideLayer(); if (wasOnServer) { App::main()->deleteMessages(h->peer, toDelete); @@ -6544,7 +6544,7 @@ void HistoryWidget::updateTopBarSelection() { App::main()->topBar()->showSelected(_selCount > 0 ? _selCount : 0, (selectedForDelete == selectedForForward)); updateControlsVisibility(); updateListSize(); - if (!App::wnd()->layerShown() && !App::passcoded()) { + if (!Ui::isLayerShown() && !App::passcoded()) { if (_selCount || (_list && _list->wasSelectedText()) || _recording || isBotStart() || isBlocked() || !_canSendMessages) { _list->setFocus(); } else { diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index 211beffd4..95a670539 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -289,14 +289,14 @@ void IntroPwdCheck::onToRecover() { update(); } else { ConfirmBox *box = new InformBox(lang(lng_signin_no_email_forgot)); - App::wnd()->showLayer(box); + Ui::showLayer(box); connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onToReset())); } } void IntroPwdCheck::onToPassword() { ConfirmBox *box = new InformBox(lang(lng_signin_cant_email_forgot)); - App::wnd()->showLayer(box); + Ui::showLayer(box); connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onToReset())); } @@ -319,7 +319,7 @@ void IntroPwdCheck::onReset() { if (sentRequest) return; ConfirmBox *box = new ConfirmBox(lang(lng_signin_sure_reset), lang(lng_signin_reset), st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onResetSure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void IntroPwdCheck::onResetSure() { @@ -335,7 +335,7 @@ bool IntroPwdCheck::deleteFail(const RPCError &error) { } void IntroPwdCheck::deleteDone(const MTPBool &v) { - App::wnd()->hideLayer(); + Ui::hideLayer(); intro()->onIntroNext(); } diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index fbb19cd45..3a67d6118 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -90,7 +90,7 @@ void IntroSignup::mousePressEvent(QMouseEvent *e) { } PhotoCropBox *box = new PhotoCropBox(img, PeerId(0)); connect(box, SIGNAL(ready(const QImage &)), this, SLOT(onPhotoReady(const QImage &))); - App::wnd()->showLayer(box); + Ui::showLayer(box); } } diff --git a/Telegram/SourceFiles/localimageloader.cpp b/Telegram/SourceFiles/localimageloader.cpp index 95f0b8779..e81e1ea34 100644 --- a/Telegram/SourceFiles/localimageloader.cpp +++ b/Telegram/SourceFiles/localimageloader.cpp @@ -409,23 +409,23 @@ void FileLoadTask::process() { void FileLoadTask::finish() { if (!_result || !_result->filesize) { if (_result) App::main()->onSendFileCancel(_result); - App::wnd()->replaceLayer(new InformBox(lang(lng_send_image_empty))); + Ui::showLayer(new InformBox(lang(lng_send_image_empty)), KeepOtherLayers); return; } if (_result->filesize == -1) { // dir App::main()->onSendFileCancel(_result); - App::wnd()->replaceLayer(new InformBox(lng_send_folder(lt_name, QFileInfo(_filepath).dir().dirName()))); + Ui::showLayer(new InformBox(lng_send_folder(lt_name, QFileInfo(_filepath).dir().dirName())), KeepOtherLayers); return; } if (_result->filesize > MaxUploadDocumentSize) { App::main()->onSendFileCancel(_result); - App::wnd()->replaceLayer(new InformBox(lang(lng_send_image_too_large))); + Ui::showLayer(new InformBox(lang(lng_send_image_too_large)), KeepOtherLayers); return; } if (App::main()) { bool confirm = (_confirm == FileLoadAlwaysConfirm) || (_result->photo.type() != mtpc_photoEmpty && _confirm != FileLoadNeverConfirm); if (confirm) { - App::wnd()->showLayerLast(new PhotoSendBox(_result)); + Ui::showLayer(new PhotoSendBox(_result), ShowAfterOtherLayers); } else { if (_result->type == PrepareAuto) { _result->type = (_result->photo.type() != mtpc_photoEmpty) ? PreparePhoto : PrepareDocument; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index b5dd08893..2d5d59a84 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -89,18 +89,18 @@ void TopBarWidget::onInfoClicked() { void TopBarWidget::onAddContact() { PeerData *p = App::main() ? App::main()->profilePeer() : 0; UserData *u = p ? p->asUser() : 0; - if (u) App::wnd()->showLayer(new AddContactBox(u->firstName, u->lastName, u->phone.isEmpty() ? App::phoneFromSharedContact(peerToUser(u->id)) : u->phone)); + if (u) Ui::showLayer(new AddContactBox(u->firstName, u->lastName, u->phone.isEmpty() ? App::phoneFromSharedContact(peerToUser(u->id)) : u->phone)); } void TopBarWidget::onEdit() { PeerData *p = App::main() ? App::main()->profilePeer() : 0; if (p) { if (p->isChannel()) { - App::wnd()->showLayer(new EditChannelBox(p->asChannel())); + Ui::showLayer(new EditChannelBox(p->asChannel())); } else if (p->isChat()) { - App::wnd()->showLayer(new EditNameTitleBox(p)); + Ui::showLayer(new EditNameTitleBox(p)); } else if (p->isUser()) { - App::wnd()->showLayer(new AddContactBox(p->asUser())); + Ui::showLayer(new AddContactBox(p->asUser())); } } } @@ -111,7 +111,7 @@ void TopBarWidget::onDeleteContact() { if (u) { ConfirmBox *box = new ConfirmBox(lng_sure_delete_contact(lt_contact, p->name), lang(lng_box_delete)); connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteContactSure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } } @@ -120,7 +120,7 @@ void TopBarWidget::onDeleteContactSure() { UserData *u = p ? p->asUser() : 0; if (u) { App::main()->showDialogs(); - App::wnd()->hideLayer(); + Ui::hideLayer(); MTP::send(MTPcontacts_DeleteContact(u->inputUser), App::main()->rpcDone(&MainWidget::deletedContact, u)); } } @@ -131,7 +131,7 @@ void TopBarWidget::onDeleteAndExit() { if (c) { ConfirmBox *box = new ConfirmBox(lng_sure_delete_and_exit(lt_group, p->name), lang(lng_box_leave), st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteAndExitSure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } } @@ -140,7 +140,7 @@ void TopBarWidget::onDeleteAndExitSure() { ChatData *c = p ? p->asChat() : 0; if (c) { App::main()->showDialogs(); - App::wnd()->hideLayer(); + Ui::hideLayer(); MTP::send(MTPmessages_DeleteChatUser(c->inputChat, App::self()->inputUser), App::main()->rpcDone(&MainWidget::deleteHistoryAfterLeave, p), App::main()->rpcFail(&MainWidget::leaveChatFailed, p)); } } @@ -480,7 +480,7 @@ MainWidget::MainWidget(Window *window) : TWidget(window) bool MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) { PeerData *p = App::peer(peer); if (!peer || (p->isChannel() && !p->asChannel()->canPublish() && p->asChannel()->isBroadcast()) || (p->isChat() && !p->asChat()->canWrite()) || (p->isUser() && p->asUser()->access == UserNoAccess)) { - App::wnd()->showLayer(new InformBox(lang(lng_forward_cant))); + Ui::showLayer(new InformBox(lang(lng_forward_cant))); return false; } history.cancelReply(); @@ -514,7 +514,7 @@ bool MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) { bool MainWidget::onShareUrl(const PeerId &peer, const QString &url, const QString &text) { PeerData *p = App::peer(peer); if (!peer || (p->isChannel() && !p->asChannel()->canPublish() && p->asChannel()->isBroadcast()) || (p->isChat() && !p->asChat()->canWrite()) || (p->isUser() && p->asUser()->access == UserNoAccess)) { - App::wnd()->showLayer(new InformBox(lang(lng_share_cant))); + Ui::showLayer(new InformBox(lang(lng_share_cant))); return false; } History *h = App::history(peer); @@ -837,7 +837,7 @@ void MainWidget::deleteLayer(int32 selectedCount) { } else { connect(box, SIGNAL(confirmed()), overview ? overview : static_cast(&history), SLOT(onDeleteSelectedSure())); } - App::wnd()->showLayer(box); + Ui::showLayer(box); } void MainWidget::shareContactLayer(UserData *contact) { @@ -853,13 +853,13 @@ bool MainWidget::selectingPeer(bool withConfirm) { } void MainWidget::offerPeer(PeerId peer) { - App::wnd()->hideLayer(); + Ui::hideLayer(); if (_hider->offerPeer(peer) && !cWideMode()) { _forwardConfirm = new ConfirmBox(_hider->offeredText(), lang(lng_forward_send)); connect(_forwardConfirm, SIGNAL(confirmed()), _hider, SLOT(forward())); connect(_forwardConfirm, SIGNAL(cancelled()), this, SLOT(onForwardCancel())); connect(_forwardConfirm, SIGNAL(destroyed(QObject*)), this, SLOT(onForwardCancel(QObject*))); - App::wnd()->showLayer(_forwardConfirm); + Ui::showLayer(_forwardConfirm); } } @@ -1012,7 +1012,7 @@ bool MainWidget::addParticipantFail(UserData *user, const RPCError &error) { } else if (error.type() == "PEER_FLOOD") { text = 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))); } - App::wnd()->showLayer(new InformBox(text)); + Ui::showLayer(new InformBox(text)); return false; } @@ -1026,13 +1026,13 @@ bool MainWidget::addParticipantsFail(ChannelData *channel, const RPCError &error } else if (error.type() == "PEER_FLOOD") { text = 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))); } - App::wnd()->showLayer(new InformBox(text)); + Ui::showLayer(new InformBox(text)); return false; } void MainWidget::kickParticipant(ChatData *chat, UserData *user) { MTP::send(MTPmessages_DeleteChatUser(chat->inputChat, user->inputUser), rpcDone(&MainWidget::sentUpdatesReceived), rpcFail(&MainWidget::kickParticipantFail, chat)); - App::wnd()->hideLayer(); + Ui::hideLayer(); showPeerHistory(chat->id, ShowAtTheEndMsgId); } @@ -1131,7 +1131,7 @@ bool MainWidget::sendMessageFail(const RPCError &error) { if (mtpIsFlood(error)) return false; if (error.type() == qsl("PEER_FLOOD")) { - App::wnd()->showLayer(new InformBox(lng_cant_send_to_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_send_to_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info))))); return true; } return false; @@ -1639,7 +1639,7 @@ void MainWidget::loadFailed(mtpFileLoader *loader, bool started, const char *ret } else { connect(box, SIGNAL(confirmed()), this, SLOT(onDownloadPathSettings())); } - App::wnd()->showLayer(box); + Ui::showLayer(box); } void MainWidget::onDownloadPathSettings() { @@ -1649,7 +1649,7 @@ void MainWidget::onDownloadPathSettings() { if (App::wnd() && App::wnd()->settingsWidget()) { connect(box, SIGNAL(closed()), App::wnd()->settingsWidget(), SLOT(onDownloadPathEdited())); } - App::wnd()->showLayer(box); + Ui::showLayer(box); } void MainWidget::videoLoadFailed(mtpFileLoader *loader, bool started) { @@ -1659,7 +1659,7 @@ void MainWidget::videoLoadFailed(mtpFileLoader *loader, bool started) { } void MainWidget::videoLoadRetry() { - App::wnd()->hideLayer(); + Ui::hideLayer(); VideoData *video = App::video(failedObjId); if (video) video->save(failedFileName); } @@ -1818,7 +1818,7 @@ void MainWidget::audioLoadFailed(mtpFileLoader *loader, bool started) { } void MainWidget::audioLoadRetry() { - App::wnd()->hideLayer(); + Ui::hideLayer(); AudioData *audio = App::audio(failedObjId); if (audio) audio->save(failedFileName); } @@ -1913,7 +1913,7 @@ void MainWidget::documentLoadFailed(mtpFileLoader *loader, bool started) { } void MainWidget::documentLoadRetry() { - App::wnd()->hideLayer(); + Ui::hideLayer(); DocumentData *document = App::document(failedObjId); if (document) document->save(failedFileName); } @@ -2282,7 +2282,7 @@ void MainWidget::showPeerHistory(quint64 peerId, qint32 showAtMsgId, bool back) PeerData *wasActivePeer = activePeer(); - App::wnd()->hideLayer(); + Ui::hideLayer(); if (_hider) { _hider->startHide(); _hider = 0; @@ -2741,13 +2741,13 @@ void MainWidget::hideAll() { void MainWidget::showAll() { if (cPasswordRecovered()) { cSetPasswordRecovered(false); - App::wnd()->showLayer(new InformBox(lang(lng_signin_password_removed))); + Ui::showLayer(new InformBox(lang(lng_signin_password_removed))); } if (cWideMode()) { if (_hider) { _hider->show(); if (_forwardConfirm) { - App::wnd()->hideLayer(true); + Ui::hideLayer(true); _forwardConfirm = 0; } } @@ -2770,7 +2770,7 @@ void MainWidget::showAll() { _forwardConfirm = new ConfirmBox(_hider->offeredText(), lang(lng_forward_send)); connect(_forwardConfirm, SIGNAL(confirmed()), _hider, SLOT(forward())); connect(_forwardConfirm, SIGNAL(cancelled()), this, SLOT(onForwardCancel())); - App::wnd()->showLayer(_forwardConfirm, true); + Ui::showLayer(_forwardConfirm, ForceFastShowLayer); } } if (selectingPeer()) { @@ -3541,7 +3541,7 @@ void MainWidget::openPeerByName(const QString &username, bool toProfile, const Q if (toProfile) { if (peer->isUser() && peer->asUser()->botInfo && !peer->asUser()->botInfo->cantJoinGroups && !startToken.isEmpty()) { peer->asUser()->botInfo->startGroupToken = startToken; - App::wnd()->showLayer(new ContactsBox(peer->asUser())); + Ui::showLayer(new ContactsBox(peer->asUser())); } else if (peer->isChannel()) { showPeerHistory(peer->id, ShowAtUnreadMsgId); } else { @@ -3571,7 +3571,7 @@ void MainWidget::stickersBox(const MTPInputStickerSet &set) { App::wnd()->hideMediaview(); StickerSetBox *box = new StickerSetBox(set); connect(box, SIGNAL(installed(uint64)), this, SLOT(onStickersInstalled(uint64))); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void MainWidget::onStickersInstalled(uint64 setId) { @@ -3605,7 +3605,7 @@ bool MainWidget::contentOverlapped(const QRect &globalRect) { } void MainWidget::usernameResolveDone(QPair toProfileStartToken, const MTPcontacts_ResolvedPeer &result) { - App::wnd()->hideLayer(); + Ui::hideLayer(); if (result.type() != mtpc_contacts_resolvedPeer) return; const MTPDcontacts_resolvedPeer &d(result.c_contacts_resolvedPeer()); @@ -3618,7 +3618,7 @@ void MainWidget::usernameResolveDone(QPair toProfileStartToken, c if (toProfileStartToken.first) { if (peer->isUser() && peer->asUser()->botInfo && !peer->asUser()->botInfo->cantJoinGroups && !toProfileStartToken.second.isEmpty()) { peer->asUser()->botInfo->startGroupToken = toProfileStartToken.second; - App::wnd()->showLayer(new ContactsBox(peer->asUser())); + Ui::showLayer(new ContactsBox(peer->asUser())); } else if (peer->isChannel()) { showPeerHistory(peer->id, ShowAtUnreadMsgId); } else { @@ -3640,7 +3640,7 @@ bool MainWidget::usernameResolveFail(QString name, const RPCError &error) { if (mtpIsFlood(error)) return false; if (error.code() == 400) { - App::wnd()->showLayer(new InformBox(lng_username_not_found(lt_user, name))); + Ui::showLayer(new InformBox(lng_username_not_found(lt_user, name))); } return true; } @@ -3652,7 +3652,7 @@ void MainWidget::inviteCheckDone(QString hash, const MTPChatInvite &invite) { ConfirmBox *box = new ConfirmBox(((d.is_channel() && !d.is_megagroup()) ? lng_group_invite_want_join_channel : lng_group_invite_want_join)(lt_title, qs(d.vtitle)), lang(lng_group_invite_join)); _inviteHash = hash; connect(box, SIGNAL(confirmed()), this, SLOT(onInviteImport())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } break; case mtpc_chatInviteAlready: { @@ -3669,7 +3669,7 @@ bool MainWidget::inviteCheckFail(const RPCError &error) { if (mtpIsFlood(error)) return false; if (error.code() == 400) { - App::wnd()->showLayer(new InformBox(lang(lng_group_invite_bad_link))); + Ui::showLayer(new InformBox(lang(lng_group_invite_bad_link))); } return true; } @@ -3682,7 +3682,7 @@ void MainWidget::onInviteImport() { void MainWidget::inviteImportDone(const MTPUpdates &updates) { App::main()->sentUpdatesReceived(updates); - App::wnd()->hideLayer(); + Ui::hideLayer(); const QVector *v = 0; switch (updates.type()) { case mtpc_updates: v = &updates.c_updates().vchats.c_vector().v; break; @@ -3702,7 +3702,7 @@ bool MainWidget::inviteImportFail(const RPCError &error) { if (mtpIsFlood(error)) return false; if (error.code() == 400) { - App::wnd()->showLayer(new InformBox(lang(error.type() == qsl("USERS_TOO_MUCH") ? lng_group_invite_no_room : lng_group_invite_bad_link))); + Ui::showLayer(new InformBox(lang(error.type() == qsl("USERS_TOO_MUCH") ? lng_group_invite_no_room : lng_group_invite_bad_link))); } return true; } @@ -3915,7 +3915,7 @@ void MainWidget::activate() { } else { dialogs.activate(); } - } else if (App::wnd() && !App::wnd()->layerShown()) { + } else if (App::wnd() && !Ui::isLayerShown()) { if (!cSendPaths().isEmpty()) { forwardLayer(-1); } else if (history.peer()) { @@ -4519,7 +4519,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { case mtpc_updateServiceNotification: { const MTPDupdateServiceNotification &d(update.c_updateServiceNotification()); if (mtpIsTrue(d.vpopup)) { - App::wnd()->showLayer(new InformBox(qs(d.vmessage))); + Ui::showLayer(new InformBox(qs(d.vmessage))); } else { App::wnd()->serviceNotification(qs(d.vmessage), d.vmedia); } diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index e26a3bd39..2963e2411 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -486,7 +486,7 @@ void MediaView::showSaveMsgFile() { void MediaView::close() { if (_menu) _menu->hideMenu(true); if (App::wnd()) { - App::wnd()->hideLayer(true); + Ui::hideLayer(true); } } diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 78c6af5eb..b1c6ea31a 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -2875,7 +2875,7 @@ void OverviewWidget::updateTopBarSelection() { App::main()->topBar()->showSelected(_selCount > 0 ? _selCount : 0, (selectedForDelete == selectedForForward)); App::main()->topBar()->update(); } - if (App::wnd() && !App::wnd()->layerShown()) { + if (App::wnd() && !Ui::isLayerShown()) { _inner.activate(); } update(); @@ -3127,7 +3127,7 @@ void OverviewWidget::onDeleteSelectedSure() { if (App::main() && App::main()->peer() == peer()) { App::main()->itemResized(0); } - App::wnd()->hideLayer(); + Ui::hideLayer(); for (QMap >::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) { App::main()->deleteMessages(i.key(), i.value()); @@ -3151,7 +3151,7 @@ void OverviewWidget::onDeleteContextSure() { if (App::main() && (App::main()->peer() == h->peer || (App::main()->peer() && App::main()->peer() == h->peer->migrateTo()))) { App::main()->itemResized(0); } - App::wnd()->hideLayer(); + Ui::hideLayer(); if (wasOnServer) { App::main()->deleteMessages(h->peer, toDelete); diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index 79022abac..3beede857 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -232,7 +232,7 @@ void ProfileInner::onShareContact() { } void ProfileInner::onInviteToGroup() { - App::wnd()->showLayer(new ContactsBox(_peerUser)); + Ui::showLayer(new ContactsBox(_peerUser)); } void ProfileInner::onSendMessage() { @@ -302,36 +302,35 @@ void ProfileInner::onUpdatePhoto() { } PhotoCropBox *box = new PhotoCropBox(img, _peer); connect(box, SIGNAL(closed()), this, SLOT(onPhotoUpdateStart())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void ProfileInner::onClearHistory() { if (_peerChannel) return; ConfirmBox *box = new ConfirmBox(_peer->isUser() ? lng_sure_delete_history(lt_contact, _peer->name) : lng_sure_delete_group_history(lt_group, _peer->name), lang(lng_box_delete), st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onClearHistorySure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void ProfileInner::onClearHistorySure() { - App::wnd()->hideLayer(); + Ui::hideLayer(); App::main()->clearHistory(_peer); } void ProfileInner::onDeleteConversation() { ConfirmBox *box = new ConfirmBox(_peer->isUser() ? lng_sure_delete_history(lt_contact, _peer->name) : (_peer->isChat() ? lng_sure_delete_and_exit(lt_group, _peer->name) : lang(_peer->isMegagroup() ? lng_sure_leave_group : lng_sure_leave_channel)), lang(_peer->isUser() ? lng_box_delete : lng_box_leave), _peer->isChannel() ? st::defaultBoxButton : st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteConversationSure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void ProfileInner::onDeleteConversationSure() { + Ui::hideLayer(); if (_peerUser) { App::main()->deleteConversation(_peer); } else if (_peerChat) { - App::wnd()->hideLayer(); App::main()->showDialogs(); MTP::send(MTPmessages_DeleteChatUser(_peerChat->inputChat, App::self()->inputUser), App::main()->rpcDone(&MainWidget::deleteHistoryAfterLeave, _peer), App::main()->rpcFail(&MainWidget::leaveChatFailed, _peer)); } else if (_peerChannel) { - App::wnd()->hideLayer(); App::main()->showDialogs(); if (_peerChannel->migrateFrom()) { App::main()->deleteConversation(_peerChannel->migrateFrom()); @@ -344,12 +343,12 @@ void ProfileInner::onDeleteChannel() { if (!_peerChannel) return; ConfirmBox *box = new ConfirmBox(lang(_peer->isMegagroup() ? lng_sure_delete_group : lng_sure_delete_channel), lang(lng_box_delete), st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteChannelSure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void ProfileInner::onDeleteChannelSure() { if (_peerChannel) { - App::wnd()->hideLayer(); + Ui::hideLayer(); App::main()->showDialogs(); if (_peerChannel->migrateFrom()) { App::main()->deleteConversation(_peerChannel->migrateFrom()); @@ -382,13 +381,13 @@ bool ProfileInner::blockFail(const RPCError &error) { void ProfileInner::onAddParticipant() { if (_peerChat) { - App::wnd()->showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); + Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); } else if (_peerChannel && _peerChannel->mgInfo) { MembersAlreadyIn already; for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) { already.insert(*i, true); } - App::wnd()->showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already)); + Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already)); } } @@ -397,7 +396,7 @@ void ProfileInner::onMigrate() { ConfirmBox *box = new ConfirmBox(lang(lng_profile_migrate_sure)); connect(box, SIGNAL(confirmed()), this, SLOT(onMigrateSure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void ProfileInner::onMigrateSure() { @@ -459,7 +458,7 @@ void ProfileInner::onInvitationLink() { if (!_peerChat && !_peerChannel) return; QApplication::clipboard()->setText(_peerChat ? _peerChat->invitationUrl : (_peerChannel ? _peerChannel->invitationUrl : QString())); - App::wnd()->showLayer(new InformBox(lang(lng_group_invite_copied))); + Ui::showLayer(new InformBox(lang(lng_group_invite_copied))); } void ProfileInner::onPublicLink() { @@ -467,22 +466,22 @@ void ProfileInner::onPublicLink() { if (_peerChannel->isPublic()) { QApplication::clipboard()->setText(qsl("https://telegram.me/") + _peerChannel->username); - App::wnd()->showLayer(new InformBox(lang(lng_channel_public_link_copied))); + Ui::showLayer(new InformBox(lang(lng_channel_public_link_copied))); } else { - App::wnd()->showLayer(new SetupChannelBox(_peerChannel, true)); + Ui::showLayer(new SetupChannelBox(_peerChannel, true)); } } void ProfileInner::onMembers() { if (!_peerChannel) return; - App::wnd()->showLayer(new MembersBox(_peerChannel, MembersFilterRecent)); + Ui::showLayer(new MembersBox(_peerChannel, MembersFilterRecent)); } void ProfileInner::onAdmins() { if (_peerChannel) { - App::wnd()->showLayer(new MembersBox(_peerChannel, MembersFilterAdmins)); + Ui::showLayer(new MembersBox(_peerChannel, MembersFilterAdmins)); } else if (_peerChat) { - App::wnd()->showLayer(new ContactsBox(_peerChat, MembersFilterAdmins)); + Ui::showLayer(new ContactsBox(_peerChat, MembersFilterAdmins)); } } @@ -491,7 +490,7 @@ void ProfileInner::onCreateInvitationLink() { ConfirmBox *box = new ConfirmBox(lang(((_peerChat && _peerChat->invitationUrl.isEmpty()) || (_peerChannel && _peerChannel->invitationUrl.isEmpty())) ? lng_group_invite_about : lng_group_invite_about_new)); connect(box, SIGNAL(confirmed()), this, SLOT(onCreateInvitationLinkSure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void ProfileInner::onCreateInvitationLinkSure() { @@ -514,7 +513,7 @@ void ProfileInner::chatInviteDone(const MTPExportedChatInvite &result) { updateInvitationLink(); showAll(); resizeEvent(0); - App::wnd()->hideLayer(); + Ui::hideLayer(); } void ProfileInner::onFullPeerUpdated(PeerData *peer) { @@ -1075,7 +1074,7 @@ void ProfileInner::mouseReleaseEvent(QMouseEvent *e) { _kickConfirm = _kickOver; ConfirmBox *box = new ConfirmBox(lng_profile_sure_kick(lt_user, _kickOver->firstName), lang(lng_box_remove)); connect(box, SIGNAL(confirmed()), this, SLOT(onKickConfirm())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } if (textlnkDown()) { TextLinkPtr lnk = textlnkDown(); @@ -1104,7 +1103,7 @@ void ProfileInner::onKickConfirm() { if (_peerChat) { App::main()->kickParticipant(_peerChat, _kickConfirm); } else if (_peerChannel) { - App::wnd()->hideLayer(); + Ui::hideLayer(); App::api()->kickParticipant(_peerChannel, _kickConfirm); } } @@ -1236,7 +1235,7 @@ bool ProfileInner::updateMediaLinks(int32 *addToScroll) { } void ProfileInner::migrateDone(const MTPUpdates &updates) { - App::wnd()->hideLayer(); + Ui::hideLayer(); App::main()->sentUpdatesReceived(updates); const QVector *v = 0; switch (updates.type()) { @@ -1262,7 +1261,7 @@ void ProfileInner::migrateDone(const MTPUpdates &updates) { bool ProfileInner::migrateFail(const RPCError &error) { if (mtpIsFlood(error)) return false; - App::wnd()->hideLayer(); + Ui::hideLayer(); return true; } diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp index 3cd1c4945..dd678769f 100644 --- a/Telegram/SourceFiles/settingswidget.cpp +++ b/Telegram/SourceFiles/settingswidget.cpp @@ -783,14 +783,14 @@ void SettingsInner::keyPressEvent(QKeyEvent *e) { QString text = cDebug() ? qsl("Do you want to disable DEBUG logs?") : qsl("Do you want to enable DEBUG logs?\n\nAll network events will be logged."); ConfirmBox *box = new ConfirmBox(text); connect(box, SIGNAL(confirmed()), App::app(), SLOT(onSwitchDebugMode())); - App::wnd()->showLayer(box); + Ui::showLayer(box); from = size; break; } else if (str == qstr("testmode")) { QString text = cTestMode() ? qsl("Do you want to disable TEST mode?") : qsl("Do you want to enable TEST mode?\n\nYou will be switched to test cloud."); ConfirmBox *box = new ConfirmBox(text); connect(box, SIGNAL(confirmed()), App::app(), SLOT(onSwitchTestMode())); - App::wnd()->showLayer(box); + Ui::showLayer(box); from = size; break; } else if (str == qstr("loadlang")) { @@ -831,7 +831,7 @@ void SettingsInner::mousePressEvent(QMouseEvent *e) { return; } if (QRect(_uploadPhoto.x() + st::setNameLeft, st::setTop + st::setNameTop, qMin(_uploadPhoto.width() - int(st::setNameLeft), _nameText.maxWidth()), st::setNameFont->height).contains(e->pos())) { - App::wnd()->showLayer(new EditNameTitleBox(self())); + Ui::showLayer(new EditNameTitleBox(self())); } else if (QRect(_left, st::setTop, st::setPhotoSize, st::setPhotoSize).contains(e->pos())) { if (_photoLink) { App::photo(self()->photoId)->full->load(); @@ -1178,12 +1178,12 @@ void SettingsInner::onUpdatePhoto() { } PhotoCropBox *box = new PhotoCropBox(img, self()); connect(box, SIGNAL(closed()), this, SLOT(onPhotoUpdateStart())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onShowSessions() { SessionsBox *box = new SessionsBox(); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onAskQuestion() { @@ -1192,7 +1192,7 @@ void SettingsInner::onAskQuestion() { ConfirmBox *box = new ConfirmBox(lang(lng_settings_ask_sure), lang(lng_settings_ask_ok), st::defaultBoxButton, lang(lng_settings_faq_button)); connect(box, SIGNAL(confirmed()), this, SLOT(onAskQuestionSure())); connect(box, SIGNAL(cancelPressed()), this, SLOT(onTelegramFAQ())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onAskQuestionSure() { @@ -1217,9 +1217,9 @@ void SettingsInner::chooseCustomLang() { cancel = result.value(lng_cancel, langOriginal(lng_cancel)); ConfirmBox *box = new ConfirmBox(text, save, st::defaultBoxButton, cancel); connect(box, SIGNAL(confirmed()), this, SLOT(onSaveTestLang())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } else { - App::wnd()->showLayer(new InformBox("Custom lang failed :(\n\nError: " + loader.errors())); + Ui::showLayer(new InformBox("Custom lang failed :(\n\nError: " + loader.errors())); } } } @@ -1228,7 +1228,7 @@ void SettingsInner::onChangeLanguage() { if ((_changeLanguage.clickModifiers() & Qt::ShiftModifier) && (_changeLanguage.clickModifiers() & Qt::AltModifier)) { chooseCustomLang(); } else { - App::wnd()->showLayer(new LanguageBox()); + Ui::showLayer(new LanguageBox()); } } @@ -1293,19 +1293,19 @@ void SettingsInner::onRestartNow() { void SettingsInner::onPasscode() { PasscodeBox *box = new PasscodeBox(); connect(box, SIGNAL(closed()), this, SLOT(passcodeChanged())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onPasscodeOff() { PasscodeBox *box = new PasscodeBox(true); connect(box, SIGNAL(closed()), this, SLOT(passcodeChanged())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onPassword() { PasscodeBox *box = new PasscodeBox(_newPasswordSalt, _curPasswordSalt, _hasPasswordRecovery, _curPasswordHint); connect(box, SIGNAL(reloadPassword()), this, SLOT(onReloadPassword())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onPasswordOff() { @@ -1319,7 +1319,7 @@ void SettingsInner::onPasswordOff() { } else { PasscodeBox *box = new PasscodeBox(_newPasswordSalt, _curPasswordSalt, _hasPasswordRecovery, _curPasswordHint, true); connect(box, SIGNAL(reloadPassword()), this, SLOT(onReloadPassword())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } } @@ -1332,19 +1332,19 @@ void SettingsInner::onReloadPassword(Qt::ApplicationState state) { void SettingsInner::onAutoLock() { AutoLockBox *box = new AutoLockBox(); connect(box, SIGNAL(closed()), this, SLOT(passcodeChanged())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onConnectionType() { ConnectionBox *box = new ConnectionBox(); connect(box, SIGNAL(closed()), this, SLOT(updateConnectionType()), Qt::QueuedConnection); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onUsername() { UsernameBox *box = new UsernameBox(); connect(box, SIGNAL(closed()), this, SLOT(usernameChanged())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onWorkmodeTray() { @@ -1445,7 +1445,7 @@ void SettingsInner::setScale(DBIScale newScale) { if (cEvalScale(cConfigScale()) != cEvalScale(cRealScale())) { ConfirmBox *box = new ConfirmBox(lang(lng_settings_need_restart), lang(lng_settings_restart_now), st::defaultBoxButton, lang(lng_settings_restart_later)); connect(box, SIGNAL(confirmed()), this, SLOT(onRestartNow())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } } @@ -1528,11 +1528,11 @@ void SettingsInner::onReplaceEmojis() { } void SettingsInner::onViewEmojis() { - App::showLayer(new EmojiBox()); + Ui::showLayer(new EmojiBox()); } void SettingsInner::onStickers() { - App::showLayer(new StickersBox()); + Ui::showLayer(new StickersBox()); } void SettingsInner::onEnterSend() { @@ -1553,7 +1553,7 @@ void SettingsInner::onCtrlEnterSend() { void SettingsInner::onBackFromGallery() { BackgroundBox *box = new BackgroundBox(); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onBackFromFile() { @@ -1631,7 +1631,7 @@ void SettingsInner::onDontAskDownloadPath() { void SettingsInner::onDownloadPathEdit() { DownloadPathBox *box = new DownloadPathBox(); connect(box, SIGNAL(closed()), this, SLOT(onDownloadPathEdited())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onDownloadPathEdited() { @@ -1650,11 +1650,11 @@ void SettingsInner::onDownloadPathEdited() { void SettingsInner::onDownloadPathClear() { ConfirmBox *box = new ConfirmBox(lang(lng_sure_clear_downloads)); connect(box, SIGNAL(confirmed()), this, SLOT(onDownloadPathClearSure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void SettingsInner::onDownloadPathClearSure() { - App::wnd()->hideLayer(); + Ui::hideLayer(); App::wnd()->tempDirDelete(Local::ClearManagerDownloads); _tempDirClearState = TempDirClearing; showAll(); diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index b7c3bfed0..93f24d368 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -1042,7 +1042,7 @@ void PeerLink::onClick(Qt::MouseButton button) const { if (button == Qt::LeftButton && App::main()) { if (peer() && peer()->isChannel() && App::main()->historyPeer() != peer()) { if (!peer()->asChannel()->isPublic() && !peer()->asChannel()->amIn()) { - App::wnd()->showLayer(new InformBox(lang((peer()->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); + Ui::showLayer(new InformBox(lang((peer()->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); } else { App::main()->showPeerHistory(peer()->id, ShowAtUnreadMsgId); } diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp index 364d23341..36c72feb6 100644 --- a/Telegram/SourceFiles/title.cpp +++ b/Telegram/SourceFiles/title.cpp @@ -143,12 +143,12 @@ void TitleWidget::onContacts() { if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray(); if (!App::self()) return; - App::wnd()->showLayer(new ContactsBox()); + Ui::showLayer(new ContactsBox()); } void TitleWidget::onAbout() { if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray(); - App::wnd()->showLayer(new AboutBox()); + Ui::showLayer(new AboutBox()); } TitleWidget::~TitleWidget() { @@ -365,7 +365,7 @@ void TitleWidget::maximizedChanged(bool maximized, bool force) { } HitTestType TitleWidget::hitTest(const QPoint &p) { - if (App::wnd() && App::wnd()->layerShown()) return HitTestNone; + if (App::wnd() && Ui::isLayerShown()) return HitTestNone; int x(p.x()), y(p.y()), w(width()), h(height()); if (cWideMode() && hider && x >= App::main()->dlgsWidth()) return HitTestNone; diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index dae1346f9..3a998622f 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -443,3 +443,13 @@ enum ForwardWhatMessages { ForwardPressedMessage, ForwardPressedLinkMessage }; + +enum ShowLayerOption { + CloseOtherLayers = 0x00, + KeepOtherLayers = 0x01, + ShowAfterOtherLayers = 0x03, + + AnimatedShowLayer = 0x00, + ForceFastShowLayer = 0x04, +}; +typedef QFlags ShowLayerOptions; diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index ada34c9e4..aa47b1f77 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -474,7 +474,7 @@ QWidget *Window::filedialogParent() { } void Window::clearWidgets() { - hideLayer(true); + Ui::hideLayer(true); if (_passcode) { _passcode->hide(); _passcode->deleteLater(); @@ -679,7 +679,7 @@ void Window::showSettings() { if (isHidden()) showFromTray(); - App::wnd()->hideLayer(); + Ui::hideLayer(); if (settings) { return hideSettings(); } @@ -774,49 +774,65 @@ void Window::showPhoto(const PhotoLink *lnk, HistoryItem *item) { } void Window::showPhoto(PhotoData *photo, HistoryItem *item) { - hideLayer(true); + Ui::hideLayer(true); _mediaView->showPhoto(photo, item); _mediaView->activateWindow(); _mediaView->setFocus(); } void Window::showPhoto(PhotoData *photo, PeerData *peer) { - hideLayer(true); + Ui::hideLayer(true); _mediaView->showPhoto(photo, peer); _mediaView->activateWindow(); _mediaView->setFocus(); } void Window::showDocument(DocumentData *doc, HistoryItem *item) { - hideLayer(true); + Ui::hideLayer(true); _mediaView->showDocument(doc, item); _mediaView->activateWindow(); _mediaView->setFocus(); } -void Window::showLayer(LayeredWidget *w, bool forceFast) { - bool fast = forceFast || layerShown(); - hideLayer(true); - layerBg = new BackgroundWidget(this, w); - if (fast) { - layerBg->showFast(); +void Window::ui_showLayer(LayeredWidget *box, ShowLayerOptions options) { + if (box) { + bool fast = (options.testFlag(ForceFastShowLayer)) || Ui::isLayerShown(); + if (layerBg) { + if (options.testFlag(KeepOtherLayers)) { + if (options.testFlag(ShowAfterOtherLayers)) { + layerBg->showLayerLast(box); + return; + } else { + layerBg->replaceInner(box); + return; + } + } else { + layerBg->onClose(); + layerBg->hide(); + layerBg->deleteLater(); + layerBg = 0; + } + } + + layerBg = new BackgroundWidget(this, box); + if (fast) { + layerBg->showFast(); + } + } else { + if (layerBg) { + layerBg->onClose(); + if (options.testFlag(ForceFastShowLayer)) { + layerBg->hide(); + layerBg->deleteLater(); + layerBg = 0; + } + } + hideMediaview(); } } -void Window::replaceLayer(LayeredWidget *w) { - if (layerBg) { - layerBg->replaceInner(w); - } else { - layerBg = new BackgroundWidget(this, w); - } -} - -void Window::showLayerLast(LayeredWidget *w) { - if (layerBg) { - layerBg->showLayerLast(w); - } else { - layerBg = new BackgroundWidget(this, w); - } +bool Window::ui_isLayerShown() { + return !!layerBg; } void Window::showConnecting(const QString &text, const QString &reconnect) { @@ -843,29 +859,6 @@ void Window::hideConnecting() { if (settings) settings->update(); } -void Window::hideLayer(bool fast) { - if (layerBg) { - layerBg->onClose(); - if (fast) { - layerBg->hide(); - layerBg->deleteLater(); - layerBg = 0; - } - } - hideMediaview(); -} - -bool Window::hideInnerLayer() { - if (layerBg) { - return layerBg->onInnerClose(); - } - return true; -} - -bool Window::layerShown() { - return !!layerBg; -} - bool Window::historyIsActive() const { return isActive(false) && main && main->historyIsActive() && (!settings || !settings->isVisible()); } @@ -1090,13 +1083,13 @@ void Window::onShowAddContact() { void Window::onShowNewGroup() { if (isHidden()) showFromTray(); - if (main) replaceLayer(new GroupInfoBox(CreatingGroupGroup, false)); + if (main) Ui::showLayer(new GroupInfoBox(CreatingGroupGroup, false), KeepOtherLayers); } void Window::onShowNewChannel() { if (isHidden()) showFromTray(); - if (main) replaceLayer(new GroupInfoBox(CreatingGroupChannel, false)); + if (main) Ui::showLayer(new GroupInfoBox(CreatingGroupChannel, false), KeepOtherLayers); } void Window::onLogout() { @@ -1104,7 +1097,7 @@ void Window::onLogout() { ConfirmBox *box = new ConfirmBox(lang(lng_sure_logout), lang(lng_settings_logout), st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onLogoutSure())); - App::wnd()->showLayer(box); + Ui::showLayer(box); } void Window::onLogoutSure() { @@ -1198,7 +1191,7 @@ void Window::toggleTray(QSystemTrayIcon::ActivationReason reason) { void Window::toggleDisplayNotifyFromTray() { if (App::passcoded()) { if (!isActive()) showFromTray(); - showLayer(new InformBox(lang(lng_passcode_need_unblock))); + Ui::showLayer(new InformBox(lang(lng_passcode_need_unblock))); return; } cSetDesktopNotify(!cDesktopNotify()); @@ -1781,9 +1774,7 @@ void Window::sendPaths() { if (settings) { hideSettings(); } else { - if (layerShown()) { - hideLayer(); - } + Ui::hideLayer(); if (main) { main->activate(); } diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index ba8b81825..2fed69a3b 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -182,14 +182,8 @@ public: void showPhoto(PhotoData *photo, PeerData *item); void showDocument(DocumentData *doc, HistoryItem *item); - void showLayer(LayeredWidget *w, bool forceFast = false); - void replaceLayer(LayeredWidget *w); - void showLayerLast(LayeredWidget *w); - - void hideLayer(bool fast = false); - bool hideInnerLayer(); - - bool layerShown(); + void ui_showLayer(LayeredWidget *box, ShowLayerOptions options); + bool ui_isLayerShown(); bool historyIsActive() const; diff --git a/Telegram/Telegram.pro b/Telegram/Telegram.pro index 8c851b7b2..4ff133b83 100644 --- a/Telegram/Telegram.pro +++ b/Telegram/Telegram.pro @@ -89,6 +89,7 @@ SOURCES += \ ./SourceFiles/autoupdater.cpp \ ./SourceFiles/dialogswidget.cpp \ ./SourceFiles/dropdown.cpp \ + ./SourceFiles/facades.cpp \ ./SourceFiles/fileuploader.cpp \ ./SourceFiles/history.cpp \ ./SourceFiles/historywidget.cpp \ @@ -112,6 +113,7 @@ SOURCES += \ ./SourceFiles/types.cpp \ ./SourceFiles/window.cpp \ ./SourceFiles/mtproto/mtp.cpp \ + ./SourceFiles/mtproto/mtpAuthKey.cpp \ ./SourceFiles/mtproto/mtpConnection.cpp \ ./SourceFiles/mtproto/mtpCoreTypes.cpp \ ./SourceFiles/mtproto/mtpDC.cpp \ @@ -175,6 +177,7 @@ HEADERS += \ ./SourceFiles/countries.h \ ./SourceFiles/dialogswidget.h \ ./SourceFiles/dropdown.h \ + ./SourceFiles/facades.h \ ./SourceFiles/fileuploader.h \ ./SourceFiles/history.h \ ./SourceFiles/historywidget.h \ From 0b96dd5362eb2eb2e7af7b84a76edf86c3c10ae6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 8 Dec 2015 15:33:37 +0300 Subject: [PATCH 004/145] animations refactored --- Telegram/Resources/style.txt | 1 - Telegram/SourceFiles/boxes/abstractbox.cpp | 13 +- Telegram/SourceFiles/boxes/abstractbox.h | 4 +- Telegram/SourceFiles/boxes/addcontactbox.cpp | 54 ++- Telegram/SourceFiles/boxes/addcontactbox.h | 6 +- Telegram/SourceFiles/boxes/confirmbox.cpp | 22 +- Telegram/SourceFiles/boxes/confirmbox.h | 7 +- Telegram/SourceFiles/boxes/stickersetbox.cpp | 53 +-- Telegram/SourceFiles/boxes/stickersetbox.h | 4 +- Telegram/SourceFiles/dialogswidget.cpp | 9 +- Telegram/SourceFiles/dialogswidget.h | 2 +- Telegram/SourceFiles/dropdown.cpp | 409 ++++++++++-------- Telegram/SourceFiles/dropdown.h | 51 ++- Telegram/SourceFiles/gui/animation.cpp | 57 +-- Telegram/SourceFiles/gui/animation.h | 278 ++++++------ Telegram/SourceFiles/gui/flatbutton.cpp | 68 +-- Telegram/SourceFiles/gui/flatbutton.h | 14 +- Telegram/SourceFiles/gui/flatcheckbox.cpp | 94 ++-- Telegram/SourceFiles/gui/flatcheckbox.h | 14 +- Telegram/SourceFiles/gui/flatinput.cpp | 276 ++++++------ Telegram/SourceFiles/gui/flatinput.h | 23 +- Telegram/SourceFiles/gui/flattextarea.cpp | 47 +- Telegram/SourceFiles/gui/flattextarea.h | 6 +- Telegram/SourceFiles/gui/popupmenu.cpp | 12 +- Telegram/SourceFiles/gui/popupmenu.h | 2 +- Telegram/SourceFiles/gui/scrollarea.cpp | 42 +- Telegram/SourceFiles/gui/scrollarea.h | 6 +- Telegram/SourceFiles/gui/switcher.cpp | 160 ------- Telegram/SourceFiles/gui/switcher.h | 65 --- Telegram/SourceFiles/history.cpp | 19 +- Telegram/SourceFiles/history.h | 14 +- Telegram/SourceFiles/historywidget.cpp | 110 ++--- Telegram/SourceFiles/historywidget.h | 21 +- Telegram/SourceFiles/intro/intro.cpp | 65 ++- Telegram/SourceFiles/intro/intro.h | 7 +- Telegram/SourceFiles/intro/introcode.cpp | 43 +- Telegram/SourceFiles/intro/introcode.h | 7 +- Telegram/SourceFiles/intro/introphone.cpp | 45 +- Telegram/SourceFiles/intro/introphone.h | 7 +- Telegram/SourceFiles/intro/intropwdcheck.cpp | 51 ++- Telegram/SourceFiles/intro/intropwdcheck.h | 7 +- Telegram/SourceFiles/intro/introsignup.cpp | 67 +-- Telegram/SourceFiles/intro/introsignup.h | 9 +- Telegram/SourceFiles/layerwidget.cpp | 48 +- Telegram/SourceFiles/layerwidget.h | 13 +- Telegram/SourceFiles/mainwidget.cpp | 28 +- Telegram/SourceFiles/mainwidget.h | 7 +- Telegram/SourceFiles/mediaview.cpp | 26 +- Telegram/SourceFiles/mediaview.h | 6 +- Telegram/SourceFiles/overviewwidget.cpp | 13 +- Telegram/SourceFiles/overviewwidget.h | 2 +- Telegram/SourceFiles/passcodewidget.cpp | 11 +- Telegram/SourceFiles/passcodewidget.h | 4 +- Telegram/SourceFiles/playerwidget.cpp | 34 +- Telegram/SourceFiles/playerwidget.h | 8 +- Telegram/SourceFiles/profilewidget.cpp | 38 +- Telegram/SourceFiles/profilewidget.h | 9 +- Telegram/SourceFiles/settingswidget.cpp | 190 ++++---- Telegram/SourceFiles/settingswidget.h | 11 +- Telegram/SourceFiles/stdafx.h | 1 - Telegram/SourceFiles/sysbuttons.cpp | 20 +- Telegram/SourceFiles/sysbuttons.h | 6 +- Telegram/SourceFiles/title.cpp | 41 +- Telegram/SourceFiles/title.h | 6 +- Telegram/SourceFiles/window.cpp | 75 ++-- Telegram/SourceFiles/window.h | 12 +- Telegram/Telegram.pro | 2 - Telegram/Telegram.vcxproj | 27 -- Telegram/Telegram.vcxproj.filters | 15 - Telegram/Telegram.xcodeproj/project.pbxproj | 10 - Telegram/Telegram.xcodeproj/qt_preprocess.mak | 20 +- 71 files changed, 1370 insertions(+), 1584 deletions(-) delete mode 100644 Telegram/SourceFiles/gui/switcher.cpp delete mode 100644 Telegram/SourceFiles/gui/switcher.h diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 81fc1f8e1..64ac38cae 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1480,7 +1480,6 @@ notifySlowHideFunc: transition(easeInCirc); notifyWaitShortHide: 0; notifyWaitLongHide: 20000; notifyFastAnim: 150; -notifyFastAnimFunc: transition(linear); notifyWidth: 316px; notifyHeight: 80px; notifyDeltaX: 6px; diff --git a/Telegram/SourceFiles/boxes/abstractbox.cpp b/Telegram/SourceFiles/boxes/abstractbox.cpp index 10c8d4393..e61b40fce 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 8f3d47f97..4920cebd1 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 732997d75..ccd5f0eec 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -342,7 +342,7 @@ void NewGroupBox::onNext() { 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() { @@ -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,17 +772,15 @@ 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() { diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index 00c600c30..b50c715de 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 8fad31da6..e6dd0269a 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -187,11 +187,13 @@ void ConfirmLinkBox::onOpenLink() { 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 f2a23bf40..ba439eb95 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/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index f4954e258..8e305cc9a 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -333,7 +333,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 +355,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 +489,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 { @@ -538,33 +538,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 +556,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 +566,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() { diff --git a/Telegram/SourceFiles/boxes/stickersetbox.h b/Telegram/SourceFiles/boxes/stickersetbox.h index b896c8af4..901dd586b 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.h +++ b/Telegram/SourceFiles/boxes/stickersetbox.h @@ -144,13 +144,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 { diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 15531628e..098981019 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1737,7 +1737,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 +1839,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 +1863,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() { diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 602774d23..b35f51de9 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -242,7 +242,7 @@ public: void dialogsToUp(); void animShow(const QPixmap &bgAnimCache); - bool animStep_show(float64 ms); + void step_show(float64 ms, bool timer); void destroyData(); diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 2ff383332..44fe3a6ab 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -32,8 +32,15 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #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 +125,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 +158,7 @@ void Dropdown::enterEvent(QEvent *e) { } void Dropdown::leaveEvent(QEvent *e) { - if (animating()) { + if (_a_appearance.animating()) { hideStart(); } else { _hideTimer.start(300); @@ -231,7 +238,7 @@ void Dropdown::otherEnter() { } void Dropdown::otherLeave() { - if (animating()) { + if (_a_appearance.animating()) { hideStart(); } else { _hideTimer.start(0); @@ -239,8 +246,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 +263,7 @@ void Dropdown::adjustButtons() { void Dropdown::hideStart() { _hiding = true; a_opacity.start(0); - anim::start(this); + _a_appearance.start(); } void Dropdown::hideFinish() { @@ -276,24 +283,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 +316,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 +335,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 +346,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 +361,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 +392,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 +411,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 +423,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 +437,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 +562,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 +620,7 @@ void EmojiColorPicker::hideStart(bool fast) { } _hiding = true; a_opacity.start(0); - anim::start(this); + _a_appearance.start(); } } @@ -613,8 +629,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 +642,7 @@ void EmojiColorPicker::showStart() { } show(); a_opacity.start(1); - anim::start(this); + _a_appearance.start(); } void EmojiColorPicker::clearSelection(bool fast) { @@ -676,7 +692,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 +710,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); @@ -1030,7 +1051,7 @@ void EmojiPanInner::clearSelection(bool fast) { _hovers[tab][sel] = 0; } _selected = _pressedSel = -1; - anim::stop(this); + _a_selected.stop(); } else { updateSelected(); } @@ -1155,15 +1176,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 +1193,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,6 +1217,7 @@ void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { } StickerPanInner::StickerPanInner() : TWidget() +, _a_selected(animation(this, &StickerPanInner::step_selected)) , _top(0) , _selected(-1) , _pressedSel(-1) @@ -1459,7 +1480,7 @@ void StickerPanInner::clearSelection(bool fast) { _sets[tab].hovers[sel] = 0; } _selected = _pressedSel = -1; - anim::stop(this); + _a_selected.stop(); } else { updateSelected(); } @@ -1733,7 +1754,7 @@ 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() { @@ -1750,12 +1771,11 @@ void StickerPanInner::onPreview() { } } -bool StickerPanInner::animStep(float64 ms) { - uint64 now = getms(); +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,8 +1785,8 @@ 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) { @@ -1869,22 +1889,43 @@ 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) +, _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) +, _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) { setFocusPolicy(Qt::NoFocus); e_scroll.setFocusPolicy(Qt::NoFocus); e_scroll.viewport()->setFocusPolicy(Qt::NoFocus); @@ -2121,7 +2162,7 @@ void EmojiPan::enterEvent(QEvent *e) { void EmojiPan::leaveEvent(QEvent *e) { if (_removingSetId) return; - if (animating()) { + if (_a_appearance.animating()) { hideStart(); } else { _hideTimer.start(300); @@ -2134,7 +2175,7 @@ void EmojiPan::otherEnter() { } void EmojiPan::otherLeave() { - if (animating()) { + if (_a_appearance.animating()) { hideStart(); } else { _hideTimer.start(0); @@ -2170,7 +2211,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 +2229,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; @@ -2220,7 +2261,7 @@ bool EmojiPan::event(QEvent *e) { if (newX != _iconsX.current()) { _iconsX = anim::ivalue(newX, newX); _iconsStartAnim = 0; - if (_iconAnimations.isEmpty()) _iconAnim.stop(); + if (_iconAnimations.isEmpty()) _a_icons.stop(); updateSelected(); updateIcons(); } @@ -2229,8 +2270,8 @@ bool EmojiPan::event(QEvent *e) { } void EmojiPan::fastHide() { - if (animating()) { - anim::stop(this); + if (_a_appearance.animating()) { + _a_appearance.stop(); } a_opacity = anim::fvalue(0, 0); _hideTimer.stop(); @@ -2254,7 +2295,7 @@ void EmojiPan::onRefreshIcons() { _iconsX = anim::ivalue(0, 0); _iconSelX.finish(); _iconsStartAnim = 0; - _iconAnim.stop(); + _a_icons.stop(); if (_icons.isEmpty()) { _iconsMax = 0; } else { @@ -2330,7 +2371,7 @@ void EmojiPan::updateSelected() { _iconAnimations.insert(_iconOver + 1, getms()); } } - if (startanim) _iconAnim.start(); + if (startanim && !_a_icons.animating()) _a_icons.start(); } } @@ -2339,13 +2380,15 @@ void EmojiPan::updateIcons() { 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 +2401,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,55 +2410,56 @@ 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() { @@ -2429,14 +2473,14 @@ void EmojiPan::hideStart() { hideAll(); _hiding = true; a_opacity.start(0); - anim::start(this); + _a_appearance.start(); } void EmojiPan::hideFinish() { hide(); e_inner.hideFinish(); _cache = _toCache = _fromCache = QPixmap(); - _moveStart = 0; + _a_slide.stop(); _horizontal = false; e_scroll.scrollToY(0); @@ -2451,7 +2495,7 @@ 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(); } @@ -2466,7 +2510,7 @@ void EmojiPan::showStart() { s_inner.preloadImages(); _stickersShown = false; _fromCache = _toCache = QPixmap(); - _moveStart = 0; + _a_slide.stop(); } if (_cache.isNull()) { QPixmap from = _fromCache, to = _toCache; @@ -2479,7 +2523,7 @@ void EmojiPan::showStart() { _hiding = false; show(); a_opacity.start(1); - anim::start(this); + _a_appearance.start(); emit updateStickers(); } @@ -2626,7 +2670,7 @@ void EmojiPan::onScroll() { _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(); + _a_icons.start(); updateSelected(); updateIcons(); } @@ -2642,7 +2686,7 @@ void EmojiPan::onSwitch() { _iconOver = -1; _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); _iconAnimations.clear(); - _iconAnim.stop(); + _a_icons.stop(); _cache = QPixmap(); showAll(); @@ -2650,7 +2694,6 @@ void EmojiPan::onSwitch() { _cache = cache; hideAll(); - _moveStart = getms(); if (_stickersShown) { e_inner.hideFinish(); @@ -2661,7 +2704,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(); } @@ -2966,8 +3009,16 @@ 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, &_rows, &_hrows, &_crows) +, _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))); @@ -2997,7 +3048,7 @@ _scroll(this, st::mentionScroll), _inner(this, &_rows, &_hrows, &_crows), _chat( void MentionsDropdown::paintEvent(QPaintEvent *e) { QPainter p(this); - if (animating()) { + if (_a_appearance.animating()) { p.setOpacity(a_opacity.current()); p.drawPixmap(0, 0, _cache); return; @@ -3202,8 +3253,8 @@ void MentionsDropdown::recount(bool toDown) { } 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 +3271,7 @@ void MentionsDropdown::hideStart() { _hiding = true; a_opacity.start(0); setAttribute(Qt::WA_OpaquePaintEvent, false); - anim::start(this); + _a_appearance.start(); } } @@ -3244,13 +3295,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 +3311,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 { diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index 9a64fce25..4b5ef4741 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); @@ -283,6 +288,7 @@ private: typedef QMap Animations; // index - showing, -index - hiding Animations _animations; + Animation _a_selected; int32 _top, _counts[emojiTabCount]; @@ -310,7 +316,7 @@ struct StickerIcon { int32 pixw, pixh; }; -class StickerPanInner : public TWidget, public Animated { +class StickerPanInner : public TWidget { Q_OBJECT public: @@ -327,7 +333,7 @@ public: void leaveToChildEvent(QEvent *e); void enterFromChildEvent(QEvent *e); - bool animStep(float64 ms); + void step_selected(uint64 ms, bool timer); void showStickerSet(uint64 setId); @@ -376,6 +382,7 @@ private: typedef QMap Animations; // index - showing, -index - hiding Animations _animations; + Animation _a_selected; int32 _top; @@ -454,7 +461,7 @@ protected: }; -class EmojiPan : public TWidget, public Animated { +class EmojiPan : public TWidget { Q_OBJECT public: @@ -480,9 +487,9 @@ 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); @@ -547,6 +554,7 @@ private: QPixmap _cache; anim::fvalue a_opacity; + Animation _a_appearance; QTimer _hideTimer; @@ -559,7 +567,7 @@ private: 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; @@ -570,7 +578,7 @@ private: 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; @@ -590,7 +598,7 @@ typedef QList HashtagRows; typedef QList > BotCommandRows; class MentionsDropdown; -class MentionsInner : public QWidget { +class MentionsInner : public TWidget { Q_OBJECT public: @@ -636,7 +644,7 @@ private: bool _overDelete; }; -class MentionsDropdown : public TWidget, public Animated { +class MentionsDropdown : public TWidget { Q_OBJECT public: @@ -652,7 +660,7 @@ public: void updateFiltered(bool toDown = false); void setBoundings(QRect boundings); - bool animStep(float64 ms); + void step_appearance(float64 ms, bool timer); const QString &filter() const; ChatData *chat() const; @@ -706,6 +714,7 @@ private: bool _hiding; anim::fvalue a_opacity; + Animation _a_appearance; QTimer _hideTimer; diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 72a2a8d27..333db29bb 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -26,7 +26,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "window.h" namespace { - AnimationManager *manager = 0; + AnimationManager *_manager = 0; }; namespace anim { @@ -78,34 +78,34 @@ 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(); + delete _manager; + _manager = new AnimationManager(); } void stopManager() { - delete manager; - manager = 0; + delete _manager; + _manager = 0; } } -bool AnimatedGif::animStep(float64 ms) { +void Animation::start() { + if (!_manager) return; + + _cb->start(); + _manager->start(this); + _animating = true; +} + +void Animation::stop() { + if (!_manager) return; + + _animating = false; + _manager->stop(this); +} + +void AnimatedGif::step_frame(float64 ms, bool timer) { int32 f = frame; while (f < images.size() && ms > delays[f]) { ++f; @@ -152,13 +152,14 @@ bool AnimatedGif::animStep(float64 ms) { } if (frame != f) { frame = f; - if (msg && App::main()) { - App::main()->msgUpdated(msg); - } else { - emit updated(); + if (timer) { + if (msg && App::main()) { + App::main()->msgUpdated(msg); + } else { + emit updated(); + } } } - return true; } void AnimatedGif::start(HistoryItem *row, const FileLocation &f) { @@ -205,7 +206,7 @@ void AnimatedGif::start(HistoryItem *row, const FileLocation &f) { msg = row; - anim::start(this); + _a_frames.start(); if (msg) { msg->initDimensions(); if (App::main()) App::main()->itemResized(msg, true); @@ -233,7 +234,7 @@ void AnimatedGif::stop(bool onItemRemoved) { delays.clear(); w = h = frame = framesCount = duration = 0; - anim::stop(this); + _a_frames.stop(); if (row && !onItemRemoved) { row->initDimensions(); if (App::main()) App::main()->itemResized(row, true); diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index 4f92671ed..abb9b2ce4 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -24,8 +24,6 @@ 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); @@ -187,216 +185,203 @@ namespace anim { 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 AnimationCallbacks { +public: + virtual void start() { + } + + virtual void step(Animation *a, uint64 ms, bool timer) = 0; + + virtual ~AnimationCallbacks() { + } +}; + +class Animation { public: - Animated() : animStarted(0), animInProcess(false) { + Animation(AnimationCallbacks *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(); + delete _cb; } private: - AnimationFunc *_func; + AnimationCallbacks *_cb; + bool _animating; }; +template +class AnimationCallbacksRelative : public AnimationCallbacks { +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 +AnimationCallbacks *animation(Type *obj, typename AnimationCallbacksRelative::Method method) { + return new AnimationCallbacksRelative(obj, method); +} + +template +class AnimationCallbacksAbsolute : public AnimationCallbacks { +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 +AnimationCallbacks *animation(Type *obj, typename AnimationCallbacksAbsolute::Method method) { + return new AnimationCallbacksAbsolute(obj, method); +} + class AnimationManager : public QObject { Q_OBJECT public: - AnimationManager() : timer(this), iterating(false) { - timer.setSingleShot(false); - connect(&timer, SIGNAL(timeout()), this, SLOT(timeout())); + 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); + void start(Animation *obj) { + if (_iterating) { + _starting.insert(obj, NullType()); + if (!_stopping.isEmpty()) { + _stopping.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; + if (_objects.isEmpty()) { + _timer.start(AnimationTimerDelta); } + _objects.insert(obj, NullType()); } } - void stop(Animated *obj) { - if (iterating) { - toStop.insert(obj); - if (!toStart.isEmpty()) { - toStart.insert(obj); + void stop(Animation *obj) { + if (_iterating) { + _stopping.insert(obj, NullType()); + if (!_starting.isEmpty()) { + _starting.insert(obj, NullType()); } } else { - AnimObjs::iterator i = objs.find(obj); - if (i != objs.cend()) { - objs.erase(i); - if (!objs.size()) { - timer.stop(); + AnimatingObjects::iterator i = _objects.find(obj); + if (i != _objects.cend()) { + _objects.erase(i); + if (_objects.isEmpty()) { + _timer.stop(); } } } - obj->animInProcess = false; } 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 = 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 (!toStart.isEmpty()) { - for (AnimObjs::iterator i = toStart.begin(), e = toStart.end(); i != e; ++i) { - objs.insert(*i); + _iterating = false; + + if (!_starting.isEmpty()) { + for (AnimatingObjects::iterator i = _starting.begin(), e = _starting.end(); i != e; ++i) { + _objects.insert(i.key(), NullType()); } - toStart.clear(); + _starting.clear(); } - if (!toStop.isEmpty()) { - for (AnimObjs::iterator i = toStop.begin(), e = toStop.end(); i != e; ++i) { - objs.remove(*i); + if (!_stopping.isEmpty()) { + for (AnimatingObjects::iterator i = _stopping.begin(), e = _stopping.end(); i != e; ++i) { + _objects.remove(i.key()); } - toStop.clear(); + _stopping.clear(); } - if (!objs.size()) { - timer.stop(); + if (!_objects.size()) { + _timer.stop(); } } 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 { +class AnimatedGif : public QObject { Q_OBJECT public: - AnimatedGif() : msg(0), file(0), access(false), reader(0), w(0), h(0), frame(0), framesCount(0), duration(0) { + AnimatedGif() : QObject() + , msg(0) + , file(0) + , access(false) + , reader(0) + , w(0) + , h(0) + , frame(0) + , framesCount(0) + , duration(0) + , _a_frames(animation(this, &AnimatedGif::step_frame)) { } - bool animStep(float64 ms); + void step_frame(float64 ms, bool timer); void start(HistoryItem *row, const FileLocation &file); void stop(bool onItemRemoved = false); @@ -430,4 +415,7 @@ private: QVector images; QVector delays; int32 framesCount, duration; + + Animation _a_frames; + }; diff --git a/Telegram/SourceFiles/gui/flatbutton.cpp b/Telegram/SourceFiles/gui/flatbutton.cpp index e881a5af1..e02c47fa2 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,14 @@ 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)) { +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 +332,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 c6f0dc54b..9f542949e 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,14 +92,14 @@ 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); @@ -117,6 +120,7 @@ protected: anim::fvalue a_opacity; anim::cvalue a_bg; + Animation _a_appearance; float64 _opacity; }; @@ -141,7 +145,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 7e4b8225e..bace80fc7 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 c0296e405..560045fdf 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 f217b16f7..dd387c23e 100644 --- a/Telegram/SourceFiles/gui/flatinput.cpp +++ b/Telegram/SourceFiles/gui/flatinput.cpp @@ -51,10 +51,20 @@ 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); @@ -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); @@ -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,36 +1265,37 @@ 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); @@ -1834,47 +1839,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 +1980,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); @@ -2188,47 +2188,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 95aa9b8ce..98b8d3be1 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 7dccc46b1..465e71cb4 100644 --- a/Telegram/SourceFiles/gui/flattextarea.cpp +++ b/Telegram/SourceFiles/gui/flattextarea.cpp @@ -24,12 +24,27 @@ 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) +, _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); @@ -74,10 +89,10 @@ _touchPress(false), _touchRightButton(false), _touchMove(false), _correcting(fal void FlatTextarea::setTextFast(const QString &text) { setPlainText(text); - if (animating()) { + if (_a_appearance.animating()) { a_phLeft.finish(); a_phAlpha.finish(); - anim::stop(this); + _a_appearance.stop(); update(); } } @@ -184,7 +199,7 @@ 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; } @@ -203,13 +218,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); } @@ -807,11 +822,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,8 +837,7 @@ 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) { @@ -839,7 +852,7 @@ void FlatTextarea::updatePlaceholder() { 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 524838216..0a153a5a7 100644 --- a/Telegram/SourceFiles/gui/flattextarea.h +++ b/Telegram/SourceFiles/gui/flattextarea.h @@ -24,7 +24,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "style.h" #include "animation.h" -class FlatTextarea : public QTextEdit, public Animated { +class FlatTextarea : public QTextEdit { Q_OBJECT T_WIDGET @@ -56,7 +56,7 @@ public: QRect getTextRect() const; int32 fakeMargin() const; - bool animStep(float64 ms); + void step_appearance(float64 ms, bool timer); QSize sizeHint() const; QSize minimumSizeHint() const; @@ -127,6 +127,8 @@ private: 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/popupmenu.cpp b/Telegram/SourceFiles/gui/popupmenu.cpp index 17f96535b..21fefa3e1 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 887f15bd6..121984e83 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 0f76852b6..9fdc86a76 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 4029ffc25..76add3f2b 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/switcher.cpp b/Telegram/SourceFiles/gui/switcher.cpp deleted file mode 100644 index e0e9173c3..000000000 --- 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 5b633514a..000000000 --- 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/history.cpp b/Telegram/SourceFiles/history.cpp index 28854b397..b4c16dcb7 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -1357,11 +1357,10 @@ void Histories::regSendAction(History *history, UserData *user, const MTPSendMes } history->updateTyping(ms, history->typingFrame, true); - anim::start(this); + _a_typings.start(); } -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); @@ -1371,7 +1370,9 @@ bool Histories::animStep(float64) { ++i; } } - return !typing.isEmpty(); + if (typing.isEmpty()) { + _a_typings.stop(); + } } void Histories::remove(const PeerId &peer) { @@ -2968,23 +2969,25 @@ void HistoryBlock::removeItem(HistoryItem *item) { } } -bool ItemAnimations::animStep(float64 ms) { +void ItemAnimations::step_animate(float64 ms, bool timer) { for (Animations::iterator i = _animations.begin(); i != _animations.end();) { const HistoryItem *item = i.key(); if (item->animating()) { - App::main()->msgUpdated(item); + if (timer) App::main()->msgUpdated(item); ++i; } else { i = _animations.erase(i); } } - return !_animations.isEmpty(); + if (_animations.isEmpty()) { + _a_animate.stop(); + } } uint64 ItemAnimations::animate(const HistoryItem *item, uint64 ms) { if (_animations.isEmpty()) { _animations.insert(item, ms); - anim::start(this); + _a_animate.start(); return 0; } Animations::const_iterator i = _animations.constFind(item); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 8abc9b95b..8871977b4 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -45,16 +45,16 @@ enum NewMessageType { }; class History; -class Histories : public Animated { +class Histories { public: typedef QHash Map; Map map; - Histories() : unreadFull(0), unreadMuted(0) { + Histories() : unreadFull(0), unreadMuted(0), _a_typings(animation(this, &Histories::step_typings)) { } 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); @@ -71,6 +71,7 @@ public: typedef QMap TypingHistories; // when typing in this history started TypingHistories typing; + Animation _a_typings; int32 unreadFull, unreadMuted; }; @@ -789,16 +790,19 @@ protected: }; -class ItemAnimations : public Animated { +class ItemAnimations { public: - bool animStep(float64 ms); + ItemAnimations() : _a_animate(animation(this, &ItemAnimations::step_animate)) { + } + void step_animate(float64 ms, bool timer); uint64 animate(const HistoryItem *item, uint64 ms); void remove(const HistoryItem *item); private: typedef QMap Animations; Animations _animations; + Animation _a_animate; }; ItemAnimations &itemAnimations(); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 27e4e207b..e4dd0b2a9 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -1945,8 +1945,16 @@ void ReportSpamPanel::setReported(bool reported, PeerData *onPeer) { update(); } -BotKeyboard::BotKeyboard() : _height(0), _maxOuterHeight(0), _maximizeSize(false), _singleUse(false), _forceReply(false), -_sel(-1), _down(-1), _hoverAnim(animFunc(this, &BotKeyboard::hoverStep)), _st(&st::botKbButton) { +BotKeyboard::BotKeyboard() : TWidget() +, _height(0) +, _maxOuterHeight(0) +, _maximizeSize(false) +, _singleUse(false) +, _forceReply(false) +, _sel(-1) +, _down(-1) +, _a_selected(animation(this, &BotKeyboard::step_selected)) +, _st(&st::botKbButton) { setGeometry(0, 0, _st->margin, _st->margin); _height = _st->margin; setMouseTracking(true); @@ -2124,11 +2132,10 @@ bool BotKeyboard::forceReply() const { return _forceReply; } -bool BotKeyboard::hoverStep(float64 ms) { - uint64 now = getms(); +void BotKeyboard::step_selected(uint64 ms, bool timer) { for (Animations::iterator i = _animations.begin(); i != _animations.end();) { int index = qAbs(i.key()) - 1, row = (index / MatrixRowShift), col = index % MatrixRowShift; - float64 dt = float64(now - i.value()) / st::botKbDuration; + float64 dt = float64(ms - i.value()) / st::botKbDuration; if (dt >= 1) { _btns[row][col].hover = (i.key() > 0) ? 1 : 0; i = _animations.erase(i); @@ -2137,8 +2144,10 @@ bool BotKeyboard::hoverStep(float64 ms) { ++i; } } - update(); - return !_animations.isEmpty(); + if (timer) update(); + if (_animations.isEmpty()) { + _a_selected.stop(); + } } void BotKeyboard::resizeToWidth(int32 width, int32 maxOuterHeight) { @@ -2183,7 +2192,7 @@ void BotKeyboard::clearSelection() { _btns[row][col].hover = 0; } _animations.clear(); - _hoverAnim.stop(); + _a_selected.stop(); if (_sel >= 0) { int row = (_sel / MatrixRowShift), col = _sel % MatrixRowShift; _btns[row][col].hover = 0; @@ -2246,7 +2255,7 @@ void BotKeyboard::updateSelected() { _animations.insert(_sel + 1, getms()); } } - if (startanim) _hoverAnim.start(); + if (startanim && !_a_selected.animating()) _a_selected.start(); } } @@ -2258,11 +2267,11 @@ HistoryHider::HistoryHider(MainWidget *parent, bool forwardSelected) : TWidget(p , _cancel(this, lang(lng_cancel), st::cancelBoxButton) , offered(0) , a_opacity(0, 1) +, _a_appearance(animation(this, &HistoryHider::step_appearance)) , hiding(false) , _forwardRequest(0) , toTextWidth(0) -, shadow(st::boxShadow) -{ +, shadow(st::boxShadow) { init(); } @@ -2274,11 +2283,11 @@ HistoryHider::HistoryHider(MainWidget *parent, UserData *sharedContact) : TWidge , _cancel(this, lang(lng_cancel), st::cancelBoxButton) , offered(0) , a_opacity(0, 1) +, _a_appearance(animation(this, &HistoryHider::step_appearance)) , hiding(false) , _forwardRequest(0) , toTextWidth(0) -, shadow(st::boxShadow) -{ +, shadow(st::boxShadow) { init(); } @@ -2290,11 +2299,11 @@ HistoryHider::HistoryHider(MainWidget *parent) : TWidget(parent) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) , offered(0) , a_opacity(0, 1) +, _a_appearance(animation(this, &HistoryHider::step_appearance)) , hiding(false) , _forwardRequest(0) , toTextWidth(0) -, shadow(st::boxShadow) -{ +, shadow(st::boxShadow) { init(); } @@ -2308,11 +2317,11 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString , _cancel(this, lang(lng_cancel), st::cancelBoxButton) , offered(0) , a_opacity(0, 1) +, _a_appearance(animation(this, &HistoryHider::step_appearance)) , hiding(false) , _forwardRequest(0) , toTextWidth(0) -, shadow(st::boxShadow) -{ +, shadow(st::boxShadow) { init(); } @@ -2324,24 +2333,22 @@ void HistoryHider::init() { _chooseWidth = st::forwardFont->width(lang(lng_forward_choose)); resizeEvent(0); - anim::start(this); + _a_appearance.start(); } -bool HistoryHider::animStep(float64 ms) { +void HistoryHider::step_appearance(float64 ms, bool timer) { float64 dt = ms / 200; - bool res = true; if (dt >= 1) { + _a_appearance.stop(); a_opacity.finish(); if (hiding) { QTimer::singleShot(0, this, SLOT(deleteLater())); } - res = false; } else { a_opacity.update(dt, anim::linear); } App::wnd()->getTitle()->setHideLevel(a_opacity.current()); - update(); - return res; + if (timer) update(); } bool HistoryHider::withConfirm() const { @@ -2411,7 +2418,7 @@ void HistoryHider::startHide() { a_opacity.start(0); _send.hide(); _cancel.hide(); - anim::start(this); + _a_appearance.start(); } else { QTimer::singleShot(0, this, SLOT(deleteLater())); } @@ -2581,8 +2588,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _broadcast(this, QString(), true, st::broadcastToggle) , _cmdStartShown(false) , _field(this, st::taMsgField, lang(lng_message_ph)) -, _recordAnim(animFunc(this, &HistoryWidget::recordStep)) -, _recordingAnim(animFunc(this, &HistoryWidget::recordingStep)) +, _a_record(animation(this, &HistoryWidget::step_record)) +, _a_recording(animation(this, &HistoryWidget::step_recording)) , _recording(false), _inRecord(false), _inField(false), _inReply(false) , a_recordingLevel(0, 0), _recordingSamples(0) , a_recordOver(0, 0), a_recordDown(0, 0), a_recordCancel(st::recordCancel->c, st::recordCancel->c) @@ -2601,7 +2608,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _serviceImageCacheSize(0) , _confirmWithTextId(0) , _titlePeerTextWidth(0) -, _a_show(animFunc(this, &HistoryWidget::animStep_show)) +, _a_show(animation(this, &HistoryWidget::step_show)) , _scrollDelta(0) , _saveDraftStart(0) , _saveDraftText(false) @@ -2756,7 +2763,7 @@ void HistoryWidget::onTextChange() { } else if (!_field.isHidden() && _send.isHidden()) { _send.show(); setMouseTracking(false); - _recordAnim.stop(); + _a_record.stop(); _inRecord = _inField = false; a_recordOver = a_recordDown = anim::fvalue(0, 0); a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c); @@ -2921,7 +2928,7 @@ void HistoryWidget::onRecordUpdate(qint16 level, qint32 samples) { } a_recordingLevel.start(level); - _recordingAnim.start(); + _a_recording.start(); _recordingSamples = samples; if (samples < 0 || samples >= AudioVoiceMsgFrequency * AudioVoiceMsgMaxLength) { stopRecording(_peer && samples > 0 && _inField); @@ -3625,7 +3632,7 @@ void HistoryWidget::updateControlsVisibility() { } else { _send.show(); setMouseTracking(false); - _recordAnim.stop(); + _a_record.stop(); _inRecord = _inField = false; a_recordOver = anim::fvalue(0, 0); } @@ -4431,15 +4438,13 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo activate(); } -bool HistoryWidget::animStep_show(float64 ms) { +void HistoryWidget::step_show(float64 ms, bool timer) { float64 dt = ms / st::slideDuration; - bool res = true; if (dt >= 1) { _a_show.stop(); _sideShadow.setVisible(cWideMode()); _topShadow.setVisible(_peer ? true : false); - res = false; a_coordUnder.finish(); a_coordOver.finish(); a_shadow.finish(); @@ -4453,9 +4458,10 @@ bool HistoryWidget::animStep_show(float64 ms) { a_coordOver.update(dt, st::slideFunction); a_shadow.update(dt, st::slideFunction); } - update(); - App::main()->topBar()->update(); - return res; + if (timer) { + update(); + App::main()->topBar()->update(); + } } void HistoryWidget::doneShow() { @@ -4481,11 +4487,10 @@ void HistoryWidget::animStop() { _topShadow.setVisible(_peer ? true : false); } -bool HistoryWidget::recordStep(float64 ms) { +void HistoryWidget::step_record(float64 ms, bool timer) { float64 dt = ms / st::btnSend.duration; - bool res = true; if (dt >= 1 || !_send.isHidden() || isBotStart() || isBlocked()) { - res = false; + _a_record.stop(); a_recordOver.finish(); a_recordDown.finish(); a_recordCancel.finish(); @@ -4494,25 +4499,24 @@ bool HistoryWidget::recordStep(float64 ms) { a_recordDown.update(dt, anim::linear); a_recordCancel.update(dt, anim::linear); } - if (_recording) { - updateField(); - } else { - update(_send.geometry()); + if (timer) { + if (_recording) { + updateField(); + } else { + update(_send.geometry()); + } } - return res; } -bool HistoryWidget::recordingStep(float64 ms) { +void HistoryWidget::step_recording(float64 ms, bool timer) { float64 dt = ms / AudioVoiceMsgUpdateView; - bool res = true; if (dt >= 1) { - res = false; + _a_recording.stop(); a_recordingLevel.finish(); } else { a_recordingLevel.update(dt, anim::linear); } - update(_attachDocument.geometry()); - return res; + if (timer) update(_attachDocument.geometry()); } void HistoryWidget::onPhotoSelect() { @@ -4623,7 +4627,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { _inReply = inReply; setCursor(inReply ? style::cur_pointer : style::cur_default); } - if (startAnim) _recordAnim.start(); + if (startAnim) _a_record.start(); } void HistoryWidget::leaveToChildEvent(QEvent *e) { // e -- from enterEvent() of child TWidget @@ -4648,7 +4652,7 @@ void HistoryWidget::stopRecording(bool send) { audioCapture()->stop(send); a_recordingLevel = anim::ivalue(0, 0); - _recordingAnim.stop(); + _a_recording.stop(); _recording = false; _recordingSamples = 0; @@ -4664,7 +4668,7 @@ void HistoryWidget::stopRecording(bool send) { a_recordDown.start(0); a_recordOver.restart(); a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c); - _recordAnim.start(); + _a_record.start(); } void HistoryWidget::sendBotCommand(const QString &cmd, MsgId replyTo) { // replyTo != 0 from ReplyKeyboardMarkup, == 0 from cmd links @@ -6001,7 +6005,7 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { a_recordDown.start(1); a_recordOver.restart(); - _recordAnim.start(); + _a_record.start(); } else if (_inReply) { App::main()->showPeerHistory(_peer->id, replyToId()); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 78212d1ec..f01e5fb8b 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -249,7 +249,7 @@ private: }; -class BotKeyboard : public QWidget { +class BotKeyboard : public TWidget { Q_OBJECT public: @@ -267,7 +267,7 @@ public: bool hasMarkup() const; bool forceReply() const; - bool hoverStep(float64 ms); + void step_selected(uint64 ms, bool timer); void resizeToWidth(int32 width, int32 maxOuterHeight); bool maximizeSize() const; @@ -308,13 +308,13 @@ private: typedef QMap Animations; Animations _animations; - Animation _hoverAnim; + Animation _a_selected; const style::botKeyboardButton *_st; }; -class HistoryHider : public TWidget, public Animated { +class HistoryHider : public TWidget { Q_OBJECT public: @@ -324,7 +324,7 @@ public: HistoryHider(MainWidget *parent); // send path from command line argument HistoryHider(MainWidget *parent, const QString &url, const QString &text); // share url - bool animStep(float64 ms); + void step_appearance(float64 ms, bool timer); bool withConfirm() const; void paintEvent(QPaintEvent *e); @@ -362,7 +362,10 @@ private: BoxButton _send, _cancel; PeerData *offered; + anim::fvalue a_opacity; + Animation _a_appearance; + QRect box; bool hiding; @@ -471,7 +474,7 @@ public: HistoryItem *atTopImportantMsg(int32 &bottomUnderScrollTop) const; void animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false); - bool animStep_show(float64 ms); + void step_show(float64 ms, bool timer); void animStop(); void updateWideMode(); @@ -510,8 +513,8 @@ public: void updatePreview(); void previewCancel(); - bool recordStep(float64 ms); - bool recordingStep(float64 ms); + void step_record(float64 ms, bool timer); + void step_recording(float64 ms, bool timer); void stopRecording(bool send); void onListEscapePressed(); @@ -759,7 +762,7 @@ private: FlatCheckbox _broadcast; bool _cmdStartShown; MessageField _field; - Animation _recordAnim, _recordingAnim; + Animation _a_record, _a_recording; bool _recording, _inRecord, _inField, _inReply; anim::ivalue a_recordingLevel; int32 _recordingSamples; diff --git a/Telegram/SourceFiles/intro/intro.cpp b/Telegram/SourceFiles/intro/intro.cpp index 82d54b98f..e1fff2962 100644 --- a/Telegram/SourceFiles/intro/intro.cpp +++ b/Telegram/SourceFiles/intro/intro.cpp @@ -52,27 +52,27 @@ namespace { } } -IntroWidget::IntroWidget(Window *window) : TWidget(window), -_langChangeTo(0), -_a_stage(animFunc(this, &IntroWidget::animStep_stage)), -_cacheHideIndex(0), -_cacheShowIndex(0), -_a_show(animFunc(this, &IntroWidget::animStep_show)), -wnd(window), -steps(new IntroSteps(this)), -phone(0), -code(0), -signup(0), -pwdcheck(0), -current(0), -moving(0), -_callTimeout(60), -_registered(false), -_hasRecovery(false), -_codeByTelegram(false), -_back(this, st::setClose), -_backFrom(0), _backTo(0) { - setGeometry(QRect(0, st::titleHeight, wnd->width(), wnd->height() - st::titleHeight)); +IntroWidget::IntroWidget(Window *window) : TWidget(window) +, _langChangeTo(0) +, _a_stage(animation(this, &IntroWidget::step_stage)) +, _cacheHideIndex(0) +, _cacheShowIndex(0) +, _a_show(animation(this, &IntroWidget::step_show)) +, steps(new IntroSteps(this)) +, phone(0) +, code(0) +, signup(0) +, pwdcheck(0) +, current(0) +, moving(0) +, _callTimeout(60) +, _registered(false) +, _hasRecovery(false) +, _codeByTelegram(false) +, _back(this, st::setClose) +, _backFrom(0) +, _backTo(0) { + setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight)); connect(&_back, SIGNAL(clicked()), this, SLOT(onIntroBack())); _back.hide(); @@ -162,7 +162,7 @@ void IntroWidget::prepareMove() { _backTo = stages[current + moving]->hasBack() ? 1 : 0; _backFrom = stages[current]->hasBack() ? 1 : 0; - animStep_stage(0); + _a_stage.step(); if (_backFrom > 0 || _backTo > 0) { _back.show(); } else { @@ -227,13 +227,11 @@ void IntroWidget::animShow(const QPixmap &bgAnimCache, bool back) { show(); } -bool IntroWidget::animStep_show(float64 ms) { +void IntroWidget::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(); @@ -253,21 +251,19 @@ bool IntroWidget::animStep_show(float64 ms) { a_coordOver.update(dt, st::slideFunction); a_shadow.update(dt, st::slideFunction); } - update(); - return res; + if (timer) update(); } -void IntroWidget::animStop_show() { +void IntroWidget::stop_show() { _a_show.stop(); } -bool IntroWidget::animStep_stage(float64 ms) { - bool res = true; - +void IntroWidget::step_stage(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 (dt >= 1) { - res = false; + _a_stage.stop(); + a_coordShow.finish(); a_opacityShow.finish(); @@ -292,8 +288,7 @@ bool IntroWidget::animStep_stage(float64 ms) { _back.setOpacity(1); } } - update(); - return res; + if (timer) update(); } void IntroWidget::paintEvent(QPaintEvent *e) { @@ -414,7 +409,7 @@ void IntroWidget::mousePressEvent(QMouseEvent *e) { } void IntroWidget::finish(const MTPUser &user, const QImage &photo) { - wnd->setupMain(true, &user); + App::wnd()->setupMain(true, &user); if (!photo.isNull()) { App::app()->uploadProfilePhoto(photo, MTP::authedId()); } diff --git a/Telegram/SourceFiles/intro/intro.h b/Telegram/SourceFiles/intro/intro.h index bc2b634f7..ba21c7dab 100644 --- a/Telegram/SourceFiles/intro/intro.h +++ b/Telegram/SourceFiles/intro/intro.h @@ -44,10 +44,10 @@ public: void updateWideMode(); void animShow(const QPixmap &bgAnimCache, bool back = false); - bool animStep_show(float64 ms); - void animStop_show(); + void step_show(float64 ms, bool timer); + void stop_show(); - bool animStep_stage(float64 ms); + void step_stage(float64 ms, bool timer); QRect innerRect() const; QString currentCountry() const; @@ -108,7 +108,6 @@ private: anim::ivalue a_coordUnder, a_coordOver; anim::fvalue a_shadow; - Window *wnd; IntroSteps *steps; IntroPhone *phone; IntroCode *code; diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index bf23e6235..71cd2bd94 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -72,12 +72,15 @@ void CodeInput::correctValue(const QString &was, QString &now) { if (strict) emit codeEntered(); } -IntroCode::IntroCode(IntroWidget *parent) : IntroStage(parent), errorAlpha(0), - next(this, lang(lng_intro_next), st::btnIntroNext), - _desc(st::introTextSize.width()), - _noTelegramCode(this, lang(lng_code_no_telegram), st::introLink), - _noTelegramCodeRequestId(0), - code(this, st::inpIntroCode, lang(lng_code_ph)), waitTillCall(intro()->getCallTimeout()) { +IntroCode::IntroCode(IntroWidget *parent) : IntroStage(parent) +, a_errorAlpha(0) +, _a_error(animation(this, &IntroCode::step_error)) +, next(this, lang(lng_intro_next), st::btnIntroNext) +, _desc(st::introTextSize.width()) +, _noTelegramCode(this, lang(lng_code_no_telegram), st::introLink) +, _noTelegramCodeRequestId(0) +, code(this, st::inpIntroCode, lang(lng_code_ph)) +, waitTillCall(intro()->getCallTimeout()) { setVisible(false); setGeometry(parent->innerRect()); @@ -132,8 +135,8 @@ void IntroCode::paintEvent(QPaintEvent *e) { } p.drawText(QRect(textRect.left(), code.y() + code.height() + st::introCallSkip, st::introTextSize.width(), st::introErrHeight), callText, style::al_center); } - if (animating() || error.length()) { - p.setOpacity(errorAlpha.current()); + if (_a_error.animating() || error.length()) { + p.setOpacity(a_errorAlpha.current()); p.setFont(st::introErrFont->f); p.setPen(st::introErrColor->p); p.drawText(QRect(textRect.left(), next.y() + next.height() + st::introErrTop, st::introTextSize.width(), st::introErrHeight), error, style::al_center); @@ -151,32 +154,30 @@ void IntroCode::resizeEvent(QResizeEvent *e) { void IntroCode::showError(const QString &err) { if (!err.isEmpty()) code.notaBene(); - if (!animating() && err == error) return; + if (!_a_error.animating() && err == error) return; if (err.length()) { error = err; - errorAlpha.start(1); + a_errorAlpha.start(1); } else { - errorAlpha.start(0); + a_errorAlpha.start(0); } - anim::start(this); + _a_error.start(); } -bool IntroCode::animStep(float64 ms) { +void IntroCode::step_error(float64 ms, bool timer) { float64 dt = ms / st::introErrDuration; - bool res = true; if (dt >= 1) { - res = false; - errorAlpha.finish(); - if (!errorAlpha.current()) { + _a_error.stop(); + a_errorAlpha.finish(); + if (!a_errorAlpha.current()) { error = ""; } } else { - errorAlpha.update(dt, st::introErrFunc); + a_errorAlpha.update(dt, st::introErrFunc); } - update(); - return res; + if (timer) update(); } void IntroCode::activate() { @@ -185,7 +186,7 @@ void IntroCode::activate() { callTimer.start(1000); } error = ""; - errorAlpha = anim::fvalue(0); + a_errorAlpha = anim::fvalue(0); sentCode = QString(); show(); code.setDisabled(false); diff --git a/Telegram/SourceFiles/intro/introcode.h b/Telegram/SourceFiles/intro/introcode.h index 68d8e557d..2ba90769d 100644 --- a/Telegram/SourceFiles/intro/introcode.h +++ b/Telegram/SourceFiles/intro/introcode.h @@ -42,7 +42,7 @@ protected: }; -class IntroCode : public IntroStage, public Animated, public RPCSender { +class IntroCode : public IntroStage, public RPCSender { Q_OBJECT public: @@ -52,7 +52,7 @@ public: void paintEvent(QPaintEvent *e); void resizeEvent(QResizeEvent *e); - bool animStep(float64 ms); + void step_error(float64 ms, bool timer); void activate(); void prepareShow(); @@ -86,7 +86,8 @@ private: void stopCheck(); QString error; - anim::fvalue errorAlpha; + anim::fvalue a_errorAlpha; + Animation _a_error; FlatButton next; diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index a7cbbdc54..21ea020ee 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -45,13 +45,16 @@ namespace { }; } -IntroPhone::IntroPhone(IntroWidget *parent) : IntroStage(parent), - errorAlpha(0), changed(false), - next(this, lang(lng_intro_next), st::btnIntroNext), - country(this, st::introCountry), - phone(this, st::inpIntroPhone), code(this, st::inpIntroCountryCode), - _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), st::introErrLabel, st::introErrLabelTextStyle), - _showSignup(false) { +IntroPhone::IntroPhone(IntroWidget *parent) : IntroStage(parent) +, a_errorAlpha(0) +, _a_error(animation(this, &IntroPhone::step_error)) +, changed(false) +, next(this, lang(lng_intro_next), st::btnIntroNext) +, country(this, st::introCountry) +, phone(this, st::inpIntroPhone) +, code(this, st::inpIntroCountryCode) +, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), st::introErrLabel, st::introErrLabelTextStyle) +, _showSignup(false) { setVisible(false); setGeometry(parent->innerRect()); @@ -92,9 +95,9 @@ void IntroPhone::paintEvent(QPaintEvent *e) { p.setFont(st::introFont->f); p.drawText(textRect, lang(lng_phone_desc), style::al_bottom); } - if (animating() || error.length()) { + if (_a_error.animating() || error.length()) { int32 errorY = _showSignup ? ((phone.y() + phone.height() + next.y() - st::introErrFont->height) / 2) : (next.y() + next.height() + st::introErrTop); - p.setOpacity(errorAlpha.current()); + p.setOpacity(a_errorAlpha.current()); p.setFont(st::introErrFont->f); p.setPen(st::introErrColor->p); p.drawText(QRect(textRect.x(), errorY, textRect.width(), st::introErrFont->height), error, style::al_top); @@ -123,36 +126,34 @@ void IntroPhone::showError(const QString &err, bool signUp) { _showSignup = signUp; } - if (!animating() && err == error) return; + if (!_a_error.animating() && err == error) return; if (err.length()) { error = err; - errorAlpha.start(1); + a_errorAlpha.start(1); } else { - errorAlpha.start(0); + a_errorAlpha.start(0); } _signup.hide(); - anim::start(this); + _a_error.start(); } -bool IntroPhone::animStep(float64 ms) { +void IntroPhone::step_error(float64 ms, bool timer) { float64 dt = ms / st::introErrDuration; - bool res = true; if (dt >= 1) { - res = false; - errorAlpha.finish(); - if (!errorAlpha.current()) { + _a_error.stop(); + a_errorAlpha.finish(); + if (!a_errorAlpha.current()) { error = ""; _signup.hide(); } else if (!error.isEmpty() && _showSignup) { _signup.show(); } } else { - errorAlpha.update(dt, st::introErrFunc); + a_errorAlpha.update(dt, st::introErrFunc); } - update(); - return res; + if (timer) update(); } void IntroPhone::countryChanged() { @@ -293,7 +294,7 @@ void IntroPhone::selectCountry(const QString &c) { void IntroPhone::activate() { error = ""; - errorAlpha = anim::fvalue(0); + a_errorAlpha = anim::fvalue(0); show(); enableAll(true); } diff --git a/Telegram/SourceFiles/intro/introphone.h b/Telegram/SourceFiles/intro/introphone.h index 22a5b963b..8baa29d44 100644 --- a/Telegram/SourceFiles/intro/introphone.h +++ b/Telegram/SourceFiles/intro/introphone.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "gui/countryinput.h" #include "intro.h" -class IntroPhone : public IntroStage, public Animated, public RPCSender { +class IntroPhone : public IntroStage, public RPCSender { Q_OBJECT public: @@ -35,7 +35,7 @@ public: void paintEvent(QPaintEvent *e); void resizeEvent(QResizeEvent *e); - bool animStep(float64 ms); + void step_error(float64 ms, bool timer); void selectCountry(const QString &country); @@ -67,7 +67,8 @@ private: void showError(const QString &err, bool signUp = false); QString error; - anim::fvalue errorAlpha; + anim::fvalue a_errorAlpha; + Animation _a_error; bool changed; FlatButton next; diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index 95a670539..2aa13f573 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -30,18 +30,19 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "intro/intropwdcheck.h" #include "intro/intro.h" -IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStage(parent), -errorAlpha(0), -_next(this, lang(lng_intro_submit), st::btnIntroNext), -_salt(parent->getPwdSalt()), -_hasRecovery(parent->getHasRecovery()), -_hint(parent->getPwdHint()), -_pwdField(this, st::inpIntroPassword, lang(lng_signin_password)), -_codeField(this, st::inpIntroPassword, lang(lng_signin_code)), -_toRecover(this, lang(lng_signin_recover)), -_toPassword(this, lang(lng_signin_try_password)), -_reset(this, lang(lng_signin_reset_account), st::btnRedLink), -sentRequest(0) { +IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStage(parent) +, a_errorAlpha(0) +, _a_error(animation(this, &IntroPwdCheck::step_error)) +, _next(this, lang(lng_intro_submit), st::btnIntroNext) +, _salt(parent->getPwdSalt()) +, _hasRecovery(parent->getHasRecovery()) +, _hint(parent->getPwdHint()) +, _pwdField(this, st::inpIntroPassword, lang(lng_signin_password)) +, _codeField(this, st::inpIntroPassword, lang(lng_signin_code)) +, _toRecover(this, lang(lng_signin_recover)) +, _toPassword(this, lang(lng_signin_try_password)) +, _reset(this, lang(lng_signin_reset_account), st::btnRedLink) +, sentRequest(0) { setVisible(false); setGeometry(parent->innerRect()); @@ -86,8 +87,8 @@ void IntroPwdCheck::paintEvent(QPaintEvent *e) { } else if (!_hint.isEmpty()) { _hintText.drawElided(p, _pwdField.x(), _pwdField.y() + _pwdField.height() + st::introFinishSkip, _pwdField.width(), 1, style::al_top); } - if (animating() || error.length()) { - p.setOpacity(errorAlpha.current()); + if (_a_error.animating() || error.length()) { + p.setOpacity(a_errorAlpha.current()); QRect errRect((width() - st::introErrWidth) / 2, (_pwdField.y() + _pwdField.height() + st::introFinishSkip + st::introFont->height + _next.y() - st::introErrHeight) / 2, st::introErrWidth, st::introErrHeight); p.setFont(st::introErrFont->f); @@ -111,32 +112,30 @@ void IntroPwdCheck::resizeEvent(QResizeEvent *e) { } void IntroPwdCheck::showError(const QString &err) { - if (!animating() && err == error) return; + if (!_a_error.animating() && err == error) return; if (err.length()) { error = err; - errorAlpha.start(1); + a_errorAlpha.start(1); } else { - errorAlpha.start(0); + a_errorAlpha.start(0); } - anim::start(this); + _a_error.start(); } -bool IntroPwdCheck::animStep(float64 ms) { +void IntroPwdCheck::step_error(float64 ms, bool timer) { float64 dt = ms / st::introErrDuration; - bool res = true; if (dt >= 1) { - res = false; - errorAlpha.finish(); - if (!errorAlpha.current()) { + _a_error.stop(); + a_errorAlpha.finish(); + if (!a_errorAlpha.current()) { error = ""; } } else { - errorAlpha.update(dt, st::introErrFunc); + a_errorAlpha.update(dt, st::introErrFunc); } - update(); - return res; + if (timer) update(); } void IntroPwdCheck::activate() { diff --git a/Telegram/SourceFiles/intro/intropwdcheck.h b/Telegram/SourceFiles/intro/intropwdcheck.h index 7925c3148..71081358a 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.h +++ b/Telegram/SourceFiles/intro/intropwdcheck.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "gui/flatinput.h" #include "intro.h" -class IntroPwdCheck : public IntroStage, public Animated, public RPCSender { +class IntroPwdCheck : public IntroStage, public RPCSender { Q_OBJECT public: @@ -35,7 +35,7 @@ public: void paintEvent(QPaintEvent *e); void resizeEvent(QResizeEvent *e); - bool animStep(float64 ms); + void step_error(float64 ms, bool timer); void activate(); void deactivate(); @@ -69,7 +69,8 @@ private: bool deleteFail(const RPCError &error); QString error; - anim::fvalue errorAlpha; + anim::fvalue a_errorAlpha; + Animation _a_error; FlatButton _next; diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index 3a67d6118..42fbde07b 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -30,12 +30,15 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "intro/introsignup.h" #include "intro/intro.h" -IntroSignup::IntroSignup(IntroWidget *parent) : IntroStage(parent), - errorAlpha(0), a_photo(0), - next(this, lang(lng_intro_finish), st::btnIntroNext), - first(this, st::inpIntroName, lang(lng_signup_firstname)), - last(this, st::inpIntroName, lang(lng_signup_lastname)), - _invertOrder(langFirstNameGoesSecond()) { +IntroSignup::IntroSignup(IntroWidget *parent) : IntroStage(parent) +, a_errorAlpha(0) +, a_photoOver(0) +, _a_error(animation(this, &IntroSignup::step_error)) +, _a_photo(animation(this, &IntroSignup::step_photo)) +, next(this, lang(lng_intro_finish), st::btnIntroNext) +, first(this, st::inpIntroName, lang(lng_signup_firstname)) +, last(this, st::inpIntroName, lang(lng_signup_lastname)) +, _invertOrder(langFirstNameGoesSecond()) { setVisible(false); setGeometry(parent->innerRect()); @@ -54,9 +57,8 @@ void IntroSignup::mouseMoveEvent(QMouseEvent *e) { if (photoOver != _photoOver) { _photoOver = photoOver; if (_photoSmall.isNull()) { - a_photo.start(_photoOver ? 1 : 0); - errorAlpha.restart(); - anim::start(this); + a_photoOver.start(_photoOver ? 1 : 0); + _a_photo.start(); } } @@ -107,8 +109,8 @@ void IntroSignup::paintEvent(QPaintEvent *e) { p.setFont(st::introFont->f); p.drawText(textRect, lang(lng_signup_desc), style::al_bottom); } - if (animating() || error.length()) { - p.setOpacity(errorAlpha.current()); + if (_a_error.animating() || error.length()) { + p.setOpacity(a_errorAlpha.current()); QRect errRect; if (_invertOrder) { @@ -124,17 +126,17 @@ void IntroSignup::paintEvent(QPaintEvent *e) { } if (_photoSmall.isNull()) { - if (a_photo.current() < 1) { + if (a_photoOver.current() < 1) { QRect pix(st::setPhotoImg); pix.moveTo(pix.x() + (pix.width() - st::introPhotoSize) / 2, pix.y() + (pix.height() - st::introPhotoSize) / 2); pix.setSize(QSize(st::introPhotoSize, st::introPhotoSize)); p.drawPixmap(QPoint(_phLeft, _phTop), App::sprite(), pix); } - if (a_photo.current() > 0) { + if (a_photoOver.current() > 0) { QRect pix(st::setOverPhotoImg); pix.moveTo(pix.x() + (pix.width() - st::introPhotoSize) / 2, pix.y() + (pix.height() - st::introPhotoSize) / 2); pix.setSize(QSize(st::introPhotoSize, st::introPhotoSize)); - p.setOpacity(a_photo.current()); + p.setOpacity(a_photoOver.current()); p.drawPixmap(QPoint(_phLeft, _phTop), App::sprite(), pix); p.setOpacity(1); } @@ -160,35 +162,42 @@ void IntroSignup::resizeEvent(QResizeEvent *e) { } void IntroSignup::showError(const QString &err) { - if (!animating() && err == error) return; + if (!_a_error.animating() && err == error) return; if (err.length()) { error = err; - errorAlpha.start(1); + a_errorAlpha.start(1); } else { - errorAlpha.start(0); + a_errorAlpha.start(0); } - a_photo.restart(); - anim::start(this); + _a_error.start(); } -bool IntroSignup::animStep(float64 ms) { +void IntroSignup::step_error(float64 ms, bool timer) { float64 dt = ms / st::introErrDuration; - bool res = true; if (dt >= 1) { - res = false; - errorAlpha.finish(); - if (!errorAlpha.current()) { + _a_error.stop(); + a_errorAlpha.finish(); + if (!a_errorAlpha.current()) { error = ""; } - a_photo.finish(); } else { - errorAlpha.update(dt, st::introErrFunc); - a_photo.update(dt, anim::linear); + a_errorAlpha.update(dt, st::introErrFunc); } - update(); - return res; + if (timer) update(); +} + +void IntroSignup::step_photo(float64 ms, bool timer) { + float64 dt = ms / st::introErrDuration; + + if (dt >= 1) { + _a_photo.stop(); + a_photoOver.finish(); + } else { + a_photoOver.update(dt, anim::linear); + } + if (timer) update(); } void IntroSignup::activate() { diff --git a/Telegram/SourceFiles/intro/introsignup.h b/Telegram/SourceFiles/intro/introsignup.h index c3ec33e54..b183f7fc6 100644 --- a/Telegram/SourceFiles/intro/introsignup.h +++ b/Telegram/SourceFiles/intro/introsignup.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "gui/flatinput.h" #include "intro.h" -class IntroSignup : public IntroStage, public Animated, public RPCSender { +class IntroSignup : public IntroStage, public RPCSender { Q_OBJECT public: @@ -37,7 +37,8 @@ public: void mouseMoveEvent(QMouseEvent *e); void mousePressEvent(QMouseEvent *e); - bool animStep(float64 ms); + void step_error(float64 ms, bool timer); + void step_photo(float64 ms, bool timer); void activate(); void deactivate(); @@ -60,7 +61,9 @@ private: void stopCheck(); QString error; - anim::fvalue errorAlpha, a_photo; + anim::fvalue a_errorAlpha, a_photoOver; + Animation _a_error; + Animation _a_photo; FlatButton next; diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index 85f0fe664..f31a95f33 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -29,15 +29,15 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org BackgroundWidget::BackgroundWidget(QWidget *parent, LayeredWidget *w) : TWidget(parent) , w(w) -, aBackground(0) -, aBackgroundFunc(anim::easeOutCirc) +, a_bg(0) +, _a_background(animation(this, &BackgroundWidget::step_background)) , hiding(false) , shadow(st::boxShadow) { w->setParent(this); if (App::app()) App::app()->mtpPause(); setGeometry(0, 0, App::wnd()->width(), App::wnd()->height()); - aBackground.start(1); - anim::start(this); + a_bg.start(1); + _a_background.start(); show(); connect(w, SIGNAL(closed()), this, SLOT(onInnerClose())); connect(w, SIGNAL(resized()), this, SLOT(update())); @@ -46,7 +46,7 @@ BackgroundWidget::BackgroundWidget(QWidget *parent, LayeredWidget *w) : TWidget( } void BackgroundWidget::showFast() { - animStep(st::layerSlideDuration + 1); + _a_background.step(getms() + st::layerSlideDuration + 1); update(); } @@ -58,10 +58,10 @@ void BackgroundWidget::paintEvent(QPaintEvent *e) { if (!trivial) { p.setClipRect(e->rect()); } - p.setOpacity(st::layerAlpha * aBackground.current()); + p.setOpacity(st::layerAlpha * a_bg.current()); p.fillRect(rect(), st::layerBg->b); - p.setOpacity(aBackground.current()); + p.setOpacity(a_bg.current()); shadow.paint(p, w->geometry(), st::boxShadowShift); } @@ -90,7 +90,7 @@ bool BackgroundWidget::onInnerClose() { _hidden.pop_back(); w->show(); resizeEvent(0); - w->animStep(1); + w->showStep(1); update(); return false; } @@ -101,8 +101,8 @@ void BackgroundWidget::startHide() { hiding = true; if (App::wnd()) App::wnd()->setInnerFocus(); - aBackground.start(0); - anim::start(this); + a_bg.start(0); + _a_background.start(); w->startHide(); } @@ -139,7 +139,7 @@ void BackgroundWidget::replaceInner(LayeredWidget *n) { connect(w, SIGNAL(destroyed(QObject*)), this, SLOT(boxDestroyed(QObject*))); w->show(); resizeEvent(0); - w->animStep(1); + w->showStep(1); update(); } @@ -150,28 +150,25 @@ void BackgroundWidget::showLayerLast(LayeredWidget *n) { connect(n, SIGNAL(resized()), this, SLOT(update())); connect(n, SIGNAL(destroyed(QObject*)), this, SLOT(boxDestroyed(QObject*))); n->parentResized(); - n->animStep(1); + n->showStep(1); n->hide(); update(); } -bool BackgroundWidget::animStep(float64 ms) { +void BackgroundWidget::step_background(float64 ms, bool timer) { float64 dt = ms / (hiding ? st::layerHideDuration : st::layerSlideDuration); - w->animStep(dt); - bool res = true; + w->showStep(dt); if (dt >= 1) { - aBackground.finish(); + a_bg.finish(); if (hiding) { App::wnd()->layerFinishedHide(this); } - anim::stop(this); - res = false; + _a_background.stop(); if (App::app()) App::app()->mtpUnpause(); } else { - aBackground.update(dt, aBackgroundFunc); + a_bg.update(dt, anim::easeOutCirc); } - update(); - return res; + if (timer) update(); } void BackgroundWidget::boxDestroyed(QObject *obj) { @@ -196,7 +193,7 @@ BackgroundWidget::~BackgroundWidget() { StickerPreviewWidget::StickerPreviewWidget(QWidget *parent) : TWidget(parent) , a_shown(0, 0) -, _a_shown(animFunc(this, &StickerPreviewWidget::animStep_shown)) +, _a_shown(animation(this, &StickerPreviewWidget::step_shown)) , _doc(0) , _cacheStatus(CacheNotLoaded) { setAttribute(Qt::WA_TransparentForMouseEvents); @@ -222,17 +219,16 @@ void StickerPreviewWidget::resizeEvent(QResizeEvent *e) { update(); } -bool StickerPreviewWidget::animStep_shown(float64 ms) { +void StickerPreviewWidget::step_shown(float64 ms, bool timer) { float64 dt = ms / st::stickerPreviewDuration; if (dt >= 1) { - a_shown.finish(); _a_shown.stop(); + a_shown.finish(); if (a_shown.current() < 0.5) hide(); } else { a_shown.update(dt, anim::linear); } - update(); - return true; + if (timer) update(); } void StickerPreviewWidget::showPreview(DocumentData *sticker) { diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h index 98d6eb3b3..133a4072e 100644 --- a/Telegram/SourceFiles/layerwidget.h +++ b/Telegram/SourceFiles/layerwidget.h @@ -27,7 +27,7 @@ class LayeredWidget : public TWidget { public: - virtual void animStep(float64 ms) { + virtual void showStep(float64 ms) { } virtual void parentResized() = 0; virtual void startHide() { @@ -57,7 +57,7 @@ signals: }; -class BackgroundWidget : public TWidget, public Animated { +class BackgroundWidget : public TWidget { Q_OBJECT public: @@ -76,7 +76,7 @@ public: void replaceInner(LayeredWidget *n); void showLayerLast(LayeredWidget *n); - bool animStep(float64 ms); + void step_background(float64 ms, bool timer); bool canSetFocus() const; void setInnerFocus(); @@ -98,8 +98,9 @@ private: LayeredWidget *w; typedef QList HiddenLayers; HiddenLayers _hidden; - anim::fvalue aBackground; - anim::transition aBackgroundFunc; + anim::fvalue a_bg; + Animation _a_background; + bool hiding; BoxShadow shadow; @@ -115,7 +116,7 @@ public: void paintEvent(QPaintEvent *e); void resizeEvent(QResizeEvent *e); - bool animStep_shown(float64 ms); + void step_shown(float64 ms, bool timer); void showPreview(DocumentData *sticker); void hidePreview(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 2d5d59a84..a04bb47b4 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -38,6 +38,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w) , a_over(0) +, _a_appearance(animation(this, &TopBarWidget::step_appearance)) , _selPeer(0) , _selCount(0) , _canDelete(false) @@ -147,35 +148,33 @@ void TopBarWidget::onDeleteAndExitSure() { void TopBarWidget::enterEvent(QEvent *e) { a_over.start(1); - anim::start(this); + _a_appearance.start(); } void TopBarWidget::enterFromChildEvent(QEvent *e) { a_over.start(1); - anim::start(this); + _a_appearance.start(); } void TopBarWidget::leaveEvent(QEvent *e) { a_over.start(0); - anim::start(this); + _a_appearance.start(); } void TopBarWidget::leaveToChildEvent(QEvent *e) { a_over.start(0); - anim::start(this); + _a_appearance.start(); } -bool TopBarWidget::animStep(float64 ms) { +void TopBarWidget::step_appearance(float64 ms, bool timer) { float64 dt = ms / st::topBarDuration; - bool res = true; if (dt >= 1) { + _a_appearance.stop(); a_over.finish(); - res = false; } else { a_over.update(dt, anim::linear); } - update(); - return res; + if (timer) update(); } void TopBarWidget::paintEvent(QPaintEvent *e) { @@ -383,7 +382,7 @@ MainWidget::MainWidget(Window *window) : TWidget(window) , _started(0) , failedObjId(0) , _toForwardNameVersion(0) -, _a_show(animFunc(this, &MainWidget::animStep_show)) +, _a_show(animation(this, &MainWidget::step_show)) , _dialogsWidth(st::dlgMinWidth) , dialogs(this) , history(this) @@ -422,7 +421,7 @@ MainWidget::MainWidget(Window *window) : TWidget(window) _ptsWaiter.setRequesting(true); updateScrollColors(); - connect(window, SIGNAL(resized(const QSize&)), this, SLOT(onParentResize(const QSize&))); + connect(App::wnd(), SIGNAL(resized(const QSize&)), this, SLOT(onParentResize(const QSize&))); connect(&dialogs, SIGNAL(cancelled()), this, SLOT(dialogsCancelled())); connect(&history, SIGNAL(cancelled()), &dialogs, SLOT(activate())); connect(this, SIGNAL(peerPhotoChanged(PeerData*)), this, SIGNAL(dialogsUpdated())); @@ -2676,13 +2675,11 @@ void MainWidget::animShow(const QPixmap &bgAnimCache, bool back) { show(); } -bool MainWidget::animStep_show(float64 ms) { +void MainWidget::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(); @@ -2698,8 +2695,7 @@ bool MainWidget::animStep_show(float64 ms) { a_coordOver.update(dt, st::slideFunction); a_shadow.update(dt, st::slideFunction); } - update(); - return res; + if (timer) update(); } void MainWidget::animStop_show() { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index c8cbc6965..4d8c638e5 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -32,7 +32,7 @@ struct DialogRow; class MainWidget; class ConfirmBox; -class TopBarWidget : public TWidget, public Animated { +class TopBarWidget : public TWidget { Q_OBJECT public: @@ -47,7 +47,7 @@ public: void mousePressEvent(QMouseEvent *e); void resizeEvent(QResizeEvent *e); - bool animStep(float64 ms); + void step_appearance(float64 ms, bool timer); void enableShadow(bool enable = true); void startAnim(); @@ -87,6 +87,7 @@ private: MainWidget *main(); anim::fvalue a_over; + Animation _a_appearance; PeerData *_selPeer; uint32 _selCount; @@ -202,7 +203,7 @@ public: int32 contentScrollAddToY() const; void animShow(const QPixmap &bgAnimCache, bool back = false); - bool animStep_show(float64 ms); + void step_show(float64 ms, bool timer); void animStop_show(); void start(const MTPUser &user); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 2963e2411..78ad42528 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -111,6 +111,7 @@ MediaView::MediaView() : TWidget(App::wnd()) , _down(OverNone) , _lastAction(-st::mvDeltaFromLastAction, -st::mvDeltaFromLastAction) , _ignoringDropdown(false) +, _a_state(animation(this, &MediaView::step_state)) , _controlsState(ControlsShown) , _controlsAnimStarted(0) , _menu(0) @@ -300,8 +301,8 @@ void MediaView::updateControls() { _docCancel.moveToLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocBlue.pxWidth(), _docRect.y() + st::mvDocPadding + st::mvDocLinksTop); _docCancel.show(); if (!_docRadialFirst) _docRadialFirst = _docRadialLast = _docRadialStart = getms(); - if (!animating()) anim::start(this); - anim::step(this); + if (!_a_state.animating()) _a_state.start(); + _a_state.step(); } else { if (_doc->already(true).isEmpty()) { _docDownload.moveToLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocBlue.pxWidth(), _docRect.y() + st::mvDocPadding + st::mvDocLinksTop); @@ -395,9 +396,8 @@ void MediaView::updateDropdown() { _dropdown.moveToRight(0, height() - _dropdown.height()); } -bool MediaView::animStep(float64 msp) { +void MediaView::step_state(uint64 ms, bool timer) { bool result = false; - uint64 ms = getms(); for (Showing::iterator i = _animations.begin(); i != _animations.end();) { int64 start = i.value(); switch (i.key()) { @@ -472,7 +472,9 @@ bool MediaView::animStep(float64 msp) { a_docRadialStart.update(fromstart - qFloor(fromstart), anim::linear); update(_docIconRect); } - return result || !_animations.isEmpty(); + if (!result && _animations.isEmpty()) { + _a_state.stop(); + } } MediaView::~MediaView() { @@ -496,7 +498,7 @@ void MediaView::activateControls() { _controlsState = ControlsShowing; _controlsAnimStarted = getms(); a_cOpacity.start(1); - if (!animating()) anim::start(this); + if (!_a_state.animating()) _a_state.start(); } } @@ -506,7 +508,7 @@ void MediaView::onHideControls(bool force) { _controlsState = ControlsHiding; _controlsAnimStarted = getms(); a_cOpacity.start(0); - if (!animating()) anim::start(this); + if (!_a_state.animating()) _a_state.start(); } void MediaView::onDropdownHiding() { @@ -743,7 +745,7 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) { setCursor(style::cur_default); if (!_animations.isEmpty()) { _animations.clear(); - anim::stop(this); + _a_state.stop(); } if (!_animOpacities.isEmpty()) _animOpacities.clear(); @@ -774,7 +776,7 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) { setCursor(style::cur_default); if (!_animations.isEmpty()) { _animations.clear(); - anim::stop(this); + _a_state.stop(); } if (!_animOpacities.isEmpty()) _animOpacities.clear(); @@ -828,7 +830,7 @@ void MediaView::showDocument(DocumentData *doc, HistoryItem *context) { setCursor(style::cur_default); if (!_animations.isEmpty()) { _animations.clear(); - anim::stop(this); + _a_state.stop(); } if (!_animOpacities.isEmpty()) _animOpacities.clear(); @@ -1721,7 +1723,7 @@ bool MediaView::updateOverState(OverState newState) { } else { _animOpacities.insert(_over, anim::fvalue(1, 0)); } - if (!animating()) anim::start(this); + if (!_a_state.animating()) _a_state.start(); } else { result = false; } @@ -1734,7 +1736,7 @@ bool MediaView::updateOverState(OverState newState) { } else { _animOpacities.insert(_over, anim::fvalue(0, 1)); } - if (!animating()) anim::start(this); + if (!_a_state.animating()) _a_state.start(); setCursor(style::cur_pointer); } else { setCursor(style::cur_default); diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index d87f2fec5..72c5cf3b4 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "dropdown.h" -class MediaView : public TWidget, public RPCSender, public Animated { +class MediaView : public TWidget, public RPCSender { Q_OBJECT public: @@ -65,7 +65,7 @@ public: void updateControls(); void updateDropdown(); - bool animStep(float64 dt); + void step_state(uint64 ms, bool timer); void showSaveMsgFile(); void close(); @@ -184,6 +184,8 @@ private: QPoint _lastAction, _lastMouseMovePos; bool _ignoringDropdown; + Animation _a_state; + enum ControlsState { ControlsShowing, ControlsShown, diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index b1c6ea31a..3368d9f3d 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -2669,7 +2669,7 @@ OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewTyp , _scroll(this, st::historyScroll, false) , _inner(this, &_scroll, peer, type) , _noDropResizeIndex(false) -, _a_show(animFunc(this, &OverviewWidget::animStep_show)) +, _a_show(animation(this, &OverviewWidget::step_show)) , _scrollSetAfterShow(0) , _scrollDelta(0) , _selCount(0) @@ -2946,15 +2946,13 @@ void OverviewWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimT _inner.activate(); } -bool OverviewWidget::animStep_show(float64 ms) { +void OverviewWidget::step_show(float64 ms, bool timer) { float64 dt = ms / st::slideDuration; - bool res = true; if (dt >= 1) { _a_show.stop(); _sideShadow.setVisible(cWideMode()); _topShadow.show(); - res = false; a_coordUnder.finish(); a_coordOver.finish(); a_shadow.finish(); @@ -2969,9 +2967,10 @@ bool OverviewWidget::animStep_show(float64 ms) { a_coordOver.update(dt, st::slideFunction); a_shadow.update(dt, st::slideFunction); } - update(); - App::main()->topBar()->update(); - return res; + if (timer) { + update(); + App::main()->topBar()->update(); + } } void OverviewWidget::updateWideMode() { diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index ac11901bb..5955272e4 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -307,7 +307,7 @@ public: void fastShow(bool back = false, int32 lastScrollTop = -1); void animShow(const QPixmap &oldAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false, int32 lastScrollTop = -1); - bool animStep_show(float64 ms); + void step_show(float64 ms, bool timer); void updateWideMode(); void doneShow(); diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index 5e97dc7a2..bb7bea235 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -30,7 +30,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "gui/text.h" PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent) -, _a_show(animFunc(this, &PasscodeWidget::animStep_show)) +, _a_show(animation(this, &PasscodeWidget::step_show)) , _passcode(this, st::passcodeInput) , _submit(this, lang(lng_passcode_submit), st::passcodeSubmit) , _logout(this, lang(lng_passcode_logout)) { @@ -129,13 +129,11 @@ void PasscodeWidget::animShow(const QPixmap &bgAnimCache, bool back) { show(); } -bool PasscodeWidget::animStep_show(float64 ms) { +void PasscodeWidget::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(); @@ -151,11 +149,10 @@ bool PasscodeWidget::animStep_show(float64 ms) { a_coordOver.update(dt, st::slideFunction); a_shadow.update(dt, st::slideFunction); } - update(); - return res; + if (timer) update(); } -void PasscodeWidget::animStop_show() { +void PasscodeWidget::stop_show() { _a_show.stop(); } diff --git a/Telegram/SourceFiles/passcodewidget.h b/Telegram/SourceFiles/passcodewidget.h index 6d824bff4..9fe8cbb54 100644 --- a/Telegram/SourceFiles/passcodewidget.h +++ b/Telegram/SourceFiles/passcodewidget.h @@ -34,8 +34,8 @@ public: void setInnerFocus(); void animShow(const QPixmap &bgAnimCache, bool back = false); - bool animStep_show(float64 ms); - void animStop_show(); + void step_show(float64 ms, bool timer); + void stop_show(); ~PasscodeWidget(); diff --git a/Telegram/SourceFiles/playerwidget.cpp b/Telegram/SourceFiles/playerwidget.cpp index 5a76d3f51..44adf5884 100644 --- a/Telegram/SourceFiles/playerwidget.cpp +++ b/Telegram/SourceFiles/playerwidget.cpp @@ -41,7 +41,7 @@ PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent) , _downCoord(0) , _downFrequency(AudioVoiceMsgFrequency) , _downProgress(0.) -, _stateAnim(animFunc(this, &PlayerWidget::stateStep)) +, _a_state(animation(this, &PlayerWidget::step_state)) , _msgmigrated(false) , _index(-1) , _migrated(0) @@ -54,7 +54,7 @@ PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent) , _loaded(0) , a_progress(0., 0.) , a_loadProgress(0., 0.) -, _progressAnim(animFunc(this, &PlayerWidget::progressStep)) +, _a_progress(animation(this, &PlayerWidget::step_progress)) , _sideShadow(this, st::shadowColor) { resize(st::wndMinWidth, st::playerHeight); setMouseTracking(true); @@ -236,7 +236,7 @@ void PlayerWidget::updateOverState(OverState newState) { if (_over != OverNone) { _stateAnimations.remove(_over); _stateAnimations[-_over] = getms() - ((1. - _stateHovers[_over]) * st::playerDuration); - if (!_stateAnim.animating()) _stateAnim.start(); + if (!_a_state.animating()) _a_state.start(); } else { result = false; } @@ -244,7 +244,7 @@ void PlayerWidget::updateOverState(OverState newState) { if (newState != OverNone) { _stateAnimations.remove(-_over); _stateAnimations[_over] = getms() - (_stateHovers[_over] * st::playerDuration); - if (!_stateAnim.animating()) _stateAnim.start(); + if (!_a_state.animating()) _a_state.start(); setCursor(style::cur_pointer); } else { setCursor(style::cur_default); @@ -375,9 +375,7 @@ bool PlayerWidget::seekingSong(const SongMsgId &song) const { return (_down == OverPlayback) && (song == _song); } -bool PlayerWidget::stateStep(float64 msc) { - bool result = false; - uint64 ms = getms(); +void PlayerWidget::step_state(uint64 ms, bool timer) { for (StateAnimations::iterator i = _stateAnimations.begin(); i != _stateAnimations.cend();) { int32 over = qAbs(i.key()); updateOverRect(OverState(over)); @@ -391,7 +389,9 @@ bool PlayerWidget::stateStep(float64 msc) { ++i; } } - return !_stateAnimations.isEmpty(); + if (_stateAnimations.isEmpty()) { + _a_state.stop(); + } } void PlayerWidget::mouseMoveEvent(QMouseEvent *e) { @@ -462,7 +462,7 @@ void PlayerWidget::mouseReleaseEvent(QMouseEvent *e) { _showPause = true; a_progress = anim::fvalue(_downProgress, _downProgress); - _progressAnim.stop(); + _a_progress.stop(); } update(); } else if (_down == OverClose && _over == OverClose) { @@ -573,19 +573,17 @@ void PlayerWidget::resizeEvent(QResizeEvent *e) { update(); } -bool PlayerWidget::progressStep(float64 ms) { +void PlayerWidget::step_progress(float64 ms, bool timer) { float64 dt = ms / (2 * AudioVoiceMsgUpdateView); - bool res = true; if (_duration && dt >= 1) { + _a_progress.stop(); a_progress.finish(); a_loadProgress.finish(); - res = false; } else { a_progress.update(qMin(dt, 1.), anim::linear); a_loadProgress.update(1. - (st::radialDuration / (st::radialDuration + ms)), anim::linear); } - rtlupdate(_playbackRect); - return res; + if (timer) rtlupdate(_playbackRect); } void PlayerWidget::updateState() { @@ -668,11 +666,11 @@ void PlayerWidget::updateState(SongMsgId playing, AudioPlayerState playingState, if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) { a_progress.start(progress); a_loadProgress.start(loadProgress); - _progressAnim.start(); + _a_progress.start(); } else { a_progress = anim::fvalue(progress, progress); a_loadProgress = anim::fvalue(loadProgress, loadProgress); - _progressAnim.stop(); + _a_progress.stop(); } _position = position; _duration = duration; @@ -683,11 +681,11 @@ void PlayerWidget::updateState(SongMsgId playing, AudioPlayerState playingState, if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) { a_progress.start(progress); a_loadProgress.start(loadProgress); - _progressAnim.start(); + _a_progress.start(); } else { a_progress = anim::fvalue(progress, progress); a_loadProgress = anim::fvalue(loadProgress, loadProgress); - _progressAnim.stop(); + _a_progress.stop(); } _position = position; _duration = duration; diff --git a/Telegram/SourceFiles/playerwidget.h b/Telegram/SourceFiles/playerwidget.h index fefac68ac..e854ffee7 100644 --- a/Telegram/SourceFiles/playerwidget.h +++ b/Telegram/SourceFiles/playerwidget.h @@ -43,8 +43,8 @@ public: void nextPressed(); void stopPressed(); - bool progressStep(float64 ms); - bool stateStep(float64 ms); + void step_progress(float64 ms, bool timer); + void step_state(uint64 ms, bool timer); void updateState(SongMsgId playing, AudioPlayerState playingState, int64 playingPosition, int64 playingDuration, int32 playingFrequency); void updateState(); @@ -97,7 +97,7 @@ private: float64 _stateHovers[OverStateCount]; typedef QMap StateAnimations; StateAnimations _stateAnimations; - Animation _stateAnim; + Animation _a_state; SongMsgId _song; bool _msgmigrated; @@ -114,7 +114,7 @@ private: int32 _loaded; anim::fvalue a_progress, a_loadProgress; - Animation _progressAnim; + Animation _a_progress; PlainShadow _sideShadow; diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index 3beede857..c8bc70cb3 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -67,7 +67,8 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData , _aboutTop(0) , _aboutHeight(0) -, a_photo(0) +, a_photoOver(0) +, _a_photo(animation(this, &ProfileInner::step_photo)) , _photoOver(false) // migrate to megagroup @@ -766,11 +767,11 @@ void ProfileInner::paintEvent(QPaintEvent *e) { if (_photoLink || _peerUser || (_peerChat && !_peerChat->canEdit()) || (_peerChannel && !_amCreator)) { p.drawPixmap(_left, top, _peer->photo->pix(st::profilePhotoSize)); } else { - if (a_photo.current() < 1) { + if (a_photoOver.current() < 1) { p.drawPixmap(QPoint(_left, top), App::sprite(), st::setPhotoImg); } - if (a_photo.current() > 0) { - p.setOpacity(a_photo.current()); + if (a_photoOver.current() > 0) { + p.setOpacity(a_photoOver.current()); p.drawPixmap(QPoint(_left, top), App::sprite(), st::setOverPhotoImg); p.setOpacity(1); } @@ -990,8 +991,8 @@ void ProfileInner::mouseMoveEvent(QMouseEvent *e) { if (photoOver != _photoOver) { _photoOver = photoOver; if (!_photoLink && ((_peerChat && _peerChat->canEdit()) || (_peerChannel && _amCreator))) { - a_photo.start(_photoOver ? 1 : 0); - anim::start(this); + a_photoOver.start(_photoOver ? 1 : 0); + _a_photo.start(); } } if (!_photoLink && (_peerUser || (_peerChat && !_peerChat->canEdit()) || (_peerChannel && !_amCreator))) { @@ -1424,17 +1425,15 @@ void ProfileInner::onCopyUsername() { QApplication::clipboard()->setText('@' + _peerUser->username); } -bool ProfileInner::animStep(float64 ms) { +void ProfileInner::step_photo(float64 ms, bool timer) { float64 dt = ms / st::setPhotoDuration; - bool res = true; if (dt >= 1) { - res = false; - a_photo.finish(); + _a_photo.stop(); + a_photoOver.finish(); } else { - a_photo.update(dt, anim::linear); + a_photoOver.update(dt, anim::linear); } - update(_left, st::profilePadding.top(), st::setPhotoSize, st::setPhotoSize); - return res; + if (timer) update(_left, st::profilePadding.top(), st::setPhotoSize, st::setPhotoSize); } PeerData *ProfileInner::peer() const { @@ -1723,7 +1722,7 @@ QString ProfileInner::overviewLinkText(int32 type, int32 count) { ProfileWidget::ProfileWidget(QWidget *parent, PeerData *peer) : TWidget(parent) , _scroll(this, st::setScroll) , _inner(this, &_scroll, peer) -, _a_show(animFunc(this, &ProfileWidget::animStep_show)) +, _a_show(animation(this, &ProfileWidget::step_show)) , _sideShadow(this, st::shadowColor) , _topShadow(this, st::shadowColor) , _inGrab(false) { @@ -1857,15 +1856,13 @@ void ProfileWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo _inner.setFocus(); } -bool ProfileWidget::animStep_show(float64 ms) { +void ProfileWidget::step_show(float64 ms, bool timer) { float64 dt = ms / st::slideDuration; - bool res = true; if (dt >= 1) { _a_show.stop(); _sideShadow.setVisible(cWideMode()); _topShadow.show(); - res = false; a_coordUnder.finish(); a_coordOver.finish(); a_shadow.finish(); @@ -1882,9 +1879,10 @@ bool ProfileWidget::animStep_show(float64 ms) { a_coordOver.update(dt, st::slideFunction); a_shadow.update(dt, st::slideFunction); } - update(); - App::main()->topBar()->update(); - return res; + if (timer) { + update(); + App::main()->topBar()->update(); + } } void ProfileWidget::updateOnlineDisplay() { diff --git a/Telegram/SourceFiles/profilewidget.h b/Telegram/SourceFiles/profilewidget.h index d960e501d..611f8a7a9 100644 --- a/Telegram/SourceFiles/profilewidget.h +++ b/Telegram/SourceFiles/profilewidget.h @@ -21,7 +21,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #pragma once class ProfileWidget; -class ProfileInner : public TWidget, public RPCSender, public Animated { +class ProfileInner : public TWidget, public RPCSender { Q_OBJECT public: @@ -44,7 +44,7 @@ public: void resizeEvent(QResizeEvent *e); void contextMenuEvent(QContextMenuEvent *e); - bool animStep(float64 ms); + void step_photo(float64 ms, bool timer); PeerData *peer() const; bool allMediaShown() const; @@ -162,7 +162,8 @@ private: Text _about; int32 _aboutTop, _aboutHeight; - anim::fvalue a_photo; + anim::fvalue a_photoOver; + Animation _a_photo; bool _photoOver; QString _errorText; @@ -235,7 +236,7 @@ public: int32 lastScrollTop() const; void animShow(const QPixmap &oldAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false, int32 lastScrollTop = -1); - bool animStep_show(float64 ms); + void step_show(float64 ms, bool timer); void updateOnlineDisplay(); void updateOnlineDisplayTimer(); diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp index dd678769f..41c2be4ea 100644 --- a/Telegram/SourceFiles/settingswidget.cpp +++ b/Telegram/SourceFiles/settingswidget.cpp @@ -111,96 +111,99 @@ bool scaleIs(DBIScale scale) { return cRealScale() == scale || (cRealScale() == dbisAuto && cScreenScale() == scale); } -SettingsInner::SettingsInner(SettingsWidget *parent) : QWidget(parent), - _self(App::self()), +SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent) +, _self(App::self()) - // profile - _nameCache(self() ? self()->name : QString()), - _uploadPhoto(this, lang(lng_settings_upload), st::btnSetUpload), - _cancelPhoto(this, lang(lng_cancel)), _nameOver(false), _photoOver(false), a_photo(0), +// profile +, _nameCache(self() ? self()->name : QString()) +, _uploadPhoto(this, lang(lng_settings_upload), st::btnSetUpload) +, _cancelPhoto(this, lang(lng_cancel)) +, _nameOver(false) +, _photoOver(false) +, a_photoOver(0) +, _a_photo(animation(this, &SettingsInner::step_photo)) - // contact info - _phoneText(self() ? App::formatPhone(self()->phone) : QString()), - _chooseUsername(this, (self() && !self()->username.isEmpty()) ? ('@' + self()->username) : lang(lng_settings_choose_username)), +// contact info +, _phoneText(self() ? App::formatPhone(self()->phone) : QString()) +, _chooseUsername(this, (self() && !self()->username.isEmpty()) ? ('@' + self()->username) : lang(lng_settings_choose_username)) - // notifications - _desktopNotify(this, lang(lng_settings_desktop_notify), cDesktopNotify()), - _senderName(this, lang(lng_settings_show_name), cNotifyView() <= dbinvShowName), - _messagePreview(this, lang(lng_settings_show_preview), cNotifyView() <= dbinvShowPreview), - _windowsNotifications(this, lang(lng_settings_use_windows), cWindowsNotifications()), - _soundNotify(this, lang(lng_settings_sound_notify), cSoundNotify()), - _includeMuted(this, lang(lng_settings_include_muted), cIncludeMuted()), +// notifications +, _desktopNotify(this, lang(lng_settings_desktop_notify), cDesktopNotify()) +, _senderName(this, lang(lng_settings_show_name), cNotifyView() <= dbinvShowName) +, _messagePreview(this, lang(lng_settings_show_preview), cNotifyView() <= dbinvShowPreview) +, _windowsNotifications(this, lang(lng_settings_use_windows), cWindowsNotifications()) +, _soundNotify(this, lang(lng_settings_sound_notify), cSoundNotify()) +, _includeMuted(this, lang(lng_settings_include_muted), cIncludeMuted()) - // general - _changeLanguage(this, lang(lng_settings_change_lang)), - #ifndef TDESKTOP_DISABLE_AUTOUPDATE - _autoUpdate(this, lang(lng_settings_auto_update), cAutoUpdate()), - _checkNow(this, lang(lng_settings_check_now)), - _restartNow(this, lang(lng_settings_update_now)), - #endif +// general +, _changeLanguage(this, lang(lng_settings_change_lang)) +#ifndef TDESKTOP_DISABLE_AUTOUPDATE +, _autoUpdate(this, lang(lng_settings_auto_update), cAutoUpdate()) +, _checkNow(this, lang(lng_settings_check_now)) +, _restartNow(this, lang(lng_settings_update_now)) +#endif - _supportTray(cSupportTray()), - _workmodeTray(this, lang(lng_settings_workmode_tray), (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray)), - _workmodeWindow(this, lang(lng_settings_workmode_window), (cWorkMode() == dbiwmWindowOnly || cWorkMode() == dbiwmWindowAndTray)), +, _supportTray(cSupportTray()) +, _workmodeTray(this, lang(lng_settings_workmode_tray), (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray)) +, _workmodeWindow(this, lang(lng_settings_workmode_window), (cWorkMode() == dbiwmWindowOnly || cWorkMode() == dbiwmWindowAndTray)) - _autoStart(this, lang(lng_settings_auto_start), cAutoStart()), - _startMinimized(this, lang(lng_settings_start_min), cStartMinimized()), - _sendToMenu(this, lang(lng_settings_add_sendto), cSendToMenu()), +, _autoStart(this, lang(lng_settings_auto_start), cAutoStart()) +, _startMinimized(this, lang(lng_settings_start_min), cStartMinimized()) +, _sendToMenu(this, lang(lng_settings_add_sendto), cSendToMenu()) - _dpiAutoScale(this, lng_settings_scale_auto(lt_cur, scaleLabel(cScreenScale())), (cConfigScale() == dbisAuto)), - _dpiSlider(this, st::dpiSlider, dbisScaleCount - 1, cEvalScale(cConfigScale()) - 1), - _dpiWidth1(st::dpiFont1->width(scaleLabel(dbisOne))), - _dpiWidth2(st::dpiFont2->width(scaleLabel(dbisOneAndQuarter))), - _dpiWidth3(st::dpiFont3->width(scaleLabel(dbisOneAndHalf))), - _dpiWidth4(st::dpiFont4->width(scaleLabel(dbisTwo))), +, _dpiAutoScale(this, lng_settings_scale_auto(lt_cur, scaleLabel(cScreenScale())), (cConfigScale() == dbisAuto)) +, _dpiSlider(this, st::dpiSlider, dbisScaleCount - 1, cEvalScale(cConfigScale()) - 1) +, _dpiWidth1(st::dpiFont1->width(scaleLabel(dbisOne))) +, _dpiWidth2(st::dpiFont2->width(scaleLabel(dbisOneAndQuarter))) +, _dpiWidth3(st::dpiFont3->width(scaleLabel(dbisOneAndHalf))) +, _dpiWidth4(st::dpiFont4->width(scaleLabel(dbisTwo))) - // chat options - _replaceEmojis(this, lang(lng_settings_replace_emojis), cReplaceEmojis()), - _viewEmojis(this, lang(lng_settings_view_emojis)), - _stickers(this, lang(lng_stickers_you_have)), +// chat options +, _replaceEmojis(this, lang(lng_settings_replace_emojis), cReplaceEmojis()) +, _viewEmojis(this, lang(lng_settings_view_emojis)) +, _stickers(this, lang(lng_stickers_you_have)) - _enterSend(this, qsl("send_key"), 0, lang(lng_settings_send_enter), !cCtrlEnter()), - _ctrlEnterSend(this, qsl("send_key"), 1, lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_settings_send_cmdenter : lng_settings_send_ctrlenter), cCtrlEnter()), +, _enterSend(this, qsl("send_key"), 0, lang(lng_settings_send_enter), !cCtrlEnter()) +, _ctrlEnterSend(this, qsl("send_key"), 1, lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_settings_send_cmdenter : lng_settings_send_ctrlenter), cCtrlEnter()) - _dontAskDownloadPath(this, lang(lng_download_path_dont_ask), !cAskDownloadPath()), - _downloadPathWidth(st::linkFont->width(lang(lng_download_path_label)) + st::linkFont->spacew), - _downloadPathEdit(this, cDownloadPath().isEmpty() ? lang(lng_download_path_default) : ((cDownloadPath() == qsl("tmp")) ? lang(lng_download_path_temp) : st::linkFont->elided(QDir::toNativeSeparators(cDownloadPath()), st::setWidth - st::setVersionLeft - _downloadPathWidth))), - _downloadPathClear(this, lang(lng_download_path_clear)), - _tempDirClearingWidth(st::linkFont->width(lang(lng_download_path_clearing))), - _tempDirClearedWidth(st::linkFont->width(lang(lng_download_path_cleared))), - _tempDirClearFailedWidth(st::linkFont->width(lang(lng_download_path_clear_failed))), +, _dontAskDownloadPath(this, lang(lng_download_path_dont_ask), !cAskDownloadPath()) +, _downloadPathWidth(st::linkFont->width(lang(lng_download_path_label)) + st::linkFont->spacew) +, _downloadPathEdit(this, cDownloadPath().isEmpty() ? lang(lng_download_path_default) : ((cDownloadPath() == qsl("tmp")) ? lang(lng_download_path_temp) : st::linkFont->elided(QDir::toNativeSeparators(cDownloadPath()), st::setWidth - st::setVersionLeft - _downloadPathWidth))) +, _downloadPathClear(this, lang(lng_download_path_clear)) +, _tempDirClearingWidth(st::linkFont->width(lang(lng_download_path_clearing))) +, _tempDirClearedWidth(st::linkFont->width(lang(lng_download_path_cleared))) +, _tempDirClearFailedWidth(st::linkFont->width(lang(lng_download_path_clear_failed))) - // chat background - _backFromGallery(this, lang(lng_settings_bg_from_gallery)), - _backFromFile(this, lang(lng_settings_bg_from_file)), - _tileBackground(this, lang(lng_settings_bg_tile), cTileBackground()), - _needBackgroundUpdate(false), +// chat background +, _backFromGallery(this, lang(lng_settings_bg_from_gallery)) +, _backFromFile(this, lang(lng_settings_bg_from_file)) +, _tileBackground(this, lang(lng_settings_bg_tile), cTileBackground()) +, _needBackgroundUpdate(false) - // local storage - _localStorageClear(this, lang(lng_local_storage_clear)), - _localStorageHeight(1), - _storageClearingWidth(st::linkFont->width(lang(lng_local_storage_clearing))), - _storageClearedWidth(st::linkFont->width(lang(lng_local_storage_cleared))), - _storageClearFailedWidth(st::linkFont->width(lang(lng_local_storage_clear_failed))), +// local storage +, _localStorageClear(this, lang(lng_local_storage_clear)) +, _localStorageHeight(1) +, _storageClearingWidth(st::linkFont->width(lang(lng_local_storage_clearing))) +, _storageClearedWidth(st::linkFont->width(lang(lng_local_storage_cleared))) +, _storageClearFailedWidth(st::linkFont->width(lang(lng_local_storage_clear_failed))) - // advanced - _passcodeEdit(this, lang(cHasPasscode() ? lng_passcode_change : lng_passcode_turn_on)), - _passcodeTurnOff(this, lang(lng_passcode_turn_off)), - _autoLock(this, (cAutoLock() % 3600) ? lng_passcode_autolock_minutes(lt_count, cAutoLock() / 60) : lng_passcode_autolock_hours(lt_count, cAutoLock() / 3600)), - _autoLockText(lang(psIdleSupported() ? lng_passcode_autolock_away : lng_passcode_autolock_inactive) + ' '), - _autoLockWidth(st::linkFont->width(_autoLockText)), - _passwordEdit(this, lang(lng_cloud_password_set)), - _passwordTurnOff(this, lang(lng_passcode_turn_off)), - _hasPasswordRecovery(false), - _connectionType(this, lang(lng_connection_auto_connecting)), - _connectionTypeText(lang(lng_connection_type) + ' '), - _connectionTypeWidth(st::linkFont->width(_connectionTypeText)), - _showSessions(this, lang(lng_settings_show_sessions)), - _askQuestion(this, lang(lng_settings_ask_question)), - _telegramFAQ(this, lang(lng_settings_faq)), - _logOut(this, lang(lng_settings_logout), st::btnLogout), - _supportGetRequest(0) -{ +// advanced +, _passcodeEdit(this, lang(cHasPasscode() ? lng_passcode_change : lng_passcode_turn_on)) +, _passcodeTurnOff(this, lang(lng_passcode_turn_off)) +, _autoLock(this, (cAutoLock() % 3600) ? lng_passcode_autolock_minutes(lt_count, cAutoLock() / 60) : lng_passcode_autolock_hours(lt_count, cAutoLock() / 3600)) +, _autoLockText(lang(psIdleSupported() ? lng_passcode_autolock_away : lng_passcode_autolock_inactive) + ' ') +, _autoLockWidth(st::linkFont->width(_autoLockText)) +, _passwordEdit(this, lang(lng_cloud_password_set)) +, _passwordTurnOff(this, lang(lng_passcode_turn_off)) +, _hasPasswordRecovery(false) +, _connectionType(this, lang(lng_connection_auto_connecting)) +, _connectionTypeText(lang(lng_connection_type) + ' ') +, _connectionTypeWidth(st::linkFont->width(_connectionTypeText)) +, _showSessions(this, lang(lng_settings_show_sessions)) +, _askQuestion(this, lang(lng_settings_ask_question)) +, _telegramFAQ(this, lang(lng_settings_faq)) +, _logOut(this, lang(lng_settings_logout), st::btnLogout) +, _supportGetRequest(0) { if (self()) { connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); connect(App::api(), SIGNAL(fullPeerUpdated(PeerData*)), this, SLOT(onFullPeerUpdated(PeerData*))); @@ -393,11 +396,11 @@ void SettingsInner::paintEvent(QPaintEvent *e) { if (_photoLink) { p.drawPixmap(_left, top, self()->photo->pix(st::setPhotoSize)); } else { - if (a_photo.current() < 1) { + if (a_photoOver.current() < 1) { p.drawPixmap(QPoint(_left, top), App::sprite(), st::setPhotoImg); } - if (a_photo.current() > 0) { - p.setOpacity(a_photo.current()); + if (a_photoOver.current() > 0) { + p.setOpacity(a_photoOver.current()); p.drawPixmap(QPoint(_left, top), App::sprite(), st::setOverPhotoImg); p.setOpacity(1); } @@ -816,8 +819,8 @@ void SettingsInner::mouseMoveEvent(QMouseEvent *e) { if (photoOver != _photoOver) { _photoOver = photoOver; if (!_photoLink) { - a_photo.start(_photoOver ? 1 : 0); - anim::start(this); + a_photoOver.start(_photoOver ? 1 : 0); + _a_photo.start(); } } @@ -845,17 +848,15 @@ void SettingsInner::mousePressEvent(QMouseEvent *e) { void SettingsInner::contextMenuEvent(QContextMenuEvent *e) { } -bool SettingsInner::animStep(float64 ms) { +void SettingsInner::step_photo(float64 ms, bool timer) { float64 dt = ms / st::setPhotoDuration; - bool res = true; if (dt >= 1) { - res = false; - a_photo.finish(); + _a_photo.stop(); + a_photoOver.finish(); } else { - a_photo.update(dt, anim::linear); + a_photoOver.update(dt, anim::linear); } - update(_left, st::setTop, st::setPhotoSize, st::setPhotoSize); - return res; + if (timer) update(_left, st::setTop, st::setPhotoSize, st::setPhotoSize); } void SettingsInner::updateSize(int32 newWidth) { @@ -1763,7 +1764,7 @@ void SettingsInner::onPhotoUpdateDone(PeerId peer) { } SettingsWidget::SettingsWidget(Window *parent) : TWidget(parent) -, _a_show(animFunc(this, &SettingsWidget::animStep_show)) +, _a_show(animation(this, &SettingsWidget::step_show)) , _scroll(this, st::setScroll) , _inner(this) , _close(this, st::setClose) { @@ -1800,13 +1801,11 @@ void SettingsWidget::animShow(const QPixmap &bgAnimCache, bool back) { show(); } -bool SettingsWidget::animStep_show(float64 ms) { +void SettingsWidget::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(); @@ -1822,11 +1821,10 @@ bool SettingsWidget::animStep_show(float64 ms) { a_coordOver.update(dt, st::slideFunction); a_shadow.update(dt, st::slideFunction); } - update(); - return res; + if (timer) update(); } -void SettingsWidget::animStop_show() { +void SettingsWidget::stop_show() { _a_show.stop(); } diff --git a/Telegram/SourceFiles/settingswidget.h b/Telegram/SourceFiles/settingswidget.h index 1a8dcdfac..52f8ff153 100644 --- a/Telegram/SourceFiles/settingswidget.h +++ b/Telegram/SourceFiles/settingswidget.h @@ -57,7 +57,7 @@ private: }; -class SettingsInner : public QWidget, public RPCSender, public Animated { +class SettingsInner : public TWidget, public RPCSender { Q_OBJECT public: @@ -71,7 +71,7 @@ public: void mousePressEvent(QMouseEvent *e); void contextMenuEvent(QContextMenuEvent *e); - bool animStep(float64 ms); + void step_photo(float64 ms, bool timer); void updateSize(int32 newWidth); @@ -203,7 +203,8 @@ private: FlatButton _uploadPhoto; LinkButton _cancelPhoto; bool _nameOver, _photoOver; - anim::fvalue a_photo; + anim::fvalue a_photoOver; + Animation _a_photo; QString _errorText; @@ -315,8 +316,8 @@ public: void updateWideMode(); void animShow(const QPixmap &bgAnimCache, bool back = false); - bool animStep_show(float64 ms); - void animStop_show(); + void step_show(float64 ms, bool timer); + void stop_show(); void updateOnlineDisplay(); void updateConnectionType(); diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index aa28660a2..6e65c5757 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -64,7 +64,6 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "gui/flatbutton.h" #include "gui/boxshadow.h" #include "gui/popupmenu.h" -#include "gui/switcher.h" #include "gui/scrollarea.h" #include "gui/images.h" #include "gui/text.h" diff --git a/Telegram/SourceFiles/sysbuttons.cpp b/Telegram/SourceFiles/sysbuttons.cpp index d84b6cdb7..5aad22596 100644 --- a/Telegram/SourceFiles/sysbuttons.cpp +++ b/Telegram/SourceFiles/sysbuttons.cpp @@ -28,8 +28,12 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "application.h" #include "autoupdater.h" -SysBtn::SysBtn(QWidget *parent, const style::sysButton &st, const QString &text) : Button(parent), -_st(st), a_color(_st.color->c), _overLevel(0), _text(text) { +SysBtn::SysBtn(QWidget *parent, const style::sysButton &st, const QString &text) : Button(parent) +, _st(st) +, a_color(_st.color->c) +, _a_color(animation(this, &SysBtn::step_color)) +, _overLevel(0) +, _text(text) { int32 w = _st.size.width() + (_text.isEmpty() ? 0 : ((_st.size.width() - _st.img.pxWidth()) / 2 + st::titleTextButton.font->width(_text))); resize(w, _st.size.height()); setCursor(style::cur_default); @@ -51,11 +55,11 @@ void SysBtn::onStateChange(int oldState, ButtonStateChangeSource source) { a_color.start((_state & StateOver ? _st.overColor : _st.color)->c); if (source == ButtonByUser || source == ButtonByPress) { - anim::stop(this); + _a_color.stop(); a_color.finish(); update(); } else { - anim::start(this); + _a_color.start(); } } @@ -96,17 +100,15 @@ HitTestType SysBtn::hitTest(const QPoint &p) const { return HitTestNone; } -bool SysBtn::animStep(float64 ms) { +void SysBtn::step_color(float64 ms, bool timer) { float64 dt = ms / _st.duration; - bool res = true; if (dt >= 1) { + _a_color.stop(); a_color.finish(); - res = false; } else { a_color.update(dt, anim::linear); } - update(); - return res; + if (timer) update(); } MinimizeBtn::MinimizeBtn(QWidget *parent, Window *window) : SysBtn(parent, st::sysMin), wnd(window) { diff --git a/Telegram/SourceFiles/sysbuttons.h b/Telegram/SourceFiles/sysbuttons.h index 4b4a616a9..de7c3f93f 100644 --- a/Telegram/SourceFiles/sysbuttons.h +++ b/Telegram/SourceFiles/sysbuttons.h @@ -26,7 +26,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org class Window; -class SysBtn : public Button, public Animated { +class SysBtn : public Button { Q_OBJECT public: @@ -41,7 +41,7 @@ public: void setOverLevel(float64 level); - bool animStep(float64 ms); + void step_color(float64 ms, bool timer); public slots: @@ -51,6 +51,8 @@ protected: style::sysButton _st; anim::cvalue a_color; + Animation _a_color; + float64 _overLevel; QString _text; diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp index 36c72feb6..58a7c5506 100644 --- a/Telegram/SourceFiles/title.cpp +++ b/Telegram/SourceFiles/title.cpp @@ -49,23 +49,23 @@ void TitleHider::setLevel(float64 level) { update(); } -TitleWidget::TitleWidget(Window *window) - : QWidget(window) - , wnd(window) - , hideLevel(0) - , hider(0) - , _back(this, st::titleBackButton, lang(lng_menu_back)) - , _cancel(this, lang(lng_cancel), st::titleTextButton) - , _settings(this, lang(lng_menu_settings), st::titleTextButton) - , _contacts(this, lang(lng_menu_contacts), st::titleTextButton) - , _about(this, lang(lng_menu_about), st::titleTextButton) - , _lock(this, window) - , _update(this, window, lang(lng_menu_update)) - , _minimize(this, window) - , _maximize(this, window) - , _restore(this, window) - , _close(this, window) - , lastMaximized(!(window->windowState() & Qt::WindowMaximized)) +TitleWidget::TitleWidget(Window *window) : TWidget(window) +, wnd(window) +, hideLevel(0) +, hider(0) +, _back(this, st::titleBackButton, lang(lng_menu_back)) +, _cancel(this, lang(lng_cancel), st::titleTextButton) +, _settings(this, lang(lng_menu_settings), st::titleTextButton) +, _contacts(this, lang(lng_menu_contacts), st::titleTextButton) +, _about(this, lang(lng_menu_about), st::titleTextButton) +, _lock(this, window) +, _update(this, window, lang(lng_menu_update)) +, _minimize(this, window) +, _maximize(this, window) +, _restore(this, window) +, _close(this, window) +, _a_update(animation(this, &TitleWidget::step_update)) +, lastMaximized(!(window->windowState() & Qt::WindowMaximized)) { setGeometry(0, 0, wnd->width(), st::titleHeight); setAttribute(Qt::WA_OpaquePaintEvent); @@ -110,11 +110,10 @@ void TitleWidget::paintEvent(QPaintEvent *e) { } } -bool TitleWidget::animStep(float64 ms) { +void TitleWidget::step_update(float64 ms, bool timer) { float64 phase = sin(M_PI_2 * (ms / st::updateBlinkDuration)); if (phase < 0) phase = -phase; _update.setOverLevel(phase); - return true; } void TitleWidget::setHideLevel(float64 level) { @@ -332,7 +331,7 @@ void TitleWidget::showUpdateBtn() { _restore.hide(); _maximize.hide(); _close.hide(); - anim::start(this); + _a_update.start(); } else { _update.hide(); if (cPlatform() == dbipWindows) { @@ -340,7 +339,7 @@ void TitleWidget::showUpdateBtn() { maximizedChanged(lastMaximized, true); _close.show(); } - anim::stop(this); + _a_update.stop(); } resizeEvent(0); update(); diff --git a/Telegram/SourceFiles/title.h b/Telegram/SourceFiles/title.h index 1dc420a74..ff85f2528 100644 --- a/Telegram/SourceFiles/title.h +++ b/Telegram/SourceFiles/title.h @@ -39,7 +39,7 @@ private: }; -class TitleWidget : public QWidget, public Animated { +class TitleWidget : public TWidget { Q_OBJECT public: @@ -61,7 +61,7 @@ public: void setHideLevel(float64 level); - bool animStep(float64 ms); + void step_update(float64 ms, bool timer); ~TitleWidget(); @@ -97,6 +97,8 @@ private: RestoreBtn _restore; CloseBtn _close; + Animation _a_update; + bool lastMaximized; QPixmap _counter; diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index aa47b1f77..37f4d41be 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -82,9 +82,10 @@ NotifyWindow::NotifyWindow(HistoryItem *msg, int32 x, int32 y, int32 fwdCount) : , posDuration(st::notifyFastAnim) , hiding(false) , _index(0) -, aOpacity(0) -, aOpacityFunc(st::notifyFastAnimFunc) -, aY(y + st::notifyHeight + st::notifyDeltaY) { +, a_opacity(0) +, a_y(y + st::notifyHeight + st::notifyDeltaY) +, a_func(anim::linear) +, _a_appearance(animation(this, &NotifyWindow::step_appearance)) { updateNotifyDisplay(); @@ -99,19 +100,19 @@ NotifyWindow::NotifyWindow(HistoryItem *msg, int32 x, int32 y, int32 fwdCount) : close.move(st::notifyWidth - st::notifyClose.width - st::notifyClosePos.x(), st::notifyClosePos.y()); close.show(); - aY.start(y); - setGeometry(x, aY.current(), st::notifyWidth, st::notifyHeight); + a_y.start(y); + setGeometry(x, a_y.current(), st::notifyWidth, st::notifyHeight); - aOpacity.start(1); + a_opacity.start(1); setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); setAttribute(Qt::WA_MacAlwaysShowToolWindow); show(); - setWindowOpacity(aOpacity.current()); + setWindowOpacity(a_opacity.current()); alphaDuration = posDuration = st::notifyFastAnim; - anim::start(this); + _a_appearance.start(); checkLastInput(); } @@ -140,11 +141,11 @@ void NotifyWindow::moveTo(int32 x, int32 y, int32 index) { if (index >= 0) { _index = index; } - move(x, aY.current()); - aY.start(y); - aOpacity.restart(); + move(x, a_y.current()); + a_y.start(y); + a_opacity.restart(); posDuration = st::notifyFastAnim; - anim::start(this); + _a_appearance.start(); } void NotifyWindow::updateNotifyDisplay() { @@ -262,7 +263,7 @@ void NotifyWindow::unlinkHistoryAndNotify() { void NotifyWindow::unlinkHistory(History *hist) { if (!hist || hist == history) { - animHide(st::notifyFastAnim, st::notifyFastAnimFunc); + animHide(st::notifyFastAnim, anim::linear); history = 0; item = 0; } @@ -309,22 +310,22 @@ void NotifyWindow::paintEvent(QPaintEvent *e) { void NotifyWindow::animHide(float64 duration, anim::transition func) { if (!history) return; alphaDuration = duration; - aOpacityFunc = func; - aOpacity.start(0); - aY.restart(); + a_func = func; + a_opacity.start(0); + a_y.restart(); hiding = true; - anim::start(this); + _a_appearance.start(); } void NotifyWindow::stopHiding() { if (!history) return; alphaDuration = st::notifyFastAnim; - aOpacityFunc = st::notifyFastAnimFunc; - aOpacity.start(1); - aY.restart(); + a_func = anim::linear; + a_opacity.start(1); + a_y.restart(); hiding = false; hideTimer.stop(); - anim::start(this); + _a_appearance.start(); } void NotifyWindow::hideByTimer() { @@ -332,25 +333,27 @@ void NotifyWindow::hideByTimer() { animHide(st::notifySlowHide, st::notifySlowHideFunc); } -bool NotifyWindow::animStep(float64 ms) { +void NotifyWindow::step_appearance(float64 ms, bool timer) { float64 dtAlpha = ms / alphaDuration, dtPos = ms / posDuration; if (dtAlpha >= 1) { - aOpacity.finish(); + a_opacity.finish(); if (hiding) { + _a_appearance.stop(); deleteLater(); + } else if (dtPos >= 1) { + _a_appearance.stop(); } } else { - aOpacity.update(dtAlpha, aOpacityFunc); + a_opacity.update(dtAlpha, a_func); } - setWindowOpacity(aOpacity.current()); + setWindowOpacity(a_opacity.current()); if (dtPos >= 1) { - aY.finish(); + a_y.finish(); } else { - aY.update(dtPos, anim::linear); + a_y.update(dtPos, anim::linear); } - move(x(), aY.current()); + move(x(), a_y.current()); update(); - return (dtAlpha < 1 || (!hiding && dtPos < 1)); } NotifyWindow::~NotifyWindow() { @@ -481,7 +484,7 @@ void Window::clearWidgets() { _passcode = 0; } if (settings) { - settings->animStop_show(); + settings->stop_show(); settings->hide(); settings->deleteLater(); settings->rpcInvalidate(); @@ -495,7 +498,7 @@ void Window::clearWidgets() { main = 0; } if (intro) { - intro->animStop_show(); + intro->stop_show(); intro->hide(); intro->deleteLater(); intro->rpcInvalidate(); @@ -524,7 +527,7 @@ void Window::clearPasscode() { QPixmap bg = grabInner(); - _passcode->animStop_show(); + _passcode->stop_show(); _passcode->hide(); _passcode->deleteLater(); _passcode = 0; @@ -544,7 +547,7 @@ void Window::setupPasscode(bool anim) { QPixmap bg = grabInner(); if (_passcode) { - _passcode->animStop_show(); + _passcode->stop_show(); _passcode->hide(); _passcode->deleteLater(); } @@ -686,7 +689,7 @@ void Window::showSettings() { QPixmap bg = grabInner(); if (intro) { - intro->animStop_show(); + intro->stop_show(); intro->hide(); } else if (main) { main->animStop_show(); @@ -703,7 +706,7 @@ void Window::hideSettings(bool fast) { if (!settings || _passcode) return; if (fast) { - settings->animStop_show(); + settings->stop_show(); settings->hide(); settings->deleteLater(); settings->rpcInvalidate(); @@ -716,7 +719,7 @@ void Window::hideSettings(bool fast) { } else { QPixmap bg = grabInner(); - settings->animStop_show(); + settings->stop_show(); settings->hide(); settings->deleteLater(); settings->rpcInvalidate(); diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index 2fed69a3b..e98da0f7c 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -59,7 +59,7 @@ private: }; -class NotifyWindow : public QWidget, public Animated { +class NotifyWindow : public TWidget { Q_OBJECT public: @@ -71,7 +71,7 @@ public: void mousePressEvent(QMouseEvent *e); void paintEvent(QPaintEvent *e); - bool animStep(float64 ms); + void step_appearance(float64 ms, bool timer); void animHide(float64 duration, anim::transition func); void startHiding(); void stopHiding(); @@ -111,9 +111,11 @@ private: QTimer hideTimer, inputTimer; bool hiding; int32 _index; - anim::fvalue aOpacity; - anim::transition aOpacityFunc; - anim::ivalue aY; + anim::fvalue a_opacity; + anim::transition a_func; + anim::ivalue a_y; + Animation _a_appearance; + ImagePtr peerPhoto; }; diff --git a/Telegram/Telegram.pro b/Telegram/Telegram.pro index 4ff133b83..5e704e1cc 100644 --- a/Telegram/Telegram.pro +++ b/Telegram/Telegram.pro @@ -138,7 +138,6 @@ SOURCES += \ ./SourceFiles/gui/style_core.cpp \ ./SourceFiles/gui/text.cpp \ ./SourceFiles/gui/twidget.cpp \ - ./SourceFiles/gui/switcher.cpp \ ./GeneratedFiles/lang_auto.cpp \ ./GeneratedFiles/style_auto.cpp \ ./GeneratedFiles/numbers.cpp \ @@ -231,7 +230,6 @@ HEADERS += \ ./SourceFiles/gui/style_core.h \ ./SourceFiles/gui/text.h \ ./SourceFiles/gui/twidget.h \ - ./SourceFiles/gui/switcher.h \ ./GeneratedFiles/lang_auto.h \ ./GeneratedFiles/style_auto.h \ ./GeneratedFiles/style_classes.h \ diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index 51fd57c4f..fd4bf6a95 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -389,10 +389,6 @@ true true - - true - true - true true @@ -651,10 +647,6 @@ true true - - true - true - true true @@ -939,10 +931,6 @@ true true - - true - true - true true @@ -1009,7 +997,6 @@ - @@ -1635,20 +1622,6 @@ $(QTDIR)\bin\moc.exe;%(FullPath) - - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing switcher.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl\Release\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui" "-fstdafx.h" "-f../../SourceFiles/gui/switcher.h" - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing switcher.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl_debug\Debug\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui" "-fstdafx.h" "-f../../SourceFiles/gui/switcher.h" - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing switcher.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl\Release\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui" "-fstdafx.h" "-f../../SourceFiles/gui/switcher.h" - $(QTDIR)\bin\moc.exe;%(FullPath) diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index 8363c7486..5f84eb8b0 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -609,18 +609,6 @@ Generated Files\Release - - gui - - - Generated Files\Deploy - - - Generated Files\Debug - - - Generated Files\Release - Source Files @@ -1129,9 +1117,6 @@ gui - - gui - Source Files diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index c93c1d170..aac8117ac 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -68,8 +68,6 @@ 07B604351B46A20900CA29FE /* moc_playerwidget.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07B604341B46A20900CA29FE /* moc_playerwidget.cpp */; }; 07BE850F1A2093C9008ACB9F /* localstorage.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07BE850D1A2093C9008ACB9F /* localstorage.cpp */; }; 07BE85121A20961F008ACB9F /* moc_localstorage.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07BE85111A20961F008ACB9F /* moc_localstorage.cpp */; }; - 07C4753B1967DF1C00CAAFE9 /* switcher.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07C475391967DF1C00CAAFE9 /* switcher.cpp */; }; - 07C4753F1967E37300CAAFE9 /* moc_switcher.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07C4753E1967E37300CAAFE9 /* moc_switcher.cpp */; }; 07C7596F1B1F7E0000662169 /* autoupdater.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07C7596D1B1F7E0000662169 /* autoupdater.cpp */; }; 07C759721B1F7E2800662169 /* moc_autoupdater.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07C759711B1F7E2800662169 /* moc_autoupdater.cpp */; }; 07CAACD81AEA64F00058E508 /* AudioUnit.framework in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 07CAACD71AEA64F00058E508 /* AudioUnit.framework */; }; @@ -315,9 +313,6 @@ 07C3AF27194336B90016CFF1 /* pspecific_mac_p.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pspecific_mac_p.h; path = SourceFiles/pspecific_mac_p.h; sourceTree = SOURCE_ROOT; }; 07C3AF2919433ABF0016CFF1 /* style_classes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = style_classes.txt; path = Resources/style_classes.txt; sourceTree = SOURCE_ROOT; }; 07C3AF2A19433ABF0016CFF1 /* style.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = style.txt; path = Resources/style.txt; sourceTree = SOURCE_ROOT; }; - 07C475391967DF1C00CAAFE9 /* switcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = switcher.cpp; path = SourceFiles/gui/switcher.cpp; sourceTree = SOURCE_ROOT; }; - 07C4753A1967DF1C00CAAFE9 /* switcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = switcher.h; path = SourceFiles/gui/switcher.h; sourceTree = SOURCE_ROOT; }; - 07C4753E1967E37300CAAFE9 /* moc_switcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_switcher.cpp; path = GeneratedFiles/Debug/moc_switcher.cpp; sourceTree = SOURCE_ROOT; }; 07C7596D1B1F7E0000662169 /* autoupdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = autoupdater.cpp; path = SourceFiles/autoupdater.cpp; sourceTree = SOURCE_ROOT; }; 07C7596E1B1F7E0000662169 /* autoupdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = autoupdater.h; path = SourceFiles/autoupdater.h; sourceTree = SOURCE_ROOT; }; 07C759711B1F7E2800662169 /* moc_autoupdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_autoupdater.cpp; path = GeneratedFiles/Debug/moc_autoupdater.cpp; sourceTree = SOURCE_ROOT; }; @@ -840,7 +835,6 @@ 6A510365F9F6367ECB0DB065 /* images.cpp */, 6E1859D714E4471E053D90C9 /* scrollarea.cpp */, 420A06A32B66D250142B4B6D /* style_core.cpp */, - 07C475391967DF1C00CAAFE9 /* switcher.cpp */, 135FD3715BFDC50AD7B00E04 /* text.cpp */, BB1602EA641643DE565005B1 /* twidget.cpp */, 85FABD67716E36CD8B3CA4FA /* animation.h */, @@ -858,7 +852,6 @@ 0F8FFD87AEBAC448568570DC /* images.h */, 83A36F229E897566E011B79E /* scrollarea.h */, 0FC38EE7F29EF895925A2C49 /* style_core.h */, - 07C4753A1967DF1C00CAAFE9 /* switcher.h */, 6E8FD0ED1B60D43929944CD2 /* text.h */, 507CCEEC4CBA3E3BD6EEDED1 /* twidget.h */, ); @@ -1119,7 +1112,6 @@ 074FCB9019D36E60004C6EB2 /* moc_popupmenu.cpp */, 07D703BA19B88FB900C4EED2 /* moc_audio.cpp */, 0732E4AB199E268A00D50FE7 /* moc_overviewwidget.cpp */, - 07C4753E1967E37300CAAFE9 /* moc_switcher.cpp */, E181C525E21A16F2D4396CA7 /* moc_application.cpp */, 3B3ED09AB00290D78CF1181B /* moc_dialogswidget.cpp */, AC9B5F6FB4B984C8D76F7AE2 /* moc_dropdown.cpp */, @@ -1554,7 +1546,6 @@ EBE29731916DB43BF49FE7A4 /* aboutbox.cpp in Compile Sources */, 4426AF526AAD86D6F73CE36F /* addcontactbox.cpp in Compile Sources */, 07D7034B19B8755A00C4EED2 /* audio.cpp in Compile Sources */, - 07C4753B1967DF1C00CAAFE9 /* switcher.cpp in Compile Sources */, A0A6B97F7DBEC81004EC9461 /* confirmbox.cpp in Compile Sources */, 4FEA8F51B7BC7CAC71347A1A /* connectionbox.cpp in Compile Sources */, 07C7596F1B1F7E0000662169 /* autoupdater.cpp in Compile Sources */, @@ -1562,7 +1553,6 @@ 298BFAB73BF182297584F96F /* contactsbox.cpp in Compile Sources */, BA41D511A9BBCA09365DF88C /* downloadpathbox.cpp in Compile Sources */, 07DB67511AD07CB800A51329 /* intropwdcheck.cpp in Compile Sources */, - 07C4753F1967E37300CAAFE9 /* moc_switcher.cpp in Compile Sources */, 3ABE4F9B2264F770D944106D /* emojibox.cpp in Compile Sources */, 07D703BB19B88FB900C4EED2 /* moc_audio.cpp in Compile Sources */, 77B998AC22A13EF3DDEE07AC /* photocropbox.cpp in Compile Sources */, diff --git a/Telegram/Telegram.xcodeproj/qt_preprocess.mak b/Telegram/Telegram.xcodeproj/qt_preprocess.mak index 1bfebb9eb..109bb8a07 100644 --- a/Telegram/Telegram.xcodeproj/qt_preprocess.mak +++ b/Telegram/Telegram.xcodeproj/qt_preprocess.mak @@ -43,7 +43,7 @@ compilers: GeneratedFiles/qrc_telegram.cpp GeneratedFiles/qrc_telegram_emojis.cp GeneratedFiles/Debug/moc_popupmenu.cpp\ GeneratedFiles/Debug/moc_countryinput.cpp GeneratedFiles/Debug/moc_flatbutton.cpp GeneratedFiles/Debug/moc_flatcheckbox.cpp\ GeneratedFiles/Debug/moc_flatinput.cpp GeneratedFiles/Debug/moc_flatlabel.cpp GeneratedFiles/Debug/moc_flattextarea.cpp\ - GeneratedFiles/Debug/moc_switcher.cpp GeneratedFiles/Debug/moc_scrollarea.cpp GeneratedFiles/Debug/moc_twidget.cpp\ + GeneratedFiles/Debug/moc_scrollarea.cpp GeneratedFiles/Debug/moc_twidget.cpp\ GeneratedFiles/Debug/moc_aboutbox.cpp GeneratedFiles/Debug/moc_abstractbox.cpp GeneratedFiles/Debug/moc_addcontactbox.cpp\ GeneratedFiles/Debug/moc_autolockbox.cpp\ GeneratedFiles/Debug/moc_backgroundbox.cpp\ @@ -99,9 +99,9 @@ GeneratedFiles/qrc_telegram_mac.cpp: SourceFiles/telegram_mac.qrc \ SourceFiles/art/osxtray.png /usr/local/Qt-5.5.1/bin/rcc -name telegram_mac SourceFiles/telegram_mac.qrc -o GeneratedFiles/qrc_telegram_mac.cpp -compiler_moc_header_make_all: GeneratedFiles/Debug/moc_apiwrap.cpp GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp GeneratedFiles/Debug/moc_autoupdater.cpp GeneratedFiles/Debug/moc_dialogswidget.cpp GeneratedFiles/Debug/moc_dropdown.cpp GeneratedFiles/Debug/moc_fileuploader.cpp GeneratedFiles/Debug/moc_history.cpp GeneratedFiles/Debug/moc_historywidget.cpp GeneratedFiles/Debug/moc_layerwidget.cpp GeneratedFiles/Debug/moc_mediaview.cpp GeneratedFiles/Debug/moc_overviewwidget.cpp GeneratedFiles/Debug/moc_playerwidget.cpp GeneratedFiles/Debug/moc_profilewidget.cpp GeneratedFiles/Debug/moc_passcodewidget.cpp GeneratedFiles/Debug/moc_localimageloader.cpp GeneratedFiles/Debug/moc_localstorage.cpp GeneratedFiles/Debug/moc_mainwidget.cpp GeneratedFiles/Debug/moc_settingswidget.cpp GeneratedFiles/Debug/moc_sysbuttons.cpp GeneratedFiles/Debug/moc_title.cpp GeneratedFiles/Debug/moc_types.cpp GeneratedFiles/Debug/moc_window.cpp GeneratedFiles/Debug/moc_mtp.cpp GeneratedFiles/Debug/moc_mtpConnection.cpp GeneratedFiles/Debug/moc_mtpDC.cpp GeneratedFiles/Debug/moc_mtpFileLoader.cpp GeneratedFiles/Debug/moc_mtpSession.cpp GeneratedFiles/Debug/moc_animation.cpp GeneratedFiles/Debug/moc_button.cpp GeneratedFiles/Debug/moc_popupmenu.cpp GeneratedFiles/Debug/moc_countryinput.cpp GeneratedFiles/Debug/moc_flatbutton.cpp GeneratedFiles/Debug/moc_flatcheckbox.cpp GeneratedFiles/Debug/moc_flatinput.cpp GeneratedFiles/Debug/moc_flatlabel.cpp GeneratedFiles/Debug/moc_flattextarea.cpp GeneratedFiles/Debug/moc_switcher.cpp GeneratedFiles/Debug/moc_scrollarea.cpp GeneratedFiles/Debug/moc_twidget.cpp GeneratedFiles/Debug/moc_aboutbox.cpp GeneratedFiles/Debug/moc_abstractbox.cpp GeneratedFiles/Debug/moc_addcontactbox.cpp GeneratedFiles/Debug/moc_autolockbox.cpp GeneratedFiles/Debug/moc_backgroundbox.cpp GeneratedFiles/Debug/moc_confirmbox.cpp GeneratedFiles/Debug/moc_connectionbox.cpp GeneratedFiles/Debug/moc_contactsbox.cpp GeneratedFiles/Debug/moc_downloadpathbox.cpp GeneratedFiles/Debug/moc_emojibox.cpp GeneratedFiles/Debug/moc_languagebox.cpp GeneratedFiles/Debug/moc_passcodebox.cpp GeneratedFiles/Debug/moc_photocropbox.cpp GeneratedFiles/Debug/moc_photosendbox.cpp GeneratedFiles/Debug/moc_sessionsbox.cpp GeneratedFiles/Debug/moc_stickersetbox.cpp GeneratedFiles/Debug/moc_usernamebox.cpp GeneratedFiles/Debug/moc_intro.cpp GeneratedFiles/Debug/moc_introcode.cpp GeneratedFiles/Debug/moc_introphone.cpp GeneratedFiles/Debug/moc_intropwdcheck.cpp GeneratedFiles/Debug/moc_introsignup.cpp GeneratedFiles/Debug/moc_pspecific_mac.cpp +compiler_moc_header_make_all: GeneratedFiles/Debug/moc_apiwrap.cpp GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp GeneratedFiles/Debug/moc_autoupdater.cpp GeneratedFiles/Debug/moc_dialogswidget.cpp GeneratedFiles/Debug/moc_dropdown.cpp GeneratedFiles/Debug/moc_fileuploader.cpp GeneratedFiles/Debug/moc_history.cpp GeneratedFiles/Debug/moc_historywidget.cpp GeneratedFiles/Debug/moc_layerwidget.cpp GeneratedFiles/Debug/moc_mediaview.cpp GeneratedFiles/Debug/moc_overviewwidget.cpp GeneratedFiles/Debug/moc_playerwidget.cpp GeneratedFiles/Debug/moc_profilewidget.cpp GeneratedFiles/Debug/moc_passcodewidget.cpp GeneratedFiles/Debug/moc_localimageloader.cpp GeneratedFiles/Debug/moc_localstorage.cpp GeneratedFiles/Debug/moc_mainwidget.cpp GeneratedFiles/Debug/moc_settingswidget.cpp GeneratedFiles/Debug/moc_sysbuttons.cpp GeneratedFiles/Debug/moc_title.cpp GeneratedFiles/Debug/moc_types.cpp GeneratedFiles/Debug/moc_window.cpp GeneratedFiles/Debug/moc_mtp.cpp GeneratedFiles/Debug/moc_mtpConnection.cpp GeneratedFiles/Debug/moc_mtpDC.cpp GeneratedFiles/Debug/moc_mtpFileLoader.cpp GeneratedFiles/Debug/moc_mtpSession.cpp GeneratedFiles/Debug/moc_animation.cpp GeneratedFiles/Debug/moc_button.cpp GeneratedFiles/Debug/moc_popupmenu.cpp GeneratedFiles/Debug/moc_countryinput.cpp GeneratedFiles/Debug/moc_flatbutton.cpp GeneratedFiles/Debug/moc_flatcheckbox.cpp GeneratedFiles/Debug/moc_flatinput.cpp GeneratedFiles/Debug/moc_flatlabel.cpp GeneratedFiles/Debug/moc_flattextarea.cpp GeneratedFiles/Debug/moc_scrollarea.cpp GeneratedFiles/Debug/moc_twidget.cpp GeneratedFiles/Debug/moc_aboutbox.cpp GeneratedFiles/Debug/moc_abstractbox.cpp GeneratedFiles/Debug/moc_addcontactbox.cpp GeneratedFiles/Debug/moc_autolockbox.cpp GeneratedFiles/Debug/moc_backgroundbox.cpp GeneratedFiles/Debug/moc_confirmbox.cpp GeneratedFiles/Debug/moc_connectionbox.cpp GeneratedFiles/Debug/moc_contactsbox.cpp GeneratedFiles/Debug/moc_downloadpathbox.cpp GeneratedFiles/Debug/moc_emojibox.cpp GeneratedFiles/Debug/moc_languagebox.cpp GeneratedFiles/Debug/moc_passcodebox.cpp GeneratedFiles/Debug/moc_photocropbox.cpp GeneratedFiles/Debug/moc_photosendbox.cpp GeneratedFiles/Debug/moc_sessionsbox.cpp GeneratedFiles/Debug/moc_stickersetbox.cpp GeneratedFiles/Debug/moc_usernamebox.cpp GeneratedFiles/Debug/moc_intro.cpp GeneratedFiles/Debug/moc_introcode.cpp GeneratedFiles/Debug/moc_introphone.cpp GeneratedFiles/Debug/moc_intropwdcheck.cpp GeneratedFiles/Debug/moc_introsignup.cpp GeneratedFiles/Debug/moc_pspecific_mac.cpp compiler_moc_header_clean: - -$(DEL_FILE) GeneratedFiles/Debug/moc_apiwrap.cpp GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp GeneratedFiles/Debug/moc_autoupdater.cpp GeneratedFiles/Debug/moc_dialogswidget.cpp GeneratedFiles/Debug/moc_dropdown.cpp GeneratedFiles/Debug/moc_fileuploader.cpp GeneratedFiles/Debug/moc_history.cpp GeneratedFiles/Debug/moc_historywidget.cpp GeneratedFiles/Debug/moc_layerwidget.cpp GeneratedFiles/Debug/moc_mediaview.cpp GeneratedFiles/Debug/moc_overviewwidget.cpp GeneratedFiles/Debug/moc_playerwidget.cpp GeneratedFiles/Debug/moc_profilewidget.cpp GeneratedFiles/Debug/moc_passcodewidget.cpp GeneratedFiles/Debug/moc_localimageloader.cpp GeneratedFiles/Debug/moc_localstorage.cpp GeneratedFiles/Debug/moc_mainwidget.cpp GeneratedFiles/Debug/moc_settingswidget.cpp GeneratedFiles/Debug/moc_sysbuttons.cpp GeneratedFiles/Debug/moc_title.cpp GeneratedFiles/Debug/moc_types.cpp GeneratedFiles/Debug/moc_window.cpp GeneratedFiles/Debug/moc_mtp.cpp GeneratedFiles/Debug/moc_mtpConnection.cpp GeneratedFiles/Debug/moc_mtpDC.cpp GeneratedFiles/Debug/moc_mtpFileLoader.cpp GeneratedFiles/Debug/moc_mtpSession.cpp GeneratedFiles/Debug/moc_animation.cpp GeneratedFiles/Debug/moc_button.cpp GeneratedFiles/Debug/moc_popupmenu.cpp GeneratedFiles/Debug/moc_countryinput.cpp GeneratedFiles/Debug/moc_flatbutton.cpp GeneratedFiles/Debug/moc_flatcheckbox.cpp GeneratedFiles/Debug/moc_flatinput.cpp GeneratedFiles/Debug/moc_flatlabel.cpp GeneratedFiles/Debug/moc_flattextarea.cpp GeneratedFiles/Debug/moc_switcher.cpp GeneratedFiles/Debug/moc_scrollarea.cpp GeneratedFiles/Debug/moc_twidget.cpp GeneratedFiles/Debug/moc_aboutbox.cpp GeneratedFiles/Debug/moc_abstractbox.cpp GeneratedFiles/Debug/moc_addcontactbox.cpp GeneratedFiles/Debug/moc_autolockbox.cpp GeneratedFiles/Debug/moc_backgroundbox.cpp GeneratedFiles/Debug/moc_confirmbox.cpp GeneratedFiles/Debug/moc_connectionbox.cpp GeneratedFiles/Debug/moc_contactsbox.cpp GeneratedFiles/Debug/moc_downloadpathbox.cpp GeneratedFiles/Debug/moc_emojibox.cpp GeneratedFiles/Debug/moc_languagebox.cpp GeneratedFiles/Debug/moc_passcodebox.cpp GeneratedFiles/Debug/moc_photocropbox.cpp GeneratedFiles/Debug/moc_photosendbox.cpp GeneratedFiles/Debug/moc_sessionsbox.cpp GeneratedFiles/Debug/moc_stickersetbox.cpp GeneratedFiles/Debug/moc_usernamedbox.cpp GeneratedFiles/Debug/moc_intro.cpp GeneratedFiles/Debug/moc_introcode.cpp GeneratedFiles/Debug/moc_introphone.cpp GeneratedFiles/Debug/moc_intropwdcheck.cpp GeneratedFiles/Debug/moc_introsignup.cpp GeneratedFiles/Debug/moc_pspecific_mac.cpp + -$(DEL_FILE) GeneratedFiles/Debug/moc_apiwrap.cpp GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp GeneratedFiles/Debug/moc_autoupdater.cpp GeneratedFiles/Debug/moc_dialogswidget.cpp GeneratedFiles/Debug/moc_dropdown.cpp GeneratedFiles/Debug/moc_fileuploader.cpp GeneratedFiles/Debug/moc_history.cpp GeneratedFiles/Debug/moc_historywidget.cpp GeneratedFiles/Debug/moc_layerwidget.cpp GeneratedFiles/Debug/moc_mediaview.cpp GeneratedFiles/Debug/moc_overviewwidget.cpp GeneratedFiles/Debug/moc_playerwidget.cpp GeneratedFiles/Debug/moc_profilewidget.cpp GeneratedFiles/Debug/moc_passcodewidget.cpp GeneratedFiles/Debug/moc_localimageloader.cpp GeneratedFiles/Debug/moc_localstorage.cpp GeneratedFiles/Debug/moc_mainwidget.cpp GeneratedFiles/Debug/moc_settingswidget.cpp GeneratedFiles/Debug/moc_sysbuttons.cpp GeneratedFiles/Debug/moc_title.cpp GeneratedFiles/Debug/moc_types.cpp GeneratedFiles/Debug/moc_window.cpp GeneratedFiles/Debug/moc_mtp.cpp GeneratedFiles/Debug/moc_mtpConnection.cpp GeneratedFiles/Debug/moc_mtpDC.cpp GeneratedFiles/Debug/moc_mtpFileLoader.cpp GeneratedFiles/Debug/moc_mtpSession.cpp GeneratedFiles/Debug/moc_animation.cpp GeneratedFiles/Debug/moc_button.cpp GeneratedFiles/Debug/moc_popupmenu.cpp GeneratedFiles/Debug/moc_countryinput.cpp GeneratedFiles/Debug/moc_flatbutton.cpp GeneratedFiles/Debug/moc_flatcheckbox.cpp GeneratedFiles/Debug/moc_flatinput.cpp GeneratedFiles/Debug/moc_flatlabel.cpp GeneratedFiles/Debug/moc_flattextarea.cpp GeneratedFiles/Debug/moc_scrollarea.cpp GeneratedFiles/Debug/moc_twidget.cpp GeneratedFiles/Debug/moc_aboutbox.cpp GeneratedFiles/Debug/moc_abstractbox.cpp GeneratedFiles/Debug/moc_addcontactbox.cpp GeneratedFiles/Debug/moc_autolockbox.cpp GeneratedFiles/Debug/moc_backgroundbox.cpp GeneratedFiles/Debug/moc_confirmbox.cpp GeneratedFiles/Debug/moc_connectionbox.cpp GeneratedFiles/Debug/moc_contactsbox.cpp GeneratedFiles/Debug/moc_downloadpathbox.cpp GeneratedFiles/Debug/moc_emojibox.cpp GeneratedFiles/Debug/moc_languagebox.cpp GeneratedFiles/Debug/moc_passcodebox.cpp GeneratedFiles/Debug/moc_photocropbox.cpp GeneratedFiles/Debug/moc_photosendbox.cpp GeneratedFiles/Debug/moc_sessionsbox.cpp GeneratedFiles/Debug/moc_stickersetbox.cpp GeneratedFiles/Debug/moc_usernamedbox.cpp GeneratedFiles/Debug/moc_intro.cpp GeneratedFiles/Debug/moc_introcode.cpp GeneratedFiles/Debug/moc_introphone.cpp GeneratedFiles/Debug/moc_intropwdcheck.cpp GeneratedFiles/Debug/moc_introsignup.cpp GeneratedFiles/Debug/moc_pspecific_mac.cpp GeneratedFiles/Debug/moc_apiwrap.cpp: SourceFiles/types.h \ SourceFiles/logs.h \ SourceFiles/apiwrap.h @@ -428,20 +428,6 @@ GeneratedFiles/Debug/moc_flattextarea.cpp: ../../Libraries/QtStatic/qtbase/inclu SourceFiles/gui/flattextarea.h /usr/local/Qt-5.5.1/bin/moc $(DEFINES) -D__APPLE__ -D__GNUC__=4 -I/usr/local/Qt-5.5.1/mkspecs/macx-clang -I. -I/usr/local/Qt-5.5.1/include/QtGui/5.5.1/QtGui -I/usr/local/Qt-5.5.1/include/QtCore/5.5.1/QtCore -I/usr/local/Qt-5.5.1/include -I./SourceFiles -I./GeneratedFiles -I../../Libraries/lzma/C -I../../Libraries/libexif-0.6.20 -I/usr/local/Qt-5.5.1/include -I/usr/local/Qt-5.5.1/include/QtMultimedia -I/usr/local/Qt-5.5.1/include/QtWidgets -I/usr/local/Qt-5.5.1/include/QtNetwork -I/usr/local/Qt-5.5.1/include/QtGui -I/usr/local/Qt-5.5.1/include/QtCore -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/backward -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.1/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include SourceFiles/gui/flattextarea.h -o GeneratedFiles/Debug/moc_flattextarea.cpp -GeneratedFiles/Debug/moc_switcher.cpp: ../../Libraries/QtStatic/qtbase/include/QtWidgets/QWidget \ - SourceFiles/gui/twidget.h \ - SourceFiles/style.h \ - GeneratedFiles/style_classes.h \ - GeneratedFiles/style_auto.h \ - SourceFiles/gui/animation.h \ - SourceFiles/types.h \ - ../../Libraries/QtStatic/qtbase/include/QtCore/QReadWriteLock \ - SourceFiles/logs.h \ - ../../Libraries/QtStatic/qtbase/include/QtCore/QTimer \ - ../../Libraries/QtStatic/qtbase/include/QtGui/QColor \ - SourceFiles/gui/switcher.h - /usr/local/Qt-5.5.1/bin/moc $(DEFINES) -D__APPLE__ -D__GNUC__=4 -I/usr/local/Qt-5.5.1/mkspecs/macx-clang -I. -I/usr/local/Qt-5.5.1/include/QtGui/5.5.1/QtGui -I/usr/local/Qt-5.5.1/include/QtCore/5.5.1/QtCore -I/usr/local/Qt-5.5.1/include -I./SourceFiles -I./GeneratedFiles -I../../Libraries/lzma/C -I../../Libraries/libexif-0.6.20 -I/usr/local/Qt-5.5.1/include -I/usr/local/Qt-5.5.1/include/QtMultimedia -I/usr/local/Qt-5.5.1/include/QtWidgets -I/usr/local/Qt-5.5.1/include/QtNetwork -I/usr/local/Qt-5.5.1/include/QtGui -I/usr/local/Qt-5.5.1/include/QtCore -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/backward -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.1/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include SourceFiles/gui/switcher.h -o GeneratedFiles/Debug/moc_switcher.cpp - GeneratedFiles/Debug/moc_scrollarea.cpp: ../../Libraries/QtStatic/qtbase/include/QtWidgets/QScrollArea \ SourceFiles/style.h \ GeneratedFiles/style_classes.h \ From 6bad3b4d7ea1a23f03a690f812b7d73f3bdecd04 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 8 Dec 2015 22:07:50 +0300 Subject: [PATCH 005/145] started media redesign (does not compile!) --- Telegram/Resources/lang.strings | 1 + Telegram/Resources/style.txt | 27 +- Telegram/SourceFiles/boxes/stickersetbox.cpp | 2 +- Telegram/SourceFiles/dropdown.cpp | 4 +- Telegram/SourceFiles/fileuploader.cpp | 4 +- Telegram/SourceFiles/history.cpp | 796 ++++++++++--------- Telegram/SourceFiles/history.h | 145 +++- Telegram/SourceFiles/historywidget.cpp | 13 +- Telegram/SourceFiles/layerwidget.cpp | 2 +- Telegram/SourceFiles/mainwidget.cpp | 9 +- Telegram/SourceFiles/mediaview.cpp | 5 +- Telegram/SourceFiles/structs.h | 3 +- 12 files changed, 575 insertions(+), 436 deletions(-) diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 28928e798..7124d3e91 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -664,6 +664,7 @@ 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_choose_images" = "Choose images"; "lng_context_view_profile" = "View profile"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 64ac38cae..c841c6597 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1165,8 +1165,6 @@ introErrLabelTextStyle: textStyle(defaultTextStyle) { lineHeight: 27px; } -mediaMaxWidth: 250px; -mediaFont: font(fsize); mediaPadding: margins(7px, 6px, 7px, 6px); mediaThumbSize: 48px; mediaNameTop: 3px; @@ -1191,26 +1189,13 @@ mediaInUnreadColor: #999; mediaInUnreadSelectColor: #7b95aa; mediaUnreadSize: 4px; mediaUnreadSkip: 5px; -mediaSaveDelta: 14px; // between bubble and download -mediaSaveButton: flatButton(btnDefFlat) { - color: #507da2; - overColor: #507da2; - downColor: #507da2; - bgColor: white; - overBgColor: overBg; - downBgColor: overBg; - - width: -28px; - height: 34px; - - textTop: 7px; - overTextTop: 7px; - downTextTop: 8px; - - font: font(fsize); - overFont: font(fsize); -} +msgFileMenuSize: size(36px, 36px); +msgFileSize: 44px; +msgFilePadding: margins(14px, 12px, 10px, 12px); +msgFileThumbSize: 72px; +msgFileThumbPadding: margins(10px, 10px, 14px, 10px); +msgFileMinWidth: 294px; sendPadding: 9px; btnSend: flatButton(btnDefFlat) { diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 8e305cc9a..2ca8888c0 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -148,7 +148,7 @@ void StickerSetInner::paintEvent(QPaintEvent *e) { doc->thumb->load(); } else { bool already = !doc->already().isEmpty(), hasdata = !doc->data.isEmpty(); - if (!doc->loader && doc->status != FileFailed && !already && !hasdata) { + if (!already && !hasdata && !doc->loader && doc->status == FileReady) { doc->save(QString()); } if (doc->sticker()->img->isNull() && (already || hasdata)) { diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 44fe3a6ab..49d6b8731 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -1332,7 +1332,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) { sticker->thumb->load(); } else { bool already = !sticker->already().isEmpty(), hasdata = !sticker->data.isEmpty(); - if (!sticker->loader && sticker->status != FileFailed && !already && !hasdata) { + if (!already && !hasdata && !sticker->loader && sticker->status == FileReady) { sticker->save(QString()); } if (sticker->sticker()->img->isNull() && (already || hasdata)) { @@ -1521,7 +1521,7 @@ void StickerPanInner::preloadImages() { sticker->thumb->load(); } else { bool already = !sticker->already().isEmpty(), hasdata = !sticker->data.isEmpty(); - if (!sticker->loader && sticker->status != FileFailed && !already && !hasdata) { + if (!already && !hasdata && !sticker->loader && sticker->status == FileReady) { sticker->save(QString()); } //if (sticker->sticker->img->isNull() && (already || hasdata)) { diff --git a/Telegram/SourceFiles/fileuploader.cpp b/Telegram/SourceFiles/fileuploader.cpp index 3a2c7eae2..dd41f5725 100644 --- a/Telegram/SourceFiles/fileuploader.cpp +++ b/Telegram/SourceFiles/fileuploader.cpp @@ -83,13 +83,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()); } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index b4c16dcb7..186aea427 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3476,8 +3476,6 @@ 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)) @@ -3485,21 +3483,13 @@ HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, Histo , _cancell(new VideoCancelLink(data)) , _caption(st::minPhotoSize) , _dldDone(0) -, _uplDone(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(); @@ -3515,11 +3505,9 @@ HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, Histo void HistoryVideo::initDimensions(const HistoryItem *parent) { if (_caption.hasSkipBlock()) _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight()); - _maxw = st::mediaMaxWidth; + _maxw = st::msgMaxWidth; 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; @@ -3590,10 +3578,6 @@ int32 HistoryVideo::countHeight(const HistoryItem *parent, int32 width) const { } 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; @@ -3630,19 +3614,6 @@ void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x 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; @@ -3714,30 +3685,6 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in 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)); @@ -3788,7 +3735,7 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in } statusText = _dldTextCache; } else { - if (data->status == FileFailed) { + if (data->status == FileUploadFailed || data->status == FileDownloadFailed) { statusText = lang(lng_attach_failed); } else if (data->status == FileUploading) { if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) { @@ -3841,10 +3788,6 @@ int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) { } 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; @@ -3870,25 +3813,13 @@ HistoryAudio::HistoryAudio(const MTPDaudio &audio) : HistoryMedia() , _savel(new AudioSaveLink(data)) , _cancell(new AudioCancelLink(data)) , _dldDone(0) -, _uplDone(0) -{ +, _uplDone(0) { _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); - } } void HistoryAudio::initDimensions(const HistoryItem *parent) { - _maxw = st::mediaMaxWidth; + _maxw = st::msgMaxWidth; 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; - } _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); if (parent->displayFromName()) { @@ -3936,25 +3867,6 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in 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)); @@ -3985,7 +3897,7 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in QRect img; QString statusText; - if (data->status == FileFailed) { + if (data->status == FileUploadFailed || data->status == FileDownloadFailed) { statusText = lang(lng_attach_failed); img = outbg ? st::mediaAudioOutImg : st::mediaAudioInImg; } else if (data->status == FileUploading) { @@ -4113,15 +4025,6 @@ void HistoryAudio::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x 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; - } - 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; @@ -4159,75 +4062,75 @@ 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); } + + int32 documentMaxStatusWidth(DocumentData *document) { + SongData *song = document->song(); + + QString size = song ? formatDurationAndSizeText(song->duration, document->size) : formatSizeText(document->size); + int32 result = st::normalFont->width(size); + + QString downloaded = formatDownloadText(document->size, document->size); + result = qMax(result, st::normalFont->width(downloaded)); + + if (song) { + QString duration = formatDurationText(song->duration); + QString played = lng_duration_played(lt_played, duration, lt_duration, duration); + result = qMax(result, st::normalFont->width(played)); + } + + return result; + } } 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); +, _data(document) +, _openl(new DocumentOpenLink(_data)) +, _savel(new DocumentSaveLink(_data)) +, _cancell(new DocumentCancelLink(_data)) +, _name(documentName(_data)) +, _statusSize(-1) { + if (_name.isEmpty()) _name = qsl("Unknown File"); + _namew = st::normalFont->width(_name); - _height = _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); + _statusText = formatStatus(_statusSize); - 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 (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; + } + _height = _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); } else { - _thumbw = st::mediaThumbSize; + _thumbw = 0; + _height = _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); } } void HistoryDocument::initDimensions(const HistoryItem *parent) { - if (parent == animated.msg) { - _maxw = animated.w / cIntRetinaFactor(); - _minh = animated.h / cIntRetinaFactor(); - } 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(); + _maxw = st::msgFileMinWidth; - 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; - } - } + 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 { + tleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); + tright = st::msgFileThumbPadding.left(); + _maxw = qMax(_maxw, tleft + documentMaxStatusWidth(_data) + parent->skipBlockWidth() + st::msgPadding.right()); } - _height = _minh; + + _maxw = qMax(tleft + _namew + tright, _maxw); + _maxw = qMin(_maxw, int(st::msgMaxWidth)); } void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const { @@ -4235,86 +4138,19 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, 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; - } - - 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; - } + bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); 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()) { + int32 statusSize = 0; + if (_data->song()) { SongMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; int64 playingPosition = 0, playingDuration = 0; @@ -4324,13 +4160,12 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, } 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); + if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { + statusSize = -2; + } 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; @@ -4340,16 +4175,62 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, 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); + 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 (_data->loader) { + int32 offset = _data->loader->currentOffset(); if (_dldTextCache.isEmpty() || _dldDone != offset) { _dldDone = offset; - _dldTextCache = formatDownloadText(_dldDone, data->size); + _dldTextCache = formatDownloadText(_dldDone, _data->size); + } + statusText = _dldTextCache; + } else { + statusText = _size; + } + img = outbg ? st::mediaMusicOutImg : st::mediaMusicInImg; + } + } + + 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 == FileDownloadFailed || _data->status == FileUploadFailed) { + 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 { @@ -4360,19 +4241,19 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), img); } else { - if (data->status == FileFailed) { + if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { statusText = lang(lng_attach_failed); - } else if (data->status == FileUploading) { - if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) { - _uplDone = data->uploadOffset; - _uplTextCache = formatDownloadText(_uplDone, data->size); + } 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(); + } else if (_data->loader) { + int32 offset = _data->loader->currentOffset(); if (_dldTextCache.isEmpty() || _dldDone != offset) { _dldDone = offset; - _dldTextCache = formatDownloadText(_dldDone, data->size); + _dldTextCache = formatDownloadText(_dldDone, _data->size); } statusText = _dldTextCache; } else { @@ -4380,8 +4261,8 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, } if (_thumbw) { - data->thumb->checkload(); - p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize)); + _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)); } @@ -4418,14 +4299,14 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, 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(); + 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()) { + if (_data->song()) { SongMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; int64 playingPosition = 0, playingDuration = 0; @@ -4435,13 +4316,13 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool } QRect img; - if (data->status == FileFailed) { + if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { statusText = lang(lng_attach_failed); img = st::mediaMusicInImg; - } else if (data->status == FileUploading) { - if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) { + } else if (_data->status == FileUploading) { + if (_uplTextCache.isEmpty() || _uplDone != _data->uploadOffset) { _uplDone = data->uploadOffset; - _uplTextCache = formatDownloadText(_uplDone, data->size); + _uplTextCache = formatDownloadText(_uplDone, _data->size); } statusText = _uplTextCache; img = st::mediaMusicInImg; @@ -4452,16 +4333,16 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool 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); + 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 (_data->loader) { + int32 offset = _data->loader->currentOffset(); if (_dldTextCache.isEmpty() || _dldDone != offset) { _dldDone = offset; - _dldTextCache = formatDownloadText(_dldDone, data->size); + _dldTextCache = formatDownloadText(_dldDone, _data->size); } statusText = _dldTextCache; } else { @@ -4472,19 +4353,19 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), App::sprite(), img); } else { - if (data->status == FileFailed) { + if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { statusText = lang(lng_attach_failed); - } else if (data->status == FileUploading) { - if (_uplTextCache.isEmpty() || _uplDone != data->uploadOffset) { - _uplDone = data->uploadOffset; - _uplTextCache = formatDownloadText(_uplDone, data->size); + } 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(); + } else if (_data->loader) { + int32 offset = _data->loader->currentOffset(); if (_dldTextCache.isEmpty() || _dldDone != offset) { _dldDone = offset; - _dldTextCache = formatDownloadText(_dldDone, data->size); + _dldTextCache = formatDownloadText(_dldDone, _data->size); } statusText = _dldTextCache; } else { @@ -4492,8 +4373,8 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool } if (_thumbw) { - data->thumb->checkload(); - p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize)); + _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); } @@ -4520,23 +4401,23 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool } TextLinkPtr HistoryDocument::linkInPlaylist() { - if (!data->loader && data->access) { + if (!_data->loader && _data->access) { return _openl; } return TextLinkPtr(); } void HistoryDocument::regItem(HistoryItem *item) { - App::regDocumentItem(data, item); + App::regDocumentItem(_data, item); } void HistoryDocument::unregItem(HistoryItem *item) { - App::unregDocumentItem(data, item); + App::unregDocumentItem(_data, item); } void HistoryDocument::updateFrom(const MTPMessageMedia &media) { if (media.type() == mtpc_messageMediaDocument) { - App::feedDocument(media.c_messageMediaDocument().vdocument, data); + App::feedDocument(media.c_messageMediaDocument().vdocument, _data); } } @@ -4607,7 +4488,7 @@ void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int3 } const HistoryReply *reply = toHistoryReply(parent); - const HistoryForwarded *fwd = (reply || data->song()) ? 0 : toHistoryForwarded(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(); @@ -4623,15 +4504,6 @@ void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int3 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; @@ -4649,7 +4521,7 @@ void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int3 } } - if (x >= 0 && y >= skipy && x < width && y < _height && !data->loader && data->access) { + if (x >= 0 && y >= skipy && x < width && y < _height && !_data->loader && _data->access) { lnk = _openl; bool inDate = parent->pointInTime(width, _height, x, y, InfoDisplayDefault); @@ -4666,7 +4538,142 @@ HistoryMedia *HistoryDocument::clone() const { } ImagePtr HistoryDocument::replyPreview() { - return data->makeReplyPreview(); + return _data->makeReplyPreview(); +} + +HistoryGif::HistoryGif(DocumentData *document) : HistoryMedia() +, _data(document) +, _openl(new DocumentOpenLink(_data)) +, _savel(new DocumentSaveLink(_data)) +, _cancell(new DocumentCancelLink(_data)) +, _name(documentName(_data)) +, _statusSize(-1) { + _data->thumb->load(); +} + +void HistoryGif::initDimensions(const HistoryItem *parent) { + if (parent == animated.msg) { + _maxw = animated.w / cIntRetinaFactor(); + _minh = animated.h / cIntRetinaFactor(); + } else { + + } + _height = _minh; +} + +void HistoryGif::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; + } + +} + +void HistoryGif::regItem(HistoryItem *item) { + App::regDocumentItem(_data, item); +} + +void HistoryGif::unregItem(HistoryItem *item) { + App::unregDocumentItem(_data, item); +} + +void HistoryGif::updateFrom(const MTPMessageMedia &media) { + if (media.type() == mtpc_messageMediaDocument) { + App::feedDocument(media.c_messageMediaDocument().vdocument, _data); + } +} + +int32 HistoryGif::resize(int32 width, const HistoryItem *parent) { + w = qMin(width, _maxw); + if (parent == animated.msg) { + 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; + } + } else { + _height = _minh; + } + return _height; +} + +const QString HistoryGif::inDialogsText() const { + return _name.isEmpty() ? lang(lng_in_dlg_file) : _name; +} + +const QString HistoryGif::inHistoryText() const { + return qsl("[ ") + lang(lng_in_dlg_file) + (_name.isEmpty() ? QString() : (qsl(" : ") + _name)) + qsl(" ]"); +} + +bool HistoryGif::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const { + if (width < 0) width = w; + if (width >= _maxw) { + width = _maxw; + } + 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); +} + +int32 HistoryGif::countHeight(const HistoryItem *parent, int32 width) const { + if (width < 0) width = w; + if (width >= _maxw) { + width = _maxw; + } + if (parent == animated.msg) { + int32 h = (width == w) ? _height : (width * animated.h / animated.w); + if (h < 1) h = 1; + return h; + } + return _height; +} + +void HistoryGif::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; + } + +} + +HistoryMedia *HistoryGif::clone() const { + return new HistoryGif(*this); +} + +ImagePtr HistoryGif::replyPreview() { + return _data->makeReplyPreview(); } HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia() @@ -4722,7 +4729,7 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, bool selected, } } - if (!data->loader && data->status != FileFailed && !already && !hasdata) { + if (!already && !hasdata && !data->loader && data->status == FileReady) { data->save(QString()); } if (data->sticker()->img->isNull() && (already || hasdata)) { @@ -4834,11 +4841,10 @@ HistoryMedia *HistorySticker::clone() const { 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)) -{ +, contact(App::userLoaded(userId)) { App::regSharedContactPhone(userId, phone); - _maxw = st::mediaMaxWidth; + _maxw = st::msgMaxWidth; name.setText(st::mediaFont, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions); phonew = st::mediaFont->width(phone); @@ -4851,11 +4857,10 @@ HistoryContact::HistoryContact(int32 userId, const QString &first, const QString HistoryContact::HistoryContact(int32 userId, const QString &fullname, const QString &phone) : HistoryMedia(0) , userId(userId) , phone(App::formatPhone(phone)) -, contact(App::userLoaded(userId)) -{ +, contact(App::userLoaded(userId)) { App::regSharedContactPhone(userId, phone); - _maxw = st::mediaMaxWidth; + _maxw = st::msgMaxWidth; name.setText(st::mediaFont, fullname.trimmed(), _textNameOptions); phonew = st::mediaFont->width(phone); @@ -5131,7 +5136,7 @@ void HistoryWebPage::initDimensions(const HistoryItem *parent) { _minh = animated.h / cIntRetinaFactor(); _minh += st::webPagePhotoSkip; } else { - _maxw = qMax(st::webPageLeft + st::mediaThumbSize + st::mediaPadding.right() + _docNameWidth + st::mediaPadding.right(), st::mediaMaxWidth); + _maxw = qMax(st::webPageLeft + st::mediaThumbSize + st::mediaPadding.right() + _docNameWidth + st::mediaPadding.right(), st::msgMaxWidth); _minh = st::mediaThumbSize; } } else { @@ -5371,7 +5376,7 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, bool selected, bool already = !data->doc->already().isEmpty(), hasdata = !data->doc->data.isEmpty(); QRect img; - if (data->doc->status == FileFailed) { + if (data->doc->status == FileDownloadFailed || data->doc->status == FileUploadFailed) { statusText = lang(lng_attach_failed); img = outbg ? st::mediaMusicOutImg : st::mediaMusicInImg; } else if (already || hasdata) { @@ -5400,7 +5405,7 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, bool selected, p.drawPixmap(QPoint(0, 0), App::sprite(), img); } else { - if (data->doc->status == FileFailed) { + if (data->doc->status == FileDownloadFailed || data->doc->status == FileUploadFailed) { statusText = lang(lng_attach_failed); } else if (data->doc->loader) { int32 offset = data->doc->loader->currentOffset(); @@ -6519,11 +6524,7 @@ void HistoryMessage::initMediaFromDocument(DocumentData *doc) { } void HistoryMessage::initDimensions() { - if (justMedia()) { - _media->initDimensions(this); - _maxw = _media->maxWidth(); - _minh = _media->minHeight(); - } else { + if (drawBubble()) { _maxw = _text.maxWidth(); _minh = _text.minHeight(); _maxw += st::msgPadding.left() + st::msgPadding.right(); @@ -6544,6 +6545,10 @@ void HistoryMessage::initDimensions() { _minh += st::msgPadding.bottom() + _media->minHeight(); } } + } else { + _media->initDimensions(this); + _maxw = _media->maxWidth(); + _minh = _media->minHeight(); } fromNameUpdated(); } @@ -6569,8 +6574,7 @@ QString HistoryMessage::selectedText(uint32 selection) const { } QString HistoryMessage::inDialogsText() const { - QString result = _media ? _media->inDialogsText() : QString(); - return result.isEmpty() ? _text.original(0, 0xFFFF, Text::ExpandLinksNone) : result; + return justMedia() ? (_media ? _media->inDialogsText() : QString()) : _text.original(0, 0xFFFF, Text::ExpandLinksNone); } HistoryMedia *HistoryMessage::getMedia(bool inOverview) const { @@ -6602,37 +6606,36 @@ void HistoryMessage::setMedia(const MTPMessageMedia *media, bool allowEmitResize } 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; - } + 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; } + _textWidth = 0; + _textHeight = 0; } QString HistoryMessage::originalText() const { - return _text.isEmpty() ? QString() : _text.original(); + return justMedia() ? QString() : _text.original(); } EntitiesInText HistoryMessage::originalEntities() const { - return _text.isEmpty() ? EntitiesInText() : _text.originalEntities(); + return justMedia() ? EntitiesInText() : _text.originalEntities(); } bool HistoryMessage::textHasLinks() { - return _text.hasLinks(); + return justMedia() ? false : _text.hasLinks(); } void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, bool selected, InfoDisplayType type) const { @@ -6764,7 +6767,7 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const { _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 (!drawBubble()) { if (_media->maxWidth() > mwidth) mwidth = _media->maxWidth(); if (_media->currentWidth() < mwidth) mwidth = _media->currentWidth(); } @@ -6792,12 +6795,7 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const { } width = _maxw; } - if (justMedia()) { - p.save(); - p.translate(left, st::msgMargin.top()); - _media->draw(p, this, selected); - p.restore(); - } else { + if (drawBubble()) { 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)); @@ -6825,6 +6823,11 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const { p.restore(); } HistoryMessage::drawInfo(p, r.x() + r.width(), r.y() + r.height(), selected, InfoDisplayDefault); + } else { + p.save(); + p.translate(left, st::msgMargin.top()); + _media->draw(p, this, selected); + p.restore(); } textstyleRestore(); @@ -6842,9 +6845,7 @@ 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 (drawBubble()) { if (width < st::msgPadding.left() + st::msgPadding.right() + 1) { width = st::msgPadding.left() + st::msgPadding.right() + 1; } else if (width > st::msgMaxWidth) { @@ -6868,6 +6869,8 @@ int32 HistoryMessage::resize(int32 width) { _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; @@ -6875,7 +6878,7 @@ int32 HistoryMessage::resize(int32 width) { 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 (!drawBubble()) { if (_media->maxWidth() > mwidth) mwidth = _media->maxWidth(); if (_media->currentWidth() < mwidth) mwidth = _media->currentWidth(); } @@ -6901,11 +6904,12 @@ bool HistoryMessage::hasPoint(int32 x, int32 y) const { } 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 { @@ -6930,7 +6934,7 @@ void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 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 (!drawBubble()) { if (_media->maxWidth() > mwidth) mwidth = _media->maxWidth(); if (_media->currentWidth() < mwidth) mwidth = _media->currentWidth(); } @@ -6961,20 +6965,20 @@ void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 } width = _maxw; } - if (justMedia()) { - _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; + if (drawBubble()) { + 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); } - r.setTop(r.top() + st::msgNameFont->height); - } - getStateFromMessageText(lnk, state, x, y, r); + getStateFromMessageText(lnk, state, x, y, r); + } else { + _media->getState(lnk, state, x - left, y - st::msgMargin.top(), this); + } } void HistoryMessage::getStateFromMessageText(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const QRect &r) const { @@ -7008,43 +7012,40 @@ void HistoryMessage::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, symbol = 0; after = false; upon = false; - if (justMedia()) 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; + if (drawBubble()) { + 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() && out()) { + left += width - mwidth; + } + width = mwidth; } - 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; + if (displayFromPhoto()) { // from user left photo +// width -= st::msgPhotoSkip; + left += st::msgPhotoSkip; } - width = _maxw; + if (width < 1) return; + + if (width >= _maxw) { + if (!fromChannel() && out()) { + left += width - _maxw; + } + width = _maxw; + } + 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(); } - 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 { @@ -7122,13 +7123,14 @@ void HistoryForwarded::initDimensions() { 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; + if (drawBubble()) { + int32 _namew = fromWidth + fwdFromName.maxWidth() + st::msgPadding.left() + st::msgPadding.right(); + if (_namew > _maxw) _maxw = _namew; + } } void HistoryForwarded::draw(Painter &p, uint32 selection) const { - if (!justMedia() && fwdFrom->nameVersion > fwdFromVersion) { + if (drawBubble() && fwdFrom->nameVersion > fwdFromVersion) { fwdNameUpdated(); fwdFromVersion = fwdFrom->nameVersion; } @@ -7163,12 +7165,14 @@ void HistoryForwarded::drawMessageText(Painter &p, const QRect &trect, uint32 se int32 HistoryForwarded::resize(int32 width) { HistoryMessage::resize(width); - if (!justMedia()) _height += st::msgServiceNameFont->height; + if (drawBubble()) { + _height += st::msgServiceNameFont->height; + } return _height; } bool HistoryForwarded::hasPoint(int32 x, int32 y) const { - if (!justMedia()) { + if (drawBubble()) { 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()) { @@ -7203,7 +7207,7 @@ void HistoryForwarded::getState(TextLinkPtr &lnk, HistoryCursorState &state, int lnk = TextLinkPtr(); state = HistoryDefaultCursorState; - if (!justMedia()) { + if (drawBubble()) { 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()) { @@ -7269,7 +7273,7 @@ void HistoryForwarded::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 after = false; upon = false; - if (!justMedia()) { + if (drawBubble()) { 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()) { @@ -7346,7 +7350,7 @@ void HistoryReply::initDimensions() { HistoryMessage::initDimensions(); if (replyToMsg) { replyToNameUpdated(); - } else if (!justMedia()) { + } else if (drawBubble()) { int maxw = _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.left() + st::msgPadding.right(); if (maxw > _maxw) _maxw = maxw; } @@ -7382,7 +7386,7 @@ void HistoryReply::replyToNameUpdated() const { _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()) { + if (drawBubble()) { int maxw = _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.left() + st::msgPadding.right(); if (maxw > _maxw) _maxw = maxw; } @@ -7490,12 +7494,14 @@ void HistoryReply::drawMessageText(Painter &p, const QRect &trect, uint32 select int32 HistoryReply::resize(int32 width) { HistoryMessage::resize(width); - if (!justMedia()) _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); + if (drawBubble()) { + _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); + } return _height; } bool HistoryReply::hasPoint(int32 x, int32 y) const { - if (!justMedia()) { + if (drawBubble()) { 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()) { @@ -7530,7 +7536,7 @@ void HistoryReply::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x lnk = TextLinkPtr(); state = HistoryDefaultCursorState; - if (!justMedia()) { + if (drawBubble()) { 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()) { @@ -7593,7 +7599,7 @@ void HistoryReply::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, i after = false; upon = false; - if (!justMedia()) { + if (drawBubble()) { 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()) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 8871977b4..d93a89c34 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -108,6 +108,7 @@ enum HistoryMediaType { MediaTypeContact, MediaTypeAudio, MediaTypeDocument, + MediaTypeGif, MediaTypeSticker, MediaTypeImageLink, MediaTypeWebPage, @@ -131,6 +132,7 @@ inline MediaOverviewType mediaToOverviewType(HistoryMediaType t) { case MediaTypePhoto: return OverviewPhotos; case MediaTypeVideo: return OverviewVideos; case MediaTypeDocument: return OverviewDocuments; + case MediaTypeGif: return OverviewDocuments; // case MediaTypeSticker: return OverviewDocuments; case MediaTypeAudio: return OverviewAudios; } @@ -1163,6 +1165,8 @@ public: virtual QString getCaption() const { return QString(); } + virtual bool needsBubble(const HistoryItem *parent) const = 0; + virtual bool customTime() const = 0; int32 currentWidth() const { return qMin(w, _maxw); @@ -1217,6 +1221,12 @@ public: QString getCaption() const { return _caption.original(); } + bool needsBubble(const HistoryItem *parent) const { + return !_caption.isEmpty() || parent->toHistoryReply(); + } + bool customTime() const { + return _caption.isEmpty(); + } private: int16 pixw, pixh; @@ -1259,6 +1269,13 @@ public: } ImagePtr replyPreview(); + bool needsBubble(const HistoryItem *parent) const { + return !_caption.isEmpty() || parent->toHistoryReply(); + } + bool customTime() const { + return _caption.isEmpty(); + } + private: VideoData *data; TextLinkPtr _openl, _savel, _cancell; @@ -1300,6 +1317,13 @@ public: void updateFrom(const MTPMessageMedia &media); + bool needsBubble(const HistoryItem *parent) const { + return true; + } + bool customTime() const { + return false; + } + private: AudioData *data; TextLinkPtr _openl, _savel, _cancell; @@ -1316,6 +1340,10 @@ public: HistoryDocument(DocumentData *document); void initDimensions(const HistoryItem *parent); + bool withThumb() const { + return !_data->song() && !_data->thumb->isNull() && _data->thumb->width() && _data->thumb->height(); + } + void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; int32 resize(int32 width, const HistoryItem *parent); HistoryMediaType type() const { @@ -1326,13 +1354,13 @@ public: 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); + 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; + return _data; } void regItem(HistoryItem *item); @@ -1341,24 +1369,95 @@ public: void updateFrom(const MTPMessageMedia &media); 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(); + bool needsBubble(const HistoryItem *parent) const { + return true; + } + bool customTime() const { + return false; + } + private: - DocumentData *data; + DocumentData *_data; + TextLinkPtr _openl, _savel, _cancell; + + int32 _namew; + QString _name; + int32 _thumbw, _thumbx, _thumby; + + // >= 0 will contain download / upload string, _statusSize = loaded bytes + // < 0 will contain played string, _statusSize = seconds played + // 0x7FFFFF0 will contain status for not yet downloaded file + // 0x7FFFFF1 will contain status for already downloaded file + // 0x7FFFFF2 will contain status for failed to download / upload file + mutable int32 _statusSize; + mutable QString _statusText; +}; + +class HistoryGif : public HistoryMedia { +public: + + HistoryGif(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 MediaTypeGif; + } + 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; + } + + void regItem(HistoryItem *item); + void unregItem(HistoryItem *item); + + void updateFrom(const MTPMessageMedia &media); + + bool hasReplyPreview() const { + return !_data->thumb->isNull(); + } + ImagePtr replyPreview(); + + void drawInPlaylist(Painter &p, const HistoryItem *parent, bool selected, bool over, int32 width) const; + TextLinkPtr linkInPlaylist(); + + bool needsBubble(const HistoryItem *parent) const { + return parent->toHistoryReply(); + } + bool customTime() const { + return true; + } + +private: + + DocumentData *_data; TextLinkPtr _openl, _savel, _cancell; int32 _namew; QString _name, _size; int32 _thumbw, _thumbx, _thumby; - mutable QString _dldTextCache, _uplTextCache; - mutable int32 _dldDone, _uplDone; + mutable QString _statusText; + mutable int32 _statusSize; // -1 will contain just size string, -2 will contain "failed" language key + }; class HistorySticker : public HistoryMedia { @@ -1388,6 +1487,13 @@ public: void updateFrom(const MTPMessageMedia &media); + bool needsBubble(const HistoryItem *parent) const { + return false; + } + bool customTime() const { + return true; + } + private: int16 pixw, pixh; @@ -1416,6 +1522,13 @@ public: void updateFrom(const MTPMessageMedia &media); + bool needsBubble(const HistoryItem *parent) const { + return true; + } + bool customTime() const { + return false; + } + private: int32 userId; int32 phonew; @@ -1461,6 +1574,13 @@ public: return data; } + bool needsBubble(const HistoryItem *parent) const { + return true; + } + bool customTime() const { + return false; + } + private: WebPageData *data; TextLinkPtr _openl, _attachl; @@ -1552,6 +1672,13 @@ public: return true; } + bool needsBubble(const HistoryItem *parent) const { + return !_title.isEmpty() || !_description.isEmpty() || parent->toHistoryReply(); + } + bool customTime() const { + return true; + } + private: ImageLinkData *data; Text _title, _description; @@ -1574,9 +1701,11 @@ public: void fromNameUpdated() const; bool justMedia() const { - return _media && _text.isEmpty(); + return _text.isEmpty(); + } + bool drawBubble() const { + return _media ? (!justMedia() || _media->needsBubble(this)) : true; } - bool uploading() const; void drawInfo(Painter &p, int32 right, int32 bottom, bool selected, InfoDisplayType type) const; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index e4dd0b2a9..a405f021a 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -5449,7 +5449,18 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { if (!MTP::authedId()) return; if (HistoryItem *item = App::histItemById(newId)) { - DocumentData *doc = (item->getMedia() && item->getMedia()->type() == MediaTypeDocument) ? static_cast(item->getMedia())->document() : 0; + HistoryMedia *media = item->getMedia(); + DocumentData *doc = 0; + if (media) { + HistoryMediaType type = media->type(); + if (type == MediaTypeDocument) { + doc = static_cast(item->getMedia())->document(); + } else if (type == MediaTypeGif) { + doc = static_cast(item->getMedia())->document(); + } else if (type == MediaTypeSticker) { + doc = static_cast(item->getMedia())->document(); + } + } if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadFile, doc ? doc->uploadOffset : 0); } diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index f31a95f33..8287ec48e 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -274,7 +274,7 @@ QSize StickerPreviewWidget::currentDimensions() const { QPixmap StickerPreviewWidget::currentImage() const { if (_doc && _cacheStatus != CacheLoaded) { bool already = !_doc->already().isEmpty(), hasdata = !_doc->data.isEmpty(); - if (!_doc->loader && _doc->status != FileFailed && !already && !hasdata) { + if (!already && !hasdata && !_doc->loader && _doc->status == FileReady) { _doc->save(QString()); } if (_doc->sticker()->img->isNull() && (already || hasdata)) { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index a04bb47b4..5fc39f736 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1654,7 +1654,10 @@ void MainWidget::onDownloadPathSettings() { void MainWidget::videoLoadFailed(mtpFileLoader *loader, bool started) { loadFailed(loader, started, SLOT(videoLoadRetry())); VideoData *video = App::video(loader->objId()); - if (video && video->loader) video->finish(); + if (video) { + if (video->loader) video->finish(); + video->status = FileDownloadFailed; + } } void MainWidget::videoLoadRetry() { @@ -1811,7 +1814,7 @@ void MainWidget::audioLoadFailed(mtpFileLoader *loader, bool started) { loadFailed(loader, started, SLOT(audioLoadRetry())); AudioData *audio = App::audio(loader->objId()); if (audio) { - audio->status = FileFailed; + audio->status = FileDownloadFailed; if (audio->loader) audio->finish(); } } @@ -1907,7 +1910,7 @@ void MainWidget::documentLoadFailed(mtpFileLoader *loader, bool started) { DocumentData *document = App::document(loader->objId()); if (document) { if (document->loader) document->finish(); - document->status = FileFailed; + document->status = FileDownloadFailed; } } diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 78ad42528..7a88a1740 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -435,7 +435,7 @@ void MediaView::step_state(uint64 ms, bool timer) { if (dt < 1) result = true; } if (_doc && _docRadialStart > 0) { - float64 prg = _doc->loader ? qMax(_doc->loader->currentProgress(), 0.0001) : (_doc->status == FileFailed ? 0 : (_doc->already().isEmpty() ? 0 : 1)); + float64 prg = _doc->loader ? qMax(_doc->loader->currentProgress(), 0.0001) : (_doc->status == FileDownloadFailed ? 0 : (_doc->already().isEmpty() ? 0 : 1)); if (prg != a_docRadial.to()) { a_docRadial.start(prg); _docRadialStart = _docRadialLast; @@ -1514,6 +1514,7 @@ void MediaView::moveToNext(int32 delta) { switch (item->getMedia()->type()) { case MediaTypePhoto: displayPhoto(static_cast(item->getMedia())->photo(), item); preloadData(delta); break; case MediaTypeDocument: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; + case MediaTypeGif: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; case MediaTypeSticker: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; } } else { @@ -1561,6 +1562,7 @@ void MediaView::preloadData(int32 delta) { switch (media->type()) { case MediaTypePhoto: static_cast(media)->photo()->full->load(); break; case MediaTypeDocument: static_cast(media)->document()->thumb->load(); break; + case MediaTypeGif: static_cast(media)->document()->thumb->load(); break; case MediaTypeSticker: static_cast(media)->document()->sticker()->img->load(); break; } } @@ -1584,6 +1586,7 @@ void MediaView::preloadData(int32 delta) { switch (media->type()) { case MediaTypePhoto: static_cast(media)->photo()->forget(); break; case MediaTypeDocument: static_cast(media)->document()->forget(); break; + case MediaTypeGif: static_cast(media)->document()->forget(); break; case MediaTypeSticker: static_cast(media)->document()->forget(); break; } } diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 1bc85bf05..917c3673f 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -784,7 +784,8 @@ private: }; enum FileStatus { - FileFailed = -1, + FileDownloadFailed = -2, + FileUploadFailed = -1, FileUploading = 0, FileReady = 1, }; From 2eaa0148d00d8de77da114bf634d132250231bf9 Mon Sep 17 00:00:00 2001 From: telegramdesktop Date: Wed, 9 Dec 2015 18:49:35 +0300 Subject: [PATCH 006/145] fixed some mistakes --- XCODE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/XCODE.md b/XCODE.md index a9121f3ba..87c754728 100644 --- a/XCODE.md +++ b/XCODE.md @@ -78,7 +78,7 @@ In Terminal go to **/Users/user/TBuild/Libraries/xz-5.0.5** and there run: ####zlib 1.2.8 -Using se system lib +Using the system lib ####libexif 0.6.20 #####Get the source code @@ -154,7 +154,7 @@ then go to **/Users/user/TBuild/Libraries/opus** and run: * Extract to **/Users/user/TBuild/Libraries** to have **/Users/user/TBuild/Libraries/ffmpeg-2.6.3** * Download [libiconv-1.14](http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz) from http://www.gnu.org/software/libiconv/#downloading -* Extract to **/Users/user/TBuild/Libraries** to have **/Users/user/TBuild/Libraries/ibiconv-1.14 ** +* Extract to **/Users/user/TBuild/Libraries** to have **/Users/user/TBuild/Libraries/ibiconv-1.14** #####Building library In Terminal go to **/Users/user/TBuild/Libraries/libiconv-1.14** and run: From f8e006001bd7165371625ad946d0a1bd1a880b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Wed, 9 Dec 2015 16:39:36 +0100 Subject: [PATCH 007/145] Build function countBetaVersionSignature() when autoupdater is disabled because it is needed by boxes/aboutbox.cpp. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Eduardo Sánchez Muñoz (github: eduardosm) --- Telegram/SourceFiles/autoupdater.cpp | 4 ++-- Telegram/SourceFiles/autoupdater.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/autoupdater.cpp b/Telegram/SourceFiles/autoupdater.cpp index d560cfbc6..e438ae83d 100644 --- a/Telegram/SourceFiles/autoupdater.cpp +++ b/Telegram/SourceFiles/autoupdater.cpp @@ -569,6 +569,8 @@ bool checkReadyUpdate() { return true; } +#endif + QString countBetaVersionSignature(uint64 version) { // duplicated in packer.cpp if (cBetaPrivateKey().isEmpty()) { LOG(("Error: Trying to count beta version signature without beta private key!")); @@ -612,5 +614,3 @@ QString countBetaVersionSignature(uint64 version) { // duplicated in packer.cpp signature = signature.replace('-', '8').replace('_', 'B'); return QString::fromUtf8(signature.mid(19, 32)); } - -#endif diff --git a/Telegram/SourceFiles/autoupdater.h b/Telegram/SourceFiles/autoupdater.h index 0bee6fae3..e2f2a9ac9 100644 --- a/Telegram/SourceFiles/autoupdater.h +++ b/Telegram/SourceFiles/autoupdater.h @@ -66,6 +66,6 @@ private: bool checkReadyUpdate(); -QString countBetaVersionSignature(uint64 version); - #endif + +QString countBetaVersionSignature(uint64 version); From dda1ad987d093a98849abed8aa0f29df173a232e Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 9 Dec 2015 21:06:20 +0300 Subject: [PATCH 008/145] photos redesigned --- Telegram/Build.bat | 2 +- Telegram/Resources/style.txt | 4 +- Telegram/SourceFiles/app.cpp | 1 - Telegram/SourceFiles/app.h | 1 - Telegram/SourceFiles/boxes/photosendbox.cpp | 16 +- Telegram/SourceFiles/history.cpp | 1019 +++++++------------ Telegram/SourceFiles/history.h | 62 +- 7 files changed, 392 insertions(+), 713 deletions(-) diff --git a/Telegram/Build.bat b/Telegram/Build.bat index 33c14d499..0cf84f61e 100644 --- a/Telegram/Build.bat +++ b/Telegram/Build.bat @@ -52,7 +52,7 @@ if exist %ReleasePath%\deploy\%AppVersionStrMajor%\%AppVersionStr%\ ( ) cd SourceFiles\ -rem copy telegram.qrc /B+,,/Y +copy telegram.qrc /B+,,/Y cd ..\ if %errorlevel% neq 0 goto error diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index c841c6597..070ca1adc 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1165,7 +1165,9 @@ introErrLabelTextStyle: textStyle(defaultTextStyle) { lineHeight: 27px; } -mediaPadding: margins(7px, 6px, 7px, 6px); +mediaPadding: margins(0px, 0px, 0px, 0px);//2px, 2px, 2px, 2px); +mediaCaptionSkip: 5px; +mediaHeaderSkip: 5px; mediaThumbSize: 48px; mediaNameTop: 3px; mediaDetailsShift: 3px; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 81ba97b9d..f932265f3 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2147,7 +2147,6 @@ namespace App { prepareCorners(MessageInSelectedCorners, st::msgRadius, st::msgInSelectBg, &st::msgInSelectShadow); 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); } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 1402a7e05..b4d2ae7c9 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -75,7 +75,6 @@ enum RoundCorners { MessageInSelectedCorners, MessageOutCorners, MessageOutSelectedCorners, - ButtonHoverCorners, RoundCornersCount }; diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index 177520701..0d23c269e 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -100,9 +100,9 @@ PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxW } _name = _file->filename; - _namew = st::mediaFont->width(_name); + _namew = st::normalFont->width(_name); _size = formatSizeText(_file->filesize); - _textw = qMax(_namew, st::mediaFont->width(_size)); + _textw = qMax(_namew, st::normalFont->width(_size)); } updateBoxSize(); _caption.setMaxLength(MaxPhotoCaption); @@ -136,9 +136,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); + _namew = st::normalFont->width(_name); _size = _phone; - _textw = qMax(_namew, st::mediaFont->width(_size)); + _textw = qMax(_namew, st::normalFont->width(_size)); updateBoxSize(); prepare(); @@ -210,16 +210,16 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) { p.drawPixmap(x + st::mediaPadding.left(), y + st::mediaPadding.top(), userDefPhoto(1)->pix(st::mediaThumbSize)); } - p.setFont(st::mediaFont->f); + p.setFont(st::normalFont->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)); + p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(_name, twidth)); } else { - p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, _name); + p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, _name); } p.setPen(st::mediaOutColor->p); - p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->descent, _size); + p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->descent, _size); } } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 186aea427..15cc29ced 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3101,10 +3101,11 @@ HistoryItem *regItem(HistoryItem *item, bool returnExisting) { } 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)) { +, _data(App::feedPhoto(photo)) +, _openl(new PhotoLink(_data)) +, _pixw(1) +, _pixh(1) +, _caption(st::minPhotoSize) { if (!caption.isEmpty()) { _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent)); } @@ -3112,66 +3113,29 @@ HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, Histo } HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryMedia(width) -, pixw(1), pixh(1) -, data(App::feedPhoto(photo)) -, openl(new PhotoLink(data, chat)) { +, _data(App::feedPhoto(photo)) +, _openl(new PhotoLink(_data, chat)) +, _pixw(1) +, _pixh(1) +, _caption(st::minPhotoSize) { 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()); + bool bubble = parent->hasBubble(); - int32 tw = convertScale(data->full->width()), th = convertScale(data->full->height()); + if (_caption.hasSkipBlock()) { + _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight()); + } + + 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; @@ -3180,39 +3144,61 @@ 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()) { + w = tw; } else { - pixw = tw; + th = w; // square chat photo updates } - if (pixh > width) { - pixw = (pixw * width) / pixh; - pixh = width; - } - 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; - } + _maxw = qMax(qMax(w, int32(st::minPhotoSize)), th); + _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()) { - _height += st::webPagePhotoSkip + _caption.countHeight(w); + _minh += st::mediaCaptionSkip + _caption.minHeight() + st::msgPadding.bottom(); } - _height += st::mediaPadding.bottom(); + } +} + +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 (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; + w = qMax(_pixw, int16(st::minPhotoSize)); + _height = qMax(_pixh, int16(st::minPhotoSize)); + if (bubble) { + _height += st::mediaPadding.top() + st::mediaPadding.bottom(); w += st::mediaPadding.left() + st::mediaPadding.right(); + if (!_caption.isEmpty()) { + int32 captionw = w - st::msgPadding.left() - st::msgPadding.right(); + _height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom(); + } } return _height; } @@ -3231,64 +3217,32 @@ bool HistoryPhoto::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 w } void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const { + bool bubble = parent->hasBubble(); + if (width < 0) width = w; if (width < 1) return; 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()) { + if (bubble) { 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(); + skipy = st::mediaPadding.top(); 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) { + 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 - skipx, y - skipy - height - st::webPagePhotoSkip, width); - state = inDate ? HistoryInDateCursorState : (inText ? HistoryInTextCursorState : HistoryDefaultCursorState); + _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; + lnk = _openl; if (_caption.isEmpty()) { - int32 fullRight = skipx + width, fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0); + int32 fullRight = skipx + width, fullBottom = skipy + height; bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage); if (inDate) { state = HistoryInDateCursorState; @@ -3325,11 +3279,11 @@ void HistoryPhoto::updateFrom(const MTPMessageMedia &media) { } if (!loc || loc->type() != mtpc_fileLocation) continue; if (size == 's') { - Local::writeImage(storageKey(loc->c_fileLocation()), data->thumb); + Local::writeImage(storageKey(loc->c_fileLocation()), _data->thumb); } else if (size == 'm') { - Local::writeImage(storageKey(loc->c_fileLocation()), data->medium); + Local::writeImage(storageKey(loc->c_fileLocation()), _data->medium); } else if (size == 'x') { - Local::writeImage(storageKey(loc->c_fileLocation()), data->full); + Local::writeImage(storageKey(loc->c_fileLocation()), _data->full); } } } @@ -3337,60 +3291,34 @@ void HistoryPhoto::updateFrom(const MTPMessageMedia &media) { } 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 bubble = parent->hasBubble(); 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()) { + int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); + if (bubble) { skipx = st::mediaPadding.left(); + skipy = st::mediaPadding.top(); - 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); + height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom(); } } 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(); + _data->full->load(false, false); + + bool full = _data->full->loaded(); QPixmap pix; if (full) { - pix = data->full->pixSingle(pixw, pixh, width, height); + pix = _data->full->pixSingle(_pixw, _pixh, width, height); } else { - pix = data->thumb->pixBlurredSingle(pixw, pixh, width, height); + pix = _data->thumb->pixBlurredSingle(_pixw, _pixh, width, height); } + p.drawPixmap(skipx, skipy, pix); if (!full) { uint64 dt = itemAnimations().animate(parent, getms()); @@ -3420,19 +3348,16 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, bool selected, in // date QString time(parent->timeText()); if (_caption.isEmpty()) { - int32 fullRight = skipx + width, fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0); + int32 fullRight = skipx + width, fullBottom = skipy + height; 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); + p.setPen(st::black); + _caption.draw(p, st::msgPadding.left(), skipy + height + st::mediaPadding.bottom() + st::mediaCaptionSkip, captionw); } } ImagePtr HistoryPhoto::replyPreview() { - return data->makeReplyPreview(); + return _data->makeReplyPreview(); } QString formatSizeText(qint64 size) { @@ -3476,6 +3401,10 @@ QString formatDurationAndSizeText(qint64 duration, qint64 size) { return lng_duration_and_size(lt_duration, formatDurationText(duration), lt_size, formatSizeText(size)); } +QString formatPlayedText(qint64 played, qint64 duration) { + return lng_duration_played(lt_played, formatDurationText(played), lt_duration, formatDurationText(duration)); +} + HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent) : HistoryMedia() , data(App::feedVideo(video)) , _openl(new VideoOpenLink(data)) @@ -3718,9 +3647,9 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in int32 twidth = width - tleft - st::mediaPadding.right(); int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth(); - p.setFont(st::mediaFont->f); + p.setFont(st::normalFont->f); p.setPen(st::black->c); - p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, lang(lng_media_video)); + p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, lang(lng_media_video)); QString statusText; @@ -3747,15 +3676,15 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in statusText = _size; } } - int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::mediaFont->height; - p.drawText(tleft, texty + st::mediaFont->ascent, statusText); + int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height; + p.drawText(tleft, texty + st::normalFont->ascent, statusText); if (parent->isMediaUnread()) { - int32 w = st::mediaFont->width(statusText); + int32 w = st::normalFont->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.drawEllipse(QRect(tleft + w + st::mediaUnreadSkip, texty + ((st::normalFont->height - st::mediaUnreadSize) / 2), st::mediaUnreadSize, st::mediaUnreadSize)); p.setRenderHint(QPainter::HighQualityAntialiasing, false); } } @@ -3863,7 +3792,7 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in width = _maxw; } - if (!data->loader && data->status != FileFailed && !already && !hasdata && data->size < AudioVoiceMsgInMemory) { + if (!data->loader && data->status == FileReady && !already && !hasdata && data->size < AudioVoiceMsgInMemory) { data->save(QString()); } @@ -3939,21 +3868,21 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in int32 twidth = width - tleft - st::mediaPadding.right(); int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth(); - p.setFont(st::mediaFont->f); + p.setFont(st::normalFont->f); p.setPen(st::black->c); - p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, lang(lng_media_audio)); + p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, lang(lng_media_audio)); 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); + int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height; + p.drawText(tleft, texty + st::normalFont->ascent, statusText); if (parent->isMediaUnread()) { - int32 w = st::mediaFont->width(statusText); + int32 w = st::normalFont->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.drawEllipse(QRect(tleft + w + st::mediaUnreadSkip, texty + ((st::normalFont->height - st::mediaUnreadSize) / 2), st::mediaUnreadSize, st::mediaUnreadSize)); p.setRenderHint(QPainter::HighQualityAntialiasing, false); } } @@ -4069,20 +3998,13 @@ namespace { } int32 documentMaxStatusWidth(DocumentData *document) { - SongData *song = document->song(); - - QString size = song ? formatDurationAndSizeText(song->duration, document->size) : formatSizeText(document->size); - int32 result = st::normalFont->width(size); - - QString downloaded = formatDownloadText(document->size, document->size); - result = qMax(result, st::normalFont->width(downloaded)); - - if (song) { - QString duration = formatDurationText(song->duration); - QString played = lng_duration_played(lt_played, duration, lt_duration, duration); - result = qMax(result, st::normalFont->width(played)); + 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; } } @@ -4092,12 +4014,11 @@ HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia() , _openl(new DocumentOpenLink(_data)) , _savel(new DocumentSaveLink(_data)) , _cancell(new DocumentCancelLink(_data)) -, _name(documentName(_data)) -, _statusSize(-1) { +, _name(documentName(_data)) { if (_name.isEmpty()) _name = qsl("Unknown File"); _namew = st::normalFont->width(_name); - - _statusText = formatStatus(_statusSize); + + setStatusSize(DocumentStatusSizeReady); if (withThumb()) { _data->thumb->load(); @@ -4114,6 +4035,58 @@ HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia() } } +void HistoryDocument::setStatusSize(int32 newSize, qint64 realDuration) const { + _statusSize = newSize; + if (_statusSize == DocumentStatusSizeReady) { + _statusText = _data->song() ? formatDurationAndSizeText(_data->song()->duration, _data->size) : formatSizeText(_data->size); + } else if (_statusSize == DocumentStatusSizeLoaded) { + _statusText = _data->song() ? formatDurationText(_data->song()->duration) : formatSizeText(_data->size); + } else if (_statusSize == DocumentStatusSizeFailed) { + _statusText = lang(lng_attach_failed); + } else if (_statusSize >= 0) { + _statusText = formatDownloadText(_statusSize, _data->size); + } else { + _statusText = formatPlayedText(-_statusSize - 1, realDuration); + } +} + +bool HistoryDocument::updateStatusText(const HistoryItem *parent) const { + bool showPause = false; + int32 statusSize = 0, realDuration = 0; + if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { + statusSize = DocumentStatusSizeFailed; + } else if (_data->status == FileUploading) { + statusSize = _data->uploadOffset; + } else if (_data->loader) { + statusSize = _data->loader->currentOffset(); + } else if (_data->song() && (!_data->already().isEmpty() || !_data->data.isEmpty())) { + 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 = DocumentStatusSizeLoaded; + } + if (!showPause && playing.msgId == parent->fullId() && App::main() && App::main()->player()->seekingSong(playing)) { + showPause = true; + } + } else { + statusSize = DocumentStatusSizeReady; + } + if (statusSize != _statusSize) { + setStatusSize(statusSize, realDuration); + } + return showPause; +} + void HistoryDocument::initDimensions(const HistoryItem *parent) { _maxw = st::msgFileMinWidth; @@ -4149,144 +4122,24 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners)); App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); - int32 statusSize = 0; - 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 == FileDownloadFailed || _data->status == FileUploadFailed) { - statusSize = -2; - } 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; - } - } - - 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 == FileDownloadFailed || _data->status == FileUploadFailed) { - 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); - } else { - if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { - 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); - } + bool showPause = updateStatusText(parent); 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.setFont(st::normalFont->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)); + p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(_name, twidth)); } else { - p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, _name); + p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, _name); } 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.drawText(tleft, st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->descent, _statusText); p.setFont(st::msgDateFont->f); @@ -4305,7 +4158,7 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool style::color bg(selected ? st::msgInSelectBg : (over ? st::playlistHoverBg : st::msgInBg)); p.fillRect(0, 0, width, height, bg->b); - QString statusText; + bool showPause = updateStatusText(parent); if (_data->song()) { SongMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; @@ -4317,61 +4170,17 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool QRect img; if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { - 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); } else { - if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { - 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)); @@ -4387,17 +4196,17 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool int32 twidth = width - tleft - st::mediaPadding.right(); int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth(); - p.setFont(st::mediaFont->f); + p.setFont(st::normalFont->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)); + p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(_name, twidth)); } else { - p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, _name); + p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::normalFont->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); + p.drawText(tleft, st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->descent, _statusText); } TextLinkPtr HistoryDocument::linkInPlaylist() { @@ -4845,9 +4654,9 @@ HistoryContact::HistoryContact(int32 userId, const QString &first, const QString App::regSharedContactPhone(userId, phone); _maxw = st::msgMaxWidth; - name.setText(st::mediaFont, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions); + name.setText(st::normalFont, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions); - phonew = st::mediaFont->width(phone); + phonew = st::normalFont->width(phone); if (contact) { contact->photo->load(); @@ -4861,9 +4670,9 @@ HistoryContact::HistoryContact(int32 userId, const QString &fullname, const QStr App::regSharedContactPhone(userId, phone); _maxw = st::msgMaxWidth; - name.setText(st::mediaFont, fullname.trimmed(), _textNameOptions); + name.setText(st::normalFont, fullname.trimmed(), _textNameOptions); - phonew = st::mediaFont->width(phone); + phonew = st::normalFont->width(phone); if (contact) { contact->photo->load(); @@ -5015,18 +4824,18 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, bool selected, int32 twidth = width - tleft - st::mediaPadding.right(); int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth(); - p.setFont(st::mediaFont->f); + p.setFont(st::normalFont->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)); + p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(phone, twidth)); } else { - p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::mediaFont->ascent, phone); + p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->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); + name.drawElided(p, tleft, skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height, secondwidth); int32 fullRight = width, fullBottom = _height; parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayDefault); @@ -5129,7 +4938,7 @@ void HistoryWebPage::initDimensions(const HistoryItem *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); + _docNameWidth = st::normalFont->width(_docName.isEmpty() ? qsl("Document") : _docName); if (parent == animated.msg) { _maxw = st::webPageLeft + (animated.w / cIntRetinaFactor()) + parent->skipBlockWidth(); @@ -5433,18 +5242,18 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, bool selected, int32 twidth = width - tleft - st::mediaPadding.right(); int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth(); - p.setFont(st::mediaFont->f); + p.setFont(st::normalFont->f); p.setPen(st::black->c); if (twidth < _docNameWidth) { - p.drawText(tleft, st::mediaNameTop + st::mediaFont->ascent, st::mediaFont->elided(_docName, twidth)); + p.drawText(tleft, st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(_docName, twidth)); } else { - p.drawText(tleft, st::mediaNameTop + st::mediaFont->ascent, _docName); + p.drawText(tleft, st::mediaNameTop + st::normalFont->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.drawText(tleft, st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->descent, statusText); } } @@ -6523,27 +6332,34 @@ void HistoryMessage::initMediaFromDocument(DocumentData *doc) { _media->regItem(this); } +int32 HistoryMessage::plainMaxWidth() const { + return st::msgPadding.left() + _text.maxWidth() + st::msgPadding.right(); +} + void HistoryMessage::initDimensions() { if (drawBubble()) { - _maxw = _text.maxWidth(); - _minh = _text.minHeight(); - _maxw += st::msgPadding.left() + st::msgPadding.right(); + _maxw = plainMaxWidth(); + if (_text.isEmpty()) { + _minh = 0; + } else { + _minh = st::msgPadding.top() + _text.minHeight() + st::msgPadding.bottom(); + } 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; + } + int32 maxw = _media->maxWidth(); + if (maxw > _maxw) _maxw = maxw; + _minh += _media->minHeight(); + } 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(); - } } } else { _media->initDimensions(this); @@ -6553,10 +6369,31 @@ void HistoryMessage::initDimensions() { fromNameUpdated(); } +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() const { - if (_media) return; - int32 _namew = (displayFromName() ? _from->nameText.maxWidth() : 0) + st::msgPadding.left() + st::msgPadding.right(); - if (_namew > _maxw) _maxw = _namew; + if (!_media && displayFromName()) { + int32 _namew = st::msgPadding.left() + _from->nameText.maxWidth() + st::msgPadding.right(); + if (_namew > _maxw) _maxw = _namew; + } } bool HistoryMessage::uploading() const { @@ -6744,7 +6581,7 @@ void HistoryMessage::setId(MsgId newId) { } void HistoryMessage::draw(Painter &p, uint32 selection) const { - bool outbg = out() && !fromChannel(); + bool outbg = out() && !fromChannel(), bubble = drawBubble(); textstyleSet(&(outbg ? st::outTextStyle : st::inTextStyle)); @@ -6766,36 +6603,15 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const { 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 (!drawBubble()) { - 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; - } + int32 left = 0, width = 0; + countPositionAndSize(left, 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 (drawBubble()) { + 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)); @@ -6816,13 +6632,17 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const { 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()); + p.translate(left, _height - st::msgMargin.bottom() - _media->height()); _media->draw(p, this, selected); p.restore(); + if (!_media->customInfoLayout()) { + HistoryMessage::drawInfo(p, r.x() + r.width(), r.y() + r.height(), selected, InfoDisplayDefault); + } + } else { + HistoryMessage::drawInfo(p, r.x() + r.width(), r.y() + r.height(), selected, InfoDisplayDefault); } - HistoryMessage::drawInfo(p, r.x() + r.width(), r.y() + r.height(), selected, InfoDisplayDefault); } else { p.save(); p.translate(left, st::msgMargin.top()); @@ -6851,24 +6671,32 @@ int32 HistoryMessage::resize(int32 width) { } 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(); - } + 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 (justMedia()) { + _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); } @@ -6877,33 +6705,10 @@ int32 HistoryMessage::resize(int32 width) { } 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 (!drawBubble()) { - 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 (drawBubble()) { QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom()); return r.contains(x, y); @@ -6933,38 +6738,16 @@ 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 (!drawBubble()) { - 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; - } - width = _maxw; - } if (drawBubble()) { QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom()); if (displayFromName()) { // from user left name @@ -6982,18 +6765,24 @@ void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 } 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)); 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()); @@ -7013,33 +6802,17 @@ void HistoryMessage::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, after = false; upon = false; if (drawBubble()) { - 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() && out()) { - left += width - mwidth; - } - width = mwidth; - } - - if (displayFromPhoto()) { // from user left photo -// width -= st::msgPhotoSkip; - left += st::msgPhotoSkip; - } + int32 left = 0, width = 0; + countPositionAndSize(left, width); if (width < 1) return; - if (width >= _maxw) { - if (!fromChannel() && out()) { - left += width - _maxw; - } - width = _maxw; - } 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()); + trect.setBottom(trect.bottom() - _media->height()); } textstyleSet(&((out() && !fromChannel()) ? st::outTextStyle : st::inTextStyle)); @@ -7123,8 +6896,8 @@ void HistoryForwarded::initDimensions() { void HistoryForwarded::fwdNameUpdated() const { fwdFromName.setText(st::msgServiceNameFont, App::peerName(fwdFrom), _textNameOptions); - if (drawBubble()) { - int32 _namew = fromWidth + fwdFromName.maxWidth() + st::msgPadding.left() + st::msgPadding.right(); + if (!_media) { + int32 _namew = st::msgPadding.left() + fromWidth + fwdFromName.maxWidth() + st::msgPadding.right(); if (_namew > _maxw) _maxw = _namew; } } @@ -7164,39 +6937,22 @@ void HistoryForwarded::drawMessageText(Painter &p, const QRect &trect, uint32 se int32 HistoryForwarded::resize(int32 width) { HistoryMessage::resize(width); - if (drawBubble()) { - _height += st::msgServiceNameFont->height; + if (justMedia() && !displayFromName()) { + _height += st::msgPadding.top() + st::msgServiceNameFont->height + st::mediaHeaderSkip; + } else { + _height += st::msgServiceNameFont->height; + } } return _height; } bool HistoryForwarded::hasPoint(int32 x, int32 y) const { if (drawBubble()) { - 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; - } + 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); } @@ -7208,33 +6964,15 @@ void HistoryForwarded::getState(TextLinkPtr &lnk, HistoryCursorState &state, int state = HistoryDefaultCursorState; if (drawBubble()) { - 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) { + 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); @@ -7274,30 +7012,10 @@ void HistoryForwarded::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 upon = false; if (drawBubble()) { - 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; - } + 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); @@ -7344,16 +7062,8 @@ 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(); - } HistoryMessage::initDimensions(); - if (replyToMsg) { - replyToNameUpdated(); - } else if (drawBubble()) { - int maxw = _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.left() + st::msgPadding.right(); - if (maxw > _maxw) _maxw = maxw; - } + replyToNameUpdated(); } bool HistoryReply::updateReplyTo(bool force) { @@ -7377,18 +7087,21 @@ bool HistoryReply::updateReplyTo(bool force) { } 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) { + replyToName.setText(st::msgServiceNameFont, App::peerName(replyToMsg->from()), _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; - _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 (drawBubble()) { - int maxw = _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.left() + st::msgPadding.right(); - if (maxw > _maxw) _maxw = maxw; + _maxReplyWidth = previewSkip + qMax(replyToName.maxWidth(), qMin(replyToText.maxWidth(), 4 * replyToName.maxWidth())); + } else { + _maxReplyWidth = st::msgDateFont->width(lang(replyToMsgId ? lng_profile_loading : lng_deleted_message)); + } + _maxReplyWidth = st::msgReplyPadding.left() + st::msgReplyBarSkip + _maxReplyWidth + st::msgReplyPadding.right(); + + if (!_media) { + int32 replyw = st::msgPadding.left() + _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.right(); + if (replyw > _maxw) _maxw = replyw; } } @@ -7495,37 +7208,25 @@ int32 HistoryReply::resize(int32 width) { HistoryMessage::resize(width); if (drawBubble()) { - _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); + if (justMedia()) { + if (!displayFromName()) { + _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() - st::mediaHeaderSkip + st::mediaHeaderSkip; + } + } else { + _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); + } } return _height; } bool HistoryReply::hasPoint(int32 x, int32 y) const { if (drawBubble()) { - 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; - } + 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); } @@ -7537,33 +7238,15 @@ void HistoryReply::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x state = HistoryDefaultCursorState; if (drawBubble()) { - 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; - } - + 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); @@ -7600,30 +7283,10 @@ void HistoryReply::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, i upon = false; if (drawBubble()) { - 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; - } + 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); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index d93a89c34..d416ec53a 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -941,6 +941,9 @@ public: } virtual void updateMedia(const MTPMessageMedia *media, bool allowEmitResize) { } + virtual bool hasBubble() const { + return false; + } virtual QString selectedText(uint32 selection) const { return qsl("[-]"); @@ -1166,7 +1169,7 @@ public: return QString(); } virtual bool needsBubble(const HistoryItem *parent) const = 0; - virtual bool customTime() const = 0; + virtual bool customInfoLayout() const = 0; int32 currentWidth() const { return qMin(w, _maxw); @@ -1199,22 +1202,22 @@ public: HistoryMedia *clone() const; PhotoData *photo() const { - return data; + return _data; } void updateFrom(const MTPMessageMedia &media); TextLinkPtr lnk() const { - return openl; + return _openl; } virtual bool animating() const { - if (data->full->loaded()) return false; - return data->full->loading() ? true : !data->medium->loaded(); + if (_data->full->loaded()) return false; + return _data->full->loading() ? true : !_data->medium->loaded(); } bool hasReplyPreview() const { - return !data->thumb->isNull(); + return !_data->thumb->isNull(); } ImagePtr replyPreview(); @@ -1224,15 +1227,16 @@ public: bool needsBubble(const HistoryItem *parent) const { return !_caption.isEmpty() || parent->toHistoryReply(); } - bool customTime() const { + bool customInfoLayout() const { return _caption.isEmpty(); } private: - int16 pixw, pixh; - PhotoData *data; + PhotoData *_data; + TextLinkPtr _openl; + + int16 _pixw, _pixh; Text _caption; - TextLinkPtr openl; }; @@ -1272,7 +1276,7 @@ public: bool needsBubble(const HistoryItem *parent) const { return !_caption.isEmpty() || parent->toHistoryReply(); } - bool customTime() const { + bool customInfoLayout() const { return _caption.isEmpty(); } @@ -1320,7 +1324,7 @@ public: bool needsBubble(const HistoryItem *parent) const { return true; } - bool customTime() const { + bool customInfoLayout() const { return false; } @@ -1379,7 +1383,7 @@ public: bool needsBubble(const HistoryItem *parent) const { return true; } - bool customTime() const { + bool customInfoLayout() const { return false; } @@ -1390,16 +1394,22 @@ private: int32 _namew; QString _name; - int32 _thumbw, _thumbx, _thumby; + int32 _thumbw; // >= 0 will contain download / upload string, _statusSize = loaded bytes - // < 0 will contain played string, _statusSize = seconds played - // 0x7FFFFF0 will contain status for not yet downloaded file - // 0x7FFFFF1 will contain status for already downloaded file - // 0x7FFFFF2 will contain status for failed to download / upload file + // < 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; + + void setStatusSize(int32 newSize, qint64 realDuration = 0) const; + bool updateStatusText(const HistoryItem *parent) const; // returns showPause }; +static const int32 DocumentStatusSizeReady = 0x7FFFFFF0; +static const int32 DocumentStatusSizeLoaded = 0x7FFFFFF1; +static const int32 DocumentStatusSizeFailed = 0x7FFFFFF2; class HistoryGif : public HistoryMedia { public: @@ -1442,7 +1452,7 @@ public: bool needsBubble(const HistoryItem *parent) const { return parent->toHistoryReply(); } - bool customTime() const { + bool customInfoLayout() const { return true; } @@ -1490,7 +1500,7 @@ public: bool needsBubble(const HistoryItem *parent) const { return false; } - bool customTime() const { + bool customInfoLayout() const { return true; } @@ -1525,7 +1535,7 @@ public: bool needsBubble(const HistoryItem *parent) const { return true; } - bool customTime() const { + bool customInfoLayout() const { return false; } @@ -1577,7 +1587,7 @@ public: bool needsBubble(const HistoryItem *parent) const { return true; } - bool customTime() const { + bool customInfoLayout() const { return false; } @@ -1675,7 +1685,7 @@ public: bool needsBubble(const HistoryItem *parent) const { return !_title.isEmpty() || !_description.isEmpty() || parent->toHistoryReply(); } - bool customTime() const { + bool customInfoLayout() const { return true; } @@ -1700,12 +1710,18 @@ public: void initDimensions(); void fromNameUpdated() const; + int32 plainMaxWidth() const; + void countPositionAndSize(int32 &left, int32 &width) const; + bool justMedia() const { return _text.isEmpty(); } bool drawBubble() const { return _media ? (!justMedia() || _media->needsBubble(this)) : true; } + bool hasBubble() const { + return drawBubble(); + } bool uploading() const; void drawInfo(Painter &p, int32 right, int32 bottom, bool selected, InfoDisplayType type) const; From 978554ce28b49ce329dd0b6be33485085d86c79e Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 9 Dec 2015 22:09:29 +0300 Subject: [PATCH 009/145] photos redesign improved --- Telegram/Resources/style.txt | 2 +- Telegram/SourceFiles/history.cpp | 423 ++----------------------- Telegram/SourceFiles/history.h | 35 +- Telegram/SourceFiles/historywidget.cpp | 2 +- Telegram/SourceFiles/window.cpp | 2 +- 5 files changed, 67 insertions(+), 397 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 070ca1adc..ccf2a1942 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1165,7 +1165,7 @@ introErrLabelTextStyle: textStyle(defaultTextStyle) { lineHeight: 27px; } -mediaPadding: margins(0px, 0px, 0px, 0px);//2px, 2px, 2px, 2px); +mediaPadding: margins(0px, 0px, 0px, 0px);//1px, 1px, 1px, 1px);//2px, 2px, 2px, 2px); mediaCaptionSkip: 5px; mediaHeaderSkip: 5px; mediaThumbSize: 48px; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 15cc29ced..340b09f0b 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3438,17 +3438,6 @@ void HistoryVideo::initDimensions(const HistoryItem *parent) { int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); _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 { @@ -3494,17 +3483,6 @@ int32 HistoryVideo::countHeight(const HistoryItem *parent, int32 width) const { } 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(); h += st::webPagePhotoSkip + _caption.countHeight(textw); @@ -3521,45 +3499,13 @@ void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x } 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 (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; @@ -3590,22 +3536,7 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in } 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(); @@ -3619,21 +3550,6 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in 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)); } else { @@ -3704,17 +3620,6 @@ int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) { if (_caption.isEmpty()) return _height; _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(); _height += st::webPagePhotoSkip + _caption.countHeight(textw); @@ -3751,17 +3656,6 @@ void HistoryAudio::initDimensions(const HistoryItem *parent) { int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); _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; } @@ -3769,22 +3663,7 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in 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, hovered, pressed; bool already = !data->already().isEmpty(), hasdata = !data->data.isEmpty(); @@ -3801,21 +3680,6 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in 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); - } - AudioMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; int64 playingPosition = 0, playingDuration = 0; @@ -3932,45 +3796,13 @@ void HistoryAudio::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x 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, hovered, pressed; if (width >= _maxw) { width = _maxw; } - 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; @@ -4296,39 +4128,7 @@ void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int3 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 (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; @@ -4689,17 +4489,6 @@ void HistoryContact::initDimensions(const HistoryItem *parent) { _maxw = phonew + tleft + st::mediaPadding.right(); } _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; } @@ -4719,39 +4508,7 @@ bool HistoryContact::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 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; @@ -4773,22 +4530,7 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, bool selected, 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) { @@ -4800,21 +4542,6 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, bool selected, 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); @@ -5897,23 +5624,8 @@ void HistoryImageLink::initDimensions(const HistoryItem *parent) { } _maxw = w; _minh = thumbh; - const HistoryReply *reply = toHistoryReply(parent); - const HistoryForwarded *fwd = toHistoryForwarded(parent); - if (reply || !_title.isEmpty() || !_description.isEmpty()) { + if (!_title.isEmpty() || !_description.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 (!_title.isEmpty()) { _maxw = qMax(_maxw, int32(st::webPageLeft + _title.maxWidth())); _minh += qMin(_title.minHeight(), 2 * st::webPageTitleFont->height); @@ -5933,11 +5645,9 @@ void HistoryImageLink::initDimensions(const HistoryItem *parent) { 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; - if (reply || !_title.isEmpty() || !_description.isEmpty()) { + if (!_title.isEmpty() || !_description.isEmpty()) { skipx = st::mediaPadding.left(); style::color bg(selected ? (outbg ? st::msgOutSelectBg : st::msgInSelectBg) : (outbg ? st::msgOutBg : st::msgInBg)); @@ -5946,29 +5656,9 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, bool selected 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); - } + fwdFrom = st::msgPadding.top(); + skipy += fwdFrom; + width -= st::mediaPadding.left() + st::mediaPadding.right(); if (!_title.isEmpty()) { @@ -6036,11 +5726,8 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, bool selected } int32 HistoryImageLink::resize(int32 width, const HistoryItem *parent) { - const HistoryReply *reply = toHistoryReply(parent); - const HistoryForwarded *fwd = toHistoryForwarded(parent); - w = qMin(width, _maxw); - if (reply || !_title.isEmpty() || !_description.isEmpty()) { + if (!_title.isEmpty() || !_description.isEmpty()) { w -= st::mediaPadding.left() + st::mediaPadding.right(); } @@ -6065,20 +5752,7 @@ int32 HistoryImageLink::resize(int32 width, const HistoryItem *parent) { 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 (!_title.isEmpty()) { _height += qMin(_title.countHeight(w), st::webPageTitleFont->height * 2); } @@ -6129,39 +5803,11 @@ void HistoryImageLink::getState(TextLinkPtr &lnk, HistoryCursorState &state, int 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()) { + if (!_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()); - } - } + fwdFrom = st::msgPadding.top(); + skipy += fwdFrom; height -= skipy + st::mediaPadding.bottom(); width -= st::mediaPadding.left() + st::mediaPadding.right(); } @@ -6411,7 +6057,7 @@ QString HistoryMessage::selectedText(uint32 selection) const { } QString HistoryMessage::inDialogsText() const { - return justMedia() ? (_media ? _media->inDialogsText() : QString()) : _text.original(0, 0xFFFF, Text::ExpandLinksNone); + return emptyText() ? (_media ? _media->inDialogsText() : QString()) : _text.original(0, 0xFFFF, Text::ExpandLinksNone); } HistoryMedia *HistoryMessage::getMedia(bool inOverview) const { @@ -6464,15 +6110,15 @@ void HistoryMessage::setText(const QString &text, const EntitiesInText &entities } QString HistoryMessage::originalText() const { - return justMedia() ? QString() : _text.original(); + return emptyText() ? QString() : _text.original(); } EntitiesInText HistoryMessage::originalEntities() const { - return justMedia() ? EntitiesInText() : _text.originalEntities(); + return emptyText() ? EntitiesInText() : _text.originalEntities(); } bool HistoryMessage::textHasLinks() { - return justMedia() ? false : _text.hasLinks(); + return emptyText() ? false : _text.hasLinks(); } void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, bool selected, InfoDisplayType type) const { @@ -6691,7 +6337,7 @@ int32 HistoryMessage::resize(int32 width) { if (media) _height += _media->resize(width, this); } if (displayFromName()) { - if (justMedia()) { + if (emptyText()) { _height += st::msgPadding.top() + st::msgNameFont->height + st::mediaHeaderSkip; } else { _height += st::msgNameFont->height; @@ -6837,7 +6483,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(); } @@ -6928,17 +6574,18 @@ void HistoryForwarded::drawForwardedFrom(Painter &p, int32 x, int32 y, int32 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); + if (displayForwardedFrom()) { + drawForwardedFrom(p, realtrect.x(), realtrect.y(), realtrect.width(), (selection == FullItemSel)); + realtrect.setY(trect.y() + st::msgServiceNameFont->height); + } HistoryMessage::drawMessageText(p, realtrect, selection); } int32 HistoryForwarded::resize(int32 width) { HistoryMessage::resize(width); - if (drawBubble()) { - if (justMedia() && !displayFromName()) { + if (drawBubble() && displayForwardedFrom()) { + if (emptyText() && !displayFromName()) { _height += st::msgPadding.top() + st::msgServiceNameFont->height + st::mediaHeaderSkip; } else { _height += st::msgServiceNameFont->height; @@ -6948,7 +6595,7 @@ int32 HistoryForwarded::resize(int32 width) { } bool HistoryForwarded::hasPoint(int32 x, int32 y) const { - if (drawBubble()) { + if (drawBubble() && displayForwardedFrom()) { int32 left = 0, width = 0; countPositionAndSize(left, width); if (width < 1) return false; @@ -6963,7 +6610,7 @@ void HistoryForwarded::getState(TextLinkPtr &lnk, HistoryCursorState &state, int lnk = TextLinkPtr(); state = HistoryDefaultCursorState; - if (drawBubble()) { + if (drawBubble() && displayForwardedFrom()) { int32 left = 0, width = 0; countPositionAndSize(left, width); if (displayFromPhoto()) { @@ -6993,7 +6640,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); } @@ -7011,7 +6660,7 @@ void HistoryForwarded::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 after = false; upon = false; - if (drawBubble()) { + if (drawBubble() && displayForwardedFrom()) { int32 left = 0, width = 0; countPositionAndSize(left, width); if (width < 1) return; @@ -7035,8 +6684,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, const MTPDmess , replyToMsgId(msg.vreply_to_msg_id.v) , replyToMsg(0) , replyToVersion(0) -, _maxReplyWidth(0) -{ +, _maxReplyWidth(0) { if (!updateReplyTo() && App::api()) { App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId); } @@ -7046,8 +6694,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, i , replyToMsgId(replyTo) , replyToMsg(0) , replyToVersion(0) -, _maxReplyWidth(0) -{ +, _maxReplyWidth(0) { if (!updateReplyTo() && App::api()) { App::api()->requestReplyTo(this, history->peer->asChannel(), replyToMsgId); } @@ -7173,7 +6820,7 @@ void HistoryReply::drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selec HistoryMessage *replyToAsMsg = replyToMsg->toHistoryMessage(); if (likeService) { - } else if ((replyToAsMsg && replyToAsMsg->justMedia()) || replyToMsg->serviceMsg()) { + } else if ((replyToAsMsg && replyToAsMsg->emptyText()) || replyToMsg->serviceMsg()) { style::color date(selected ? (outbg ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (outbg ? st::msgOutDateColor : st::msgInDateColor)); p.setPen(date->p); } else { @@ -7208,12 +6855,8 @@ int32 HistoryReply::resize(int32 width) { HistoryMessage::resize(width); if (drawBubble()) { - if (justMedia()) { - if (!displayFromName()) { - _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() - st::mediaHeaderSkip + st::mediaHeaderSkip; - } + if (emptyText() && !displayFromName()) { + _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(); } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index d416ec53a..1de6589c3 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1060,7 +1060,7 @@ public: return 0; } - bool displayFromName() const { + bool hasFromName() const { return (!out() || fromChannel()) && !history()->peer->isUser(); } bool displayFromPhoto() const { @@ -1170,6 +1170,12 @@ public: } virtual bool needsBubble(const HistoryItem *parent) const = 0; virtual bool customInfoLayout() const = 0; + virtual bool hideFromName() const { + return false; + } + virtual bool hideForwardedFrom() const { + return false; + } int32 currentWidth() const { return qMin(w, _maxw); @@ -1230,6 +1236,12 @@ public: bool customInfoLayout() const { return _caption.isEmpty(); } + bool hideFromName() const { + return true; + } + bool hideForwardedFrom() const { + return true; + } private: PhotoData *_data; @@ -1279,6 +1291,12 @@ public: bool customInfoLayout() const { return _caption.isEmpty(); } + bool hideFromName() const { + return true; + } + bool hideForwardedFrom() const { + return true; + } private: VideoData *data; @@ -1386,6 +1404,9 @@ public: bool customInfoLayout() const { return false; } + bool hideForwardedFrom() const { + return _data->song(); + } private: @@ -1683,7 +1704,7 @@ public: } bool needsBubble(const HistoryItem *parent) const { - return !_title.isEmpty() || !_description.isEmpty() || parent->toHistoryReply(); + return !_title.isEmpty() || !_description.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply(); } bool customInfoLayout() const { return true; @@ -1713,15 +1734,18 @@ public: int32 plainMaxWidth() const; void countPositionAndSize(int32 &left, int32 &width) const; - bool justMedia() const { + bool emptyText() const { return _text.isEmpty(); } bool drawBubble() const { - return _media ? (!justMedia() || _media->needsBubble(this)) : true; + return _media ? (!emptyText() || _media->needsBubble(this)) : true; } bool hasBubble() const { return drawBubble(); } + bool displayFromName() const { + return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || !_media->hideFromName()); + } bool uploading() const; void drawInfo(Painter &p, int32 right, int32 bottom, bool selected, InfoDisplayType type) const; @@ -1857,6 +1881,9 @@ public: return fwdFrom; } QString selectedText(uint32 selection) const; + bool displayForwardedFrom() const { + return (!_media || !_media->isDisplayed() || !_media->hideForwardedFrom()); + } HistoryForwarded *toHistoryForwarded() { return this; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index a405f021a..0da2a49b5 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -6645,7 +6645,7 @@ void HistoryWidget::drawField(Painter &p) { } p.setPen(st::replyColor->p); _replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), width() - replyLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); - p.setPen((((drawReplyTo->toHistoryMessage() && drawReplyTo->toHistoryMessage()->justMedia()) || drawReplyTo->serviceMsg()) ? st::msgInDateColor : st::msgColor)->p); + p.setPen((((drawReplyTo->toHistoryMessage() && drawReplyTo->toHistoryMessage()->emptyText()) || drawReplyTo->serviceMsg()) ? st::msgInDateColor : st::msgColor)->p); _replyToText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - replyLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); } else { p.setFont(st::msgDateFont->f); diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index 37f4d41be..f072cc85a 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -207,7 +207,7 @@ void NotifyWindow::updateNotifyDisplay() { item->drawInDialog(p, r, active, textCachedFor, itemTextCache); } else { p.setFont(st::dlgHistFont->f); - if (item->displayFromName() && !item->fromChannel()) { + if (item->hasFromName() && !item->fromChannel()) { itemTextCache.setText(st::dlgHistFont, item->from()->name); p.setPen(st::dlgSystemColor->p); itemTextCache.drawElided(p, r.left(), r.top(), r.width(), st::dlgHistFont->height); From d6893d09dce3eae0247a8d5db7739cd9eb70738b Mon Sep 17 00:00:00 2001 From: Ali Vakilzade Date: Wed, 9 Dec 2015 22:43:06 +0330 Subject: [PATCH 010/145] force telegram respect my workdir settings Signed-off-by: Ali Vakilzade (github: aliva) --- Telegram/SourceFiles/logs.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/logs.cpp b/Telegram/SourceFiles/logs.cpp index e13de2b27..41529c94a 100644 --- a/Telegram/SourceFiles/logs.cpp +++ b/Telegram/SourceFiles/logs.cpp @@ -223,7 +223,9 @@ void logsInit() { #ifdef _DEBUG cForceWorkingDir(cExeDir()); #else - cForceWorkingDir(psAppDataPath()); + if(cWorkingDir().isEmpty()){ + cForceWorkingDir(psAppDataPath()); + } #endif #if (defined Q_OS_LINUX && !defined _DEBUG) // fix first version From 009b326531bbc759def8370a6e8f4444b4475d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=97=D0=BE=D0=BD=D0=BE=D0=B2?= Date: Mon, 30 Nov 2015 16:16:06 +0300 Subject: [PATCH 011/145] =?UTF-8?q?Do=20not=20depend=20on=20MFC=20Signed-o?= =?UTF-8?q?ff-by:=20=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=97=D0=BE=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=20=20(github:=20OZ1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Telegram/Telegram.rc | Bin 5540 -> 2769 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc index da9e0cb32f80976b6f0636bb2d99a7ee2d15221e..47e1ea11ca72966bfeae08054698761e636962f2 100644 GIT binary patch literal 2769 zcmcImUvJtl5PwJFJKVCDRznbIJE@N$F)V6G5J}J~Aw?M&uofI-n{}1?;dc%p4Qt0H z4g3<{`R;swes@l%10yyUxy+XercCbA4D3&zU`06IYt^_0PEW?sPgZtUO+d zd;@AtAtGNR*SEdcA<(M=c@x1C;}}Hg{~28?^0ecakW<;XKt5oylai$zuJ)VjDt1|v zh%Mp6zD$cNYw#6I6bA9cCW+&D*YNTJ^~1=T43kM5UN;+_7xtQUHZhi0Q&XF{{lCqvh7HUL)*vNIDwOMG;!+320n zc!j%Bp~aYq8AtWPWZlrs2}6I<`AGQ!%YqBTsd}eMoA&R_$kgb>yWDxs4>>DIcw%Y| zS^h+&0@r~bX||)V&C)rJgC!HlRXI>DcY;E?-DYf_DwcC#r7eWpCS_a#(vnF+ zvkc@jk{gLV=sIrV+JWEILD!b%)yQgVgvA&du#C#tEEQ@t)A#l6KDY6UbvR1lsSY@E zA}>-KAbgcExrVElGd+ewh0|F6e2QU#3B87SXwQFP@t>NX(s7!e6!%|<}43_5T$QT)4au#!m;RaOpvD~3mWHPja3^w zeJ`*dzzCIKe5HZx@})+u#&}JvAorr?t$AAT-vwI8lrH8GpoZqr9R`bw0x#8hP z{+w{{S=V~jw}Bm6%Q|*upRB>^A>$Kk+pnzj_&v6LR@#gseeqDeFLD=qXHE37tm6?{ zy0<&zpP?P;p^wy``E4Qf0dh(o4c2#Q%NF@(8=PbHuZwee{Z~(8k)z-pb95z78X@<3 zz7aJa@m8yk+&TPw&do}=3FpY{>{V{%b%EWJ%q_a{8u}@Bn=ZkzXGi!^xJ5l~%mAzh zc+wgE)aDc3zxiD0$v!wf;kCjs;J&@~RNJhcusWb`de4k25w3^t3T6}BJ>d4C7S4!v z1HSfHZR6w8L9n)@D7Uum!Vnoga{ADpsFp3W+(f&^zU)TFef4T}v{iQZwZD(-O&=9B zbBnJy%AVTJBGNX{T+oOV zk~^k&5`!!}+~dgI_so>@RVTFY z8)tv`I$`cJ(*%ffUkRu>Q0AU4U}v0kUc;&^l%*Vy6TVxZE*Q_~#f|gxEMFn3-2tNt zA^{@st#~L-lrzOoIIXHhbxUG@Eq3qdLmm{zEuvM6IH#IHH|nu3!aW9)-K!DJ(rV}k z{%+we!QW5eehQZwNnY`~YwzqmoP~L~59MCCZVsrH7iPJM{uVtlyH}lY4L&k&Q3fR~sVL8NUsx8C58X5Q-Hi)FU0@M1Ci(>!bssg$5f-Fpey{ zX&-2H?)oOP0NsT`hgdWq4)vdSCQ>-^wNeufkExP+R6L^c)!qdifSKP z%?0&UOaVjv;rta|o}jC|Y$@dczmtJiB`&J@6CL;quy5f}-`mBRRAYT!Y+W>$$C>Vj z`mh+PxZ+VY*+H|?j3V+3n=5l%W2F$0Tg*oIK)wbk#unpy9;+nT!LsUq<2R2fJyTSX z#ogEsv7=sb(m@Tr_1c>dNycQb6Z$(WQS6PVJ)G9BzByH{~`RX}`Rp`ET-RHsV)nEa|f?#O25SvsissR7;{P sHA*gyB*E4}lUE2jLvS5cQ)E~+&ahvoi0Cc5aIvie`us@;X@2T1*sLI3~& From 6d74140e393ff08d3c712978759fd610aa30a641 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 10 Dec 2015 16:37:45 +0300 Subject: [PATCH 012/145] documents design improved --- Telegram/Resources/style.txt | 46 +++++- Telegram/SourceFiles/app.cpp | 3 + Telegram/SourceFiles/art/sprite.png | Bin 182674 -> 181869 bytes Telegram/SourceFiles/art/sprite_200x.png | Bin 245523 -> 245695 bytes Telegram/SourceFiles/boxes/photosendbox.cpp | 2 +- Telegram/SourceFiles/history.cpp | 166 ++++++++++++++------ Telegram/SourceFiles/history.h | 9 +- Telegram/SourceFiles/structs.cpp | 50 +++++- Telegram/SourceFiles/structs.h | 6 + 9 files changed, 217 insertions(+), 65 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index ccf2a1942..5dc633a2a 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1090,7 +1090,7 @@ msgDateImgSelectBg: #1c4a7187; msgDateImgPadding: point(8px, 2px); msgDateImgCheckSpace: 4px; -msgDogImg: sprite(213px, 93px, 126px, 126px); +msgDogImg: sprite(216px, 93px, 126px, 126px); historyPadding: 10px; collapseButton: flatButton(btnDefFlat) { @@ -1171,10 +1171,6 @@ 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); @@ -1197,7 +1193,47 @@ msgFileSize: 44px; msgFilePadding: margins(14px, 12px, 10px, 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, 18px, 20px); +msgFileInPlaySelected: sprite(180px, 164px, 20px, 18px); sendPadding: 9px; btnSend: flatButton(btnDefFlat) { diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index f932265f3..4b326a577 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1612,6 +1612,7 @@ namespace App { convert->thumb = thumb; convert->dc = dc; convert->size = size; + convert->recountIsImage(); } else { if (!thumb->isNull() && (convert->thumb->isNull() || convert->thumb->width() < thumb->width() || convert->thumb->height() < thumb->height())) { convert->thumb = thumb; @@ -1644,6 +1645,7 @@ namespace App { 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); @@ -1658,6 +1660,7 @@ namespace App { result->thumb = thumb; result->dc = dc; result->size = size; + result->recountIsImage(); } else { if (!thumb->isNull() && (result->thumb->isNull() || result->thumb->width() < thumb->width() || result->thumb->height() < thumb->height())) { result->thumb = thumb; diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index b40fa3297048d961b262bfac8fac2a518d6039b6..91a9f8ac0e266a5218fd152a4867f8bcb7726a73 100644 GIT binary patch delta 57917 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

>Isw_*?c)} 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

mNm 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 delta 58752 zcma&NbyO4&)IU6QcM3?SEZyBHhzMW0rMp3DmhMJUBn1JHkdzJyk?vkZM5Pv_^Bte( z_dDl(|9j7Yv$M0avv+Ra`?>cZ2`?iPFHHgkr$KTU4Ub<`UqDn+P*_qxEE6AH>i_fp z1-cBYgdM-QfQY>ik2t@LEswB>uqcm>y}dP$xR`{!wV;TAwYY%9r%&jH=>Na2%;d$9 zB>aER8?2CVE?z%L#{3Yg2Us_xmU4JmLnDfQEXu|O4 zN~EM2X%_yU-28uD41bGR9$&D{bRCgqFckfC)cN;WaM8soPRcf;LIgSvX$ZYAoE3Nk z1Oaha#3+o|@E4$C8P;>6VpMq&xHQ0l@&nDvL<)%57B2@4myCIh+C0;309Ld(G-3=! zqc%sul0p*sLL?Uj?;0Cm2k@f3|BcU4ASBjz&@vE9!~wXX6w7)ddif(5%NOx_*N;A& ztt9*^s61jV@0R-LVW~#T+Z_z#GKRbW(6N8?0sLqPFp3Bu?vEvHo(9^mtzwa*E(9;H z1PcgJ`RRJCfKcF+Lcr;135q98?;~Kje|O1`UrZL)DnW_>yg6v)!-*I{2lBMCH8NO@ zvN3>MtqPX@0r1BVt;cPwvIHrsFEu7=E}E4v{5inBRYpY;^|VCZNZkP#K}QAv!N3Qn zQLuB@C2ETZ!3R{JQ1hviZm0rq>wbsu`U6t9IUtI|Fhs?xdL0nW`z~o~AQlTXkg|97 zFe6%vy~D8ciD1F_B0YBg1bMgp{rRd#nz~X%l)oAsJQ!sS_ANfhx@48PPwkRADiyaE zA0R`yMMZD}Uka1?D^T0a#nX|5;$e*-(ipoKy>}7_1L|j~cqfos1M|QJkmmfCjE%U+ z^65xFg{|OnmwC8w_p9q?{RP!o6Jb1N&FLTiE3kNk0mn?M~gge@8UkW z)&8EudkzHB^_l}q2I}O8{oY`bI^cow3k!j!fP2i82bH7I9d$8#W6cNvWk$Pf6KciDrJ^1oE6=b8>-bKj=~ zp2KE_Y?04Ybl?HwZkUaQn8J9yuK6Qe%8P`8~%e^(DYUJbXMX2r*SNzI+4u}nf5V`mq(=yz^$M#X*vq6Ff) zeejU>M934su~4iokz0JIffVxR;b>SDL=3XG23s^7!NngHk2bm#h|!-FN=7Fn;D(8C zW|kP%Szv=iL~1Wyke-~Jn25Mrvf{03=xPy~>pq^hP<0O%wn`UfZ3F-qrNJ2uLl7%0 zW5~ujeAAtDs1TSikF{kQ3I!(ml_Gt)8)vRy-6l1{Dc1r`a z==TLcG>=cnchR^UNxwb%saK=uONB8`9^+?xCIUIFC&EVoK|2@28>SOw_3 z=fh2EW2POOTxuY0tEi|jVx#u;^?kH+ezzGcter}wb5ffQTRYH+2$+AKB5R{j4mMXy zHqdB(cxb9I^q$ zoPaOl;{7%E$`r;$Z#Wv=Wv}Y$fhr)@HjAY_%b%x32)3f1=#uX=)>-bZ3-t+>n6ke* z{vmrNXUT)6aT*ZNA244e+!a7q1wveKo!8?@7hcf1-9#UYa?ktmCG)0G%H3Gi(+mvY z2r;~Sb>)4J7V;)~DAuE0mwHD9dZOJKXV!zd4BRF2)a38q@|@T(SO2}Il6yO zPd?obu`U-mW>^?v9B$w(sleaD=GcY0UcXM#+F^tuji1Pt_wK zwV&~J?xZkwEjW(RQkDJlJL>4Y9G_*1uXW$zubr@+{^1(EX#NF!kEhgnh!V?tza(2E z3ivV#T*cEK#WGL#H#UA05gD1DogD*7+>}il4;x$F%#02n6V2IqZF1T%9Ur!dVm144 zw|!N$87A|hvsUEp3KW#|@iY(4jKZzzyI@AW0$m-z6=`)2zK!l zA&-@&-{`|$xe`A1Gz!GzoiC}ZqZ{rCdPzMT(zw)7TOmK8p$oEnV^9y6Cvay@hc_@Wz?u=W`2QDqnYC;p#Y zz`aWA$q%+h&t!ANEIEUR3WIYX(^c`jo|Pzzm>AWoSFa4xHwXv_(h{8g9;;V9U(s12 zHTc6`SEq%8Dy;3}cDa!s)c~%)=%+>*wco-4&m-z~bf;c$J#r)8v*xG%U?ae)o;Jm< zj%kTqrHwt1k=JMXh-RNyl?WK+`7MJVlJyDwVhgL=QR6Aj%Jz>){DG#tzAm1dY{o4~ z70F8zX}1!w@9G)0TnXQ?BOLJrA}NO6Ze)878#`IT;y^^Z>6#r})_BrsX{5AUQUIR>!dO zA$z3szg%qD+zP<&S_;^d3u@547m`kU(< z#HpgyKe(3odmCL%H(W5~J2|2RutHQML7dtFJfNjtXtJu}M9vx7EiL}GJaLW!M?z9A3S4Xiz zAMY*J=~(uJ-L;(UEvk9aVL252JNO8ly172vyIHyE!nmaAR4ZTzJlhlFp`eMnyNkN- z&ZfUPAAWNHE_;o6=n0y;{WaKpR_r+TmsO*&R=RvJt-~j36=9FwfS&3pc8R+Gwm%~T zsa9~1{UcIo{lIa1D4A;&Y;iMfcsZQ%-Mqu!Yqiy*r^#)##cmUP2-Sc3?AO=V+{c)a zTXtO*4~f%`(jR7ji~c#g=9qXRLH5(ykRiPF!u8>IWf3!-$o0RMKD8Mg0NFrwI2t^b z^R1goo(YZu;AZpXbzz6pzSKd6*IR~9>6AjC+Q!MH>bxlq8UgH%0%N8I9J<;;2=BVY(`z=--a*d2PNG}zd z+6l3puTK*&Evt3d#*f@0xDi5%lF%L3_j2-F8B9VLyGcvydHQ+la9 z2~)b&8fN}k_z4WQubaUdONyR8=w&85)`(S8T`g&q8R^Cp)4(|%Scxz7`LoMa+ z$`=8GoYEwHPJrD<)+9sLMn}Ct+>98dH=nsl&=uf}*!VQUzwY}Yv2qCMrQ7_ciWPL4 zPm2pg-Tu6MN(rftOa6b3`Go&*lDo)V|0)9luDQ9npSG~!wVTNrDSIn~g@t*zt+h?p z#`#)*FTzGN$bKv;TBO3*=~Ibc!)Ee?QH!43k(XAo^XPzlE4UkLq#b+5(~$ zbKC%Nd_sP?D;@Lo|-@t%-M(j=kOhF#}vXYaZI zE;_#gV{$jDlX44*F#uP%6zj7OE(O(hb_tV2Z~7`Mm0U2d`mh)VzuGp!Qnt@?va`=k z+B~=VJC3?U}qEaBs= z(xNt# zXiGskZ)0PlpnKa^%vJ>XcP{HGAmMSLJT+XcyuEWB3DN|X@od}l6Hq)9W@l4+9j-|v z7pq=#tLW!#1@)tXDcc-(;;hfjF1*S@rWjT=hTbxdXG~5CoNX-67iy>P$W=pisILi~t zX9)`4cwybi^*H|iY#G%pQ`D*9oxkjQe;&&aR&P3g482UP`_5`_z91Kwq-{xDm1{Fs z8Qt+OqG~YtW62@+6Ij^#`@+<;pi2MA<|nqO?CO$EbFazFZqq<6zEZpJU^?J$90^N| z+j3*{8_4Z7{12`DQ$YA9yaW&`q%WkW6z<6@g3_&^wBboVT3DRePE zMTrCYfHs7BzY%PF>i|~{00ZVnYz0Ny#R|rMZtqSMj(G$rV)t`ikC$8{;yK<<3E

`v6}2umupc#4aG%r{42dP)$Tx#l?BCA5FszcS>52OcwMlsz7>Ab%;- zPfN8II}A?cB@fw8j`chX`xY+;^y;P*QN4R{^;WTjx(;%JP1&$PvJNrs!}i$#ZeF{T zVkXDmcoa#V@D8wu_6Y3m8qrDzl9!OAcC>p zZY(<-)i`&$(TMm3bugGx)+aYb;owFHjJ>KmiHi;VmOQ`H%KYN;t9^zzGpSB$$1mqk z1us_+_EN0|kOO*ESxNOJW&At&rLy}obN52|!yW&F`Bn@HRt@&{z-JjiAPJA0!+Fmv!y>}yMpr!K-(H9x}Px16XH3j~<|b&ppR-Rg z!~-aHpehgp*r-#2bhv-C+w>h28-}7Hg2*(R@HtD(lf}CLeE-vz&!0c%9QNXipOZ;- zu(d6ZzXvziq{)A-tgIwY5Y8 z1{q&ik)p|Zh^|T#O`1YV*qD0-r}M@^>!!P5Lg(H2>VM=a<1dZYt=ko8N*{@aL1o>qV${!2rm34X2S zKC8C9X*CAF8bKp{{W-SCJ|rDoAmBx3SU3CI0;&%xR-=y|zfx-VZa-+6sV&g`-Q3(< z*9fEK_xm%)uo3cCyXJ4$-*GV(NTlF3+E^pXzt4kQTql$79C#gs*PTT34`lh+%i2vA z-}mhO#R5IAI*rt!x_EjNDHRxtRV|W=3)uIkcLnku$ISilYk7CrcZI!rGH;8s0n# z#CeMouSfK&^jBl>A2p6ohgWFjHI_6qcrgy}b8EBfMb`&)_49wLgesCpdX>-Ck4AgZ z(9i<(4GboB#NEjhKw=R^a0fOPW~s05>)`14YjhzadM7}Qw-su+K|!=-%(l@gf>Bjj zO|8hh!RAW>=OXYy}!F5T7h(X>hdyh$X3t` zf4~#!*HrvVfk&XyUZ_i+l6dH|DRr=rSY7)ftwydu7*nt}Z=TPIfPs)5F>Ab-Y=mJHL(w+S zx+5KA9i@w4j3|8UI+XNi7x4vuP(VN+8o2)*{c)i`dT-yYb=`m|P{)l*{Uy&DxA40F zAN*^|qz;~&p#%!y1J4pIl6z%@4id?-qY`+ zS?V{6Edgjj^#!Io6bRa`einR^NZA(k*pLQ4Ks5$hmog;MuYJTorWJ(hm^b;Z6N;@g+hVNQhbE5NZ1qP|uFij&x!sG%dxdtB>D+t`btJ)! zKdS_9(2sltX&1cy<6%#hg;JD6v0!Hk2hP@? zIclS1w-WZA}q&VQS;38!|_=O|F`i^~ys{bT6t0^UB$E z4}NNkQcle;{NHFb-<_=Q9kcZuw5)>9`o_OTwXXY{N=S2C-@K$H?9#sWm!~9M%blXY z{St`OVBip6`B~d7)0+dRhS#NAyY4u71G!mQSsVSE8`Y0^v5J%qEWan)^i!uS>@BPm zvi?beeWe{|#9`Ft8FkRmm?it#HG$L5thVNe^yfeTXOgE+XjD}36ti&%&4yKn1&5W9d^zk8o=ydG=3 z7B@CZ!`K+}U<#Zk-(|^31rS75(fq37nfxJ{tl09Co}4biO2hSW$CaKAlYrLm!&I*I zJL2l{&agGNXo8ViyD6y`I4O3kH%(YTh>1_4q$VRy&1LRJSFjq?f28G1xUMndO(kW= zi>*@lOaqmR>!o?Kg*c1rSwC7bKert%EYMwKQCnQ^#~Mn`-UXlatsbN-X6m!NeRi_( z?B#d#vEItQBK+b$HQraiCF#uA{v9WK(uS-GIl7hbU)B&k|Qr&g9LqGv;FJB2vo0H6ik< zCUF>?%Fx(Y@9tv4M+u}4sn~6hm8S_um^&85qUp%uSsUSY`-*Gu!=6_Ol?_>`=4K2o zQM0jsIi`OWsz`Alm_k#FVw&g|hoc{{N3GFN)O$)p+ka^QmOTGProP2ppz#a)Cmsm6*&IdV2AHcPD+^=3DF1B9$p?*^_XxHP$kUG^I;ulZWhJW9J^D ztVh)zd_|g%FITwg@G?|cR^FetlBFSJO2e>_5&Z0J%t?eSI6`>(wBO!O<&ki*OpEEK zIK?}!z4?`T4{jy+Gt55lE^LMHL?$_+!d980n6QY65J`3eP!6Ezl<%%dpg4QFO+i7i z3Ke1#DDp2~Zu918*<-@1tE-clozPr|)gk*igu& zJBmGScv6gJS-;6E#@wqjP11rV5Cn+(octI~o12>ph(6uvWA0bSAtI7<5vJ*RNo~)f zJ*#7kzXdhe$9vNwmjn2Nq(+21H>H&zc z4B-Bu09&J@1g-IrcvMI-yb`nzv=LNgz}!(op36K0?63B(*G=qiP-12#j4 zC0Ga+QmCJ>;|H)wXWL06I0xZ~uXel6o3buvfpDMVpMOZT>^uZ77SK0QxU@u3#c^dT zH^&h&*{6u@*e(>@kjp5_kmnnQs7`I;g7SYGv)oYgSv^(8;Oya`cHH1cW3NllN{h9^ z_u>Ml5rA9b3AdG^>D$Hzw@2e-j>u<@`VQI(J{Bmb&Fsk=x6hn*bcI^m$*N@y>P_p8 zf_W)znglbinX+LEAB+dunehOma=#~AsQ9(Gvx8_sfh_(2+&b6Z%{UulfFJrIKTYzC z_F*R{CX)2hw!8qn9~Aj3)GQ-gIT5PpSEGI2kDpOH14gZ{hJB^DqqqQfU`j8LaROxtbHmZDne43&j zQ7FXL*!r|+84V>ZN}t^cw`1q^^e99Mt4)JKyj*tZ5AXK>!zBhi7e4 z4N6GV`)F!=G$StJ(EtR?mm{YGz*g`P)KiRwcmm~V;55f!ItyA029H42YDNr&QfnH_ ztg3LZ+&YziaOR)jXp#+Ppd1yOh{%K88tS0?9&MQC+tleavEO0gAn+7yuD%Y09$_$O z-4@%OXEzToV{)tSf1ZiM5}K2JpDg%tmW`kvmAtz+g)~#5H(0=EBm#pUvzH0Dz$5B+ z=m#~w7b7p2k*c*{YT5AqS;_zu3qL27Z@D$rzfXrftb8&y3Tx%CHqzH#|Lo@zWcJh) zhjbs|16;)G8y;p1bs@-zNdfG!@;TlO@&**)u!+U*t7|6T_O?ySz1ox`yX-JoW3W)) z===sf(2(`NjwaC?mr$%a>ks_5Za4sto}xtt*UG_V6cvCPgKS;yJd6av=UApEp6u33 znvb5zkL-9ue^F6UzN33?egY=H>3$yEOa75udNUvp4#PGZliy1a9<=R45myaTnDeq^ zXNE5UkLcIT!}(JFyPW$n@pZ8@y;$Lz(Ada6E>Coo2;SCfd^QDA54+rs2j^Tme8n3a*t0n)cifukgmmvm~ zIOC%!DXr*QJJ|in-hXS?KC+^3YZTR9u^({HAdFt)Y`LYKwSW}$X_uJD`CV8bwR}Z! zL=bT=8Un8kKiqG=ortM=jYHLh#?qh!N>a7UnbI+ID6@}jZlWv8t#6o3Eldo{OBOB1 zE>xlLlQR|%nG&A_338;L#Yt9zhhURAmn;$7GoI>{y&?mD=Fg>#H_+mh zH2DcIy>AUb`@uRDid^4nLhy(z%D78u`mKuYrxTH>NJF)&v?wO_<1YxAWy z?d`%Istociion&Mh#FD+u}?$%Nw2RpTp4@-JP_p;Z$XqmoCU8TN_UQgaj|en>4G3M`V2J zAe~07&FFk|`BoNeFe-g9onz7)=xb0XET57R1a4fg*BY8p#qN#ZM?N)(F5xHR$75e4 z>1qU-wKT3*gshL=e!wWo+Gj^LM~UGS1w^|Y0}Ji5?L1Dtl#S}z+J4twRDXYt(|+Us zMq>D?99G^39XM1e&|&9sVFw0Q&_7>zd|LhB{Kc$p= zcs29qsCV!_(yi6F-n#Y7uftAj2=VIg&-Qnv^;F=r@w~Pe{!u{3&ZZpUo9mJQP3Z|U z*puTZ*kI^~JDHok7G~UluwC(q!@Ix!;DHKV%g|v|REsvAV_xXr(BDkW>U0|6N}}q9AAOha;l@)N3pkzwQI76wAH1=N~MASLik5_lN2@U3%YNM*N2h;u$ZU&rfsv zS{~N1l~4COd|T)}nAurSq;}}rWjf(9Voiwx-ii}0lIGTzpm!vSSo{XTr{S1Xf5x#- zyIk2EraJ%3V<95Xnfs^=Ujgz-$abPmT6fk!rOV(M;Un|WAG>lo_P4jY8i&xq{k)!E z3}2g7%P*!*BmEb~b+r^ar+UNqxsX4E*$Vi5gSXo>zW=be@R_?%-P1$CX3udNx3P`L zy=^VQ=jg_D3mwm^9ISGBukYQNdr-KP%o9yBvLo1?pXssnetfeoQp_H$lcSdhVQ&9G z)Y&?xic|>7=CJ358@^lo>M{aYP3ztvCK6%nX%m{%HZzxJliQo^N;Kyq-xuXl!CkjT zM(oo6idqiq%CO5RY>z3{;%15KYKg)xb}x+iVN)z|LvLTmfGtqi)U%OKys37adTAI> z-wSIl)J!?-Tt^ofEeHCGPDW?Ra|{Y?{;rZB1s*9WuVUh@x(cCh za9*7U=fK<_Wln$7oM?pYPE-n6#tBg+YW}-7i-r<&;$8#{3g3n^#80Vj`gZwBL$8vI z3FC;08~!E`7T^izE--UN4)8_OpmS1mw}B@$C`COR5u3mo({(U#`Rix^;iGO`uEYaZ z5DmQ(b(|_5UeQE%=Ps|PXo*iM3O*}eghHibK6^*M>$dvL$aqg95pd1=B_(U5R9gY) zyiTfcni3{>H*>I}2n}lKX(-(m8={RIk{c!i5#;2uk`}9_cYONJ$7&6PUg3h^C8F^t zz(@^tkK65;fB{mme=-K%q9I}M9$e+dsh=?(=^Fw^>Xf3a8+MQ@`;|#9^@16%Xjq0hkK=CDdavMcK(=!L=s!iZruacvi2zz zNk0y)B2r@1W;|bH`!%S!Q--dPRLQA1&i~CT+wLLY>SV_*Rp#8NmQS6AvH%lM&-iji z6pVRTCxvDW04e}(es71G%Nl!<<%!695t6uzF&xj}Whyp}G3`u@Kl(0ShZjKpEp)obVL>q{gO+2cbemc%-${%P4v zk$CXq?d|Pr2-N$SfgL$~fjBd`@0JbBM$168TM9Ccef%Xkmw0zq(cZS4ik^WVT*fT8w5{8Fohbbo-}sW)fDW*~-u+cDpwmtQN~qRvv>Vd708 zfZU2@s3s#LlPh-$3NI?!j3JV{^(=7FK#E0fP_amj{tBun- znSLuE1Pzfsfs{`}XRO2t32Gc-v!>&l|E~U;#ES`~+n|*eko7;l5o@FDbpVb??3iMf z5d;WbMDjKSPAlag=$$SzY#ZPezcK7TwYL-(_a+cW9pNq3Sz2=Ej8O#~ZG;N2?qcSE zwop>%|K#;oXx?qP^uq_(vLa=+n|FIiJ%nk&v@ZWIhH=k;j`h0N!nrDzaMvAq88l`pG@O16#fb>`&CA9KOc9$fPFv{b*0FCs8pp-)5T^D z^v+BuQp2!VF~8Qg7n3a8N#cWvFC@Yq@hvEgy(ku+pvoi&JB-kR+4e=WTGF9<_VnHs zp|oLlSBT-rel^AGMrNR2s1Ik8F4nUkGIFqaR$Ee zk35-Te)4=Pu}Xe}GX1?NL3SMg`4ty7pdMzr^jf2&rA1V)O26Ad>Z&yxCi{OdQ@>U3_RcDJHl$*p7O`?} zj?SJRFxl$42_VmNag2?PJ+z8s@1k3N%^EZI{|6NLXJf@N!#U+>E)HSC_unyVHl2kf zrCTfM`tCJ}yLUBjU}haN2?EMiVGTEPP};;#brYmT2_V;hxSNMgJ2TL6XYx0vE@Ft%WK>bejG@^K~J`x3$_ zm~9?3Pl1+v*c0EMA0_#gSrS>?6evI0kDG9{fwLPBJai0^9L?-EAN$W&b$5rc0nm&9 zL4?VlGG1OgE#mf(enos}4v7Gow|E~|z;U-QHwPmkASeVj-tWi3G+W%)KhK=BQk8b{ z;HC4KEKl}Cjhc7XADa{%suPa&(aK60(FGgLV|Ii`qRkBlpH{jzc*G3fL@)DjK%aD7MG{y#L+ptthtbac&%p z65Cc_t6`YFrujcZ9<#Q3c}Fp*-BrYMp7ik{`p7d!=AoYc+E=_SeyAQ_3`lu74fR3Gvr-Uw|(J#b7X36pf5Bi4;6* z;}*K!iSOUPzvklN+Ij5zd8^If5b>9CUv8x@z_16!v*%oe>9Q>;_1*9+cNiurdyP?sAy?vF~Py>rTTkb zM`~v;o=CrrKC+kR96)3F@ca2oTpnT7$yJ|KGvG_alRYow{d{&4V-LXjwsp5f;^TGI zR9-FqvUwDYF%1J1i$4bM~EfLaD(YY zz1nGL9ahgPR%;_96;dMA=3Ve2GhHm+O$1q^V>XTL{QEN(?1n0c+cqB&Ix zu=45Ux#zW7>f%Pq*s*TdCFUdjsH;nf9fzOy+S0hIN8cGdH{?kh+u?%-Csu}rhN4N0 zeVw#0z%+D=vHMqeH`KHJo6_0*fQ6i_!DkPi2uecI!uI~#u21>zFpWi=@A9vL9oFSM zCf$fW(lm!X4lFGD64u~&zhOMK_FCB6CCU7Yy%+kz1`DM|nK_i8LqKONP(1tFy;+U2 zbt7m`(G+Kps&Q8H*K=vc+Z(A%effiw?75?E5MA~&_j zhG`9iG#wAjg!;y>qTEg=+!g!}r$Rr?G4_kry~F$+Q9jKJ`OSZmQvi^`T7cbETx_MN zpwJfu4KXP<#AYc|l7hVsHO+0hAdYN@-_vPc_vRFSgQDOyrG`y81|9yV?C#3;`|uj`&A; z_RM4~ARflLgizSpWI$;Wwen0=4_eym^8o&LqiFZF*63_sWk+$x<#;W{$ai?FWD|&f zI1brQAs`D79~3*`z=wMt-_1%MtMdJ5?>pHUBZ|=RTxwFsL>jhLotrc{l05X1eB5jC zlzlF{Sm@P42?6oy`Ze{8*iq z6M0H-GH+eBl6n)-_6c3(zSvcg%sWl08}V< zv|3>XE^cm}t1B5~ojyvl0ciP~B#)c@M5|6(V#I`i(rECRjGiVQBM zeB^-kvAyl{UnkAv$!e{37 z6Ohez7J!!^To7$|we?4_sK!kiMQ(1zvKX>Tzm!mCG0Zu{&T%iF7(BS8Mvj|xG#Wdn zB?s_|3;P^=y+KKXXhkkVWb)yP<)GI)4&_s2 z9N@!JVFOtQb(@&qGXFkgRf*DN%$m4p{Arlac&iY{!I59&$_aQ&M4&|=XB_0CE3w;IG)RAXTf^5E_MI|mTm{9!*=UK>;?*nco?@R@xR zEw}pHUnYou{4o7ykYW|S?`K%)UjGjC9UMZ&n%yjMLwOR z_}9jhcGOAm^Ms>)x`!Y&U$%L7p!*re!-t&1l->6qJ`7HmsR=+m6@&B`H@Sn~sHPx_ z#nHXQWyFs}$>}pOluP{H4o4OuP6yPO@l7oIkLJrTk+}MC3uofss0Q@T@YRJlbCxyQ zU``AMLqUpO3?LKG*Q~SO{i4JNl5SIx=0BTl#D~}yYOO`C@RbYK$y?6OB-P@sLgw#u zzVxT?@#W5w-L|zW1_!vA_=^?cIVs#>Zl_5|9@!M`QIqZD)=!hse>9ZkMW-5}Ru^vl8`{Mc4m`D50{c2H={qkqy z)Sj!Nxrv~Iqpk~!#KdcCU>cQ4oF&n*q!u4xE3C3G=S-71Y01lgLMWILxqrKJlHP1$ zE~uUE2XLS<;t>%I7b`@9`PY&`r<_l?WlUG&M^WX;ey2SSyqgFYqvnWMoP59)`p)*hv^7;pM4Ox2D zo*&inj-UemaGlx@F*U+%q#-ViL04VKN;wOeGKWpsR4&lAk=7P{x>hK;h#e=-Otk_F z{boG$e*1_a$ocml6#3W*OP&};<19&L0utjalXBwx(yyuJZH?cHxWlCW)7QQEXr|iW z{b-!haE|A0x2N@uqWf z%UgZwU7Q5ZF;EAFq^Mv+&2)m~a2cV+kDWe{tYkNZ9k!?j~?EcAwqr&as0oz8)%i8cHdRXVYDE zhJxi9%uxoT4$=>!7u%;n14T!2B1ds`uoEa7gP}7q6L3SzZ$bl@VI4bDWT9kbZOdZ# zof?l37aU_=56r+|G!`foJOhsa{Z>;vu8y*mQoBwrs_LdsMI?F^X%3f!&C;X!NMoqq z^;gS16<#1m?pW*fRx;p_&9Q_b^Z-)>nk2?{$G%GuC|$CR2=JjzET^BNS;=*#vM-aMT~|DE>93 z>`q_R&RC8mhDTOFo+dPDTC*nolB!EUzeNA`HLF1S_c+ce{SV8o6T9LclU^<#V480S zbUfT?eq_}FM!sdq{B7kJO7nJ}J7d&tWAjQ=PU|W1UhygVF!rIgX6^P0kBHLmM<5IW z31d73oQ!_Tn7$0uRT&4-Ff-$|;uDu1R}tQ>kMN_YW-gyZJYQFg2-8VUT=KbcKFcjl z$iK-b9r%#4Md;L~M5!H>E+mEmbiR(7s;N)XW(@ySI@+L(NL=g{jK{UwykGny?Wf*b zDEv!7IN2!1`dR?q`TqTPbOmC&bILD>+2+8~2s&PMOOR~NrzH^7V2q>c3rh>HX~ z_Ub>l#>T6EMVCK31XMTgZ1RgNr)J>t4|(JBr23?Rw6oZGX7{&Qu^Z-9Ty??Ln-_JK zUh_w9Bz&+9XVe_hm|c&5No|&2-C%4eXDX{3{?N>4E$dfeJ_@_uo>%jKcy*b2Cl~U* zgMMPq*-xoLlSvIUD-YtvB4$;nW+(h}MbaD2ibH|r_>yJ5r-uAQz|e&zW`#5c0xxr5 zu&BP}yf?M=j=v>SbxxqA?xvgmP-yL@3R-a7)zs9qL1AQP|EXziZoU#pBKzFcrGHLo?LNg7wypDzcjr6H|E4MuDAPHz|BYo3pRkzjn}vNpN;M6ghb(Xk{wH z?0rLx4-22@n1^HPMq)>TOvEo%cW}@ei&Aa%UhzY%g4tS9q%a{ z>xrZ7f;~Nv?US-M-zrvBS9`n*~Bn@4jxs+Jse9roDGsssegM?-xBR);1~Fhrb+s_;bKee4DtZzR$}J zx)GNroabzwd>eL|@xOcJS;nVHdyc4`7Jn{tdsHJZZ=3|I{`gKm;{|8RT4_6bKkUXM5v1DKni ze>V^PFQ>&UhJN_e*vK9?(x1_H9L5JSsN%?ye(u4#!3XLl4cj`Dg>7?HLg{@NfLPHB5cZ|&LNpnMT6L-am71H)=F_{oxGyTckhYfK_XbIufOoZPpT zNO{Lqol#MxnZ2F637wF5^*bTx-z89gFK4E?_9CkT+JClgBHe|SDGD(GXqO@d8ZUjk zOK2>8I;FHLkuu;=6q;>+RZGB{%t#{9DGm*hE<%q5^`~&GR1L4-S6-GS|$Sz1Kc# zm0ho59xQjb&yM!0V+$p(s;v%j>atG`Lp~P%Z>04o_Ae&-j|hZRR;#xopJi6Ei$n`l z8GK$XMDxka+4h~r@GmgOjW_Cf4|liWk%njw-5jiDI1W@>#fJJJCT2;~Fi|PrLvqWH zN7MOAcL%~!QT@0&p)z8uR*S<`FS(>HU+C)^7*!tp3{pv~nC9&XM&5!GmeDYXf=(s= zx;V*eZ~Pj+M@rS1j-oO6l-U~_%owu@tgefpnPDcTZ zezgf74f?9?w-8X(r8+Ort!lc0W0>R{uwhrGH)lK8DEE#qcC$W=CTm45x9{jn{Sh>r9qHBW9qo(+o$|@5~N=NyD_yc3#)}Rd}y4%L*cOgxXVN zTrj#vRYM5cKONHyqVWBx);s%0@6tE;>hby-i%s8b9?payA>LRtdpb5r1(~j|E++}p zU~Ar2VYid>EuE##+3;#jY0^;|p4UkY?0YS3d#~&n^#m7-Jxb!|$>u=tqb8(nOhDWv9bGI&U^#d5PR&2D7P>@`)2V(`N54lsTb<-txz1l2x&gifb4 z{{rcHgE`GkEBm#uLiI*yUHu&-UmJ_8LVmXsKW2`I5OhIdC+60ir$kDpMd8C6-})yp z?>b-2e+{}#bm&8YN3Xz3(LIgx%F6&rh)1&E#TMj@+EPXh9DtEO{WlskiR|3`bYbt& z^3Dmb&JN244~vV&TL#%3QhvmlB;6e}N445kH=2(Zwu`@pwl`I^D|BrJ;K}-vpFN*+ zhh5W5gw~4@;xB4QHJ-COiHDU9?yT`Mm<;b-t~WlLWUo`1tpN&l=eJl;=d@} zxcyKYg;m;t+u#05x5+MPVWy-^@^nlLJN@x&QmM=aFVp9yNNg&eR;dV(mqP=0vxZu# zfcr5nFPB5ej9f}EcjUU#2OQGTuF3Id7r5p8Gc%JeHaN<92MoL4|Dix=XjQ723GwmV z!66|;4rFdg|DRJ92h?ZVgVj;V9N(MzT2j_d1G?+LP=%iZEPY}}WqseeZ%rSDX>su^ z?+xwWd-VFn%Wr}9oC=I6?6BbYJN8+_9A#P z(;lAP5qzd0IH;xCYhZt9IhI}ZSrI&pO8Ui?K};}zY%o{7uKwh0NPYh>I5;I{5N}4- zPoL6MH9Qx_a(}b9*0rS~Qd}7?*$UrgUM5$C0}?jmLqkKgfg=VZmW~$AH|INXEZr6z zBjcm8fx*&@jGKZr6EZL6LPC4laE^>1{oSU)N7F_FC=$rTI;>inyyB+nf+Yp9G9A;F zgFbvcNOy$yJWIEqh-dCqw_>)Jn#lS&yG1DQh2M)$!{dc0me8H?3aBg!Ltv8cdJ#Cz zkhkW~3hTf-U)Zee&=Vo7T}3!xjqPWiFCNsKN7%^dQis$ z5cHJb;-hy9ska?NnG&Geen_A}!NZ%^5tJwtsiTSEnJ6=#|e9_~M6NWjSFtM~mrC)zls7w-Bk&U6S|Xw1nuw!Pxcfr_H8bPHj<;sapWSS@*zP&m~8;2Pbia+&EQQ| zZQvTughK!;Y-?dwm|_wH`>O>5%Fe36vp|zQ zs5ALh5H?>@D>uJus|$32&>HXde`)NlSEc1CDT|3&(E|+cPp8AgjmV8$KT~ZD6h04W zdfu@N_Mp~gaJfubmhxsK8Fv=mQ{@=5$JUCasSV#ZGUb@2x`V*Yk!`z(7wlp)`yM{3m z56w&RUk{6SfneS)y;yNaW9{4q> z)#T#~KCSpBe}H&GuqbZUFYQ+*nzc%09W6-m08*a?72F zND)O-g`QS!(u+n5ryPjkAoAK;bGkUKa&_6k+b4ODb}+$AjXb%o=-ghT^!$_5)YQbd z>IvYnb;s^~Ib6L&h4IgPx?5vQL`1~>YApm4=;&jpAe8rd{*|Z4yy<%VOE2|@ z;nl*fLRG`j`D@dc$?{H_=hVt{B|4V4ToPgD~K%jVr8 z@JtQ$R~X#^kY zOc;VUMw>t>ttHIM8W#ugJQBz^0y3%h{`U*1tj?q|*`%(a7NOg#@MX!1 zhT9Sgx7uF!9`m)oJ%Q@l%1H#h2LaJ)=JRS0r4Rp4tv=_=g--%jLtiGAQAy!ikd2SQ z;pP45e~gmF9j0Zn_44kT@!gy;i-=i?#|CM%H0Z(t?zz|Z@K!yj?K>^AddCdC8~T%f)#N=DVEgIy>g^1^2)0yuwtYERw9gcHt1+2Hjs*3c z#mxO818)VINLmA+qJ8kqJoMDG7A_NkZGR~Jq*O?fQ2S(j_XhyB#Qc>+8RC>Ez*{X8 zLvodBvDqPpXUS$|17o+oj-69tGvaKK)3X4Abk=j_Oip&eKzE?9Ew*106NgDIKD3o=*1DLQWQ@G77Y%S~YK7c^%p$Fsxu}jVyqWTrszy(CKOoj+ZGk=Mo7D1` zKjjMK*T3dvu_;>JG8Wje5ZrCS1-`I_9EO5_czyls{~P$bweJPpp=0B&DWMESR=Y)C z-D8ctLu6$JX}n?A6d`X96-~`)rtBp)(dK%xqQa*Rr#d>A%Bo?H<2mZ;8X)PbUeE#biD4*XOamK+&_)8 zy}e=h%32!p9~t=)3w4u}be562Z#%O(`6?06tZ#dil`EF;sK;w+5FHBoths+s2q+lu!|GW2q z|M_chaGnDkAWKFG-j;cIz;}3$kjih}F2Au*`%LSb3-i(EqWzr>w}@-i@P*_CRkgc% zHQ>Z%N{Uki^Spr3T*b&JYIXIK4A08^zbaNLCZ-s=fn~6PaDHUSG;?gLDviIh7;HXh zS_c_I{gDv4m;xpw^G%W#+zg=fHReoq02u`88XDQ9r5M(=q`($kB}ba>2lm#Ll}GkP zXDWv<#^qZ#%9K1c{@puCY}vgyJfd;N4x{I?c!(w+bqWk~o9DBpX3}4>Ho70GT3LBU z&8_4=v-_HDWc9mW}XqA<{}2n z+`?iZ)nWd_#lYa82g-~o3&rr@;0(fKQFy^Now7T3i{7sm?2A^R z^M5g~$!wH(;*wxMR99)g(#Fu`Fg>kB?b>V`H|Uw1%$ZNKS`f)spJbQ4%g>0Nj88^D zJSQ)XrO^-9(XlLp|AKoyxMb>lYm^Am7j)a!@1&pZNowT@_7U7zpTNsiGo=-s==v=} zF=JS0m9jlU>tk*%ZG8DW_2RC6T;)RMwLinacgE#=-qZWSP4eCETUF04P~0+>hW&2_ z%48F{uLfQ2JBXMjTz>b0jH5w62IZJBR$VfLOn~U70BGK)GA235ZV2>T( z>sS2Ycye6uC69ht=@7ps)JV5=oa`@$ls>+F$T8Ik=i?Z&JGOlsZTACr_b?_~>hrPL-#XLP^lf!RJ<%ln^NY2(x1%-W1A8)Z8}sbb81en#~BIFq3YW* z{DWV50H(S4DQv64yFxARaOrz_^25Jun1+jMa$#n`hzc2YWqor7?XZA&jzdJF#O7Y< zs8j5&+OzG~<)A!np6Uc{`(Wh3|G>ZfSgyeXc5oj%qubi^&^a1TkKX(yJ}1rtoALqU zN;{sHF??b|m7BXkYfQOhDyN}=_{WbQR5BA=KXV0t=77um>y&KcB9*pM!$GpKL>>MS zKVQFKn*x8i5b~6F&-@y2oAima=5N>io0(58T5JbQvGLEYSW>HA-?9)`OEELpQ@>M9 z=WK#T+dLKZ`Ia_SkwmVPnzvqyIZ@u4R0zs6^H>|Z5);Rq73iAOAY?GF%0+iQ(&4?D;3-ZgfxoFSMHC-F= zye{T00;g)J-ntg(IWfuovSI03<->aW_AN-W`}XbI@J|{(K4KFS6J;g>;1Bvc8{0uBM`icIIp17}%+S2lTgJ%p1&HQ#z6YMj+xY@uJTS#f5W zQa~dLfoQ}jZSt>YhNxom0qiiKB8U?IxG7>^AX1+|u`h$ky8zUfOZOL~T`ITDD~7ZT z=!!{7x*9ne^27^pEwhoAZwWiT`Ku!kt;aEn$FSmzmE;T`xtp{Q)=qoXczYk|u5AjL z&5d1VGm5=zy#Ky+%SmCSi+Q^HlDD`FNmC2b3OPC1jMS!+p~J3XWYJ=Td2bIy@WJassW_aj3vx6wu>>54lM*%=gMJ8?`}EWRiQFwob1; z?KJ3M{en4evb(dNxn|zX8XOum&DR(lLs!1I`JaA7>#yQ>oC{BO{{0`4@n3p+59+c9 zm%NP1JC=s=S4fn9_tEICw1dc3Pl*zA7p=*odpY$>_6~|ym z@ikGWYXqx^O~GPg8yT;=14gogt$ncB3k5-6Bbb-EpA>h0oi+oonOTDN5xkOOH!Iv|uHBtiC ze0#(Gf$?v^*29rN6Y)04=z(The$SZW>a<|2x)I?w>+PpeY(a>cQayr+v2a*pk&s zKUB=5PJu8H9x7$;lC+#Zk@M^y86JQ6A=r1{WAH`!UP?PNZ=={^`1$z(T~Eiu{mjX2 zP)IY8DjqJbi3BkT3CZ!LTlq*mJtkawBrBm{eQ9b0jT!&$l_YI$JCwo5`Bq|Z;oKeW z#Lq!xqSGMsfY7dKB+QIOzub#W#(d+;y^k!Q2%h5{}ltKiCW1(mqys>QQN8AkG4=d++_0WeamkJ z!F$(B>`B81vY`Yr`AZKSLvZiC9+WB?D)>2Fn`KB37){)Nyq06LvY%0@1H?`sq5z1) z2zIM$=N)@Jdqm>@w_qIYOW>SMTUXU4=I75m_U-IDn2^$j1n`TnyKIlaI7DXW2oYgG zgE`<-aEr z7XlO#IOd|Gzg@HwnJsZqViEhRG?!Je=ru%Saq@y)XG%Qd*sVLI-j;OU-guxVtO^!) zbuI`tL3B142U{@Knwi5dho5~%NxhzRP#qV^>Q|psnr!tT!ofhS6DFVgah4?O=OlRs zATFA=7Dz})kd~Eo@}^}e8%o^T+{^(UymF)On@D!e!45{fPq=FVYLysQhGC1Uioo?d z?>D=o+_fJPYCj)%>yxnUZD4j)evepGk*b9lq%yTMg$(!i-mIodaV(f ziuZW@rd6?e!ndfyY@H^MTz2)H^rOe)WQDqng0qaeOF(&#i_< z{vr7ESjjCLbxMh4jNyCa6jp1Xf%o-COqXf8^h-8=VR;PMgFpFCyMlDAxVri&PV~u% z6JRR)?4W=D{@KL^Z42ysC_&fEDDYNo?2&T4f8VkVEd3d>9J72;M=>b4sM3E#L6K_B z)ow6zTo9q(X5vhqd8xy70gA9{FfW2ZIM^u{e}E(eRsT-r>Xu00#gA7KV2giM&Ee?@IN z)-n?pHq?SVWiK38y=8qoKIMC@< zA5I{HV(YO*nBCBj!&MujUsz<=Tn@>Q9-UFr9tY76WaYhy=>ar)46uj%R{H$;GuKpH znRrZE8b(Ti!Dg(}^G>$tU?n*slb9Gm?+>gA5D#WbPSxOIUc>jc1}q1m_N(KJ+$|@5 zR|0iB7@F7IHc$%jU!W?@-!uh|pHT!<2;K(?Bf_HL_OjTyd|x;W3ppBrV;Vl-H2oB% z1BPBs+kur+2zm93$?5kqzDMO$pMYoUFyA+uHpLGvBL%zBd*&h^e#q!|);cwm(T77G zt10)QHr18YWfZFhrZTaC@(f~5PE|wNXy_m?&f{VqHCcYzN;Tcn#4+ZN+{X3kZ0}MV z=YP}E{Lgtl=YM+OYst?Y^wA#PGh8<)qfT=H&J!^LP%+ss2#0mjnkB?Q2P^7COkQ;VD=8u%v38ip!r?_TNUx+`#sZnM_VVO?-;_m7#++T zj@or0m1_Hb!4qkb9wR|^ie6gu&PRp&`JNoYAPX}~yT0mNcF9T8P4G!(9i{H|yw>~n zo1r$dPb7WSx-X_`&ov(JKRiOMI=Q(#$CucLhh=R5ZQJ4{ai5q@q{q-B!+C(nvD!v> zIuc*k8ozdb(W~UYyW0kUWI0D7i-8{2lJHaqHv1_ZTt{=iimm>R@e$kpgSE2Q&l5 zbYI#1Xvy#?Wq2XNpY_W@1XY?1|J7Mjmn4BJ4Fc)QroX?d5DU0FsJ&wKjcT7>u&u@)va9|N() zy|txtiD!1*-ovt_uO1{AGeT@+x^1g)Hpk9Yne^IypmK=Voa=gBcKei0EE zfk8ngAmb4Q9X&uc-(R<^4;aF6DJUY(&Rj4+{-fHTgjQs$jb|nATQbD1(-T2-I&d1uBSscDCA4DgFe)QG?K1QVCg_cnGhqnG7lKO zG2o)Du>=*YBHFBnSmi#H$Vj@N(U2}PElO$xe?c`-1Zoj!4M^v@{Y?j1D z(oE*dd1GFK9m&Xr1#vLH)5VE|QS*P?GDxqrD}_-zHqPiWNGq0XS2T;6=f}~VnjVy% zUHQwN!YVkoXD)a%${+D%%*srS8DLlD>UAk1O&`iharQi5R>I_76rXQ!0)N@c0&r=s+^_HmwNCGsI_@86nulr;oVi*%8P)wcI#$ zxOa+C|6Y-y{6#~owuD#XbIQAggg}_}lN#fIY|Go^zjflOA;uXR!q(bqhLqWXV{rZPUOTFntF?Mp8ODuy=PkT0N z+=C7t=r62ZZ4o8#5AP}~>#7`B3oyPSFgTvtlVMt&EWce2Tc9Er*xxp15%y#e^<2q6 zjUMUN+gc-V&iulXM1!(&PQA%jE|UA1`9mO5@$ZYePoxSI^h9hes*Zi>|F+MNLW0kq zR)bN`GT|4a}L9@^41AKhL)(@6|<(G{6PZF zd2@E11p5m$Ra?%t6Nqb-Inx6z>-t7*|Ix`5dD%!CvFSD)B7~Yw8n^pM&I0bF4SrP- z(u`H()DBZk%}&*3Uutg*)U9?I`pQF^{M;Mmk3Oy3m$(;op? z9rul@0pfMIm_MOP*`C(s+I|FAi9f^q-#hpX5xuYMbkdgSm540hA~QzXoq9ETE?1Xj z;=tivVPYal#OmHE<$~#kv)1$X>M;~8T&Oyb_4`U)s@>pK$=S824kD_NK9vi?@!Km^ zynvQ*O&SbTym?qngzD7|G3LA6A0oMBF4KLZN<}H}ZU407UHuHD_@@$s;L#IxsXZS% z@Tc0IjF+E%Y-KGkFdmk^NfqGDOS0#xQ81!vYP$6s}FRU4c1h#DaUb-Su<$_3%;XMsRUJmuYj* zrHdpX%;C&NMbV-m5@RU&-MB3yOaMbD?uzEk!bgOprEEOOT$PaZT|Dk>?ks6M>Idtd zpgv!kBE|EGopTS+#E6tk`R6bpK6^&Z%sjaD>HT(u2U(K~ftK~4FocI_DrUyc{e|jJ zO^ccV6wv9LmPQtzm>58X;<7i5xwEqqJ@9Vi&}Ng**`mSqsf9h)H822>&Q{LEC+j`t zld!=-{$33Eu82KK@emxOen&o^Yl29kO<{`~#baVZ9RIbKV$nDhTc=u48+@RBCbSsM z2r{(yrU2mL{VjAUB`+VL6xk}N7oLe_A1o5D(Il%D5dI<2VIg`4MfI6$7-K_uITm#H z4>=;IY6K(|GO@DufIv!IoH34n>SW!V+jK2@nuedLnO@peR(vbTs=48Qhssy3hKq#! zDdBhfqUC}kbCKXy^^eCc>PoVZ&H)5=+YW-o#Yd#GNEt{Xp#Z5p;|k%6)~WYHF{Pjf zL4On_@=(?GOa*1%w-+HG4JHSqcYZG`>pfg*cD$~#XQ*6sezg$B+M~MifFAO0I1W17 zR$&2Kn>YB)BjM!CJ#LAnSA-fj`UE`P?2Da?clhZacL1cFV4(KA1(|?54p_d)>n3WE|NEe#ztIpVsq$H-(EUW`zuu`5E%J)A zp}}`HHXf}ads~IO1T+7(gSzepXm2<LqrgrPDry{3p(45pvJ?t4<>~d1)~KfXNkYzu{;Oe`u<+eIbh9bm zUDC_Hf718V-=uALam3lZ_0?K6l(DX#uL}=!tosA+nz@Aiz7g%s2YE=`mlMYCbZI;} zzEZih2zTf1KpXko@HB%K6>rTidu+Sba_m;|dRZ><)f&LLm6;QWxjtfmCD`G3Rb+qm z2Lg!6?X+*d59YEc|KP-c0uSCQP#R;_wG*uKL6t|h*e)L(Jd8-9l2~EB1y;qL^V{bQ~niB;FwaPYba%h}HBcIm} zaV$-f*g?M>K4GGI-~NjcWUX@jP7srv@{@*%3FT=dfuSKpK|@Q6OGz08!q7H;7paAY zhl?2-XH9)c8aC+MQ}mz8iTi6Uf=6!nNXtHEM9+YZP9uhZ&y;M_K4)&Kjc@jckIbnzpyn^kk|CdBp=P)U zHu3&gP1dgtB4S@tHYY)Rw+%-!lt_Kb8)jlnGv9byVFFmIIxvmH8e6q4rs5|56-7H_ znEc@ziY0$q$JKRQG8VqCcAdCMDIBP1$?ya+CR@p^-)DHZaU}y35rX9XjN2pMcqAqy zm;h@b%omqdk_x!3pFT4vt*)*v_K&}aL9A0WME4Z{3?jMX+_=G-B|-YHE??%>2fFHh zPEmoY+CZ1gcZ~|Is~7ya@|u&EhVRNr=EHMXTARAlJb4#;ihN(K9`;@HvFwLx0d>&e zVm^I^C>pfzeOFSQP@v`d$nuA?{?uASeMbKk(@Xo57SRCpY;J`(*q`y3-T3XFelH=| z^bM(%<=`%}?n3{9k^PB#rp00M_(b;-_UeaY8RSf-dfU{Zyum$|A8eOO4rCz%_kFal zxRDscog8ac5k(P1*&A2fnat5DS;MWutSoOWT=h$SYX{^&TCO&y1L9QC_vKC(0F3wGBYXZ zTsTk>HcJ3NFz?Z7fK8DvdN{-Qk|7yi)`mh*OAzHLO8W%aVtO$DMic`+Gv>nC zk5#~WfdMa={=b~@vCyyHeN=Bb;R-9#dNZ7nS1XRYe@DnD99BUY?|tvWhNQ!!g~gTm zFc=VA)0eRSGaF}VVk96Z{%rF#e{9EvnAe6Fcvv0&txJ%-bwXrLKr`!$u!<#%Bzx<> zzHZvn+e^*OO(2WEUz8Q2y4_Gq4<*m~4 zZReCK8Qj*Hu*&YPJO>3lu*Z8Pt}28vA`Hb ztXhxqSy|dx*lodeE!*!$UPJ+_Ya~`8osK%pGU0_e3*9rfY&^QJ5Mw7Jk&TL8g&gsD z6VhEqHG-}ftz;plR7Fz8&x(c!1B(lhmul+;5noX*Hd=$Q4Bv%K_FCZL;{~C5O9uSa zzVoqWET2xuHS#*b`gSpG$vsr4oY)UnPlWuS-@}jR@XUtugz#}4-(Yr_-h{tX)2_;|NkG#=SMg8+L z!Df0h_0eo{kDw@W{@|vjcNk=RH~^MrVq@!V^S%YVD!c2xc3{Yu4iBNZyylS z=YDCp-UaqKlG^4PyzE|4`aAQQ$Ic<}T?ggrC-XEGRxoz0c{X$mMJdWiS7k$;xd^QA z3qDsiYUXb4pdg%VdSITmpC$VW^|3H9puLj7am3#Sc5y*UB+(e4S3llbKjZlN0#sA*l;na6Pc`$x9rWtul_BE9dHzEE_ z!93kRiACb#CsA%Hea9G35nz96?0>`OFrjfKV*r#6fWib4 zZ$C2tx=jHOslwmt(L9j#)5X_Cc80it?0DRAtT6J4*NxbSFU=-1z&xWl&RDZ9&t^}3 z(a_S;a(kB~$y9*S)gwS}U!>-+qxusk&V5R1&s4Q_^XDb?W@f7TekbdP9ouVjO!AR3 zIN)ZHh7~@uary%&6Uoa;J`5R((32b4JCAj2cGiHWHIc(#OCp1%H8kDgLX=u0@SM>HyFk2$4&* zApv*D#;Kb8ty7*-w8Z4~_b<1aR&4emA|bLt!ge*g9TXS6ZQSRvxe*4pF?av?cf zYAcz4F%$;SFX8w7%PddKdw4kAxzgTptCap9s!smmx1C8|j7o`LH_9K5N4VOEYoux< z3T~@QMPg9W?8Ii%eAqifCIP1Yg zoU+YU{wTsUL>OIQznN3Jqh`@B4JUd#`OMIyXeO`^;fsFwH5<*8`4)R9FEe#$LDJ0B zl-=o%G|Nt>saaS=gv}rm1}r8f=0Io};a16z+gU$kD~N+Mt;F%}Ufsm(_L59~$Px_( zMW(%KaM=}IqiDusQNW*+%vWLzsyn1&E^VyGhpGSmolw5wW8VDt=!wezf#A9P=cv$W zThr2%I1@EAa87+YclUcOEEw(z(W_6#LL1Atf!}taN6z{v$j0)FG(wg<*vGLI2J4-o zzY?@S=5t7j6`}y`DW8THVaVwh{yylNKY4ee12{o&^YWSk^lN*rnh~~_6cIx!1;PZ$ zp4!!?<${cCKMr`w1Ms*UvRSe7YqcD&CLA#oDSR_j1jo3Fk(pl8)~sZ&bV#iC97P+cCbw8F_j1f*wwGvR|_5nUEe>IN{M{HZ3^klhTIp5)aa;*|?IT_DR}_9c5b)0Q@}-E23-`pt6>4>HF&Z)! z8EmME_^lINPEJmAVxmRDtDZ!JGs%LJ;(~z0NYkMKTT5=}B-Y#A*b-mo_a15G&bGb6 zMDKQuiI5uwp@y2GBG$v*>1cs29nfJam}ie$R)R@I3_DMciM+6mJ@uWy&tK-e&H4$q zw?%is7YZaqMC7ERV=et1yDL@DtCI2`C7OGH<+tcOQ>h;$h^$@=4nahcr<_~N*IfAi z>F&$S4`}MCXCo8A;aC!#s*il(;w)|{jBTGL=jpqqy@Wh^Z$#lm2?jep%Vd$AOsO+W zWVe2?*IC7?+C-1~Cb2Vft16p^+tlmmsqJP`Re0%O*4I<8ZD&M_@!yB>R{nN&b7CZL zd<{}8>;@a|Q=<*$kbwhZWpZj+1&t6lM977-6lz=Ox}o}o8jU#|hlDs1oH!lG!cT6P z)w$KdC{V=PXC~kU48;gLZaf8TSr<4sRYH$G1Vlu+d3lK79vtJI8U zNP*#Rc5T-mpE@8g3lP|^#q$-89ZgMXz^nrJl*8H6s|VwnmLFhU5g~oV!PBuaDjR{NfgJR02&3t< z{zrSuns21JUpWGc1NJ#xK78&@pnUlGkX>3;hXq4Pp<~28r$X{v{Lx z8C6Vf=A1%nOujEyC%s$XsV3s;457F#cv0$Hb3W(40v`Q&{&dnk!Q!;MbfnKQHD*1m ziGHz$5A1As{|AJ^^l=4NfBycZe*2aLh$lEX@xWpWbakDe-$_wdOAlgYRUSxa;4q{4 zRZ0SW`)Ws-5Vl$&W0L zJ7*G@{f2nSFyWTvuSl{tUf&D(f-)oowTbVOFJQ)_G-y9p4i;+-)qb-gFoyEdAW|$yFi9s z)Za5H+{7F#t@l269cx`>Tc~q7Rit%aOhTs0sz~vi^s=8S)<>&vP1s4;>Am4S#2Icb z@`_cdIXG~MUK@(Ly7B_Q69Q;vz+6i*AunRk*yl=q6ZP`)D)csGinR{=2Dciw%jhN? zYp!O4;ftm5{%zN0#hZ+#bHDawNF3KZl|q#=&-w8*(RvEo+G+W+a0jp6#(Bn>Ec}R(iWT>YN{@N)rnbLgtBZq zB8tcpFP5x~waL7B`BxP0t=rTmhJtPlRff20Cf#JLJhmTC%DHyvd`b&~IS?vV%`bbt zegq>5Dmvec{(6hPy389hC3QyT?_q#2lK6McONZi54Vh#gia!;5-ApLMZE!^{^!Na{ z1Yr>oI5af0{}T=h;~*koQ!JXVL{oJ>SIm4^zWT-ZNlBUDZJrq&S+&$(WUCYg{{VQJ zko-dVon(TkqwS(D=)V8-6%pwf13t14)$7~ez36j=4xLp>x=t?Ku%6(eW(^+hY0DZU z149*^M%xvi2ie}5n4R27JhR{dT^xlXF5-9UUB(?LawntY&C8i{Kq7P+c6^=y{_Tdd z{i9iu^_O_gnF(u)Qp&)LTsu-Mni(cY6F0`lP&TR0-lz{n0;X&oSeL+uf%Q95$>0HU zdV4$-W@URC%ZX*DmC$-V7hOVrD3}Xvj=zMr@j3r<@#)s zFGzwv{3@NzpT_fI#OkX4Z$9Bfq!r%-L-reG5f)nL=l%{`E&31ZNs%p4WZ`L3nRnF> z9$#Knmp}-qp>Q+n>?p@ChuId!KL)}bm!|++X#b385--$EaKrD#gO5R4KOoM%JiJO) zJPJ0Gqh>ahxtH3j4fNi>&oB0r7x!?NRuCi}??b*HzZ^Y*VP1bP572AEFf~idy@`?o6hqp;J>>9igQxI7b~U zT-dX!d=fwHjYn5QIXJ|8(bFTDUtb@eZ6RNF!J1>0f`OJd<-;uD08ZBS0iAholI1<{ zhj?}|`hq3X+d7T8Ul2IymaG0mxQjo*d7m#@rvCHZoI*V4mQ#B=?RM&YX6e~qjxY@w zLdcYuaDEk+Bb0-+1`c>`@l3$<$K|WbRBSDQ$A6a=BKY3(ZaL1$6kYi1RIs0aTJEVU zS(e(vs!E_61`c6y48Jl4q7Kx; zcnL)8Iofz@{KYb&4h9u9xxV2pi>Emnb9N&eJ@&vfIJ9KFIqMp(tvOM(S0Gs_I{olW zmw4Li&nYCNy?NmC5pT5?c77h-kD0E%D(@%5@l3m0TEA9?8v%f zC5RSSYgMgkFCD(ewmaHBbrr|3Rls|?8>%IBD*VlZJbF|k6SD4n<$sNa*w>G z1lg&~Mf95IB!fxTd#pUA%eICx=b?b2i_?cROFaS<*vXC~Unb47(?Ko$CcJ|217fb&62i*3abOTrN+g z>N2JAqMz%tjl&0@1q{TxZ|cft4oYIhgj2a^4MG$D_VJ51;rVy*1vqwZ;Qd zESLZN;@|V^V_cHLH%}#Tsa7&*iuT@}sQTJ{^RY+rd$TjETyX$Gj$d**XZmky=vw0M^8+5N)`6PKZVMuVKtvWCEDGbDK~Rz- z?(1b5MR1)y&Oo>Yz!Z-@DRFq==*;%^chQOB{_ydBK4_oBoE@@^BKW;YzZH0-cbaSL zRvVU-*#1s+fjL}Te?WyM2>loWR2k?6IfH{DKQLwNi zU0iDVc}f>@lsHaKPgS(F!CD<19qrrGBff)q%sDtTq@u2l2!sI{f}T0g{$dDp9e;Z+ z3#))Sxt?prq3!g57+~K{msh)sh}y!ePBLZ|Ks| zO>30i+yxf5707jD+MX`2R)ooyuKp!-Z^BG&F+D`b$5hfH{sR zU~eDkRfk5`O(DVWQz3R5hJ3y6#C`YQ43i6HWrzYOVN9eaT&&|cbLXHp^ z-wPn31@$cf&}e`dQ;M(q7~P4SSyuC9c*WgsG+V;|doi#g$kBZ30b^5O^Z_o;eOKru zGWLIh%?<?)v`yd&fW=Bf!(1jyfqR);v11sq!P4@1wxz zK_CkP;tFwb?U_KkaG~CkrTg2mt$e_1XbN{dbIQvZ!A(qY{w(p6``8)W$ zxjiT_kcx||nnZ_?fFKJna0C(nwX7j=BCCCC=ytv%frEv*pnT|`7gU?!jh^AyA&0|WA~@Nh6X z8o-77b+Tb5!~}$dPmmBSIzApuJwP1eK9KElfO4KcFmBf1S2sU)$2HEM7>4HMRJ_~$ z+p^KA1+RL0cFhlRkS5fpT-lT4p_cn?vX=_s&3KY#X0%|c!n4Q?oEI;`!N8y9MU_{H zxj?N4rfxp55k-(%;nNjfU0-LH-4Nt9H8qjw09+n`)P5U$fI962_=$ox$SxSpgUL~{ z4y^;`5laS~miwqdN`#&%0RaI(wJNM~KI#yHdFB$JaDb5l2UHbM<~{Wl3d|JfRv<4j ztFDgF%4*trA4I9^$h-k47GP>RJE2a1V1e23@G`NA1E#ATeu@J*EiGgq?$>X?9H7Ea zU=gUq)Vv>ttsdDBG|K%XJ_NX;#D@?03Fb4iv!S4T6JyhxxdgkNvJmU2bf<$zX8d*d z1)BFRGh1^S_vY%`6(SBeG8w+&ST&spokcj#@JEdt;8bb1Fo!_!tL-*E9^U*ETqwS- zb9ZNibDPpb;^w@mxa8yq4yI3|u#WpFA?}6cW;c5<1+CkLcLR*B){ILn5hCwY$utFn zVLzI4fO`lMx}P-E-67cabvLz3$i7p|OQNLfUx)omw9*SZiQ;r^ZOfkPXb;Ph{crdO zC|VL7C={$qfyKqlCAOgPLP16SK~n_!X#E!VCv?1TB%R-JdyEA2W zK|x?hDxGDD z?F7)%EB1p?>YsS#_)}qQPXS7-U_Gk4;RlQvU{s0u9I^~NF@S@qq(tm#B{FFG1L7p} z%0O+KT^+B1p>$w4G`O?Az@YVX##DFVLCW7tEw+-x=xR z{0CuRbmn^?eHnywA2lpED`K9FRQasKh<_XJ?s*5H30VfNqz*|Y1tSd&Ji$~|RWpGE&m1Z6 zpsao$ZD|pBA}pUKgx@YK*ew(ahBE3yS}Z{M0RX8iVB2(+5!;{opv1<&fd0d;t*&mG z#jUn{q>aU0Tbno-i#*Kd{-*JaA=hf24ftg#1U;yDdFysEbbd<7cY*f_VCY~|RJ`R( z9cE%?c9JDD&2g$PTmIE)yjePsqlIJm6Qfg4>eKPxop)n-YPPYszB2NJ5Ewao7e8a@ zc~GupR2*D-B$D%0?XY0$&)V@vhC<;M|eHJ>4qO>Hyp&-c)Zx6^VF7EQP| ztQi)uw<^bh)$z(i;1ECfgKCw9`8nc$Q(m^VY-Hy;U=s#qH=q9uOkEQE?r?UOukCB}j*?VNqkdcv15wiElUXhVFvbQ9o%%~(}uWT|(NcLWp z$c*gq|DN~v?{hxAZ^v=YdG6=Fzw^4TuinNDMP=m(y^aN>oZi#WnK{x=Ptn%q)%A*u zT&i%?Rnc49`$M#kz27qOyjcGJ09&D4uzBzMYZ%npAb~I-XWoOd5 zf|0l9lp&bg{cAomi=A)lm|pCL&DqV6;OA&k@fqsr-96gUwlI1=uV6Ps*fvM3bL)nl z_6BA5Pb$YgjiDDT`b4JE-Ipb15ac$mdG?)gl%0RghA0aQM!KM-3g8)Lrvx^KscLyx zVdw-FF0oj0eCg11s>$cdd7v$Ve}m5V)8u3f_$j(*`RWx0s1JeFcygyOOt(l0MvBfJ zf=cf%%I}^%STXGCpL%+*Ld*k`42sOPxlL^h#b92}JkLPA=JJtCnMo`E;Mk347+lf>SlFKz!T`fs~Hz3`I7 zwb&)lzfAkP6el?FQYPSUAn@g41HqD2@mL{>v1W>bIL692O61JM^+w|O5<-wfRQ{RB?O#Jz}# zX;{r1lgPJDyxQEbX8HN7QSWIC6&`~LvNERX>gq!Mo^O>?dxq8p9QyVx1V-3Etk%n4 zV-CGp9v+iiuKro84k#IB$)VjW@;A);j;N-0O^0*-Jksn6AF$Gx`F z-g4iPn7-+Dj~@)_wQJX)IBaE%s(oPy(K5B#GgpEcZ{=9zWqvJ9q!TM)+{+>`K=wi` z2n?K>b^8BV7V%$Vtdz{^^U)>=F>IWse^x>^jPn`+*> zM(TLjRyH?e(#2?ol=f$EMR! zj1_`u2*1M2Py$h~`ypxpPlEkID-wVTk{rnl(!TNn>9deWZM$05=~Zk%w2p-^{nPrbs`Y7;csQMg5! z$Acm*H8szvLgoH9!>gcqtOM*Zh;kwL!-cCtw^aVFsRO9U(eYZf3ICnkn{GdASHCWQ zwh}U$MDcwe9tH+6)XpHo$ig{@87eju1ih>VpPm}&g1h>nY%*5af|{>%jQ;WaX8(gR zS%B!uT)ze>pp|j$B)cV~Q7>Q8X7t~OhY%4K#PNXPgB3=*3bb1QSQGl*mKwx?1BOOh zqyAP+xDNfg9!}=#u;-|smG$>yMYdzG&(@owH}fj0w4UY4UdWRvkXrJA#*5L*=LxUX z6(a@kQeHHF*SXhnlw!!p6R1T<2oSb*vp}z$>LLZ&KV6 zNM~bX>s*dWVaD70Gd?I#^x3ZRZG|+PW0Nm7-iUvaV4QP*+S9n$SRL4aXx0q4o>ag6GF!J$HBfuys?@OEXRoZIY}H?z^*WDN z^$KG8S9NtS^*Q^foOMeuwJiV9wg`Cl>1ua1MAB>_rWhrAihetYLG{j4L(KYn)OBY^ z>V?BzFcbV109#T5iwZ#>OiV*llZ=WA75M}!;N-UHo>SwOTJuyA2xYk+m==#N0Yk-C z{hI}qsc9vc=vH&e*{-;&cXf0?NE)4%3XP}wHvyc;ljgMpOB{6g>c2iVg{{!k)Wj+H z7R~!(ypMQ7M97(L7wE<((1{aw(Dj{1`L4k}#~WRBdBx)GB*Qrp62c^=+vG|( z%A`O3dEO!F8>J?CE4w8ZFupUOxCSPe4`&;r#EteA+7Yok7hrxPX~RYck%>E5J!$>g zOe3+vL>SD8o-2q9+F6U#i81e2j;g4t76ANJK(x;SG8(XN?kM}>e+Ic9UPnP08cG*$ z#inXglKC;bpux>`C$F;dJAY-lDlLd5_!PZ$a&`R#(8ZyN2}i@A(N+{H$@u~Dxcwb0 zN>n&T9=AuTZqcXTLpjplp+63In|!E10HwjJ&$}=4DPCt_J*0xSmTbvYzh6nc^F+tD z+gqptWtKemTJ~^pnNXV$=i*L8SMh^i1n)Welrkz{Gbg6(+afY}3%+Jm%?ZZhazWaJ zNx6wG+!qN5l|W|hd3NK8DWwjNpkNO~XW#+R00y6Np49h6z7bs$uEOt4h^i9}y4^q? zpa-8`F1U%t2~tQ2`{EjoI4E;W^mC!^K}i6@lGktE1pMIk1;`W>gU}wAUqTN87SKjC zosE%p32nuLC1*yrN@l#1mUit%2UH%VFEldEVfR5t^cs7(jhN!I<^cYOT4rx6w-TEcgbUm{)Jy zUf!w8G1l>HHuqDwUQN@+?uU1)dR`Jqf_oBd%8O@97vf}d&nOQ6mdss{#K?!z1619h zTqAb76Y#FiH6QO?Z*HFC^5ic==|ippB~d6%DZX-iPqF#sl78rxB5VBqt@#$eUXSDH zJ*6q_G zL2OU&@Zr~2vl^OTQ(ykR%ty5zc}Dnj1u-eP5R9uqd7tnOZ5VDJ{)1QB1# z)Wf;1_1+MDDdJK2ddTtpus()-jY5Ix*_i0{@08DhEP;(r&Th7n_h)U5y}Le7SCR2^N>RqgiA z5_vZ-!~NTdr11<>UY1@}w~prh@mortHx6;nEd=q&KL~o#uM9JhDO^*Yk3#CNw%HEX zjyg=ZuvxgqwMcB>{?YK=XrMPFh}-&eRDg}Z{V>qIXV&vnU)LOkWs?fGpj2Cv+PUUs zMs4YwwCL8a2ez-lI>OgU^?PaU5e=Wy%j$a6J8|YLDwM z#5Q^|jGr5r^Ae<48idu2t#`yU={`3Yg@svgL#@UJk~}jlg*!DxN4u?Jb2NN*DkSv0 zL%Pr>`hZKgHHus+vhxoYomXD|=R{Xbk49#`le54+vMeYxHhR#;SH8q?o$;g6xRDT8 zNTRIr;Q2-{c@y?$%{8g09~`6Czu`B({*JM}v8j#IBGpDhreX1=xAeO0suKgx-tGN0 z9u&eR0ZTi<4=>^`b;mnBMd9kqwmH1HlStgvktsq(`>t||WOGt_CiDjfM}WFIy3bS| zpsPb1GMiq*$0%3Q)$m9(_?pndivkmvjnhU&W{k+g4~Jeo@&B7BtQJqu{=8jwXQJe; z4HPZ-=hzAH$Bq8mnNpZehz`naVf&CJ>SY0A;y&TN;Zl)ww|`}BxPc;?3G(#YCMj!u zBUUd?IgWhk?-dR7M8;LPS9bT;n-<aANbmmB86-a^(s+2gXD-8Y>};4 zH!2t$@WkJAIJmslSj~>*f4j@Wtoae;$^HgR;)Nk1P=>NHRcFw~)9rFbBKuQ9&o_U4 z&^A*|&qNlg7!#hMw+D6a>L|!oI>oW}$U|o?AsKXM`i4$Qj{^Kcct+C4GAK!@J z!V4WP)8R1n0$pQ9 zfX^%pkUAax%&yi3dr}&5m0JpA2=mo+j}4xu2kS+6C+l(HfZyTL68C?X1^D-`Swj;n z-y@a&XGQSlbpB-DdCtDABgkPvvk;XoqMvs(xwZnks>i2YQ>B$dkeA!v*cunBu?JCk*F?5I1Pmo-igV%K+$)!Ru8}zGEa_?1s78-& zhBCjxhe{vhu&Pk1K_EUN4?Ng(i|yPdk0%Ws#>4b~n@dnewA~q0r12D6h}$I6{?=*o z75P_+gG-YICMCQ=(YGihkzXGk=l*uTtMQMw45h4cTCY%A@ANWfcs1V4mJN4~!5Gar zW3MK8uEEir#6%(|Cx`YLXuRTLDJ>n`5PnQ1RPvyg{G9|EKHRU->C2X#Slj+gl=@em ztZU$qFpVtvlu}A$;aV~+Z+*|k{Bibul#JK#O?YsXh=#aTUc8&5kSC{JW-_cf3T?V0 zq3jaRAPqD8FcBRiqi6{C{7Wz3SP>)j42U=--Od;{Lp&epAJtInCuM@accx-g#4=tc zkGOTTkSk$E&G!#uvIftM@E_JlP`);q?HwI2;9sZvZ^7CHpq190$Uyk=*9IZrRX#zv zqB$U%vu-NrCufrwIT(uOADkR|AHo>_-;&HO0&=W9`28CU`(Vv{&MHELd9X@C!ALY@JBzy zU1V$i4q7a*eS&cYTU$suw)_XTX}_yxOaq2Kk0F>xmHM%s-9JlxhdRiXI{T1)MIrgh zD}D9<&g&EvlqfoTli8A?6QmG^6HxVsPI*3CIN7L9r0Z6tBw~ewppanXEye^%-r&RF zX}qolmSi#))NiumQHf-qvKAs!Qy0u>DG#3i&d36}mzNj7j5F#QXNhiJ4`_uIM%gV; z5LsL~3nr7FhkrUEG)NBib{y_mD;D#_z&^9WTK%~K>FbNHt=LNE9(rcq`pVwr>6bf3 z^klw%_6$sKl-2I*Jlh4)A(Qi)!eEjV5mcyOcC!mSYqnTNkKABnw8>AI=ahV^A&L0= zwNo!W`Z#7M9}=perYryI9V+6^)o7}*88o#M8^1+~H84C+W)`eD?)0{f+0};E@$S=1 z*Z<`{wi97IMC}jA+bzT6t<(9d3FH-EKKAtXEqKaLu+UO~AyRNt-W-XDX~(lnDA zg0wGj2437{23-c0O4m$^^O;%d%il3;sxgy3dn%jamd0K+e{wENh)S`m^bk#1IXU`) z<-OJ5Fd*x&c{IC{wJWm{ot&!ilN1l<@fJHZwt|1AMb%)ms9Cf zqjEMjPCGS!##N8%R$*aFiD9N9iBVQgnZs5%4{t;_k&4s?wuFVG6d? zFVqn>pVIJ7P!U!}d{mGS$QKOBmyMH15{Y&Sfrr&8(uaxnQEKe@J%fEy#-#=YZ{FCe zP2B+HB{DLy;PC;X&iK*FQk-qZkn?qJY1}piqz-Fhvwy?+re5CA&DMs2>H}{UgoQU< zQ>f#d{iYx__!c<+;axT~J{B^Aw+e?93Ws0SqH3uCt^Z-RCwj7wL=G?m7Ie3yzJTplTzWUUNlwb#n6S7m;z zD+7~;J?OSEG=DVSJZSR}hR-P;RfI3!&_J^?klhK$Q8lrqyzIEx^>&z2dW(z10O_n;8`8qfz$IW=9sFQ|aj8QK;&5WqOjE3WkGG z%LU6oI*B_w0F49q2TBGCOfXhfS$SmO(=}df5*0pcWP-?Y(?!4RRK4_<9|>Lk)JtZA zB0Cn+_(xn zq6x2nqof#JK(&cKb_{j68qMn>qpxvPtO|3je!YvmRT7CsZoS3jtcu+Wzr@Hwl{%#{ zxl%P||I~Iiyj5TBV_>@;HhV36ae6x8iObdx{~SU`)RsA0iGeU%NXj>dQv=|*z_i}vqP~p7$do0qtXfmrQ$d8&wugsd@FtFo@te0fiE2*J zBV)0h!_K<>4Ks{|V>>c~w|haTA(8W{X2Ten5BIMkn&l#s~ zaZp=GL_vKWB)JISbAvudD59|GPM%IfLF;HjJ2mP;usR1|vH-`r<-4=J1b&G5t zQvdY{Usz5hnv@%>A^HiRrY3urbrL9#?itV7;nPkE;tDYkC22T( zFimJ2MbI$y0rl_hhvn_>>J-#SYc{9CvyMM1KaDx%e>DY_b^hUt2T?J_;!##+W+Wyi z25$cY)CTvun^CDjb#3j^_cxjlngX5&WTco03rMa|QbNSa3@={pPS}m{72xF!Yqtu~ zc+>GIX0FCMlZ$X!DeQ7tj>wbcCk=NMOK1ewz8(3hc>Jnpo(L^?Xi!eH-F5~4ZG_+Y zLKNHJCSQhM?AQ8tH7v^9@@AxAzvZdC^RZF&?|ypbeUH`Q4*R2be3O0D{pU_V17&X7 zq~R1l6IaJ|b;aKB_yZ&AxwwPN>QJmHfS60S=nBYWSQ*b}q^w9118XA^dI+%V4KHg4 z6j-6PBlGhy;YE6dcm5Um-h^g76z1TZH97mgud*7c+dbU9i=`#L@I{gZ6`aGe>1}Z# z&E!^@7ju-bqT{cN&|F;Pa=%gn8_mQ{3$Pv*G|j>+!NTz6N}xM{9v;hXiS))#sS z;`ASCmVDT73pz!f5zuJiR}EI#Mc}Im9Wzr~{Ch1G_3GKPXOG`fU?6Gs5J8MAGDgo$ zgNohc%GBhIoz0DnQ`E{=l+a{Iz|gGUx=X8T&y--}2cnaJNb24E2zY*D(@1!Emm`Qk$oV)hZ3M}nHiYlqy8yH z3>z>USE#-qm!UC5T4X(C1fF>Qr{4PWFa;;wc3g~&YcFxzsUr(JX5+&=3BUDz5`%vm z5iLr6i($LIul^7hr1lIDQkji-?Rg$Qv+_*r{~WYhI()Vn6_x-XSHeHLQ0Dm+ag$l^+GXdw3&Ia8J7Slp25L^L zy>l$hD%-wbSQ;2@59{A8>zUnMPY@?ea#OxCC>{<_!zPl|vArz>j6^`u$O=r0z0iW@JG7OaD87={(jr1d zL~L8X0_sAfO2Mksh?SUP**KtpXf@uD3kwV1Tzx)n`I5n7j^%sH!^hbx!nORV7zir^ z#V{B;)+phL8-v9Z%J8j2-{9xpWbhlTlGuN4dUx2@nE$>K-cvGG`RDP<)+v`wM3X{Q z=&<=nnLhcF5z!?D8xyJUlF;?r#-n80(NGR(z#;N{RE;)TF-ok^#i^Pk0uu(cc|+2V zP!F`;W#^2Hg893LXGMgMQPFNAZ;KT-lWa zK57~k%q+Lf7S+o>e1>jS+X93I-Z^r&DIbn# zn@`qftM{{#@6$von3&K)zy48{kv%mI+%B|pr;hYeF5+mSOHc`e-W~LT39d|C`N}y;kj0__R1sysZlthc}{6OR1KLUg4cQVQsrP==SlnV5zIQ&Y6!yLJl z;J^C1Xj-Om`B`G8WsAfUtRy)3!h$AvIk>T=lBYj$DQ@yt`DBbU_jXW|0!$1E0XGTF z(ETGR09yjA2c$F#06hoo(7nFxC%%cWGDb#5xn+ca+0@k~M*%iQ$JBJXY(82rnel7B zSr$nn}`Xox%h*D7dvj3$(8U zwu(n|11_#YMi^BIQSAi!wGX~!_`C9oEeq~qdXC!(zYWLV_A0t`-Hje1rKkCJ$zNL>l&qy~M~CN!r(|(mSbwV%k!Q+`p9YzgaDKmCQxrU?oi^W0quc zca6s6yYFJJ_qnu|lrK%w^0t)Ky~_KHlknL+VnzyO;hMy7$;$URvYWqwNycnP%Cph@ z=l!>Xc_NzL>vqJ@Bs8tzB!5o zPY$RPP`!ACc|~k;yR7Q|%=Lx~gYHI$en+A0ULwkR%)N){E3ll)VH{Xrc3b%HO|)c8 zv{3s;WCf!wCfrJ2te%nWF z8(W9)_U{0P>ZvD7d*zH@{~8r);{>>z_(@Bb{aXfT?Hw7^Crij+S~9^OMeG2g`+k#vXB`M2qSltvQuEO8OLo!o(;`OxJDgGRr^I z*}(H9?$8X&W%|f`-_2~NMa-FWj&`|$40xN^4o6EP_P zz_YoT0lLg0lWkw`t{m2z2^8i`YTmte1yX@1gW@;7n`~qlM2s1%UK_0_^O+8X*@1%D zFO5q=2_&2I=hLuee(Yb2PXh)PqHK+DGHAHlzV;%Io_2q<0=EUG4sa19{%@WS6J}s{ zHcmId=?0^MWx;*V)D)-}e96oX$;?*+gL1+TmLv-N00JPu+c~*&5C!HO>V?=iIKY;A z7EEKIVPuqn>iYurpH5#|RarS<^3iGa*U*#Q%l->_7!Y#ob*^C)4E;W4BkW8<>mgW_ z^}NwcC#9yIfas&N^fGLGUp;Bo&>T`GM~Cb?pk(_1-hF>k*U`o07F4W|_|^1K(c87e zC;ICjtrVVxc73vtDa=lp{QRY*)q&A)x=saE>ajw`j!D%;fP39bmi)1v+x&s_ZH~6= zl&&ii-z+b&iHVI+YOeuE1yl;w?jbKTGrhpzLxXX9+p|*O@ux2Us6wl!K-vbJ?s1n! zh{Rt3Es(y4g_YIjtMS$JJ^Gu1b!|{FA)U)jH?25$MybnEE~6gqPqIsVg5oJTY?=wh zeXI6p28*t|OzqeqdG??qdx=WaD6&24+7rXEVTn-#S7{fVK`iGmN=@=`0j^seB zPc;CrXLJC~cD;QY#h~}_jquKd&sXfQ)UBa4Pu*|5iw#HV2SdJp3#M=gahrUN~>&%%!6Q1&uvFbm4@hs`@a; z@h&T6P)^p+d2i+wl8UO`t%xSJ3#_>^(}h{CI+7aqxn-4o@@IiS7`xgj84E2gVK zLl)A0rs3tu77b_T_b%j3H8rExXGYAWLHP%r^n4xFv#u(QGwSUk`Xwc`!B!(){c%Iv zy8Q1o*UE=9QV)Jv`$l&M>9J=lDTslCdTc&>CNjN$M~^e`OaGyE%w)~w;)rFi&#_FS z1|@gWnNeI8;Y@3#{(s^t!XCbsnD%Vxii|TMGbr@)nFu<_x}-XT&8zl_`F0oUJ$P`- zS3cV)R~k>n@-YDw5>R5A)roYqMvnorYh5c12n2Q*L@*27<+Tz!y9_^y_@BgF*EP z&%FKQmkPd_4aS6Dc_tl4TX59wEMG-OKD87$I zJ{1*Tu3A3cc`w*{`gcF~e6NrOW`q=MZYx4jEBKCeN@=J6d9n$eNqqXfSD$;A9cOfWyCiGTC)D=LpJns+mBq0J&3P-Q4eKP&$cnAjo%8^V zdbiN}>E?m3H}Qraq9N{yh3X7y@RHu=$~elv7QT?|?1R}__;W9-^*=O+KnFJmEP-N7Ln zrOy$!(~kM>GQHv(zWYi9PuJQ}w>a${t*@VGcm#%Hg*?6VU^!v|<8=N8$~%4cdMW)B z-{aiBv=I)G%}2j7xysBMM3!sDEl$qSXjh0iO6;`=BSE4C6nZSm{5Ebc^6JpPKd-C0 zQ%E<`LRIj@o8+>k^UCK}nAmtPh(bm(#2yix5S7qjlj;wtmh(5cJgtp>Br zla-|DE>?l)t}O>Omenv+vGRM3kd(_~3iGy=l^($i^ZKTczOV1lljJG&1(+TEmv#m#v;3G+~-%3p(V=*_?6E{+EI(U|G?`aBHD z>8LXD`1*QD@Q~C*IKYa4w4^?*;}P4Pp7PL8On45U4;UMwvSZOnNl}2CYqvt{8azh5 z&DI8jMp!%YMmOg0(7&u~#gfUf$9A4}yPHO|`aWap!1DzOJr1L^Z#5nboa)h0Z#B?i zPrnMIN0#t%g~#Q3iTQ6;_HK(6lPpw_?U-d>3!VGAoSMR!<^0=R?0eXy3@YLJ76B-F z82FFN2&_tu(|<;L6yCMp4m-b5MEDPur7aBeD~?90o`XvE_M}7Z>_;#He}IEQBRVs` z^03eOf0FP}e?k)%jz&zqA3P8#Qm|zD`}_);sQ`%=6cBIfxxdj}6(z&CVuJ(e;bk1& z#1WI^lIpStFR$>l^*gbybJxY~Jych;3&+!${jyl9DEh?*l|Ek?I3?ZK^~tsmpMqzP zUiTi~<{&wn&L{ac%-mFCIL_wjGmn;P@yVaNxHSv({0moVTewQWVct})EitwI9lw!8 zwLJVu_oqe!uLi;Fgr_mv=GpxmeD)^92E;dOipcva*Vjgixh(x=A3B-Vy56XK_-Bj) zoOEa9MfvzWRGdCf*QXc?PzE3Y<^EUS0?n1f6LR_9HU30Fr?mXxw zw!uK0cogbv7PA)a=vDYTzW;RH__5x5yzvBzhO`;f{(YxwpNKZIStIIm^>F!Y=eq=X zp8m@y6jig)l(1Q(Py5KR@wYtV#jUHkmn{_p(lvAZmtuGUC+Oc}?l*f~mYvI zBXBEFo)7fiH&ghU&>q}Z7plS4w9QfsIjO|lp5n&h3;JDVjC0tf#QioraI*spc1{L= zmsibYrZgj+;rpO8ZFe{QP1UYV_~HAhwU@2+d5g!C$>Y;%nwo?`FgeDs#H5DDwlnhb zd@wMr(@)1bnDJ~JAs6vqKhzSpGJO^Fh7?7zP^ws^TA`*_!O&EQkvH_M(WpMI`$5#3 zihtFg$t{T&g`yj} zko*=X1s-i2g?5i|Y+JsJ?Gofdhi}{(RNPQmL20Ef$wyquO?y$n ztP-ADWP^wEJDi^k(H=}y91XMj98PpDqdLWPcb8H!aT!#5%&YDD(wKrzcX~|F?V@8l zr&FieD-JJRB~4s9FC|=l6cmm9~RD1KCA4#N3=tc#YPWtEc0NzkT+9;u(1Wb#iA)d}GHExTSOgK(k$#{Mk8JP0;3v9^ZY_u{;D&#ED2g!>F8p!v2y|t0; zH+SRT{$JTHJ9S_slD+XF+_I!o~IYx(dxQ!6kI|IB=w zdv?JX@_FY%ACdM9sQ(O|F68gbr`HVoqdGlBU9fLLsgb0c$BxIA=YP|2!xdjmIMC;IOUk}#8rb+l8u4ogvLIUEs2jXXT;aJ%;fmu~Fywd| zX_AFrNQ~{q`7?naXiJbtbuGPc$T>Oz;*_b_Gy4oOf%al|o1N`fD)dEv^cLFuFiw-E zR{}9+aG9~&ZbtpYK*SNab;}mcUg#Zw!f9hml6kg212iLu9lFFb`zwLwRa(G8Rc7vG z%ojZESK;|rGDLg!W-IoBDe@w5P$ongwx;|4ejld|9U9d-4RaP68Nn)*KLmTrCPgMQ z+g~Zavz7>ZXbqSLC5({U%)rfmYP(z9IAaz2w zp=uSs@A`z7vZiLAhpDM4s?}?`TMK=GO_AuW;J@21!E~}1#T3kutoCsagG6>uXYJy2 z@B$?RSIt4+W1JhbKUsQ*(rwJYD~78x;<;-&E!i{zjWN}2V>R8528D;*iHrB5`VO4~YY zF2?xi_p6cqObNMb9o^+Ya(!uf zie;K+3kj*ipvU(7=~$->^@@=rkO;Oq?qoXmY|BJR|5&|EY8$T{M|HLgq8TKB#!t)@ zbNc=j5IUfFNaHaJw{@A`hUH;wSMJ5>ifHHENI80^4qn1eVP3^h`yF4@%GyKK%%_yj zSL50i$q2dp9%-7M{wqWA*BbFLsi>&n+=R?W8R{$dJ(S>=NA zav|oMH*a=vQ$Y{*aZvpyOe9{MktBHdn}QgoGT+=62IooRxWIsTwwI|US&5v0x3qxqir!!`Yg zMkB}oWAE{#mcuwXwa%irE}L#w@!i?%#`EJp1^ER9_Fy|o|Lx6!Ra#sn`)di{AuI&K ztdW6WEl@ai(TTFoXgqmvTVMbA;R)z22*LNB4TwiAOa6g1I_O5HU3ysjgoC>tTC>KJ zh4Y#EZ3{KL9l~s8OH%h2{be52ic9<@p~Bj(Po;d`;Eh1wxriL|BdMJf|=NcnV56uTd0f1`{N(X;~e^5rV6Bde%;^S zKgIi@e(3@@6k27i=f~?n4fK6!CXjv|DB@ppWKWvY(x}EQXb7qJ@PapL#y@DNsYR!$ zv9lu3d~pM|#fjZ?ts5mG~?RzdAUB~&3) zA>OOqlOkarn*4>ABaHS8g{0!Tb)qA<;90^Ia#O^AFvXEbMaXw^Cg3N`>u?Yy>O|zd zy{p8<#rIKLtrzD%VBgQLOA;Yata=ooNcKa^)ZD!E{C7bT>dp+9K}~GTCkO&e@rNw% zJACkG;w%t2S#h?opoC0v!H@G#cF@&<-w;Z-VutXW)_$$u9gz0U3TP-WD9)sypt$hr zVetC|33Q)?Iy-q%MSCLrntOZbB_?UP;nxxh@jHt}lT{9_W(EenzPrErFQ`k5hccToLy{)6|b_9{1LWkvi$bv&rQ9I^Ye1zeCm~JM1cMn$`2; zK>#TNK>EHG$X17CP{yi19|YtDnq>BOqYs=WRKflv+!PfReN$zGw1M7cp$`>d&%w1U z8?SM5zn=uym>7e*j};W!yVg)^&NZ3e)B)ReZ_{RIW~QWhtL0>FzQ$#|ZomhK5_fzD zbHSvjiG2m_1;J9&y3HSssqK!-JAcY(pIXY6*m$YVy&pNqUZKBewSA|wTvV9D18nWZ zKd7@mmIsna(9UW{pDT3KF3M|AatMNAnVkfkOc#eeB!qx}(8zWIQqKEd1KExLKo&Oj z1My4y$zQ+j_k&K|MW$`Yk+QSsoM`0j6>nSg;ojkj%xZG6n77I(!^R`!5^{R_CfX41h1bfyy41 zOgwuG-yFn;k8fuJ*p3KiyngJq#O!lSWUKjLj&)`GsY$8(qobecf}EV3DpJD?vX83Y z1IL=5on2|u{o(1-)z*o#@z(Pi>ivMLjcv3EzaU0*o3F4{jX60$!KhV^vZlXZ$XCpi z0s|YZe||}|bf2!}mzI^4#XwNdXSoh!m2+0;UrlW^4yL80(IU*eyqb$L1C5zV|3ziD zv>}&o-o1cOOsj8Tz`*?B=)GZ#kY&luAyuX`zH8UiNXg0bJ$=7=H0^XNBWPTK`%?Jd zzkk=DqEqPwlR7K-`aT~20f;C)uYiENWy6D?k}JYs!_H7YQnW{4lY8So0VbPV@abV` zu|eraOvL;A^hguB{2u(~AH{T;N$dYk11rdQxbt`G|{?vm5Hi4y6Ee!e|4cdeYWZP4PZB!#jQ^ zD0=#y9jdqpIHV^jVNfYBJ9wIINQA&XHLuJRnt%zqIh}~m!!?*OT-=F`*i=mX6d;3{ zTC{NHd>1<+Vxt%zIrco~*7HT$`r(i>Y7zn(ms9o84s5%^8%d2MFVGRG@O@=lw0jVD zo6=v%^0qQgOV)T}gIq&#a~Hv)(UBl+P>sqy@swvKEeC}K5_brBIS=mLhWXyB90<%L zA$(9pyEpI#7)%u`1Ith#dvg2s?N)df-Gj<%SPW+u7t`RQKFfh?f`S+K?CpPc0@grp zb{Jw3mIF8zFAqUV;X~?YJ6#0&5E65b{*+pNEJU&O$O7ujLhOOuHH`ILnhT#(rcMqj z1YkDy&Y(#JN|r?4jKxJocO787?ZWB$UYsXuss;yKBR^}h+G~BDFhsu8AQQ<0=GRdy z?*{|{npOI(Y@KU9F@&8$kS^dsgTW;pL>0UR7B7>KkQi4xCTkjj7XaB3O%%wnKb)#| zT17zYocy3i?p%1F-$YpMa3Snsb3Nb=8e3=E|NU^>Gd3axbRbR%t0rA5CS7a6!{_$( z_WJkC;=9BCQJtQizAQ7i>*nUR0Wfjtv}e!$CPBD!$W~)A>snz{0!$tfoDj%nwogF) zBu=GZ=!bw96AEoS`E= z{!=eO@BeoX$Beg7iclRF=g;cKpb*kMa(NdeBlmVQojH1w#&zah1t+ywl(5RRksvmV zatzD}a0bsClk%3#f*f?_`@+ZYr@R$2`I%C(7rgLuA%$?GqpH z?%Mh=`7m|*Gbu6Uu@qI zUG`a8YP5NX4>+~IQzQ*SIcUL`zj z4yV4O7?+IteeB%@UnapuRkVZ&T3OUTM-}8Z(U9qi`eh4s@)&G*08jxEbhM@p5RK5{ z1l)RW0U#KPT_E`Q?`JNQwaANgC&W;Sn8DS<1NEpYmNG$Ky7SxJP$?}N{rR!8_v;%Q z8)&xn;4YvFI-yh**ssP%#U?6B0X`e}l<+fo(BQKZ2}H~*a~sim>nR}G0#kkus`#Wo z@A$Q9aq@zA$rk&2maPc;$-75vcpgh&kjf`rqhnApt0_kHqEP4pR9V~oszvxgdM^Ka zgTZK}{Hw-kmv_F=M4%MCtdWHUqlSi*>&VJZy*#sLzoq0ZTRZAEPNX$9 z)(urKQB%M@MdbDMdsY*mlLtE=NYF*vJp~0D!3q5Q{D|ZCz)v@TV6 z?w;;8G&GboG7a|iX*y8fzdE?P^1$2M@Gd{ar-^PF{fa6*8$cPLw*g!qAXJWRpcG3U ze|O0?%^vq7u^MVX=A99Y`o|?g*-%v*sQ5J3ERpEy5~Y4o87QxWM_@h zmgoTOej%5!5&=yj{0=6e%#9Pqhvz|sAp3ItdX47sW@4PueyIc>6(N52wf6QFuf7m3 zia370zVq%>;hq{|zeT~Z^j8LYNm=oi)QIACCsn|QqClw(>;7O)0&Kio-i!Hz*4dvE zL7Krgepj>gL!3PSbE33vPU?5HEyF7vJm?%3kFKrl&GKK;wwTEoxDnCuqUR}LHw1md z{y7dnXw|tdM9nwi!>}08l5I_W6fzpQOnb03pW80as!kWkq6 z*rI}uU8AvCb6p96HC@(nOaQ9g8B!0Oqj2MxRS4onV+{&0rhFMjKB|b-U0+_~z7h6C z6aaZ}7;(a0b#5JlNraJxDjFIDpuEIP6$d0p^Z@3#tAdK``v{bA zOfcCha_Q}zl_edSUka}O-pO4K6uK@duE7~IK9z4@uTn+MubUBzP`%%vZm9sjf3m~A|;0aWTF z9i4=nT<%JMMWg?Y%{^2Z7EG3A0KLjyYjsT5wWj_usn|gj^87km6wj3=O3K1)|mu{5(900GPtYRKV5V zA**UMPP0cxv7aHjZCdWSYH0?gx_T?Yz{U|co3pK`R&qo#k@t^4>28)N|KP64BYm@n zVFesLJ3|$Nd2ZuJdqG)J4+%~VwxTS`3L`m=!}D@G)t`{lJnJAAmIx5<2Tybw`U*2>WlvhPWM0IC|f4~JjKEY#m`*_|R=@AW}KjRcwO%7))p>^5F7 zOS_44_T1i{B+dgWxaDKi&ZW0^vSUFuV~s2ZgM#@Pt3G3-k-b<4r8OSDt^zB$0(-Sz zryD00t!b2Rf)0$ofg9lY>njb~Gr>E+>Y-=B%FBni$PvL222d}EC$zhY-(n`U`upQE zgJNbi0K+^ajIHbuOv|q9Iy|%jX@H>lz-x@_gdFEjRtBe_l?1D(c08 z$sv;a_*}cg97dmj z{{J;I>CJok`gveLdiG{F>Lu1Zil7^f|6fH2>_ubnudgo(eks)W?<}qO=g9R}u6IDN z$HmYyX9=KzFU~c0s!U3UsRS)bOq%?6P7u(#K49;wBn-%rYexUhF^g8%+I8?{ef|CX zU;6u-pR61iN;zyLklay5>0S4wKf}AarV;cJ3q9lrDg_@uO|#%>mt;KR|Mm9vEnOwP z=T8)5W4p>tB``2DMC=J5SxnBzz>84CcvT<+oqa%m1Dd&aV4xeq@Q*>>%k!?i*P1fT*$k(rdtOw(4M)$b_-V!+u> zMl2kR_aCiXKRYFkw8j}P+nTNu44$mCPX()b-hX~}l2pNWLe(jxm}Vc=bc8{>9yA<8 z+7d1yRsH4XgLp{0+r|l-*YC4y%j|qgg>2@_4D8oM;1uS%xx0(Uj~u|n*5DWE>2>ke z87M#FwC0?ZqyKB`%EO`T-v2X(F+y2NlU*iSWX6(xjVz^7QkKeIDr;mJ%QI9%yauT# zm8eMZ+A6%Zk(jq$OWG9;QFcZcGU#`YuIs&i-|PEz{Nv0s&viV{Ip;q2=lemvnEPCbAZRCO&242G(^Ptg9QRqQd#b^rFG-L2e!Aibs|Xz5h!P4Tqs_+H-Aj z8;LYYprULeH%+}}+1r%8A}JR8TKN7ZYUYTp7e(NtmSr5JBWf!i6W{-{Q+wfhy-{DW zl)Pcblm4R_F?y)q&Ie+Zc4GWTzJPNfxhrs z*Nd^TinmTMpM*}u}~Sk(BFEg*Y?ehu$1;f^J4_o(Ae)Ifv+(&n)U zAem>c#GWu#b{ELHWhCbysluIcGX19!$@*_L9ik0sqwBkxgB}asLDv7@% zIGw9q^sZrrIkbcMW)si1!6Ii5bKz{BWf;yY^Yh@qB@d?OliWLB)!N^#&3*TAMC%)8 zcOwv{0(*EIlQpQx{GHPLd>Y@+h%FiYD(=Te{O|(xb-`XhZOz}C{e2Z?uNd||sc$JV z8&Qz$5xCVk_m@ry<@J9BdTgQ#LUu+eHi#mtkSRn3$r4iBuBtkSL($E!GpXIK5}Q&g zGl@|EoRBv**lZpYDC0y><`mh*&b=|Ou(*)UV zjiN!VN0U_i$Q|)O%4N}H#4aR}mLZLzdav^i1~t!Dr{QA^C89yw{6zarp)#kjjBk@2 zx4XZTqm`-)6{?jV##wFH^&{$ihjwA4XGD9sy;mQedeeUp4cSE^dB+h6J;C72=XbJ4 zCIkeiHpr>f|9GfS1y*+s3EgcFZh38+y!h1w;CbtkmgiItC9~x&i_Jw_MjCGnB{_ZM z3ct*o*=9pFxcgyr@7)c$FR|&!1eW~S!~FRxFUZSMRe$d|vsnsp?^6@Yy=%sAL(Iz5 zT$4bO=R}};+vm55=O3DR;WAvgW6oB~dm}FJm|XYdDAK#MPDz~+*z7EBF)5fU%X=B8gFJa&zM-@h$IHhQbQ9lyzZEUp<9`AII< z!kg}XLHX+~*hx3FVb>V3f4;oBEj)IrdHts2o2BYH@x9v+6cdD{hB8?@0Gt@b$QGd1 ziHa?&|IsQL<{Wjg-1_}~$Iigq>lJ7qHNw&aZObU_ZS!S|#D}07 zx&bHt18ivGW3}75^-|;In~~M8nHf&)?#s zGC3@X^P`_U#6=1ZZSB>gv+AP4`*;#!O`s07Et)+SWK2CvQA=Dl>$7Z;QaY=D&0Bru z*1qkINy_h3Zu*yhy(^6CPLraJ5j4uZo>$$gXb@K2s%|JBi-tB*EA}2HZso5saWpWB zhcu&yb)$dQm~>T#ob2YH)B!zaUz^?Uy<(9a9teG{BD(DAagf3EfreOB^9WPRUM_1RiVo+xvqRqsGxi{4eS zEn92W9)0U{0L?FZ)`DFnlq{Gpuu8p;airz+Nim`) zo87ATG2)J$S91pIQ%r%bn!WqY-FS1|_v*HDAN!7(qFW4m1bYOHB`q#cF$d3+K8qGM z_9P296D$(i1YgeI>t}tA8ESjlK4oueJ7+$pAv>pZb$8AFT_L-P1PdEQDtmD;T8kPK zNE#zH@06r6Wl@u4C}Nh(y~9zoA-CWf5qVsx6sL}d6vpsy3O4{2u3Wj&+h||oHrrK! zHUbIrB18rnhc4F7|FIca#4BEUn5?*%6pk?(Y4VxWh1@iNP!-(EoD5_ifkRZ}Tmi+C z-hR8GVLW^*Cx;Z~B&30;%=TSQrHBi=Sel8|zsNvznkoq0`^&GoySpbtyw2H-Y)4Ed2Y1AD z4Gf3R42kpi3A2q@@X0_t1y&|FgA3>F%H|FCJyA5#j&E?z8=S?cP5na}nKpl*Ru2{+aNKu zu2|)FEJdejjxGAolytt3G9doNEpVT!t1uFBwX>5odqtgdaXoHP?na>e?B@k7M@KB< zsO?d|)Y$@;sz?TF>Ytc3KmC^+wGZ<#x-XQ@%a2Zu`?$jO4cx$L)_XVqyEx>#RYD=9 zs9}BVW$mHK*Q2v0$5PK$*OJmp47&TFMg+yK)~~Bv9i?bU(eBX!JRN*p)5sX@IOMo@ zRyT3sk#Etx1sThwcRHsZEYqmqmG*%t!v591MLl*qq4itVg0+n{j_SIU?kjRq6R!+geQrNKq}sl4$sSiZLh4V=$hsEmFCr&d$15e0G3Cn z5QOyh@u@BnEZCHO_3A}l)ss`VKG5Ge9kd1yyX)o9(A^m==~7?sJzqTwmvQRST|ypH zhfwVLl(8W)dbQ(&LdP`l-+AP8P=^i)(VC$cOQ|2#8({4K9S87ca_`?40IIjmzI|4Q z4y^^tedog{202^ZeYGC)NC6pw^9D$zb!SP5NQkbq6mh1XPLsDLq!04)idV9S?^|R| z-Tp3x+e4ukKx~Q*a4#UmsNGFHanG`<^Y-BQ@e&S;57+z|?5yauWL;2P+(s7|%9Xp| zOC1Tc&oiIZ)$3%xUjU8;5H%o>=Nk}U1;YhtKX;wE+9TO!iiD?{7ilz4i2MR7M|CN- z0&r*TDglUvBD@5!f;Vx4k%`ToAiM$cn{Uq<`_H3T*)1*=v=&Rj1L*9e!;)zJ@-@GP z^pQZZ*~;(Z8OI$^fIdOClPBW^=R~dOh2etvkw+!=_V!fJ={er)NrdV%%@Fp1pn%Zr z!Q6MJ!6oFC;sEtNwoxj8pRGBj4dgw&+wjIgTH@b1;o+&5$pox94_cbXOI7abi(KwoX{v_$&9k4wxM34^{pw*M{<8*NIz3gQNxk8J+!taZ}1iWIn-u?wkx z+0l`@sZp`>cVlntEOjY{j09fp>I?55*F1eNdRKCCOmnA&e$mmxhS87K($^L{XBY=_ zId@@QNTrPZ?YdOEOpczd^ya2?aX&;yTsa-c$V;zV%5<Ic1j+Rdmn{(%I1!|d7ccY;RQS((ba@QVMATS+PtV(Nrrc{HWtO!diN85# z@K|r8WEoxB(S;YC`@G=g95;PD;psv|`zC3J_~q}+SteD1o|99zpCHxg-t|S$AAn5= z2@AuTPI+u|4_hV35Jg(R?${%dKY#vw1ZvYJvr&z`eosAg1D6&=JIXLd?^sBJjNc)k zOq}Sq%3H=62LsCpa$yOyILJrdv82mFqu<o^-S%9g`u09H?Cu3BA0RLDPoLl80EFH`hIzHvFFrzi0s=RKKvdnIb$@g(RuyU zn|#sxKEVjXzP36V=dN3Pc_Ti4NR=0NOuE?ClvZV@(gd(rP2CC5+QhF>D37f3w;)PQ zB2`MNss*>ycx0~FvmjwCorjE1bE1Tjfq^tc0iaHS{MJ#=P1I3} z?FqhDs};qlXjQg$HnOOps+u6U7~{Mh#zt0L%jrIsUr=D7k`ZYHt{9~8g4R9{{Eags z%clG1{0JH=YI6BoFbHx$n7u0G3M6{++BHEo?2zqy$ls^(ORfc#K3GX&O8 zmPrhyBoZs#;@yUf96+1b>VlyKwpUJwQG6 zC1N zZ)1c1-jOqzvMU*v1`Oh9N7IrsNYF7Da)&h|3vWHjmqs<6ubo>; z3G3pKy%cG*@WQb=i&!3|kC8z~0|mvtSsd=H3Gh+neTfh-aJE{A;YByilkv4lf}WMp z5Qv&c6htf~BoW61sYHbU-Y54%3Ziytj}?1=(jR+ZR(}6Pte-Jdei&O(K(h+mSu8zuZ>! z<{)QRIE1v-IL0Uv`}M@B#rNaCf^KpJsfIO?HG))0ddMFZ$ihbH3C~8uAB4>zhB3Ew zvv%jAP{{^pgMOj-xF}}Br>XGn@p%{PPZJSMZO?*|T6&hmlHKN-woX0o+?TT^gGWUa zk(=Xl8st>-GaQk}^oBFC%$r3P9TrwG>r!&1g!L=~O{qG}h6aM&32ovoDD-}P6jS?^ zQ{AW}CMr*t!--AkaaN7RiYv@2N(V_^K--%fnv@_ui{W-RraEiKVOTRm z>bhG+LgA0R1gO~3j#GGKEC~N1E;b2AvsI(XO7oT!qKS#dn~lx;g*nNvW`o+oS9g*P zO_Jpzy*WWC1$|p`#DsaQF}&hxhCH&Hc~YK1@=%zbOu^D0w>uu(ZH*oeUJ^^`Tbryj z+r)cmo@$a4LpD8z^GwX5o9T~vQ|68dzQ+oggSu83mF_uB_-8(*A}dr&Oexlk_IWT9 zG18?*-^Q|aD?9q-BBS!A`NQ2ZyI1r7XyVNb6~FUUPf0bzNEgv}3Z^N`n)hM}k9Q{l zce3>~;#Td9ZfL;1(nGd+$x&G~G3sFhJ=soG9nPVZ15!g@GHSJMz1j54MD40*FP`d_ zei5FI23guYb)Dn4Fh6B#&a853PVU{x@O5KT+0qM2Qg!-sdleXUdzmoELD`l~D+7&l z5a@OJSXb3IRLY|DknZYA8NN19etB9h(s9-TRf9R9y0At1jYbvW&iS(Q<WJepxPxZSJ4 zZcC*|WVPpY_K@GEEqJVqEA(4%fZZr~lV;yN5{F2s_H}{=S;UUr6ep Vt9c4R(++_j+g;w|9?rgHgEs{ diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index 05d810234d4f8062b71fdb3381b01e8c3ffac48d..41326714a8a82b2840cc762628c45ac46af0c44f 100644 GIT binary patch delta 88039 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 z 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

me%Xwvuf>;o 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::mediaThumbSize)); } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 340b09f0b..6f56da026 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3156,6 +3156,7 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) { _maxw += st::mediaPadding.left() + st::mediaPadding.right(); _minh += st::mediaPadding.top() + st::mediaPadding.bottom(); if (!_caption.isEmpty()) { + _maxw = qMax(_maxw, st::msgPadding.left() + _caption.maxWidth() + st::msgPadding.right()); _minh += st::mediaCaptionSkip + _caption.minHeight() + st::msgPadding.bottom(); } } @@ -3550,11 +3551,11 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners)); App::roundRect(p, 0, 0, width, height, bg, cors, &sh); - if (_thumbw) { - 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 (_thumbw) { + // 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); } @@ -3692,14 +3693,14 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in QString statusText; if (data->status == FileUploadFailed || data->status == FileDownloadFailed) { statusText = lang(lng_attach_failed); - img = outbg ? st::mediaAudioOutImg : st::mediaAudioInImg; +// 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; +// img = outbg ? st::mediaAudioOutImg : st::mediaAudioInImg; } else if (already || hasdata) { bool showPause = false; if (playing.msgId == parent->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) { @@ -3708,7 +3709,7 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in } else { statusText = formatDurationText(data->duration); } - img = outbg ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg); +// img = outbg ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg); } else { if (data->loader) { int32 offset = data->loader->currentOffset(); @@ -3720,10 +3721,10 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in } else { statusText = _size; } - img = outbg ? st::mediaAudioOutImg : st::mediaAudioInImg; +// img = outbg ? st::mediaAudioOutImg : st::mediaAudioInImg; } - p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), img); + //p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), img); if (selected) { App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } @@ -3850,7 +3851,7 @@ HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia() if (_name.isEmpty()) _name = qsl("Unknown File"); _namew = st::normalFont->width(_name); - setStatusSize(DocumentStatusSizeReady); + setStatusSize(FileStatusSizeReady); if (withThumb()) { _data->thumb->load(); @@ -3869,11 +3870,11 @@ HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia() void HistoryDocument::setStatusSize(int32 newSize, qint64 realDuration) const { _statusSize = newSize; - if (_statusSize == DocumentStatusSizeReady) { + if (_statusSize == FileStatusSizeReady) { _statusText = _data->song() ? formatDurationAndSizeText(_data->song()->duration, _data->size) : formatSizeText(_data->size); - } else if (_statusSize == DocumentStatusSizeLoaded) { + } else if (_statusSize == FileStatusSizeLoaded) { _statusText = _data->song() ? formatDurationText(_data->song()->duration) : formatSizeText(_data->size); - } else if (_statusSize == DocumentStatusSizeFailed) { + } else if (_statusSize == FileStatusSizeFailed) { _statusText = lang(lng_attach_failed); } else if (_statusSize >= 0) { _statusText = formatDownloadText(_statusSize, _data->size); @@ -3886,7 +3887,7 @@ bool HistoryDocument::updateStatusText(const HistoryItem *parent) const { bool showPause = false; int32 statusSize = 0, realDuration = 0; if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { - statusSize = DocumentStatusSizeFailed; + statusSize = FileStatusSizeFailed; } else if (_data->status == FileUploading) { statusSize = _data->uploadOffset; } else if (_data->loader) { @@ -3905,13 +3906,15 @@ bool HistoryDocument::updateStatusText(const HistoryItem *parent) const { realDuration = playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency); showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting); } else { - statusSize = DocumentStatusSizeLoaded; + statusSize = FileStatusSizeLoaded; } if (!showPause && playing.msgId == parent->fullId() && App::main() && App::main()->player()->seekingSong(playing)) { showPause = true; } + } else if (!_data->already().isEmpty() || !_data->data.isEmpty()) { + statusSize = FileStatusSizeLoaded; } else { - statusSize = DocumentStatusSizeReady; + statusSize = FileStatusSizeReady; } if (statusSize != _statusSize) { setStatusSize(statusSize, realDuration); @@ -3942,44 +3945,109 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, if (width < 0) width = w; if (width < 1) return; - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); 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); - bool showPause = updateStatusText(parent); - 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(); + int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 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; - p.setFont(st::normalFont->f); - p.setPen(st::black->c); - if (twidth < _namew) { - p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(_name, twidth)); + QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width)); + if (_data->thumb->loaded()) { + QPixmap thumb = (already || hasdata) ? _data->thumb->pixSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize); + p.drawPixmap(rthumb.topLeft(), thumb); + } else { + App::roundRect(p, rthumb, st::black, BlackCorners); + } + if (selected) { + App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + } + + QString link; + if (already || hasdata) { + link = lang(lng_media_open_with).toUpper(); + } else { + link = lang(_data->loader ? lng_media_cancel : lng_media_download).toUpper(); + + p.setRenderHint(QPainter::HighQualityAntialiasing); + + QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); + p.setPen(Qt::NoPen); + p.setBrush(selected ? st::msgDateImgSelectBg : st::msgDateImgBg); + p.drawEllipse(inner); + + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + + style::sprite icon; + if (_data->loader) { + icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } else { + icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); + } + p.drawSpriteCenter(inner, icon); + } + + p.setFont(st::semiboldFont); + p.setPen(outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg)); + p.drawTextLeft(nameleft, linktop, width, link); } else { - p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, _name); + nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); + nametop = st::msgFileNameTop; + nameright = st::msgFilePadding.left(); + statustop = st::msgFileStatusTop; + + p.setRenderHint(QPainter::HighQualityAntialiasing); + + QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width)); + p.setPen(Qt::NoPen); + p.setBrush(outbg ? (selected ? st::msgFileOutBgSelected : st::msgFileOutBg) : (selected ? st::msgFileInBgSelected : st::msgFileInBg)); + p.drawEllipse(inner); + + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + + style::sprite icon; + if (showPause) { + icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause); + } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { + 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 if (_data->loader) { + icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } 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::normalFont->elided(_name, namewidth)); + } else { + p.drawTextLeft(nameleft, nametop, width, _name, _namew); } style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor)); - p.setPen(status->p); - - p.drawText(tleft, st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->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); + p.setFont(st::normalFont); + p.setPen(status); + p.drawTextLeft(nameleft, statustop, width, _statusText); } void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool selected, bool over, int32 width) const { @@ -3990,6 +4058,7 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool style::color bg(selected ? st::msgInSelectBg : (over ? st::playlistHoverBg : st::msgInBg)); p.fillRect(0, 0, width, height, bg->b); + QRect img = st::mediaMusicInImg; bool showPause = updateStatusText(parent); if (_data->song()) { SongMsgId playing; @@ -4000,7 +4069,6 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency); } - QRect img; if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { img = st::mediaMusicInImg; } else if (_data->status == FileUploading) { @@ -4011,15 +4079,9 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool } else { img = st::mediaMusicInImg; } - p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), App::sprite(), img); - } else { - 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); - } } + + p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), App::sprite(), img); if (selected) { App::roundRect(p, st::mediaPadding.left(), st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } @@ -4958,7 +5020,7 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, bool selected, 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)); + p.drawPixmap(QPoint(0, 0), App::sprite(), (outbg ? st::mediaMusicOutImg : st::mediaMusicInImg)); } } if (selected) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 1de6589c3..9c826679f 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1252,9 +1252,15 @@ private: }; +static const int32 FileStatusSizeReady = 0x7FFFFFF0; +static const int32 FileStatusSizeLoaded = 0x7FFFFFF1; +static const int32 FileStatusSizeFailed = 0x7FFFFFF2; + QString formatSizeText(qint64 size); QString formatDownloadText(qint64 ready, qint64 total); QString formatDurationText(qint64 duration); +QString formatDurationAndSizeText(qint64 duration, qint64 size); +QString formatPlayedText(qint64 played, qint64 duration); class HistoryVideo : public HistoryMedia { public: @@ -1428,9 +1434,6 @@ private: void setStatusSize(int32 newSize, qint64 realDuration = 0) const; bool updateStatusText(const HistoryItem *parent) const; // returns showPause }; -static const int32 DocumentStatusSizeReady = 0x7FFFFFF0; -static const int32 DocumentStatusSizeLoaded = 0x7FFFFFF1; -static const int32 DocumentStatusSizeFailed = 0x7FFFFFF2; class HistoryGif : public HistoryMedia { public: diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 93f24d368..5b241d640 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -961,8 +961,20 @@ void DocumentCancelLink::onClick(Qt::MouseButton button) const { data->cancel(); } -DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 date, const QVector &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) : -id(id), type(FileDocument), access(access), date(date), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), loader(0), _additional(0) { +DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 date, const QVector &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) : id(id) +, type(FileDocument) +, access(access) +, date(date) +, mime(mime) +, thumb(thumb) +, dc(dc) +, size(size) +, status(FileReady) +, uploadOffset(0) +, openOnSave(0) +, loader(0) +, _additional(0) +, _isImage(false) { setattributes(attributes); _location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id)); } @@ -1034,8 +1046,38 @@ const FileLocation &DocumentData::location(bool check) { return _location; } -WebPageData::WebPageData(const WebPageId &id, WebPageType type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill) : -id(id), type(type), url(url), displayUrl(displayUrl), siteName(siteName), title(title), description(description), duration(duration), author(author), photo(photo), doc(doc), pendingTill(pendingTill) { +void DocumentData::recountIsImage() { + _isImage = false; + + QString lowermime = mime.toLower(), namelower = name.toLower(); + if (lowermime.startsWith(qstr("image/"))) { + _isImage = true; + } else if (namelower.endsWith(qstr(".bmp")) + || namelower.endsWith(qstr(".jpg")) + || namelower.endsWith(qstr(".jpeg")) + || namelower.endsWith(qstr(".gif")) + || namelower.endsWith(qstr(".webp")) + || namelower.endsWith(qstr(".tga")) + || namelower.endsWith(qstr(".tiff")) + || namelower.endsWith(qstr(".tif")) + || namelower.endsWith(qstr(".psd")) + || namelower.endsWith(qstr(".png"))) { + _isImage = true; + } +} + +WebPageData::WebPageData(const WebPageId &id, WebPageType type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill) : id(id) +, type(type) +, url(url) +, displayUrl(displayUrl) +, siteName(siteName) +, title(title) +, description(description) +, duration(duration) +, author(author) +, photo(photo) +, doc(doc) +, pendingTill(pendingTill) { } void PeerLink::onClick(Qt::MouseButton button) const { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 917c3673f..ba02bc189 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -1118,6 +1118,10 @@ struct DocumentData { SongData *song() { return (type == SongDocument) ? static_cast(_additional) : 0; } + bool isImage() const { + return _isImage; + } + void recountIsImage(); DocumentId id; DocumentType type; @@ -1144,6 +1148,8 @@ struct DocumentData { private: FileLocation _location; + bool _isImage; + }; struct SongMsgId { From d858e9635220d79fa0cca9c826b8f30172eef6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=97=D0=BE=D0=BD=D0=BE=D0=B2?= Date: Thu, 10 Dec 2015 17:19:46 +0300 Subject: [PATCH 013/145] =?UTF-8?q?Do=20not=20depend=20on=20MFC=20Signed-o?= =?UTF-8?q?ff-by:=20=D0=9E=D0=BB=D0=B5=D0=B3=20=D0=97=D0=BE=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=20=20(github:=20OZ1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Telegram/Updater.rc | Bin 4370 -> 4370 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Telegram/Updater.rc b/Telegram/Updater.rc index b55372ad83ebca332b8f7829be9b1afa5a83e86e..4c166b94045b89886bd3aad772da1cb7f151ba93 100644 GIT binary patch delta 29 fcmbQFG)ZX#4-;!SLncGsW Date: Thu, 10 Dec 2015 17:33:59 +0300 Subject: [PATCH 014/145] fixed crash in groups uniting --- Telegram/SourceFiles/history.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 28854b397..2504c9c45 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -1987,12 +1987,7 @@ void History::addOlderSlice(const QVector &slice, const QVectortype() == 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(); - } + till->destroy(); till = blocks.isEmpty() ? 0 : blocks.front()->items.front(); } if (till && prev && prev->date.date() != till->date.date()) { From 92c31324b05e394714f5832d788f14e514d9d06d Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Dec 2015 10:44:53 +0300 Subject: [PATCH 015/145] links done in new documents design --- Telegram/Resources/style.txt | 2 +- Telegram/SourceFiles/art/sprite_200x.png | Bin 245695 -> 245738 bytes Telegram/SourceFiles/history.cpp | 107 ++++++++++++----------- Telegram/SourceFiles/history.h | 6 +- Telegram/SourceFiles/overviewwidget.cpp | 6 +- 5 files changed, 63 insertions(+), 58 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 5dc633a2a..aa0a0fba3 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1232,7 +1232,7 @@ 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, 18px, 20px); +msgFileInPlay: sprite(160px, 164px, 20px, 18px); msgFileInPlaySelected: sprite(180px, 164px, 20px, 18px); sendPadding: 9px; diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index 41326714a8a82b2840cc762628c45ac46af0c44f..53fa4b7928a60df78651aa4a4fa7a109c361c676 100644 GIT binary patch delta 76177 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 aoG?O1MhqBcHP>?xT$|k7*I!@9iu(_+y97W0 diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index d00e5c990..176636931 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3180,7 +3180,7 @@ HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) , _openl(new PhotoLink(_data, chat)) , _pixw(1) , _pixh(1) { - w = width; + _width = width; init(); } @@ -3208,9 +3208,9 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) { if (parent->toHistoryMessage()) { bool bubble = parent->hasBubble(); - w = tw; + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - int32 maxActualWidth = qMax(w, minWidth); + int32 maxActualWidth = qMax(tw, minWidth); _maxw = qMax(maxActualWidth, th); _minh = qMax(th, int32(st::minPhotoSize)); if (bubble) { @@ -3222,8 +3222,7 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) { } } } else { - th = w; // square chat photo updates - _maxw = _minh = w; + _maxw = _minh = _width; } } @@ -3258,39 +3257,92 @@ int32 HistoryPhoto::resize(int32 width, const HistoryItem *parent) { if (_pixh < 1) _pixh = 1; int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - w = qMax(_pixw, int16(minWidth)); + _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(); - w += st::mediaPadding.left() + st::mediaPadding.right(); if (!_caption.isEmpty()) { - int32 captionw = w - st::msgPadding.left() - st::msgPadding.right(); + int32 captionw = _width - st::msgPadding.left() - st::msgPadding.right(); _height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom(); } } return _height; } -const QString HistoryPhoto::inDialogsText() const { - return _caption.isEmpty() ? lang(lng_in_dlg_photo) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone); +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; + 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 (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); + } + _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.drawPixmapLeft(skipx, skipy, _width, pix); + if (!full) { + uint64 dt = itemAnimations().animate(parent, ms); + 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, rtlrect(skipx, skipy, width, height, _width), textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + } + + // 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); + } } -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); -} - -void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const { +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 (width < 0) width = w; - if (width < 1) return; - - int skipx = 0, skipy = 0, height = _height; if (bubble) { skipx = st::mediaPadding.left(); skipy = st::mediaPadding.top(); @@ -3320,8 +3372,10 @@ void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x } } -HistoryMedia *HistoryPhoto::clone() const { - return new HistoryPhoto(*this); +void HistoryPhoto::getStateOverview(TextLinkPtr &lnk, int32 x, int32 y, const HistoryItem *parent, int32 width) const { + if (x >= 0 && y >= 0 && x < width && y < width) { + lnk = _openl; + } } void HistoryPhoto::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { @@ -3358,72 +3412,12 @@ void HistoryPhoto::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, } } -void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { - if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; - int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); +const QString HistoryPhoto::inDialogsText() const { + return _caption.isEmpty() ? lang(lng_in_dlg_photo) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone); +} - bool bubble = parent->hasBubble(); - bool fromChannel = parent->fromChannel(), out = parent->out(), outbg = out && !fromChannel; - - 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); - } - _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.drawPixmapLeft(skipx, skipy, w, pix); - if (!full) { - uint64 dt = itemAnimations().animate(parent, ms); - 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, rtlrect(skipx, skipy, width, height, w), textstyleCurrent()->selectOverlay, SelectedOverlayCorners); - } - - // 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); - } +const QString HistoryPhoto::inHistoryText() const { + return qsl("[ ") + lang(lng_in_dlg_photo) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]"); } ImagePtr HistoryPhoto::replyPreview() { @@ -3651,10 +3645,10 @@ void HistoryVideo::initDimensions(const HistoryItem *parent) { th = st::msgVideoSize.height(); } - w = _thumbw = qMax(tw, 1); + _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(w, minWidth); + _maxw = qMax(_thumbw, minWidth); _minh = qMax(th, int32(st::minPhotoSize)); if (bubble) { _maxw += st::mediaPadding.left() + st::mediaPadding.right(); @@ -3665,56 +3659,7 @@ void HistoryVideo::initDimensions(const HistoryItem *parent) { } } -void HistoryVideo::setStatusSize(int32 newSize) const { - HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration, 0); -} - -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->loader) { - statusSize = _data->loader->currentOffset(); - } else if (!_data->already().isEmpty()) { - statusSize = FileStatusSizeLoaded; - } else { - statusSize = FileStatusSizeReady; - } - if (statusSize != _statusSize) { - setStatusSize(statusSize); - } -} - -void HistoryVideo::regItem(HistoryItem *item) { - App::regVideoItem(_data, item); -} - -void HistoryVideo::unregItem(HistoryItem *item) { - App::unregVideoItem(_data, item); -} - -const QString HistoryVideo::inDialogsText() const { - return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone); -} - -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 { - if (width < 0) width = w; - return (x >= 0 && y >= 0 && x < width && y < _height); -} - -int32 HistoryVideo::countHeight(const HistoryItem *parent, int32 width) const { - if (width < 0) width = w; - if (width >= _maxw) { - width = _maxw; - } - +int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) { bool bubble = parent->hasBubble(); int32 tw = convertScale(_data->thumb->width()), th = convertScale(_data->thumb->height()); @@ -3736,64 +3681,29 @@ int32 HistoryVideo::countHeight(const HistoryItem *parent, int32 width) const { th = qRound((width / float64(tw)) * th); tw = width; } - int32 h = qMax(th, int32(st::minPhotoSize)); + + 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, minWidth); + _height = qMax(th, int32(st::minPhotoSize)); if (bubble) { - tw += st::mediaPadding.left() + st::mediaPadding.right(); - h += st::mediaPadding.top() + st::mediaPadding.bottom(); + _width += st::mediaPadding.left() + st::mediaPadding.right(); + _height += st::mediaPadding.top() + st::mediaPadding.bottom(); if (!_caption.isEmpty()) { - h += st::mediaCaptionSkip + _caption.countHeight(tw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom(); + int32 captionw = _width - st::msgPadding.left() - st::msgPadding.right(); + _height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom(); } } - return h; -} - -void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const { - bool bubble = parent->hasBubble(); - - if (width < 0) width = w; - if (width < 1) return; - - int skipx = 0, skipy = 0, height = _height; - 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 = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; - 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; - } -} - -HistoryMedia *HistoryVideo::clone() const { - return new HistoryVideo(*this); + return _height; } void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { - if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; - int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); - + if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 skipx = 0, skipy = 0, width = _width, height = _height; bool bubble = parent->hasBubble(); - bool fromChannel = parent->fromChannel(), out = parent->out(), outbg = out && !fromChannel; + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + + int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); if (_data->loader) { ensureAnimation(parent); @@ -3814,11 +3724,11 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b 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); + App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); } _data->thumb->checkload(); - QRect rthumb(rtlrect(skipx, skipy, width, height, w)); + QRect rthumb(rtlrect(skipx, skipy, width, height, _width)); QPixmap pix = _data->thumb->pixBlurredSingle(_thumbw, 0, width, height); p.drawPixmap(rthumb.topLeft(), pix); @@ -3865,10 +3775,10 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b 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, w), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + 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, w, _statusText, statusW - 2 * st::msgDateImgPadding.x()); + p.drawTextLeft(statusX, statusY, _width, _statusText, statusW - 2 * st::msgDateImgPadding.x()); // date if (_caption.isEmpty()) { @@ -3882,44 +3792,86 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b } } -int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) { +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; + int32 skipx = 0, skipy = 0, width = _width, height = _height; 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; - } - w = _thumbw = tw; - - 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())); - w = qMax(w, minWidth); - - _height = qMax(th, int32(st::minPhotoSize)); - if (bubble) { - w += st::mediaPadding.left() + st::mediaPadding.right(); - _height += st::mediaPadding.top() + st::mediaPadding.bottom(); + skipx = st::mediaPadding.left(); + skipy = st::mediaPadding.top(); if (!_caption.isEmpty()) { - int32 captionw = w - st::msgPadding.left() - st::msgPadding.right(); - _height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom(); + 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(); } - return _height; + if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { + lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; + 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 HistoryVideo::drawOverview(Painter &p, int32 width, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { +} + +void HistoryVideo::getStateOverview(TextLinkPtr &lnk, int32 x, int32 y, const HistoryItem *parent, int32 width) const { + if (x >= 0 && y >= 0 && x < width && y < width) { + lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; + } +} + +void HistoryVideo::setStatusSize(int32 newSize) const { + HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration, 0); +} + +const QString HistoryVideo::inDialogsText() const { + return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone); +} + +const QString HistoryVideo::inHistoryText() const { + return qsl("[ ") + lang(lng_in_dlg_video) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]"); +} + +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->loader) { + statusSize = _data->loader->currentOffset(); + } else if (!_data->already().isEmpty()) { + statusSize = FileStatusSizeLoaded; + } else { + statusSize = FileStatusSizeReady; + } + if (statusSize != _statusSize) { + setStatusSize(statusSize); + } +} + +void HistoryVideo::regItem(HistoryItem *item) { + App::regVideoItem(_data, item); +} + +void HistoryVideo::unregItem(HistoryItem *item) { + App::unregVideoItem(_data, item); } ImagePtr HistoryVideo::replyPreview() { @@ -3950,6 +3902,159 @@ HistoryAudio::HistoryAudio(const HistoryAudio &other) : HistoryFileMedia() setStatusSize(other._statusSize); } +void HistoryAudio::initDimensions(const HistoryItem *parent) { + _maxw = st::msgFileMinWidth; + + 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, const QRect &r, bool selected, uint64 ms) const { + if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; + + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); + + if (!_data->loader && _data->status == FileReady && !already && !hasdata && _data->size < AudioVoiceMsgInMemory) { + _data->save(QString()); + } + + if (_data->loader) { + ensureAnimation(parent); + if (!_animation->radial.animating()) { + _animation->radial.start(_data->progress()); + } + } + bool showPause = updateStatusText(parent); + bool radial = isRadialAnimation(ms); + + int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; + + 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.setPen(Qt::NoPen); + if (selected) { + p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); + } else if (_animation && _animation->_a_thumbOver.animating()) { + _animation->_a_thumbOver.step(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->loader ? _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, bg); + } + + style::sprite icon; + if (showPause) { + icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause); + } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { + icon = outbg ? (selected ? st::msgFileOutPlaySelected : st::msgFileOutPlay) : (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); + } else if (_data->loader) { + icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } 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); + + if (parent->isMediaUnread()) { + int32 w = st::normalFont->width(_statusText); + if (w + st::mediaUnreadSkip + st::mediaUnreadSize <= namewidth) { + p.setPen(Qt::NoPen); + 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); + } + } +} + +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; + + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); + + 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->loader || _data->status == FileUploading || (!already && !hasdata)) && inner.contains(x, y)) { + lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; + return; + } + + if (x >= 0 && y >= 0 && x < _width && y < _height && !_data->loader && _data->access) { + lnk = _openl; + return; + } +} + +void HistoryAudio::drawOverview(Painter &p, int32 width, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { +} + +void HistoryAudio::getStateOverview(TextLinkPtr &lnk, int32 x, int32 y, const HistoryItem *parent, int32 width) const { +} + +const QString HistoryAudio::inDialogsText() const { + return lang(lng_in_dlg_audio); +} + +const QString HistoryAudio::inHistoryText() const { + return qsl("[ ") + lang(lng_in_dlg_audio) + qsl(" ]"); +} + +void HistoryAudio::regItem(HistoryItem *item) { + App::regAudioItem(_data, item); +} + +void HistoryAudio::unregItem(HistoryItem *item) { + App::unregAudioItem(_data, item); +} + +void HistoryAudio::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { + 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); + } + } +} + void HistoryAudio::setStatusSize(int32 newSize, qint64 realDuration) const { HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration, realDuration); } @@ -3988,175 +4093,6 @@ bool HistoryAudio::updateStatusText(const HistoryItem *parent) const { return showPause; } -void HistoryAudio::initDimensions(const HistoryItem *parent) { - _maxw = st::msgFileMinWidth; - - 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, const QRect &r, bool selected, uint64 ms) const { - if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; - - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; - bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); - - if (!_data->loader && _data->status == FileReady && !already && !hasdata && _data->size < AudioVoiceMsgInMemory) { - _data->save(QString()); - } - - if (width >= _maxw) { - width = _maxw; - } - - if (_data->loader) { - ensureAnimation(parent); - if (!_animation->radial.animating()) { - _animation->radial.start(_data->progress()); - } - } - bool showPause = updateStatusText(parent); - bool radial = isRadialAnimation(ms); - - int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; - - 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.setPen(Qt::NoPen); - if (selected) { - p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); - } else if (_animation && _animation->_a_thumbOver.animating()) { - _animation->_a_thumbOver.step(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->loader ? _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, bg); - } - - style::sprite icon; - if (showPause) { - icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause); - } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { - icon = outbg ? (selected ? st::msgFileOutPlaySelected : st::msgFileOutPlay) : (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); - } else if (_data->loader) { - icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); - } 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); - - if (parent->isMediaUnread()) { - int32 w = st::normalFont->width(_statusText); - if (w + st::mediaUnreadSkip + st::mediaUnreadSize <= namewidth) { - p.setPen(Qt::NoPen); - 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); - } - } -} - -void HistoryAudio::regItem(HistoryItem *item) { - App::regAudioItem(_data, item); -} - -void HistoryAudio::unregItem(HistoryItem *item) { - App::unregAudioItem(_data, item); -} - -void HistoryAudio::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { - 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); - } - } -} - -const QString HistoryAudio::inDialogsText() const { - return lang(lng_in_dlg_audio); -} - -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::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; - bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); - - if (width >= _maxw) { - width = _maxw; - } - - 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->loader || _data->status == FileUploading || (!already && !hasdata)) && inner.contains(x, y)) { - lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; - return; - } - - if (x >= 0 && y >= 0 && x < width && y < _height && !_data->loader && _data->access) { - lnk = _openl; - return; - } -} - -HistoryMedia *HistoryAudio::clone() const { - return new HistoryAudio(*this); -} - HistoryDocument::HistoryDocument(DocumentData *document) : HistoryFileMedia() , _data(document) , _linksavel(new DocumentSaveLink(_data)) @@ -4194,6 +4130,232 @@ HistoryDocument::HistoryDocument(const HistoryDocument &other) : HistoryFileMedi setStatusSize(other._statusSize); } +void HistoryDocument::initDimensions(const HistoryItem *parent) { + _maxw = st::msgFileMinWidth; + + 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 { + tleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); + tright = st::msgFileThumbPadding.left(); + _maxw = qMax(_maxw, tleft + documentMaxStatusWidth(_data) + parent->skipBlockWidth() + st::msgPadding.right()); + } + + _maxw = qMax(tleft + _namew + tright, _maxw); + _maxw = qMin(_maxw, int(st::msgMaxWidth)); + + if (wthumb) { + _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); + } else { + _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); + } + _height = _minh; +} + +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; + + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); + + if (_data->loader) { + ensureAnimation(parent); + if (!_animation->radial.animating()) { + _animation->radial.start(_data->progress()); + } + } + bool showPause = updateStatusText(parent); + bool radial = isRadialAnimation(ms); + + int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 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; + + QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); + if (_data->thumb->loaded()) { + QPixmap thumb = (already || hasdata) ? _data->thumb->pixSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize); + p.drawPixmap(rthumb.topLeft(), thumb); + } else { + App::roundRect(p, rthumb, st::black, BlackCorners); + } + if (selected) { + App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + } + + if (!radial && (already || hasdata)) { + } else { + 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 (radial && (already || hasdata)) { + p.setOpacity(st::msgDateImgBg->c.alphaF() * _animation->radial.opacity()); + p.setBrush(st::black); + } else if (_animation && _animation->_a_thumbOver.animating()) { + _animation->_a_thumbOver.step(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->loader ? _cancell : _savel); + p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); + } + + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.drawEllipse(inner); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + + style::sprite icon; + if (already || hasdata || _data->loader) { + icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } else { + icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); + } + p.setOpacity(radial ? _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, selected ? st::msgInBgSelected : st::msgInBg); + } + } + + if (_data->status != FileUploadFailed) { + const TextLinkPtr &lnk((_data->loader || _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 { + 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.setPen(Qt::NoPen); + if (selected) { + p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); + } else if (_animation && _animation->_a_thumbOver.animating()) { + 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->loader ? _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, bg); + } + + style::sprite icon; + if (showPause) { + icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause); + } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { + 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 if (_data->loader) { + icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } 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); +} + +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 already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); + + bool showPause = updateStatusText(parent); + + int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; + bool wthumb = withThumb(); + if (wthumb) { + nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); + linktop = st::msgFileThumbLinkTop; + + QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); + + if (already || hasdata) { + } else { + if (rthumb.contains(x, y)) { + lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; + return; + } + } + + if (_data->status != FileUploadFailed) { + if (rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, _width).contains(x, y)) { + lnk = (_data->loader || _data->status == FileUploading) ? _linkcancell : _linksavel; + return; + } + } + } else { + QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width)); + if ((_data->loader || _data->status == FileUploading || (!already && !hasdata)) && inner.contains(x, y)) { + lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; + return; + } + } + if (x >= 0 && y >= 0 && x < _width && y < _height && !_data->loader && _data->access) { + lnk = _openl; + return; + } +} + +void HistoryDocument::drawOverview(Painter &p, int32 width, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { +} + +void HistoryDocument::getStateOverview(TextLinkPtr &lnk, int32 x, int32 y, const HistoryItem *parent, int32 width) const { +} + +const QString HistoryDocument::inDialogsText() const { + return _name.isEmpty() ? lang(lng_in_dlg_file) : _name; +} + +const QString HistoryDocument::inHistoryText() const { + return qsl("[ ") + lang(lng_in_dlg_file) + (_name.isEmpty() ? QString() : (qsl(" : ") + _name)) + qsl(" ]"); +} + void HistoryDocument::setStatusSize(int32 newSize, qint64 realDuration) const { HistoryFileMedia::setStatusSize(newSize, _data->size, _data->song() ? _data->song()->duration : -1, realDuration); @@ -4250,239 +4412,6 @@ bool HistoryDocument::updateStatusText(const HistoryItem *parent) const { return showPause; } -void HistoryDocument::initDimensions(const HistoryItem *parent) { - _maxw = st::msgFileMinWidth; - - 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 { - tleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); - tright = st::msgFileThumbPadding.left(); - _maxw = qMax(_maxw, tleft + documentMaxStatusWidth(_data) + parent->skipBlockWidth() + st::msgPadding.right()); - } - - _maxw = qMax(tleft + _namew + tright, _maxw); - _maxw = qMin(_maxw, int(st::msgMaxWidth)); - - if (wthumb) { - _height = _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); - } else { - _height = _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); - } -} - -void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { - if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; - - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; - bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); - - if (width >= _maxw) { - width = _maxw; - } - - if (_data->loader) { - ensureAnimation(parent); - if (!_animation->radial.animating()) { - _animation->radial.start(_data->progress()); - } - } - bool showPause = updateStatusText(parent); - bool radial = isRadialAnimation(ms); - - int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 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; - - QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width)); - if (_data->thumb->loaded()) { - QPixmap thumb = (already || hasdata) ? _data->thumb->pixSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize); - p.drawPixmap(rthumb.topLeft(), thumb); - } else { - App::roundRect(p, rthumb, st::black, BlackCorners); - } - if (selected) { - App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); - } - - if (!radial && (already || hasdata)) { - } else { - 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 (radial && (already || hasdata)) { - p.setOpacity(st::msgDateImgBg->c.alphaF() * _animation->radial.opacity()); - p.setBrush(st::black); - } else if (_animation && _animation->_a_thumbOver.animating()) { - _animation->_a_thumbOver.step(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->loader ? _cancell : _savel); - p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); - } - - p.setRenderHint(QPainter::HighQualityAntialiasing); - p.drawEllipse(inner); - p.setRenderHint(QPainter::HighQualityAntialiasing, false); - - style::sprite icon; - if (already || hasdata || _data->loader) { - icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); - } else { - icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); - } - p.setOpacity(radial ? _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, selected ? st::msgInBgSelected : st::msgInBg); - } - } - - if (_data->status != FileUploadFailed) { - const TextLinkPtr &lnk((_data->loader || _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 { - 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.setPen(Qt::NoPen); - if (selected) { - p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); - } else if (_animation && _animation->_a_thumbOver.animating()) { - 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->loader ? _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, bg); - } - - style::sprite icon; - if (showPause) { - icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause); - } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { - 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 if (_data->loader) { - icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); - } 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); -} - -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::msgPadding.top() + st::mediaThumbSize + st::msgPadding.bottom(); - - style::color bg(selected ? st::msgInBgSelected : (over ? st::playlistHoverBg : st::msgInBg)); - p.fillRect(0, 0, width, height, bg->b); - - style::sprite img = st::mediaMusicInImg; - bool showPause = updateStatusText(parent); - 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 (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { - img = st::mediaMusicInImg; - } else if (_data->status == FileUploading) { - img = st::mediaMusicInImg; - } else if (already || hasdata) { - bool isPlaying = (playing.msgId == parent->fullId()); - img = isPlaying ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg); - } else { - img = st::mediaMusicInImg; - } - } - - p.drawSpriteLeft(QPoint(st::msgPadding.left(), st::msgPadding.top()), width, img); - if (selected) { - App::roundRect(p, rtlrect(st::msgPadding.left(), st::msgPadding.top(), st::mediaThumbSize, st::mediaThumbSize, width), textstyleCurrent()->selectOverlay, SelectedOverlayCorners); - } - - int32 tleft = st::msgPadding.left() + st::mediaThumbSize + st::msgPadding.right(); - int32 twidth = width - tleft - st::msgPadding.right(); - int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth(); - - p.setFont(st::normalFont->f); - p.setPen(st::black->c); - if (twidth < _namew) { - p.drawTextLeft(tleft, st::msgPadding.top() + st::mediaNameTop, width, st::normalFont->elided(_name, twidth)); - } else { - p.drawTextLeft(tleft, st::msgPadding.top() + st::mediaNameTop, width, _name, _namew); - } - - style::color status(selected ? st::mediaInFgSelected : st::mediaInFg); - p.setPen(status->p); - p.drawTextLeft(tleft, st::msgPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height, width, _statusText); -} - -TextLinkPtr HistoryDocument::linkInPlaylist() { - if (!_data->loader && _data->access) { - return _openl; - } - return TextLinkPtr(); -} - void HistoryDocument::regItem(HistoryItem *item) { App::regDocumentItem(_data, item); } @@ -4497,88 +4426,6 @@ void HistoryDocument::updateFrom(const MTPMessageMedia &media, HistoryItem *pare } } -int32 HistoryDocument::resize(int32 width, const HistoryItem *parent) { - w = qMin(width, _maxw); - _height = _minh; - return _height; -} - -const QString HistoryDocument::inDialogsText() const { - return _name.isEmpty() ? lang(lng_in_dlg_file) : _name; -} - -const QString HistoryDocument::inHistoryText() const { - return qsl("[ ") + lang(lng_in_dlg_file) + (_name.isEmpty() ? QString() : (qsl(" : ") + _name)) + qsl(" ]"); -} - -bool HistoryDocument::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); -} - -int32 HistoryDocument::countHeight(const HistoryItem *parent, int32 width) const { - if (width < 0) width = w; - if (width >= _maxw) { - width = _maxw; - } - return _height; -} - -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; - bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); - - if (width >= _maxw) { - width = _maxw; - } - - bool showPause = updateStatusText(parent); - - int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; - bool wthumb = withThumb(); - if (wthumb) { - nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); - linktop = st::msgFileThumbLinkTop; - - QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width)); - - if (already || hasdata) { - } else { - if (rthumb.contains(x, y)) { - lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; - return; - } - } - - if (_data->status != FileUploadFailed) { - if (rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, width).contains(x, y)) { - lnk = (_data->loader || _data->status == FileUploading) ? _linkcancell : _linksavel; - return; - } - } - } else { - QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width)); - if ((_data->loader || _data->status == FileUploading || (!already && !hasdata)) && inner.contains(x, y)) { - lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; - return; - } - } - if (x >= 0 && y >= 0 && x < width && y < _height && !_data->loader && _data->access) { - lnk = _openl; - return; - } -} - -HistoryMedia *HistoryDocument::clone() const { - return new HistoryDocument(*this); -} - ImagePtr HistoryDocument::replyPreview() { return _data->makeReplyPreview(); } @@ -4648,17 +4495,69 @@ void HistoryGif::initDimensions(const HistoryItem *parent) { _maxw += st::mediaPadding.left() + st::mediaPadding.right(); _minh += st::mediaPadding.top() + st::mediaPadding.bottom(); } - w = _maxw; - _height = _minh; +} + +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(); + } + + return _height; } void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { - if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; + 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; bool animating = (_gif && _gif->started()); - bool bubble = parent->hasBubble(); - bool fromChannel = parent->fromChannel(), out = parent->out(), outbg = out && !fromChannel; if (!animating) { if (_data->loader) { @@ -4682,7 +4581,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo } _data->thumb->checkload(); - QRect rthumb(rtlrect(skipx, skipy, width, height, w)); + QRect rthumb(rtlrect(skipx, skipy, width, height, _width)); if (animating) { p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, width, height, ms)); @@ -4734,10 +4633,10 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo 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, w), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + 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, w, _statusText, statusW - 2 * st::msgDateImgPadding.x()); + p.drawTextLeft(statusX, statusY, _width, _statusText, statusW - 2 * st::msgDateImgPadding.x()); // date if (parent->getMedia() == this) { @@ -4748,6 +4647,43 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo } } +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(); + width -= st::mediaPadding.left() + st::mediaPadding.right(); + height -= skipy + st::mediaPadding.bottom(); + } + if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { + if (_gif && _gif->started()) { + lnk = _savel; + } else { + lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _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 _data->name.isEmpty() ? lang(lng_in_dlg_file) : _data->name; +} + +const QString HistoryGif::inHistoryText() const { + return qsl("[ ") + lang(lng_in_dlg_file) + (_data->name.isEmpty() ? QString() : (qsl(" : ") + _data->name)) + qsl(" ]"); +} + void HistoryGif::setStatusSize(int32 newSize) const { HistoryFileMedia::setStatusSize(newSize, _data->size, -2, 0); } @@ -4785,155 +4721,6 @@ void HistoryGif::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, b } } -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; - - w = qMax(tw, int32(st::minPhotoSize)); - _height = qMax(th, int32(st::minPhotoSize)); - if (_gif && _gif->ready()) { - if (!_gif->started()) { - _gif->start(_thumbw, _thumbh, w, _height, true); - } - } else { - w = qMax(w, parent->infoWidth() + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); - w = qMax(w, gifMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); - } - if (bubble) { - w += st::mediaPadding.left() + st::mediaPadding.right(); - _height += st::mediaPadding.top() + st::mediaPadding.bottom(); - } - - return _height; -} - -const QString HistoryGif::inDialogsText() const { - return _data->name.isEmpty() ? lang(lng_in_dlg_file) : _data->name; -} - -const QString HistoryGif::inHistoryText() const { - return qsl("[ ") + lang(lng_in_dlg_file) + (_data->name.isEmpty() ? QString() : (qsl(" : ") + _data->name)) + qsl(" ]"); -} - -bool HistoryGif::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); -} - -int32 HistoryGif::countHeight(const HistoryItem *parent, int32 width) const { - if (width < 0) width = w; - if (width >= _maxw) { - width = _maxw; - } - - bool bubble = parent->hasBubble(); - - int32 tw = 0, th = 0; - if (_gif && _gif->started()) { - 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; - } - - int32 h = qMax(th, int32(st::minPhotoSize)); - if (bubble) { - tw += st::mediaPadding.left() + st::mediaPadding.right(); - h += st::mediaPadding.top() + st::mediaPadding.bottom(); - } - return h; -} - -void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const { - bool bubble = parent->hasBubble(); - - if (width < 0) width = w; - if (width < 1) return; - - int skipx = 0, skipy = 0, height = _height; - if (bubble) { - skipx = st::mediaPadding.left(); - skipy = st::mediaPadding.top(); - width -= st::mediaPadding.left() + st::mediaPadding.right(); - height -= skipy + st::mediaPadding.bottom(); - } - if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { - if (_gif && _gif->started()) { - lnk = _savel; - } else { - lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _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; - } -} - -HistoryMedia *HistoryGif::clone() const { - return new HistoryGif(*this); -} - ImagePtr HistoryGif::replyPreview() { return _data->makeReplyPreview(); } @@ -4967,8 +4754,9 @@ HistoryGif::~HistoryGif() { } HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia() -, pixw(1), pixh(1), data(document), lastw(0) -{ +, pixw(1) +, pixh(1) +, data(document) { data->thumb->load(); if (!data->sticker()->alt.isEmpty()) { _emoji = data->sticker()->alt; @@ -4998,14 +4786,10 @@ void HistorySticker::initDimensions(const HistoryItem *parent) { _maxw += st::msgReplyPadding.left() + reply->replyToWidth(); } _height = _minh; - w = qMin(lastw, _maxw); } void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { - if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; - - if (width > _maxw) width = _maxw; + if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; bool already = !data->already().isEmpty(), hasdata = !data->data.isEmpty(); @@ -5016,10 +4800,10 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, usew -= st::msgReplyPadding.left() + reply->replyToWidth(); if (fromChannel) { } else if (out) { - usex = width - usew; + usex = _width - usew; } } - if (rtl()) usex = width - usex - usew; + if (rtl()) usex = _width - usex - usew; if (!already && !hasdata && !data->loader && data->status == FileReady) { data->save(QString()); @@ -5049,9 +4833,9 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, 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 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; + if (rtl()) rx = _width - rx - rw; App::roundRect(p, rx, ry, rw, rh, selected ? App::msgServiceSelectBg() : App::msgServiceBg(), selected ? ServiceSelectedCorners : ServiceCorners); @@ -5060,10 +4844,44 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, } } -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; + + 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; + } + } + 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; + } + } +} + +const QString HistorySticker::inDialogsText() const { + return _emoji.isEmpty() ? lang(lng_in_dlg_sticker) : lng_in_dlg_sticker_emoji(lt_emoji, _emoji); +} + +const QString HistorySticker::inHistoryText() const { + return qsl("[ ") + inDialogsText() + qsl(" ]"); } void HistorySticker::regItem(HistoryItem *item) { @@ -5083,58 +4901,6 @@ void HistorySticker::updateFrom(const MTPMessageMedia &media, HistoryItem *paren } } -const QString HistorySticker::inDialogsText() const { - return _emoji.isEmpty() ? lang(lng_in_dlg_sticker) : lng_in_dlg_sticker_emoji(lt_emoji, _emoji); -} - -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); -} - -int32 HistorySticker::countHeight(const HistoryItem *parent, int32 width) const { - return _minh; -} - -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; - } - } - if (parent->getMedia() == this) { - bool inDate = parent->pointInTime(usex + usew, _height, x, y, InfoDisplayOverImage); - if (inDate) { - state = HistoryInDateCursorState; - } - } -} - -HistoryMedia *HistorySticker::clone() const { - return new HistorySticker(*this); -} - void SendMessageLink::onClick(Qt::MouseButton button) const { if (button == Qt::LeftButton) { Ui::showPeerHistory(peer()->id, ShowAtUnreadMsgId); @@ -5200,75 +4966,16 @@ void HistoryContact::initDimensions(const HistoryItem *parent) { _maxw = qMin(_maxw, int(st::msgMaxWidth)); if (_userId) { - _height = _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); + _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); } else { - _height = _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); - } -} - -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(" ]"); -} - -bool HistoryContact::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 HistoryContact::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; - - 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(); - linktop = st::msgFileThumbLinkTop; - - QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width)); - - 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; - } -} - -HistoryMedia *HistoryContact::clone() const { - return new HistoryContact(_userId, _fname, _lname, _phone); -} - -void HistoryContact::regItem(HistoryItem *item) { - if (_userId) { - App::regSharedContactItem(_userId, item); - } -} - -void HistoryContact::unregItem(HistoryItem *item) { - if (_userId) { - App::unregSharedContactItem(_userId, item); + _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 (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; + 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; @@ -5313,13 +5020,51 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, const QRect &r, 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(" ]"); +} + +void HistoryContact::regItem(HistoryItem *item) { + if (_userId) { + App::regSharedContactItem(_userId, item); + } +} + +void HistoryContact::unregItem(HistoryItem *item) { + if (_userId) { + App::unregSharedContactItem(_userId, item); + } +} + void HistoryContact::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { if (media.type() == mtpc_messageMediaContact) { if (_userId != media.c_messageMediaContact().vuser_id.v) { @@ -5516,127 +5261,16 @@ void HistoryWebPage::initDimensions(const HistoryItem *parent) { if (_asArticle) { _minh += st::msgDateFont->height; } - w = _maxw; - _height = _minh; -} - -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::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { - if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; - - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; - - 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)); - - 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; - } - - QRect bar(rtlrect(st::msgPadding.left(), 0, st::webPageBar, _height - bshift, w)); - p.fillRect(bar, barfg); - - if (_asArticle) { - _data->photo->medium->load(false, false); - bool full = _data->photo->medium->loaded(); - QPixmap pix; - int32 pw = qMax(_pixw, int16(_lineHeight)); - if (full) { - pix = _data->photo->medium->pixSingle(_pixw, articleThumbHeight(_data->photo, _pixw), pw, _pixh); - } else { - pix = _data->photo->thumb->pixBlurredSingle(_pixw, articleThumbHeight(_data->photo, _pixw), pw, _pixh); - } - p.drawPixmapLeft(lshift + width - pw, 0, w, pix); - if (selected) { - App::roundRect(p, rtlrect(lshift + width - pw, 0, pw, _pixh, w), textstyleCurrent()->selectOverlay, SelectedOverlayCorners); - } - width -= pw + st::webPagePhotoDelta; - } - int32 tshift = 0; - if (_siteNameWidth) { - p.setFont(st::webPageTitleFont); - p.setPen(semibold); - p.drawTextLeft(lshift, tshift, w, (width >= _siteNameWidth) ? _data->siteName : st::webPageTitleFont->elided(_data->siteName, width)); - tshift += _lineHeight; - } - if (_titleLines) { - p.setPen(st::black); - int32 endskip = 0; - if (_title.hasSkipBlock()) { - endskip = parent->skipBlockWidth(); - } - _title.drawLeftElided(p, lshift, tshift, width, w, _titleLines, style::al_left, 0, -1, endskip); - tshift += _titleLines * _lineHeight; - } - if (_descriptionLines) { - p.setPen(st::black); - int32 endskip = 0; - if (_description.hasSkipBlock()) { - endskip = parent->skipBlockWidth(); - } - _description.drawLeftElided(p, lshift, tshift, width, w, _descriptionLines, style::al_left, 0, -1, endskip); - tshift += _descriptionLines * _lineHeight; - } - if (_attach) { - if (tshift) tshift += st::webPagePhotoSkip; - - int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top(); - if (rtl()) attachLeft = w - attachLeft - _attach->currentWidth(); - - p.save(); - p.translate(attachLeft, attachTop); - - _attach->draw(p, parent, r.translated(-attachLeft, -attachTop), selected, ms); - int32 pixwidth = _attach->currentWidth(), pixheight = _attach->height(); - - 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); - } - if (_durationWidth) { - int32 dateX = pixwidth - _durationWidth - st::msgDateImgDelta - 2 * st::msgDateImgPadding.x(); - int32 dateY = pixheight - st::msgDateFont->height - 2 * st::msgDateImgPadding.y() - st::msgDateImgDelta; - int32 dateW = pixwidth - dateX - st::msgDateImgDelta; - int32 dateH = pixheight - dateY - st::msgDateImgDelta; - - 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.drawTextLeft(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y(), pixwidth, _duration); - } - } - - p.restore(); - } } int32 HistoryWebPage::resize(int32 width, const HistoryItem *parent) { if (_data->pendingTill) { - w = width; + _width = width; _height = _minh; return _height; } - w = width; + _width = qMin(width, _maxw); width -= st::msgPadding.left() + st::webPageLeft + st::msgPadding.right(); int32 linesMax = 5; @@ -5718,6 +5352,161 @@ int32 HistoryWebPage::resize(int32 width, const HistoryItem *parent) { 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 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)); + + 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; + } + + QRect bar(rtlrect(st::msgPadding.left(), 0, st::webPageBar, _height - bshift, _width)); + p.fillRect(bar, barfg); + + if (_asArticle) { + _data->photo->medium->load(false, false); + bool full = _data->photo->medium->loaded(); + QPixmap pix; + int32 pw = qMax(_pixw, int16(_lineHeight)); + if (full) { + pix = _data->photo->medium->pixSingle(_pixw, articleThumbHeight(_data->photo, _pixw), pw, _pixh); + } else { + pix = _data->photo->thumb->pixBlurredSingle(_pixw, articleThumbHeight(_data->photo, _pixw), pw, _pixh); + } + p.drawPixmapLeft(lshift + width - pw, 0, _width, pix); + if (selected) { + App::roundRect(p, rtlrect(lshift + width - pw, 0, pw, _pixh, _width), textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + } + width -= pw + st::webPagePhotoDelta; + } + int32 tshift = 0; + if (_siteNameWidth) { + 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 (_titleLines) { + p.setPen(st::black); + int32 endskip = 0; + if (_title.hasSkipBlock()) { + endskip = parent->skipBlockWidth(); + } + _title.drawLeftElided(p, lshift, tshift, width, _width, _titleLines, style::al_left, 0, -1, endskip); + tshift += _titleLines * _lineHeight; + } + if (_descriptionLines) { + p.setPen(st::black); + int32 endskip = 0; + if (_description.hasSkipBlock()) { + endskip = parent->skipBlockWidth(); + } + _description.drawLeftElided(p, lshift, tshift, width, _width, _descriptionLines, style::al_left, 0, -1, endskip); + tshift += _descriptionLines * _lineHeight; + } + if (_attach) { + if (tshift) tshift += st::webPagePhotoSkip; + + int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top(); + if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth(); + + p.save(); + p.translate(attachLeft, attachTop); + + _attach->draw(p, parent, r.translated(-attachLeft, -attachTop), selected, ms); + int32 pixwidth = _attach->currentWidth(), pixheight = _attach->height(); + + 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); + } + if (_durationWidth) { + int32 dateX = pixwidth - _durationWidth - st::msgDateImgDelta - 2 * st::msgDateImgPadding.x(); + int32 dateY = pixheight - st::msgDateFont->height - 2 * st::msgDateImgPadding.y() - st::msgDateImgDelta; + int32 dateW = pixwidth - dateX - st::msgDateImgDelta; + int32 dateH = pixheight - dateY - st::msgDateImgDelta; + + App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + + p.setFont(st::msgDateFont); + p.setPen(st::msgDateImgColor); + p.drawTextLeft(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y(), pixwidth, _duration); + } + } + + p.restore(); + } +} + +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; + } + + if (_asArticle) { + int32 pw = qMax(_pixw, int16(_lineHeight)); + if (rtlrect(lshift + width - pw, 0, pw, _pixh, _width).contains(x, y)) { + lnk = _openl; + return; + } + 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 (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); + } + } +} + +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 (_attach) _attach->regItem(item); @@ -5736,64 +5525,6 @@ 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 < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 height = _height, skipx = 0, skipy = 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; - } - - if (_asArticle) { - int32 pw = qMax(_pixw, int16(_lineHeight)); - if (rtlrect(lshift + width - pw, 0, pw, _pixh, w).contains(x, y)) { - lnk = _openl; - return; - } - 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, w); - state = inText ? HistoryInTextCursorState : HistoryDefaultCursorState; - return; - } - tshift += _descriptionLines * _lineHeight; - } - if (_attach) { - if (tshift) tshift += 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 = w - attachLeft - _attach->currentWidth(); - _attach->getState(lnk, state, x - attachLeft, y - attachTop, parent); - } - } -} - -HistoryMedia *HistoryWebPage::clone() const { - return new HistoryWebPage(*this); -} - ImagePtr HistoryWebPage::replyPreview() { return _data->photo ? _data->photo->makeReplyPreview() : (_data->doc ? _data->doc->makeReplyPreview() : ImagePtr()); } @@ -6016,24 +5747,6 @@ _description(st::msgMinWidth) { } } -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 HistoryImageLink::initDimensions(const HistoryItem *parent) { bool bubble = parent->hasBubble(); @@ -6043,7 +5756,7 @@ void HistoryImageLink::initDimensions(const HistoryItem *parent) { tw = st::maxMediaSize; } int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - _maxw = w = qMax(tw, int32(minWidth)); + _maxw = qMax(tw, int32(minWidth)); _minh = qMax(th, int32(st::minPhotoSize)); if (bubble) { @@ -6063,15 +5776,53 @@ void HistoryImageLink::initDimensions(const HistoryItem *parent) { } } } - _height = _minh; +} + +int32 HistoryImageLink::resize(int32 width, const HistoryItem *parent) { + bool bubble = parent->hasBubble(); + + _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()) { + _height += qMin(_title.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()), st::webPageTitleFont->height * 2); + } + if (!_description.isEmpty()) { + _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(); - - if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; if (bubble) { @@ -6085,15 +5836,15 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, const QRect & } width -= st::mediaPadding.left() + st::mediaPadding.right(); - int32 textw = w - st::msgPadding.left() - st::msgPadding.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, w, 2); + _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, w, 3); + _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()) { @@ -6101,7 +5852,7 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, const QRect & } height -= skipy + st::mediaPadding.bottom(); } else { - App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); + App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); } _data->load(); @@ -6132,77 +5883,11 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, const QRect & } } -int32 HistoryImageLink::resize(int32 width, const HistoryItem *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 (bubble) { - w -= 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 > w) { - _height = (w * _height / tw); - } else { - w = tw; - } - int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - w = qMax(w, int32(minWidth)); - _height = qMax(_height, int32(st::minPhotoSize)); - if (bubble) { - w += st::mediaPadding.left() + st::mediaPadding.right(); - if (!_title.isEmpty()) { - _height += qMin(_title.countHeight(w - st::msgPadding.left() - st::msgPadding.right()), st::webPageTitleFont->height * 2); - } - if (!_description.isEmpty()) { - _height += qMin(_description.countHeight(w - st::msgPadding.left() - st::msgPadding.right()), st::webPageDescriptionFont->height * 3); - } - _height += st::mediaPadding.top() + st::mediaPadding.bottom(); - if (!_title.isEmpty() || !_description.isEmpty()) { - _height += st::webPagePhotoSkip; - if (!parent->toHistoryForwarded() && !parent->toHistoryReply()) { - _height += st::msgPadding.top(); - } - } - } - return _height; -} - -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(" ]"); -} - -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 { - bool bubble = parent->hasBubble(); - if (width < 0) width = w; - - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; - - int skipx = 0, skipy = 0, height = _height; if (bubble) { skipx = st::mediaPadding.left(); skipy = st::mediaPadding.top(); @@ -6214,7 +5899,7 @@ void HistoryImageLink::getState(TextLinkPtr &lnk, HistoryCursorState &state, int } width -= st::mediaPadding.left() + st::mediaPadding.right(); - int32 textw = w - st::msgPadding.left() - st::msgPadding.right(); + int32 textw = _width - st::msgPadding.left() - st::msgPadding.right(); if (!_title.isEmpty()) { skipy += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height); @@ -6240,8 +5925,40 @@ 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; } HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg) : diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 8b576d3bb..b8d8c94f7 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1145,32 +1145,40 @@ private: class HistoryMedia : public HistoryElem { public: - HistoryMedia() : w(0) { + 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, 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 drawOverview(Painter &p, int32 width, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + } + virtual void getStateOverview(TextLinkPtr &lnk, int32 x, int32 y, const HistoryItem *parent, int32 width) const { + } + virtual void linkOver(HistoryItem *parent, const TextLinkPtr &lnk) { } virtual void linkOut(HistoryItem *parent, const TextLinkPtr &lnk) { } - virtual void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const = 0; + virtual bool uploading() const { return false; } @@ -1225,12 +1233,12 @@ public: } int32 currentWidth() const { - return qMin(w, _maxw); + return _width; } protected: - int32 w; + int32 _width; }; @@ -1240,20 +1248,24 @@ public: HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent); HistoryPhoto(PhotoData *photo); HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0); - void init(); - void initDimensions(const HistoryItem *parent); - - void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) 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; + + void getStateOverview(TextLinkPtr &lnk, int32 x, int32 y, const HistoryItem *parent, int32 width) 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; @@ -1261,10 +1273,6 @@ public: void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize); - TextLinkPtr lnk() const { - return _openl; - } - virtual bool animating() const { if (_data->full->loaded()) return false; return _data->full->loading() ? true : !_data->medium->loaded(); @@ -1376,23 +1384,32 @@ public: HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent); HistoryVideo(const HistoryVideo &other); - - void initDimensions(const HistoryItem *parent); - - void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; - int32 resize(int32 width, const HistoryItem *parent); 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; + + void drawOverview(Painter &p, int32 width, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; + void getStateOverview(TextLinkPtr &lnk, int32 x, int32 y, const HistoryItem *parent, int32 width) 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; + + VideoData *video() const { + return _data; + } + bool uploading() const { return (_data->status == FileUploading); } - HistoryMedia *clone() const; void regItem(HistoryItem *item); void unregItem(HistoryItem *item); @@ -1442,21 +1459,27 @@ public: HistoryAudio(const MTPDaudio &audio); 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; - HistoryMediaType type() const { - return MediaTypeAudio; - } + void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const; + + void drawOverview(Painter &p, int32 width, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; + void getStateOverview(TextLinkPtr &lnk, int32 x, int32 y, const HistoryItem *parent, int32 width) 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); } - HistoryMedia *clone() const; AudioData *audio() { return _data; @@ -1502,28 +1525,32 @@ public: HistoryDocument(DocumentData *document); HistoryDocument(const HistoryDocument &other); + HistoryMediaType type() const { + return MediaTypeDocument; + } + HistoryMedia *clone() const { + return new HistoryDocument(*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; + + void drawOverview(Painter &p, int32 width, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; + void getStateOverview(TextLinkPtr &lnk, int32 x, int32 y, const HistoryItem *parent, int32 width) const; + + const QString inDialogsText() const; + const QString inHistoryText() const; + + bool uploading() const { + return (_data->status == FileUploading); + } + bool withThumb() const { return !_data->song() && !_data->thumb->isNull() && _data->thumb->width() && _data->thumb->height(); } - void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; - int32 resize(int32 width, const HistoryItem *parent); - HistoryMediaType type() const { - return MediaTypeDocument; - } - 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 *getDocument() { return _data; } @@ -1538,9 +1565,6 @@ public: } ImagePtr replyPreview(); - void drawInPlaylist(Painter &p, const HistoryItem *parent, bool selected, bool over, int32 width) const; - TextLinkPtr linkInPlaylist(); - bool needsBubble(const HistoryItem *parent) const { return true; } @@ -1588,27 +1612,30 @@ public: HistoryGif(DocumentData *document); HistoryGif(const HistoryGif &other); - - void initDimensions(const HistoryItem *parent); - - void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; - int32 resize(int32 width, const HistoryItem *parent); 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 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 *getDocument() { return _data; } + bool playInline(HistoryItem *item); void stopInline(HistoryItem *item); @@ -1664,19 +1691,20 @@ class HistorySticker : public HistoryMedia { public: HistorySticker(DocumentData *document); - void initDimensions(const HistoryItem *parent); - - void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) 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); + + 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; @@ -1699,7 +1727,6 @@ private: int16 pixw, pixh; DocumentData *data; QString _emoji; - int32 lastw; }; @@ -1727,17 +1754,20 @@ class HistoryContact : public HistoryMedia { public: HistoryContact(int32 userId, const QString &first, const QString &last, const QString &phone); - void initDimensions(const HistoryItem *parent); - - void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) 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 regItem(HistoryItem *item); void unregItem(HistoryItem *item); @@ -1780,25 +1810,28 @@ public: HistoryWebPage(WebPageData *data); 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; void linkOver(HistoryItem *parent, const TextLinkPtr &lnk); void linkOut(HistoryItem *parent, const TextLinkPtr &lnk); - void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; bool isDisplayed() const { return !_data->pendingTill; } - int32 resize(int32 width, const HistoryItem *parent); - HistoryMediaType type() const { - return MediaTypeWebPage; - } - 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; - DocumentData *getDocument() { return _attach ? _attach->getDocument() : 0; } @@ -1903,20 +1936,21 @@ 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, const QRect &r, bool selected, uint64 ms) 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; @@ -1934,6 +1968,9 @@ private: Text _title, _description; TextLinkPtr _link; + int32 fullWidth() const; + int32 fullHeight() const; + }; class HistoryMessage : public HistoryItem { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index a69741405..d292c49ba 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1497,9 +1497,6 @@ void MainWidget::itemResized(HistoryItem *row, bool scrollToIt) { history.resizeEvent(0); } } - if (overview) { - overview->itemResized(row, scrollToIt); - } if (row) Ui::redrawHistoryItem(row); } diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 35f02ae9b..4381571bb 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -146,14 +146,12 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD , _migrated(_peer->migrateFrom() ? App::history(_peer->migrateFrom()->id) : 0) , _history(App::history(_peer->id)) , _channel(peerToChannel(_peer->id)) +, _rowsLeft(st::msgMargin.left()) +, _rowWidth(st::msgMinWidth) +, _rowHeight(0) , _photosInRow(1) , _photosToAdd(0) , _selMode(false) -, _audioLeft(st::msgMargin.left()) -, _audioWidth(st::msgMinWidth) -, _audioHeight(st::msgPadding.top() + st::mediaThumbSize + st::msgPadding.bottom()) -, _linksLeft(st::linksSearchMargin.left()) -, _linksWidth(st::msgMinWidth) , _search(this, st::dlgFilter, lang(lng_dlg_filter)) , _cancelSearch(this, st::btnCancelSearch) , _itemsToBeLoaded(LinksOverviewPerPage * 2) @@ -217,7 +215,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchMessages())); _cancelSearch.hide(); - if (_type == OverviewLinks) { + if (_type == OverviewLinks || _type == OverviewDocuments) { _search.show(); } else { _search.hide(); @@ -294,7 +292,7 @@ int32 OverviewInner::migratedIndexSkip() const { void OverviewInner::fixItemIndex(int32 ¤t, MsgId msgId) const { if (!msgId) { current = -1; - } else if (_type == OverviewPhotos || _type == OverviewAudioDocuments) { + } else if (_type == OverviewPhotos || _type == OverviewVideos || _type == OverviewAudioDocuments) { History *history = itemMigrated(msgId) ? _migrated : _history; int32 l = history->overview[_type].size(), indexskip = migratedIndexSkip(); int32 index = (current >= 0 && history == _history) ? (current - indexskip) : current; @@ -433,14 +431,14 @@ QString OverviewInner::urlByIndex(MsgId msgid, int32 index, int32 lnkIndex, bool if (index < 0 || !_items[index].link) return QString(); if (lnkIndex < 0) { - if (fullShown) *fullShown = (_items[index].link->urls.size() == 1) && (_items[index].link->urls.at(0).width <= _linksWidth - (st::dlgPhotoSize + st::dlgPhotoPadding)); + if (fullShown) *fullShown = (_items[index].link->urls.size() == 1) && (_items[index].link->urls.at(0).width <= _rowWidth - (st::dlgPhotoSize + st::dlgPhotoPadding)); if (_items[index].link->page) { return _items[index].link->page->url; } else if (!_items[index].link->urls.isEmpty()) { return _items[index].link->urls.at(0).url; } } else if (lnkIndex > 0 && lnkIndex <= _items[index].link->urls.size()) { - if (fullShown) *fullShown = _items[index].link->urls.at(lnkIndex - 1).width <= _linksWidth - (st::dlgPhotoSize + st::dlgPhotoPadding); + if (fullShown) *fullShown = _items[index].link->urls.at(lnkIndex - 1).width <= _rowWidth - (st::dlgPhotoSize + st::dlgPhotoPadding); return _items[index].link->urls.at(lnkIndex - 1).url; } return QString(); @@ -455,43 +453,27 @@ bool OverviewInner::itemHasPoint(MsgId msgId, int32 index, int32 x, int32 y) con fixItemIndex(index, msgId); if (index < 0) return false; - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { if (x >= 0 && x < _vsize && y >= 0 && y < _vsize) { return true; } - } else if (_type == OverviewAudioDocuments) { - if (x >= _audioLeft && x < _audioLeft + _audioWidth && y >= 0 && y < _audioHeight) { - return true; - } - } else if (_type == OverviewLinks) { - if (x >= _linksLeft && x < _linksLeft + _linksWidth && y >= 0 && y < itemHeight(msgId, index)) { - return true; - } } else { - HistoryItem *item = App::histItemById(itemChannel(msgId), itemMsgId(msgId)); - HistoryMedia *media = item ? item->getMedia(true) : 0; - if (media) { - int32 w = _width - st::msgMargin.left() - st::msgMargin.right(); - bool out = item->out(), fromChannel = item->fromChannel(), outbg = out && !fromChannel; - int32 mw = media->maxWidth(), left = (fromChannel ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out ? st::msgMargin.right() : st::msgMargin.left())) + ((mw < w) ? (fromChannel ? 0 : (out ? w - mw : 0)) : 0); - if (item->displayFromPhoto()) { - left += st::msgPhotoSkip; - } - return media->hasPoint(x - left, y - st::msgMargin.top(), item, w); + if (x >= _rowsLeft && x < _rowsLeft + _rowWidth && y >= 0 && y < itemHeight(msgId, index)) { + return true; } } return false; } int32 OverviewInner::itemHeight(MsgId msgId, int32 index) const { - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { return _vsize; } else if (_type == OverviewAudioDocuments) { - return _audioHeight; + return _rowHeight; } fixItemIndex(index, msgId); - if (_type == OverviewLinks) { + if (_type == OverviewLinks || _type == OverviewDocuments) { return (index < 0) ? 0 : ((index + 1 < _items.size() ? _items[index + 1].y : (_height - _addToY)) - _items[index].y); } return (index < 0) ? 0 : (_items[index].y - (index > 0 ? _items[index - 1].y : 0)); @@ -506,7 +488,7 @@ void OverviewInner::moveToNextItem(MsgId &msgId, int32 &index, MsgId upTo, int32 } index += delta; - if (_type == OverviewPhotos || _type == OverviewAudioDocuments) { + if (_type == OverviewPhotos || _type == OverviewVideos || _type == OverviewAudioDocuments) { int32 indexskip = migratedIndexSkip(); if (index < 0 || index >= indexskip + _history->overview[_type].size()) { msgId = 0; @@ -530,16 +512,16 @@ void OverviewInner::moveToNextItem(MsgId &msgId, int32 &index, MsgId upTo, int32 void OverviewInner::redrawItem(MsgId itemId, int32 itemIndex) { fixItemIndex(itemIndex, itemId); if (itemIndex >= 0) { - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { float64 w = (float64(_width - st::overviewPhotoSkip) / _photosInRow); int32 vsize = (_vsize + st::overviewPhotoSkip); int32 row = (_photosToAdd + itemIndex) / _photosInRow, col = (_photosToAdd + itemIndex) % _photosInRow; update(int32(col * w), _addToY + int32(row * vsize), qCeil(w), vsize); } else if (_type == OverviewAudioDocuments) { - update(_audioLeft, _addToY + int32(itemIndex * _audioHeight), _audioWidth, _audioHeight); - } else if (_type == OverviewLinks) { - update(_linksLeft, _addToY + _items[itemIndex].y, _linksWidth, itemHeight(itemId, itemIndex)); - } else { + update(_rowsLeft, _addToY + int32(itemIndex * _rowHeight), _rowWidth, _rowHeight); + } else if (_type == OverviewLinks || _type == OverviewDocuments) { + update(_rowsLeft, _addToY + _items[itemIndex].y, _rowWidth, itemHeight(itemId, itemIndex)); + } else if (_type == OverviewAudios) { update(0, _addToY + _height - _items[itemIndex].y, _width, itemHeight(itemId, itemIndex)); } } @@ -909,7 +891,7 @@ void OverviewInner::addSelectionRange(int32 selFrom, int32 selTo, History *histo if (selFrom < 0 || selTo < 0) return; for (int32 i = selFrom; i <= selTo; ++i) { MsgId msgid = 0; - if (_type == OverviewPhotos || _type == OverviewAudioDocuments) { + if (_type == OverviewPhotos || _type == OverviewVideos || _type == OverviewAudioDocuments) { msgid = ((history == _history) ? 1 : -1) * history->overview[_type][i]; } else { msgid = _items[i].msgid; @@ -939,7 +921,7 @@ void OverviewInner::applyDragSelection() { _selected.clear(); } int32 selfrom = _dragSelToIndex, selto = _dragSelFromIndex; - if (_migrated && (_type == OverviewPhotos || _type == OverviewAudioDocuments)) { + if (_migrated && (_type == OverviewPhotos || _type == OverviewVideos || _type == OverviewAudioDocuments)) { int32 indexskip = migratedIndexSkip(); if (selfrom < indexskip) { if (selto < indexskip) { @@ -968,23 +950,23 @@ QPoint OverviewInner::mapMouseToItem(QPoint p, MsgId itemId, int32 itemIndex) { fixItemIndex(itemIndex, itemId); if (itemIndex < 0) return QPoint(0, 0); - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { int32 row = (_photosToAdd + itemIndex) / _photosInRow, col = (_photosToAdd + itemIndex) % _photosInRow; float64 w = (_width - st::overviewPhotoSkip) / float64(_photosInRow); p.setX(p.x() - int32(col * w) - st::overviewPhotoSkip); p.setY(p.y() - _addToY - row * (_vsize + st::overviewPhotoSkip) - st::overviewPhotoSkip); } else if (_type == OverviewAudioDocuments) { - p.setY(p.y() - _addToY - itemIndex * _audioHeight); - } else if (_type == OverviewLinks) { + p.setY(p.y() - _addToY - itemIndex * _rowHeight); + } else if (_type == OverviewLinks || _type == OverviewDocuments) { p.setY(p.y() - _addToY - _items[itemIndex].y); - } else { + } else if (_type == OverviewAudios) { p.setY(p.y() - _addToY - (_height - _items[itemIndex].y)); } return p; } void OverviewInner::activate() { - if (_type == OverviewLinks) { + if (_type == OverviewLinks || _type == OverviewDocuments) { _search.setFocus(); } else { setFocus(); @@ -1000,11 +982,11 @@ int32 OverviewInner::itemTop(const FullMsgId &msgId) const { if (msgId.channel == _channel) { int32 index = _history->overview[_type].indexOf(msgId.msg); if (index >= 0) { - return _addToY + int32((index + migratedIndexSkip()) * _audioHeight); + return _addToY + int32((index + migratedIndexSkip()) * _rowHeight); } } else if (_migrated && msgId.channel == _migrated->channelId()) { int32 index = _migrated->overview[_type].indexOf(msgId.msg); - return _addToY + int32(index * _audioHeight); + return _addToY + int32(index * _rowHeight); } } return -1; @@ -1062,6 +1044,19 @@ QPixmap OverviewInner::genPix(PhotoData *photo, int32 size) { return QPixmap::fromImage(img, Qt::ColorOnly); } +QPixmap OverviewInner::genPix(VideoData *video, int32 size) { + size *= cIntRetinaFactor(); + int32 tw = video->thumb->width(), th = video->thumb->height(); + QPixmap result; + if (tw > th) { + result = video->thumb->pixNoCache((tw * size) / th, size, true, true, false, size, size); + } else { + result = video->thumb->pixNoCache(size, 0, true, true, false, size, size); + } + video->forget(); + return result; +} + void OverviewInner::paintEvent(QPaintEvent *e) { if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; @@ -1081,7 +1076,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { } else if (_inSearch && _searchResults.isEmpty() && _searchFull && (!_migrated || _searchFullMigrated) && !_searchTimer.isActive()) { p.setFont(st::noContactsFont->f); p.setPen(st::noContactsColor->p); - p.drawText(QRect(_linksLeft, _addToY, _linksWidth, _addToY), lng_search_found_results(lt_count, 0), style::al_center); + p.drawText(QRect(_rowsLeft, _addToY, _rowWidth, _addToY), lng_search_found_results(lt_count, 0), style::al_center); return; } @@ -1094,7 +1089,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { SelectedItems::const_iterator selEnd = _selected.cend(); bool hasSel = !_selected.isEmpty(); - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { History::MediaOverview &overview(_history->overview[_type]), *migratedOverview = _migrated ? &_migrated->overview[_type] : 0; int32 migratedCount = migratedIndexSkip(); int32 count = migratedCount + overview.size(); @@ -1113,9 +1108,9 @@ void OverviewInner::paintEvent(QPaintEvent *e) { HistoryItem *item = App::histItemById(migratedindex ? _migrated->channelId() : _channel, (migratedindex ? *migratedOverview : overview)[bareindex]); HistoryMedia *m = item ? item->getMedia(true) : 0; - if (!m) continue; - switch (m->type()) { + QPoint pos(int32(i * w + st::overviewPhotoSkip), _addToY + row * (_vsize + st::overviewPhotoSkip) + st::overviewPhotoSkip); + if (m) switch (m->type()) { case MediaTypePhoto: { PhotoData *photo = static_cast(m)->photo(); bool quality = photo->full->loaded(); @@ -1139,7 +1134,6 @@ void OverviewInner::paintEvent(QPaintEvent *e) { it->vsize = _vsize; it->pix = genPix(photo, _vsize); } - QPoint pos(int32(i * w + st::overviewPhotoSkip), _addToY + row * (_vsize + st::overviewPhotoSkip) + st::overviewPhotoSkip); p.drawPixmap(pos, it->pix); if (!quality) { uint64 dt = itemAnimations().animate(item, ms); @@ -1161,23 +1155,45 @@ void OverviewInner::paintEvent(QPaintEvent *e) { p.fillRect(x + i * (st::overviewLoaderPoint.width() + st::overviewLoaderSkip), y, st::overviewLoaderPoint.width(), st::overviewLoaderPoint.height(), b); } } - - uint32 sel = 0; - if (index >= selfrom && index <= selto) { - sel = (_dragSelecting && item->id > 0) ? FullItemSel : 0; - } else if (hasSel) { - SelectedItems::const_iterator i = _selected.constFind(migratedindex ? -item->id : item->id); - if (i != selEnd) { - sel = i.value(); - } - } - if (sel == FullItemSel) { - p.fillRect(QRect(pos.x(), pos.y(), _vsize, _vsize), st::overviewPhotoSelectOverlay->b); - p.drawPixmap(QPoint(pos.x() + _vsize - st::overviewPhotoCheck.pxWidth(), pos.y() + _vsize - st::overviewPhotoCheck.pxHeight()), App::sprite(), st::overviewPhotoChecked); - } else if (_selMode/* || (selfrom < count && selfrom <= selto && 0 <= selto)*/) { - p.drawPixmap(QPoint(pos.x() + _vsize - st::overviewPhotoCheck.pxWidth(), pos.y() + _vsize - st::overviewPhotoCheck.pxHeight()), App::sprite(), st::overviewPhotoCheck); - } } break; + + case MediaTypeVideo: { + VideoData *video = static_cast(m)->video(); + CachedSizes::iterator it = _cached.find(video); + if (it == _cached.cend()) { + CachedSize size; + size.medium = 0; + size.vsize = _vsize; + size.pix = genPix(video, _vsize); + it = _cached.insert(video, size); + } else if (it->vsize != _vsize) { + it->vsize = _vsize; + it->pix = genPix(video, _vsize); + } + p.drawPixmap(pos, it->pix); + } break; + } + + uint32 sel = 0; + if (index >= selfrom && index <= selto) { + sel = (_dragSelecting && item->id > 0) ? FullItemSel : 0; + } else if (hasSel) { + SelectedItems::const_iterator i = _selected.constFind(migratedindex ? -item->id : item->id); + if (i != selEnd) { + sel = i.value(); + } + } + if (sel == FullItemSel) { + p.fillRect(QRect(pos.x(), pos.y(), _vsize, _vsize), st::overviewPhotoSelectOverlay); + p.drawSprite(QPoint(pos.x() + _vsize - st::overviewPhotoCheck.pxWidth(), pos.y() + _vsize - st::overviewPhotoCheck.pxHeight()), st::overviewPhotoChecked); + } else if (_selMode/* || (selfrom < count && selfrom <= selto && 0 <= selto)*/) { + p.drawSprite(QPoint(pos.x() + _vsize - st::overviewPhotoCheck.pxWidth(), pos.y() + _vsize - st::overviewPhotoCheck.pxHeight()), st::overviewPhotoCheck); + } + + if (m) { + p.translate(pos.x(), pos.y()); + m->drawOverview(p, _vsize, item, r.translated(-pos.x(), -pos.y()), sel == FullItemSel, ms); + p.translate(-pos.x(), -pos.y()); } } } @@ -1185,9 +1201,9 @@ void OverviewInner::paintEvent(QPaintEvent *e) { History::MediaOverview &overview(_history->overview[_type]), *migratedOverview = _migrated ? &_migrated->overview[_type] : 0; int32 migratedCount = migratedIndexSkip(); int32 count = migratedCount + overview.size(); - int32 from = floorclamp(r.y() - _addToY, _audioHeight, 0, count); - int32 to = ceilclamp(r.y() + r.height() - _addToY, _audioHeight, 0, count); - p.translate(_audioLeft, _addToY + from * _audioHeight); + int32 from = floorclamp(r.y() - _addToY, _rowHeight, 0, count); + int32 to = ceilclamp(r.y() + r.height() - _addToY, _rowHeight, 0, count); + p.translate(_rowsLeft, _addToY + from * _rowHeight); for (int32 index = from; index < to; ++index) { if (index >= count) break; @@ -1196,25 +1212,24 @@ void OverviewInner::paintEvent(QPaintEvent *e) { HistoryItem *item = App::histItemById(migratedindex ? _migrated->channelId() : _channel, (migratedindex ? *migratedOverview : overview)[bareindex]); HistoryMedia *m = item ? item->getMedia(true) : 0; - if (!m || m->type() != MediaTypeDocument) continue; - - uint32 sel = 0; - if (index >= selfrom && index <= selto) { - sel = (_dragSelecting && item->id > 0) ? FullItemSel : 0; - } else if (hasSel) { - SelectedItems::const_iterator i = _selected.constFind(migratedindex ? -item->id : item->id); - if (i != selEnd) { - sel = i.value(); + if (m) { + uint32 sel = 0; + if (index >= selfrom && index <= selto) { + sel = (_dragSelecting && item->id > 0) ? FullItemSel : 0; + } else if (hasSel) { + SelectedItems::const_iterator i = _selected.constFind(migratedindex ? -item->id : item->id); + if (i != selEnd) { + sel = i.value(); + } } - } - bool drawOver = _menu ? (App::contextItem() ? (App::contextItem() == item) : false) : (itemMsgId(_selectedMsgId) == item->id && itemChannel(_selectedMsgId) == item->channelId()); - static_cast(m)->drawInPlaylist(p, item, (sel == FullItemSel), drawOver, _audioWidth); - p.translate(0, _audioHeight); + m->drawOverview(p, _rowWidth, item, r.translated(-_rowsLeft, -_addToY - index * _rowHeight), (sel == FullItemSel), ms); + } + p.translate(0, _rowHeight); } } else if (_type == OverviewLinks) { - p.translate(_linksLeft, _addToY); - int32 y = 0, w = _linksWidth; + p.translate(_rowsLeft, _addToY); + int32 y = 0, w = _rowWidth; for (int32 i = 0, l = _items.size(); i < l; ++i) { if (i + 1 == l || _addToY + _items[i + 1].y > r.top()) { int32 left = st::dlgPhotoSize + st::dlgPhotoPadding, top = st::linksMargin + st::linksBorder, curY = _items[i].y; @@ -1275,24 +1290,24 @@ void OverviewInner::paintEvent(QPaintEvent *e) { p.setPen(st::black->p); p.setFont(st::webPageTitleFont->f); if (!lnk->title.isEmpty()) { - p.drawText(left, top + st::webPageTitleFont->ascent, (_linksWidth - left < lnk->titleWidth) ? st::webPageTitleFont->elided(lnk->title, _linksWidth - left) : lnk->title); + p.drawText(left, top + st::webPageTitleFont->ascent, (_rowWidth - left < lnk->titleWidth) ? st::webPageTitleFont->elided(lnk->title, _rowWidth - left) : lnk->title); top += st::webPageTitleFont->height; } p.setFont(st::msgFont->f); if (!lnk->text.isEmpty()) { - lnk->text.drawElided(p, left, top, _linksWidth - left, 3); - top += qMin(st::msgFont->height * 3, lnk->text.countHeight(_linksWidth - left)); + lnk->text.drawElided(p, left, top, _rowWidth - left, 3); + top += qMin(st::msgFont->height * 3, lnk->text.countHeight(_rowWidth - left)); } p.setPen(st::btnYesColor->p); for (int32 j = 0, c = lnk->urls.size(); j < c; ++j) { bool sel = (_mousedItem == _items[i].msgid && j + 1 == _lnkOverIndex); if (sel) p.setFont(st::msgFont->underline()->f); - p.drawText(left, top + st::msgFont->ascent, (_linksWidth - left < lnk->urls[j].width) ? st::msgFont->elided(lnk->urls[j].text, _linksWidth - left) : lnk->urls[j].text); + p.drawText(left, top + st::msgFont->ascent, (_rowWidth - left < lnk->urls[j].width) ? st::msgFont->elided(lnk->urls[j].text, _rowWidth - left) : lnk->urls[j].text); if (sel) p.setFont(st::msgFont->f); top += st::msgFont->height; } - p.fillRect(left, _items[i].y - curY, _linksWidth - left, st::linksBorder, st::linksBorderColor->b); + p.fillRect(left, _items[i].y - curY, _rowWidth - left, st::linksBorder, st::linksBorderColor->b); } else { QString str = langDayOfMonth(_items[i].date); @@ -1303,8 +1318,43 @@ void OverviewInner::paintEvent(QPaintEvent *e) { y = curY; } } - } else { - p.translate(0, st::msgMargin.top() + _addToY); + } else if (_type == OverviewDocuments) { + p.translate(_rowsLeft, _addToY); + int32 y = 0, w = _rowWidth; + for (int32 i = 0, l = _items.size(); i < l; ++i) { + if (i + 1 == l || _addToY + _items[i + 1].y > r.top()) { + int32 curY = _items[i].y; + if (_addToY + curY >= r.y() + r.height()) break; + + p.translate(0, curY - y); + if (_items[i].msgid) { // draw item + HistoryItem *item = App::histItemById(itemChannel(_items[i].msgid), itemMsgId(_items[i].msgid)); + HistoryMedia *m = item ? item->getMedia(true) : 0; + if (m) { + uint32 sel = 0; + if (i >= selfrom && i <= selto) { + sel = (_dragSelecting && itemMsgId(_items[i].msgid) > 0) ? FullItemSel : 0; + } else if (hasSel) { + SelectedItems::const_iterator j = _selected.constFind(_items[i].msgid); + if (j != selEnd) { + sel = j.value(); + } + } + + m->drawOverview(p, _rowWidth, item, r.translated(-_rowsLeft, -_addToY - curY), (sel == FullItemSel), ms); + } + } else { + QString str = langDayOfMonth(_items[i].date); + + p.setPen(st::linksDateColor->p); + p.setFont(st::msgFont->f); + p.drawText(0, st::linksDateMargin + st::msgFont->ascent, str); + } + y = curY; + } + } + } else if (_type == OverviewAudios) { + p.translate(_rowsLeft, _addToY); int32 y = 0, w = _width - st::msgMargin.left() - st::msgMargin.right(); for (int32 i = _items.size(); i > 0;) { --i; @@ -1315,18 +1365,11 @@ void OverviewInner::paintEvent(QPaintEvent *e) { p.translate(0, curY - y); if (_items[i].msgid) { // draw item HistoryItem *item = App::histItemById(itemChannel(_items[i].msgid), itemMsgId(_items[i].msgid)); - HistoryMedia *media = item ? item->getMedia(true) : 0; - if (media) { - bool out = item->out(), fromChannel = item->fromChannel(), outbg = out && !fromChannel; - int32 mw = media->maxWidth(), left = (fromChannel ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out ? st::msgMargin.right() : st::msgMargin.left())) + ((mw < w) ? (fromChannel ? 0 : (out ? w - mw : 0)) : 0); - if (item->displayFromPhoto()) { - p.drawPixmap(left, media->countHeight(item, w) - st::msgPhotoSize, item->from()->photo->pixRounded(st::msgPhotoSize)); - left += st::msgPhotoSkip; - } - + HistoryMedia *m = item ? item->getMedia(true) : 0; + if (m) { uint32 sel = 0; if (i >= selfrom && i <= selto) { - sel = (_dragSelecting && item->id > 0) ? FullItemSel : 0; + sel = (_dragSelecting && itemMsgId(_items[i].msgid) > 0) ? FullItemSel : 0; } else if (hasSel) { SelectedItems::const_iterator j = _selected.constFind(_items[i].msgid); if (j != selEnd) { @@ -1334,30 +1377,14 @@ void OverviewInner::paintEvent(QPaintEvent *e) { } } - p.save(); - p.translate(left, 0); - media->draw(p, item, r.translated(-left, -curY - (st::msgMargin.top() + _addToY)), (sel == FullItemSel), ms); - p.restore(); + m->drawOverview(p, _rowWidth, item, r.translated(-_rowsLeft, -_addToY - curY), (sel == FullItemSel), ms); } } else { QString str = langDayOfMonth(_items[i].date); - int32 left = st::msgServiceMargin.left(), width = _width - st::msgServiceMargin.left() - st::msgServiceMargin.left(), height = st::msgServiceFont->height + st::msgServicePadding.top() + st::msgServicePadding.bottom(); - if (width < 1) return; - - int32 strwidth = st::msgServiceFont->width(str) + st::msgServicePadding.left() + st::msgServicePadding.right(); - - QRect trect(QRect(left, st::msgServiceMargin.top(), width, height).marginsAdded(-st::msgServicePadding)); - left += (width - strwidth) / 2; - width = strwidth; - - QRect r(left, st::msgServiceMargin.top(), width, height); - App::roundRect(p, r, App::msgServiceBg(), ServiceCorners); - - p.setBrush(Qt::NoBrush); - p.setPen(st::msgServiceColor->p); - p.setFont(st::msgServiceFont->f); - p.drawText(r.x() + st::msgServicePadding.left(), r.y() + st::msgServicePadding.top() + st::msgServiceFont->ascent, str); + p.setPen(st::linksDateColor->p); + p.setFont(st::msgFont->f); + p.drawText(0, st::linksDateMargin + st::msgFont->ascent, str); } y = curY; } @@ -1384,7 +1411,7 @@ void OverviewInner::onUpdateSelected() { int32 index = -1; int32 newsel = 0; HistoryCursorState cursorState = HistoryDefaultCursorState; - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { float64 w = (float64(_width - st::overviewPhotoSkip) / _photosInRow); int32 inRow = int32((m.x() - (st::overviewPhotoSkip / 2)) / w), vsize = (_vsize + st::overviewPhotoSkip); int32 row = int32((m.y() - _addToY - (st::overviewPhotoSkip / 2)) / vsize); @@ -1412,8 +1439,8 @@ void OverviewInner::onUpdateSelected() { if (upon && m.x() >= inRow * w + st::overviewPhotoSkip && m.x() < inRow * w + st::overviewPhotoSkip + _vsize) { if (m.y() >= _addToY + row * vsize + st::overviewPhotoSkip && m.y() < _addToY + (row + 1) * vsize + st::overviewPhotoSkip) { HistoryMedia *media = item->getMedia(true); - if (media && media->type() == MediaTypePhoto) { - lnk = static_cast(media)->lnk(); + if (media) { + media->getStateOverview(lnk, m.x() - inRow * w - st::overviewPhotoSkip, m.y() - _addToY - row * vsize - st::overviewPhotoSkip, item, _vsize); } } } @@ -1422,7 +1449,7 @@ void OverviewInner::onUpdateSelected() { } else if (_type == OverviewAudioDocuments) { History::MediaOverview &overview(_history->overview[_type]), *migratedOverview = _migrated ? &_migrated->overview[_type] : 0; int32 migratedCount = migratedIndexSkip(); - int32 i = int32((m.y() - _addToY) / _audioHeight), count = migratedCount + overview.size(); + int32 i = int32((m.y() - _addToY) / _rowHeight), count = migratedCount + overview.size(); bool upon = true; if (m.y() < _addToY) { @@ -1439,10 +1466,10 @@ void OverviewInner::onUpdateSelected() { if (histItem) { item = histItem; index = i; - if (upon && m.x() >= _audioLeft && m.x() < _audioLeft + _audioWidth) { + if (upon && m.x() >= _rowsLeft && m.x() < _rowsLeft + _rowWidth) { HistoryMedia *media = item->getMedia(true); - if (media && media->type() == MediaTypeDocument) { - lnk = static_cast(media)->linkInPlaylist(); + if (media) { + media->getStateOverview(lnk, m.x() - _rowsLeft, m.y() - _addToY - i * _rowHeight, item, _rowWidth); newsel = (item->history() == _migrated) ? (-item->id) : item->id; } } @@ -1453,7 +1480,7 @@ void OverviewInner::onUpdateSelected() { _selectedMsgId = newsel; redrawItem(item); } - } else if (_type == OverviewLinks) { + } else if (_type == OverviewLinks || _type == OverviewDocuments) { int32 w = _width - st::msgMargin.left() - st::msgMargin.right(); for (int32 i = 0, l = _items.size(); i < l; ++i) { if ((i + 1 == l) || (_addToY + _items[i + 1].y > m.y())) { @@ -1478,30 +1505,37 @@ void OverviewInner::onUpdateSelected() { item = histItem; index = i; - int32 top = y + st::linksMargin + st::linksBorder, left = _linksLeft + st::dlgPhotoSize + st::dlgPhotoPadding, w = _linksWidth - st::dlgPhotoSize - st::dlgPhotoPadding; - if (!_items[i].link->title.isEmpty() && _items[i].link->text.isEmpty() && _items[i].link->urls.size() == 1) { - top += (st::dlgPhotoSize - st::webPageTitleFont->height - st::msgFont->height) / 2; - } - if (QRect(_linksLeft, y + st::linksMargin + st::linksBorder, st::dlgPhotoSize, st::dlgPhotoSize).contains(m)) { - lnkIndex = -1; - } else if (!_items[i].link->title.isEmpty() && QRect(left, top, qMin(w, _items[i].link->titleWidth), st::webPageTitleFont->height).contains(m)) { - lnkIndex = -1; - } else { - if (!_items[i].link->title.isEmpty()) top += st::webPageTitleFont->height; - if (!_items[i].link->text.isEmpty()) top += qMin(st::msgFont->height * 3, _items[i].link->text.countHeight(w)); - for (int32 j = 0, c = _items[i].link->urls.size(); j < c; ++j) { - if (QRect(left, top, qMin(w, _items[i].link->urls[j].width), st::msgFont->height).contains(m)) { - lnkIndex = j + 1; - break; + if (_type == OverviewLinks) { + int32 top = y + st::linksMargin + st::linksBorder, left = _rowsLeft + st::dlgPhotoSize + st::dlgPhotoPadding, w = _rowWidth - st::dlgPhotoSize - st::dlgPhotoPadding; + if (!_items[i].link->title.isEmpty() && _items[i].link->text.isEmpty() && _items[i].link->urls.size() == 1) { + top += (st::dlgPhotoSize - st::webPageTitleFont->height - st::msgFont->height) / 2; + } + if (QRect(_rowsLeft, y + st::linksMargin + st::linksBorder, st::dlgPhotoSize, st::dlgPhotoSize).contains(m)) { + lnkIndex = -1; + } else if (!_items[i].link->title.isEmpty() && QRect(left, top, qMin(w, _items[i].link->titleWidth), st::webPageTitleFont->height).contains(m)) { + lnkIndex = -1; + } else { + if (!_items[i].link->title.isEmpty()) top += st::webPageTitleFont->height; + if (!_items[i].link->text.isEmpty()) top += qMin(st::msgFont->height * 3, _items[i].link->text.countHeight(w)); + for (int32 j = 0, c = _items[i].link->urls.size(); j < c; ++j) { + if (QRect(left, top, qMin(w, _items[i].link->urls[j].width), st::msgFont->height).contains(m)) { + lnkIndex = j + 1; + break; + } + top += st::msgFont->height; } - top += st::msgFont->height; + } + } else if (_type == OverviewDocuments) { + HistoryMedia *media = item->getMedia(true); + if (media) { + media->getStateOverview(lnk, m.x() - _rowsLeft, m.y() - y, item, _rowWidth); } } } break; } } - } else { + } else if (_type == OverviewAudios) { int32 w = _width - st::msgMargin.left() - st::msgMargin.right(); for (int32 i = _items.size(); i > 0;) { --i; @@ -1528,17 +1562,7 @@ void OverviewInner::onUpdateSelected() { index = i; HistoryMedia *media = item->getMedia(true); if (media) { - bool out = item->out(), fromChannel = item->fromChannel(), outbg = out && !fromChannel; - int32 mw = media->maxWidth(), left = (fromChannel ? (st::msgMargin.left() + st::msgMargin.left()) / 2 : (out ? st::msgMargin.right() : st::msgMargin.left())) + ((mw < w) ? (fromChannel ? 0 : (out ? w - mw : 0)) : 0); - if (item->displayFromPhoto()) { - if (QRect(left, y + st::msgMargin.top() + media->countHeight(item, w) - st::msgPhotoSize, st::msgPhotoSize, st::msgPhotoSize).contains(m)) { - lnk = item->from()->lnk; - } - left += st::msgPhotoSkip; - } - TextLinkPtr link; - media->getState(link, cursorState, m.x() - left, m.y() - y - st::msgMargin.top(), item, w); - if (link) lnk = link; + media->getStateOverview(lnk, m.x() - _rowsLeft, m.y() - y, item, _rowWidth); } } break; @@ -1609,12 +1633,12 @@ void OverviewInner::onUpdateSelected() { _selected[_dragItem] = 0; updateDragSelection(0, -1, 0, -1, false); } else if (canSelectMany) { - bool selectingDown = ((_type == OverviewPhotos || _type == OverviewAudioDocuments || _type == OverviewLinks) ? (_mousedItemIndex > _dragItemIndex) : (_mousedItemIndex < _dragItemIndex)) || (_mousedItemIndex == _dragItemIndex && (_type == OverviewPhotos ? (_dragStartPos.x() < m.x()) : (_dragStartPos.y() < m.y()))); + bool selectingDown = ((_type == OverviewPhotos || _type == OverviewVideos || _type == OverviewAudioDocuments || _type == OverviewLinks) ? (_mousedItemIndex > _dragItemIndex) : (_mousedItemIndex < _dragItemIndex)) || (_mousedItemIndex == _dragItemIndex && ((_type == OverviewPhotos || _type == OverviewVideos) ? (_dragStartPos.x() < m.x()) : (_dragStartPos.y() < m.y()))); MsgId dragSelFrom = _dragItem, dragSelTo = _mousedItem; int32 dragSelFromIndex = _dragItemIndex, dragSelToIndex = _mousedItemIndex; if (!itemHasPoint(dragSelFrom, dragSelFromIndex, _dragStartPos.x(), _dragStartPos.y())) { // maybe exclude dragSelFrom if (selectingDown) { - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { if (_dragStartPos.x() >= _vsize || ((_mousedItem == dragSelFrom) && (m.x() < _dragStartPos.x() + QApplication::startDragDistance()))) { moveToNextItem(dragSelFrom, dragSelFromIndex, dragSelTo, 1); } @@ -1632,7 +1656,7 @@ void OverviewInner::onUpdateSelected() { } } } else { - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { if (_dragStartPos.x() < 0 || ((_mousedItem == dragSelFrom) && (m.x() >= _dragStartPos.x() - QApplication::startDragDistance()))) { moveToNextItem(dragSelFrom, dragSelFromIndex, dragSelTo, -1); } @@ -1653,7 +1677,7 @@ void OverviewInner::onUpdateSelected() { } if (_dragItem != _mousedItem) { // maybe exclude dragSelTo if (selectingDown) { - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { if (m.x() < 0) { moveToNextItem(dragSelTo, dragSelToIndex, dragSelFrom, -1); } @@ -1671,7 +1695,7 @@ void OverviewInner::onUpdateSelected() { } } } else { - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { if (m.x() >= _vsize) { moveToNextItem(dragSelTo, dragSelToIndex, dragSelFrom, 1); } @@ -1694,7 +1718,7 @@ void OverviewInner::onUpdateSelected() { MsgId dragFirstAffected = dragSelFrom; int32 dragFirstAffectedIndex = dragSelFromIndex; while (dragFirstAffectedIndex >= 0 && itemMsgId(dragFirstAffected) <= 0) { - moveToNextItem(dragFirstAffected, dragFirstAffectedIndex, dragSelTo, ((selectingDown && (_type == OverviewPhotos || _type == OverviewAudioDocuments)) || (!selectingDown && (_type != OverviewPhotos && _type != OverviewAudioDocuments))) ? -1 : 1); + moveToNextItem(dragFirstAffected, dragFirstAffectedIndex, dragSelTo, ((selectingDown && (_type == OverviewPhotos || _type == OverviewVideos || _type == OverviewAudioDocuments)) || (!selectingDown && (_type != OverviewPhotos && _type != OverviewVideos && _type != OverviewAudioDocuments))) ? -1 : 1); } if (dragFirstAffectedIndex >= 0) { SelectedItems::const_iterator i = _selected.constFind(dragFirstAffected); @@ -1809,12 +1833,21 @@ void OverviewInner::leaveEvent(QEvent *e) { void OverviewInner::resizeEvent(QResizeEvent *e) { _width = width(); - _audioWidth = qMin(_width - st::profilePadding.left() - st::profilePadding.right(), int(st::profileMaxWidth)); - _audioLeft = (_width - _audioWidth) / 2; - _linksWidth = qMin(_width - st::linksSearchMargin.left() - st::linksSearchMargin.right(), int(st::linksMaxWidth)); - _linksLeft = (_width - _linksWidth) / 2; - _search.setGeometry(_linksLeft, st::linksSearchMargin.top(), _linksWidth, _search.height()); - _cancelSearch.move(_linksLeft + _linksWidth - _cancelSearch.width(), _search.y()); + if (_type == OverviewLinks) { + _rowWidth = qMin(_width - st::linksSearchMargin.left() - st::linksSearchMargin.right(), int(st::linksMaxWidth)); + } else { + _rowWidth = qMin(_width - st::profilePadding.left() - st::profilePadding.right(), int(st::profileMaxWidth)); + if (_type == OverviewAudioDocuments) { + _rowHeight = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); + } else { + _rowHeight = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); + } + } + _rowsLeft = (_width - _rowWidth) / 2; + + _search.setGeometry(_rowsLeft, st::linksSearchMargin.top(), _rowWidth, _search.height()); + _cancelSearch.moveToLeft(_rowsLeft + _rowWidth - _cancelSearch.width(), _search.y()); + showAll(true); onUpdateSelected(); update(); @@ -1962,13 +1995,13 @@ int32 OverviewInner::resizeToWidth(int32 nwidth, int32 scrollTop, int32 minHeigh } else { _addToY = (_height < _minHeight) ? (_minHeight - _height) : 0; } - if (_type == OverviewPhotos && _resizeIndex < 0) { + if ((_type == OverviewPhotos || _type == OverviewVideos) && _resizeIndex < 0) { _resizeIndex = _photosInRow * ((scrollTop + minHeight) / int32(_vsize + st::overviewPhotoSkip)) + _photosInRow - 1; _resizeSkip = (scrollTop + minHeight) - ((scrollTop + minHeight) / int32(_vsize + st::overviewPhotoSkip)) * int32(_vsize + st::overviewPhotoSkip); } resize(nwidth, height() > _minHeight ? height() : _minHeight); showAll(); - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { int32 newRow = _resizeIndex / _photosInRow; return newRow * int32(_vsize + st::overviewPhotoSkip) + _resizeSkip - minHeight; } @@ -2268,7 +2301,7 @@ void OverviewInner::onTouchScrollTimer() { void OverviewInner::mediaOverviewUpdated(bool fromResize) { int32 oldHeight = _height; - if (_type == OverviewLinks) { + if (_type == OverviewLinks || _type == OverviewDocuments) { History::MediaOverview &o(_history->overview[_type]), *migratedOverview = _migrated ? &_migrated->overview[_type] : 0; int32 migrateCount = migratedIndexSkip(); int32 l = _inSearch ? _searchResults.size() : (migrateCount + o.size()), tocheck = qMin(l, _itemsToBeLoaded); @@ -2282,9 +2315,9 @@ void OverviewInner::mediaOverviewUpdated(bool fromResize) { if (allGood) { if (_items.size() > in && _items.at(in).msgid == msgid) { prevDate = _items.at(in).date; - if (fromResize) { + if (fromResize && _type == OverviewLinks) { _items[in].y = y; - y += _items[in].link->countHeight(_linksWidth); + y += _items[in].link->countHeight(_rowWidth); } else { y = (in + 1 < _items.size()) ? _items.at(in + 1).y : _height; } @@ -2292,15 +2325,15 @@ void OverviewInner::mediaOverviewUpdated(bool fromResize) { continue; } if (_items.size() > in + 1 && !_items.at(in).msgid && _items.at(in + 1).msgid == msgid) { // day item - if (fromResize) { + if (fromResize && _type == OverviewLinks) { _items[in].y = y; y += st::msgFont->height + st::linksDateMargin * 2 + st::linksBorder; } ++in; prevDate = _items.at(in).date; - if (fromResize) { + if (fromResize && _type == OverviewLinks) { _items[in].y = y; - y += _items[in].link->countHeight(_linksWidth); + y += _items[in].link->countHeight(_rowWidth); } else { y = (in + 1 < _items.size()) ? _items.at(in + 1).y : _height; } @@ -2326,14 +2359,25 @@ void OverviewInner::mediaOverviewUpdated(bool fromResize) { prevDate = date; } + HistoryMedia *media = item ? item->getMedia(true) : 0; + if (media) media->initDimensions(item); + if (_items.size() > in) { _items[in] = CachedItem(msgid, item->date.date(), y); - _items[in].link = cachedLink(item); - y += _items[in].link->countHeight(_linksWidth); + if (_type == OverviewLinks) { + _items[in].link = cachedLink(item); + y += _items[in].link->countHeight(_rowWidth); + } else { + y += _rowHeight; + } } else { _items.push_back(CachedItem(msgid, item->date.date(), y)); - _items.back().link = cachedLink(item); - y += _items.back().link->countHeight(_linksWidth); + if (_type == OverviewLinks) { + _items.back().link = cachedLink(item); + y += _items.back().link->countHeight(_rowWidth); + } else { + y += _rowHeight; + } } ++in; } @@ -2348,7 +2392,7 @@ void OverviewInner::mediaOverviewUpdated(bool fromResize) { } dragActionUpdate(QCursor::pos()); update(); - } else if (_type != OverviewPhotos && _type != OverviewAudioDocuments) { + } else if (_type == OverviewAudios) { History::MediaOverview &o(_history->overview[_type]), *migratedOverview = _migrated ? &_migrated->overview[_type] : 0; int32 migrateCount = migratedIndexSkip(); int32 l = migrateCount + o.size(); @@ -2363,36 +2407,14 @@ void OverviewInner::mediaOverviewUpdated(bool fromResize) { if (allGood) { if (_items.size() > in && _items.at(in).msgid == msgid) { prevDate = _items.at(in).date; - if (fromResize) { - HistoryItem *item = App::histItemById(_channel, msgid); - HistoryMedia *media = item ? item->getMedia(true) : 0; - if (media) { - y += media->countHeight(item, w) + st::msgMargin.top() + st::msgMargin.bottom(); // item height - } - _items[in].y = y; - } else { - y = _items.at(in).y; - } + y = _items.at(in).y; ++in; continue; } if (_items.size() > in + 1 && !_items.at(in).msgid && _items.at(in + 1).msgid == msgid) { // day item - if (fromResize) { - y += st::msgServiceFont->height + st::msgServicePadding.top() + st::msgServicePadding.bottom() + st::msgServiceMargin.top() + st::msgServiceMargin.bottom(); // day item height - _items[in].y = y; - } ++in; prevDate = _items.at(in).date; - if (fromResize) { - HistoryItem *item = App::histItemById(_channel, msgid); - HistoryMedia *media = item ? item->getMedia(true) : 0; - if (media) { - y += media->countHeight(item, w) + st::msgMargin.top() + st::msgMargin.bottom(); // item height - } - _items[in].y = y; - } else { - y = _items.at(in).y; - } + y = _items.at(in).y; ++in; continue; } @@ -2405,7 +2427,7 @@ void OverviewInner::mediaOverviewUpdated(bool fromResize) { QDate date = item->date.date(); if (in > 0) { if (date != prevDate) { // add day item - y += st::msgServiceFont->height + st::msgServicePadding.top() + st::msgServicePadding.bottom() + st::msgServiceMargin.top() + st::msgServiceMargin.bottom(); // day item height + y += st::msgFont->height + st::linksDateMargin * 2 + st::linksBorder; // day item height if (_items.size() > in) { _items[in].msgid = 0; _items[in].date = prevDate; @@ -2420,7 +2442,7 @@ void OverviewInner::mediaOverviewUpdated(bool fromResize) { prevDate = date; } media->initDimensions(item); - y += media->countHeight(item, w) + st::msgMargin.top() + st::msgMargin.bottom(); // item height + y += _rowHeight; if (_items.size() > in) { _items[in].msgid = msgid; _items[in].date = date; @@ -2431,7 +2453,7 @@ void OverviewInner::mediaOverviewUpdated(bool fromResize) { ++in; } if (!_items.isEmpty()) { - y += st::msgServiceFont->height + st::msgServicePadding.top() + st::msgServicePadding.bottom() + st::msgServiceMargin.top() + st::msgServiceMargin.bottom(); // day item height + y += st::msgFont->height + st::linksDateMargin * 2 + st::linksBorder; // day item height if (_items.size() > in) { _items[in].msgid = 0; _items[in].date = prevDate; @@ -2530,44 +2552,6 @@ void OverviewInner::itemRemoved(HistoryItem *item) { update(); } -void OverviewInner::itemResized(HistoryItem *item, bool scrollToIt) { - if (_type != OverviewPhotos && _type != OverviewAudioDocuments && _type != OverviewLinks) { - HistoryMedia *media = item ? item->getMedia(true) : 0; - if (!media) return; - - MsgId msgId = (item->history() == _migrated) ? -item->id : item->id; - for (int32 i = 0, l = _items.size(); i < l; ++i) { - if (_items[i].msgid == msgId) { - int32 from = 0; - if (i > 0) from = _items[i - 1].y; - - int32 oldh = _items[i].y - from; - int32 w = _width - st::msgMargin.left() - st::msgMargin.right(); - int32 newh = media->countHeight(item, w) + st::msgMargin.top() + st::msgMargin.bottom(); // item height - if (oldh != newh) { - newh -= oldh; - for (int32 j = i; j < l; ++j) { - _items[j].y += newh; - } - _height = _items[l - 1].y; - _addToY = (_height < _minHeight) ? (_minHeight - _height) : 0; - resize(width(), _minHeight > _height ? _minHeight : _height); - if (scrollToIt) { - if (_addToY + _height - from > _scroll->scrollTop() + _scroll->height()) { - _scroll->scrollToY(_addToY + _height - from - _scroll->height()); - } - if (_addToY + _height - _items[i].y < _scroll->scrollTop()) { - _scroll->scrollToY(_addToY + _height - _items[i].y); - } - } - update(); - } - break; - } - } - } -} - void OverviewInner::redrawItem(const HistoryItem *msg) { if (!msg) return; @@ -2577,7 +2561,7 @@ void OverviewInner::redrawItem(const HistoryItem *msg) { int32 migrateindex = migratedIndexSkip(); MsgId msgid = msg->id; if (history->overviewHasMsgId(_type, msgid) && (history == _history || migrateindex > 0)) { - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { int32 index = history->overview[_type].indexOf(msgid); if (index >= 0) { if (history == _history) index += migrateindex; @@ -2590,13 +2574,13 @@ void OverviewInner::redrawItem(const HistoryItem *msg) { int32 index = history->overview[_type].indexOf(msgid); if (index >= 0) { if (history == _history) index += migrateindex; - update(_audioLeft, _addToY + int32(index * _audioHeight), _audioWidth, _audioHeight); + update(_rowsLeft, _addToY + int32(index * _rowHeight), _rowWidth, _rowHeight); } } else if (_type == OverviewLinks) { if (history == _migrated) msgid = -msgid; for (int32 i = 0, l = _items.size(); i != l; ++i) { if (_items[i].msgid == msgid) { - update(_linksLeft, _addToY + _items[i].y, _linksWidth, itemHeight(msgid, i)); + update(_rowsLeft, _addToY + _items[i].y, _rowWidth, itemHeight(msgid, i)); break; } } @@ -2614,7 +2598,7 @@ void OverviewInner::redrawItem(const HistoryItem *msg) { void OverviewInner::showAll(bool recountHeights) { int32 newHeight = height(); - if (_type == OverviewPhotos) { + if (_type == OverviewPhotos || _type == OverviewVideos) { _photosInRow = int32(width() - st::overviewPhotoSkip) / int32(st::overviewPhotoMinSize + st::overviewPhotoSkip); _vsize = (int32(width() - st::overviewPhotoSkip) / _photosInRow) - st::overviewPhotoSkip; int32 migratedCount = migratedIndexSkip(), count = migratedCount + _history->overview[_type].size(); @@ -2632,7 +2616,7 @@ void OverviewInner::showAll(bool recountHeights) { _addToY = (_height < _minHeight) ? (_minHeight - _height) : 0; } else if (_type == OverviewAudioDocuments) { int32 migratedCount = migratedIndexSkip(), count = migratedCount + _history->overview[_type].size(); - newHeight = _height = count * _audioHeight + 2 * st::playlistPadding; + newHeight = _height = count * _rowHeight + 2 * st::playlistPadding; _addToY = st::playlistPadding; } else if (_type == OverviewLinks) { if (recountHeights) { // recount heights because of texts @@ -2641,9 +2625,6 @@ void OverviewInner::showAll(bool recountHeights) { newHeight = _height; _addToY = st::linksSearchMargin.top() + _search.height() + st::linksSearchMargin.bottom(); } else { - if (recountHeights && _type == OverviewVideos) { // recount heights because of captions - mediaOverviewUpdated(true); - } newHeight = _height; _addToY = (_height < _minHeight) ? (_minHeight - _height) : 0; } @@ -2754,40 +2735,7 @@ void OverviewWidget::paintEvent(QPaintEvent *e) { return; } - QRect r(e->rect()); - if (type() == OverviewPhotos || type() == OverviewAudioDocuments || type() == OverviewLinks) { - p.fillRect(r, st::white->b); - } else { - bool hasTopBar = !App::main()->topBar()->isHidden(), hasPlayer = !App::main()->player()->isHidden(); - QRect fill(0, 0, width(), App::main()->height()); - int fromy = (hasTopBar ? (-st::topBarHeight) : 0) + (hasPlayer ? (-st::playerHeight) : 0), x = 0, y = 0; - QPixmap cached = App::main()->cachedBackground(fill, x, y); - if (cached.isNull()) { - const QPixmap &pix(*cChatBackground()); - if (cTileBackground()) { - int left = r.left(), top = r.top(), right = r.left() + r.width(), bottom = r.top() + r.height(); - float64 w = pix.width() / cRetinaFactor(), h = pix.height() / cRetinaFactor(); - int sx = qFloor(left / w), sy = qFloor((top - fromy) / h), cx = qCeil(right / w), cy = qCeil((bottom - fromy) / h); - for (int i = sx; i < cx; ++i) { - for (int j = sy; j < cy; ++j) { - p.drawPixmap(QPointF(i * w, fromy + j * h), pix); - } - } - } else { - bool smooth = p.renderHints().testFlag(QPainter::SmoothPixmapTransform); - p.setRenderHint(QPainter::SmoothPixmapTransform); - - QRect to, from; - App::main()->backgroundParams(fill, to, from); - to.moveTop(to.top() + fromy); - p.drawPixmap(to, pix, from); - - if (!smooth) p.setRenderHint(QPainter::SmoothPixmapTransform, false); - } - } else { - p.drawPixmap(x, fromy + y, cached); - } - } + p.fillRect(e->rect(), st::white); } void OverviewWidget::contextMenuEvent(QContextMenuEvent *e) { @@ -3007,12 +2955,6 @@ void OverviewWidget::itemRemoved(HistoryItem *row) { _inner.itemRemoved(row); } -void OverviewWidget::itemResized(HistoryItem *row, bool scrollToIt) { - if (!row || row->history()->peer == peer() || row->history()->peer == migratePeer()) { - _inner.itemResized(row, scrollToIt); - } -} - void OverviewWidget::fillSelectedItems(SelectedItemSet &sel, bool forDelete) { _inner.fillSelectedItems(sel, forDelete); } diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index 8953db130..ec778004a 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -71,8 +71,7 @@ public: void changingMsgId(HistoryItem *row, MsgId newId); void redrawItem(const HistoryItem *msg); void itemRemoved(HistoryItem *item); - void itemResized(HistoryItem *item, bool scrollToIt); - + void getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const; void clearSelectedItems(bool onlyTextSelection = false); void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true); @@ -133,6 +132,7 @@ private: void addSelectionRange(int32 selFrom, int32 selTo, History *history); QPixmap genPix(PhotoData *photo, int32 size); + QPixmap genPix(VideoData *video, int32 size); void showAll(bool recountHeights = false); OverviewWidget *_overview; @@ -144,6 +144,9 @@ private: History *_migrated, *_history; ChannelId _channel; + // for audio files, files, voice messages and links + int32 _rowsLeft, _rowWidth, _rowHeight; + // photos int32 _photosInRow, _photosToAdd, _vsize; struct CachedSize { @@ -151,15 +154,11 @@ private: bool medium; QPixmap pix; }; - typedef QMap CachedSizes; + typedef QMap CachedSizes; CachedSizes _cached; bool _selMode; - // audio documents - int32 _audioLeft, _audioWidth, _audioHeight; - // shared links - int32 _linksLeft, _linksWidth; struct Link { Link() : width(0) { } @@ -214,9 +213,9 @@ private: // other struct CachedItem { - CachedItem() : msgid(0), y(0) { + CachedItem() : msgid(0), y(0), link(0) { } - CachedItem(MsgId msgid, const QDate &date, int32 y) : msgid(msgid), date(date), y(y) { + CachedItem(MsgId msgid, const QDate &date, int32 y) : msgid(msgid), date(date), y(y), link(0) { } MsgId msgid; QDate date; @@ -314,8 +313,7 @@ public: void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type); void changingMsgId(HistoryItem *row, MsgId newId); void itemRemoved(HistoryItem *item); - void itemResized(HistoryItem *row, bool scrollToIt); - + QPoint clampMousePosition(QPoint point); void checkSelectingScroll(QPoint point); From 7d8c45ec923dbc119f901982322370909b1eae09 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 20 Dec 2015 10:41:05 +0300 Subject: [PATCH 036/145] youtube icon updated --- Telegram/Resources/style.txt | 9 ++++----- Telegram/SourceFiles/art/sprite.png | Bin 179013 -> 180183 bytes Telegram/SourceFiles/art/sprite_200x.png | Bin 240472 -> 243094 bytes 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 4dddb8965..5e54f0201 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -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; @@ -1091,7 +1091,7 @@ msgDateImgBgSelected: #1c4a7187; msgDateImgPadding: point(8px, 2px); msgDateImgCheckSpace: 4px; -msgDogImg: sprite(216px, 93px, 126px, 126px); +msgDogImg: sprite(216px, 92px, 126px, 126px); historyPadding: 10px; collapseButton: flatButton(btnDefFlat) { @@ -2196,8 +2196,7 @@ usernameTextStyle: textStyle(defaultTextStyle) { } usernameDefaultFg: #777; -youtubeIcon: sprite(336px, 221px, 60px, 60px); -vimeoIcon: sprite(336px, 283px, 60px, 60px); +youtubeIcon: sprite(116px, 338px, 90px, 62px); videoIcon: sprite(0px, 340px, 60px, 60px); locationSize: size(320px, 240px); diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index fd9350ad111973ba93e3ef68405a8d15af590066..038a85cbebf215b9d0f0ddb27a29fa640cfbb2aa 100644 GIT binary patch delta 56140 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 Id&95&54s0~eEn4N=&t%IGgy?8?dx)J*S)18@o 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

2u#di{WaS+Z= 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 delta 76071 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 amuGmhj_Y7$a!U*Y5O})!xvXsong() ? formatDurationAndSizeText(_data->song()->duration, _data->size) : formatSizeText(_data->size); + _link = lang(lng_media_download).toUpper(); } else if (_statusSize == FileStatusSizeLoaded) { _statusText = _data->song() ? formatDurationText(_data->song()->duration) : formatSizeText(_data->size); + _link = lang(lng_media_open_with).toUpper(); } else if (_statusSize == FileStatusSizeFailed) { _statusText = lang(lng_attach_failed); + _link = lang(lng_media_download).toUpper(); } else if (_statusSize >= 0) { _statusText = formatDownloadText(_statusSize, _data->size); + _link = lang(lng_media_cancel).toUpper(); } else { _statusText = formatPlayedText(-_statusSize - 1, realDuration); + _link = lang(lng_media_open_with).toUpper(); } + _linkw = st::semiboldFont->width(_link); } bool HistoryDocument::updateStatusText(const HistoryItem *parent) const { @@ -3974,12 +3981,8 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - QString link; if (already || hasdata) { - link = lang(lng_media_open_with).toUpper(); } else { - link = lang(_data->loader ? lng_media_cancel : lng_media_download).toUpper(); - p.setRenderHint(QPainter::HighQualityAntialiasing); QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); @@ -3998,9 +4001,13 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, p.drawSpriteCenter(inner, icon); } - p.setFont(st::semiboldFont); - p.setPen(outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg)); - p.drawTextLeft(nameleft, linktop, width, link); + if (_data->status != FileUploadFailed) { + const TextLinkPtr &lnk((_data->loader || _data->status == FileUploading) ? _cancell : _savel); + bool over = (textlnkOver() == lnk) && (!textlnkDown() || textlnkDown() == 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 { nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); nametop = st::msgFileNameTop; @@ -4053,12 +4060,12 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, 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(); + int32 height = st::msgPadding.top() + st::mediaThumbSize + st::msgPadding.bottom(); style::color bg(selected ? st::msgInSelectBg : (over ? st::playlistHoverBg : st::msgInBg)); p.fillRect(0, 0, width, height, bg->b); - QRect img = st::mediaMusicInImg; + style::sprite img = st::mediaMusicInImg; bool showPause = updateStatusText(parent); if (_data->song()) { SongMsgId playing; @@ -4081,26 +4088,26 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool } } - p.drawPixmap(QPoint(st::mediaPadding.left(), st::mediaPadding.top()), App::sprite(), img); + p.drawSpriteLeft(QPoint(st::msgPadding.left(), st::msgPadding.top()), width, img); if (selected) { - App::roundRect(p, st::mediaPadding.left(), st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + App::roundRect(p, rtlrect(st::msgPadding.left(), st::msgPadding.top(), st::mediaThumbSize, st::mediaThumbSize, width), textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); - int32 twidth = width - tleft - st::mediaPadding.right(); + int32 tleft = st::msgPadding.left() + st::mediaThumbSize + st::msgPadding.right(); + int32 twidth = width - tleft - st::msgPadding.right(); int32 secondwidth = width - tleft - st::msgPadding.right() - parent->skipBlockWidth(); p.setFont(st::normalFont->f); p.setPen(st::black->c); if (twidth < _namew) { - p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(_name, twidth)); + p.drawTextLeft(tleft, st::msgPadding.top() + st::mediaNameTop, width, st::normalFont->elided(_name, twidth)); } else { - p.drawText(tleft, st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, _name); + p.drawTextLeft(tleft, st::msgPadding.top() + st::mediaNameTop, width, _name, _namew); } style::color status(selected ? st::mediaInSelectColor : st::mediaInColor); p.setPen(status->p); - p.drawText(tleft, st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->descent, _statusText); + p.drawTextLeft(tleft, st::msgPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height, width, _statusText); } TextLinkPtr HistoryDocument::linkInPlaylist() { @@ -4126,18 +4133,7 @@ void HistoryDocument::updateFrom(const MTPMessageMedia &media) { int32 HistoryDocument::resize(int32 width, const HistoryItem *parent) { w = qMin(width, _maxw); - if (parent == animated.msg) { - 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; - } - } else { - _height = _minh; - } + _height = _minh; return _height; } @@ -4154,11 +4150,6 @@ bool HistoryDocument::hasPoint(int32 x, int32 y, const HistoryItem *parent, int3 if (width >= _maxw) { width = _maxw; } - 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); } @@ -4167,11 +4158,6 @@ int32 HistoryDocument::countHeight(const HistoryItem *parent, int32 width) const if (width >= _maxw) { width = _maxw; } - if (parent == animated.msg) { - int32 h = (width == w) ? _height : (width * animated.h / animated.w); - if (h < 1) h = 1; - return h; - } return _height; } @@ -4179,27 +4165,46 @@ void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int3 if (width < 0) width = w; if (width < 1) return; - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); + 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; - } - int skipy = 0, replyFrom = 0, fwdFrom = 0; + bool showPause = updateStatusText(parent); - if (x >= 0 && y >= skipy && x < width && y < _height && !_data->loader && _data->access) { - lnk = _openl; + int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; + bool wthumb = withThumb(); + if (wthumb) { + nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); + linktop = st::msgFileThumbLinkTop; - bool inDate = parent->pointInTime(width, _height, x, y, InfoDisplayDefault); - if (inDate) { - state = HistoryInDateCursorState; + QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width)); + + if (already || hasdata) { + } else { + if (rthumb.contains(x, y)) { + lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _thumbsavel; + return; + } } + if (_data->status != FileUploadFailed) { + if (rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, width).contains(x, y)) { + lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; + return; + } + } + } else { + QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width)); + if (_data->loader || _data->status == FileUploading || (!already && !hasdata) && inner.contains(x, y)) { + lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _thumbsavel; + return; + } + } + if (x >= 0 && y >= 0 && x < width && y < _height && !_data->loader && _data->access) { + lnk = _openl; return; } } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 9c826679f..e42153c49 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1417,7 +1417,7 @@ public: private: DocumentData *_data; - TextLinkPtr _openl, _savel, _cancell; + TextLinkPtr _openl, _savel, _thumbsavel, _cancell; int32 _namew; QString _name; @@ -1428,8 +1428,8 @@ private: // 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; + mutable int32 _statusSize, _linkw; + mutable QString _statusText, _link; void setStatusSize(int32 newSize, qint64 realDuration = 0) const; bool updateStatusText(const HistoryItem *parent) const; // returns showPause diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 3368d9f3d..989fc3d18 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -151,7 +151,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerD , _selMode(false) , _audioLeft(st::msgMargin.left()) , _audioWidth(st::msgMinWidth) -, _audioHeight(st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom()) +, _audioHeight(st::msgPadding.top() + st::mediaThumbSize + st::msgPadding.bottom()) , _linksLeft(st::linksSearchMargin.left()) , _linksWidth(st::msgMinWidth) , _search(this, st::dlgFilter, lang(lng_dlg_filter)) @@ -2897,7 +2897,7 @@ int32 OverviewWidget::countBestScroll() const { if (playing) { int32 top = _inner.itemTop(playing.msgId); if (top >= 0) { - return snap(top - int(_scroll.height() - (st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom())) / 2, 0, _scroll.scrollTopMax()); + return snap(top - int(_scroll.height() - (st::msgPadding.top() + st::mediaThumbSize + st::msgPadding.bottom())) / 2, 0, _scroll.scrollTopMax()); } } } else if (type() == OverviewLinks) { @@ -3063,7 +3063,7 @@ void OverviewWidget::onPlayerSongChanged(const FullMsgId &msgId) { if (type() == OverviewAudioDocuments) { // int32 top = _inner.itemTop(msgId); // if (top > 0) { -// _scroll.scrollToY(snap(top - int(_scroll.height() - (st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom())) / 2, 0, _scroll.scrollTopMax())); +// _scroll.scrollToY(snap(top - int(_scroll.height() - (st::msgPadding.top() + st::mediaThumbSize + st::msgPadding.bottom())) / 2, 0, _scroll.scrollTopMax())); // } } } From 603fb63c91f185f1b77e9c3ce4497280e473d5b0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Dec 2015 21:11:38 +0300 Subject: [PATCH 016/145] document new design done with radial progress, ? char added to monospace blocks edges, redraw history item through Notify:: --- Telegram/Resources/style.txt | 54 +-- Telegram/SourceFiles/app.cpp | 8 +- Telegram/SourceFiles/boxes/photosendbox.cpp | 6 +- Telegram/SourceFiles/facades.cpp | 10 +- Telegram/SourceFiles/facades.h | 1 + Telegram/SourceFiles/gui/animation.cpp | 4 +- Telegram/SourceFiles/gui/animation.h | 51 +++ Telegram/SourceFiles/gui/style_core.h | 8 + Telegram/SourceFiles/gui/text.cpp | 17 +- Telegram/SourceFiles/gui/text.h | 2 + Telegram/SourceFiles/history.cpp | 433 +++++++++++++------- Telegram/SourceFiles/history.h | 104 ++++- Telegram/SourceFiles/historywidget.cpp | 111 ++--- Telegram/SourceFiles/historywidget.h | 12 +- Telegram/SourceFiles/mainwidget.cpp | 62 +-- Telegram/SourceFiles/mainwidget.h | 11 +- Telegram/SourceFiles/overviewwidget.cpp | 79 ++-- Telegram/SourceFiles/overviewwidget.h | 7 +- Telegram/SourceFiles/settings.cpp | 2 +- Telegram/SourceFiles/structs.h | 4 + 20 files changed, 649 insertions(+), 337 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index aa0a0fba3..b364431e9 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -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,7 +1086,8 @@ msgDateDelta: point(2px, 5px); msgDateImgDelta: 4px; msgDateImgColor: #fff; msgDateImgBg: #00000054; -msgDateImgSelectBg: #1c4a7187; +msgDateImgBgOver: #00000074; +msgDateImgBgSelected: #1c4a7187; msgDateImgPadding: point(8px, 2px); msgDateImgCheckSpace: 4px; @@ -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) { @@ -1177,14 +1178,14 @@ 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; +mediaInFg: msgInDateFg; +mediaInFgSelected: msgInDateFgSelected; +mediaOutFg: msgOutDateFg; +mediaOutFgSelected: msgOutDateFgSelected; +mediaInUnreadFg: #999; +mediaInUnreadFgSelected: #7b95aa; +mediaOutUnreadFg: #6aad60; +mediaOutUnreadFgSelected: #5aa382; mediaUnreadSize: 4px; mediaUnreadSkip: 5px; @@ -1235,6 +1236,9 @@ msgFileOutPlaySelected: sprite(180px, 146px, 20px, 18px); msgFileInPlay: sprite(160px, 164px, 20px, 18px); msgFileInPlaySelected: sprite(180px, 164px, 20px, 18px); +msgFileOverDuration: 200; +msgFileRadialLine: 4px; + sendPadding: 9px; btnSend: flatButton(btnDefFlat) { color: btnYesColor; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 4b326a577..7d6663e9f 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2129,9 +2129,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); @@ -2147,9 +2147,9 @@ namespace App { prepareCorners(DocBlueCorners, st::msgRadius, st::mvDocBlueColor); 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(MessageOutSelectedCorners, st::msgRadius, st::msgOutBgSelected, &st::msgOutShadowSelected); } diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index e64337b41..c38770a69 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -210,15 +210,15 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) { p.drawPixmap(x + st::mediaPadding.left(), y + st::mediaPadding.top(), userDefPhoto(1)->pix(st::mediaThumbSize)); } - p.setFont(st::normalFont->f); - p.setPen(st::black->c); + p.setFont(st::normalFont); + p.setPen(st::black); if (twidth < _namew) { p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(_name, twidth)); } else { p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, _name); } - p.setPen(st::mediaOutColor->p); + p.setPen(st::mediaOutFg); p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->descent, _size); } } diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index afd723eea..6cf973195 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -96,15 +96,19 @@ namespace Ui { namespace Notify { void userIsBotChanged(UserData *user) { - if (MainWidget *m = App::main()) m->notifyUserIsBotChanged(user); + if (MainWidget *m = App::main()) m->notify_userIsBotChanged(user); } void botCommandsChanged(UserData *user) { - if (MainWidget *m = App::main()) m->notifyBotCommandsChanged(user); + if (MainWidget *m = App::main()) m->notify_botCommandsChanged(user); } void migrateUpdated(PeerData *peer) { - if (MainWidget *m = App::main()) m->notifyMigrateUpdated(peer); + if (MainWidget *m = App::main()) m->notify_migrateUpdated(peer); + } + + void redrawHistoryItem(const HistoryItem *item) { + if (MainWidget *m = App::main()) m->notify_redrawHistoryItem(item); } } diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index cc6cdb606..790c990de 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -53,5 +53,6 @@ namespace Notify { void userIsBotChanged(UserData *user); void botCommandsChanged(UserData *user); void migrateUpdated(PeerData *peer); + void redrawHistoryItem(const HistoryItem *item); }; diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 333db29bb..89c28b3a2 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -153,8 +153,8 @@ void AnimatedGif::step_frame(float64 ms, bool timer) { if (frame != f) { frame = f; if (timer) { - if (msg && App::main()) { - App::main()->msgUpdated(msg); + if (msg) { + Notify::redrawHistoryItem(msg); } else { emit updated(); } diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index abb9b2ce4..e20191387 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -284,6 +284,57 @@ AnimationCallbacks *animation(Type *obj, typename AnimationCallbacksAbsolute(obj, method); } +template +class AnimationCallbacksRelativeWithParam : public AnimationCallbacks { +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 +AnimationCallbacks *animation(Param param, Type *obj, typename AnimationCallbacksRelativeWithParam::Method method) { + return new AnimationCallbacksRelativeWithParam(param, obj, method); +} + +template +class AnimationCallbacksAbsoluteWithParam : public AnimationCallbacks { +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 +AnimationCallbacks *animation(Param param, Type *obj, typename AnimationCallbacksAbsoluteWithParam::Method method) { + return new AnimationCallbacksAbsoluteWithParam(param, obj, method); +} + class AnimationManager : public QObject { Q_OBJECT diff --git a/Telegram/SourceFiles/gui/style_core.h b/Telegram/SourceFiles/gui/style_core.h index 8939118bc..8642a701a 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/text.cpp b/Telegram/SourceFiles/gui/text.cpp index cddee2412..aec996a4d 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -38,8 +38,8 @@ namespace { 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 _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(); @@ -1385,6 +1389,7 @@ public: return true; } + int skipIndex = -1; QVarLengthArray visualOrder(nItems); QVarLengthArray levels(nItems); for (int i = 0; i < nItems; ++i) { @@ -1396,6 +1401,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 +1412,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]; diff --git a/Telegram/SourceFiles/gui/text.h b/Telegram/SourceFiles/gui/text.h index c1ff5a911..59cb4ad9a 100644 --- a/Telegram/SourceFiles/gui/text.h +++ b/Telegram/SourceFiles/gui/text.h @@ -680,6 +680,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/history.cpp b/Telegram/SourceFiles/history.cpp index c7efa4626..d32537a5d 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -2973,7 +2973,7 @@ void ItemAnimations::step_animate(float64 ms, bool timer) { for (Animations::iterator i = _animations.begin(); i != _animations.end();) { const HistoryItem *item = i.key(); if (item->animating()) { - if (timer) App::main()->msgUpdated(item); + if (timer) Notify::redrawHistoryItem(item); ++i; } else { i = _animations.erase(i); @@ -3100,6 +3100,72 @@ HistoryItem *regItem(HistoryItem *item, bool returnExisting) { return item; } +RadialAnimation::RadialAnimation(int32 thickness, AnimationCallbacks *callbacks) : _thickness(thickness) +, _firstStart(0) +, _lastStart(0) +, _lastTime(0) +, _opacity(0) +, a_arcEnd(0, 0) +, a_arcStart(0, 1) +, _animation(callbacks) { + +} + +void RadialAnimation::start(float64 prg) { + _firstStart = _lastStart = _lastTime = getms(); + a_arcEnd = anim::fvalue(prg, qMax(prg, 0.0001)); + _animation.start(); +} + +void RadialAnimation::update(float64 prg, bool finished, uint64 ms) { + if (prg < 0.0001) prg = 0.0001; + + if (prg != a_arcEnd.to()) { + a_arcEnd.start(prg); + _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::fvalue(0, 0); + _animation.stop(); +} + +void RadialAnimation::draw(Painter &p, const QRect &inner, const style::color &color) { + p.setRenderHint(QPainter::HighQualityAntialiasing); + + float64 o = p.opacity(); + p.setOpacity(o * _opacity); + + QPen pen(color->p), was(p.pen()); + pen.setWidth(_thickness); + p.setPen(pen); + + int32 len = 16 + a_arcEnd.current() * 5744; + p.drawArc(inner, 1440 - a_arcStart.current() * 5760 - len, len); + + p.setPen(was); + p.setOpacity(o); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); +} + HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent) : HistoryMedia() , _data(App::feedPhoto(photo)) , _openl(new PhotoLink(_data)) @@ -3291,13 +3357,14 @@ void HistoryPhoto::updateFrom(const MTPMessageMedia &media) { } } -void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const { +void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; + int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); + bool bubble = parent->hasBubble(); bool fromChannel = parent->fromChannel(), out = parent->out(), outbg = out && !fromChannel; - if (width < 0) width = w; - int skipx = 0, skipy = 0, height = _height; - int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); if (bubble) { skipx = st::mediaPadding.left(); skipy = st::mediaPadding.top(); @@ -3308,7 +3375,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, bool selected, in height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.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->full->load(false, false); @@ -3322,7 +3389,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, bool selected, in p.drawPixmap(skipx, skipy, pix); if (!full) { - uint64 dt = itemAnimations().animate(parent, getms()); + uint64 dt = itemAnimations().animate(parent, ms); 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; @@ -3350,7 +3417,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, bool selected, in QString time(parent->timeText()); if (_caption.isEmpty()) { int32 fullRight = skipx + width, fullBottom = skipy + height; - parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayOverImage); + 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); @@ -3528,16 +3595,9 @@ 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; - - int skipy = 0, replyFrom = 0, fwdFrom = 0; +void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; data->thumb->checkload(); @@ -3546,8 +3606,8 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in 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)); + style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg)); + style::color sh(outbg ? (selected ? st::msgOutShadowSelected : st::msgOutShadow) : (selected ? st::msgInShadowSelected : st::msgInShadow)); RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners)); App::roundRect(p, 0, 0, width, height, bg, cors, &sh); @@ -3570,7 +3630,7 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in QString statusText; - style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor)); + style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg)); p.setPen(status->p); if (data->loader) { @@ -3600,20 +3660,15 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, bool selected, in 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.setBrush((outbg ? (selected ? st::mediaOutUnreadFgSelected : st::mediaOutUnreadFg) : (selected ? st::mediaInUnreadFgSelected : st::mediaInUnreadFg))->b); p.drawEllipse(QRect(tleft + w + st::mediaUnreadSkip, texty + ((st::normalFont->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) { @@ -3660,12 +3715,10 @@ void HistoryAudio::initDimensions(const HistoryItem *parent) { _height = _minh; } -void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const { - if (width < 0) width = w; +void HistoryAudio::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + int32 width = w, skipy = 0, replyFrom = 0, fwdFrom = 0; if (width < 1) return; - int skipy = 0, replyFrom = 0, fwdFrom = 0; - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; bool already = !data->already().isEmpty(), hasdata = !data->data.isEmpty(); if (width >= _maxw) { @@ -3676,8 +3729,8 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in data->save(QString()); } - 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(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg)); + style::color sh(outbg ? (selected ? st::msgOutShadowSelected : st::msgOutShadow) : (selected ? st::msgInShadowSelected : st::msgInShadow)); RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners)); App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); @@ -3737,7 +3790,7 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in p.setPen(st::black->c); p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, lang(lng_media_audio)); - style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor)); + style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg)); p.setPen(status->p); int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height; p.drawText(tleft, texty + st::normalFont->ascent, statusText); @@ -3746,18 +3799,11 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, bool selected, in 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.setBrush((outbg ? (selected ? st::mediaOutUnreadFgSelected : st::mediaOutUnreadFg) : (selected ? st::mediaInUnreadFgSelected : st::mediaInUnreadFg))->b); p.drawEllipse(QRect(tleft + w + st::mediaUnreadSkip, texty + ((st::normalFont->height - st::mediaUnreadSize) / 2), st::mediaUnreadSize, st::mediaUnreadSize)); 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) { @@ -3848,9 +3894,10 @@ HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia() , _savel(new DocumentSaveLink(_data)) , _thumbsavel(new DocumentSaveLink(_data)) , _cancell(new DocumentCancelLink(_data)) -, _name(documentName(_data)) { +, _name(documentName(_data)) +, _animation(0) { if (_name.isEmpty()) _name = qsl("Unknown File"); - _namew = st::normalFont->width(_name); + _namew = st::semiboldFont->width(_name); setStatusSize(FileStatusSizeReady); @@ -3862,10 +3909,8 @@ HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia() } else { _thumbw = st::msgFileThumbSize; } - _height = _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); } else { _thumbw = 0; - _height = _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); } } @@ -3946,11 +3991,17 @@ void HistoryDocument::initDimensions(const HistoryItem *parent) { _maxw = qMax(tleft + _namew + tright, _maxw); _maxw = qMin(_maxw, int(st::msgMaxWidth)); + + if (withThumb()) { + _height = _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); + } else { + _height = _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); + } } -void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const { - if (width < 0) width = w; - if (width < 1) return; +void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); @@ -3960,6 +4011,14 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, } bool showPause = updateStatusText(parent); + bool radial = _animation && _animation->radial.animating(); + + if (_data->loader) { + ensureAnimation(parent); + if (!_animation->radial.animating()) { + _animation->radial.start(_data->progress()); + } + } int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; bool wthumb = withThumb(); @@ -3981,29 +4040,47 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - if (already || hasdata) { + if (!radial && (already || hasdata)) { } else { - p.setRenderHint(QPainter::HighQualityAntialiasing); - QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); p.setPen(Qt::NoPen); - p.setBrush(selected ? st::msgDateImgSelectBg : st::msgDateImgBg); - p.drawEllipse(inner); + if (selected) { + p.setBrush(st::msgDateImgBgSelected); + } else if (radial && (already || hasdata)) { + p.setOpacity(st::msgDateImgBg->c.alphaF() * _animation->radial.opacity()); + p.setBrush(st::black); + } else if (_animation && _animation->_a_thumbOver.animating()) { + 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(_thumbsavel); + p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); + } + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.drawEllipse(inner); p.setRenderHint(QPainter::HighQualityAntialiasing, false); style::sprite icon; - if (_data->loader) { + if (already || hasdata || _data->loader) { icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); } else { icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); } + p.setOpacity(radial ? _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, selected ? st::msgInBgSelected : st::msgInBg); + } } if (_data->status != FileUploadFailed) { const TextLinkPtr &lnk((_data->loader || _data->status == FileUploading) ? _cancell : _savel); - bool over = (textlnkOver() == lnk) && (!textlnkDown() || textlnkDown() == lnk); + 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); @@ -4014,15 +4091,28 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, nameright = st::msgFilePadding.left(); statustop = st::msgFileStatusTop; - p.setRenderHint(QPainter::HighQualityAntialiasing); - QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width)); p.setPen(Qt::NoPen); - p.setBrush(outbg ? (selected ? st::msgFileOutBgSelected : st::msgFileOutBg) : (selected ? st::msgFileInBgSelected : st::msgFileInBg)); - p.drawEllipse(inner); + if (selected) { + p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); + } else if (_animation && _animation->_a_thumbOver.animating()) { + 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(_thumbsavel); + 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, bg); + } + style::sprite icon; if (showPause) { icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause); @@ -4051,7 +4141,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, bool selected, p.drawTextLeft(nameleft, nametop, width, _name, _namew); } - style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor)); + 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); @@ -4062,7 +4152,7 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); int32 height = st::msgPadding.top() + st::mediaThumbSize + st::msgPadding.bottom(); - style::color bg(selected ? st::msgInSelectBg : (over ? st::playlistHoverBg : st::msgInBg)); + style::color bg(selected ? st::msgInBgSelected : (over ? st::playlistHoverBg : st::msgInBg)); p.fillRect(0, 0, width, height, bg->b); style::sprite img = st::mediaMusicInImg; @@ -4105,7 +4195,7 @@ void HistoryDocument::drawInPlaylist(Painter &p, const HistoryItem *parent, bool p.drawTextLeft(tleft, st::msgPadding.top() + st::mediaNameTop, width, _name, _namew); } - style::color status(selected ? st::mediaInSelectColor : st::mediaInColor); + style::color status(selected ? st::mediaInFgSelected : st::mediaInFg); p.setPen(status->p); p.drawTextLeft(tleft, st::msgPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height, width, _statusText); } @@ -4209,6 +4299,63 @@ void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int3 } } +void HistoryDocument::linkOver(HistoryItem *parent, const TextLinkPtr &lnk) { + if (lnk == _thumbsavel && _data->already().isEmpty() && _data->data.isEmpty()) { + ensureAnimation(parent); + _animation->a_thumbOver.start(1); + _animation->_a_thumbOver.start(); + } +} + +void HistoryDocument::linkOut(HistoryItem *parent, const TextLinkPtr &lnk) { + if (_animation && lnk == _thumbsavel) { + _animation->a_thumbOver.start(0); + _animation->_a_thumbOver.start(); + } +} + +void HistoryDocument::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 { + _animation->a_thumbOver.update(dt, anim::linear); + } + if (timer) { + Notify::redrawHistoryItem(parent); + } +} + +void HistoryDocument::step_radial(const HistoryItem *parent, uint64 ms, bool timer) { + _animation->radial.update(_data->progress(), !_data->loader, ms); + if (!_animation->radial.animating()) { + checkAnimationFinished(); + } + if (timer) { + Notify::redrawHistoryItem(parent); + } +} + +void HistoryDocument::ensureAnimation(const HistoryItem *parent) const { + if (!_animation) { + _animation = new AnimationData( + animation(parent, const_cast(this), &HistoryDocument::step_thumbOver), + animation(parent, const_cast(this), &HistoryDocument::step_radial) + ); + } +} + +void HistoryDocument::checkAnimationFinished() { + if (_animation && !_animation->_a_thumbOver.animating() && !_animation->radial.animating()) { + if (!_data->already().isEmpty() || !_data->data.isEmpty()) { + delete _animation; + _animation = 0; + } + } +} + HistoryMedia *HistoryDocument::clone() const { return new HistoryDocument(*this); } @@ -4217,6 +4364,11 @@ ImagePtr HistoryDocument::replyPreview() { return _data->makeReplyPreview(); } +HistoryDocument::~HistoryDocument() { + delete _animation; + _animation = 0; +} + HistoryGif::HistoryGif(DocumentData *document) : HistoryMedia() , _data(document) , _openl(new DocumentOpenLink(_data)) @@ -4237,9 +4389,9 @@ void HistoryGif::initDimensions(const HistoryItem *parent) { _height = _minh; } -void HistoryGif::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const { - if (width < 0) width = w; - if (width < 1) return; +void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); @@ -4251,7 +4403,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, bool selected, int3 if (ph < 1) ph = 1; } - App::roundShadow(p, 0, 0, pw, ph, selected ? st::msgInSelectShadow : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); + App::roundShadow(p, 0, 0, pw, ph, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); p.drawPixmap(0, 0, animated.current(pw * cIntRetinaFactor(), ph * cIntRetinaFactor(), true)); if (selected) { @@ -4387,9 +4539,10 @@ void HistorySticker::initDimensions(const HistoryItem *parent) { 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; +void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; + if (width > _maxw) width = _maxw; bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; @@ -4429,7 +4582,7 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, bool selected, } } - parent->drawInfo(p, usex + usew, _height, selected, InfoDisplayOverImage); + 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(); @@ -4593,19 +4746,17 @@ HistoryMedia *HistoryContact::clone() const { return new HistoryContact(userId, name.original(), phone); } -void HistoryContact::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const { - if (width < 0) width = w; - if (width < 1) return; - - int skipy = 0, replyFrom = 0, fwdFrom = 0; +void HistoryContact::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; 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)); + style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg)); + style::color sh(outbg ? (selected ? st::msgOutShadowSelected : st::msgOutShadow) : (selected ? st::msgInShadowSelected : st::msgInShadow)); RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners)); App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); @@ -4626,13 +4777,10 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, bool selected, p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, phone); } - style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor)); + style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg)); p.setPen(status->p); name.drawElided(p, tleft, skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height, secondwidth); - - int32 fullRight = width, fullBottom = _height; - parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayDefault); } void HistoryContact::updateFrom(const MTPMessageMedia &media) { @@ -4795,9 +4943,9 @@ void HistoryWebPage::initDimensions(const HistoryItem *parent) { _height = _minh; } -void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const { - if (width < 0) width = w; - if (width < 1 || data->pendingTill) return; +void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; int16 animw = 0, animh = 0; if (data->doc && animated.msg == parent) { @@ -4826,8 +4974,8 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, bool selected, 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)); + 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.fillRect(0, 0, st::webPageBar, _height - bottomSkip, bar->b); p.save(); @@ -4911,7 +5059,7 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, bool selected, } p.drawPixmap(0, 0, pix); if (!full) { - uint64 dt = itemAnimations().animate(parent, getms()); + uint64 dt = itemAnimations().animate(parent, ms); int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); int32 x = (pixwidth - st::photoLoader.width()) / 2, y = (pixheight - st::photoLoader.height()) / 2; @@ -4947,7 +5095,7 @@ 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); @@ -5044,7 +5192,7 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, bool selected, p.drawText(tleft, st::mediaNameTop + st::normalFont->ascent, _docName); } - style::color status(selected ? (outbg ? st::mediaOutSelectColor : st::mediaInSelectColor) : (outbg ? st::mediaOutColor : st::mediaInColor)); + style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg)); p.setPen(status->p); p.drawText(tleft, st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->descent, statusText); @@ -5709,16 +5857,17 @@ void HistoryImageLink::initDimensions(const HistoryItem *parent) { _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; +void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; if (!_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)); + 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, 0, 0, width, _height, bg, cors, &sh); @@ -5743,7 +5892,7 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, bool selected } 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(); @@ -5789,7 +5938,7 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, bool selected } int32 fullRight = skipx + width, fullBottom = _height - (skipx ? st::mediaPadding.bottom() : 0); - parent->drawInfo(p, fullRight, fullBottom, selected, InfoDisplayOverImage); + parent->drawInfo(p, fullRight, fullBottom, skipx * 2 + width, selected, InfoDisplayOverImage); } int32 HistoryImageLink::resize(int32 width, const HistoryItem *parent) { @@ -6188,8 +6337,8 @@ bool HistoryMessage::textHasLinks() { 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; @@ -6197,7 +6346,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(); @@ -6207,11 +6356,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(); @@ -6265,7 +6416,7 @@ 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); + Notify::redrawHistoryItem(this); } else { if (_text.hasSkipBlock()) { _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); @@ -6281,7 +6432,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); + Notify::redrawHistoryItem(this); } else { if (_text.hasSkipBlock()) { _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); @@ -6293,17 +6444,18 @@ void HistoryMessage::setId(MsgId newId) { } } -void HistoryMessage::draw(Painter &p, uint32 selection) const { - bool outbg = out() && !fromChannel(), bubble = drawBubble(); +void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const { + bool outbg = out() && !fromChannel(), bubble = drawBubble(), selected = (selection == FullItemSel); 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); @@ -6311,7 +6463,6 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const { } } - bool selected = (selection == FullItemSel); if (_from->nameVersion > _fromVersion) { fromNameUpdated(); _fromVersion = _from->nameVersion; @@ -6327,15 +6478,15 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const { 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); if (fromChannel()) { - p.setPen(selected ? st::msgInServiceSelColor : st::msgInServiceColor); + p.setPen(selected ? st::msgInServiceFgSelected : st::msgInServiceFg); } else { p.setPen(_from->color); } @@ -6347,19 +6498,21 @@ void HistoryMessage::draw(Painter &p, uint32 selection) const { if (_media && _media->isDisplayed()) { p.save(); - p.translate(left, _height - st::msgMargin.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(), selected, InfoDisplayDefault); + 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(), selected, InfoDisplayDefault); + HistoryMessage::drawInfo(p, r.x() + r.width(), r.y() + r.height(), 2 * r.x() + r.width(), selected, InfoDisplayDefault); } } else { p.save(); - p.translate(left, st::msgMargin.top()); - _media->draw(p, this, selected); + int32 top = st::msgMargin.top(); + p.translate(left, top); + _media->draw(p, this, r.translated(-left, -top), selected, ms); p.restore(); } @@ -6580,8 +6733,7 @@ HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const , 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) -{ +, fromWidth(st::msgServiceFont->width(lang(lng_forwarded_from)) + st::msgServiceFont->spacew) { fwdNameUpdated(); } @@ -6589,8 +6741,7 @@ HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId , fwdDate(msg->dateForwarded()) , fwdFrom(msg->fromForwarded()) , fwdFromVersion(fwdFrom->nameVersion) -, fromWidth(st::msgServiceFont->width(lang(lng_forwarded_from)) + st::msgServiceFont->spacew) -{ +, fromWidth(st::msgServiceFont->width(lang(lng_forwarded_from)) + st::msgServiceFont->spacew) { fwdNameUpdated(); } @@ -6615,19 +6766,19 @@ void HistoryForwarded::fwdNameUpdated() const { } } -void HistoryForwarded::draw(Painter &p, uint32 selection) const { +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.setPen((selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg))->p); p.setFont(serviceFont->f); if (w >= fromWidth) { @@ -6845,11 +6996,11 @@ void HistoryReply::replyToReplaced(HistoryItem *oldItem, HistoryItem *newItem) { } } -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 { @@ -6881,14 +7032,14 @@ void HistoryReply::drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selec if (likeService) { p.setPen(st::white->p); } 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))->p); } 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->emptyText()) || replyToMsg->serviceMsg()) { - style::color date(selected ? (outbg ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (outbg ? st::msgOutDateColor : st::msgInDateColor)); + style::color date(outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg)); p.setPen(date->p); } else { p.setPen(st::msgColor->p); @@ -6897,7 +7048,7 @@ void HistoryReply::drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selec } } else { p.setFont(st::msgDateFont->f); - style::color date(selected ? (outbg ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (outbg ? st::msgOutDateColor : st::msgInDateColor)); + style::color date(outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg)); if (likeService) { p.setPen(st::white->p); } else { @@ -7210,14 +7361,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); @@ -7233,8 +7385,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 == FullItemSel, ms); p.restore(); } @@ -7435,7 +7588,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 { @@ -7471,7 +7624,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 e42153c49..7efe44a1e 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -845,7 +845,7 @@ 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; @@ -933,6 +933,10 @@ 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; } @@ -955,7 +959,7 @@ 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) { } @@ -1112,6 +1116,34 @@ private: HistoryItem *regItem(HistoryItem *item, bool returnExisting = false); +class RadialAnimation { +public: + + RadialAnimation(int32 thickness, AnimationCallbacks *callbacks); + + 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 draw(Painter &p, const QRect &inner, const style::color &color); + +private: + + int32 _thickness; + uint64 _firstStart, _lastStart, _lastTime; + float64 _opacity; + anim::fvalue a_arcEnd, a_arcStart; + Animation _animation; + +}; + class HistoryMedia : public HistoryElem { public: @@ -1136,7 +1168,11 @@ public: 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 linkOver(HistoryItem *parent, const TextLinkPtr &lnk) { + } + virtual void linkOut(HistoryItem *parent, const TextLinkPtr &lnk) { + } + virtual void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const = 0; virtual bool uploading() const { return false; } @@ -1196,7 +1232,7 @@ public: void init(); void initDimensions(const HistoryItem *parent); - void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; int32 resize(int32 width, const HistoryItem *parent); HistoryMediaType type() const { return MediaTypePhoto; @@ -1268,7 +1304,7 @@ 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; + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; int32 resize(int32 width, const HistoryItem *parent); HistoryMediaType type() const { return MediaTypeVideo; @@ -1323,7 +1359,7 @@ public: HistoryAudio(const MTPDaudio &audio); void initDimensions(const HistoryItem *parent); - void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; HistoryMediaType type() const { return MediaTypeAudio; } @@ -1372,7 +1408,7 @@ public: return !_data->song() && !_data->thumb->isNull() && _data->thumb->width() && _data->thumb->height(); } - void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; int32 resize(int32 width, const HistoryItem *parent); HistoryMediaType type() const { return MediaTypeDocument; @@ -1385,6 +1421,8 @@ public: return (_data->status == FileUploading); } void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; + void linkOver(HistoryItem *parent, const TextLinkPtr &lnk); + void linkOut(HistoryItem *parent, const TextLinkPtr &lnk); HistoryMedia *clone() const; DocumentData *document() { @@ -1414,6 +1452,8 @@ public: return _data->song(); } + ~HistoryDocument(); + private: DocumentData *_data; @@ -1433,6 +1473,25 @@ private: void setStatusSize(int32 newSize, qint64 realDuration = 0) const; bool updateStatusText(const HistoryItem *parent) const; // returns showPause + + 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(); + + struct AnimationData { + AnimationData(AnimationCallbacks *thumbOverCallbacks, AnimationCallbacks *radialCallbacks) : a_thumbOver(0, 0) + , _a_thumbOver(thumbOverCallbacks) + , radial(st::msgFileRadialLine, radialCallbacks) { + } + anim::fvalue a_thumbOver; + Animation _a_thumbOver; + + RadialAnimation radial; + }; + mutable AnimationData *_animation; + }; class HistoryGif : public HistoryMedia { @@ -1441,7 +1500,7 @@ public: HistoryGif(DocumentData *document); void initDimensions(const HistoryItem *parent); - void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; int32 resize(int32 width, const HistoryItem *parent); HistoryMediaType type() const { return MediaTypeGif; @@ -1500,7 +1559,7 @@ public: HistorySticker(DocumentData *document); void initDimensions(const HistoryItem *parent); - void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; int32 resize(int32 width, const HistoryItem *parent); HistoryMediaType type() const { return MediaTypeSticker; @@ -1544,7 +1603,7 @@ public: 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; + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; HistoryMediaType type() const { return MediaTypeContact; } @@ -1577,7 +1636,7 @@ public: HistoryWebPage(WebPageData *data); void initDimensions(const HistoryItem *parent); - void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; bool isDisplayed() const { return !data->pendingTill; } @@ -1691,7 +1750,7 @@ public: int32 fullHeight() const; void initDimensions(const HistoryItem *parent); - void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; int32 resize(int32 width, const HistoryItem *parent); HistoryMediaType type() const { return MediaTypeImageLink; @@ -1751,10 +1810,11 @@ public: } bool uploading() const; - void drawInfo(Painter &p, int32 right, int32 bottom, bool selected, InfoDisplayType type) const; + void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const; void setViewsCount(int32 count); void setId(MsgId newId); - void draw(Painter &p, uint32 selection) const; + void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; + virtual void drawMessageText(Painter &p, const QRect &trect, uint32 selection) const; int32 resize(int32 width); @@ -1768,6 +1828,12 @@ 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; @@ -1867,7 +1933,7 @@ 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; int32 resize(int32 width); @@ -1923,7 +1989,7 @@ 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; int32 resize(int32 width); @@ -1966,7 +2032,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; @@ -2077,7 +2143,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; @@ -2119,7 +2185,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; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 0da2a49b5..a9a553031 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -116,11 +116,11 @@ void HistoryInner::messagesReceivedDown(PeerData *peer, const QVectordetached() || !_history) return; - int32 msgy = itemTop(msg); +void HistoryInner::redrawItem(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()) { @@ -189,7 +189,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { sel = i.value(); } } - item->draw(p, sel); + item->draw(p, r.translated(0, -y), sel, ms); if (item->hasViews()) { App::main()->scheduleViewIncrement(item); @@ -233,7 +233,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { sel = i.value(); } } - item->draw(p, sel); + item->draw(p, r.translated(0, -y), sel, ms); if (item->hasViews()) { App::main()->scheduleViewIncrement(item); @@ -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()); + redrawItem(App::pressedItem()); App::pressedItem(App::hoveredItem()); - updateMsg(App::pressedItem()); + redrawItem(App::pressedItem()); } if (textlnkDown() != textlnkOver()) { - updateMsg(App::pressedLinkItem()); + redrawItem(App::pressedLinkItem()); textlnkDown(textlnkOver()); App::pressedLinkItem(App::hoveredLinkItem()); - updateMsg(App::pressedLinkItem()); - updateMsg(App::pressedItem()); + redrawItem(App::pressedLinkItem()); + redrawItem(App::pressedItem()); } _dragAction = NoDrag; @@ -512,7 +512,7 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt uint32 selStatus = (symbol << 16) | symbol; if (selStatus != FullItemSel && (_selected.isEmpty() || _selected.cbegin().value() != FullItemSel)) { if (!_selected.isEmpty()) { - updateMsg(_selected.cbegin().key()); + redrawItem(_selected.cbegin().key()); _selected.clear(); } _selected.insert(_dragItem, selStatus); @@ -553,12 +553,12 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt uint32 selStatus = (_dragSymbol << 16) | _dragSymbol; if (selStatus != FullItemSel && (_selected.isEmpty() || _selected.cbegin().value() != FullItemSel)) { if (!_selected.isEmpty()) { - updateMsg(_selected.cbegin().key()); + redrawItem(_selected.cbegin().key()); _selected.clear(); } _selected.insert(_dragItem, selStatus); _dragAction = Selecting; - updateMsg(_dragItem); + redrawItem(_dragItem); } else { _dragAction = PrepareSelect; } @@ -731,7 +731,7 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but } } if (textlnkDown()) { - updateMsg(App::pressedLinkItem()); + redrawItem(App::pressedLinkItem()); textlnkDown(TextLinkPtr()); App::pressedLinkItem(0); if (!textlnkOver() && _cursor != style::cur_default) { @@ -740,7 +740,7 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but } } if (App::pressedItem()) { - updateMsg(App::pressedItem()); + redrawItem(App::pressedItem()); App::pressedItem(0); } @@ -764,16 +764,16 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but } else { _selected.erase(i); } - updateMsg(_dragItem); + redrawItem(_dragItem); } else if (_dragAction == PrepareDrag && !_dragWasInactive && button != Qt::RightButton) { SelectedItems::iterator i = _selected.find(_dragItem); if (i != _selected.cend() && i.value() == FullItemSel) { _selected.erase(i); - updateMsg(_dragItem); + redrawItem(_dragItem); } else if (i == _selected.cend() && !_dragItem->serviceMsg() && _dragItem->id > 0 && !_selected.isEmpty() && _selected.cbegin().value() == FullItemSel) { if (_selected.size() < MaxSelectedItems) { _selected.insert(_dragItem, FullItemSel); - updateMsg(_dragItem); + redrawItem(_dragItem); } } else { _selected.clear(); @@ -818,7 +818,7 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) { _dragAction = Selecting; uint32 selStatus = (symbol << 16) | symbol; if (!_selected.isEmpty()) { - updateMsg(_selected.cbegin().key()); + redrawItem(_selected.cbegin().key()); _selected.clear(); } _selected.insert(_dragItem, selStatus); @@ -1348,12 +1348,17 @@ void HistoryInner::enterEvent(QEvent *e) { } void HistoryInner::leaveEvent(QEvent *e) { - if (textlnkOver()) { - updateMsg(App::hoveredItem()); - updateMsg(App::hoveredLinkItem()); - textlnkOver(TextLinkPtr()); - App::hoveredLinkItem(0); + if (HistoryItem *item = App::hoveredItem()) { + redrawItem(item); App::hoveredItem(0); + } + if (textlnkOver()) { + if (HistoryItem *item = App::hoveredLinkItem()) { + item->linkOut(textlnkOver()); + redrawItem(item); + App::hoveredLinkItem(0); + } + textlnkOver(TextLinkPtr()); if (!textlnkDown() && _cursor != style::cur_default) { _cursor = style::cur_default; setCursor(_cursor); @@ -1522,11 +1527,11 @@ void HistoryInner::onUpdateSelected() { App::mousedItem(item); m = mapMouseToItem(point, item); if (item->hasPoint(m.x(), m.y())) { - updateMsg(App::hoveredItem()); + redrawItem(App::hoveredItem()); App::hoveredItem(item); - updateMsg(App::hoveredItem()); + redrawItem(App::hoveredItem()); } else if (App::hoveredItem()) { - updateMsg(App::hoveredItem()); + redrawItem(App::hoveredItem()); App::hoveredItem(0); } } @@ -1552,8 +1557,9 @@ void HistoryInner::onUpdateSelected() { if (lnk != textlnkOver()) { lnkChanged = true; if (textlnkOver()) { - if (App::hoveredLinkItem()) { - updateMsg(App::hoveredLinkItem()); + if (HistoryItem *item = App::hoveredLinkItem()) { + item->linkOut(textlnkOver()); + redrawItem(item); } else { update(_botDescRect); } @@ -1562,8 +1568,9 @@ void HistoryInner::onUpdateSelected() { QToolTip::hideText(); App::hoveredLinkItem((lnk && !lnkInDesc) ? item : 0); if (textlnkOver()) { - if (App::hoveredLinkItem()) { - updateMsg(App::hoveredLinkItem()); + if (HistoryItem *item = App::hoveredLinkItem()) { + item->linkOver(textlnkOver()); + redrawItem(item); } else { update(_botDescRect); } @@ -2947,7 +2954,7 @@ void HistoryWidget::updateStickers() { _stickersUpdateRequest = MTP::send(MTPmessages_GetAllStickers(MTP_int(cStickersHash())), rpcDone(&HistoryWidget::stickersGot), rpcFail(&HistoryWidget::stickersFailed)); } -void HistoryWidget::notifyBotCommandsChanged(UserData *user) { +void HistoryWidget::notify_botCommandsChanged(UserData *user) { if (_peer && (_peer == user || !_peer->isUser())) { if (_attachMention.clearFilteredCommands()) { checkMentionDropdown(); @@ -2955,7 +2962,7 @@ void HistoryWidget::notifyBotCommandsChanged(UserData *user) { } } -void HistoryWidget::notifyUserIsBotChanged(UserData *user) { +void HistoryWidget::notify_userIsBotChanged(UserData *user) { if (_peer && _peer == user) { _list->notifyIsBotChanged(); _list->updateBotInfo(); @@ -2964,7 +2971,7 @@ void HistoryWidget::notifyUserIsBotChanged(UserData *user) { } } -void HistoryWidget::notifyMigrateUpdated(PeerData *peer) { +void HistoryWidget::notify_migrateUpdated(PeerData *peer) { if (_peer) { if (_peer == peer) { if (peer->migrateTo()) { @@ -5442,7 +5449,7 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadPhoto, 0); } -// msgUpdated(item); +// Notify::redrawHistoryItem(item); } } @@ -5464,7 +5471,7 @@ void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadFile, doc ? doc->uploadOffset : 0); } - msgUpdated(item); + Notify::redrawHistoryItem(item); } } @@ -5475,7 +5482,7 @@ void HistoryWidget::onAudioProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadAudio, audio ? audio->uploadOffset : 0); } - msgUpdated(item); + Notify::redrawHistoryItem(item); } } @@ -5486,7 +5493,7 @@ void HistoryWidget::onPhotoFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadPhoto, -1); } -// msgUpdated(item); +// Notify::redrawHistoryItem(item); } } @@ -5497,7 +5504,7 @@ void HistoryWidget::onDocumentFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadFile, -1); } - msgUpdated(item); + Notify::redrawHistoryItem(item); } } @@ -5508,7 +5515,7 @@ void HistoryWidget::onAudioFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadAudio, -1); } - msgUpdated(item); + Notify::redrawHistoryItem(item); } } @@ -5595,9 +5602,9 @@ void HistoryWidget::peerMessagesUpdated() { if (_list) peerMessagesUpdated(_peer->id); } -void HistoryWidget::msgUpdated(const HistoryItem *msg) { - if (_peer && _list && (msg->history() == _history || (_migrated && msg->history() == _migrated))) { - _list->updateMsg(msg); +void HistoryWidget::notify_redrawHistoryItem(const HistoryItem *item) { + if (_peer && _list && (item->history() == _history || (_migrated && item->history() == _migrated))) { + _list->redrawItem(item); } } @@ -6528,14 +6535,14 @@ void HistoryWidget::onAnimActiveStep() { if (getms() - _animActiveStart > st::activeFadeInDuration + st::activeFadeOutDuration) { stopAnimActive(); } else { - App::main()->msgUpdated(item); + Notify::redrawHistoryItem(item); } } -uint64 HistoryWidget::animActiveTime(const HistoryItem *msg) const { +uint64 HistoryWidget::animActiveTimeStart(const HistoryItem *msg) const { if (!msg) return 0; if ((msg->history() == _history && msg->id == _activeAnimMsgId) || (_migrated && msg->history() == _migrated && msg->id == -_activeAnimMsgId)) { - return _animActiveTimer.isActive() ? (getms() - _animActiveStart) : 0; + return _animActiveTimer.isActive() ? _animActiveStart : 0; } return 0; } @@ -6645,11 +6652,11 @@ void HistoryWidget::drawField(Painter &p) { } p.setPen(st::replyColor->p); _replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), width() - replyLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); - p.setPen((((drawReplyTo->toHistoryMessage() && drawReplyTo->toHistoryMessage()->emptyText()) || drawReplyTo->serviceMsg()) ? st::msgInDateColor : st::msgColor)->p); + p.setPen((((drawReplyTo->toHistoryMessage() && drawReplyTo->toHistoryMessage()->emptyText()) || drawReplyTo->serviceMsg()) ? st::msgInDateFg : st::msgColor)->p); _replyToText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - replyLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); } else { p.setFont(st::msgDateFont->f); - p.setPen(st::msgInDateColor->p); + p.setPen(st::msgInDateFg->p); p.drawText(replyLeft, backy + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - replyLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right())); } } @@ -6669,7 +6676,7 @@ void HistoryWidget::drawField(Painter &p) { } p.setPen(st::replyColor->p); from->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top(), width() - forwardLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); - p.setPen((serviceColor ? st::msgInDateColor : st::msgColor)->p); + p.setPen((serviceColor ? st::msgInDateFg : st::msgColor)->p); text->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - forwardLeft - _replyForwardPreviewCancel.width() - st::msgReplyPadding.right()); } } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index f01e5fb8b..9a76a7412 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -69,7 +69,7 @@ public: int32 recountHeight(HistoryItem *resizedItem); void updateSize(); - void updateMsg(const HistoryItem *msg); + void redrawItem(const HistoryItem *item); bool canCopySelected() const; bool canDeleteSelected() const; @@ -430,7 +430,7 @@ public: void peerMessagesUpdated(PeerId peer); void peerMessagesUpdated(); - void msgUpdated(const HistoryItem *msg); + void notify_redrawHistoryItem(const HistoryItem *item); void newUnreadMsg(History *history, HistoryItem *item); void historyToDown(History *history); void historyWasRead(bool force = true); @@ -487,7 +487,7 @@ public: bool touchScroll(const QPoint &delta); - uint64 animActiveTime(const HistoryItem *msg) const; + uint64 animActiveTimeStart(const HistoryItem *msg) const; void stopAnimActive(); void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true); @@ -558,9 +558,9 @@ public: resizeEvent(0); } - void notifyBotCommandsChanged(UserData *user); - void notifyUserIsBotChanged(UserData *user); - void notifyMigrateUpdated(PeerData *peer); + void notify_botCommandsChanged(UserData *user); + void notify_userIsBotChanged(UserData *user); + void notify_migrateUpdated(PeerData *peer); ~HistoryWidget(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 5fc39f736..e3e5e325b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -683,18 +683,6 @@ void MainWidget::updateStickers() { history.updateStickers(); } -void MainWidget::notifyBotCommandsChanged(UserData *bot) { - history.notifyBotCommandsChanged(bot); -} - -void MainWidget::notifyUserIsBotChanged(UserData *bot) { - history.notifyUserIsBotChanged(bot); -} - -void MainWidget::notifyMigrateUpdated(PeerData *peer) { - history.notifyMigrateUpdated(peer); -} - void MainWidget::onUpdateMuted() { App::updateMuted(); } @@ -758,6 +746,27 @@ void MainWidget::ui_hideStickerPreview() { _stickerPreview->hidePreview(); } +void MainWidget::notify_botCommandsChanged(UserData *bot) { + history.notify_botCommandsChanged(bot); +} + +void MainWidget::notify_userIsBotChanged(UserData *bot) { + history.notify_userIsBotChanged(bot); +} + +void MainWidget::notify_migrateUpdated(PeerData *peer) { + history.notify_migrateUpdated(peer); +} + +void MainWidget::notify_redrawHistoryItem(const HistoryItem *msg) { + if (!msg) return; + history.notify_redrawHistoryItem(msg); + if (!msg->history()->dialogs.isEmpty() && msg->history()->lastMsg == msg) { + dialogs.dlgUpdated(msg->history()->dialogs[0]); + } + if (overview) overview->notify_redrawHistoryItem(msg); +} + void MainWidget::noHider(HistoryHider *destroyed) { if (_hider == destroyed) { _hider = 0; @@ -1303,8 +1312,8 @@ void MainWidget::readServerHistory(History *hist, bool force) { } } -uint64 MainWidget::animActiveTime(const HistoryItem *msg) const { - return history.animActiveTime(msg); +uint64 MainWidget::animActiveTimeStart(const HistoryItem *msg) const { + return history.animActiveTimeStart(msg); } void MainWidget::stopAnimActive() { @@ -1474,7 +1483,7 @@ void MainWidget::itemResized(HistoryItem *row, bool scrollToIt) { if (overview) { overview->itemResized(row, scrollToIt); } - if (row) msgUpdated(row); + if (row) Notify::redrawHistoryItem(row); } bool MainWidget::overviewFailed(PeerData *peer, const RPCError &error, mtpRequestId req) { @@ -1624,7 +1633,7 @@ void MainWidget::videoLoadProgress(mtpFileLoader *loader) { VideoItems::const_iterator i = items.constFind(video); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - msgUpdated(j.key()); + Notify::redrawHistoryItem(j.key()); } } } @@ -1701,7 +1710,7 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) { AudioItems::const_iterator i = items.constFind(audio); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - msgUpdated(j.key()); + Notify::redrawHistoryItem(j.key()); } } } @@ -1736,7 +1745,7 @@ void MainWidget::audioPlayProgress(const AudioMsgId &audioId) { } if (HistoryItem *item = App::histItemById(audioId.msgId)) { - msgUpdated(item); + Notify::redrawHistoryItem(item); } } @@ -1797,7 +1806,7 @@ void MainWidget::documentPlayProgress(const SongMsgId &songId) { } if (HistoryItem *item = App::histItemById(songId.msgId)) { - msgUpdated(item); + Notify::redrawHistoryItem(item); } } @@ -1884,7 +1893,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { DocumentItems::const_iterator i = items.constFind(document); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - msgUpdated(j.key()); + Notify::redrawHistoryItem(j.key()); } } App::wnd()->documentUpdated(document); @@ -2630,15 +2639,6 @@ void MainWidget::onActiveChannelUpdateFull() { } } -void MainWidget::msgUpdated(const HistoryItem *msg) { - if (!msg) return; - history.msgUpdated(msg); - if (!msg->history()->dialogs.isEmpty() && msg->history()->lastMsg == msg) { - dialogs.dlgUpdated(msg->history()->dialogs[0]); - } - if (overview) overview->msgUpdated(msg); -} - void MainWidget::historyToDown(History *hist) { history.historyToDown(hist); } @@ -4227,7 +4227,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { msgRow->history()->unregTyping(App::self()); } if (!App::historyRegItem(msgRow)) { - msgUpdated(msgRow); + Notify::redrawHistoryItem(msgRow); } else { History *h = msgRow->history(); bool wasLast = (h->lastMsg == msgRow); @@ -4256,7 +4256,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (HistoryItem *item = App::histItemById(NoChannel, v.at(i).v)) { if (item->isMediaUnread()) { item->markMediaRead(); - msgUpdated(item); + Notify::redrawHistoryItem(item); if (item->out() && item->history()->peer->isUser()) { item->history()->peer->asUser()->madeAction(); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 4d8c638e5..1e143148b 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -237,7 +237,6 @@ public: return sentUpdatesReceived(0, updates); } void inviteToChannelDone(ChannelData *channel, const MTPUpdates &updates); - void msgUpdated(const HistoryItem *msg); void historyToDown(History *hist); void dialogsToUp(); void newUnreadMsg(History *history, HistoryItem *item); @@ -328,7 +327,7 @@ public: void readServerHistory(History *history, bool force = true); - uint64 animActiveTime(const HistoryItem *msg) const; + uint64 animActiveTimeStart(const HistoryItem *msg) const; void stopAnimActive(); void sendBotCommand(const QString &cmd, MsgId msgId); @@ -386,9 +385,11 @@ public: void updateMutedIn(int32 seconds); void updateStickers(); - void notifyBotCommandsChanged(UserData *bot); - void notifyUserIsBotChanged(UserData *bot); - void notifyMigrateUpdated(PeerData *peer); + + void notify_botCommandsChanged(UserData *bot); + void notify_userIsBotChanged(UserData *bot); + void notify_migrateUpdated(PeerData *peer); + void notify_redrawHistoryItem(const HistoryItem *msg); void choosePeer(PeerId peerId, MsgId showAtMsgId); // does offerPeer or showPeerHistory void clearBotStartToken(PeerData *peer); diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 989fc3d18..a189b99db 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -527,13 +527,7 @@ void OverviewInner::moveToNextItem(MsgId &msgId, int32 &index, MsgId upTo, int32 } } -void OverviewInner::updateMsg(HistoryItem *item) { - if (App::main() && item) { - App::main()->msgUpdated(item); - } -} - -void OverviewInner::updateMsg(MsgId itemId, int32 itemIndex) { +void OverviewInner::redrawItem(MsgId itemId, int32 itemIndex) { fixItemIndex(itemIndex, itemId); if (itemIndex >= 0) { if (_type == OverviewPhotos) { @@ -665,15 +659,15 @@ void OverviewInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton but if (button != Qt::LeftButton) return; if (textlnkDown() != textlnkOver()) { - updateMsg(App::pressedLinkItem()); + redrawItem(App::pressedLinkItem()); textlnkDown(textlnkOver()); App::pressedLinkItem(App::hoveredLinkItem()); - updateMsg(App::pressedLinkItem()); + redrawItem(App::pressedLinkItem()); } if (_lnkDownIndex != _lnkOverIndex) { - if (_dragItem) updateMsg(_dragItem, _dragItemIndex); + if (_dragItem) redrawItem(_dragItem, _dragItemIndex); _lnkDownIndex = _lnkOverIndex; - if (_mousedItem) updateMsg(_mousedItem, _mousedItemIndex); + if (_mousedItem) redrawItem(_mousedItem, _mousedItemIndex); } _dragAction = NoDrag; @@ -702,12 +696,12 @@ void OverviewInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton but uint32 selStatus = (_dragSymbol << 16) | _dragSymbol; if (selStatus != FullItemSel && (_selected.isEmpty() || _selected.cbegin().value() != FullItemSel)) { if (!_selected.isEmpty()) { - updateMsg(_selected.cbegin().key(), -1); + redrawItem(_selected.cbegin().key(), -1); _selected.clear(); } _selected.insert(_dragItem, selStatus); _dragAction = Selecting; - updateMsg(_dragItem, _dragItemIndex); + redrawItem(_dragItem, _dragItemIndex); _overview->updateTopBarSelection(); } else { _dragAction = PrepareSelect; @@ -752,7 +746,7 @@ void OverviewInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton bu } } if (textlnkDown()) { - updateMsg(App::pressedLinkItem()); + redrawItem(App::pressedLinkItem()); textlnkDown(TextLinkPtr()); App::pressedLinkItem(0); if (!textlnkOver() && _cursor != style::cur_default) { @@ -761,7 +755,7 @@ void OverviewInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton bu } } if (_lnkDownIndex) { - updateMsg(_dragItem, _dragItemIndex); + redrawItem(_dragItem, _dragItemIndex); _lnkDownIndex = 0; if (!_lnkOverIndex && _cursor != style::cur_default) { _cursor = style::cur_default; @@ -795,16 +789,16 @@ void OverviewInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton bu } else { _selected.erase(i); } - updateMsg(_dragItem, _dragItemIndex); + redrawItem(_dragItem, _dragItemIndex); } else if (_dragAction == PrepareDrag && !needClick && !_dragWasInactive && button != Qt::RightButton) { SelectedItems::iterator i = _selected.find(_dragItem); if (i != _selected.cend() && i.value() == FullItemSel) { _selected.erase(i); - updateMsg(_dragItem, _dragItemIndex); + redrawItem(_dragItem, _dragItemIndex); } else if (i == _selected.cend() && itemMsgId(_dragItem) > 0 && !_selected.isEmpty() && _selected.cbegin().value() == FullItemSel) { if (_selected.size() < MaxSelectedItems) { _selected.insert(_dragItem, FullItemSel); - updateMsg(_dragItem, _dragItemIndex); + redrawItem(_dragItem, _dragItemIndex); } } else { _selected.clear(); @@ -1074,7 +1068,11 @@ void OverviewInner::paintEvent(QPaintEvent *e) { Painter p(this); QRect r(e->rect()); - p.setClipRect(r); + bool trivial = (r == rect()); + if (!trivial) { + p.setClipRect(r); + } + uint64 ms = getms(); if (_history->overview[_type].isEmpty() && (!_migrated || !_history->overviewLoaded(_type) || _migrated->overview[_type].isEmpty())) { QPoint dogPos((_width - st::msgDogImg.pxWidth()) / 2, ((height() - st::msgDogImg.pxHeight()) * 4) / 9); @@ -1144,7 +1142,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { QPoint pos(int32(i * w + st::overviewPhotoSkip), _addToY + row * (_vsize + st::overviewPhotoSkip) + st::overviewPhotoSkip); p.drawPixmap(pos, it->pix); if (!quality) { - uint64 dt = itemAnimations().animate(item, getms()); + uint64 dt = itemAnimations().animate(item, ms); int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); int32 x = pos.x() + (_vsize - st::overviewLoader.width()) / 2, y = pos.y() + (_vsize - st::overviewLoader.height()) / 2; @@ -1338,7 +1336,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { p.save(); p.translate(left, 0); - media->draw(p, item, (sel == FullItemSel), w); + media->draw(p, item, r.translated(-left, -curY - (st::msgMargin.top() + _addToY)), (sel == FullItemSel), ms); p.restore(); } } else { @@ -1451,9 +1449,9 @@ void OverviewInner::onUpdateSelected() { } } if (newsel != _selectedMsgId) { - if (_selectedMsgId) updateMsg(_selectedMsgId, -1); + if (_selectedMsgId) redrawItem(_selectedMsgId, -1); _selectedMsgId = newsel; - updateMsg(item); + redrawItem(item); } } else if (_type == OverviewLinks) { int32 w = _width - st::msgMargin.left() - st::msgMargin.right(); @@ -1558,19 +1556,19 @@ void OverviewInner::onUpdateSelected() { bool lnkChanged = false; if (lnk != textlnkOver()) { lnkChanged = true; - updateMsg(App::hoveredLinkItem()); + redrawItem(App::hoveredLinkItem()); textlnkOver(lnk); App::hoveredLinkItem(lnk ? item : 0); - updateMsg(App::hoveredLinkItem()); + redrawItem(App::hoveredLinkItem()); QToolTip::hideText(); } else { App::mousedItem(item); } if (lnkIndex != _lnkOverIndex || _mousedItem != oldMousedItem) { lnkChanged = true; - if (oldMousedItem) updateMsg(oldMousedItem, oldMousedItemIndex); + if (oldMousedItem) redrawItem(oldMousedItem, oldMousedItemIndex); _lnkOverIndex = lnkIndex; - if (item) updateMsg(item); + if (item) redrawItem(item); QToolTip::hideText(); } if (_cursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) { @@ -1794,11 +1792,11 @@ void OverviewInner::enterEvent(QEvent *e) { void OverviewInner::leaveEvent(QEvent *e) { if (_selectedMsgId) { - updateMsg(_selectedMsgId, -1); + redrawItem(_selectedMsgId, -1); _selectedMsgId = 0; } if (textlnkOver()) { - updateMsg(App::hoveredLinkItem()); + redrawItem(App::hoveredLinkItem()); textlnkOver(TextLinkPtr()); App::hoveredLinkItem(0); if (!textlnkDown() && _cursor != style::cur_default) { @@ -1826,8 +1824,8 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (_menu) { _menu->deleteLater(); _menu = 0; - updateMsg(App::contextItem()); - if (_selectedMsgId) updateMsg(_selectedMsgId, -1); + redrawItem(App::contextItem()); + if (_selectedMsgId) redrawItem(_selectedMsgId, -1); } if (e->reason() == QContextMenuEvent::Mouse) { dragActionUpdate(e->globalPos()); @@ -1904,8 +1902,8 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } App::contextItem(App::hoveredLinkItem()); - updateMsg(App::contextItem()); - if (_selectedMsgId) updateMsg(_selectedMsgId, -1); + redrawItem(App::contextItem()); + if (_selectedMsgId) redrawItem(_selectedMsgId, -1); } else if (!ignoreMousedItem && App::mousedItem() && App::mousedItem()->channelId() == itemChannel(_mousedItem) && App::mousedItem()->id == itemMsgId(_mousedItem)) { _contextMenuUrl = _lnkOverIndex ? urlByIndex(_mousedItem, _mousedItemIndex, _lnkOverIndex) : QString(); _menu = new PopupMenu(); @@ -1944,8 +1942,8 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } App::contextItem(App::mousedItem()); - updateMsg(App::contextItem()); - if (_selectedMsgId) updateMsg(_selectedMsgId, -1); + redrawItem(App::contextItem()); + if (_selectedMsgId) redrawItem(_selectedMsgId, -1); } if (_menu) { connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); @@ -2197,8 +2195,8 @@ void OverviewInner::onMenuDestroy(QObject *obj) { if (_menu == obj) { _menu = 0; dragActionUpdate(QCursor::pos()); - updateMsg(App::contextItem()); - if (_selectedMsgId) updateMsg(_selectedMsgId, -1); + redrawItem(App::contextItem()); + if (_selectedMsgId) redrawItem(_selectedMsgId, -1); } } @@ -2570,8 +2568,9 @@ void OverviewInner::itemResized(HistoryItem *item, bool scrollToIt) { } } -void OverviewInner::msgUpdated(const HistoryItem *msg) { +void OverviewInner::redrawItem(const HistoryItem *msg) { if (!msg) return; + History *history = (msg->history() == _history) ? _history : (msg->history() == _migrated ? _migrated : 0); if (!history) return; @@ -2998,9 +2997,9 @@ void OverviewWidget::changingMsgId(HistoryItem *row, MsgId newId) { } } -void OverviewWidget::msgUpdated(const HistoryItem *msg) { +void OverviewWidget::notify_redrawHistoryItem(const HistoryItem *msg) { if (peer() == msg->history()->peer || migratePeer() == msg->history()->peer) { - _inner.msgUpdated(msg); + _inner.redrawItem(msg); } } diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index 5955272e4..91154ef29 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -69,7 +69,7 @@ public: void mediaOverviewUpdated(bool fromResize = false); void changingMsgId(HistoryItem *row, MsgId newId); - void msgUpdated(const HistoryItem *msg); + void redrawItem(const HistoryItem *msg); void itemRemoved(HistoryItem *item); void itemResized(HistoryItem *item, bool scrollToIt); @@ -123,8 +123,7 @@ private: void updateDragSelection(MsgId dragSelFrom, int32 dragSelFromIndex, MsgId dragSelTo, int32 dragSelToIndex, bool dragSelecting); - void updateMsg(HistoryItem *item); - void updateMsg(MsgId itemId, int32 itemIndex); + void redrawItem(MsgId itemId, int32 itemIndex); void touchResetSpeed(); void touchUpdateSpeed(); @@ -314,7 +313,7 @@ public: void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type); void changingMsgId(HistoryItem *row, MsgId newId); - void msgUpdated(const HistoryItem *msg); + void notify_redrawHistoryItem(const HistoryItem *msg); void itemRemoved(HistoryItem *item); void itemResized(HistoryItem *row, bool scrollToIt); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 65387d7df..8e02cf416 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "settings.h" #include "lang.h" -bool gRtl = false; +bool gRtl = true;// false; Qt::LayoutDirection gLangDir = gRtl ? Qt::RightToLeft : Qt::LeftToRight; mtpDcOptions gDcOptions; diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index ba02bc189..3290ebb58 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -1123,6 +1123,10 @@ struct DocumentData { } void recountIsImage(); + float64 progress() const { + return loader ? loader->currentProgress() : ((status == FileDownloadFailed || (_location.name().isEmpty() && data.isEmpty())) ? 0 : 1); + } + DocumentId id; DocumentType type; QSize dimensions; From 2225abec5c966054353a7372a878f1c9d21b7427 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 13 Dec 2015 01:29:33 +0300 Subject: [PATCH 017/145] voice messages and shared contacts redesigned (contacts not realtime-updated yet) --- Telegram/Resources/style.txt | 5 +- Telegram/SourceFiles/app.cpp | 36 +- Telegram/SourceFiles/app.h | 15 +- Telegram/SourceFiles/facades.cpp | 8 + Telegram/SourceFiles/facades.h | 3 + Telegram/SourceFiles/history.cpp | 742 +++++++++++++++---------- Telegram/SourceFiles/history.h | 203 +++++-- Telegram/SourceFiles/historywidget.cpp | 16 +- Telegram/SourceFiles/historywidget.h | 1 + Telegram/SourceFiles/mainwidget.cpp | 17 +- Telegram/SourceFiles/mainwidget.h | 3 +- Telegram/SourceFiles/settings.cpp | 2 +- Telegram/SourceFiles/structs.cpp | 26 +- Telegram/SourceFiles/structs.h | 17 + Telegram/SourceFiles/types.h | 10 + 15 files changed, 721 insertions(+), 383 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index b364431e9..bbec08d02 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1186,12 +1186,13 @@ mediaInUnreadFg: #999; mediaInUnreadFgSelected: #7b95aa; mediaOutUnreadFg: #6aad60; mediaOutUnreadFgSelected: #5aa382; -mediaUnreadSize: 4px; +mediaUnreadSize: 7px; mediaUnreadSkip: 5px; +mediaUnreadTop: 6px; msgFileMenuSize: size(36px, 36px); msgFileSize: 44px; -msgFilePadding: margins(14px, 12px, 10px, 12px); +msgFilePadding: margins(14px, 12px, 11px, 12px); msgFileThumbSize: 72px; msgFileThumbPadding: margins(10px, 10px, 14px, 10px); msgFileThumbNameTop: 12px; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 7d6663e9f..6c8c88b43 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -73,12 +73,11 @@ namespace { AudioItems audioItems; DocumentItems documentItems; WebPageItems webPageItems; + SharedContactItems sharedContactItems; + typedef QMap > RepliesTo; RepliesTo repliesTo; - typedef QMap SharedContactPhones; - SharedContactPhones sharedContactPhones; - Histories histories; typedef QHash MsgsData; @@ -1997,7 +1996,7 @@ namespace App { ::audioItems.clear(); ::documentItems.clear(); ::webPageItems.clear(); - ::sharedContactPhones.clear(); + ::sharedContactItems.clear(); ::repliesTo.clear(); lastPhotos.clear(); lastPhotosMap.clear(); @@ -2373,7 +2372,7 @@ namespace App { } void regVideoItem(VideoData *data, HistoryItem *item) { - ::videoItems[data][item] = true; + ::videoItems[data].insert(item, NullType()); } void unregVideoItem(VideoData *data, HistoryItem *item) { @@ -2385,7 +2384,7 @@ namespace App { } void regAudioItem(AudioData *data, HistoryItem *item) { - ::audioItems[data][item] = true; + ::audioItems[data].insert(item, NullType()); } void unregAudioItem(AudioData*data, HistoryItem *item) { @@ -2397,7 +2396,7 @@ namespace App { } void regDocumentItem(DocumentData *data, HistoryItem *item) { - ::documentItems[data][item] = true; + ::documentItems[data].insert(item, NullType()); } void unregDocumentItem(DocumentData *data, HistoryItem *item) { @@ -2409,7 +2408,7 @@ namespace App { } void regWebPageItem(WebPageData *data, HistoryItem *item) { - ::webPageItems[data][item] = true; + ::webPageItems[data].insert(item, NullType()); } void unregWebPageItem(WebPageData *data, HistoryItem *item) { @@ -2420,12 +2419,27 @@ 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; } 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) { diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index b4d2ae7c9..23ab34bd3 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -33,11 +33,12 @@ class FileUploader; #include "history.h" -typedef QMap HistoryItemsMap; -typedef QHash VideoItems; -typedef QHash AudioItems; -typedef QHash DocumentItems; -typedef QHash WebPageItems; +typedef QMap HistoryItemsMap; +typedef QMap VideoItems; +typedef QMap AudioItems; +typedef QMap DocumentItems; +typedef QMap WebPageItems; +typedef QMap SharedContactItems; struct ReplyMarkup { ReplyMarkup(int32 flags = 0) : flags(flags) { } @@ -244,7 +245,9 @@ namespace App { 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 regMuted(PeerData *peer, int32 changeIn); diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 6cf973195..2a6f40c72 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -91,6 +91,10 @@ namespace Ui { return false; } + void showPeerHistory(const PeerId &peer, MsgId msgId, bool back) { + if (MainWidget *m = App::main()) m->showPeerHistory(peer, msgId, back); + } + } namespace Notify { @@ -111,4 +115,8 @@ namespace Notify { if (MainWidget *m = App::main()) m->notify_redrawHistoryItem(item); } + void historyItemLayoutChanged(const HistoryItem *item) { + if (MainWidget *m = App::main()) m->notify_historyItemLayoutChanged(item); + } + } diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 790c990de..9ba8cb66b 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -46,6 +46,8 @@ namespace Ui { // it doesn't allow me to use UI :( void hideLayer(bool fast = false); bool isLayerShown(); + void showPeerHistory(const PeerId &peer, MsgId msgId, bool back = false); + }; namespace Notify { @@ -54,5 +56,6 @@ namespace Notify { void botCommandsChanged(UserData *user); void migrateUpdated(PeerData *peer); void redrawHistoryItem(const HistoryItem *item); + void historyItemLayoutChanged(const HistoryItem *item); }; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index d32537a5d..e1fc158f1 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -29,6 +29,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "window.h" #include "gui/filedialog.h" +#include "boxes/addcontactbox.h" + #include "audio.h" #include "localstorage.h" @@ -3106,22 +3108,22 @@ RadialAnimation::RadialAnimation(int32 thickness, AnimationCallbacks *callbacks) , _lastTime(0) , _opacity(0) , a_arcEnd(0, 0) -, a_arcStart(0, 1) +, a_arcStart(0, FullArcLength) , _animation(callbacks) { } void RadialAnimation::start(float64 prg) { _firstStart = _lastStart = _lastTime = getms(); - a_arcEnd = anim::fvalue(prg, qMax(prg, 0.0001)); + 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) { - if (prg < 0.0001) prg = 0.0001; - - if (prg != a_arcEnd.to()) { - a_arcEnd.start(prg); + int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength); + if (iprg != a_arcEnd.to()) { + a_arcEnd.start(iprg); _lastStart = _lastTime; } _lastTime = ms; @@ -3144,13 +3146,15 @@ void RadialAnimation::update(float64 prg, bool finished, uint64 ms) { void RadialAnimation::stop() { _firstStart = _lastStart = _lastTime = 0; - a_arcEnd = anim::fvalue(0, 0); + a_arcEnd = anim::ivalue(0, 0); _animation.stop(); } -void RadialAnimation::draw(Painter &p, const QRect &inner, const style::color &color) { - p.setRenderHint(QPainter::HighQualityAntialiasing); +void RadialAnimation::step(uint64 ms) { + _animation.step(ms); +} +void RadialAnimation::draw(Painter &p, const QRect &inner, const style::color &color) { float64 o = p.opacity(); p.setOpacity(o * _opacity); @@ -3158,12 +3162,19 @@ void RadialAnimation::draw(Painter &p, const QRect &inner, const style::color &c pen.setWidth(_thickness); p.setPen(pen); - int32 len = 16 + a_arcEnd.current() * 5744; - p.drawArc(inner, 1440 - a_arcStart.current() * 5760 - len, len); + 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); - p.setRenderHint(QPainter::HighQualityAntialiasing, false); } HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent) : HistoryMedia() @@ -3178,12 +3189,13 @@ HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, Histo init(); } -HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryMedia(width) +HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryMedia() , _data(App::feedPhoto(photo)) , _openl(new PhotoLink(_data, chat)) , _pixw(1) , _pixh(1) , _caption(st::minPhotoSize) { + w = width; init(); } @@ -3323,7 +3335,7 @@ HistoryMedia *HistoryPhoto::clone() const { return new HistoryPhoto(*this); } -void HistoryPhoto::updateFrom(const MTPMessageMedia &media) { +void HistoryPhoto::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { if (media.type() == mtpc_messageMediaPhoto) { const MTPPhoto &photo(media.c_messageMediaPhoto().vphoto); if (photo.type() == mtpc_photo) { @@ -3697,128 +3709,286 @@ ImagePtr HistoryVideo::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); +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) : formatSizeText(fullSize); + } else if (_statusSize == FileStatusSizeLoaded) { + _statusText = (duration >= 0) ? formatDurationText(duration) : 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 { + _animation->a_thumbOver.update(dt, anim::linear); + } + if (timer) { + Notify::redrawHistoryItem(parent); + } +} + +void HistoryFileMedia::step_radial(const HistoryItem *parent, uint64 ms, bool timer) { + _animation->radial.update(dataProgress(), dataFinished(), ms); + if (!_animation->radial.animating()) { + checkAnimationFinished(); + } + if (timer) { + Notify::redrawHistoryItem(parent); + } +} + +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() { + if (_animation) { + delete _animation; + setBadLink(_animation); + } +} + +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); + } + + 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 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; + } +} + +HistoryAudio::HistoryAudio(const MTPDaudio &audio) : HistoryFileMedia() +, _data(App::feedAudio(audio)) { + setLinks(new AudioOpenLink(_data), new AudioSaveLink(_data), new AudioCancelLink(_data)); + + setStatusSize(FileStatusSizeReady); +} + +void HistoryAudio::setStatusSize(int32 newSize, qint64 realDuration) const { + HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration, realDuration); +} + +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->loader) { + statusSize = _data->loader->currentOffset(); + } else if (!_data->already().isEmpty() || !_data->data.isEmpty()) { + AudioMsgId 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; + } + } else { + statusSize = FileStatusSizeReady; + } + if (statusSize != _statusSize) { + setStatusSize(statusSize, realDuration); + } + return showPause; } void HistoryAudio::initDimensions(const HistoryItem *parent) { - _maxw = st::msgMaxWidth; - int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); + _maxw = st::msgFileMinWidth; - _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); - _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, const QRect &r, bool selected, uint64 ms) const { - int32 width = w, skipy = 0, replyFrom = 0, fwdFrom = 0; - if (width < 1) return; + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; + + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); + + if (!_data->loader && _data->status == FileReady && !already && !hasdata && _data->size < AudioVoiceMsgInMemory) { + _data->save(QString()); + } - 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; } - if (!data->loader && data->status == FileReady && !already && !hasdata && data->size < AudioVoiceMsgInMemory) { - data->save(QString()); - } - - style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg)); - style::color sh(outbg ? (selected ? st::msgOutShadowSelected : st::msgOutShadow) : (selected ? st::msgInShadowSelected : st::msgInShadow)); - RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners)); - App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); - - AudioMsgId playing; - AudioPlayerState playingState = AudioPlayerStopped; - int64 playingPosition = 0, playingDuration = 0; - int32 playingFrequency = 0; - if (audioPlayer()) { - audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency); - } - - QRect img; - QString statusText; - if (data->status == FileUploadFailed || data->status == FileDownloadFailed) { - 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); + if (_data->loader) { + ensureAnimation(parent); + if (!_animation->radial.animating()) { + _animation->radial.start(_data->progress()); } - 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; } + bool showPause = updateStatusText(parent); + bool radial = isRadialAnimation(ms); - //p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), img); + int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; + + 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.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 (_animation && _animation->_a_thumbOver.animating()) { + _animation->_a_thumbOver.step(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->loader ? _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::normalFont->f); - p.setPen(st::black->c); - p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->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, bg); + } + + style::sprite icon; + if (showPause) { + icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause); + } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { + icon = outbg ? (selected ? st::msgFileOutPlaySelected : st::msgFileOutPlay) : (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); + } else if (_data->loader) { + icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } 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.setPen(status->p); - int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height; - p.drawText(tleft, texty + st::normalFont->ascent, statusText); + p.setFont(st::normalFont); + p.setPen(status); + p.drawTextLeft(nameleft, statustop, width, _statusText); + if (parent->isMediaUnread()) { - int32 w = st::normalFont->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::mediaOutUnreadFgSelected : st::mediaOutUnreadFg) : (selected ? st::mediaInUnreadFgSelected : st::mediaInUnreadFg))->b); - p.drawEllipse(QRect(tleft + w + st::mediaUnreadSkip, texty + ((st::normalFont->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); } } } void HistoryAudio::regItem(HistoryItem *item) { - App::regAudioItem(data, item); + App::regAudioItem(_data, item); } void HistoryAudio::unregItem(HistoryItem *item) { - App::unregAudioItem(data, item); + App::unregAudioItem(_data, item); } -void HistoryAudio::updateFrom(const MTPMessageMedia &media) { +void HistoryAudio::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { 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); + App::feedAudio(media.c_messageMediaAudio().vaudio, _data); + if (!_data->data.isEmpty()) { + Local::writeAudio(mediaKey(mtpToLocationType(mtpc_inputAudioFileLocation), _data->dc, _data->id), _data->data); } } } @@ -3843,21 +4013,25 @@ void HistoryAudio::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x if (width < 0) width = w; if (width < 1) return; - int skipy = 0, replyFrom = 0, fwdFrom = 0; + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + bool already = !_data->already().isEmpty(), hasdata = !_data->data.isEmpty(); - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; if (width >= _maxw) { width = _maxw; } - if (x >= 0 && y >= skipy && x < width && y < _height && !data->loader && data->access) { + 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->loader || _data->status == FileUploading || (!already && !hasdata)) && inner.contains(x, y)) { + lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; + return; + } + + if (x >= 0 && y >= 0 && x < width && y < _height && !_data->loader && _data->access) { lnk = _openl; - - bool inDate = parent->pointInTime(width, _height, x, y, InfoDisplayDefault); - if (inDate) { - state = HistoryInDateCursorState; - } - return; } } @@ -3866,36 +4040,13 @@ HistoryMedia *HistoryAudio::clone() const { return new HistoryAudio(*this); } -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); - } - - 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; - } -} - -HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia() +HistoryDocument::HistoryDocument(DocumentData *document) : HistoryFileMedia() , _data(document) -, _openl(new DocumentOpenLink(_data)) -, _savel(new DocumentSaveLink(_data)) -, _thumbsavel(new DocumentSaveLink(_data)) -, _cancell(new DocumentCancelLink(_data)) -, _name(documentName(_data)) -, _animation(0) { +, _linksavel(new DocumentSaveLink(_data)) +, _linkcancell(new DocumentCancelLink(_data)) +, _name(documentName(_data)) { + setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); + if (_name.isEmpty()) _name = qsl("Unknown File"); _namew = st::semiboldFont->width(_name); @@ -3915,21 +4066,17 @@ HistoryDocument::HistoryDocument(DocumentData *document) : HistoryMedia() } void HistoryDocument::setStatusSize(int32 newSize, qint64 realDuration) const { - _statusSize = newSize; + HistoryFileMedia::setStatusSize(newSize, _data->size, _data->song() ? _data->song()->duration : -1, realDuration); + if (_statusSize == FileStatusSizeReady) { - _statusText = _data->song() ? formatDurationAndSizeText(_data->song()->duration, _data->size) : formatSizeText(_data->size); _link = lang(lng_media_download).toUpper(); } else if (_statusSize == FileStatusSizeLoaded) { - _statusText = _data->song() ? formatDurationText(_data->song()->duration) : formatSizeText(_data->size); _link = lang(lng_media_open_with).toUpper(); } else if (_statusSize == FileStatusSizeFailed) { - _statusText = lang(lng_attach_failed); _link = lang(lng_media_download).toUpper(); } else if (_statusSize >= 0) { - _statusText = formatDownloadText(_statusSize, _data->size); _link = lang(lng_media_cancel).toUpper(); } else { - _statusText = formatPlayedText(-_statusSize - 1, realDuration); _link = lang(lng_media_open_with).toUpper(); } _linkw = st::semiboldFont->width(_link); @@ -3992,7 +4139,7 @@ void HistoryDocument::initDimensions(const HistoryItem *parent) { _maxw = qMax(tleft + _namew + tright, _maxw); _maxw = qMin(_maxw, int(st::msgMaxWidth)); - if (withThumb()) { + if (wthumb) { _height = _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); } else { _height = _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); @@ -4010,15 +4157,14 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r width = _maxw; } - bool showPause = updateStatusText(parent); - bool radial = _animation && _animation->radial.animating(); - if (_data->loader) { ensureAnimation(parent); if (!_animation->radial.animating()) { _animation->radial.start(_data->progress()); } } + bool showPause = updateStatusText(parent); + bool radial = isRadialAnimation(ms); int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; bool wthumb = withThumb(); @@ -4050,11 +4196,12 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r p.setOpacity(st::msgDateImgBg->c.alphaF() * _animation->radial.opacity()); p.setBrush(st::black); } else if (_animation && _animation->_a_thumbOver.animating()) { + _animation->_a_thumbOver.step(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(_thumbsavel); + bool over = textlnkDrawOver(_data->loader ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } @@ -4079,7 +4226,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r } if (_data->status != FileUploadFailed) { - const TextLinkPtr &lnk((_data->loader || _data->status == FileUploading) ? _cancell : _savel); + const TextLinkPtr &lnk((_data->loader || _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)); @@ -4099,7 +4246,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r 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(_thumbsavel); + bool over = textlnkDrawOver(_data->loader ? _cancell : _savel); p.setBrush(outbg ? (over ? st::msgFileOutBgOver : st::msgFileOutBg) : (over ? st::msgFileInBgOver : st::msgFileInBg)); } @@ -4215,7 +4362,7 @@ void HistoryDocument::unregItem(HistoryItem *item) { App::unregDocumentItem(_data, item); } -void HistoryDocument::updateFrom(const MTPMessageMedia &media) { +void HistoryDocument::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { if (media.type() == mtpc_messageMediaDocument) { App::feedDocument(media.c_messageMediaDocument().vdocument, _data); } @@ -4275,21 +4422,21 @@ void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int3 if (already || hasdata) { } else { if (rthumb.contains(x, y)) { - lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _thumbsavel; + lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; return; } } if (_data->status != FileUploadFailed) { if (rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, width).contains(x, y)) { - lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; + lnk = (_data->loader || _data->status == FileUploading) ? _linkcancell : _linksavel; return; } } } else { QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width)); - if (_data->loader || _data->status == FileUploading || (!already && !hasdata) && inner.contains(x, y)) { - lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _thumbsavel; + if ((_data->loader || _data->status == FileUploading || (!already && !hasdata)) && inner.contains(x, y)) { + lnk = (_data->loader || _data->status == FileUploading) ? _cancell : _savel; return; } } @@ -4299,63 +4446,6 @@ void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int3 } } -void HistoryDocument::linkOver(HistoryItem *parent, const TextLinkPtr &lnk) { - if (lnk == _thumbsavel && _data->already().isEmpty() && _data->data.isEmpty()) { - ensureAnimation(parent); - _animation->a_thumbOver.start(1); - _animation->_a_thumbOver.start(); - } -} - -void HistoryDocument::linkOut(HistoryItem *parent, const TextLinkPtr &lnk) { - if (_animation && lnk == _thumbsavel) { - _animation->a_thumbOver.start(0); - _animation->_a_thumbOver.start(); - } -} - -void HistoryDocument::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 { - _animation->a_thumbOver.update(dt, anim::linear); - } - if (timer) { - Notify::redrawHistoryItem(parent); - } -} - -void HistoryDocument::step_radial(const HistoryItem *parent, uint64 ms, bool timer) { - _animation->radial.update(_data->progress(), !_data->loader, ms); - if (!_animation->radial.animating()) { - checkAnimationFinished(); - } - if (timer) { - Notify::redrawHistoryItem(parent); - } -} - -void HistoryDocument::ensureAnimation(const HistoryItem *parent) const { - if (!_animation) { - _animation = new AnimationData( - animation(parent, const_cast(this), &HistoryDocument::step_thumbOver), - animation(parent, const_cast(this), &HistoryDocument::step_radial) - ); - } -} - -void HistoryDocument::checkAnimationFinished() { - if (_animation && !_animation->_a_thumbOver.animating() && !_animation->radial.animating()) { - if (!_data->already().isEmpty() || !_data->data.isEmpty()) { - delete _animation; - _animation = 0; - } - } -} - HistoryMedia *HistoryDocument::clone() const { return new HistoryDocument(*this); } @@ -4364,11 +4454,6 @@ ImagePtr HistoryDocument::replyPreview() { return _data->makeReplyPreview(); } -HistoryDocument::~HistoryDocument() { - delete _animation; - _animation = 0; -} - HistoryGif::HistoryGif(DocumentData *document) : HistoryMedia() , _data(document) , _openl(new DocumentOpenLink(_data)) @@ -4422,7 +4507,7 @@ void HistoryGif::unregItem(HistoryItem *item) { App::unregDocumentItem(_data, item); } -void HistoryGif::updateFrom(const MTPMessageMedia &media) { +void HistoryGif::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { if (media.type() == mtpc_messageMediaDocument) { App::feedDocument(media.c_messageMediaDocument().vdocument, _data); } @@ -4608,7 +4693,7 @@ void HistorySticker::unregItem(HistoryItem *item) { App::unregDocumentItem(data, item); } -void HistorySticker::updateFrom(const MTPMessageMedia &media) { +void HistorySticker::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { if (media.type() == mtpc_messageMediaDocument) { App::feedDocument(media.c_messageMediaDocument().vdocument, data); if (!data->data.isEmpty()) { @@ -4667,49 +4752,75 @@ 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::msgMaxWidth; - name.setText(st::normalFont, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions); - - phonew = st::normalFont->width(phone); - - if (contact) { - contact->photo->load(); +void SendMessageLink::onClick(Qt::MouseButton button) const { + if (button == Qt::LeftButton) { + Ui::showPeerHistory(peer()->id, ShowAtUnreadMsgId); } } -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); - - _maxw = st::msgMaxWidth; - name.setText(st::normalFont, fullname.trimmed(), _textNameOptions); - - phonew = st::normalFont->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 &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); + + _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(); + } + _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()); + } + + _maxw = qMax(tleft + _name.maxWidth() + tright, _maxw); + _maxw = qMin(_maxw, int(st::msgMaxWidth)); + + if (_userId) { + _height = _minh = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); + } else { + _height = _minh = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); } - _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); - _height = _minh; } const QString HistoryContact::inDialogsText() const { @@ -4717,33 +4828,59 @@ const QString HistoryContact::inDialogsText() const { } 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); + if (width >= _maxw) { + width = _maxw; + } + return (x >= 0 && y >= 0 && x < width && y < _height); } void HistoryContact::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const { if (width < 0) width = w; + if (width < 1) return; - int skipy = 0, replyFrom = 0, fwdFrom = 0; + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; - if (x >= 0 && y >= skipy && x < width && y < _height && contact) { - lnk = contact->lnk; + if (width >= _maxw) { + width = _maxw; + } - bool inDate = parent->pointInTime(width, _height, x, y, InfoDisplayDefault); - if (inDate) { - state = HistoryInDateCursorState; + 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; + + QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width)); + + 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; } } HistoryMedia *HistoryContact::clone() const { - return new HistoryContact(userId, name.original(), phone); + return new HistoryContact(_userId, _fname, _lname, _phone); +} + +void HistoryContact::regItem(HistoryItem *item) { + if (_userId) { + App::regSharedContactItem(_userId, item); + } +} + +void HistoryContact::unregItem(HistoryItem *item) { + if (_userId) { + App::unregSharedContactItem(_userId, item); + } } void HistoryContact::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { @@ -4751,50 +4888,63 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, const QRect &r, int32 width = w, height = _height, skipx = 0, skipy = 0; bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + if (width >= _maxw) { width = _maxw; } - style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg)); - style::color sh(outbg ? (selected ? st::msgOutShadowSelected : st::msgOutShadow) : (selected ? st::msgInShadowSelected : st::msgInShadow)); - RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners)); - App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); + 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; - 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); - } + 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 : (_userId % UserColorsCount))->pixRounded(st::msgFileThumbSize, st::msgFileThumbSize)); + } + if (selected) { + App::roundRect(p, rthumb, 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::normalFont->f); - p.setPen(st::black->c); - if (twidth < phonew) { - p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(phone, twidth)); + 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 { - p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, phone); + 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(0)->pix(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.setPen(status->p); - - name.drawElided(p, tleft, skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height, secondwidth); + p.setFont(st::normalFont); + p.setPen(status); + p.drawTextLeft(nameleft, statustop, width, _phone); } -void HistoryContact::updateFrom(const MTPMessageMedia &media) { +void HistoryContact::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { 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); + parent->initDimensions(); + if (allowEmitResize && App::main()) App::main()->itemResized(parent); } } } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 7efe44a1e..85e029dc3 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1132,6 +1132,11 @@ public: 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, const style::color &color); private: @@ -1139,7 +1144,7 @@ private: int32 _thickness; uint64 _firstStart, _lastStart, _lastTime; float64 _opacity; - anim::fvalue a_arcEnd, a_arcStart; + anim::ivalue a_arcEnd, a_arcStart; Animation _animation; }; @@ -1147,7 +1152,7 @@ private: class HistoryMedia : public HistoryElem { public: - HistoryMedia(int32 width = 0) : w(width) { + HistoryMedia() : w(0) { } HistoryMedia(const HistoryMedia &other) : w(0) { } @@ -1184,7 +1189,7 @@ public: virtual void unregItem(HistoryItem *item) { } - virtual void updateFrom(const MTPMessageMedia &media) { + virtual void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { } virtual bool isImageLink() const { @@ -1247,7 +1252,7 @@ public: return _data; } - void updateFrom(const MTPMessageMedia &media); + void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize); TextLinkPtr lnk() const { return _openl; @@ -1353,7 +1358,63 @@ private: mutable int32 _dldDone, _uplDone; }; -class HistoryAudio : public HistoryMedia { +class HistoryFileMedia : public HistoryMedia { +public: + + 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; + + 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(); + } + + virtual float64 dataProgress() const = 0; + virtual bool dataFinished() const = 0; + virtual bool dataLoaded() const = 0; + + struct AnimationData { + AnimationData(AnimationCallbacks *thumbOverCallbacks, AnimationCallbacks *radialCallbacks) : a_thumbOver(0, 0) + , _a_thumbOver(thumbOverCallbacks) + , radial(st::msgFileRadialLine, radialCallbacks) { + } + anim::fvalue a_thumbOver; + Animation _a_thumbOver; + + RadialAnimation radial; + }; + mutable AnimationData *_animation; + +}; + +class HistoryAudio : public HistoryFileMedia { public: HistoryAudio(const MTPDaudio &audio); @@ -1368,18 +1429,18 @@ public: 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->status == FileUploading); } 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 allowEmitResize); bool needsBubble(const HistoryItem *parent) const { return true; @@ -1388,17 +1449,26 @@ public: return false; } +protected: + + float64 dataProgress() const { + return _data->progress(); + } + bool dataFinished() const { + return !_data->loader; + } + bool dataLoaded() const { + return !_data->already().isEmpty() || !_data->data.isEmpty(); + } + private: - AudioData *data; - TextLinkPtr _openl, _savel, _cancell; + AudioData *_data; + void setStatusSize(int32 newSize, qint64 realDuration = 0) const; + bool updateStatusText(const HistoryItem *parent) const; // returns showPause - QString _size; - - mutable QString _dldTextCache, _uplTextCache; - mutable int32 _dldDone, _uplDone; }; -class HistoryDocument : public HistoryMedia { +class HistoryDocument : public HistoryFileMedia { public: HistoryDocument(DocumentData *document); @@ -1421,8 +1491,6 @@ public: return (_data->status == FileUploading); } void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; - void linkOver(HistoryItem *parent, const TextLinkPtr &lnk); - void linkOut(HistoryItem *parent, const TextLinkPtr &lnk); HistoryMedia *clone() const; DocumentData *document() { @@ -1432,7 +1500,7 @@ public: void regItem(HistoryItem *item); void unregItem(HistoryItem *item); - void updateFrom(const MTPMessageMedia &media); + void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize); bool hasReplyPreview() const { return !_data->thumb->isNull(); @@ -1452,46 +1520,33 @@ public: return _data->song(); } - ~HistoryDocument(); +protected: + + float64 dataProgress() const { + return _data->progress(); + } + bool dataFinished() const { + return !_data->loader; + } + bool dataLoaded() const { + return !_data->already().isEmpty() || !_data->data.isEmpty(); + } private: DocumentData *_data; - TextLinkPtr _openl, _savel, _thumbsavel, _cancell; + TextLinkPtr _linksavel, _linkcancell; int32 _namew; QString _name; int32 _thumbw; - // >= 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, _linkw; - mutable QString _statusText, _link; + mutable int32 _linkw; + mutable QString _link; void setStatusSize(int32 newSize, qint64 realDuration = 0) const; bool updateStatusText(const HistoryItem *parent) const; // returns showPause - 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(); - - struct AnimationData { - AnimationData(AnimationCallbacks *thumbOverCallbacks, AnimationCallbacks *radialCallbacks) : a_thumbOver(0, 0) - , _a_thumbOver(thumbOverCallbacks) - , radial(st::msgFileRadialLine, radialCallbacks) { - } - anim::fvalue a_thumbOver; - Animation _a_thumbOver; - - RadialAnimation radial; - }; - mutable AnimationData *_animation; - }; class HistoryGif : public HistoryMedia { @@ -1522,7 +1577,7 @@ public: void regItem(HistoryItem *item); void unregItem(HistoryItem *item); - void updateFrom(const MTPMessageMedia &media); + void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize); bool hasReplyPreview() const { return !_data->thumb->isNull(); @@ -1578,7 +1633,7 @@ public: void regItem(HistoryItem *item); void unregItem(HistoryItem *item); - void updateFrom(const MTPMessageMedia &media); + void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize); bool needsBubble(const HistoryItem *parent) const { return false; @@ -1596,11 +1651,30 @@ private: }; +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; + +}; + 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, const QRect &r, bool selected, uint64 ms) const; @@ -1613,7 +1687,10 @@ public: 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 allowEmitResize); bool needsBubble(const HistoryItem *parent) const { return true; @@ -1622,12 +1699,28 @@ public: 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 { @@ -1841,7 +1934,7 @@ public: void updateMedia(const MTPMessageMedia *media, bool allowEmitResize) { if (media && _media && _media->type() != MediaTypeWebPage) { - _media->updateFrom(*media); + _media->updateFrom(*media, this, allowEmitResize); } else { setMedia(media, allowEmitResize); } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index a9a553031..b6bb34fc0 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -1527,9 +1527,11 @@ void HistoryInner::onUpdateSelected() { App::mousedItem(item); m = mapMouseToItem(point, item); if (item->hasPoint(m.x(), m.y())) { - redrawItem(App::hoveredItem()); - App::hoveredItem(item); - redrawItem(App::hoveredItem()); + if (App::hoveredItem() != item) { + redrawItem(App::hoveredItem()); + App::hoveredItem(item); + redrawItem(App::hoveredItem()); + } } else if (App::hoveredItem()) { redrawItem(App::hoveredItem()); App::hoveredItem(0); @@ -1670,7 +1672,7 @@ void HistoryInner::onUpdateSelected() { _widget->noSelectingScroll(); } - if (lnkChanged || cur != _cursor) { + if (_dragAction == NoDrag && (lnkChanged || cur != _cursor)) { setCursor(_cursor = cur); } } @@ -5608,6 +5610,12 @@ void HistoryWidget::notify_redrawHistoryItem(const HistoryItem *item) { } } +void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { + if (_peer && _list && (item == App::mousedItem() || item == App::hoveredItem() || item == App::hoveredLinkItem())) { + _list->onUpdateSelected(); + } +} + void HistoryWidget::resizeEvent(QResizeEvent *e) { _reportSpamPanel.resize(width(), _reportSpamPanel.height()); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 9a76a7412..9009e9c2d 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -431,6 +431,7 @@ public: void peerMessagesUpdated(); void notify_redrawHistoryItem(const HistoryItem *item); + void notify_historyItemLayoutChanged(const HistoryItem *item); void newUnreadMsg(History *history, HistoryItem *item); void historyToDown(History *history); void historyWasRead(bool force = true); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index e3e5e325b..b68f1f966 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -758,13 +758,18 @@ void MainWidget::notify_migrateUpdated(PeerData *peer) { history.notify_migrateUpdated(peer); } -void MainWidget::notify_redrawHistoryItem(const HistoryItem *msg) { - if (!msg) return; - history.notify_redrawHistoryItem(msg); - if (!msg->history()->dialogs.isEmpty() && msg->history()->lastMsg == msg) { - dialogs.dlgUpdated(msg->history()->dialogs[0]); +void MainWidget::notify_redrawHistoryItem(const HistoryItem *item) { + if (!item) return; + + history.notify_redrawHistoryItem(item); + if (!item->history()->dialogs.isEmpty() && item->history()->lastMsg == item) { + dialogs.dlgUpdated(item->history()->dialogs[0]); } - if (overview) overview->notify_redrawHistoryItem(msg); + if (overview) overview->notify_redrawHistoryItem(item); +} + +void MainWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { + history.notify_historyItemLayoutChanged(item); } void MainWidget::noHider(HistoryHider *destroyed) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 1e143148b..8be8fda5c 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -389,7 +389,8 @@ public: void notify_botCommandsChanged(UserData *bot); void notify_userIsBotChanged(UserData *bot); void notify_migrateUpdated(PeerData *peer); - void notify_redrawHistoryItem(const HistoryItem *msg); + void notify_redrawHistoryItem(const HistoryItem *item); + void notify_historyItemLayoutChanged(const HistoryItem *item); void choosePeer(PeerId peerId, MsgId showAtMsgId); // does offerPeer or showPeerHistory void clearBotStartToken(PeerData *peer); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 8e02cf416..65387d7df 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "settings.h" #include "lang.h" -bool gRtl = true;// false; +bool gRtl = false; Qt::LayoutDirection gLangDir = gRtl ? Qt::RightToLeft : Qt::LeftToRight; mtpDcOptions gDcOptions; diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 5b241d640..e90d5f1fd 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -62,7 +62,7 @@ style::color peerColor(int32 index) { } ImagePtr userDefPhoto(int32 index) { - static const ImagePtr userDefPhotos[8] = { + static const ImagePtr userDefPhotos[UserColorsCount] = { ImagePtr(qsl(":/ava/art/usercolor1.png"), "PNG"), ImagePtr(qsl(":/ava/art/usercolor2.png"), "PNG"), ImagePtr(qsl(":/ava/art/usercolor3.png"), "PNG"), @@ -833,6 +833,18 @@ void AudioData::save(const QString &toFile) { loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(audioLoadProgress(mtpFileLoader*))); loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(audioLoadFailed(mtpFileLoader*, bool))); loader->start(); + + notifyLayoutChanged(); +} + +void AudioData::notifyLayoutChanged() const { + const AudioItems &items(App::audioItems()); + AudioItems::const_iterator i = items.constFind(const_cast(this)); + if (i != items.cend()) { + for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { + Notify::historyItemLayoutChanged(j.key()); + } + } } QString AudioData::already(bool check) { @@ -1035,6 +1047,18 @@ void DocumentData::save(const QString &toFile) { loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(documentLoadProgress(mtpFileLoader*))); loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(documentLoadFailed(mtpFileLoader*, bool))); loader->start(); + + notifyLayoutChanged(); +} + +void DocumentData::notifyLayoutChanged() const { + const DocumentItems &items(App::documentItems()); + DocumentItems::const_iterator i = items.constFind(const_cast(this)); + if (i != items.cend()) { + for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { + Notify::historyItemLayoutChanged(j.key()); + } + } } QString DocumentData::already(bool check) { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 3290ebb58..67c767cf2 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -186,6 +186,8 @@ inline bool isNotifyMuted(NotifySettingsPtr settings, int32 *changeIn = 0) { return false; } +static const int32 UserColorsCount = 8; + style::color peerColor(int32 index); ImagePtr userDefPhoto(int32 index); ImagePtr chatDefPhoto(int32 index); @@ -906,6 +908,8 @@ struct AudioData { l->cancel(); l->deleteLater(); l->rpcInvalidate(); + + notifyLayoutChanged(); } _location = FileLocation(); if (!beforeDownload) { @@ -922,7 +926,10 @@ struct AudioData { loader->deleteLater(); loader->rpcInvalidate(); loader = 0; + + notifyLayoutChanged(); } + void notifyLayoutChanged() const; QString already(bool check = false); const FileLocation &location(bool check = false); @@ -932,6 +939,10 @@ struct AudioData { } } + float64 progress() const { + return loader ? loader->currentProgress() : ((status == FileDownloadFailed || (_location.name().isEmpty() && data.isEmpty())) ? 0 : 1); + } + AudioId id; uint64 access; int32 date; @@ -1081,6 +1092,8 @@ struct DocumentData { l->cancel(); l->deleteLater(); l->rpcInvalidate(); + + notifyLayoutChanged(); } _location = FileLocation(); if (!beforeDownload) { @@ -1100,7 +1113,11 @@ struct DocumentData { loader->deleteLater(); loader->rpcInvalidate(); loader = 0; + + notifyLayoutChanged(); } + void notifyLayoutChanged() const; + ~DocumentData() { delete _additional; } diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index 3a998622f..cf0ede271 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -20,6 +20,11 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org */ #pragma once +template +void setBadLink(Type *&link) { + link = reinterpret_cast(0x00000bad); +} + struct NullType { }; @@ -453,3 +458,8 @@ enum ShowLayerOption { ForceFastShowLayer = 0x04, }; typedef QFlags ShowLayerOptions; + +static int32 FullArcLength = 360 * 16; +static int32 QuarterArcLength = (FullArcLength / 4); +static int32 MinArcLength = (FullArcLength / 360); +static int32 AlmostFullArcLength = (FullArcLength - MinArcLength); From 6ec61d4ad6d33e2624f3f30a29116847029afeb9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 13 Dec 2015 14:17:15 +0300 Subject: [PATCH 018/145] redrawHistoryItem moved to Ui, shared contact updates when is_contact changes --- Telegram/SourceFiles/app.cpp | 18 ++--- Telegram/SourceFiles/boxes/addcontactbox.cpp | 2 +- Telegram/SourceFiles/dialogswidget.cpp | 77 ++++++++------------ Telegram/SourceFiles/dialogswidget.h | 8 +- Telegram/SourceFiles/facades.cpp | 18 ++++- Telegram/SourceFiles/facades.h | 7 +- Telegram/SourceFiles/gui/animation.cpp | 2 +- Telegram/SourceFiles/history.cpp | 14 ++-- Telegram/SourceFiles/historywidget.cpp | 16 ++-- Telegram/SourceFiles/historywidget.h | 5 +- Telegram/SourceFiles/mainwidget.cpp | 55 ++++++++------ Telegram/SourceFiles/mainwidget.h | 18 ++--- Telegram/SourceFiles/overviewwidget.cpp | 2 +- Telegram/SourceFiles/overviewwidget.h | 3 +- 14 files changed, 126 insertions(+), 119 deletions(-) diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 6c8c88b43..d54927271 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -464,10 +464,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) { @@ -1112,17 +1110,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; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index ccd5f0eec..3ba4eac43 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -235,7 +235,7 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) { } } if (uid) { - App::main()->addNewContact(uid); + Notify::userIsContactChanged(App::userLoaded(peerFromUser(uid)), true); Ui::hideLayer(); } else { _save.hide(); diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 098981019..b84d3e90d 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -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); @@ -1096,9 +1086,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 +1100,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) { @@ -1884,6 +1878,15 @@ 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()) { @@ -2270,17 +2273,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; @@ -2537,13 +2529,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(); } diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index b35f51de9..eb0f1cfa0 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(); @@ -123,6 +121,8 @@ public: void updateNotifySettings(PeerData *peer); + void notify_userIsContactChanged(UserData *user, bool fromThisApp); + ~DialogsInner(); public slots: @@ -220,7 +220,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); @@ -251,7 +250,6 @@ public: void scrollToPeer(const PeerId &peer, MsgId msgId); void removeDialog(History *history); - void removeContact(UserData *user); DialogsIndexed &contactsList(); DialogsIndexed &dialogsList(); @@ -264,6 +262,8 @@ public: void updateNotifySettings(PeerData *peer); + void notify_userIsContactChanged(UserData *user, bool fromThisApp); + signals: void cancelled(); diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 2a6f40c72..621984dd5 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -91,10 +91,20 @@ namespace Ui { return false; } + void redrawHistoryItem(const HistoryItem *item) { + if (MainWidget *m = App::main()) m->ui_redrawHistoryItem(item); + } + void showPeerHistory(const PeerId &peer, MsgId msgId, bool back) { if (MainWidget *m = App::main()) m->showPeerHistory(peer, msgId, back); } + void showPeerHistoryAsync(const PeerId &peer, MsgId msgId) { + if (MainWidget *m = App::main()) { + QMetaObject::invokeMethod(m, SLOT(ui_showPeerHistoryAsync(quint64,qint32)), Qt::QueuedConnection, Q_ARG(quint64, peer), Q_ARG(qint32, msgId)); + } + } + } namespace Notify { @@ -103,6 +113,10 @@ namespace Notify { 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->notify_botCommandsChanged(user); } @@ -111,10 +125,6 @@ namespace Notify { if (MainWidget *m = App::main()) m->notify_migrateUpdated(peer); } - void redrawHistoryItem(const HistoryItem *item) { - if (MainWidget *m = App::main()) m->notify_redrawHistoryItem(item); - } - void historyItemLayoutChanged(const HistoryItem *item) { if (MainWidget *m = App::main()) m->notify_historyItemLayoutChanged(item); } diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 9ba8cb66b..3ba786d65 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -37,7 +37,7 @@ namespace App { }; -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(); @@ -46,16 +46,19 @@ namespace Ui { // it doesn't allow me to use UI :( void hideLayer(bool fast = false); bool isLayerShown(); + void redrawHistoryItem(const HistoryItem *item); + void showPeerHistory(const PeerId &peer, MsgId msgId, bool back = false); + void showPeerHistoryAsync(const PeerId &peer, MsgId msgId); }; namespace Notify { void userIsBotChanged(UserData *user); + void userIsContactChanged(UserData *user, bool fromThisApp = false); void botCommandsChanged(UserData *user); void migrateUpdated(PeerData *peer); - void redrawHistoryItem(const HistoryItem *item); void historyItemLayoutChanged(const HistoryItem *item); }; diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 89c28b3a2..e0d7c2308 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -154,7 +154,7 @@ void AnimatedGif::step_frame(float64 ms, bool timer) { frame = f; if (timer) { if (msg) { - Notify::redrawHistoryItem(msg); + Ui::redrawHistoryItem(msg); } else { emit updated(); } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index e1fc158f1..7713a5708 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -2975,7 +2975,7 @@ void ItemAnimations::step_animate(float64 ms, bool timer) { for (Animations::iterator i = _animations.begin(); i != _animations.end();) { const HistoryItem *item = i.key(); if (item->animating()) { - if (timer) Notify::redrawHistoryItem(item); + if (timer) Ui::redrawHistoryItem(item); ++i; } else { i = _animations.erase(i); @@ -3759,7 +3759,7 @@ void HistoryFileMedia::step_thumbOver(const HistoryItem *parent, float64 ms, boo _animation->a_thumbOver.update(dt, anim::linear); } if (timer) { - Notify::redrawHistoryItem(parent); + Ui::redrawHistoryItem(parent); } } @@ -3769,7 +3769,7 @@ void HistoryFileMedia::step_radial(const HistoryItem *parent, uint64 ms, bool ti checkAnimationFinished(); } if (timer) { - Notify::redrawHistoryItem(parent); + Ui::redrawHistoryItem(parent); } } @@ -4906,7 +4906,7 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, const QRect &r, QPixmap thumb = _contact->photo->pixRounded(st::msgFileThumbSize, st::msgFileThumbSize); p.drawPixmap(rthumb.topLeft(), thumb); } else { - p.drawPixmap(rthumb.topLeft(), userDefPhoto(_contact ? _contact->colorIndex : (_userId % UserColorsCount))->pixRounded(st::msgFileThumbSize, st::msgFileThumbSize)); + 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); @@ -4923,7 +4923,7 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, const QRect &r, statustop = st::msgFileStatusTop; QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width)); - p.drawPixmap(inner.topLeft(), userDefPhoto(0)->pix(st::msgFileSize, st::msgFileSize)); + p.drawPixmap(inner.topLeft(), userDefPhoto(qAbs(parent->id) % UserColorsCount)->pixRounded(st::msgFileSize, st::msgFileSize)); } int32 namewidth = width - nameleft - nameright; @@ -6566,7 +6566,7 @@ void HistoryMessage::setViewsCount(int32 count) { _viewsText = (_views >= 0) ? formatViewsCount(_views) : QString(); _viewsWidth = _viewsText.isEmpty() ? 0 : st::msgDateFont->width(_viewsText); if (was == _viewsWidth) { - Notify::redrawHistoryItem(this); + Ui::redrawHistoryItem(this); } else { if (_text.hasSkipBlock()) { _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); @@ -6582,7 +6582,7 @@ void HistoryMessage::setId(MsgId newId) { bool wasPositive = (id > 0), positive = (newId > 0); HistoryItem::setId(newId); if (wasPositive == positive) { - Notify::redrawHistoryItem(this); + Ui::redrawHistoryItem(this); } else { if (_text.hasSkipBlock()) { _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index b6bb34fc0..1a094fbf6 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -5451,7 +5451,7 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadPhoto, 0); } -// Notify::redrawHistoryItem(item); +// Ui::redrawHistoryItem(item); } } @@ -5473,7 +5473,7 @@ void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadFile, doc ? doc->uploadOffset : 0); } - Notify::redrawHistoryItem(item); + Ui::redrawHistoryItem(item); } } @@ -5484,7 +5484,7 @@ void HistoryWidget::onAudioProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadAudio, audio ? audio->uploadOffset : 0); } - Notify::redrawHistoryItem(item); + Ui::redrawHistoryItem(item); } } @@ -5495,7 +5495,7 @@ void HistoryWidget::onPhotoFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadPhoto, -1); } -// Notify::redrawHistoryItem(item); +// Ui::redrawHistoryItem(item); } } @@ -5506,7 +5506,7 @@ void HistoryWidget::onDocumentFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadFile, -1); } - Notify::redrawHistoryItem(item); + Ui::redrawHistoryItem(item); } } @@ -5517,7 +5517,7 @@ void HistoryWidget::onAudioFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadAudio, -1); } - Notify::redrawHistoryItem(item); + Ui::redrawHistoryItem(item); } } @@ -5604,7 +5604,7 @@ void HistoryWidget::peerMessagesUpdated() { if (_list) peerMessagesUpdated(_peer->id); } -void HistoryWidget::notify_redrawHistoryItem(const HistoryItem *item) { +void HistoryWidget::ui_redrawHistoryItem(const HistoryItem *item) { if (_peer && _list && (item->history() == _history || (_migrated && item->history() == _migrated))) { _list->redrawItem(item); } @@ -6543,7 +6543,7 @@ void HistoryWidget::onAnimActiveStep() { if (getms() - _animActiveStart > st::activeFadeInDuration + st::activeFadeOutDuration) { stopAnimActive(); } else { - Notify::redrawHistoryItem(item); + Ui::redrawHistoryItem(item); } } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 9009e9c2d..35b5b361c 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -430,8 +430,6 @@ public: void peerMessagesUpdated(PeerId peer); void peerMessagesUpdated(); - void notify_redrawHistoryItem(const HistoryItem *item); - void notify_historyItemLayoutChanged(const HistoryItem *item); void newUnreadMsg(History *history, HistoryItem *item); void historyToDown(History *history); void historyWasRead(bool force = true); @@ -559,6 +557,9 @@ public: resizeEvent(0); } + void ui_redrawHistoryItem(const HistoryItem *item); + + void notify_historyItemLayoutChanged(const HistoryItem *item); void notify_botCommandsChanged(UserData *user); void notify_userIsBotChanged(UserData *user); void notify_migrateUpdated(PeerData *peer); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index b68f1f966..e626a8623 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -754,18 +754,37 @@ void MainWidget::notify_userIsBotChanged(UserData *bot) { history.notify_userIsBotChanged(bot); } +void MainWidget::notify_userIsContactChanged(UserData *user, bool fromThisApp) { + if (!user) return; + + dialogs.notify_userIsContactChanged(user, fromThisApp); + + const SharedContactItems &items(App::sharedContactItems()); + SharedContactItems::const_iterator i = items.constFind(peerToUser(user->id)); + if (i != items.cend()) { + for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { + j.key()->initDimensions(); + Ui::redrawHistoryItem(j.key()); + } + } + + if (user->contact > 0 && fromThisApp) { + Ui::showPeerHistory(user->id, ShowAtTheEndMsgId); + } +} + void MainWidget::notify_migrateUpdated(PeerData *peer) { history.notify_migrateUpdated(peer); } -void MainWidget::notify_redrawHistoryItem(const HistoryItem *item) { +void MainWidget::ui_redrawHistoryItem(const HistoryItem *item) { if (!item) return; - history.notify_redrawHistoryItem(item); + history.ui_redrawHistoryItem(item); if (!item->history()->dialogs.isEmpty() && item->history()->lastMsg == item) { dialogs.dlgUpdated(item->history()->dialogs[0]); } - if (overview) overview->notify_redrawHistoryItem(item); + if (overview) overview->ui_redrawHistoryItem(item); } void MainWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { @@ -988,10 +1007,6 @@ void MainWidget::clearHistory(PeerData *peer) { MTP::send(MTPmessages_DeleteHistory(peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, peer)); } -void MainWidget::removeContact(UserData *user) { - dialogs.removeContact(user); -} - void MainWidget::addParticipants(PeerData *chatOrChannel, const QVector &users) { if (chatOrChannel->isChat()) { for (QVector::const_iterator i = users.cbegin(), e = users.cend(); i != e; ++i) { @@ -1488,7 +1503,7 @@ void MainWidget::itemResized(HistoryItem *row, bool scrollToIt) { if (overview) { overview->itemResized(row, scrollToIt); } - if (row) Notify::redrawHistoryItem(row); + if (row) Ui::redrawHistoryItem(row); } bool MainWidget::overviewFailed(PeerData *peer, const RPCError &error, mtpRequestId req) { @@ -1638,7 +1653,7 @@ void MainWidget::videoLoadProgress(mtpFileLoader *loader) { VideoItems::const_iterator i = items.constFind(video); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - Notify::redrawHistoryItem(j.key()); + Ui::redrawHistoryItem(j.key()); } } } @@ -1665,6 +1680,10 @@ void MainWidget::onDownloadPathSettings() { Ui::showLayer(box); } +void MainWidget::ui_showPeerHistoryAsync(quint64 peerId, qint32 showAtMsgId) { + Ui::showPeerHistory(peerId, showAtMsgId); +} + void MainWidget::videoLoadFailed(mtpFileLoader *loader, bool started) { loadFailed(loader, started, SLOT(videoLoadRetry())); VideoData *video = App::video(loader->objId()); @@ -1715,7 +1734,7 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) { AudioItems::const_iterator i = items.constFind(audio); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - Notify::redrawHistoryItem(j.key()); + Ui::redrawHistoryItem(j.key()); } } } @@ -1750,7 +1769,7 @@ void MainWidget::audioPlayProgress(const AudioMsgId &audioId) { } if (HistoryItem *item = App::histItemById(audioId.msgId)) { - Notify::redrawHistoryItem(item); + Ui::redrawHistoryItem(item); } } @@ -1811,7 +1830,7 @@ void MainWidget::documentPlayProgress(const SongMsgId &songId) { } if (HistoryItem *item = App::histItemById(songId.msgId)) { - Notify::redrawHistoryItem(item); + Ui::redrawHistoryItem(item); } } @@ -1898,7 +1917,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { DocumentItems::const_iterator i = items.constFind(document); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - Notify::redrawHistoryItem(j.key()); + Ui::redrawHistoryItem(j.key()); } } App::wnd()->documentUpdated(document); @@ -3941,12 +3960,6 @@ void MainWidget::updateOnlineDisplayIn(int32 msecs) { _onlineUpdater.start(msecs); } -void MainWidget::addNewContact(int32 uid, bool show) { - if (dialogs.addNewContact(uid, show)) { - showPeerHistory(peerFromUser(uid), ShowAtTheEndMsgId); - } -} - bool MainWidget::isActive() const { return !_isIdle && isVisible() && !_a_show.animating(); } @@ -4232,7 +4245,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { msgRow->history()->unregTyping(App::self()); } if (!App::historyRegItem(msgRow)) { - Notify::redrawHistoryItem(msgRow); + Ui::redrawHistoryItem(msgRow); } else { History *h = msgRow->history(); bool wasLast = (h->lastMsg == msgRow); @@ -4261,7 +4274,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (HistoryItem *item = App::histItemById(NoChannel, v.at(i).v)) { if (item->isMediaUnread()) { item->markMediaRead(); - Notify::redrawHistoryItem(item); + Ui::redrawHistoryItem(item); if (item->out() && item->history()->peer->isUser()) { item->history()->peer->asUser()->madeAction(); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 8be8fda5c..8bd409321 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -268,8 +268,6 @@ public: void destroyData(); void updateOnlineDisplayIn(int32 msecs); - void addNewContact(int32 uid, bool show = true); - bool isActive() const; bool historyIsActive() const; bool lastWasOnline() const; @@ -301,7 +299,6 @@ public: void deletedContact(UserData *user, const MTPcontacts_Link &result); void deleteConversation(PeerData *peer, bool deleteHistory = true); void clearHistory(PeerData *peer); - void removeContact(UserData *user); void addParticipants(PeerData *chatOrChannel, const QVector &users); bool addParticipantFail(UserData *user, const RPCError &e); @@ -386,12 +383,6 @@ public: void updateStickers(); - void notify_botCommandsChanged(UserData *bot); - void notify_userIsBotChanged(UserData *bot); - void notify_migrateUpdated(PeerData *peer); - void notify_redrawHistoryItem(const HistoryItem *item); - void notify_historyItemLayoutChanged(const HistoryItem *item); - void choosePeer(PeerId peerId, MsgId showAtMsgId); // does offerPeer or showPeerHistory void clearBotStartToken(PeerData *peer); @@ -419,6 +410,13 @@ public: void ui_showStickerPreview(DocumentData *sticker); void ui_hideStickerPreview(); + void ui_redrawHistoryItem(const HistoryItem *item); + + void notify_botCommandsChanged(UserData *bot); + void notify_userIsBotChanged(UserData *bot); + void notify_userIsContactChanged(UserData *user, bool fromThisApp); + void notify_migrateUpdated(PeerData *peer); + void notify_historyItemLayoutChanged(const HistoryItem *item); ~MainWidget(); @@ -492,6 +490,8 @@ public slots: void onDownloadPathSettings(); + void ui_showPeerHistoryAsync(quint64 peerId, qint32 showAtMsgId); + private: void sendReadRequest(PeerData *peer, MsgId upTo); diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index a189b99db..d3c40cd09 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -2997,7 +2997,7 @@ void OverviewWidget::changingMsgId(HistoryItem *row, MsgId newId) { } } -void OverviewWidget::notify_redrawHistoryItem(const HistoryItem *msg) { +void OverviewWidget::ui_redrawHistoryItem(const HistoryItem *msg) { if (peer() == msg->history()->peer || migratePeer() == msg->history()->peer) { _inner.redrawItem(msg); } diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index 91154ef29..8953db130 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -313,7 +313,6 @@ public: void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type); void changingMsgId(HistoryItem *row, MsgId newId); - void notify_redrawHistoryItem(const HistoryItem *msg); void itemRemoved(HistoryItem *item); void itemResized(HistoryItem *row, bool scrollToIt); @@ -341,6 +340,8 @@ public: resizeEvent(0); } + void ui_redrawHistoryItem(const HistoryItem *msg); + ~OverviewWidget(); public slots: From f6fec9a6192a5c55d0f32b0b6e61c97fb19e8a1c Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 13 Dec 2015 14:36:08 +0300 Subject: [PATCH 019/145] showPeerHistory moved to Ui namespace --- Telegram/SourceFiles/boxes/contactsbox.cpp | 8 ++-- Telegram/SourceFiles/dialogswidget.cpp | 6 +-- Telegram/SourceFiles/facades.cpp | 2 +- Telegram/SourceFiles/facades.h | 12 ++++++ Telegram/SourceFiles/historywidget.cpp | 30 +++++++------- Telegram/SourceFiles/mainwidget.cpp | 48 ++++++++++------------ Telegram/SourceFiles/mainwidget.h | 4 +- Telegram/SourceFiles/mediaview.cpp | 4 +- Telegram/SourceFiles/overviewwidget.cpp | 2 +- Telegram/SourceFiles/profilewidget.cpp | 16 ++++---- Telegram/SourceFiles/pspecific_mac.cpp | 2 +- Telegram/SourceFiles/pspecific_wnd.cpp | 2 +- Telegram/SourceFiles/settingswidget.cpp | 2 +- Telegram/SourceFiles/structs.cpp | 6 +-- Telegram/SourceFiles/window.cpp | 2 +- 15 files changed, 75 insertions(+), 71 deletions(-) diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 8a7c16c53..4ac079ff9 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -229,7 +229,7 @@ void ContactsInner::onAddBot() { App::main()->addParticipants(_addToPeer, QVector(1, _bot)); } Ui::hideLayer(); - App::main()->showPeerHistory(_addToPeer->id, ShowAtUnreadMsgId); + Ui::showPeerHistory(_addToPeer, ShowAtUnreadMsgId); } void ContactsInner::onAddAdmin() { @@ -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); } } @@ -1591,7 +1591,7 @@ void ContactsBox::onInvite() { App::main()->addParticipants(_inner.chat() ? (PeerData*)_inner.chat() : _inner.channel(), users); if (_inner.chat()) { Ui::hideLayer(); - App::main()->showPeerHistory(_inner.chat()->id, ShowAtTheEndMsgId); + Ui::showPeerHistory(_inner.chat(), ShowAtTheEndMsgId); } else { onClose(); } @@ -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)")); diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index b84d3e90d..d2883a0e8 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -687,7 +687,7 @@ void DialogsInner::onContextDeleteAndLeaveSure() { if (!_menuActionPeer) return; Ui::hideLayer(); - App::main()->showDialogs(); + Ui::showChatsList(); if (_menuActionPeer->isUser()) { App::main()->deleteConversation(_menuActionPeer); } else if (_menuActionPeer->isChat()) { @@ -2553,7 +2553,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); @@ -2573,7 +2573,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/facades.cpp b/Telegram/SourceFiles/facades.cpp index 621984dd5..df7d14eb5 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -96,7 +96,7 @@ namespace Ui { } void showPeerHistory(const PeerId &peer, MsgId msgId, bool back) { - if (MainWidget *m = App::main()) m->showPeerHistory(peer, msgId, back); + if (MainWidget *m = App::main()) m->ui_showPeerHistory(peer, msgId, back); } void showPeerHistoryAsync(const PeerId &peer, MsgId msgId) { diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 3ba786d65..97ebf9cd7 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -49,7 +49,19 @@ namespace Ui { // openssl doesn't allow me to use UI :( void redrawHistoryItem(const HistoryItem *item); 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); + } }; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 1a094fbf6..b95941913 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3789,7 +3789,7 @@ bool HistoryWidget::messagesFailed(const RPCError &error, mtpRequestId requestId if (error.type() == qstr("CHANNEL_PRIVATE")) { PeerData *was = _peer; - App::main()->showDialogs(); + Ui::showChatsList(); Ui::showLayer(new InformBox(lang((was && was->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); return true; } @@ -3801,7 +3801,7 @@ bool HistoryWidget::messagesFailed(const RPCError &error, mtpRequestId requestId _preloadDownRequest = 0; } else if (_firstLoadRequest == requestId) { _firstLoadRequest = 0; - App::main()->showDialogs(); + Ui::showChatsList(); } else if (_delayedShowAtRequest == requestId) { _delayedShowAtRequest = 0; } @@ -4324,7 +4324,7 @@ void HistoryWidget::onBroadcastChange() { void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) { if (!contact || contact->phone.isEmpty()) return; - App::main()->showPeerHistory(peer, ShowAtTheEndMsgId); + Ui::showPeerHistory(peer, ShowAtTheEndMsgId); if (!_history) return; shareContact(peer, contact->phone, contact->firstName, contact->lastName, replyToId(), peerToUser(contact->id)); @@ -4367,7 +4367,7 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const } void HistoryWidget::onSendPaths(const PeerId &peer) { - App::main()->showPeerHistory(peer, ShowAtTheEndMsgId); + Ui::showPeerHistory(peer, ShowAtTheEndMsgId); if (!_history) return; if (cSendPaths().size() == 1) { @@ -5062,7 +5062,7 @@ void HistoryWidget::topBarClick() { if (cWideMode()) { if (_history) App::main()->showPeerProfile(_peer); } else { - App::main()->showDialogs(); + Ui::showChatsList(); } } @@ -5571,10 +5571,10 @@ void HistoryWidget::onReportSpamClear() { if (_clearPeer->isUser()) { App::main()->deleteConversation(_clearPeer); } else if (_clearPeer->isChat()) { - App::main()->showDialogs(); + Ui::showChatsList(); MTP::send(MTPmessages_DeleteChatUser(_clearPeer->asChat()->inputChat, App::self()->inputUser), App::main()->rpcDone(&MainWidget::deleteHistoryAfterLeave, _clearPeer), App::main()->rpcFail(&MainWidget::leaveChatFailed, _clearPeer)); } else if (_clearPeer->isChannel()) { - App::main()->showDialogs(); + Ui::showChatsList(); if (_clearPeer->migrateFrom()) { App::main()->deleteConversation(_clearPeer->migrateFrom()); } @@ -6033,7 +6033,7 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { a_recordOver.restart(); _a_record.start(); } else if (_inReply) { - App::main()->showPeerHistory(_peer->id, replyToId()); + Ui::showPeerHistory(_peer, replyToId()); } } @@ -6050,7 +6050,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { PeerData *after = 0; MsgId afterMsgId = 0; App::main()->peerAfter(_peer, msgid, after, afterMsgId); - if (after) App::main()->showPeerHistory(after->id, afterMsgId); + if (after) Ui::showPeerHistory(after, afterMsgId); } else { _scroll.keyPressEvent(e); } @@ -6059,7 +6059,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { PeerData *before = 0; MsgId beforeMsgId = 0; App::main()->peerBefore(_peer, msgid, before, beforeMsgId); - if (before) App::main()->showPeerHistory(before->id, beforeMsgId); + if (before) Ui::showPeerHistory(before, beforeMsgId); } else { _scroll.keyPressEvent(e); } @@ -6068,7 +6068,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { PeerData *after = 0; MsgId afterMsgId = 0; App::main()->peerAfter(_peer, msgid, after, afterMsgId); - if (after) App::main()->showPeerHistory(after->id, afterMsgId); + if (after) Ui::showPeerHistory(after, afterMsgId); } else if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { _scroll.keyPressEvent(e); } @@ -6077,7 +6077,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { PeerData *before = 0; MsgId beforeMsgId = 0; App::main()->peerBefore(_peer, msgid, before, beforeMsgId); - if (before) App::main()->showPeerHistory(before->id, beforeMsgId); + if (before) Ui::showPeerHistory(before, beforeMsgId); } else if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { _scroll.keyPressEvent(e); } @@ -6089,7 +6089,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { } else { App::main()->peerAfter(_peer, msgid, p, m); } - if (p) App::main()->showPeerHistory(p->id, m); + if (p) Ui::showPeerHistory(p, m); } else if (_history && (e->key() == Qt::Key_Search || e == QKeySequence::Find)) { App::main()->searchInPeer(_peer); } else { @@ -6380,7 +6380,7 @@ void HistoryWidget::updatePreview() { } void HistoryWidget::onCancel() { - if (App::main()) App::main()->showDialogs(); + Ui::showChatsList(); emit cancelled(); } @@ -6421,7 +6421,7 @@ void HistoryWidget::onFullPeerUpdated(PeerData *data) { void HistoryWidget::peerUpdated(PeerData *data) { if (data && data == _peer) { if (data->migrateTo()) { - App::main()->showPeerHistory(data->migrateTo()->id, ShowAtUnreadMsgId); + Ui::showPeerHistory(data->migrateTo(), ShowAtUnreadMsgId); QTimer::singleShot(ReloadChannelMembersTimeout, App::api(), SLOT(delayedRequestParticipantsCount())); return; } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index e626a8623..97a9ad020 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -120,7 +120,7 @@ void TopBarWidget::onDeleteContactSure() { PeerData *p = App::main() ? App::main()->profilePeer() : 0; UserData *u = p ? p->asUser() : 0; if (u) { - App::main()->showDialogs(); + Ui::showChatsList(); Ui::hideLayer(); MTP::send(MTPcontacts_DeleteContact(u->inputUser), App::main()->rpcDone(&MainWidget::deletedContact, u)); } @@ -140,7 +140,7 @@ void TopBarWidget::onDeleteAndExitSure() { PeerData *p = App::main() ? App::main()->profilePeer() : 0; ChatData *c = p ? p->asChat() : 0; if (c) { - App::main()->showDialogs(); + Ui::showChatsList(); Ui::hideLayer(); MTP::send(MTPmessages_DeleteChatUser(c->inputChat, App::self()->inputUser), App::main()->rpcDone(&MainWidget::deleteHistoryAfterLeave, p), App::main()->rpcFail(&MainWidget::leaveChatFailed, p)); } @@ -504,7 +504,7 @@ bool MainWidget::onForward(const PeerId &peer, ForwardWhatMessages what) { } } updateForwardingTexts(); - showPeerHistory(peer, ShowAtUnreadMsgId); + Ui::showPeerHistory(peer, ShowAtUnreadMsgId); history.onClearSelected(); history.updateForwarding(); return true; @@ -525,7 +525,7 @@ bool MainWidget::onShareUrl(const PeerId &peer, const QString &url, const QStrin if (opened) { history.applyDraft(); } else { - showPeerHistory(peer, ShowAtUnreadMsgId); + Ui::showPeerHistory(peer, ShowAtUnreadMsgId); } return true; } @@ -703,7 +703,7 @@ void MainWidget::onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data) } else if (data->hasFormat(qsl("application/x-td-forward-pressed"))) { onForward(peer, ForwardPressedMessage); } else { - showPeerHistory(peer, ShowAtTheEndMsgId); + Ui::showPeerHistory(peer, ShowAtTheEndMsgId); history.onFilesDrop(data); } } @@ -967,7 +967,7 @@ void MainWidget::removeDialog(History *history) { void MainWidget::deleteConversation(PeerData *peer, bool deleteHistory) { if (activePeer() == peer) { - showDialogs(); + Ui::showChatsList(); } if (History *h = App::historyLoaded(peer->id)) { removeDialog(h); @@ -1003,7 +1003,7 @@ void MainWidget::clearHistory(PeerData *peer) { h->clear(); h->newLoaded = h->oldLoaded = true; } - showPeerHistory(peer->id, ShowAtUnreadMsgId); + Ui::showPeerHistory(peer->id, ShowAtUnreadMsgId); MTP::send(MTPmessages_DeleteHistory(peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, peer)); } @@ -1061,7 +1061,7 @@ bool MainWidget::addParticipantsFail(ChannelData *channel, const RPCError &error void MainWidget::kickParticipant(ChatData *chat, UserData *user) { MTP::send(MTPmessages_DeleteChatUser(chat->inputChat, user->inputUser), rpcDone(&MainWidget::sentUpdatesReceived), rpcFail(&MainWidget::kickParticipantFail, chat)); Ui::hideLayer(); - showPeerHistory(chat->id, ShowAtTheEndMsgId); + Ui::showPeerHistory(chat->id, ShowAtTheEndMsgId); } bool MainWidget::kickParticipantFail(ChatData *chat, const RPCError &error) { @@ -1352,7 +1352,7 @@ void MainWidget::searchMessages(const QString &query, PeerData *inPeer) { App::wnd()->hideMediaview(); dialogs.searchMessages(query, inPeer); if (!cWideMode()) { - showDialogs(); + Ui::showChatsList(); } else { dialogs.activate(); } @@ -2278,7 +2278,7 @@ void MainWidget::choosePeer(PeerId peerId, MsgId showAtMsgId) { if (selectingPeer()) { offerPeer(peerId); } else { - showPeerHistory(peerId, showAtMsgId); + Ui::showPeerHistory(peerId, showAtMsgId); } } @@ -2304,7 +2304,7 @@ void MainWidget::ctrlEnterSubmitUpdated() { history.ctrlEnterSubmitUpdated(); } -void MainWidget::showPeerHistory(quint64 peerId, qint32 showAtMsgId, bool back) { +void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, bool back) { if (PeerData *peer = App::peerLoaded(peerId)) { if (peer->migrateTo()) { peerId = peer->migrateTo()->id; @@ -2579,7 +2579,7 @@ void MainWidget::showPeerProfile(PeerData *peer, bool back, int32 lastScrollTop) void MainWidget::showBackFromStack() { if (selectingPeer()) return; if (_stack.isEmpty()) { - showDialogs(); + Ui::showChatsList(); if (App::wnd()) QTimer::singleShot(0, App::wnd(), SLOT(setInnerFocus())); return; } @@ -2598,7 +2598,7 @@ void MainWidget::showBackFromStack() { } } StackItemHistory *histItem = static_cast(item); - showPeerHistory(histItem->peer->id, App::main()->activeMsgId(), true); + Ui::showPeerHistory(histItem->peer->id, App::main()->activeMsgId(), true); history.setReplyReturns(histItem->peer->id, histItem->replyReturns); } else if (item->type() == ProfileStackItem) { StackItemProfile *profItem = static_cast(item); @@ -2889,10 +2889,6 @@ bool MainWidget::needBackButton() { return overview || profile || (history.peer() && history.peer()->id); } -void MainWidget::showDialogs() { - showPeerHistory(0, 0); -} - void MainWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth) { if (profile) { profile->paintTopBar(p, over, decreaseWidth); @@ -2971,7 +2967,7 @@ void MainWidget::searchInPeer(PeerData *peer) { dialogs.activate(); } else { dialogsToUp(); - showDialogs(); + Ui::showChatsList(); } } @@ -3561,12 +3557,10 @@ void MainWidget::openPeerByName(const QString &username, bool toProfile, const Q PeerData *peer = App::peerByName(username); if (peer) { - if (toProfile) { + if (toProfile && !peer->isChannel()) { if (peer->isUser() && peer->asUser()->botInfo && !peer->asUser()->botInfo->cantJoinGroups && !startToken.isEmpty()) { peer->asUser()->botInfo->startGroupToken = startToken; Ui::showLayer(new ContactsBox(peer->asUser())); - } else if (peer->isChannel()) { - showPeerHistory(peer->id, ShowAtUnreadMsgId); } else { showPeerProfile(peer); } @@ -3578,7 +3572,7 @@ void MainWidget::openPeerByName(const QString &username, bool toProfile, const Q history.resizeEvent(0); } } - emit showPeerAsync(peer->id, 0); + Ui::showPeerHistoryAsync(peer->id, ShowAtUnreadMsgId); } } else { MTP::send(MTPcontacts_ResolveUsername(MTP_string(username)), rpcDone(&MainWidget::usernameResolveDone, qMakePair(toProfile, startToken)), rpcFail(&MainWidget::usernameResolveFail, username)); @@ -3643,7 +3637,7 @@ void MainWidget::usernameResolveDone(QPair toProfileStartToken, c peer->asUser()->botInfo->startGroupToken = toProfileStartToken.second; Ui::showLayer(new ContactsBox(peer->asUser())); } else if (peer->isChannel()) { - showPeerHistory(peer->id, ShowAtUnreadMsgId); + Ui::showPeerHistory(peer->id, ShowAtUnreadMsgId); } else { showPeerProfile(peer); } @@ -3655,7 +3649,7 @@ void MainWidget::usernameResolveDone(QPair toProfileStartToken, c history.resizeEvent(0); } } - showPeerHistory(peer->id, ShowAtUnreadMsgId); + Ui::showPeerHistory(peer->id, ShowAtUnreadMsgId); } } @@ -3682,7 +3676,7 @@ void MainWidget::inviteCheckDone(QString hash, const MTPChatInvite &invite) { const MTPDchatInviteAlready &d(invite.c_chatInviteAlready()); PeerData *chat = App::feedChats(MTP_vector(1, d.vchat)); if (chat) { - showPeerHistory(chat->id, ShowAtUnreadMsgId); + Ui::showPeerHistory(chat->id, ShowAtUnreadMsgId); } } break; } @@ -3714,9 +3708,9 @@ void MainWidget::inviteImportDone(const MTPUpdates &updates) { } if (v && !v->isEmpty()) { if (v->front().type() == mtpc_chat) { - App::main()->showPeerHistory(peerFromChat(v->front().c_chat().vid.v), ShowAtTheEndMsgId); + Ui::showPeerHistory(peerFromChat(v->front().c_chat().vid.v), ShowAtTheEndMsgId); } else if (v->front().type() == mtpc_channel) { - App::main()->showPeerHistory(peerFromChannel(v->front().c_channel().vid.v), ShowAtTheEndMsgId); + Ui::showPeerHistory(peerFromChannel(v->front().c_channel().vid.v), ShowAtTheEndMsgId); } } } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 8bd409321..215f7711b 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -194,7 +194,6 @@ public: void updateWideMode(); bool needBackButton(); - void showDialogs(); void paintTopBar(QPainter &p, float64 over, int32 decreaseWidth); TopBarWidget *topBar(); @@ -411,6 +410,7 @@ public: void ui_showStickerPreview(DocumentData *sticker); void ui_hideStickerPreview(); void ui_redrawHistoryItem(const HistoryItem *item); + void ui_showPeerHistory(quint64 peer, qint32 msgId, bool back); void notify_botCommandsChanged(UserData *bot); void notify_userIsBotChanged(UserData *bot); @@ -427,7 +427,6 @@ signals: void peerPhotoChanged(PeerData *peer); void dialogRowReplaced(DialogRow *oldRow, DialogRow *newRow); void dialogsUpdated(); - void showPeerAsync(quint64 peerId, qint32 showAtMsgId); void stickersUpdated(); public slots: @@ -459,7 +458,6 @@ public slots: void checkIdleFinish(); void updateOnlineDisplay(); - void showPeerHistory(quint64 peer, qint32 msgId, bool back = false); void onTopBarClick(); void onHistoryShown(History *history, MsgId atMsgId); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 7a88a1740..380899f13 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -526,7 +526,7 @@ void MediaView::onToMessage() { if (HistoryItem *item = _msgid ? App::histItemById(_msgmigrated ? 0 : _channel, _msgid) : 0) { if (App::wnd()) { close(); - if (App::main()) App::main()->showPeerHistory(item->history()->peer->id, _msgid); + Ui::showPeerHistoryAtItem(item); } } } @@ -1808,7 +1808,7 @@ void MediaView::mouseReleaseEvent(QMouseEvent *e) { } else { if (reBotCommand().match(lnk->encoded()).hasMatch() && _history) { App::wnd()->hideMediaview(); - App::main()->showPeerHistory(_history->peer->id, ShowAtTheEndMsgId); + Ui::showPeerHistory(_history, ShowAtTheEndMsgId); } lnk->onClick(e->button()); } diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index d3c40cd09..b2b454d7a 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -2044,7 +2044,7 @@ void OverviewInner::goToMessage() { HistoryItem *item = App::contextItem(); if (!item) return; - App::main()->showPeerHistory(item->history()->peer->id, item->id); + Ui::showPeerHistoryAtItem(item); } void OverviewInner::forwardMessage() { diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index c8bc70cb3..47424ee92 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -237,7 +237,7 @@ void ProfileInner::onInviteToGroup() { } void ProfileInner::onSendMessage() { - App::main()->showPeerHistory(_peer->id, ShowAtUnreadMsgId); + Ui::showPeerHistory(_peer, ShowAtUnreadMsgId); } void ProfileInner::onSearchInPeer() { @@ -329,10 +329,10 @@ void ProfileInner::onDeleteConversationSure() { if (_peerUser) { App::main()->deleteConversation(_peer); } else if (_peerChat) { - App::main()->showDialogs(); + Ui::showChatsList(); MTP::send(MTPmessages_DeleteChatUser(_peerChat->inputChat, App::self()->inputUser), App::main()->rpcDone(&MainWidget::deleteHistoryAfterLeave, _peer), App::main()->rpcFail(&MainWidget::leaveChatFailed, _peer)); } else if (_peerChannel) { - App::main()->showDialogs(); + Ui::showChatsList(); if (_peerChannel->migrateFrom()) { App::main()->deleteConversation(_peerChannel->migrateFrom()); } @@ -350,7 +350,7 @@ void ProfileInner::onDeleteChannel() { void ProfileInner::onDeleteChannelSure() { if (_peerChannel) { Ui::hideLayer(); - App::main()->showDialogs(); + Ui::showChatsList(); if (_peerChannel->migrateFrom()) { App::main()->deleteConversation(_peerChannel->migrateFrom()); } @@ -562,7 +562,7 @@ void ProfileInner::onBotSettings() { for (int32 i = 0, l = _peerUser->botInfo->commands.size(); i != l; ++i) { QString cmd = _peerUser->botInfo->commands.at(i).command; if (!cmd.compare(qsl("settings"), Qt::CaseInsensitive)) { - App::main()->showPeerHistory(_peer->id, ShowAtTheEndMsgId); + Ui::showPeerHistory(_peer, ShowAtTheEndMsgId); App::main()->sendBotCommand('/' + cmd, 0); return; } @@ -576,7 +576,7 @@ void ProfileInner::onBotHelp() { for (int32 i = 0, l = _peerUser->botInfo->commands.size(); i != l; ++i) { QString cmd = _peerUser->botInfo->commands.at(i).command; if (!cmd.compare(qsl("help"), Qt::CaseInsensitive)) { - App::main()->showPeerHistory(_peer->id, ShowAtTheEndMsgId); + Ui::showPeerHistory(_peer, ShowAtTheEndMsgId); App::main()->sendBotCommand('/' + cmd, 0); return; } @@ -1085,7 +1085,7 @@ void ProfileInner::mouseReleaseEvent(QMouseEvent *e) { App::searchByHashtag(lnk->encoded(), _peerChannel); } else { if (reBotCommand().match(lnk->encoded()).hasMatch()) { - App::main()->showPeerHistory(_peer->id, ShowAtTheEndMsgId); + Ui::showPeerHistory(_peer, ShowAtTheEndMsgId); } lnk->onClick(e->button()); } @@ -1250,7 +1250,7 @@ void ProfileInner::migrateDone(const MTPUpdates &updates) { for (int32 i = 0, l = v->size(); i < l; ++i) { if (v->at(i).type() == mtpc_channel) { peer = App::channel(v->at(i).c_channel().vid.v); - App::main()->showPeerHistory(peer->id, ShowAtUnreadMsgId); + Ui::showPeerHistory(peer, ShowAtUnreadMsgId); QTimer::singleShot(ReloadChannelMembersTimeout, App::api(), SLOT(delayedRequestParticipantsCount())); } } diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp index 61cc6d1db..420ff8ad9 100644 --- a/Telegram/SourceFiles/pspecific_mac.cpp +++ b/Telegram/SourceFiles/pspecific_mac.cpp @@ -76,7 +76,7 @@ void MacPrivate::notifyClicked(unsigned long long peer, int msgid) { tomsg = false; } } - App::main()->showPeerHistory(history->peer->id, tomsg ? msgid : ShowAtUnreadMsgId); + Ui::showPeerHistory(history, tomsg ? msgid : ShowAtUnreadMsgId); App::wnd()->notifyClear(history); } } diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp index 8bfef9751..6f5c0794f 100644 --- a/Telegram/SourceFiles/pspecific_wnd.cpp +++ b/Telegram/SourceFiles/pspecific_wnd.cpp @@ -2620,7 +2620,7 @@ public: tomsg = false; } } - App::main()->showPeerHistory(history->peer->id, tomsg ? _msgId : ShowAtUnreadMsgId); + Ui::showPeerHistory(history, tomsg ? _msgId : ShowAtUnreadMsgId); App::wnd()->notifyClear(history); } SetForegroundWindow(App::wnd()->psHwnd()); diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp index 41c2be4ea..b5ef77c74 100644 --- a/Telegram/SourceFiles/settingswidget.cpp +++ b/Telegram/SourceFiles/settingswidget.cpp @@ -1139,7 +1139,7 @@ void SettingsInner::supportGot(const MTPhelp_Support &support) { if (support.type() == mtpc_help_support) { const MTPDhelp_support &d(support.c_help_support()); UserData *u = App::feedUsers(MTP_vector(1, d.vuser)); - App::main()->showPeerHistory(u->id, ShowAtUnreadMsgId); + Ui::showPeerHistory(u, ShowAtUnreadMsgId); App::wnd()->hideSettings(); } } diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index e90d5f1fd..ecad9645f 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -1110,7 +1110,7 @@ void PeerLink::onClick(Qt::MouseButton button) const { if (!peer()->asChannel()->isPublic() && !peer()->asChannel()->amIn()) { Ui::showLayer(new InformBox(lang((peer()->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); } else { - App::main()->showPeerHistory(peer()->id, ShowAtUnreadMsgId); + Ui::showPeerHistory(peer(), ShowAtUnreadMsgId); } } else { App::main()->showPeerProfile(peer()); @@ -1124,13 +1124,13 @@ void MessageLink::onClick(Qt::MouseButton button) const { if (current && current->history()->peer->id == peer()) { App::main()->pushReplyReturn(current); } - App::main()->showPeerHistory(peer(), msgid()); + Ui::showPeerHistory(peer(), msgid()); } } void CommentsLink::onClick(Qt::MouseButton button) const { if (button == Qt::LeftButton && App::main() && _item->history()->isChannel()) { - App::main()->showPeerHistory(_item->history()->peer->id, _item->id); + Ui::showPeerHistoryAtItem(_item); } } diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index f072cc85a..3f067221e 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -296,7 +296,7 @@ void NotifyWindow::mousePressEvent(QMouseEvent *e) { App::wnd()->notifyClear(); } else { App::wnd()->hideSettings(); - App::main()->showPeerHistory(peer, (!history->peer->isUser() && item && item->mentionsMe() && item->id > 0) ? item->id : ShowAtUnreadMsgId); + Ui::showPeerHistory(peer, (!history->peer->isUser() && item && item->mentionsMe() && item->id > 0) ? item->id : ShowAtUnreadMsgId); } e->ignore(); } From 6100c1dccac6124007422905411394db08e4ba71 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 13 Dec 2015 18:21:20 +0300 Subject: [PATCH 020/145] video redesigned --- Telegram/Resources/style.txt | 4 +- Telegram/SourceFiles/gui/images.h | 2 +- Telegram/SourceFiles/history.cpp | 610 ++++++++++++++++++------------ Telegram/SourceFiles/history.h | 120 +++--- Telegram/SourceFiles/structs.cpp | 12 + Telegram/SourceFiles/structs.h | 10 + 6 files changed, 453 insertions(+), 305 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index bbec08d02..9c234ea0b 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1240,6 +1240,8 @@ msgFileInPlaySelected: sprite(180px, 164px, 20px, 18px); msgFileOverDuration: 200; msgFileRadialLine: 4px; +msgVideoSize: size(320px, 240px); + sendPadding: 9px; btnSend: flatButton(btnDefFlat) { color: btnYesColor; @@ -2186,7 +2188,7 @@ mediaviewLoader: size(78px, 33px); mediaviewLoaderPoint: size(9px, 9px); mediaviewLoaderSkip: 9px; -minPhotoSize: 100px; +minPhotoSize: 104px; maxMediaSize: 420px; maxStickerSize: 256px; diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index 384da3da1..92f7115bc 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -65,7 +65,7 @@ public: 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; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 7713a5708..307f1dc5e 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3182,7 +3182,7 @@ HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, Histo , _openl(new PhotoLink(_data)) , _pixw(1) , _pixh(1) -, _caption(st::minPhotoSize) { +, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { if (!caption.isEmpty()) { _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent)); } @@ -3193,8 +3193,7 @@ HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) , _data(App::feedPhoto(photo)) , _openl(new PhotoLink(_data, chat)) , _pixw(1) -, _pixh(1) -, _caption(st::minPhotoSize) { +, _pixh(1) { w = width; init(); } @@ -3225,18 +3224,21 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) { if (parent->toHistoryMessage()) { w = tw; + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); + int32 maxActualWidth = qMax(w, 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 { th = w; // square chat photo updates - } - _maxw = qMax(qMax(w, int32(st::minPhotoSize)), th); - _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()) { - _maxw = qMax(_maxw, st::msgPadding.left() + _caption.maxWidth() + st::msgPadding.right()); - _minh += st::mediaCaptionSkip + _caption.minHeight() + st::msgPadding.bottom(); - } + _maxw = _minh = w; } } @@ -3269,7 +3271,9 @@ int32 HistoryPhoto::resize(int32 width, const HistoryItem *parent) { } if (_pixw < 1) _pixw = 1; if (_pixh < 1) _pixh = 1; - w = qMax(_pixw, int16(st::minPhotoSize)); + + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); + w = qMax(_pixw, int16(minWidth)); _height = qMax(_pixh, int16(st::minPhotoSize)); if (bubble) { _height += st::mediaPadding.top() + st::mediaPadding.bottom(); @@ -3399,7 +3403,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b pix = _data->thumb->pixBlurredSingle(_pixw, _pixh, width, height); } - p.drawPixmap(skipx, skipy, pix); + p.drawPixmapLeft(skipx, skipy, w, pix); if (!full) { uint64 dt = itemAnimations().animate(parent, ms); int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); @@ -3422,7 +3426,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b } if (selected) { - App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + App::roundRect(p, rtlrect(skipx, skipy, width, height, w), textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } // date @@ -3481,232 +3485,53 @@ QString formatDurationAndSizeText(qint64 duration, qint64 size) { return lng_duration_and_size(lt_duration, formatDurationText(duration), lt_size, formatSizeText(size)); } +QString formatGifAndSizeText(qint64 size) { + return lng_duration_and_size(lt_duration, qsl("GIF"), lt_size, formatSizeText(size)); +} + QString formatPlayedText(qint64 played, qint64 duration) { return lng_duration_played(lt_played, formatDurationText(played), lt_duration, formatDurationText(duration)); } -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)); +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); } - _size = formatDurationAndSizeText(data->duration, data->size); - - 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::msgMaxWidth; - int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); - - _minh = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); - 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); -} - -const QString HistoryVideo::inDialogsText() const { - return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone); -} - -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 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 h = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); - if (!_caption.isEmpty()) { - int32 textw = width - st::mediaPadding.left() - st::mediaPadding.right(); - 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; - - int skipy = 0, replyFrom = 0, fwdFrom = 0; - - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; - if (width >= _maxw) { - width = _maxw; + 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; } - 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, const QRect &r, bool selected, uint64 ms) const { - if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 width = w, height = _height, skipx = 0, skipy = 0; - - data->thumb->checkload(); - - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; - if (width >= _maxw) { - width = _maxw; - } - - style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg)); - style::color sh(outbg ? (selected ? st::msgOutShadowSelected : st::msgOutShadow) : (selected ? st::msgInShadowSelected : st::msgInShadow)); - RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners)); - App::roundRect(p, 0, 0, width, height, bg, cors, &sh); - - //if (_thumbw) { - // 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); - } - - 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::normalFont->f); - p.setPen(st::black->c); - p.drawText(tleft, skipy + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, lang(lng_media_video)); - - QString statusText; - - style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg)); - 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 == FileUploadFailed || data->status == FileDownloadFailed) { - 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; + 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 { - statusText = _size; + result = qMax(result, st::normalFont->width(formatSizeText(document->size))); } + return result; } - int32 texty = skipy + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->height; - p.drawText(tleft, texty + st::normalFont->ascent, statusText); - if (parent->isMediaUnread()) { - int32 w = st::normalFont->width(statusText); - if (w + st::mediaUnreadSkip + st::mediaUnreadSize <= twidth) { - p.setRenderHint(QPainter::HighQualityAntialiasing, true); - p.setPen(Qt::NoPen); - p.setBrush((outbg ? (selected ? st::mediaOutUnreadFgSelected : st::mediaOutUnreadFg) : (selected ? st::mediaInUnreadFgSelected : st::mediaInUnreadFg))->b); - p.drawEllipse(QRect(tleft + w + st::mediaUnreadSkip, texty + ((st::normalFont->height - st::mediaUnreadSize) / 2), st::mediaUnreadSize, st::mediaUnreadSize)); - p.setRenderHint(QPainter::HighQualityAntialiasing, false); - } - } - 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 HistoryVideo::resize(int32 width, const HistoryItem *parent) { - w = qMin(width, _maxw); - if (_caption.isEmpty()) return _height; - - _height = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); - if (!_caption.isEmpty()) { - int32 textw = w - st::mediaPadding.left() - st::mediaPadding.right(); - _height += st::webPagePhotoSkip + _caption.countHeight(textw); + 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; } - return _height; -} - -ImagePtr HistoryVideo::replyPreview() { - 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"); - } else { - data->thumb->load(); - } - } - return data->replyPreview; } HistoryFileMedia::HistoryFileMedia() : HistoryMedia() @@ -3797,33 +3622,322 @@ HistoryFileMedia::~HistoryFileMedia() { } } -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); +HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent) : HistoryFileMedia() +, _data(App::feedVideo(video)) +, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) +, _thumbw(1) { + if (!caption.isEmpty()) { + _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent)); } - 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))); + setLinks(new VideoOpenLink(_data), new VideoSaveLink(_data), new VideoCancelLink(_data)); + + setStatusSize(FileStatusSizeReady); + + _data->thumb->load(); +} + +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(); + } + + w = _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(w, 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()) { + _minh += st::mediaCaptionSkip + _caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom(); } - return result; + } +} + +void HistoryVideo::setStatusSize(int32 newSize) const { + HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration, 0); +} + +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->loader) { + statusSize = _data->loader->currentOffset(); + } else if (!_data->already().isEmpty()) { + statusSize = FileStatusSizeLoaded; + } else { + statusSize = FileStatusSizeReady; + } + if (statusSize != _statusSize) { + setStatusSize(statusSize); + } +} + +void HistoryVideo::regItem(HistoryItem *item) { + App::regVideoItem(_data, item); +} + +void HistoryVideo::unregItem(HistoryItem *item) { + App::unregVideoItem(_data, item); +} + +const QString HistoryVideo::inDialogsText() const { + return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone); +} + +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 { + if (width < 0) width = w; + return (x >= 0 && y >= 0 && x < width && y < _height); +} + +int32 HistoryVideo::countHeight(const HistoryItem *parent, int32 width) const { + if (width < 0) width = w; + if (width >= _maxw) { + width = _maxw; } - 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; + 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 h = qMax(th, int32(st::minPhotoSize)); + if (bubble) { + tw += st::mediaPadding.left() + st::mediaPadding.right(); + h += st::mediaPadding.top() + st::mediaPadding.bottom(); + if (!_caption.isEmpty()) { + h += st::mediaCaptionSkip + _caption.countHeight(tw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom(); + } + } + return h; +} + +void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const { + bool bubble = parent->hasBubble(); + + if (width < 0) width = w; + if (width < 1) return; + + int skipx = 0, skipy = 0, height = _height; + 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 = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; + if (_caption.isEmpty()) { + int32 fullRight = skipx + width, fullBottom = skipy + height; + bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage); + if (inDate) { + state = HistoryInDateCursorState; + } + } + return; + } +} + +HistoryMedia *HistoryVideo::clone() const { + return new HistoryVideo(*this); +} + +void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 width = w, height = _height, skipx = 0, skipy = 0; + int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); + + bool bubble = parent->hasBubble(); + bool fromChannel = parent->fromChannel(), out = parent->out(), outbg = out && !fromChannel; + + if (_data->loader) { + 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); + } + _data->thumb->checkload(); + + QRect rthumb(rtlrect(skipx, skipy, width, height, w)); + + QPixmap pix = _data->thumb->pixBlurredSingle(_thumbw, 0, width, height); + p.drawPixmap(rthumb.topLeft(), pix); + 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 (_animation && _animation->_a_thumbOver.animating()) { + _animation->_a_thumbOver.step(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->loader ? _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 (!_data->already().isEmpty()) { + icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); + } else if (_data->loader) { + 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, 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, w), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + p.setFont(st::normalFont); + p.setPen(st::white); + p.drawTextLeft(statusX, statusY, w, _statusText, statusW - 2 * st::msgDateImgPadding.x()); + + // date + QString time(parent->timeText()); + if (_caption.isEmpty()) { + 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); + } +} + +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; + } + w = _thumbw = tw; + + 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())); + w = qMax(w, minWidth); + + _height = qMax(th, int32(st::minPhotoSize)); + if (bubble) { + w += st::mediaPadding.left() + st::mediaPadding.right(); + _height += st::mediaPadding.top() + st::mediaPadding.bottom(); + if (!_caption.isEmpty()) { + int32 captionw = w - st::msgPadding.left() - st::msgPadding.right(); + _height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom(); + } + } + return _height; +} + +ImagePtr HistoryVideo::replyPreview() { + 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"); + } else { + _data->thumb->load(); + } + } + return _data->replyPreview; } HistoryAudio::HistoryAudio(const MTPDaudio &audio) : HistoryFileMedia() diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 85e029dc3..a87b91f78 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1303,61 +1303,6 @@ QString formatDurationText(qint64 duration); QString formatDurationAndSizeText(qint64 duration, qint64 size); QString formatPlayedText(qint64 played, qint64 duration); -class HistoryVideo : public HistoryMedia { -public: - - HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent); - void initDimensions(const HistoryItem *parent); - - void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; - int32 resize(int32 width, const HistoryItem *parent); - HistoryMediaType type() const { - return MediaTypeVideo; - } - 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); - } - HistoryMedia *clone() const; - - void regItem(HistoryItem *item); - void unregItem(HistoryItem *item); - - bool hasReplyPreview() const { - return !data->thumb->isNull(); - } - ImagePtr replyPreview(); - - bool needsBubble(const HistoryItem *parent) const { - return !_caption.isEmpty() || parent->toHistoryReply(); - } - bool customInfoLayout() const { - return _caption.isEmpty(); - } - bool hideFromName() const { - return true; - } - bool hideForwardedFrom() const { - return true; - } - -private: - VideoData *data; - TextLinkPtr _openl, _savel, _cancell; - - Text _caption; - - QString _size; - int32 _thumbw; - - mutable QString _dldTextCache, _uplTextCache; - mutable int32 _dldDone, _uplDone; -}; - class HistoryFileMedia : public HistoryMedia { public: @@ -1414,6 +1359,70 @@ protected: }; +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, const QRect &r, bool selected, uint64 ms) const; + int32 resize(int32 width, const HistoryItem *parent); + HistoryMediaType type() const { + return MediaTypeVideo; + } + 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); + } + HistoryMedia *clone() const; + + void regItem(HistoryItem *item); + void unregItem(HistoryItem *item); + + bool hasReplyPreview() const { + return !_data->thumb->isNull(); + } + ImagePtr replyPreview(); + + bool needsBubble(const HistoryItem *parent) const { + return !_caption.isEmpty() || parent->toHistoryReply(); + } + bool customInfoLayout() const { + return _caption.isEmpty(); + } + bool hideFromName() const { + return true; + } + bool hideForwardedFrom() const { + return true; + } + +protected: + + float64 dataProgress() const { + return _data->progress(); + } + bool dataFinished() const { + return !_data->loader; + } + bool dataLoaded() const { + return !_data->already().isEmpty(); + } + +private: + VideoData *_data; + Text _caption; + int32 _thumbw; + + void setStatusSize(int32 newSize) const; + void updateStatusText(const HistoryItem *parent) const; + +}; + class HistoryAudio : public HistoryFileMedia { public: @@ -1463,6 +1472,7 @@ protected: private: AudioData *_data; + void setStatusSize(int32 newSize, qint64 realDuration = 0) const; bool updateStatusText(const HistoryItem *parent) const; // returns showPause diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index ecad9645f..1443a40cc 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -719,6 +719,18 @@ void VideoData::save(const QString &toFile) { loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(videoLoadProgress(mtpFileLoader*))); loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(videoLoadFailed(mtpFileLoader*, bool))); loader->start(); + + notifyLayoutChanged(); +} + +void VideoData::notifyLayoutChanged() const { + const VideoItems &items(App::videoItems()); + VideoItems::const_iterator i = items.constFind(const_cast(this)); + if (i != items.cend()) { + for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { + Notify::historyItemLayoutChanged(j.key()); + } + } } QString VideoData::already(bool check) { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 67c767cf2..f9dc10b82 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -809,6 +809,8 @@ struct VideoData { l->cancel(); l->deleteLater(); l->rpcInvalidate(); + + notifyLayoutChanged(); } _location = FileLocation(); if (!beforeDownload) { @@ -824,11 +826,19 @@ struct VideoData { loader->deleteLater(); loader->rpcInvalidate(); loader = 0; + + notifyLayoutChanged(); } + void notifyLayoutChanged() const; + QString already(bool check = false); const FileLocation &location(bool check = false); + float64 progress() const { + return loader ? loader->currentProgress() : ((status == FileDownloadFailed || _location.name().isEmpty()) ? 0 : 1); + } + VideoId id; uint64 access; int32 date; From a66c051eb54c46a4409319e24ed7c9e9d53beec8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 13 Dec 2015 20:05:32 +0300 Subject: [PATCH 021/145] gif redesign done, started ClipReader - gif animation reader in separate thread --- Telegram/SourceFiles/gui/animation.cpp | 34 +++ Telegram/SourceFiles/gui/animation.h | 31 +++ Telegram/SourceFiles/history.cpp | 317 ++++++++++++++++++++----- Telegram/SourceFiles/history.h | 37 +-- Telegram/SourceFiles/historywidget.cpp | 4 + Telegram/SourceFiles/mainwidget.cpp | 2 +- Telegram/SourceFiles/structs.cpp | 2 +- Telegram/SourceFiles/types.h | 2 +- 8 files changed, 351 insertions(+), 78 deletions(-) diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index e0d7c2308..4f6004062 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -148,6 +148,13 @@ void AnimatedGif::step_frame(float64 ms, bool timer) { if (img.size() != QSize(w, h)) img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); images[f] = img; frames[f] = QPixmap(); + for (int32 i = 0; i < f; ++i) { + if (!images[i].isNull() || !frames[i].isNull()) { + images[i] = QImage(); + frames[i] = QPixmap(); + break; + } + } } } if (frame != f) { @@ -253,3 +260,30 @@ const QPixmap &AnimatedGif::current(int32 width, int32 height, bool rounded) { } return frames[frame]; } + +ClipReader::ClipReader(const FileLocation &location, const QByteArray &data) : _state(ClipStopped) +, _location(location.isEmpty() ? 0 : new FileLocation(location)) +, _data(data) +, _width(0) +, _height(0) +, _rounded(false) +, _currentDisplayed(true) { +} + +void ClipReader::start(int32 framew, int32 frameh, bool rounded) { + _rounded = rounded; +} + +const QPixmap &ClipReader::current(int32 framew, int32 frameh) { + _currentDisplayed = true; + return _current; +} + +ClipState ClipReader::state() const { + return _state; +} + +ClipReader::~ClipReader() { + delete _location; + setBadPointer(_location); +} diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index e20191387..81d01efb2 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -470,3 +470,34 @@ private: Animation _a_frames; }; + +enum ClipState { + ClipPlaying, + ClipStopped, +}; + +class ClipReader { +public: + + ClipReader(const FileLocation &location, const QByteArray &data); + + void start(int32 framew, int32 frameh, bool rounded); + const QPixmap ¤t(int32 framew, int32 frameh); + + ClipState state() const; + + ~ClipReader(); + +private: + + ClipState _state; + + FileLocation *_location; + QByteArray _data; + int32 _width, _height; + bool _rounded; + + QPixmap _current; + bool _currentDisplayed; + +}; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 307f1dc5e..3815e262e 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3430,7 +3430,6 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b } // date - QString time(parent->timeText()); if (_caption.isEmpty()) { int32 fullRight = skipx + width, fullBottom = skipy + height; parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); @@ -3562,9 +3561,9 @@ void HistoryFileMedia::setLinks(ITextLink *openl, ITextLink *savel, ITextLink *c void HistoryFileMedia::setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const { _statusSize = newSize; if (_statusSize == FileStatusSizeReady) { - _statusText = (duration >= 0) ? formatDurationAndSizeText(duration, fullSize) : formatSizeText(fullSize); + _statusText = (duration >= 0) ? formatDurationAndSizeText(duration, fullSize) : (duration < -1 ? formatGifAndSizeText(fullSize) : formatSizeText(fullSize)); } else if (_statusSize == FileStatusSizeLoaded) { - _statusText = (duration >= 0) ? formatDurationText(duration) : formatSizeText(fullSize); + _statusText = (duration >= 0) ? formatDurationText(duration) : (duration < -1 ? qsl("GIF") : formatSizeText(fullSize)); } else if (_statusSize == FileStatusSizeFailed) { _statusText = lang(lng_attach_failed); } else if (_statusSize >= 0) { @@ -3618,7 +3617,7 @@ void HistoryFileMedia::checkAnimationFinished() { HistoryFileMedia::~HistoryFileMedia() { if (_animation) { delete _animation; - setBadLink(_animation); + setBadPointer(_animation); } } @@ -3876,7 +3875,6 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b p.drawTextLeft(statusX, statusY, w, _statusText, statusW - 2 * st::msgDateImgPadding.x()); // date - QString time(parent->timeText()); if (_caption.isEmpty()) { int32 fullRight = skipx + width, fullBottom = skipy + height; parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); @@ -4568,22 +4566,56 @@ ImagePtr HistoryDocument::replyPreview() { return _data->makeReplyPreview(); } -HistoryGif::HistoryGif(DocumentData *document) : HistoryMedia() +HistoryGif::HistoryGif(DocumentData *document) : HistoryFileMedia() , _data(document) -, _openl(new DocumentOpenLink(_data)) -, _savel(new DocumentSaveLink(_data)) -, _cancell(new DocumentCancelLink(_data)) -, _name(documentName(_data)) -, _statusSize(-1) { +, _thumbw(1) +, _thumbh(1) { + setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); + + setStatusSize(FileStatusSizeReady); + _data->thumb->load(); } void HistoryGif::initDimensions(const HistoryItem *parent) { + bool bubble = parent->hasBubble(); + int32 tw = 0, th = 0; if (parent == animated.msg) { - _maxw = animated.w / cIntRetinaFactor(); - _minh = animated.h / cIntRetinaFactor(); + tw = convertScale(animated.w / cIntRetinaFactor()); + th = convertScale(animated.h / cIntRetinaFactor()); } 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::maxMediaSize) { + th = (st::maxMediaSize * th) / tw; + tw = st::maxMediaSize; + } + if (th > st::maxMediaSize) { + tw = (st::maxMediaSize * tw) / th; + th = st::maxMediaSize; + } + if (!tw || !th) { + tw = th = 1; + } + _thumbw = tw; + _thumbh = th; + if (parent == animated.msg) { + _maxw = qMax(tw, int32(st::minPhotoSize)); + _minh = qMax(th, int32(st::minPhotoSize)); + } else { + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); + minWidth = qMax(minWidth, gifMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); + _maxw = qMax(tw, minWidth); + _minh = qMax(th, int32(st::minPhotoSize)); + } + w = _maxw; + if (bubble) { + _maxw += st::mediaPadding.left() + st::mediaPadding.right(); + _minh += st::mediaPadding.top() + st::mediaPadding.bottom(); } _height = _minh; } @@ -4592,25 +4624,115 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; int32 width = w, height = _height, skipx = 0, skipy = 0; - 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; - } + bool animating = (parent == animated.msg); + bool bubble = parent->hasBubble(); + bool fromChannel = parent->fromChannel(), out = parent->out(), outbg = out && !fromChannel; - App::roundShadow(p, 0, 0, pw, ph, selected ? st::msgInShadowSelected : 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); + if (!animating) { + if (_data->loader) { + ensureAnimation(parent); + if (!_animation->radial.animating()) { + _animation->radial.start(_data->progress()); + } } - return; + updateStatusText(parent); + } + bool radial = !animating && isRadialAnimation(ms); + + if (bubble) { + skipx = st::mediaPadding.left(); + skipy = st::mediaPadding.top(); + + width -= st::mediaPadding.left() + st::mediaPadding.right(); + height -= skipy + st::mediaPadding.bottom(); + } else { + App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); + } + _data->thumb->checkload(); + + QRect rthumb(rtlrect(skipx, skipy, width, height, w)); + + if (animating) { + p.drawPixmap(rthumb.topLeft(), animated.current(width, height, true)); + } else { + p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_thumbw, _thumbh, width, height)); + } + if (selected) { + App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } + if (!animating) { + 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 (_animation && _animation->_a_thumbOver.animating()) { + _animation->_a_thumbOver.step(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->loader ? _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 (!_data->already().isEmpty()) { + icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); + } else if (_data->loader) { + 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, 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, w), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + p.setFont(st::normalFont); + p.setPen(st::white); + p.drawTextLeft(statusX, statusY, w, _statusText, statusW - 2 * st::msgDateImgPadding.x()); + + // date + int32 fullRight = skipx + width, fullBottom = skipy + height; + parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); + } +} + +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->loader) { + statusSize = _data->loader->currentOffset(); + } else if (!_data->already().isEmpty()) { + statusSize = FileStatusSizeLoaded; + } else { + statusSize = FileStatusSizeReady; + } + if (statusSize != _statusSize) { + setStatusSize(statusSize); + } } void HistoryGif::regItem(HistoryItem *item) { @@ -4628,40 +4750,65 @@ void HistoryGif::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, b } int32 HistoryGif::resize(int32 width, const HistoryItem *parent) { - w = qMin(width, _maxw); + bool bubble = parent->hasBubble(); + + int32 tw = 0, th = 0; if (parent == animated.msg) { - 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; - } + tw = convertScale(animated.w / cIntRetinaFactor()); + th = convertScale(animated.h / cIntRetinaFactor()); } else { - _height = _minh; + 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::maxMediaSize) { + th = (st::maxMediaSize * th) / tw; + tw = st::maxMediaSize; + } + if (th > st::maxMediaSize) { + tw = (st::maxMediaSize * tw) / th; + th = st::maxMediaSize; + } + 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; + } + w = _thumbw = tw; + _thumbh = th; + + if (parent != animated.msg) { + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); + minWidth = qMax(minWidth, gifMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); + w = qMax(w, minWidth); + } + + _height = qMax(th, int32(st::minPhotoSize)); + if (bubble) { + w += st::mediaPadding.left() + st::mediaPadding.right(); + _height += st::mediaPadding.top() + st::mediaPadding.bottom(); } return _height; } const QString HistoryGif::inDialogsText() const { - return _name.isEmpty() ? lang(lng_in_dlg_file) : _name; + return _data->name.isEmpty() ? lang(lng_in_dlg_file) : _data->name; } const QString HistoryGif::inHistoryText() const { - return qsl("[ ") + lang(lng_in_dlg_file) + (_name.isEmpty() ? QString() : (qsl(" : ") + _name)) + qsl(" ]"); + return qsl("[ ") + lang(lng_in_dlg_file) + (_data->name.isEmpty() ? QString() : (qsl(" : ") + _data->name)) + qsl(" ]"); } bool HistoryGif::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const { if (width < 0) width = w; - if (width >= _maxw) { - width = _maxw; - } - 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); } @@ -4670,29 +4817,75 @@ int32 HistoryGif::countHeight(const HistoryItem *parent, int32 width) const { if (width >= _maxw) { width = _maxw; } + + bool bubble = parent->hasBubble(); + + int32 tw = 0, th = 0; if (parent == animated.msg) { - int32 h = (width == w) ? _height : (width * animated.h / animated.w); - if (h < 1) h = 1; - return h; + tw = convertScale(animated.w / cIntRetinaFactor()); + th = convertScale(animated.h / cIntRetinaFactor()); + } else { + tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height()); + if (!tw || !th) { + tw = convertScale(_data->thumb->width()); + th = convertScale(_data->thumb->height()); + } } - return _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; + } + 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; + } + + int32 h = qMax(th, int32(st::minPhotoSize)); + if (bubble) { + tw += st::mediaPadding.left() + st::mediaPadding.right(); + h += st::mediaPadding.top() + st::mediaPadding.bottom(); + } + return h; } void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const { + bool bubble = parent->hasBubble(); + 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; + int skipx = 0, skipy = 0, height = _height; + if (bubble) { + skipx = st::mediaPadding.left(); + skipy = st::mediaPadding.top(); + width -= st::mediaPadding.left() + st::mediaPadding.right(); + height -= skipy + st::mediaPadding.bottom(); } - 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(); + if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { + if (parent == animated.msg) { + lnk = _openl; + } else { + lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; + } + + int32 fullRight = skipx + width, fullBottom = skipy + height; + bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage); + if (inDate) { + state = HistoryInDateCursorState; + } return; } - } HistoryMedia *HistoryGif::clone() const { @@ -6452,6 +6645,8 @@ void HistoryMessage::initMediaFromText(QString ¤tText) { void HistoryMessage::initMediaFromDocument(DocumentData *doc) { if (doc->sticker()) { _media = new HistorySticker(doc); + } else if (doc->type == AnimatedDocument) { + _media = new HistoryGif(doc); } else { _media = new HistoryDocument(doc); } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index a87b91f78..0b98b362c 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1015,9 +1015,6 @@ public: virtual int32 timeLeft() const { return 0; } - virtual QString timeText() const { - return QString(); - } virtual int32 timeWidth() const { return 0; } @@ -1326,6 +1323,7 @@ protected: 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); @@ -1559,7 +1557,7 @@ private: }; -class HistoryGif : public HistoryMedia { +class HistoryGif : public HistoryFileMedia { public: HistoryGif(DocumentData *document); @@ -1603,18 +1601,32 @@ public: bool customInfoLayout() const { return true; } + bool hideFromName() const { + return true; + } + bool hideForwardedFrom() const { + return true; + } + +protected: + + float64 dataProgress() const { + return _data->progress(); + } + bool dataFinished() const { + return !_data->loader; + } + bool dataLoaded() const { + return !_data->already().isEmpty() && !_data->data.isEmpty(); + } private: DocumentData *_data; - TextLinkPtr _openl, _savel, _cancell; + int32 _thumbw, _thumbh; - int32 _namew; - QString _name, _size; - int32 _thumbw, _thumbx, _thumby; - - mutable QString _statusText; - mutable int32 _statusSize; // -1 will contain just size string, -2 will contain "failed" language key + void setStatusSize(int32 newSize) const; + void updateStatusText(const HistoryItem *parent) const; }; @@ -1980,9 +1992,6 @@ public: } return result; } - QString timeText() const { - return _timeText; - } int32 timeWidth() const { return _timeWidth; } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index b95941913..7a637791d 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -5366,6 +5366,8 @@ void HistoryWidget::onDocumentUploaded(const FullMsgId &newId, const MTPInputFil DocumentData *document = 0; if (HistoryDocument *media = dynamic_cast(item->getMedia())) { document = media->document(); + } else if (HistoryGif *media = dynamic_cast(item->getMedia())) { + document = media->document(); } else if (HistorySticker *media = dynamic_cast(item->getMedia())) { document = media->document(); } @@ -5395,6 +5397,8 @@ void HistoryWidget::onThumbDocumentUploaded(const FullMsgId &newId, const MTPInp DocumentData *document = 0; if (HistoryDocument *media = dynamic_cast(item->getMedia())) { document = media->document(); + } else if (HistoryGif *media = dynamic_cast(item->getMedia())) { + document = media->document(); } else if (HistorySticker *media = dynamic_cast(item->getMedia())) { document = media->document(); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 97a9ad020..7ed9b96ac 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1888,7 +1888,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { if (location.accessEnable()) { QImageReader reader(location.name()); if (reader.canRead()) { - if (reader.supportsAnimation() && reader.imageCount() > 1 && item) { + if (reader.supportsAnimation() && reader.imageCount() > 1 && item && item->getMedia() && item->getMedia()->type() == MediaTypeGif) { startGif(item, location); } else if (item) { App::wnd()->showDocument(document, item); diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 1443a40cc..eff73cd29 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -888,7 +888,7 @@ void DocumentOpenLink::doOpen(DocumentData *data) { } else if (data->size < MediaViewImageSizeLimit && location.accessEnable()) { QImageReader reader(location.name()); if (reader.canRead()) { - if (reader.supportsAnimation() && reader.imageCount() > 1 && App::hoveredLinkItem()) { + if (reader.supportsAnimation() && reader.imageCount() > 1 && App::hoveredLinkItem() && App::hoveredLinkItem()->getMedia() && App::hoveredLinkItem()->getMedia()->type() == MediaTypeGif) { startGif(App::hoveredLinkItem(), location); } else if (App::hoveredLinkItem() || App::contextItem()) { App::wnd()->showDocument(data, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem()); diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index cf0ede271..010e6d43c 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -21,7 +21,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #pragma once template -void setBadLink(Type *&link) { +void setBadPointer(Type *&link) { link = reinterpret_cast(0x00000bad); } From 5e0e0d23918c5fff003ec27c76bb60d4ad7c95dd Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 15 Dec 2015 17:50:51 +0300 Subject: [PATCH 022/145] new gif playing from separate threads --- Telegram/SourceFiles/app.cpp | 14 + Telegram/SourceFiles/app.h | 5 + Telegram/SourceFiles/config.h | 7 +- Telegram/SourceFiles/gui/animation.cpp | 493 ++++++++++++++++++++++++- Telegram/SourceFiles/gui/animation.h | 111 +++++- Telegram/SourceFiles/history.cpp | 79 ++-- Telegram/SourceFiles/history.h | 7 +- Telegram/SourceFiles/mainwidget.cpp | 13 +- Telegram/SourceFiles/structs.cpp | 12 +- 9 files changed, 669 insertions(+), 72 deletions(-) diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index d54927271..f8cafc0c9 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -74,6 +74,7 @@ namespace { DocumentItems documentItems; WebPageItems webPageItems; SharedContactItems sharedContactItems; + GifItems gifItems; typedef QMap > RepliesTo; RepliesTo repliesTo; @@ -1991,6 +1992,7 @@ namespace App { ::documentItems.clear(); ::webPageItems.clear(); ::sharedContactItems.clear(); + ::gifItems.clear(); ::repliesTo.clear(); lastPhotos.clear(); lastPhotosMap.clear(); @@ -2425,6 +2427,18 @@ namespace App { return ::sharedContactItems; } + void regGifItem(ClipReader *reader, HistoryItem *item) { + ::gifItems.insert(reader, item); + } + + void unregGifItem(ClipReader *reader) { + ::gifItems.remove(reader); + } + + const GifItems &gifItems() { + return ::gifItems; + } + QString phoneFromSharedContact(int32 userId) { SharedContactItems::const_iterator i = ::sharedContactItems.constFind(userId); if (i != ::sharedContactItems.cend()) { diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 23ab34bd3..465c2fa7c 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -39,6 +39,7 @@ typedef QMap AudioItems; typedef QMap DocumentItems; typedef QMap WebPageItems; typedef QMap SharedContactItems; +typedef QMap GifItems; struct ReplyMarkup { ReplyMarkup(int32 flags = 0) : flags(flags) { } @@ -250,6 +251,10 @@ namespace App { const SharedContactItems &sharedContactItems(); QString phoneFromSharedContact(int32 userId); + void regGifItem(ClipReader *reader, HistoryItem *item); + void unregGifItem(ClipReader *reader); + const GifItems &gifItems(); + void regMuted(PeerData *peer, int32 changeIn); void unregMuted(PeerData *peer); void updateMuted(); diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 8a5457413..3888f20d6 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -83,6 +83,7 @@ enum { LocalEncryptKeySize = 256, // 2048 bit AnimationTimerDelta = 7, + ClipThreadsCount = 8, SaveRecentEmojisTimeout = 3000, // 3 secs SaveWindowPositionTimeout = 1000, // 1 sec @@ -108,12 +109,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, // 1 Mb stickers hold in memory, auto loaded and displayed inline StickerMaxSize = 2048, // 2048x2048 is a max image size for sticker + AnimationInMemory = 2 * 1024 * 1024, // 2 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 diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 4f6004062..2fe92a5fb 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -27,6 +27,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org namespace { AnimationManager *_manager = 0; + QVector _clipThreads; + QVector _clipManagers; }; namespace anim { @@ -79,13 +81,24 @@ namespace anim { } void startManager() { - delete _manager; + stopManager(); + _manager = new AnimationManager(); + } void stopManager() { 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); + } + _clipThreads.clear(); + _clipManagers.clear(); + } } } @@ -105,6 +118,27 @@ void Animation::stop() { _manager->stop(this); } +void AnimationManager::clipReinit(ClipReader *reader) { + const GifItems &items(App::gifItems()); + GifItems::const_iterator it = items.constFind(reader); + if (it != items.cend()) { + it.value()->initDimensions(); + if (App::main()) emit App::main()->itemResized(it.value(), true); + } +} + +void AnimationManager::clipRedraw(ClipReader *reader) { + if (reader->currentDisplayed()) { + return; + } + + const GifItems &items(App::gifItems()); + GifItems::const_iterator it = items.constFind(reader); + if (it != items.cend()) { + Ui::redrawHistoryItem(it.value()); + } +} + void AnimatedGif::step_frame(float64 ms, bool timer) { int32 f = frame; while (f < images.size() && ms > delays[f]) { @@ -261,29 +295,460 @@ const QPixmap &AnimatedGif::current(int32 width, int32 height, bool rounded) { return frames[frame]; } -ClipReader::ClipReader(const FileLocation &location, const QByteArray &data) : _state(ClipStopped) -, _location(location.isEmpty() ? 0 : new FileLocation(location)) -, _data(data) +QPixmap _prepareFrame(const ClipFrameRequest &request, const QImage &original, QImage &cache, bool smooth) { + bool badSize = (original.width() != request.framew) || (original.height() != request.frameh); + bool needOuter = (request.outerw != request.framew) || (request.outerh != request.frameh); + if (badSize || needOuter || request.rounded) { + int32 factor(request.factor); + bool fill = false; + if (cache.width() != request.outerw || cache.height() != request.outerh) { + cache = QImage(request.outerw, request.outerh, QImage::Format_ARGB32_Premultiplied); + if (request.framew < request.outerw || request.frameh < request.outerh || original.hasAlphaChannel()) { + fill = true; + } + cache.setDevicePixelRatio(factor); + } + { + Painter p(&cache); + if (fill) p.fillRect(0, 0, cache.width() / factor, cache.height() / factor, st::black); + if (smooth && badSize) p.setRenderHint(QPainter::SmoothPixmapTransform); + QRect to((request.outerw - request.framew) / (2 * factor), (request.outerh - request.frameh) / (2 * factor), request.framew / factor, request.frameh / factor); + QRect from(0, 0, original.width() / factor, original.height() / factor); + p.drawImage(to, original, from, Qt::ColorOnly); + } + 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) : _state(ClipReading) , _width(0) , _height(0) -, _rounded(false) -, _currentDisplayed(true) { +, _currentDisplayed(1) +, _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 = rand() % _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); } -void ClipReader::start(int32 framew, int32 frameh, bool rounded) { - _rounded = rounded; +void ClipReader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded) { + int32 factor(cIntRetinaFactor()); + _request.factor = factor; + _request.framew = framew * factor; + _request.frameh = frameh * factor; + _request.outerw = outerw * factor; + _request.outerh = outerh * factor; + _request.rounded = rounded; + _clipManagers.at(_threadIndex)->start(this); } -const QPixmap &ClipReader::current(int32 framew, int32 frameh) { - _currentDisplayed = true; - return _current; +QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh) { + _currentDisplayed.storeRelease(1); + + int32 factor(cIntRetinaFactor()); + QPixmap result(_current); + if (result.width() == outerw * factor && result.height() == outerh * factor) { + return result; + } + + _request.framew = framew * factor; + _request.frameh = frameh * factor; + _request.outerw = outerw * factor; + _request.outerh = outerh * factor; + + QImage current(_currentOriginal); + result = _current = QPixmap(); + result = _current = _prepareFrame(_request, current, _cacheForResize, true); + + _clipManagers.at(_threadIndex)->update(this); + + return result; +} + +bool ClipReader::ready() const { + if (_width && _height) return true; + + QImage first(_currentOriginal); + if (first.isNull()) return false; + + _width = first.width(); + _height = first.height(); + return true; +} + +int32 ClipReader::width() const { + return _width; +} + +int32 ClipReader::height() const { + return _height; } ClipState ClipReader::state() const { return _state; } -ClipReader::~ClipReader() { - delete _location; - setBadPointer(_location); +void ClipReader::stop() { + _clipManagers.at(_threadIndex)->stop(this); + _width = _height = 0; +} + +void ClipReader::error() { + _private = 0; + _state = ClipError; +} + +ClipReader::~ClipReader() { + stop(); +} + +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) + , _buffer(_data.isEmpty() ? 0 : &_data) + , _reader(0) + , _currentMs(0) + , _nextUpdateMs(0) { + + if (_data.isEmpty() && !_location->accessEnable()) { + error(); + return; + } + _accessed = true; + } + + ClipProcessResult start(uint64 ms) { + _nextUpdateMs = ms + 86400 * 1000ULL; + if (!_reader && !restartReader(true)) { + return error(); + } + if (_currentOriginal.isNull()) { + if (!readNextFrame(_currentOriginal)) { + return error(); + } + --_framesLeft; + return ClipProcessReinit; + } + 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 (_current.isNull()) { // first frame read, but not yet prepared + _currentOriginal.setDevicePixelRatio(_request.factor); + + _currentMs = ms; + _current = _prepareFrame(_request, _currentOriginal, _currentCache, true); + + if (!prepareNextFrame()) { + return error(); + } + return ClipProcessStarted; + } else if (ms >= _nextUpdateMs) { + swapBuffers(); + return ClipProcessRedraw; + } + return ClipProcessWait; + } + + ClipProcessResult finishProcess(uint64 ms) { + if (!prepareNextFrame()) { + return error(); + } + + if (ms >= _nextUpdateMs) { // we are late + swapBuffers(ms); // keep up + return ClipProcessRedraw; + } + return ClipProcessWait; + } + + uint64 nextFrameDelay() { + return qMax(_reader->nextImageDelay(), 5); + } + + void swapBuffers(uint64 ms = 0) { + _currentMs = qMax(ms, _nextUpdateMs); + qSwap(_currentOriginal, _nextOriginal); + qSwap(_current, _next); + qSwap(_currentCache, _nextCache); + } + + bool readNextFrame(QImage &to) { + QImage frame; // QGifHandler always reads first to internal QImage and returns it + if (!_reader->read(&frame)) { + return false; + } + 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(); + } + return true; + } + + bool prepareNextFrame() { + _nextUpdateMs = _currentMs + nextFrameDelay(); + if (!_framesLeft) { + if (_reader->jumpToImage(0)) { + _framesLeft = _reader->imageCount(); + } else if (!restartReader()) { + return false; + } + } + if (!readNextFrame(_nextOriginal)) { + return false; + } + _nextOriginal.setDevicePixelRatio(_request.factor); + --_framesLeft; + _next = QPixmap(); + _next = _prepareFrame(_request, _nextOriginal, _nextCache, true); + return true; + } + + bool restartReader(bool first = false) { + if (first && _data.isEmpty() && QFileInfo(_location->name()).size() <= AnimationInMemory) { + QFile f(_location->name()); + if (f.open(QIODevice::ReadOnly)) { + _data = f.readAll(); + if (f.error() == QFile::NoError) { + _buffer.setBuffer(&_data); + } else { + _data = QByteArray(); + } + } + } else if (!_data.isEmpty()) { + _buffer.close(); + } + delete _reader; + + if (_data.isEmpty()) { + _reader = new QImageReader(_location->name()); + } else { + _reader = new QImageReader(&_buffer); + } + if (!_reader->canRead() || !_reader->supportsAnimation()) { + return false; + } + _framesLeft = _reader->imageCount(); + if (_framesLeft < 1) { + return false; + } + return true; + } + + ClipProcessResult error() { + stop(); + _state = ClipError; + return ClipProcessError; + } + + void stop() { + delete _reader; + _reader = 0; + + if (_location) { + if (_accessed) { + _location->accessDisable(); + } + delete _location; + _location = 0; + } + _accessed = false; + } + + ~ClipReaderPrivate() { + stop(); + setBadPointer(_location); + setBadPointer(_reader); + } + +private: + + ClipReader *_interface; + ClipState _state; + + QByteArray _data; + FileLocation *_location; + bool _accessed; + + QBuffer _buffer; + QImageReader *_reader; + + ClipFrameRequest _request; + QPixmap _current, _next; + QImage _currentOriginal, _nextOriginal, _currentCache, _nextCache; + + int32 _framesLeft; + uint64 _currentMs, _nextUpdateMs; + + friend class ClipReadManager; + +}; + +ClipReadManager::ClipReadManager(QThread *thread) : _processingInThread(0) { + moveToThread(thread); + connect(thread, SIGNAL(started()), this, SLOT(process())); + 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(reinit(ClipReader*)), _manager, SLOT(clipReinit(ClipReader*))); + connect(this, SIGNAL(redraw(ClipReader*)), _manager, SLOT(clipRedraw(ClipReader*))); +} + +void ClipReadManager::append(ClipReader *reader, const FileLocation &location, const QByteArray &data) { + reader->_private = new ClipReaderPrivate(reader, location, data); + update(reader); +} + +void ClipReadManager::start(ClipReader *reader) { + update(reader); +} + +void ClipReadManager::update(ClipReader *reader) { + QMutexLocker lock(&_readerPointersMutex); + _readerPointers.insert(reader, reader->_private); + emit processDelayed(); +} + +void ClipReadManager::stop(ClipReader *reader) { + QMutexLocker lock(&_readerPointersMutex); + _readerPointers.remove(reader); + emit processDelayed(); +} + +bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result) { + QMutexLocker lock(&_readerPointersMutex); + ReaderPointers::iterator it = _readerPointers.find(reader->_interface); + if (result == ClipProcessError) { + if (it != _readerPointers.cend()) { + it.key()->error(); + _readerPointers.erase(it); + it = _readerPointers.end(); + } + } + if (it == _readerPointers.cend()) { + return false; + } + + if (result == ClipProcessReinit || result == ClipProcessRedraw || result == ClipProcessStarted) { + it.key()->_current = reader->_current; + it.key()->_currentOriginal = reader->_currentOriginal; + it.key()->_currentDisplayed.storeRelease(0); + if (result == ClipProcessReinit) { + emit reinit(it.key()); + } else if (result == ClipProcessRedraw) { + emit redraw(it.key()); + } + } + return true; +} + +ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) { + if (!handleProcessResult(reader, result)) { + delete reader; + return ResultHandleRemove; + } + + _processingInThread->eventDispatcher()->processEvents(QEventLoop::AllEvents); + if (_processingInThread->isInterruptionRequested()) { + return ResultHandleStop; + } + + if (result == ClipProcessRedraw) { + return handleResult(reader, reader->finishProcess(ms), ms); + } + + return ResultHandleContinue; +} + +void ClipReadManager::process() { + if (_processingInThread) return; + + _timer.stop(); + _processingInThread = thread(); + + uint64 ms = getms(), minms = ms + 86400 * 1000ULL; + { + QMutexLocker lock(&_readerPointersMutex); + for (ReaderPointers::iterator i = _readerPointers.begin(), e = _readerPointers.end(); i != e; ++i) { + if (i.value()) { + Readers::iterator it = _readers.find(i.value()); + if (it == _readers.cend()) { + _readers.insert(i.value(), 0); + } else { + it.value() = ms; + } + i.value()->_request = i.key()->_request; + i.value() = 0; + } + } + } + + for (Readers::iterator i = _readers.begin(), e = _readers.end(); i != e;) { + if (i.value() <= ms) { + ClipProcessResult result = i.key()->process(ms); + + ResultHandleState state = handleResult(i.key(), result, ms); + if (state == ResultHandleRemove) { + i = _readers.erase(i); + continue; + } else if (state == ResultHandleStop) { + _processingInThread = 0; + return; + } + i.value() = i.key()->_nextUpdateMs; + } + if (i.value() < minms) { + minms = i.value(); + } + ++i; + } + + ms = getms(); + if (minms <= ms) { + _timer.start(1); + } else { + _timer.start(minms - ms); + } + + _processingInThread = 0; } diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index 81d01efb2..d2c9b4e5f 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -335,6 +335,8 @@ AnimationCallbacks *animation(Param param, Type *obj, typename AnimationCallback return new AnimationCallbacksAbsoluteWithParam(param, obj, method); } +class ClipReader; + class AnimationManager : public QObject { Q_OBJECT @@ -403,6 +405,9 @@ public slots: } } + void clipReinit(ClipReader *reader); + void clipRedraw(ClipReader *reader); + private: typedef QMap AnimatingObjects; @@ -472,19 +477,45 @@ private: }; enum ClipState { - ClipPlaying, - ClipStopped, + 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; +}; + +class ClipReaderPrivate; class ClipReader { public: ClipReader(const FileLocation &location, const QByteArray &data); - void start(int32 framew, int32 frameh, bool rounded); - const QPixmap ¤t(int32 framew, int32 frameh); + void start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded); + QPixmap current(int32 framew, int32 frameh, int32 outerw, int32 outerh); + bool currentDisplayed() const { + return _currentDisplayed.loadAcquire() > 0; + } + + int32 width() const; + int32 height() const; ClipState state() const; + bool started() const { + return _request.valid(); + } + bool ready() const; + + void stop(); + void error(); ~ClipReader(); @@ -492,12 +523,74 @@ private: ClipState _state; - FileLocation *_location; - QByteArray _data; - int32 _width, _height; - bool _rounded; + ClipFrameRequest _request; + + mutable int32 _width, _height; QPixmap _current; - bool _currentDisplayed; + QImage _currentOriginal, _cacheForResize; + QAtomicInt _currentDisplayed; + int32 _threadIndex; + + friend class ClipReadManager; + + ClipReaderPrivate *_private; + +}; + +enum ClipProcessResult { + ClipProcessError, + ClipProcessStarted, + ClipProcessReinit, + ClipProcessRedraw, + ClipProcessWait, +}; + +class ClipReadManager : public QObject { + Q_OBJECT + +public: + + ClipReadManager(QThread *thread); + int32 loadLevel() const { + return _loadLevel.loadAcquire(); + } + void append(ClipReader *reader, const FileLocation &location, const QByteArray &data); + void start(ClipReader *reader); + void update(ClipReader *reader); + void stop(ClipReader *reader); + +signals: + + void processDelayed(); + + void reinit(ClipReader *reader); + void redraw(ClipReader *reader); + +public slots: + + void process(); + +private: + + QAtomicInt _loadLevel; + typedef QMap ReaderPointers; + ReaderPointers _readerPointers; + QMutex _readerPointersMutex; + + bool handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result); + + enum ResultHandleState { + ResultHandleRemove, + ResultHandleStop, + ResultHandleContinue, + }; + ResultHandleState handleResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms); + + typedef QMap Readers; + Readers _readers; + + QTimer _timer; + QThread *_processingInThread; }; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 3815e262e..3982977e4 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -4569,7 +4569,8 @@ ImagePtr HistoryDocument::replyPreview() { HistoryGif::HistoryGif(DocumentData *document) : HistoryFileMedia() , _data(document) , _thumbw(1) -, _thumbh(1) { +, _thumbh(1) +, _gif(0) { setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); setStatusSize(FileStatusSizeReady); @@ -4580,9 +4581,9 @@ HistoryGif::HistoryGif(DocumentData *document) : HistoryFileMedia() void HistoryGif::initDimensions(const HistoryItem *parent) { bool bubble = parent->hasBubble(); int32 tw = 0, th = 0; - if (parent == animated.msg) { - tw = convertScale(animated.w / cIntRetinaFactor()); - th = convertScale(animated.h / cIntRetinaFactor()); + 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) { @@ -4603,20 +4604,17 @@ void HistoryGif::initDimensions(const HistoryItem *parent) { } _thumbw = tw; _thumbh = th; - if (parent == animated.msg) { - _maxw = qMax(tw, int32(st::minPhotoSize)); - _minh = qMax(th, int32(st::minPhotoSize)); - } else { - int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - minWidth = qMax(minWidth, gifMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); - _maxw = qMax(tw, minWidth); - _minh = qMax(th, int32(st::minPhotoSize)); + _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())); } - w = _maxw; if (bubble) { _maxw += st::mediaPadding.left() + st::mediaPadding.right(); _minh += st::mediaPadding.top() + st::mediaPadding.bottom(); } + w = _maxw; _height = _minh; } @@ -4624,7 +4622,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; int32 width = w, height = _height, skipx = 0, skipy = 0; - bool animating = (parent == animated.msg); + bool animating = (_gif && _gif->started()); bool bubble = parent->hasBubble(); bool fromChannel = parent->fromChannel(), out = parent->out(), outbg = out && !fromChannel; @@ -4653,7 +4651,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo QRect rthumb(rtlrect(skipx, skipy, width, height, w)); if (animating) { - p.drawPixmap(rthumb.topLeft(), animated.current(width, height, true)); + p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, width, height)); } else { p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_thumbw, _thumbh, width, height)); } @@ -4753,9 +4751,9 @@ int32 HistoryGif::resize(int32 width, const HistoryItem *parent) { bool bubble = parent->hasBubble(); int32 tw = 0, th = 0; - if (parent == animated.msg) { - tw = convertScale(animated.w / cIntRetinaFactor()); - th = convertScale(animated.h / cIntRetinaFactor()); + 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) { @@ -4782,20 +4780,24 @@ int32 HistoryGif::resize(int32 width, const HistoryItem *parent) { th = qRound((width / float64(tw)) * th); tw = width; } - w = _thumbw = tw; + _thumbw = tw; _thumbh = th; - if (parent != animated.msg) { - int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); - minWidth = qMax(minWidth, gifMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); - w = qMax(w, minWidth); - } - + w = qMax(tw, int32(st::minPhotoSize)); _height = qMax(th, int32(st::minPhotoSize)); + if (_gif && _gif->ready()) { + if (!_gif->started()) { + _gif->start(_thumbw, _thumbh, w, _height, true); + } + } else { + w = qMax(w, parent->infoWidth() + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); + w = qMax(w, gifMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); + } if (bubble) { w += st::mediaPadding.left() + st::mediaPadding.right(); _height += st::mediaPadding.top() + st::mediaPadding.bottom(); } + return _height; } @@ -4821,9 +4823,9 @@ int32 HistoryGif::countHeight(const HistoryItem *parent, int32 width) const { bool bubble = parent->hasBubble(); int32 tw = 0, th = 0; - if (parent == animated.msg) { - tw = convertScale(animated.w / cIntRetinaFactor()); - th = convertScale(animated.h / cIntRetinaFactor()); + if (_gif && _gif->started()) { + tw = convertScale(_gif->width()); + th = convertScale(_gif->height()); } else { tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height()); if (!tw || !th) { @@ -4873,7 +4875,7 @@ void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, height -= skipy + st::mediaPadding.bottom(); } if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { - if (parent == animated.msg) { + if (_gif && _gif->started()) { lnk = _openl; } else { lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; @@ -4896,6 +4898,25 @@ ImagePtr HistoryGif::replyPreview() { return _data->makeReplyPreview(); } +void HistoryGif::play(HistoryItem *parent) { + if (_gif) { + App::unregGifItem(_gif); + delete _gif; + _gif = 0; + } else { + _gif = new ClipReader(_data->location(), _data->data); + App::regGifItem(_gif, parent); + } +} + +HistoryGif::~HistoryGif() { + if (_gif) { + App::unregGifItem(_gif); + delete _gif; + setBadPointer(_gif); + } +} + HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia() , pixw(1), pixh(1), data(document), lastw(0) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 0b98b362c..c4f6a0aae 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1592,9 +1592,6 @@ public: } ImagePtr replyPreview(); - void drawInPlaylist(Painter &p, const HistoryItem *parent, bool selected, bool over, int32 width) const; - TextLinkPtr linkInPlaylist(); - bool needsBubble(const HistoryItem *parent) const { return parent->toHistoryReply(); } @@ -1608,6 +1605,9 @@ public: return true; } + void play(HistoryItem *parent); + ~HistoryGif(); + protected: float64 dataProgress() const { @@ -1624,6 +1624,7 @@ private: DocumentData *_data; int32 _thumbw, _thumbh; + ClipReader *_gif; void setStatusSize(int32 newSize) const; void updateStatusText(const HistoryItem *parent) const; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 7ed9b96ac..a67beecb7 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -437,7 +437,6 @@ MainWidget::MainWidget(Window *window) : TWidget(window) connect(&_topBar, SIGNAL(clicked()), this, SLOT(onTopBarClick())); connect(&history, SIGNAL(historyShown(History*,MsgId)), this, SLOT(onHistoryShown(History*,MsgId))); connect(&updateNotifySettingTimer, SIGNAL(timeout()), this, SLOT(onUpdateNotifySettings())); - connect(this, SIGNAL(showPeerAsync(quint64,qint32)), this, SLOT(showPeerHistory(quint64,qint32)), Qt::QueuedConnection); if (audioPlayer()) { connect(audioPlayer(), SIGNAL(updated(const AudioMsgId&)), this, SLOT(audioPlayProgress(const AudioMsgId&))); connect(audioPlayer(), SIGNAL(stopped(const AudioMsgId&)), this, SLOT(audioPlayProgress(const AudioMsgId&))); @@ -1886,17 +1885,15 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { } else if (document->openOnSave > 0 && document->size < MediaViewImageSizeLimit) { const FileLocation &location(document->location(true)); if (location.accessEnable()) { - QImageReader reader(location.name()); - if (reader.canRead()) { - if (reader.supportsAnimation() && reader.imageCount() > 1 && item && item->getMedia() && item->getMedia()->type() == MediaTypeGif) { - startGif(item, location); - } else if (item) { + if (item && item->getMedia() && item->getMedia()->type() == MediaTypeGif) { + static_cast(item->getMedia())->play(item); + } else { + QImageReader reader(location.name()); + if (reader.canRead() && item) { App::wnd()->showDocument(document, item); } else { psOpenFile(already); } - } else { - psOpenFile(already); } location.accessDisable(); } else { diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index eff73cd29..6dda51a59 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -886,17 +886,15 @@ void DocumentOpenLink::doOpen(DocumentData *data) { if (App::main()) App::main()->documentPlayProgress(song); } } else if (data->size < MediaViewImageSizeLimit && location.accessEnable()) { - QImageReader reader(location.name()); - if (reader.canRead()) { - if (reader.supportsAnimation() && reader.imageCount() > 1 && App::hoveredLinkItem() && App::hoveredLinkItem()->getMedia() && App::hoveredLinkItem()->getMedia()->type() == MediaTypeGif) { - startGif(App::hoveredLinkItem(), location); - } else if (App::hoveredLinkItem() || App::contextItem()) { + if (App::hoveredLinkItem() && App::hoveredLinkItem()->getMedia() && App::hoveredLinkItem()->getMedia()->type() == MediaTypeGif) { + static_cast(App::hoveredLinkItem()->getMedia())->play(App::hoveredLinkItem()); + } else { + QImageReader reader(location.name()); + if (reader.canRead() && (App::hoveredLinkItem() || App::contextItem())) { App::wnd()->showDocument(data, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem()); } else { psOpenFile(location.name()); } - } else { - psOpenFile(location.name()); } location.accessDisable(); } else { From 7f6f92ac85a84650b563cef0c3129cc63e142606 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 15 Dec 2015 18:04:27 +0300 Subject: [PATCH 023/145] fixed bidi in InputField --- Telegram/SourceFiles/gui/flatinput.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Telegram/SourceFiles/gui/flatinput.cpp b/Telegram/SourceFiles/gui/flatinput.cpp index dd387c23e..5d0a25ace 100644 --- a/Telegram/SourceFiles/gui/flatinput.cpp +++ b/Telegram/SourceFiles/gui/flatinput.cpp @@ -1300,7 +1300,6 @@ InputField::InputField(QWidget *parent, const style::InputField &st, const QStri resize(_st.width, _st.height); _inner.setWordWrapMode(QTextOption::NoWrap); - _inner.setLineWrapMode(QTextEdit::NoWrap); setAttribute(Qt::WA_OpaquePaintEvent); From 2aa44a2135035b165bf638386600aa0cff4d59ea Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 15 Dec 2015 18:06:51 +0300 Subject: [PATCH 024/145] fixed warnings --- Telegram/SourceFiles/history.h | 2 +- Telegram/SourceFiles/window.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 85e029dc3..f183c12e8 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -50,7 +50,7 @@ public: typedef QHash Map; Map map; - Histories() : unreadFull(0), unreadMuted(0), _a_typings(animation(this, &Histories::step_typings)) { + Histories() : _a_typings(animation(this, &Histories::step_typings)), unreadFull(0), unreadMuted(0) { } void regSendAction(History *history, UserData *user, const MTPSendMessageAction &action); diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index 3f067221e..bdbbb9791 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -83,8 +83,8 @@ NotifyWindow::NotifyWindow(HistoryItem *msg, int32 x, int32 y, int32 fwdCount) : , hiding(false) , _index(0) , a_opacity(0) -, a_y(y + st::notifyHeight + st::notifyDeltaY) , a_func(anim::linear) +, a_y(y + st::notifyHeight + st::notifyDeltaY) , _a_appearance(animation(this, &NotifyWindow::step_appearance)) { updateNotifyDisplay(); From f88dd15647bb9e81e807a6e024dec1e52ad0c576 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 15 Dec 2015 18:34:28 +0300 Subject: [PATCH 025/145] fixed animation deinit, fixed retina animations --- Telegram/SourceFiles/gui/animation.cpp | 34 +++++++++++++++++++++++--- Telegram/SourceFiles/gui/animation.h | 1 + 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 2fe92a5fb..50662fb23 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -313,7 +313,7 @@ QPixmap _prepareFrame(const ClipFrameRequest &request, const QImage &original, Q if (fill) p.fillRect(0, 0, cache.width() / factor, cache.height() / factor, st::black); if (smooth && badSize) p.setRenderHint(QPainter::SmoothPixmapTransform); QRect to((request.outerw - request.framew) / (2 * factor), (request.outerh - request.frameh) / (2 * factor), request.framew / factor, request.frameh / factor); - QRect from(0, 0, original.width() / factor, original.height() / factor); + QRect from(0, 0, original.width(), original.height()); p.drawImage(to, original, from, Qt::ColorOnly); } if (request.rounded) { @@ -349,6 +349,9 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data) : _ } void ClipReader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded) { + if (_clipManagers.size() <= _threadIndex) error(); + if (_state == ClipError) return; + int32 factor(cIntRetinaFactor()); _request.factor = factor; _request.framew = framew * factor; @@ -377,7 +380,10 @@ QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 oute result = _current = QPixmap(); result = _current = _prepareFrame(_request, current, _cacheForResize, true); - _clipManagers.at(_threadIndex)->update(this); + if (_clipManagers.size() <= _threadIndex) error(); + if (_state != ClipError) { + _clipManagers.at(_threadIndex)->update(this); + } return result; } @@ -406,8 +412,11 @@ ClipState ClipReader::state() const { } void ClipReader::stop() { - _clipManagers.at(_threadIndex)->stop(this); - _width = _height = 0; + if (_clipManagers.size() <= _threadIndex) error(); + if (_state != ClipError) { + _clipManagers.at(_threadIndex)->stop(this); + _width = _height = 0; + } } void ClipReader::error() { @@ -752,3 +761,20 @@ void ClipReadManager::process() { _processingInThread = 0; } + +ClipReadManager::~ClipReadManager() { + { + QMutexLocker lock(&_readerPointersMutex); + for (ReaderPointers::iterator i = _readerPointers.begin(), e = _readerPointers.end(); i != e; ++i) { + if (i.value()) { + i.key()->_private = 0; + } + } + _readerPointers.clear(); + + for (Readers::iterator i = _readers.begin(), e = _readers.end(); i != e; ++i) { + delete i.key(); + } + _readers.clear(); + } +} diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index d2c9b4e5f..3a3b92682 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -559,6 +559,7 @@ public: void start(ClipReader *reader); void update(ClipReader *reader); void stop(ClipReader *reader); + ~ClipReadManager(); signals: From 29a7c66e45b6eed7a1ec689e1cdc2d96add28007 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Dec 2015 16:35:15 +0300 Subject: [PATCH 026/145] stopping gifs on history close, only one gif playing --- Telegram/SourceFiles/app.cpp | 11 ++++++ Telegram/SourceFiles/app.h | 1 + Telegram/SourceFiles/config.h | 1 + Telegram/SourceFiles/gui/animation.cpp | 13 +++++-- Telegram/SourceFiles/gui/animation.h | 3 +- Telegram/SourceFiles/history.cpp | 48 +++++++++---------------- Telegram/SourceFiles/history.h | 7 ++-- Telegram/SourceFiles/historywidget.cpp | 2 +- Telegram/SourceFiles/mainwidget.cpp | 2 -- Telegram/SourceFiles/overviewwidget.cpp | 4 +-- Telegram/SourceFiles/profilewidget.cpp | 2 +- 11 files changed, 48 insertions(+), 46 deletions(-) diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index f8cafc0c9..2d3a1539c 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2428,6 +2428,7 @@ namespace App { } void regGifItem(ClipReader *reader, HistoryItem *item) { + stopGifItems(); ::gifItems.insert(reader, item); } @@ -2439,6 +2440,16 @@ namespace App { return ::gifItems; } + void stopGifItems() { + while (!::gifItems.isEmpty()) { + if (HistoryItem *playing = ::gifItems.begin().value()) { + if (playing->getMedia() && playing->getMedia()->type() == MediaTypeGif) { + static_cast(playing->getMedia())->stop(playing); + } + } + } + } + QString phoneFromSharedContact(int32 userId) { SharedContactItems::const_iterator i = ::sharedContactItems.constFind(userId); if (i != ::sharedContactItems.cend()) { diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 465c2fa7c..d77bfd846 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -254,6 +254,7 @@ namespace App { void regGifItem(ClipReader *reader, HistoryItem *item); void unregGifItem(ClipReader *reader); const GifItems &gifItems(); + void stopGifItems(); void regMuted(PeerData *peer, int32 changeIn); void unregMuted(PeerData *peer); diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 3888f20d6..9f7ae7e16 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -84,6 +84,7 @@ enum { AnimationTimerDelta = 7, ClipThreadsCount = 8, + AverageGifSize = 320 * 240, SaveRecentEmojisTimeout = 3000, // 3 secs SaveWindowPositionTimeout = 1000, // 1 sec diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 2fe92a5fb..700af5991 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -124,6 +124,8 @@ void AnimationManager::clipReinit(ClipReader *reader) { if (it != items.cend()) { it.value()->initDimensions(); if (App::main()) emit App::main()->itemResized(it.value(), true); + + Notify::historyItemLayoutChanged(it.value()); } } @@ -359,8 +361,9 @@ void ClipReader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, b _clipManagers.at(_threadIndex)->start(this); } -QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh) { +QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms) { _currentDisplayed.storeRelease(1); + _lastDisplayMs = ms; int32 factor(cIntRetinaFactor()); QPixmap result(_current); @@ -491,7 +494,8 @@ public: } uint64 nextFrameDelay() { - return qMax(_reader->nextImageDelay(), 5); + int delay = _reader->nextImageDelay(); + return qMax(delay, 5); } void swapBuffers(uint64 ms = 0) { @@ -636,6 +640,7 @@ ClipReadManager::ClipReadManager(QThread *thread) : _processingInThread(0) { void ClipReadManager::append(ClipReader *reader, const FileLocation &location, const QByteArray &data) { reader->_private = new ClipReaderPrivate(reader, location, data); + _loadLevel.fetchAndAddRelease(AverageGifSize); update(reader); } @@ -669,6 +674,9 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess return false; } + if (result == ClipProcessStarted) { + _loadLevel.fetchAndAddRelease(reader->_currentOriginal.width() * reader->_currentOriginal.height() - AverageGifSize); + } if (result == ClipProcessReinit || result == ClipProcessRedraw || result == ClipProcessStarted) { it.key()->_current = reader->_current; it.key()->_currentOriginal = reader->_currentOriginal; @@ -684,6 +692,7 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) { if (!handleProcessResult(reader, result)) { + _loadLevel.fetchAndAddRelease(-1 * (reader->_currentOriginal.isNull() ? AverageGifSize : reader->_currentOriginal.width() * reader->_currentOriginal.height())); delete reader; return ResultHandleRemove; } diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index d2c9b4e5f..bbe1f2396 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -500,7 +500,7 @@ public: ClipReader(const FileLocation &location, const QByteArray &data); void start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded); - QPixmap current(int32 framew, int32 frameh, int32 outerw, int32 outerh); + QPixmap current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms); bool currentDisplayed() const { return _currentDisplayed.loadAcquire() > 0; } @@ -530,6 +530,7 @@ private: QPixmap _current; QImage _currentOriginal, _cacheForResize; QAtomicInt _currentDisplayed; + uint64 _lastDisplayMs; int32 _threadIndex; friend class ClipReadManager; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 3982977e4..c586e8c0e 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -152,30 +152,6 @@ 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); @@ -4571,7 +4547,7 @@ HistoryGif::HistoryGif(DocumentData *document) : HistoryFileMedia() , _thumbw(1) , _thumbh(1) , _gif(0) { - setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); + setLinks(new DocumentOpenLink(_data), new DocumentOpenLink(_data), new DocumentCancelLink(_data)); setStatusSize(FileStatusSizeReady); @@ -4651,7 +4627,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo QRect rthumb(rtlrect(skipx, skipy, width, height, w)); if (animating) { - p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, width, height)); + p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, width, height, ms)); } else { p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_thumbw, _thumbh, width, height)); } @@ -4876,9 +4852,9 @@ void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, } if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { if (_gif && _gif->started()) { - lnk = _openl; + lnk = _savel; } else { - lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; + lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _savel; } int32 fullRight = skipx + width, fullBottom = skipy + height; @@ -4900,15 +4876,23 @@ ImagePtr HistoryGif::replyPreview() { void HistoryGif::play(HistoryItem *parent) { if (_gif) { - App::unregGifItem(_gif); - delete _gif; - _gif = 0; + stop(parent); } else { _gif = new ClipReader(_data->location(), _data->data); App::regGifItem(_gif, parent); } } +void HistoryGif::stop(HistoryItem *parent) { + App::unregGifItem(_gif); + delete _gif; + _gif = 0; + + parent->initDimensions(); + if (App::main()) emit App::main()->itemResized(parent); + Notify::historyItemLayoutChanged(parent); +} + HistoryGif::~HistoryGif() { if (_gif) { App::unregGifItem(_gif); @@ -6666,7 +6650,7 @@ void HistoryMessage::initMediaFromText(QString ¤tText) { void HistoryMessage::initMediaFromDocument(DocumentData *doc) { if (doc->sticker()) { _media = new HistorySticker(doc); - } else if (doc->type == AnimatedDocument) { + } else if (doc->type == AnimatedDocument || doc->mime.toLower() == qstr("image/gif")) { _media = new HistoryGif(doc); } else { _media = new HistoryDocument(doc); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index c4f6a0aae..21f309fb2 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -24,11 +24,6 @@ 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; @@ -1606,6 +1601,8 @@ public: } void play(HistoryItem *parent); + void stop(HistoryItem *parent); + ~HistoryGif(); protected: diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 7a637791d..7ff7ac6a8 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3225,7 +3225,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re } } - stopGif(); + App::stopGifItems(); clearReplyReturns(); clearAllLoadRequests(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index a67beecb7..25b89a349 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1454,7 +1454,6 @@ void MainWidget::itemRemoved(HistoryItem *item) { if (overview && (overview->peer() == item->history()->peer || (overview->peer() && overview->peer() == item->history()->peer->migrateTo()))) { overview->itemRemoved(item); } - itemRemovedGif(item); if (!_toForward.isEmpty()) { SelectedItemSet::iterator i = _toForward.find(item->id); if (i != _toForward.cend() && i.value() == item) { @@ -1476,7 +1475,6 @@ void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) { if (history.peer() == newItem->history()->peer || (history.peer() && history.peer() == newItem->history()->peer->migrateTo())) { history.itemReplaced(oldItem, newItem); } - itemReplacedGif(oldItem, newItem); if (!_toForward.isEmpty()) { SelectedItemSet::iterator i = _toForward.find(oldItem->id); if (i != _toForward.cend() && i.value() == oldItem) { diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index b2b454d7a..6336a2520 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -2906,7 +2906,7 @@ int32 OverviewWidget::countBestScroll() const { } void OverviewWidget::fastShow(bool back, int32 lastScrollTop) { - stopGif(); + App::stopGifItems(); resizeEvent(0); _scrollSetAfterShow = (lastScrollTop < 0 ? countBestScroll() : lastScrollTop); show(); @@ -2919,7 +2919,7 @@ void OverviewWidget::fastShow(bool back, int32 lastScrollTop) { void OverviewWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back, int32 lastScrollTop) { if (App::app()) App::app()->mtpPause(); - stopGif(); + App::stopGifItems(); (back ? _cacheOver : _cacheUnder) = bgAnimCache; (back ? _cacheTopBarOver : _cacheTopBarUnder) = bgAnimTopBarCache; diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index 47424ee92..c1f177a94 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -1832,7 +1832,7 @@ int32 ProfileWidget::lastScrollTop() const { void ProfileWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back, int32 lastScrollTop) { if (App::app()) App::app()->mtpPause(); - stopGif(); + App::stopGifItems(); (back ? _cacheOver : _cacheUnder) = bgAnimCache; (back ? _cacheTopBarOver : _cacheTopBarUnder) = bgAnimTopBarCache; From fdb93f700dfcdc76d9692e755df88d015fe3e06f Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Dec 2015 17:39:26 +0300 Subject: [PATCH 027/145] pausing gifs that are not currently displayed --- Telegram/SourceFiles/app.cpp | 2 +- Telegram/SourceFiles/config.h | 3 +- Telegram/SourceFiles/gui/animation.cpp | 57 ++++++++++++++++++++------ Telegram/SourceFiles/gui/animation.h | 31 ++++++++++++-- 4 files changed, 75 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 2d3a1539c..6007046b9 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2441,7 +2441,7 @@ namespace App { } void stopGifItems() { - while (!::gifItems.isEmpty()) { + if (!::gifItems.isEmpty()) { if (HistoryItem *playing = ::gifItems.begin().value()) { if (playing->getMedia() && playing->getMedia()->type() == MediaTypeGif) { static_cast(playing->getMedia())->stop(playing); diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 9f7ae7e16..3427921c8 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -83,8 +83,9 @@ enum { LocalEncryptKeySize = 256, // 2048 bit AnimationTimerDelta = 7, - ClipThreadsCount = 8, + ClipThreadsCount = 4, AverageGifSize = 320 * 240, + WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it SaveRecentEmojisTimeout = 3000, // 3 secs SaveWindowPositionTimeout = 1000, // 1 sec diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 3a0aa5d65..765557f27 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -330,6 +330,8 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data) : _ , _width(0) , _height(0) , _currentDisplayed(1) +, _paused(0) +, _lastDisplayMs(getms()) , _private(0) { if (_clipThreads.size() < ClipThreadsCount) { _threadIndex = _clipThreads.size(); @@ -365,8 +367,15 @@ void ClipReader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, b } QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms) { - _currentDisplayed.storeRelease(1); - _lastDisplayMs = ms; + _lastDisplayMs.set(ms); + _currentDisplayed.set(true); + if (_paused.get()) { + _paused.set(false); + if (_clipManagers.size() <= _threadIndex) error(); + if (_state != ClipError) { + _clipManagers.at(_threadIndex)->update(this); + } + } int32 factor(cIntRetinaFactor()); QPixmap result(_current); @@ -441,8 +450,10 @@ public: , _accessed(false) , _buffer(_data.isEmpty() ? 0 : &_data) , _reader(0) + , _previousMs(0) , _currentMs(0) - , _nextUpdateMs(0) { + , _nextUpdateMs(0) + , _paused(false) { if (_data.isEmpty() && !_location->accessEnable()) { error(); @@ -476,6 +487,7 @@ public: if (_current.isNull()) { // first frame read, but not yet prepared _currentOriginal.setDevicePixelRatio(_request.factor); + _previousMs = _currentMs; _currentMs = ms; _current = _prepareFrame(_request, _currentOriginal, _currentCache, true); @@ -483,7 +495,7 @@ public: return error(); } return ClipProcessStarted; - } else if (ms >= _nextUpdateMs) { + } else if (!_paused && ms >= _nextUpdateMs) { swapBuffers(); return ClipProcessRedraw; } @@ -508,6 +520,7 @@ public: } void swapBuffers(uint64 ms = 0) { + _previousMs = _currentMs; _currentMs = qMax(ms, _nextUpdateMs); qSwap(_currentOriginal, _nextOriginal); qSwap(_current, _next); @@ -628,13 +641,15 @@ private: QImage _currentOriginal, _nextOriginal, _currentCache, _nextCache; int32 _framesLeft; - uint64 _currentMs, _nextUpdateMs; + uint64 _previousMs, _currentMs, _nextUpdateMs; + + bool _paused; friend class ClipReadManager; }; -ClipReadManager::ClipReadManager(QThread *thread) : _processingInThread(0) { +ClipReadManager::ClipReadManager(QThread *thread) : _processingInThread(0), _needReProcess(false) { moveToThread(thread); connect(thread, SIGNAL(started()), this, SLOT(process())); connect(this, SIGNAL(processDelayed()), this, SLOT(process()), Qt::QueuedConnection); @@ -669,7 +684,7 @@ void ClipReadManager::stop(ClipReader *reader) { emit processDelayed(); } -bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result) { +bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) { QMutexLocker lock(&_readerPointersMutex); ReaderPointers::iterator it = _readerPointers.find(reader->_interface); if (result == ClipProcessError) { @@ -686,10 +701,20 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess if (result == ClipProcessStarted) { _loadLevel.fetchAndAddRelease(reader->_currentOriginal.width() * reader->_currentOriginal.height() - AverageGifSize); } + if (!reader->_paused && (result == ClipProcessRedraw || result == ClipProcessWait)) { + if (it.key()->_lastDisplayMs.get() + WaitBeforeGifPause < qMax(reader->_previousMs, ms)) { + reader->_paused = true; + it.key()->_paused.set(true); + if (it.key()->_lastDisplayMs.get() + WaitBeforeGifPause >= qMax(reader->_previousMs, ms)) { + it.key()->_paused.set(false); + reader->_paused = false; + } + } + } if (result == ClipProcessReinit || result == ClipProcessRedraw || result == ClipProcessStarted) { it.key()->_current = reader->_current; it.key()->_currentOriginal = reader->_currentOriginal; - it.key()->_currentDisplayed.storeRelease(0); + it.key()->_currentDisplayed.set(false); if (result == ClipProcessReinit) { emit reinit(it.key()); } else if (result == ClipProcessRedraw) { @@ -700,7 +725,7 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess } ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) { - if (!handleProcessResult(reader, result)) { + if (!handleProcessResult(reader, result, ms)) { _loadLevel.fetchAndAddRelease(-1 * (reader->_currentOriginal.isNull() ? AverageGifSize : reader->_currentOriginal.width() * reader->_currentOriginal.height())); delete reader; return ResultHandleRemove; @@ -719,7 +744,10 @@ ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPriva } void ClipReadManager::process() { - if (_processingInThread) return; + if (_processingInThread) { + _needReProcess = true; + return; + } _timer.stop(); _processingInThread = thread(); @@ -734,6 +762,9 @@ void ClipReadManager::process() { _readers.insert(i.value(), 0); } else { it.value() = ms; + if (it.key()->_paused && !i.key()->_paused.get()) { + it.key()->_paused = false; + } } i.value()->_request = i.key()->_request; i.value() = 0; @@ -754,15 +785,17 @@ void ClipReadManager::process() { return; } i.value() = i.key()->_nextUpdateMs; + ms = getms(); } - if (i.value() < minms) { + if (!i.key()->_paused && i.value() < minms) { minms = i.value(); } ++i; } ms = getms(); - if (minms <= ms) { + if (_needReProcess || minms <= ms) { + _needReProcess = false; _timer.start(1); } else { _timer.start(minms - ms); diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index fce4f1ace..e341d54fd 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -493,6 +493,28 @@ struct ClipFrameRequest { bool rounded; }; +template +class Atomic { +public: + + Atomic(const Type &value = Type()) : _v(1, value) { + } + + Type get() const { + QVector v(_v); + return v.at(0); + } + + void set(const Type &value) { + QVector v(1, value); + _v = v; + } + +private: + QVector _v; + +}; + class ClipReaderPrivate; class ClipReader { public: @@ -502,7 +524,7 @@ public: void start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded); QPixmap current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms); bool currentDisplayed() const { - return _currentDisplayed.loadAcquire() > 0; + return _currentDisplayed.get(); } int32 width() const; @@ -529,8 +551,8 @@ private: QPixmap _current; QImage _currentOriginal, _cacheForResize; - QAtomicInt _currentDisplayed; - uint64 _lastDisplayMs; + Atomic _currentDisplayed, _paused; + Atomic _lastDisplayMs; int32 _threadIndex; friend class ClipReadManager; @@ -580,7 +602,7 @@ private: ReaderPointers _readerPointers; QMutex _readerPointersMutex; - bool handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result); + bool handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms); enum ResultHandleState { ResultHandleRemove, @@ -594,5 +616,6 @@ private: QTimer _timer; QThread *_processingInThread; + bool _needReProcess; }; From ae78d132840ffca72fcf7410495219aa40006295 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Dec 2015 18:04:02 +0300 Subject: [PATCH 028/145] gif size decreased, error message added for gif errors, enable many gifs playing at the same time --- Telegram/Resources/lang.strings | 1 + Telegram/Resources/style.txt | 1 + Telegram/SourceFiles/app.cpp | 2 +- Telegram/SourceFiles/application.cpp | 2 +- Telegram/SourceFiles/autoupdater.cpp | 2 +- Telegram/SourceFiles/facades.cpp | 6 +++- Telegram/SourceFiles/gui/animation.cpp | 12 ++++--- Telegram/SourceFiles/history.cpp | 44 +++++++++++++++----------- 8 files changed, 44 insertions(+), 26 deletions(-) diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 0c6a22e61..aa1b475e5 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -108,6 +108,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"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 9c234ea0b..70acf8f2d 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -2191,6 +2191,7 @@ mediaviewLoaderSkip: 9px; minPhotoSize: 104px; maxMediaSize: 420px; maxStickerSize: 256px; +maxGifSize: 256px; downloadPathSkip: 10px; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 6007046b9..4837b1bbe 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2444,7 +2444,7 @@ namespace App { if (!::gifItems.isEmpty()) { if (HistoryItem *playing = ::gifItems.begin().value()) { if (playing->getMedia() && playing->getMedia()->type() == MediaTypeGif) { - static_cast(playing->getMedia())->stop(playing); +// static_cast(playing->getMedia())->stop(playing); } } } diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index a606c618b..70708b29e 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -550,7 +550,7 @@ void Application::startUpdateCheck(bool forceWait) { 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"); diff --git a/Telegram/SourceFiles/autoupdater.cpp b/Telegram/SourceFiles/autoupdater.cpp index e438ae83d..a1a77a4ff 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; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index df7d14eb5..f25cf048d 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -79,7 +79,11 @@ namespace Ui { } void showLayer(LayeredWidget *box, ShowLayerOptions options) { - if (Window *w = App::wnd()) w->ui_showLayer(box, options); + if (Window *w = App::wnd()) { + w->ui_showLayer(box, options); + } else { + delete box; + } } void hideLayer(bool fast) { diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 765557f27..aa7fee421 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -122,10 +122,12 @@ void AnimationManager::clipReinit(ClipReader *reader) { const GifItems &items(App::gifItems()); GifItems::const_iterator it = items.constFind(reader); if (it != items.cend()) { - it.value()->initDimensions(); - if (App::main()) emit App::main()->itemResized(it.value(), true); + HistoryItem *item = it.value(); - Notify::historyItemLayoutChanged(it.value()); + item->initDimensions(); // can delete reader and items entry it + if (App::main()) emit App::main()->itemResized(item, true); + + Notify::historyItemLayoutChanged(item); } } @@ -339,7 +341,7 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data) : _ _clipManagers.push_back(new ClipReadManager(_clipThreads.back())); _clipThreads.back()->start(); } else { - _threadIndex = rand() % _clipThreads.size(); + _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(); @@ -690,6 +692,8 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess if (result == ClipProcessError) { if (it != _readerPointers.cend()) { it.key()->error(); + emit reinit(it.key()); + _readerPointers.erase(it); it = _readerPointers.end(); } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index c586e8c0e..d2a7fb77b 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "gui/filedialog.h" #include "boxes/addcontactbox.h" +#include "boxes/confirmbox.h" #include "audio.h" #include "localstorage.h" @@ -4557,6 +4558,13 @@ HistoryGif::HistoryGif(DocumentData *document) : HistoryFileMedia() void HistoryGif::initDimensions(const HistoryItem *parent) { bool bubble = parent->hasBubble(); int32 tw = 0, th = 0; + if (_gif && _gif->state() == ClipError) { + Ui::showLayer(new InformBox(lang(lng_gif_error))); + App::unregGifItem(_gif); + delete _gif; + _gif = 0; + } + if (_gif && _gif->ready()) { tw = convertScale(_gif->width()); th = convertScale(_gif->height()); @@ -4567,13 +4575,13 @@ void HistoryGif::initDimensions(const HistoryItem *parent) { th = convertScale(_data->thumb->height()); } } - if (tw > st::maxMediaSize) { - th = (st::maxMediaSize * th) / tw; - tw = st::maxMediaSize; + if (tw > st::maxGifSize) { + th = (st::maxGifSize * th) / tw; + tw = st::maxGifSize; } - if (th > st::maxMediaSize) { - tw = (st::maxMediaSize * tw) / th; - th = st::maxMediaSize; + if (th > st::maxGifSize) { + tw = (st::maxGifSize * tw) / th; + th = st::maxGifSize; } if (!tw || !th) { tw = th = 1; @@ -4737,13 +4745,13 @@ int32 HistoryGif::resize(int32 width, const HistoryItem *parent) { th = convertScale(_data->thumb->height()); } } - if (tw > st::maxMediaSize) { - th = (st::maxMediaSize * th) / tw; - tw = st::maxMediaSize; + if (tw > st::maxGifSize) { + th = (st::maxGifSize * th) / tw; + tw = st::maxGifSize; } - if (th > st::maxMediaSize) { - tw = (st::maxMediaSize * tw) / th; - th = st::maxMediaSize; + if (th > st::maxGifSize) { + tw = (st::maxGifSize * tw) / th; + th = st::maxGifSize; } if (!tw || !th) { tw = th = 1; @@ -4809,13 +4817,13 @@ int32 HistoryGif::countHeight(const HistoryItem *parent, int32 width) const { th = convertScale(_data->thumb->height()); } } - if (tw > st::maxMediaSize) { - th = (st::maxMediaSize * th) / tw; - tw = st::maxMediaSize; + if (tw > st::maxGifSize) { + th = (st::maxGifSize * th) / tw; + tw = st::maxGifSize; } - if (th > st::maxMediaSize) { - tw = (st::maxMediaSize * tw) / th; - th = st::maxMediaSize; + if (th > st::maxGifSize) { + tw = (st::maxGifSize * tw) / th; + th = st::maxGifSize; } if (!tw || !th) { tw = th = 1; From 1545e7f796f8b803a5643585b372aebab24aa990 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Dec 2015 18:31:56 +0300 Subject: [PATCH 029/145] moved to 44 layer --- Telegram/SourceFiles/app.cpp | 3 +- Telegram/SourceFiles/history.cpp | 2 + Telegram/SourceFiles/historywidget.cpp | 2 +- Telegram/SourceFiles/mtproto/mtpCoreTypes.h | 2 +- Telegram/SourceFiles/mtproto/mtpScheme.cpp | 133 ++++++- Telegram/SourceFiles/mtproto/mtpScheme.h | 385 ++++++++++++++++++++ Telegram/SourceFiles/mtproto/scheme.tl | 12 +- Telegram/SourceFiles/overviewwidget.cpp | 4 +- Telegram/SourceFiles/profilewidget.cpp | 2 +- 9 files changed, 521 insertions(+), 24 deletions(-) diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 4837b1bbe..2672bd90c 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1328,6 +1328,7 @@ namespace App { return page; } break; case mtpc_webPagePending: return App::feedWebPage(webpage.c_webPagePending()); + case mtpc_webPageExternal: LOG(("API Error: should not get webPageExternal in App::feedWebPage")); break; } return 0; } @@ -2444,7 +2445,7 @@ namespace App { if (!::gifItems.isEmpty()) { if (HistoryItem *playing = ::gifItems.begin().value()) { if (playing->getMedia() && playing->getMedia()->type() == MediaTypeGif) { -// static_cast(playing->getMedia())->stop(playing); + static_cast(playing->getMedia())->stop(playing); } } } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index d2a7fb77b..4b3ca1d27 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -1452,6 +1452,7 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo case mtpc_webPage: case mtpc_webPageEmpty: case mtpc_webPagePending: break; + case mtpc_webPageExternal: LOG(("API Error: should not get webPageExternal in History::createItem")); default: badMedia = 1; break; } break; @@ -6640,6 +6641,7 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media, QString ¤tTex case mtpc_webPage: { _media = new HistoryWebPage(App::feedWebPage(d.c_webPage())); } break; + case mtpc_webPageExternal: LOG(("API Error: should not get webPageExternal in HistoryMessage::initMedia")); break; } } break; default: initMediaFromText(currentText); break; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 7ff7ac6a8..435c08501 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3225,7 +3225,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re } } - App::stopGifItems(); +// App::stopGifItems(); clearReplyReturns(); clearAllLoadRequests(); diff --git a/Telegram/SourceFiles/mtproto/mtpCoreTypes.h b/Telegram/SourceFiles/mtproto/mtpCoreTypes.h index 290c7716c..10925cdc7 100644 --- a/Telegram/SourceFiles/mtproto/mtpCoreTypes.h +++ b/Telegram/SourceFiles/mtproto/mtpCoreTypes.h @@ -368,7 +368,7 @@ static const mtpTypeId mtpLayers[] = { mtpTypeId(mtpc_invokeWithLayer18), }; static const uint32 mtpLayerMaxSingle = sizeof(mtpLayers) / sizeof(mtpLayers[0]); -static const mtpPrime mtpCurrentLayer = 43; +static const mtpPrime mtpCurrentLayer = 44; template class MTPBoxed : public bareT { diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.cpp b/Telegram/SourceFiles/mtproto/mtpScheme.cpp index 627f9d69c..93b7d74ef 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.cpp +++ b/Telegram/SourceFiles/mtproto/mtpScheme.cpp @@ -784,6 +784,20 @@ void _serialize_inputMediaVenue(MTPStringLogger &to, int32 stage, int32 lev, Typ } } +void _serialize_inputMediaGifExternal(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ inputMediaGifExternal"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" url: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 1: to.add(" q: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; + } +} + void _serialize_inputChatPhotoEmpty(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { to.add("{ inputChatPhotoEmpty }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); } @@ -1116,15 +1130,16 @@ void _serialize_user(MTPStringLogger &to, int32 stage, int32 lev, Types &types, case 6: to.add(" bot_chat_history: "); ++stages.back(); if (flag & MTPDuser::flag_bot_chat_history) { to.add("YES [ BY BIT 15 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 15 IN FIELD flags ]"); } break; case 7: to.add(" bot_nochats: "); ++stages.back(); if (flag & MTPDuser::flag_bot_nochats) { to.add("YES [ BY BIT 16 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 16 IN FIELD flags ]"); } break; case 8: to.add(" verified: "); ++stages.back(); if (flag & MTPDuser::flag_verified) { to.add("YES [ BY BIT 17 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 17 IN FIELD flags ]"); } break; - case 9: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 10: to.add(" access_hash: "); ++stages.back(); if (flag & MTPDuser::flag_access_hash) { types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break; - case 11: to.add(" first_name: "); ++stages.back(); if (flag & MTPDuser::flag_first_name) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break; - case 12: to.add(" last_name: "); ++stages.back(); if (flag & MTPDuser::flag_last_name) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 2 IN FIELD flags ]"); } break; - case 13: to.add(" username: "); ++stages.back(); if (flag & MTPDuser::flag_username) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 3 IN FIELD flags ]"); } break; - case 14: to.add(" phone: "); ++stages.back(); if (flag & MTPDuser::flag_phone) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 4 IN FIELD flags ]"); } break; - case 15: to.add(" photo: "); ++stages.back(); if (flag & MTPDuser::flag_photo) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break; - case 16: to.add(" status: "); ++stages.back(); if (flag & MTPDuser::flag_status) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break; - case 17: to.add(" bot_info_version: "); ++stages.back(); if (flag & MTPDuser::flag_bot_info_version) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 14 IN FIELD flags ]"); } break; + case 9: to.add(" explicit_content: "); ++stages.back(); if (flag & MTPDuser::flag_explicit_content) { to.add("YES [ BY BIT 18 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 18 IN FIELD flags ]"); } break; + case 10: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 11: to.add(" access_hash: "); ++stages.back(); if (flag & MTPDuser::flag_access_hash) { types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break; + case 12: to.add(" first_name: "); ++stages.back(); if (flag & MTPDuser::flag_first_name) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break; + case 13: to.add(" last_name: "); ++stages.back(); if (flag & MTPDuser::flag_last_name) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 2 IN FIELD flags ]"); } break; + case 14: to.add(" username: "); ++stages.back(); if (flag & MTPDuser::flag_username) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 3 IN FIELD flags ]"); } break; + case 15: to.add(" phone: "); ++stages.back(); if (flag & MTPDuser::flag_phone) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 4 IN FIELD flags ]"); } break; + case 16: to.add(" photo: "); ++stages.back(); if (flag & MTPDuser::flag_photo) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break; + case 17: to.add(" status: "); ++stages.back(); if (flag & MTPDuser::flag_status) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break; + case 18: to.add(" bot_info_version: "); ++stages.back(); if (flag & MTPDuser::flag_bot_info_version) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 14 IN FIELD flags ]"); } break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; } } @@ -1260,13 +1275,14 @@ void _serialize_channel(MTPStringLogger &to, int32 stage, int32 lev, Types &type case 6: to.add(" broadcast: "); ++stages.back(); if (flag & MTPDchannel::flag_broadcast) { to.add("YES [ BY BIT 5 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break; case 7: to.add(" verified: "); ++stages.back(); if (flag & MTPDchannel::flag_verified) { to.add("YES [ BY BIT 7 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 7 IN FIELD flags ]"); } break; case 8: to.add(" megagroup: "); ++stages.back(); if (flag & MTPDchannel::flag_megagroup) { to.add("YES [ BY BIT 8 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 8 IN FIELD flags ]"); } break; - case 9: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 10: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 11: to.add(" title: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 12: to.add(" username: "); ++stages.back(); if (flag & MTPDchannel::flag_username) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break; - case 13: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 14: to.add(" date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 15: to.add(" version: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 9: to.add(" explicit_content: "); ++stages.back(); if (flag & MTPDchannel::flag_explicit_content) { to.add("YES [ BY BIT 9 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 9 IN FIELD flags ]"); } break; + case 10: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 11: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 12: to.add(" title: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 13: to.add(" username: "); ++stages.back(); if (flag & MTPDchannel::flag_username) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break; + case 14: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 15: to.add(" date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 16: to.add(" version: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; } } @@ -4212,6 +4228,29 @@ void _serialize_webPage(MTPStringLogger &to, int32 stage, int32 lev, Types &type } } +void _serialize_webPageExternal(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ webPageExternal"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 1: to.add(" url: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 2: to.add(" display_url: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 3: to.add(" type: "); ++stages.back(); if (flag & MTPDwebPageExternal::flag_type) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break; + case 4: to.add(" title: "); ++stages.back(); if (flag & MTPDwebPageExternal::flag_title) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break; + case 5: to.add(" description: "); ++stages.back(); if (flag & MTPDwebPageExternal::flag_description) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 2 IN FIELD flags ]"); } break; + case 6: to.add(" thumb_url: "); ++stages.back(); if (flag & MTPDwebPageExternal::flag_thumb_url) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 3 IN FIELD flags ]"); } break; + case 7: to.add(" content_url: "); ++stages.back(); if (flag & MTPDwebPageExternal::flag_content_url) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 4 IN FIELD flags ]"); } break; + case 8: to.add(" w: "); ++stages.back(); if (flag & MTPDwebPageExternal::flag_w) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break; + case 9: to.add(" h: "); ++stages.back(); if (flag & MTPDwebPageExternal::flag_h) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break; + case 10: to.add(" duration: "); ++stages.back(); if (flag & MTPDwebPageExternal::flag_duration) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break; + default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; + } +} + void _serialize_authorization(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { if (stage) { to.add(",\n").addSpaces(lev); @@ -5038,6 +5077,33 @@ void _serialize_help_termsOfService(MTPStringLogger &to, int32 stage, int32 lev, } } +void _serialize_foundGif(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ foundGif"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" webpage: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; + } +} + +void _serialize_messages_foundGifs(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ messages_foundGifs"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" next_offset: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 1: to.add(" results: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; + } +} + void _serialize_req_pq(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { if (stage) { to.add(",\n").addSpaces(lev); @@ -7000,6 +7066,35 @@ void _serialize_messages_getStickerSet(MTPStringLogger &to, int32 stage, int32 l } } +void _serialize_messages_getDocumentByHash(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ messages_getDocumentByHash"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" sha256: "); ++stages.back(); types.push_back(mtpc_bytes); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 1: to.add(" size: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 2: to.add(" mime_type: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; + } +} + +void _serialize_messages_searchGifs(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ messages_searchGifs"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" q: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 1: to.add(" offset: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; + } +} + void _serialize_updates_getState(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { to.add("{ updates_getState }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); } @@ -7304,6 +7399,7 @@ namespace { _serializers.insert(mtpc_inputMediaUploadedThumbDocument, _serialize_inputMediaUploadedThumbDocument); _serializers.insert(mtpc_inputMediaDocument, _serialize_inputMediaDocument); _serializers.insert(mtpc_inputMediaVenue, _serialize_inputMediaVenue); + _serializers.insert(mtpc_inputMediaGifExternal, _serialize_inputMediaGifExternal); _serializers.insert(mtpc_inputChatPhotoEmpty, _serialize_inputChatPhotoEmpty); _serializers.insert(mtpc_inputChatUploadedPhoto, _serialize_inputChatUploadedPhoto); _serializers.insert(mtpc_inputChatPhoto, _serialize_inputChatPhoto); @@ -7587,6 +7683,7 @@ namespace { _serializers.insert(mtpc_webPageEmpty, _serialize_webPageEmpty); _serializers.insert(mtpc_webPagePending, _serialize_webPagePending); _serializers.insert(mtpc_webPage, _serialize_webPage); + _serializers.insert(mtpc_webPageExternal, _serialize_webPageExternal); _serializers.insert(mtpc_authorization, _serialize_authorization); _serializers.insert(mtpc_account_authorizations, _serialize_account_authorizations); _serializers.insert(mtpc_account_noPassword, _serialize_account_noPassword); @@ -7652,6 +7749,8 @@ namespace { _serializers.insert(mtpc_channels_channelParticipants, _serialize_channels_channelParticipants); _serializers.insert(mtpc_channels_channelParticipant, _serialize_channels_channelParticipant); _serializers.insert(mtpc_help_termsOfService, _serialize_help_termsOfService); + _serializers.insert(mtpc_foundGif, _serialize_foundGif); + _serializers.insert(mtpc_messages_foundGifs, _serialize_messages_foundGifs); _serializers.insert(mtpc_req_pq, _serialize_req_pq); _serializers.insert(mtpc_req_DH_params, _serialize_req_DH_params); @@ -7798,6 +7897,8 @@ namespace { _serializers.insert(mtpc_channels_exportInvite, _serialize_channels_exportInvite); _serializers.insert(mtpc_messages_checkChatInvite, _serialize_messages_checkChatInvite); _serializers.insert(mtpc_messages_getStickerSet, _serialize_messages_getStickerSet); + _serializers.insert(mtpc_messages_getDocumentByHash, _serialize_messages_getDocumentByHash); + _serializers.insert(mtpc_messages_searchGifs, _serialize_messages_searchGifs); _serializers.insert(mtpc_updates_getState, _serialize_updates_getState); _serializers.insert(mtpc_updates_getDifference, _serialize_updates_getDifference); _serializers.insert(mtpc_updates_getChannelDifference, _serialize_updates_getChannelDifference); diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.h b/Telegram/SourceFiles/mtproto/mtpScheme.h index 6b0d1c3a1..8d9765ce5 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.h +++ b/Telegram/SourceFiles/mtproto/mtpScheme.h @@ -96,6 +96,7 @@ enum { mtpc_inputMediaUploadedThumbDocument = 0x41481486, mtpc_inputMediaDocument = 0xd184e841, mtpc_inputMediaVenue = 0x2827a81a, + mtpc_inputMediaGifExternal = 0x4843b0fd, mtpc_inputChatPhotoEmpty = 0x1ca48f57, mtpc_inputChatUploadedPhoto = 0x94254732, mtpc_inputChatPhoto = 0xb2e1bf08, @@ -379,6 +380,7 @@ enum { mtpc_webPageEmpty = 0xeb1477e8, mtpc_webPagePending = 0xc586da1c, mtpc_webPage = 0xca820ed7, + mtpc_webPageExternal = 0xcf73f207, mtpc_authorization = 0x7bf2e6f6, mtpc_account_authorizations = 0x1250abde, mtpc_account_noPassword = 0x96dabc18, @@ -444,6 +446,8 @@ enum { mtpc_channels_channelParticipants = 0xf56ee2a8, mtpc_channels_channelParticipant = 0xd0d9b163, mtpc_help_termsOfService = 0xf1ee3e90, + mtpc_foundGif = 0xd579cccb, + mtpc_messages_foundGifs = 0x450a1c0a, mtpc_invokeAfterMsg = 0xcb9f372d, mtpc_invokeAfterMsgs = 0x3dc4b4f0, mtpc_initConnection = 0x69796de9, @@ -553,6 +557,8 @@ enum { mtpc_messages_migrateChat = 0x15a3b8e3, mtpc_messages_searchGlobal = 0x9e3cacb0, mtpc_messages_reorderStickerSets = 0x9fcfbc30, + mtpc_messages_getDocumentByHash = 0x338e2464, + mtpc_messages_searchGifs = 0xbf9a776b, mtpc_updates_getState = 0xedd4882a, mtpc_updates_getDifference = 0xa041495, mtpc_updates_getChannelDifference = 0xbb32d7c0, @@ -706,6 +712,7 @@ class MTPDinputMediaUploadedDocument; class MTPDinputMediaUploadedThumbDocument; class MTPDinputMediaDocument; class MTPDinputMediaVenue; +class MTPDinputMediaGifExternal; class MTPinputChatPhoto; class MTPDinputChatUploadedPhoto; @@ -1101,6 +1108,7 @@ class MTPwebPage; class MTPDwebPageEmpty; class MTPDwebPagePending; class MTPDwebPage; +class MTPDwebPageExternal; class MTPauthorization; class MTPDauthorization; @@ -1215,6 +1223,12 @@ class MTPDchannels_channelParticipant; class MTPhelp_termsOfService; class MTPDhelp_termsOfService; +class MTPfoundGif; +class MTPDfoundGif; + +class MTPmessages_foundGifs; +class MTPDmessages_foundGifs; + // Boxed types definitions typedef MTPBoxed MTPResPQ; @@ -1374,6 +1388,8 @@ typedef MTPBoxed MTPChannelParticipantRole; typedef MTPBoxed MTPchannels_ChannelParticipants; typedef MTPBoxed MTPchannels_ChannelParticipant; typedef MTPBoxed MTPhelp_TermsOfService; +typedef MTPBoxed MTPFoundGif; +typedef MTPBoxed MTPmessages_FoundGifs; // Type classes definitions @@ -2600,6 +2616,18 @@ public: return *(const MTPDinputMediaVenue*)data; } + MTPDinputMediaGifExternal &_inputMediaGifExternal() { + if (!data) throw mtpErrorUninitialized(); + if (_type != mtpc_inputMediaGifExternal) throw mtpErrorWrongTypeId(_type, mtpc_inputMediaGifExternal); + split(); + return *(MTPDinputMediaGifExternal*)data; + } + const MTPDinputMediaGifExternal &c_inputMediaGifExternal() const { + if (!data) throw mtpErrorUninitialized(); + if (_type != mtpc_inputMediaGifExternal) throw mtpErrorWrongTypeId(_type, mtpc_inputMediaGifExternal); + return *(const MTPDinputMediaGifExternal*)data; + } + uint32 innerLength() const; mtpTypeId type() const; void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons); @@ -2622,6 +2650,7 @@ private: explicit MTPinputMedia(MTPDinputMediaUploadedThumbDocument *_data); explicit MTPinputMedia(MTPDinputMediaDocument *_data); explicit MTPinputMedia(MTPDinputMediaVenue *_data); + explicit MTPinputMedia(MTPDinputMediaGifExternal *_data); friend MTPinputMedia MTP_inputMediaEmpty(); friend MTPinputMedia MTP_inputMediaUploadedPhoto(const MTPInputFile &_file, const MTPstring &_caption); @@ -2637,6 +2666,7 @@ private: friend MTPinputMedia MTP_inputMediaUploadedThumbDocument(const MTPInputFile &_file, const MTPInputFile &_thumb, const MTPstring &_mime_type, const MTPVector &_attributes); friend MTPinputMedia MTP_inputMediaDocument(const MTPInputDocument &_id); friend MTPinputMedia MTP_inputMediaVenue(const MTPInputGeoPoint &_geo_point, const MTPstring &_title, const MTPstring &_address, const MTPstring &_provider, const MTPstring &_venue_id); + friend MTPinputMedia MTP_inputMediaGifExternal(const MTPstring &_url, const MTPstring &_q); mtpTypeId _type; }; @@ -7547,6 +7577,18 @@ public: return *(const MTPDwebPage*)data; } + MTPDwebPageExternal &_webPageExternal() { + if (!data) throw mtpErrorUninitialized(); + if (_type != mtpc_webPageExternal) throw mtpErrorWrongTypeId(_type, mtpc_webPageExternal); + split(); + return *(MTPDwebPageExternal*)data; + } + const MTPDwebPageExternal &c_webPageExternal() const { + if (!data) throw mtpErrorUninitialized(); + if (_type != mtpc_webPageExternal) throw mtpErrorWrongTypeId(_type, mtpc_webPageExternal); + return *(const MTPDwebPageExternal*)data; + } + uint32 innerLength() const; mtpTypeId type() const; void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons); @@ -7559,10 +7601,12 @@ private: explicit MTPwebPage(MTPDwebPageEmpty *_data); explicit MTPwebPage(MTPDwebPagePending *_data); explicit MTPwebPage(MTPDwebPage *_data); + explicit MTPwebPage(MTPDwebPageExternal *_data); friend MTPwebPage MTP_webPageEmpty(const MTPlong &_id); friend MTPwebPage MTP_webPagePending(const MTPlong &_id, MTPint _date); friend MTPwebPage MTP_webPage(MTPint _flags, const MTPlong &_id, const MTPstring &_url, const MTPstring &_display_url, const MTPstring &_type, const MTPstring &_site_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPstring &_embed_url, const MTPstring &_embed_type, MTPint _embed_width, MTPint _embed_height, MTPint _duration, const MTPstring &_author, const MTPDocument &_document); + friend MTPwebPage MTP_webPageExternal(MTPint _flags, const MTPstring &_url, const MTPstring &_display_url, const MTPstring &_type, const MTPstring &_title, const MTPstring &_description, const MTPstring &_thumb_url, const MTPstring &_content_url, MTPint _w, MTPint _h, MTPint _duration); mtpTypeId _type; }; @@ -8907,6 +8951,68 @@ private: }; typedef MTPBoxed MTPhelp_TermsOfService; +class MTPfoundGif : private mtpDataOwner { +public: + MTPfoundGif(); + MTPfoundGif(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_foundGif) : mtpDataOwner(0) { + read(from, end, cons); + } + + MTPDfoundGif &_foundGif() { + if (!data) throw mtpErrorUninitialized(); + split(); + return *(MTPDfoundGif*)data; + } + const MTPDfoundGif &c_foundGif() const { + if (!data) throw mtpErrorUninitialized(); + return *(const MTPDfoundGif*)data; + } + + uint32 innerLength() const; + mtpTypeId type() const; + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_foundGif); + void write(mtpBuffer &to) const; + + typedef void ResponseType; + +private: + explicit MTPfoundGif(MTPDfoundGif *_data); + + friend MTPfoundGif MTP_foundGif(const MTPWebPage &_webpage); +}; +typedef MTPBoxed MTPFoundGif; + +class MTPmessages_foundGifs : private mtpDataOwner { +public: + MTPmessages_foundGifs(); + MTPmessages_foundGifs(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_foundGifs) : mtpDataOwner(0) { + read(from, end, cons); + } + + MTPDmessages_foundGifs &_messages_foundGifs() { + if (!data) throw mtpErrorUninitialized(); + split(); + return *(MTPDmessages_foundGifs*)data; + } + const MTPDmessages_foundGifs &c_messages_foundGifs() const { + if (!data) throw mtpErrorUninitialized(); + return *(const MTPDmessages_foundGifs*)data; + } + + uint32 innerLength() const; + mtpTypeId type() const; + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_foundGifs); + void write(mtpBuffer &to) const; + + typedef void ResponseType; + +private: + explicit MTPmessages_foundGifs(MTPDmessages_foundGifs *_data); + + friend MTPmessages_foundGifs MTP_messages_foundGifs(MTPint _next_offset, const MTPVector &_results); +}; +typedef MTPBoxed MTPmessages_FoundGifs; + // Type constructors with data class MTPDresPQ : public mtpDataImpl { @@ -9478,6 +9584,17 @@ public: MTPstring vvenue_id; }; +class MTPDinputMediaGifExternal : public mtpDataImpl { +public: + MTPDinputMediaGifExternal() { + } + MTPDinputMediaGifExternal(const MTPstring &_url, const MTPstring &_q) : vurl(_url), vq(_q) { + } + + MTPstring vurl; + MTPstring vq; +}; + class MTPDinputChatUploadedPhoto : public mtpDataImpl { public: MTPDinputChatUploadedPhoto() { @@ -9706,6 +9823,7 @@ public: flag_bot_chat_history = (1 << 15), flag_bot_nochats = (1 << 16), flag_verified = (1 << 17), + flag_explicit_content = (1 << 18), flag_access_hash = (1 << 0), flag_first_name = (1 << 1), flag_last_name = (1 << 2), @@ -9724,6 +9842,7 @@ public: bool is_bot_chat_history() const { return vflags.v & flag_bot_chat_history; } bool is_bot_nochats() const { return vflags.v & flag_bot_nochats; } bool is_verified() const { return vflags.v & flag_verified; } + bool is_explicit_content() const { return vflags.v & flag_explicit_content; } bool has_access_hash() const { return vflags.v & flag_access_hash; } bool has_first_name() const { return vflags.v & flag_first_name; } bool has_last_name() const { return vflags.v & flag_last_name; } @@ -9847,6 +9966,7 @@ public: flag_broadcast = (1 << 5), flag_verified = (1 << 7), flag_megagroup = (1 << 8), + flag_explicit_content = (1 << 9), flag_username = (1 << 6), }; @@ -9858,6 +9978,7 @@ public: bool is_broadcast() const { return vflags.v & flag_broadcast; } bool is_verified() const { return vflags.v & flag_verified; } bool is_megagroup() const { return vflags.v & flag_megagroup; } + bool is_explicit_content() const { return vflags.v & flag_explicit_content; } bool has_username() const { return vflags.v & flag_username; } }; @@ -12168,6 +12289,46 @@ public: bool has_document() const { return vflags.v & flag_document; } }; +class MTPDwebPageExternal : public mtpDataImpl { +public: + MTPDwebPageExternal() { + } + MTPDwebPageExternal(MTPint _flags, const MTPstring &_url, const MTPstring &_display_url, const MTPstring &_type, const MTPstring &_title, const MTPstring &_description, const MTPstring &_thumb_url, const MTPstring &_content_url, MTPint _w, MTPint _h, MTPint _duration) : vflags(_flags), vurl(_url), vdisplay_url(_display_url), vtype(_type), vtitle(_title), vdescription(_description), vthumb_url(_thumb_url), vcontent_url(_content_url), vw(_w), vh(_h), vduration(_duration) { + } + + MTPint vflags; + MTPstring vurl; + MTPstring vdisplay_url; + MTPstring vtype; + MTPstring vtitle; + MTPstring vdescription; + MTPstring vthumb_url; + MTPstring vcontent_url; + MTPint vw; + MTPint vh; + MTPint vduration; + + enum { + flag_type = (1 << 0), + flag_title = (1 << 1), + flag_description = (1 << 2), + flag_thumb_url = (1 << 3), + flag_content_url = (1 << 4), + flag_w = (1 << 5), + flag_h = (1 << 5), + flag_duration = (1 << 6), + }; + + bool has_type() const { return vflags.v & flag_type; } + bool has_title() const { return vflags.v & flag_title; } + bool has_description() const { return vflags.v & flag_description; } + bool has_thumb_url() const { return vflags.v & flag_thumb_url; } + bool has_content_url() const { return vflags.v & flag_content_url; } + bool has_w() const { return vflags.v & flag_w; } + bool has_h() const { return vflags.v & flag_h; } + bool has_duration() const { return vflags.v & flag_duration; } +}; + class MTPDauthorization : public mtpDataImpl { public: MTPDauthorization() { @@ -12857,6 +13018,27 @@ public: MTPstring vtext; }; +class MTPDfoundGif : public mtpDataImpl { +public: + MTPDfoundGif() { + } + MTPDfoundGif(const MTPWebPage &_webpage) : vwebpage(_webpage) { + } + + MTPWebPage vwebpage; +}; + +class MTPDmessages_foundGifs : public mtpDataImpl { +public: + MTPDmessages_foundGifs() { + } + MTPDmessages_foundGifs(MTPint _next_offset, const MTPVector &_results) : vnext_offset(_next_offset), vresults(_results) { + } + + MTPint vnext_offset; + MTPVector vresults; +}; + // RPC methods class MTPreq_pq { // RPC method 'req_pq' @@ -17807,6 +17989,93 @@ public: } }; +class MTPmessages_getDocumentByHash { // RPC method 'messages.getDocumentByHash' +public: + MTPbytes vsha256; + MTPint vsize; + MTPstring vmime_type; + + MTPmessages_getDocumentByHash() { + } + MTPmessages_getDocumentByHash(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getDocumentByHash) { + read(from, end, cons); + } + MTPmessages_getDocumentByHash(const MTPbytes &_sha256, MTPint _size, const MTPstring &_mime_type) : vsha256(_sha256), vsize(_size), vmime_type(_mime_type) { + } + + uint32 innerLength() const { + return vsha256.innerLength() + vsize.innerLength() + vmime_type.innerLength(); + } + mtpTypeId type() const { + return mtpc_messages_getDocumentByHash; + } + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getDocumentByHash) { + vsha256.read(from, end); + vsize.read(from, end); + vmime_type.read(from, end); + } + void write(mtpBuffer &to) const { + vsha256.write(to); + vsize.write(to); + vmime_type.write(to); + } + + typedef MTPDocument ResponseType; +}; +class MTPmessages_GetDocumentByHash : public MTPBoxed { +public: + MTPmessages_GetDocumentByHash() { + } + MTPmessages_GetDocumentByHash(const MTPmessages_getDocumentByHash &v) : MTPBoxed(v) { + } + MTPmessages_GetDocumentByHash(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed(from, end, cons) { + } + MTPmessages_GetDocumentByHash(const MTPbytes &_sha256, MTPint _size, const MTPstring &_mime_type) : MTPBoxed(MTPmessages_getDocumentByHash(_sha256, _size, _mime_type)) { + } +}; + +class MTPmessages_searchGifs { // RPC method 'messages.searchGifs' +public: + MTPstring vq; + MTPint voffset; + + MTPmessages_searchGifs() { + } + MTPmessages_searchGifs(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_searchGifs) { + read(from, end, cons); + } + MTPmessages_searchGifs(const MTPstring &_q, MTPint _offset) : vq(_q), voffset(_offset) { + } + + uint32 innerLength() const { + return vq.innerLength() + voffset.innerLength(); + } + mtpTypeId type() const { + return mtpc_messages_searchGifs; + } + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_searchGifs) { + vq.read(from, end); + voffset.read(from, end); + } + void write(mtpBuffer &to) const { + vq.write(to); + voffset.write(to); + } + + typedef MTPmessages_FoundGifs ResponseType; +}; +class MTPmessages_SearchGifs : public MTPBoxed { +public: + MTPmessages_SearchGifs() { + } + MTPmessages_SearchGifs(const MTPmessages_searchGifs &v) : MTPBoxed(v) { + } + MTPmessages_SearchGifs(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed(from, end, cons) { + } + MTPmessages_SearchGifs(const MTPstring &_q, MTPint _offset) : MTPBoxed(MTPmessages_searchGifs(_q, _offset)) { + } +}; + class MTPupdates_getState { // RPC method 'updates.getState' public: MTPupdates_getState() { @@ -20918,6 +21187,10 @@ inline uint32 MTPinputMedia::innerLength() const { const MTPDinputMediaVenue &v(c_inputMediaVenue()); return v.vgeo_point.innerLength() + v.vtitle.innerLength() + v.vaddress.innerLength() + v.vprovider.innerLength() + v.vvenue_id.innerLength(); } + case mtpc_inputMediaGifExternal: { + const MTPDinputMediaGifExternal &v(c_inputMediaGifExternal()); + return v.vurl.innerLength() + v.vq.innerLength(); + } } return 0; } @@ -21021,6 +21294,12 @@ inline void MTPinputMedia::read(const mtpPrime *&from, const mtpPrime *end, mtpT v.vprovider.read(from, end); v.vvenue_id.read(from, end); } break; + case mtpc_inputMediaGifExternal: _type = cons; { + if (!data) setData(new MTPDinputMediaGifExternal()); + MTPDinputMediaGifExternal &v(_inputMediaGifExternal()); + v.vurl.read(from, end); + v.vq.read(from, end); + } break; default: throw mtpErrorUnexpected(cons, "MTPinputMedia"); } } @@ -21105,6 +21384,11 @@ inline void MTPinputMedia::write(mtpBuffer &to) const { v.vprovider.write(to); v.vvenue_id.write(to); } break; + case mtpc_inputMediaGifExternal: { + const MTPDinputMediaGifExternal &v(c_inputMediaGifExternal()); + v.vurl.write(to); + v.vq.write(to); + } break; } } inline MTPinputMedia::MTPinputMedia(mtpTypeId type) : mtpDataOwner(0), _type(type) { @@ -21123,6 +21407,7 @@ inline MTPinputMedia::MTPinputMedia(mtpTypeId type) : mtpDataOwner(0), _type(typ case mtpc_inputMediaUploadedThumbDocument: setData(new MTPDinputMediaUploadedThumbDocument()); break; case mtpc_inputMediaDocument: setData(new MTPDinputMediaDocument()); break; case mtpc_inputMediaVenue: setData(new MTPDinputMediaVenue()); break; + case mtpc_inputMediaGifExternal: setData(new MTPDinputMediaGifExternal()); break; default: throw mtpErrorBadTypeId(type, "MTPinputMedia"); } } @@ -21152,6 +21437,8 @@ inline MTPinputMedia::MTPinputMedia(MTPDinputMediaDocument *_data) : mtpDataOwne } inline MTPinputMedia::MTPinputMedia(MTPDinputMediaVenue *_data) : mtpDataOwner(_data), _type(mtpc_inputMediaVenue) { } +inline MTPinputMedia::MTPinputMedia(MTPDinputMediaGifExternal *_data) : mtpDataOwner(_data), _type(mtpc_inputMediaGifExternal) { +} inline MTPinputMedia MTP_inputMediaEmpty() { return MTPinputMedia(mtpc_inputMediaEmpty); } @@ -21194,6 +21481,9 @@ inline MTPinputMedia MTP_inputMediaDocument(const MTPInputDocument &_id) { inline MTPinputMedia MTP_inputMediaVenue(const MTPInputGeoPoint &_geo_point, const MTPstring &_title, const MTPstring &_address, const MTPstring &_provider, const MTPstring &_venue_id) { return MTPinputMedia(new MTPDinputMediaVenue(_geo_point, _title, _address, _provider, _venue_id)); } +inline MTPinputMedia MTP_inputMediaGifExternal(const MTPstring &_url, const MTPstring &_q) { + return MTPinputMedia(new MTPDinputMediaGifExternal(_url, _q)); +} inline uint32 MTPinputChatPhoto::innerLength() const { switch (_type) { @@ -27996,6 +28286,10 @@ inline uint32 MTPwebPage::innerLength() const { const MTPDwebPage &v(c_webPage()); return v.vflags.innerLength() + v.vid.innerLength() + v.vurl.innerLength() + v.vdisplay_url.innerLength() + (v.has_type() ? v.vtype.innerLength() : 0) + (v.has_site_name() ? v.vsite_name.innerLength() : 0) + (v.has_title() ? v.vtitle.innerLength() : 0) + (v.has_description() ? v.vdescription.innerLength() : 0) + (v.has_photo() ? v.vphoto.innerLength() : 0) + (v.has_embed_url() ? v.vembed_url.innerLength() : 0) + (v.has_embed_type() ? v.vembed_type.innerLength() : 0) + (v.has_embed_width() ? v.vembed_width.innerLength() : 0) + (v.has_embed_height() ? v.vembed_height.innerLength() : 0) + (v.has_duration() ? v.vduration.innerLength() : 0) + (v.has_author() ? v.vauthor.innerLength() : 0) + (v.has_document() ? v.vdocument.innerLength() : 0); } + case mtpc_webPageExternal: { + const MTPDwebPageExternal &v(c_webPageExternal()); + return v.vflags.innerLength() + v.vurl.innerLength() + v.vdisplay_url.innerLength() + (v.has_type() ? v.vtype.innerLength() : 0) + (v.has_title() ? v.vtitle.innerLength() : 0) + (v.has_description() ? v.vdescription.innerLength() : 0) + (v.has_thumb_url() ? v.vthumb_url.innerLength() : 0) + (v.has_content_url() ? v.vcontent_url.innerLength() : 0) + (v.has_w() ? v.vw.innerLength() : 0) + (v.has_h() ? v.vh.innerLength() : 0) + (v.has_duration() ? v.vduration.innerLength() : 0); + } } return 0; } @@ -28037,6 +28331,21 @@ inline void MTPwebPage::read(const mtpPrime *&from, const mtpPrime *end, mtpType if (v.has_author()) { v.vauthor.read(from, end); } else { v.vauthor = MTPstring(); } if (v.has_document()) { v.vdocument.read(from, end); } else { v.vdocument = MTPDocument(); } } break; + case mtpc_webPageExternal: _type = cons; { + if (!data) setData(new MTPDwebPageExternal()); + MTPDwebPageExternal &v(_webPageExternal()); + v.vflags.read(from, end); + v.vurl.read(from, end); + v.vdisplay_url.read(from, end); + if (v.has_type()) { v.vtype.read(from, end); } else { v.vtype = MTPstring(); } + if (v.has_title()) { v.vtitle.read(from, end); } else { v.vtitle = MTPstring(); } + if (v.has_description()) { v.vdescription.read(from, end); } else { v.vdescription = MTPstring(); } + if (v.has_thumb_url()) { v.vthumb_url.read(from, end); } else { v.vthumb_url = MTPstring(); } + if (v.has_content_url()) { v.vcontent_url.read(from, end); } else { v.vcontent_url = MTPstring(); } + if (v.has_w()) { v.vw.read(from, end); } else { v.vw = MTPint(); } + if (v.has_h()) { v.vh.read(from, end); } else { v.vh = MTPint(); } + if (v.has_duration()) { v.vduration.read(from, end); } else { v.vduration = MTPint(); } + } break; default: throw mtpErrorUnexpected(cons, "MTPwebPage"); } } @@ -28070,6 +28379,20 @@ inline void MTPwebPage::write(mtpBuffer &to) const { if (v.has_author()) v.vauthor.write(to); if (v.has_document()) v.vdocument.write(to); } break; + case mtpc_webPageExternal: { + const MTPDwebPageExternal &v(c_webPageExternal()); + v.vflags.write(to); + v.vurl.write(to); + v.vdisplay_url.write(to); + if (v.has_type()) v.vtype.write(to); + if (v.has_title()) v.vtitle.write(to); + if (v.has_description()) v.vdescription.write(to); + if (v.has_thumb_url()) v.vthumb_url.write(to); + if (v.has_content_url()) v.vcontent_url.write(to); + if (v.has_w()) v.vw.write(to); + if (v.has_h()) v.vh.write(to); + if (v.has_duration()) v.vduration.write(to); + } break; } } inline MTPwebPage::MTPwebPage(mtpTypeId type) : mtpDataOwner(0), _type(type) { @@ -28077,6 +28400,7 @@ inline MTPwebPage::MTPwebPage(mtpTypeId type) : mtpDataOwner(0), _type(type) { case mtpc_webPageEmpty: setData(new MTPDwebPageEmpty()); break; case mtpc_webPagePending: setData(new MTPDwebPagePending()); break; case mtpc_webPage: setData(new MTPDwebPage()); break; + case mtpc_webPageExternal: setData(new MTPDwebPageExternal()); break; default: throw mtpErrorBadTypeId(type, "MTPwebPage"); } } @@ -28086,6 +28410,8 @@ inline MTPwebPage::MTPwebPage(MTPDwebPagePending *_data) : mtpDataOwner(_data), } inline MTPwebPage::MTPwebPage(MTPDwebPage *_data) : mtpDataOwner(_data), _type(mtpc_webPage) { } +inline MTPwebPage::MTPwebPage(MTPDwebPageExternal *_data) : mtpDataOwner(_data), _type(mtpc_webPageExternal) { +} inline MTPwebPage MTP_webPageEmpty(const MTPlong &_id) { return MTPwebPage(new MTPDwebPageEmpty(_id)); } @@ -28095,6 +28421,9 @@ inline MTPwebPage MTP_webPagePending(const MTPlong &_id, MTPint _date) { inline MTPwebPage MTP_webPage(MTPint _flags, const MTPlong &_id, const MTPstring &_url, const MTPstring &_display_url, const MTPstring &_type, const MTPstring &_site_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPstring &_embed_url, const MTPstring &_embed_type, MTPint _embed_width, MTPint _embed_height, MTPint _duration, const MTPstring &_author, const MTPDocument &_document) { return MTPwebPage(new MTPDwebPage(_flags, _id, _url, _display_url, _type, _site_name, _title, _description, _photo, _embed_url, _embed_type, _embed_width, _embed_height, _duration, _author, _document)); } +inline MTPwebPage MTP_webPageExternal(MTPint _flags, const MTPstring &_url, const MTPstring &_display_url, const MTPstring &_type, const MTPstring &_title, const MTPstring &_description, const MTPstring &_thumb_url, const MTPstring &_content_url, MTPint _w, MTPint _h, MTPint _duration) { + return MTPwebPage(new MTPDwebPageExternal(_flags, _url, _display_url, _type, _title, _description, _thumb_url, _content_url, _w, _h, _duration)); +} inline MTPauthorization::MTPauthorization() : mtpDataOwner(new MTPDauthorization()) { } @@ -29793,6 +30122,62 @@ inline MTPhelp_termsOfService MTP_help_termsOfService(const MTPstring &_text) { return MTPhelp_termsOfService(new MTPDhelp_termsOfService(_text)); } +inline MTPfoundGif::MTPfoundGif() : mtpDataOwner(new MTPDfoundGif()) { +} + +inline uint32 MTPfoundGif::innerLength() const { + const MTPDfoundGif &v(c_foundGif()); + return v.vwebpage.innerLength(); +} +inline mtpTypeId MTPfoundGif::type() const { + return mtpc_foundGif; +} +inline void MTPfoundGif::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) { + if (cons != mtpc_foundGif) throw mtpErrorUnexpected(cons, "MTPfoundGif"); + + if (!data) setData(new MTPDfoundGif()); + MTPDfoundGif &v(_foundGif()); + v.vwebpage.read(from, end); +} +inline void MTPfoundGif::write(mtpBuffer &to) const { + const MTPDfoundGif &v(c_foundGif()); + v.vwebpage.write(to); +} +inline MTPfoundGif::MTPfoundGif(MTPDfoundGif *_data) : mtpDataOwner(_data) { +} +inline MTPfoundGif MTP_foundGif(const MTPWebPage &_webpage) { + return MTPfoundGif(new MTPDfoundGif(_webpage)); +} + +inline MTPmessages_foundGifs::MTPmessages_foundGifs() : mtpDataOwner(new MTPDmessages_foundGifs()) { +} + +inline uint32 MTPmessages_foundGifs::innerLength() const { + const MTPDmessages_foundGifs &v(c_messages_foundGifs()); + return v.vnext_offset.innerLength() + v.vresults.innerLength(); +} +inline mtpTypeId MTPmessages_foundGifs::type() const { + return mtpc_messages_foundGifs; +} +inline void MTPmessages_foundGifs::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) { + if (cons != mtpc_messages_foundGifs) throw mtpErrorUnexpected(cons, "MTPmessages_foundGifs"); + + if (!data) setData(new MTPDmessages_foundGifs()); + MTPDmessages_foundGifs &v(_messages_foundGifs()); + v.vnext_offset.read(from, end); + v.vresults.read(from, end); +} +inline void MTPmessages_foundGifs::write(mtpBuffer &to) const { + const MTPDmessages_foundGifs &v(c_messages_foundGifs()); + v.vnext_offset.write(to); + v.vresults.write(to); +} +inline MTPmessages_foundGifs::MTPmessages_foundGifs(MTPDmessages_foundGifs *_data) : mtpDataOwner(_data) { +} +inline MTPmessages_foundGifs MTP_messages_foundGifs(MTPint _next_offset, const MTPVector &_results) { + return MTPmessages_foundGifs(new MTPDmessages_foundGifs(_next_offset, _results)); +} + // Human-readable text serialization #if (defined _DEBUG || defined _WITH_DEBUG) diff --git a/Telegram/SourceFiles/mtproto/scheme.tl b/Telegram/SourceFiles/mtproto/scheme.tl index db9481ad0..265877516 100644 --- a/Telegram/SourceFiles/mtproto/scheme.tl +++ b/Telegram/SourceFiles/mtproto/scheme.tl @@ -159,6 +159,7 @@ inputMediaUploadedDocument#ffe76b78 file:InputFile mime_type:string attributes:V inputMediaUploadedThumbDocument#41481486 file:InputFile thumb:InputFile mime_type:string attributes:Vector = InputMedia; inputMediaDocument#d184e841 id:InputDocument = InputMedia; inputMediaVenue#2827a81a geo_point:InputGeoPoint title:string address:string provider:string venue_id:string = InputMedia; +inputMediaGifExternal#4843b0fd url:string q:string = InputMedia; inputChatPhotoEmpty#1ca48f57 = InputChatPhoto; inputChatUploadedPhoto#94254732 file:InputFile crop:InputPhotoCrop = InputChatPhoto; @@ -203,7 +204,7 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation; userEmpty#200250ba id:int = User; -user#22e49072 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int = User; +user#22e49072 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true explicit_content:flags.18?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int = User; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto; @@ -218,7 +219,7 @@ userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#9ba2d800 id:int = Chat; chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = Chat; chatForbidden#7328bdb id:int title:string = Chat; -channel#678e9587 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true id:int access_hash:long title:string username:flags.6?string photo:ChatPhoto date:int version:int = Chat; +channel#678e9587 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true explicit_content:flags.9?true id:int access_hash:long title:string username:flags.6?string photo:ChatPhoto date:int version:int = Chat; channelForbidden#2d85832c id:int access_hash:long title:string = Chat; chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector = ChatFull; @@ -538,6 +539,7 @@ contactLinkContact#d502c2d0 = ContactLink; webPageEmpty#eb1477e8 id:long = WebPage; webPagePending#c586da1c id:long date:int = WebPage; webPage#ca820ed7 flags:# id:long url:string display_url:string type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document = WebPage; +webPageExternal#cf73f207 flags:# url:string display_url:string type:flags.0?string title:flags.1?string description:flags.2?string thumb_url:flags.3?string content_url:flags.4?string w:flags.5?int h:flags.5?int duration:flags.6?int = WebPage; authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; @@ -635,6 +637,10 @@ channels.channelParticipant#d0d9b163 participant:ChannelParticipant users:Vector help.termsOfService#f1ee3e90 text:string = help.TermsOfService; +foundGif#d579cccb webpage:WebPage = FoundGif; + +messages.foundGifs#450a1c0a next_offset:int results:Vector = messages.FoundGifs; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -751,6 +757,8 @@ messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bo messages.migrateChat#15a3b8e3 chat_id:int = Updates; messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; messages.reorderStickerSets#9fcfbc30 order:Vector = Bool; +messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document; +messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs; updates.getState#edd4882a = updates.State; updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 6336a2520..35f02ae9b 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -2906,7 +2906,7 @@ int32 OverviewWidget::countBestScroll() const { } void OverviewWidget::fastShow(bool back, int32 lastScrollTop) { - App::stopGifItems(); +// App::stopGifItems(); resizeEvent(0); _scrollSetAfterShow = (lastScrollTop < 0 ? countBestScroll() : lastScrollTop); show(); @@ -2919,7 +2919,7 @@ void OverviewWidget::fastShow(bool back, int32 lastScrollTop) { void OverviewWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back, int32 lastScrollTop) { if (App::app()) App::app()->mtpPause(); - App::stopGifItems(); +// App::stopGifItems(); (back ? _cacheOver : _cacheUnder) = bgAnimCache; (back ? _cacheTopBarOver : _cacheTopBarUnder) = bgAnimTopBarCache; diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index c1f177a94..14eb6d2b9 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -1832,7 +1832,7 @@ int32 ProfileWidget::lastScrollTop() const { void ProfileWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back, int32 lastScrollTop) { if (App::app()) App::app()->mtpPause(); - App::stopGifItems(); +// App::stopGifItems(); (back ? _cacheOver : _cacheUnder) = bgAnimCache; (back ? _cacheTopBarOver : _cacheTopBarUnder) = bgAnimTopBarCache; From 1416e4e277d055b5ecae60e81b44a3a1818553f5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Dec 2015 19:07:08 +0300 Subject: [PATCH 030/145] scheme updated --- Telegram/SourceFiles/mtproto/mtpScheme.cpp | 6 ++-- Telegram/SourceFiles/mtproto/mtpScheme.h | 42 +++++++++++++--------- Telegram/SourceFiles/mtproto/scheme.tl | 4 +-- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.cpp b/Telegram/SourceFiles/mtproto/mtpScheme.cpp index 93b7d74ef..652a18686 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.cpp +++ b/Telegram/SourceFiles/mtproto/mtpScheme.cpp @@ -1130,7 +1130,7 @@ void _serialize_user(MTPStringLogger &to, int32 stage, int32 lev, Types &types, case 6: to.add(" bot_chat_history: "); ++stages.back(); if (flag & MTPDuser::flag_bot_chat_history) { to.add("YES [ BY BIT 15 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 15 IN FIELD flags ]"); } break; case 7: to.add(" bot_nochats: "); ++stages.back(); if (flag & MTPDuser::flag_bot_nochats) { to.add("YES [ BY BIT 16 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 16 IN FIELD flags ]"); } break; case 8: to.add(" verified: "); ++stages.back(); if (flag & MTPDuser::flag_verified) { to.add("YES [ BY BIT 17 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 17 IN FIELD flags ]"); } break; - case 9: to.add(" explicit_content: "); ++stages.back(); if (flag & MTPDuser::flag_explicit_content) { to.add("YES [ BY BIT 18 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 18 IN FIELD flags ]"); } break; + case 9: to.add(" restricted: "); ++stages.back(); if (flag & MTPDuser::flag_restricted) { to.add("YES [ BY BIT 18 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 18 IN FIELD flags ]"); } break; case 10: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 11: to.add(" access_hash: "); ++stages.back(); if (flag & MTPDuser::flag_access_hash) { types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break; case 12: to.add(" first_name: "); ++stages.back(); if (flag & MTPDuser::flag_first_name) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break; @@ -1140,6 +1140,7 @@ void _serialize_user(MTPStringLogger &to, int32 stage, int32 lev, Types &types, case 16: to.add(" photo: "); ++stages.back(); if (flag & MTPDuser::flag_photo) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break; case 17: to.add(" status: "); ++stages.back(); if (flag & MTPDuser::flag_status) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break; case 18: to.add(" bot_info_version: "); ++stages.back(); if (flag & MTPDuser::flag_bot_info_version) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 14 IN FIELD flags ]"); } break; + case 19: to.add(" restiction_reason: "); ++stages.back(); if (flag & MTPDuser::flag_restiction_reason) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 18 IN FIELD flags ]"); } break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; } } @@ -1275,7 +1276,7 @@ void _serialize_channel(MTPStringLogger &to, int32 stage, int32 lev, Types &type case 6: to.add(" broadcast: "); ++stages.back(); if (flag & MTPDchannel::flag_broadcast) { to.add("YES [ BY BIT 5 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break; case 7: to.add(" verified: "); ++stages.back(); if (flag & MTPDchannel::flag_verified) { to.add("YES [ BY BIT 7 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 7 IN FIELD flags ]"); } break; case 8: to.add(" megagroup: "); ++stages.back(); if (flag & MTPDchannel::flag_megagroup) { to.add("YES [ BY BIT 8 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 8 IN FIELD flags ]"); } break; - case 9: to.add(" explicit_content: "); ++stages.back(); if (flag & MTPDchannel::flag_explicit_content) { to.add("YES [ BY BIT 9 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 9 IN FIELD flags ]"); } break; + case 9: to.add(" restricted: "); ++stages.back(); if (flag & MTPDchannel::flag_restricted) { to.add("YES [ BY BIT 9 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 9 IN FIELD flags ]"); } break; case 10: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 11: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 12: to.add(" title: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; @@ -1283,6 +1284,7 @@ void _serialize_channel(MTPStringLogger &to, int32 stage, int32 lev, Types &type case 14: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 15: to.add(" date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 16: to.add(" version: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 17: to.add(" restiction_reason: "); ++stages.back(); if (flag & MTPDchannel::flag_restiction_reason) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 9 IN FIELD flags ]"); } break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; } } diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.h b/Telegram/SourceFiles/mtproto/mtpScheme.h index 8d9765ce5..40183195e 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.h +++ b/Telegram/SourceFiles/mtproto/mtpScheme.h @@ -130,7 +130,7 @@ enum { mtpc_fileLocationUnavailable = 0x7c596b46, mtpc_fileLocation = 0x53d69076, mtpc_userEmpty = 0x200250ba, - mtpc_user = 0x22e49072, + mtpc_user = 0x3289b590, mtpc_userProfilePhotoEmpty = 0x4f11bae1, mtpc_userProfilePhoto = 0xd559d8c8, mtpc_userStatusEmpty = 0x9d05049, @@ -142,7 +142,7 @@ enum { mtpc_chatEmpty = 0x9ba2d800, mtpc_chat = 0xd91cdd54, mtpc_chatForbidden = 0x7328bdb, - mtpc_channel = 0x678e9587, + mtpc_channel = 0xe834ce68, mtpc_channelForbidden = 0x2d85832c, mtpc_chatFull = 0x2e02a614, mtpc_channelFull = 0x9e341ddf, @@ -3194,7 +3194,7 @@ private: explicit MTPuser(MTPDuser *_data); friend MTPuser MTP_userEmpty(MTPint _id); - friend MTPuser MTP_user(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_first_name, const MTPstring &_last_name, const MTPstring &_username, const MTPstring &_phone, const MTPUserProfilePhoto &_photo, const MTPUserStatus &_status, MTPint _bot_info_version); + friend MTPuser MTP_user(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_first_name, const MTPstring &_last_name, const MTPstring &_username, const MTPstring &_phone, const MTPUserProfilePhoto &_photo, const MTPUserStatus &_status, MTPint _bot_info_version, const MTPstring &_restiction_reason); mtpTypeId _type; }; @@ -3379,7 +3379,7 @@ private: friend MTPchat MTP_chatEmpty(MTPint _id); friend MTPchat MTP_chat(MTPint _flags, MTPint _id, const MTPstring &_title, const MTPChatPhoto &_photo, MTPint _participants_count, MTPint _date, MTPint _version, const MTPInputChannel &_migrated_to); friend MTPchat MTP_chatForbidden(MTPint _id, const MTPstring &_title); - friend MTPchat MTP_channel(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_title, const MTPstring &_username, const MTPChatPhoto &_photo, MTPint _date, MTPint _version); + friend MTPchat MTP_channel(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_title, const MTPstring &_username, const MTPChatPhoto &_photo, MTPint _date, MTPint _version, const MTPstring &_restiction_reason); friend MTPchat MTP_channelForbidden(MTPint _id, const MTPlong &_access_hash, const MTPstring &_title); mtpTypeId _type; @@ -9800,7 +9800,7 @@ class MTPDuser : public mtpDataImpl { public: MTPDuser() { } - MTPDuser(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_first_name, const MTPstring &_last_name, const MTPstring &_username, const MTPstring &_phone, const MTPUserProfilePhoto &_photo, const MTPUserStatus &_status, MTPint _bot_info_version) : vflags(_flags), vid(_id), vaccess_hash(_access_hash), vfirst_name(_first_name), vlast_name(_last_name), vusername(_username), vphone(_phone), vphoto(_photo), vstatus(_status), vbot_info_version(_bot_info_version) { + MTPDuser(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_first_name, const MTPstring &_last_name, const MTPstring &_username, const MTPstring &_phone, const MTPUserProfilePhoto &_photo, const MTPUserStatus &_status, MTPint _bot_info_version, const MTPstring &_restiction_reason) : vflags(_flags), vid(_id), vaccess_hash(_access_hash), vfirst_name(_first_name), vlast_name(_last_name), vusername(_username), vphone(_phone), vphoto(_photo), vstatus(_status), vbot_info_version(_bot_info_version), vrestiction_reason(_restiction_reason) { } MTPint vflags; @@ -9813,6 +9813,7 @@ public: MTPUserProfilePhoto vphoto; MTPUserStatus vstatus; MTPint vbot_info_version; + MTPstring vrestiction_reason; enum { flag_self = (1 << 10), @@ -9823,7 +9824,7 @@ public: flag_bot_chat_history = (1 << 15), flag_bot_nochats = (1 << 16), flag_verified = (1 << 17), - flag_explicit_content = (1 << 18), + flag_restricted = (1 << 18), flag_access_hash = (1 << 0), flag_first_name = (1 << 1), flag_last_name = (1 << 2), @@ -9832,6 +9833,7 @@ public: flag_photo = (1 << 5), flag_status = (1 << 6), flag_bot_info_version = (1 << 14), + flag_restiction_reason = (1 << 18), }; bool is_self() const { return vflags.v & flag_self; } @@ -9842,7 +9844,7 @@ public: bool is_bot_chat_history() const { return vflags.v & flag_bot_chat_history; } bool is_bot_nochats() const { return vflags.v & flag_bot_nochats; } bool is_verified() const { return vflags.v & flag_verified; } - bool is_explicit_content() const { return vflags.v & flag_explicit_content; } + bool is_restricted() const { return vflags.v & flag_restricted; } bool has_access_hash() const { return vflags.v & flag_access_hash; } bool has_first_name() const { return vflags.v & flag_first_name; } bool has_last_name() const { return vflags.v & flag_last_name; } @@ -9851,6 +9853,7 @@ public: bool has_photo() const { return vflags.v & flag_photo; } bool has_status() const { return vflags.v & flag_status; } bool has_bot_info_version() const { return vflags.v & flag_bot_info_version; } + bool has_restiction_reason() const { return vflags.v & flag_restiction_reason; } }; class MTPDuserProfilePhoto : public mtpDataImpl { @@ -9945,7 +9948,7 @@ class MTPDchannel : public mtpDataImpl { public: MTPDchannel() { } - MTPDchannel(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_title, const MTPstring &_username, const MTPChatPhoto &_photo, MTPint _date, MTPint _version) : vflags(_flags), vid(_id), vaccess_hash(_access_hash), vtitle(_title), vusername(_username), vphoto(_photo), vdate(_date), vversion(_version) { + MTPDchannel(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_title, const MTPstring &_username, const MTPChatPhoto &_photo, MTPint _date, MTPint _version, const MTPstring &_restiction_reason) : vflags(_flags), vid(_id), vaccess_hash(_access_hash), vtitle(_title), vusername(_username), vphoto(_photo), vdate(_date), vversion(_version), vrestiction_reason(_restiction_reason) { } MTPint vflags; @@ -9956,6 +9959,7 @@ public: MTPChatPhoto vphoto; MTPint vdate; MTPint vversion; + MTPstring vrestiction_reason; enum { flag_creator = (1 << 0), @@ -9966,8 +9970,9 @@ public: flag_broadcast = (1 << 5), flag_verified = (1 << 7), flag_megagroup = (1 << 8), - flag_explicit_content = (1 << 9), + flag_restricted = (1 << 9), flag_username = (1 << 6), + flag_restiction_reason = (1 << 9), }; bool is_creator() const { return vflags.v & flag_creator; } @@ -9978,8 +9983,9 @@ public: bool is_broadcast() const { return vflags.v & flag_broadcast; } bool is_verified() const { return vflags.v & flag_verified; } bool is_megagroup() const { return vflags.v & flag_megagroup; } - bool is_explicit_content() const { return vflags.v & flag_explicit_content; } + bool is_restricted() const { return vflags.v & flag_restricted; } bool has_username() const { return vflags.v & flag_username; } + bool has_restiction_reason() const { return vflags.v & flag_restiction_reason; } }; class MTPDchannelForbidden : public mtpDataImpl { @@ -22163,7 +22169,7 @@ inline uint32 MTPuser::innerLength() const { } case mtpc_user: { const MTPDuser &v(c_user()); - return v.vflags.innerLength() + v.vid.innerLength() + (v.has_access_hash() ? v.vaccess_hash.innerLength() : 0) + (v.has_first_name() ? v.vfirst_name.innerLength() : 0) + (v.has_last_name() ? v.vlast_name.innerLength() : 0) + (v.has_username() ? v.vusername.innerLength() : 0) + (v.has_phone() ? v.vphone.innerLength() : 0) + (v.has_photo() ? v.vphoto.innerLength() : 0) + (v.has_status() ? v.vstatus.innerLength() : 0) + (v.has_bot_info_version() ? v.vbot_info_version.innerLength() : 0); + return v.vflags.innerLength() + v.vid.innerLength() + (v.has_access_hash() ? v.vaccess_hash.innerLength() : 0) + (v.has_first_name() ? v.vfirst_name.innerLength() : 0) + (v.has_last_name() ? v.vlast_name.innerLength() : 0) + (v.has_username() ? v.vusername.innerLength() : 0) + (v.has_phone() ? v.vphone.innerLength() : 0) + (v.has_photo() ? v.vphoto.innerLength() : 0) + (v.has_status() ? v.vstatus.innerLength() : 0) + (v.has_bot_info_version() ? v.vbot_info_version.innerLength() : 0) + (v.has_restiction_reason() ? v.vrestiction_reason.innerLength() : 0); } } return 0; @@ -22193,6 +22199,7 @@ inline void MTPuser::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId if (v.has_photo()) { v.vphoto.read(from, end); } else { v.vphoto = MTPUserProfilePhoto(); } if (v.has_status()) { v.vstatus.read(from, end); } else { v.vstatus = MTPUserStatus(); } if (v.has_bot_info_version()) { v.vbot_info_version.read(from, end); } else { v.vbot_info_version = MTPint(); } + if (v.has_restiction_reason()) { v.vrestiction_reason.read(from, end); } else { v.vrestiction_reason = MTPstring(); } } break; default: throw mtpErrorUnexpected(cons, "MTPuser"); } @@ -22215,6 +22222,7 @@ inline void MTPuser::write(mtpBuffer &to) const { if (v.has_photo()) v.vphoto.write(to); if (v.has_status()) v.vstatus.write(to); if (v.has_bot_info_version()) v.vbot_info_version.write(to); + if (v.has_restiction_reason()) v.vrestiction_reason.write(to); } break; } } @@ -22232,8 +22240,8 @@ inline MTPuser::MTPuser(MTPDuser *_data) : mtpDataOwner(_data), _type(mtpc_user) inline MTPuser MTP_userEmpty(MTPint _id) { return MTPuser(new MTPDuserEmpty(_id)); } -inline MTPuser MTP_user(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_first_name, const MTPstring &_last_name, const MTPstring &_username, const MTPstring &_phone, const MTPUserProfilePhoto &_photo, const MTPUserStatus &_status, MTPint _bot_info_version) { - return MTPuser(new MTPDuser(_flags, _id, _access_hash, _first_name, _last_name, _username, _phone, _photo, _status, _bot_info_version)); +inline MTPuser MTP_user(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_first_name, const MTPstring &_last_name, const MTPstring &_username, const MTPstring &_phone, const MTPUserProfilePhoto &_photo, const MTPUserStatus &_status, MTPint _bot_info_version, const MTPstring &_restiction_reason) { + return MTPuser(new MTPDuser(_flags, _id, _access_hash, _first_name, _last_name, _username, _phone, _photo, _status, _bot_info_version, _restiction_reason)); } inline uint32 MTPuserProfilePhoto::innerLength() const { @@ -22388,7 +22396,7 @@ inline uint32 MTPchat::innerLength() const { } case mtpc_channel: { const MTPDchannel &v(c_channel()); - return v.vflags.innerLength() + v.vid.innerLength() + v.vaccess_hash.innerLength() + v.vtitle.innerLength() + (v.has_username() ? v.vusername.innerLength() : 0) + v.vphoto.innerLength() + v.vdate.innerLength() + v.vversion.innerLength(); + return v.vflags.innerLength() + v.vid.innerLength() + v.vaccess_hash.innerLength() + v.vtitle.innerLength() + (v.has_username() ? v.vusername.innerLength() : 0) + v.vphoto.innerLength() + v.vdate.innerLength() + v.vversion.innerLength() + (v.has_restiction_reason() ? v.vrestiction_reason.innerLength() : 0); } case mtpc_channelForbidden: { const MTPDchannelForbidden &v(c_channelForbidden()); @@ -22438,6 +22446,7 @@ inline void MTPchat::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId v.vphoto.read(from, end); v.vdate.read(from, end); v.vversion.read(from, end); + if (v.has_restiction_reason()) { v.vrestiction_reason.read(from, end); } else { v.vrestiction_reason = MTPstring(); } } break; case mtpc_channelForbidden: _type = cons; { if (!data) setData(new MTPDchannelForbidden()); @@ -22481,6 +22490,7 @@ inline void MTPchat::write(mtpBuffer &to) const { v.vphoto.write(to); v.vdate.write(to); v.vversion.write(to); + if (v.has_restiction_reason()) v.vrestiction_reason.write(to); } break; case mtpc_channelForbidden: { const MTPDchannelForbidden &v(c_channelForbidden()); @@ -22519,8 +22529,8 @@ inline MTPchat MTP_chat(MTPint _flags, MTPint _id, const MTPstring &_title, cons inline MTPchat MTP_chatForbidden(MTPint _id, const MTPstring &_title) { return MTPchat(new MTPDchatForbidden(_id, _title)); } -inline MTPchat MTP_channel(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_title, const MTPstring &_username, const MTPChatPhoto &_photo, MTPint _date, MTPint _version) { - return MTPchat(new MTPDchannel(_flags, _id, _access_hash, _title, _username, _photo, _date, _version)); +inline MTPchat MTP_channel(MTPint _flags, MTPint _id, const MTPlong &_access_hash, const MTPstring &_title, const MTPstring &_username, const MTPChatPhoto &_photo, MTPint _date, MTPint _version, const MTPstring &_restiction_reason) { + return MTPchat(new MTPDchannel(_flags, _id, _access_hash, _title, _username, _photo, _date, _version, _restiction_reason)); } inline MTPchat MTP_channelForbidden(MTPint _id, const MTPlong &_access_hash, const MTPstring &_title) { return MTPchat(new MTPDchannelForbidden(_id, _access_hash, _title)); diff --git a/Telegram/SourceFiles/mtproto/scheme.tl b/Telegram/SourceFiles/mtproto/scheme.tl index 265877516..59bcf2abf 100644 --- a/Telegram/SourceFiles/mtproto/scheme.tl +++ b/Telegram/SourceFiles/mtproto/scheme.tl @@ -204,7 +204,7 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation; userEmpty#200250ba id:int = User; -user#22e49072 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true explicit_content:flags.18?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int = User; +user#3289b590 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restiction_reason:flags.18?string = User; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto; @@ -219,7 +219,7 @@ userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#9ba2d800 id:int = Chat; chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = Chat; chatForbidden#7328bdb id:int title:string = Chat; -channel#678e9587 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true explicit_content:flags.9?true id:int access_hash:long title:string username:flags.6?string photo:ChatPhoto date:int version:int = Chat; +channel#e834ce68 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true id:int access_hash:long title:string username:flags.6?string photo:ChatPhoto date:int version:int restiction_reason:flags.9?string = Chat; channelForbidden#2d85832c id:int access_hash:long title:string = Chat; chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector = ChatFull; From 840ffa6482f94157df1de5cd9eb27c1252e83e31 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 17 Dec 2015 20:31:28 +0300 Subject: [PATCH 031/145] ffmpeg rendering inline animations --- MSVC.md | 4 +- Telegram/SourceFiles/audio.cpp | 24 +- Telegram/SourceFiles/config.h | 2 + Telegram/SourceFiles/gui/animation.cpp | 467 +++++++++++++++++++++---- Telegram/SourceFiles/gui/animation.h | 3 + Telegram/SourceFiles/gui/images.cpp | 28 +- Telegram/SourceFiles/history.cpp | 38 ++ Telegram/SourceFiles/history.h | 13 + Telegram/SourceFiles/stdafx.h | 10 + Telegram/SourceFiles/structs.cpp | 6 +- Telegram/SourceFiles/window.cpp | 2 +- Telegram/Telegram.vcxproj | 2 +- 12 files changed, 498 insertions(+), 101 deletions(-) diff --git a/MSVC.md b/MSVC.md index e3bdafcdf..899bc25b2 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-everything --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=gif --enable-decoder=h264 --enable-decoder=h264_crystalhd --enable-decoder=h264_qsv --enable-decoder=h264_vda --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_crystalhd --enable-decoder=mpeg4_vdpau --enable-decoder=msmpeg4_crystalhd --enable-decoder=msmpeg4_crystalhd --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-decoder=flac --enable-encoder=libopus --enable-demuxer=aac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=wav --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=mov --enable-demuxer=flac --enable-parser=aac --enable-parser=aac_latm --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-parser=flac --enable-muxer=ogg --enable-muxer=opus --enable-hwaccel=mpeg4_vaapi --enable-hwaccel=mpeg4_vdpau --enable-hwaccel=mpeg4_videotoolbox --enable-hwaccel=h264_d3d11va --enable-hwaccel=h264_dxva2 --enable-hwaccel=h264_mmal --enable-hwaccel=h264_qsv --enable-hwaccel=h264_vaapi --enable-hwaccel=h264_vda --enable-hwaccel=h264_vda_old --enable-hwaccel=h264_vdpau --enable-hwaccel=h264_videotoolbox --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/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/audio.cpp index 433e549d3..098d01ebb 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,16 @@ bool _checkALError() { Q_DECLARE_METATYPE(AudioMsgId); Q_DECLARE_METATYPE(SongMsgId); void audioInit() { - av_register_all(); - avcodec_register_all(); + if (!audioDevice) { + av_register_all(); + avcodec_register_all(); + } if (!capture) { capture = new AudioCapture(); cSetHasAudioCapture(capture->check()); } - uint64 ms = getms(); if (audioDevice) return; audioDevice = alcOpenDevice(0); @@ -223,7 +215,6 @@ void audioInit() { player = new AudioPlayer(); alcDevicePauseSOFT(audioDevice); - LOG(("Audio init time: %1").arg(getms() - ms)); cSetHasAudioPlayer(true); } @@ -1126,7 +1117,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 +1179,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) { diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 3427921c8..3d91019c8 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -87,6 +87,8 @@ enum { AverageGifSize = 320 * 240, WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it + AVBlockSize = 4096, // 4Kb for ffmpeg blocksize + SaveRecentEmojisTimeout = 3000, // 3 secs SaveWindowPositionTimeout = 1000, // 1 sec diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index aa7fee421..ac67e7a86 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -132,13 +132,12 @@ void AnimationManager::clipReinit(ClipReader *reader) { } void AnimationManager::clipRedraw(ClipReader *reader) { - if (reader->currentDisplayed()) { - return; - } - const GifItems &items(App::gifItems()); GifItems::const_iterator it = items.constFind(reader); if (it != items.cend()) { + if (reader->currentDisplayed()) { + return; + } Ui::redrawHistoryItem(it.value()); } } @@ -442,6 +441,386 @@ ClipReader::~ClipReader() { stop(); } +class ClipReaderImplementation { +public: + + ClipReaderImplementation(FileLocation *location, QByteArray *data) : _location(location), _data(data), _device(0) { + } + virtual bool readNextFrame(QImage &to) = 0; + virtual int32 nextFrameDelay() = 0; + virtual bool start() = 0; + virtual ~ClipReaderImplementation() { + } + +protected: + FileLocation *_location; + QByteArray *_data; + QFile _file; + QBuffer _buffer; + QIODevice *_device; + + void initDevice() { + if (_data->isEmpty()) { + if (_file.isOpen()) _file.close(); + _file.setFileName(_location->name()); + } else { + if (_buffer.isOpen()) _buffer.close(); + _buffer.setBuffer(_data); + } + _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(QImage &to) { + if (_framesLeft < 1 && !jumpToStart()) { + return false; + } + + _frameDelay = _reader->nextImageDelay(); + + QImage frame; // QGifHandler always reads first to internal QImage and returns it + if (!_reader->read(&frame)) { + return false; + } + --_framesLeft; + + 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(); + } + return true; + } + + int32 nextFrameDelay() { + return _frameDelay; + } + + bool start() { + return jumpToStart(); + } + + ~QtGifReaderImplementation() { + delete _reader; + setBadPointer(_reader); + } + +private: + QImageReader *_reader; + int32 _framesLeft, _frameDelay; + + bool jumpToStart() { + if (_reader && _reader->jumpToImage(0)) { + _framesLeft = _reader->imageCount(); + return true; + } + + delete _reader; + initDevice(); + _reader = new QImageReader(_device); + 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) + , _packetSize(0) + , _packetData(0) + , _packetWas(false) + , _width(0) + , _height(0) + , _swsContext(0) + , _nextFrameDelay(0) + , _currentFrameDelay(0) + , _frameMs(0) { + _frame = av_frame_alloc(); + av_init_packet(&_avpkt); + _avpkt.data = NULL; + _avpkt.size = 0; + } + + bool readNextFrame(QImage &to) { + 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; + } + + return false; + } + if (res > 0) decoded = res; + } + if (!finished) { + _avpkt.data += decoded; + _avpkt.size -= decoded; + if (_avpkt.size <= 0) freePacket(); + } + + if (got_frame) { + _hadFrame = true; + + if (!_width || !_height) { + _width = _frame->width; + _height = _frame->height; + if (!_width || !_height) { + LOG(("Gif Error: Bad frame size %1").arg(logData())); + return false; + } + } + + if (to.isNull() || to.width() != _width || to.height() != _height) { + to = QImage(_width, _height, QImage::Format_ARGB32); + } + if (_frame->width == _width && _frame->height == _height && (_frame->format == AV_PIX_FMT_BGRA || (_frame->format == -1 && _codecContext->pix_fmt == AV_PIX_FMT_BGRA))) { + 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 (_frame->width != _width || _frame->height != _height || (_frame->format != -1 && _frame->format != _codecContext->pix_fmt) || !_swsContext) { + _swsContext = sws_getCachedContext(_swsContext, _frame->width, _frame->height, AVPixelFormat(_frame->format), _width, _height, AV_PIX_FMT_BGRA, 0, 0, 0, 0); + } + uint8_t * toData[1] = { to.bits() }; + int toLinesize[1] = { to.bytesPerLine() }; + if ((res = sws_scale(_swsContext, _frame->data, _frame->linesize, 0, _frame->height, toData, toLinesize)) != _height) { + LOG(("Gif Error: Unable to sws_scale to good size %1, hieght %2, should be %3").arg(logData()).arg(res).arg(_height)); + return false; + } + } + + int64 duration = av_frame_get_pkt_duration(_frame); + if (duration == AV_NOPTS_VALUE) { + 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; + if (frameMs > _frameMs) { + _currentFrameDelay = int32(frameMs - _frameMs); + _frameMs = frameMs; + } else { + _currentFrameDelay = 0; + } + _nextFrameDelay = _currentFrameDelay; + } else { + _currentFrameDelay = _nextFrameDelay; + _nextFrameDelay = (duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; + _frameMs += _nextFrameDelay; + } + + av_frame_unref(_frame); + 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; + } + + 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() { + 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); + 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; + } + + ~FFMpegReaderImplementation() { + 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; + AVSampleFormat _inputFormat; + AVFrame *_frame; + bool _opened, _hadFrame; + + 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_free_packet(&_avpkt); + } + } + + int32 _width, _height; + SwsContext *_swsContext; + + 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: @@ -450,13 +829,11 @@ public: , _data(data) , _location(_data.isEmpty() ? new FileLocation(location) : 0) , _accessed(false) - , _buffer(_data.isEmpty() ? 0 : &_data) - , _reader(0) + , _implementation(0) , _previousMs(0) , _currentMs(0) , _nextUpdateMs(0) , _paused(false) { - if (_data.isEmpty() && !_location->accessEnable()) { error(); return; @@ -466,14 +843,13 @@ public: ClipProcessResult start(uint64 ms) { _nextUpdateMs = ms + 86400 * 1000ULL; - if (!_reader && !restartReader(true)) { + if (!_implementation && !init()) { return error(); } if (_currentOriginal.isNull()) { - if (!readNextFrame(_currentOriginal)) { + if (!_implementation->readNextFrame(_currentOriginal)) { return error(); } - --_framesLeft; return ClipProcessReinit; } return ClipProcessWait; @@ -517,7 +893,7 @@ public: } uint64 nextFrameDelay() { - int delay = _reader->nextImageDelay(); + int32 delay = _implementation->nextFrameDelay(); return qMax(delay, 5); } @@ -529,75 +905,31 @@ public: qSwap(_currentCache, _nextCache); } - bool readNextFrame(QImage &to) { - QImage frame; // QGifHandler always reads first to internal QImage and returns it - if (!_reader->read(&frame)) { - return false; - } - 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(); - } - return true; - } - bool prepareNextFrame() { - _nextUpdateMs = _currentMs + nextFrameDelay(); - if (!_framesLeft) { - if (_reader->jumpToImage(0)) { - _framesLeft = _reader->imageCount(); - } else if (!restartReader()) { - return false; - } - } - if (!readNextFrame(_nextOriginal)) { + if (!_implementation->readNextFrame(_nextOriginal)) { return false; } + _nextUpdateMs = _currentMs + nextFrameDelay(); _nextOriginal.setDevicePixelRatio(_request.factor); - --_framesLeft; _next = QPixmap(); _next = _prepareFrame(_request, _nextOriginal, _nextCache, true); return true; } - bool restartReader(bool first = false) { - if (first && _data.isEmpty() && QFileInfo(_location->name()).size() <= AnimationInMemory) { + 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) { - _buffer.setBuffer(&_data); - } else { + if (f.error() != QFile::NoError) { _data = QByteArray(); } } - } else if (!_data.isEmpty()) { - _buffer.close(); } - delete _reader; - if (_data.isEmpty()) { - _reader = new QImageReader(_location->name()); - } else { - _reader = new QImageReader(&_buffer); - } - if (!_reader->canRead() || !_reader->supportsAnimation()) { - return false; - } - _framesLeft = _reader->imageCount(); - if (_framesLeft < 1) { - return false; - } - return true; + _implementation = new FFMpegReaderImplementation(_location, &_data); +// _implementation = new QtGifReaderImplementation(_location, &_data); + return _implementation->start(); } ClipProcessResult error() { @@ -607,8 +939,8 @@ public: } void stop() { - delete _reader; - _reader = 0; + delete _implementation; + _implementation = 0; if (_location) { if (_accessed) { @@ -623,7 +955,7 @@ public: ~ClipReaderPrivate() { stop(); setBadPointer(_location); - setBadPointer(_reader); + setBadPointer(_implementation); } private: @@ -636,13 +968,12 @@ private: bool _accessed; QBuffer _buffer; - QImageReader *_reader; + ClipReaderImplementation *_implementation; ClipFrameRequest _request; QPixmap _current, _next; QImage _currentOriginal, _nextOriginal, _currentCache, _nextCache; - int32 _framesLeft; uint64 _previousMs, _currentMs, _nextUpdateMs; bool _paused; diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index e341d54fd..dd4820964 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -230,6 +230,9 @@ public: } private: + Animation(const Animation &); + Animation &operator=(const Animation &); + AnimationCallbacks *_cb; bool _animating; diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index 6b9b74756..1ba8440fe 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -403,27 +403,35 @@ QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool roun const QPixmap &p(pixData()); if (p.isNull()) return blank()->pix(); + bool n = isNull(); QImage img = p.toImage(); - if (blurred) img = imageBlur(img); - if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) { - } else if (h <= 0) { - img = img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation); - } else { - img = img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation); + if (!n || !(outerw > 0 && outerh > 0)) { + if (blurred) img = imageBlur(img); + if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) { + } else if (h <= 0) { + img = img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation); + } else { + img = img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation); + } } if (outerw > 0 && outerh > 0) { outerw *= cIntRetinaFactor(); outerh *= cIntRetinaFactor(); - if (outerw != w || outerh != h) { + if (outerw != w || outerh != h || n) { img.setDevicePixelRatio(cRetinaFactor()); QImage result(outerw, outerh, QImage::Format_ARGB32_Premultiplied); result.setDevicePixelRatio(cRetinaFactor()); - { + if (n) { QPainter p(&result); - if (w < outerw || h < outerh) { + p.fillRect(0, 0, result.width(), result.height(), st::black->b); + } else { + QPainter p(&result); + if (w < outerw || h < outerh || n) { p.fillRect(0, 0, result.width(), result.height(), st::black->b); } - p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img); + if (!n) { + p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img); + } } img = result; } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 4b3ca1d27..56290d231 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3614,6 +3614,15 @@ HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, Histo _data->thumb->load(); } +HistoryVideo::HistoryVideo(const HistoryVideo &other) : HistoryFileMedia() +, _data(other._data) +, _caption(other._caption) +, _thumbw(other._thumbw) { + setLinks(new VideoOpenLink(_data), new VideoSaveLink(_data), new VideoCancelLink(_data)); + + setStatusSize(other._statusSize); +} + void HistoryVideo::initDimensions(const HistoryItem *parent) { bool bubble = parent->hasBubble(); @@ -3923,6 +3932,13 @@ HistoryAudio::HistoryAudio(const MTPDaudio &audio) : HistoryFileMedia() setStatusSize(FileStatusSizeReady); } +HistoryAudio::HistoryAudio(const HistoryAudio &other) : HistoryFileMedia() +, _data(other._data) { + setLinks(new AudioOpenLink(_data), new AudioSaveLink(_data), new AudioCancelLink(_data)); + + setStatusSize(other._statusSize); +} + void HistoryAudio::setStatusSize(int32 newSize, qint64 realDuration) const { HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration, realDuration); } @@ -4155,6 +4171,18 @@ HistoryDocument::HistoryDocument(DocumentData *document) : HistoryFileMedia() } } +HistoryDocument::HistoryDocument(const HistoryDocument &other) : HistoryFileMedia() +, _data(other._data) +, _linksavel(new DocumentSaveLink(_data)) +, _linkcancell(new DocumentCancelLink(_data)) +, _namew(other._namew) +, _name(other._name) +, _thumbw(other._thumbw) { + setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); + + setStatusSize(other._statusSize); +} + void HistoryDocument::setStatusSize(int32 newSize, qint64 realDuration) const { HistoryFileMedia::setStatusSize(newSize, _data->size, _data->song() ? _data->song()->duration : -1, realDuration); @@ -4556,6 +4584,16 @@ HistoryGif::HistoryGif(DocumentData *document) : HistoryFileMedia() _data->thumb->load(); } +HistoryGif::HistoryGif(const HistoryGif &other) : HistoryFileMedia() +, _data(other._data) +, _thumbw(other._thumbw) +, _thumbh(other._thumbh) +, _gif(0) { + setLinks(new DocumentOpenLink(_data), new DocumentOpenLink(_data), new DocumentCancelLink(_data)); + + setStatusSize(other._statusSize); +} + void HistoryGif::initDimensions(const HistoryItem *parent) { bool bubble = parent->hasBubble(); int32 tw = 0, th = 0; diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 790dff6d3..414bf0ae7 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -784,6 +784,7 @@ public: protected: mutable int32 _height, _maxw, _minh; + HistoryElem &operator=(const HistoryElem &); }; @@ -1350,12 +1351,18 @@ protected: }; mutable AnimationData *_animation; +private: + + HistoryFileMedia(const HistoryFileMedia &other); + }; class HistoryVideo : public HistoryFileMedia { public: HistoryVideo(const MTPDvideo &video, const QString &caption, HistoryItem *parent); + HistoryVideo(const HistoryVideo &other); + void initDimensions(const HistoryItem *parent); void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; @@ -1420,6 +1427,8 @@ class HistoryAudio : public HistoryFileMedia { public: HistoryAudio(const MTPDaudio &audio); + HistoryAudio(const HistoryAudio &other); + void initDimensions(const HistoryItem *parent); void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; @@ -1475,6 +1484,8 @@ class HistoryDocument : public HistoryFileMedia { public: HistoryDocument(DocumentData *document); + HistoryDocument(const HistoryDocument &other); + void initDimensions(const HistoryItem *parent); bool withThumb() const { @@ -1556,6 +1567,8 @@ class HistoryGif : public HistoryFileMedia { public: HistoryGif(DocumentData *document); + HistoryGif(const HistoryGif &other); + void initDimensions(const HistoryItem *parent); void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index 6e65c5757..104079dc6 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -51,6 +51,16 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #define _NEED_LINUX_GENERATE_DUMP #endif +extern "C" { + +#include +#include +#include +#include +#include + +} + #include "types.h" #include "config.h" diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 6dda51a59..e906bb91b 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -1008,7 +1008,7 @@ void DocumentData::setattributes(const QVector &attributes const MTPDdocumentAttributeImageSize &d(attributes[i].c_documentAttributeImageSize()); dimensions = QSize(d.vw.v, d.vh.v); } break; - case mtpc_documentAttributeAnimated: if (type == FileDocument || type == StickerDocument) { + case mtpc_documentAttributeAnimated: if (type == FileDocument || type == StickerDocument || type == VideoDocument) { type = AnimatedDocument; delete _additional; _additional = 0; @@ -1025,7 +1025,9 @@ void DocumentData::setattributes(const QVector &attributes } break; case mtpc_documentAttributeVideo: { const MTPDdocumentAttributeVideo &d(attributes[i].c_documentAttributeVideo()); - type = VideoDocument; + if (type == FileDocument) { + type = VideoDocument; + } // duration = d.vduration.v; dimensions = QSize(d.vw.v, d.vh.v); } break; diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index bdbbb9791..3f7f61032 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -641,7 +641,7 @@ void Window::sendServiceHistoryRequest() { UserData *user = App::userLoaded(ServiceUserId); if (!user) { int32 userFlags = MTPDuser::flag_first_name | MTPDuser::flag_phone | MTPDuser::flag_status | MTPDuser::flag_verified; - user = App::feedUsers(MTP_vector(1, MTP_user(MTP_int(userFlags), MTP_int(ServiceUserId), MTPlong(), MTP_string("Telegram"), MTPstring(), MTPstring(), MTP_string("42777"), MTP_userProfilePhotoEmpty(), MTP_userStatusRecently(), MTPint()))); + user = App::feedUsers(MTP_vector(1, MTP_user(MTP_int(userFlags), MTP_int(ServiceUserId), MTPlong(), MTP_string("Telegram"), MTPstring(), MTPstring(), MTP_string("42777"), MTP_userProfilePhotoEmpty(), MTP_userStatusRecently(), MTPint(), MTPstring()))); } _serviceHistoryRequest = MTP::send(MTPmessages_GetHistory(user->input, MTP_int(0), MTP_int(0), MTP_int(1), MTP_int(0), MTP_int(0)), main->rpcDone(&MainWidget::serviceHistoryDone), main->rpcFail(&MainWidget::serviceHistoryFail)); } diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index fd4bf6a95..009ee8e90 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -83,7 +83,7 @@ Windows $(OutDir)$(ProjectName).exe .\..\..\Libraries\lzma\C\Util\LzmaLib\Debug;.\..\..\Libraries\libexif-0.6.20\win32\Debug;.\..\..\Libraries\ffmpeg;.\..\..\Libraries\opus\win32\VS2010\Win32\Debug;.\..\..\Libraries\openal-soft\build\Debug;.\..\..\Libraries\zlib-1.2.8\contrib\vstudio\vc11\x86\ZlibStatDebug;.\..\..\Libraries\openssl_debug\Debug\lib;$(QTDIR)\lib;$(QTDIR)\plugins;%(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;Shlwapi.lib;Gdiplus.lib;imm32.lib;winmm.lib;qtmaind.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Cored.lib;Qt5Guid.lib;qtharfbuzzngd.lib;qtpcred.lib;qtfreetyped.lib;Qt5Widgetsd.lib;Qt5Networkd.lib;Qt5PlatformSupportd.lib;platforms\qwindowsd.lib;imageformats\qwebpd.lib;libeay32.lib;ssleay32.lib;Crypt32.lib;zlibstat.lib;LzmaLib.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;OpenAL32.lib;common.lib;libavformat\libavformat.a;libavcodec\libavcodec.a;libavutil\libavutil.a;libswresample\libswresample.a;opus.lib;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;Shlwapi.lib;Gdiplus.lib;imm32.lib;winmm.lib;qtmaind.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Cored.lib;Qt5Guid.lib;qtharfbuzzngd.lib;qtpcred.lib;qtfreetyped.lib;Qt5Widgetsd.lib;Qt5Networkd.lib;Qt5PlatformSupportd.lib;platforms\qwindowsd.lib;imageformats\qwebpd.lib;libeay32.lib;ssleay32.lib;Crypt32.lib;zlibstat.lib;LzmaLib.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;OpenAL32.lib;common.lib;libavformat\libavformat.a;libavcodec\libavcodec.a;libavutil\libavutil.a;libswresample\libswresample.a;libswscale\libswscale.a;opus.lib;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies) true LIBCMT From 11da39b72d6b7e1c1b113a317f073bc81a641bf2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 19 Dec 2015 00:36:16 +0300 Subject: [PATCH 032/145] webpage attachs redesigned --- Telegram/SourceFiles/app.cpp | 4 +- Telegram/SourceFiles/gui/animation.cpp | 22 +- Telegram/SourceFiles/history.cpp | 853 +++++++++++-------------- Telegram/SourceFiles/history.h | 67 +- Telegram/SourceFiles/historywidget.cpp | 30 +- Telegram/SourceFiles/mainwidget.cpp | 4 +- Telegram/SourceFiles/mediaview.cpp | 14 +- Telegram/SourceFiles/playerwidget.cpp | 13 +- Telegram/SourceFiles/structs.cpp | 4 +- Telegram/SourceFiles/structs.h | 3 + 10 files changed, 452 insertions(+), 562 deletions(-) diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 2672bd90c..d814189e9 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2444,8 +2444,8 @@ namespace App { void stopGifItems() { if (!::gifItems.isEmpty()) { if (HistoryItem *playing = ::gifItems.begin().value()) { - if (playing->getMedia() && playing->getMedia()->type() == MediaTypeGif) { - static_cast(playing->getMedia())->stop(playing); + if (playing->getMedia()) { + playing->getMedia()->stopInline(playing); } } } diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index ac67e7a86..8a58af534 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -482,12 +482,11 @@ public: } bool readNextFrame(QImage &to) { + if (_reader) _frameDelay = _reader->nextImageDelay(); if (_framesLeft < 1 && !jumpToStart()) { return false; } - _frameDelay = _reader->nextImageDelay(); - QImage frame; // QGifHandler always reads first to internal QImage and returns it if (!_reader->read(&frame)) { return false; @@ -655,20 +654,17 @@ public: } int64 duration = av_frame_get_pkt_duration(_frame); + int64 framePts = (_frame->pkt_pts == AV_NOPTS_VALUE) ? _frame->pkt_dts : _frame->pkt_pts; + int64 frameMs = (framePts * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; + _currentFrameDelay = _nextFrameDelay; + if (_frameMs + _currentFrameDelay < frameMs) { + _currentFrameDelay = int32(frameMs - _frameMs); + } + _frameMs = frameMs; if (duration == AV_NOPTS_VALUE) { - 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; - if (frameMs > _frameMs) { - _currentFrameDelay = int32(frameMs - _frameMs); - _frameMs = frameMs; - } else { - _currentFrameDelay = 0; - } - _nextFrameDelay = _currentFrameDelay; + _nextFrameDelay = 0; } else { - _currentFrameDelay = _nextFrameDelay; _nextFrameDelay = (duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; - _frameMs += _nextFrameDelay; } av_frame_unref(_frame); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 56290d231..6dcf3fcc2 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -1759,7 +1759,7 @@ HistoryItem *History::addNewItem(HistoryBlock *to, bool newBlock, HistoryItem *a HistoryMediaType mt = media->type(); MediaOverviewType t = mediaToOverviewType(mt); if (t != OverviewCount) { - if (mt == MediaTypeDocument && static_cast(media)->document()->song()) { + if (mt == MediaTypeDocument && static_cast(media)->getDocument()->song()) { addToOverview(adding, OverviewAudioDocuments); } else { addToOverview(adding, t); @@ -2011,7 +2011,7 @@ void History::addOlderSlice(const QVector &slice, const QVectortype(); MediaOverviewType t = mediaToOverviewType(mt); if (t != OverviewCount) { - if (mt == MediaTypeDocument && static_cast(media)->document()->song()) { + if (mt == MediaTypeDocument && static_cast(media)->getDocument()->song()) { if (addToOverviewFront(item, OverviewAudioDocuments)) mask |= (1 << OverviewAudioDocuments); } else { if (addToOverviewFront(item, t)) mask |= (1 << t); @@ -2185,7 +2185,7 @@ void History::addNewerSlice(const QVector &slice, const QVectortype(); MediaOverviewType t = mediaToOverviewType(mt); if (t != OverviewCount) { - if (mt == MediaTypeDocument && static_cast(media)->document()->song()) { + if (mt == MediaTypeDocument && static_cast(media)->getDocument()->song()) { t = OverviewAudioDocuments; if (overviewCountData[t] != 0) { overview[t].push_back(item->id); @@ -3015,7 +3015,7 @@ void HistoryItem::destroy() { HistoryMedia *m = getMedia(true); MediaOverviewType t = m ? mediaToOverviewType(m->type()) : OverviewCount; if (t != OverviewCount) { - if (m->type() == MediaTypeDocument && static_cast(m)->document()->song()) { + if (m->type() == MediaTypeDocument && static_cast(m)->getDocument()->song()) { history()->eraseFromOverview(OverviewAudioDocuments, id); } else { history()->eraseFromOverview(t, id); @@ -3167,6 +3167,14 @@ HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, Histo init(); } +HistoryPhoto::HistoryPhoto(PhotoData *photo) : HistoryMedia() +, _data(photo) +, _openl(new PhotoLink(_data)) +, _pixw(1) +, _pixh(1) { + init(); +} + HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryMedia() , _data(App::feedPhoto(photo)) , _openl(new PhotoLink(_data, chat)) @@ -3302,7 +3310,7 @@ void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x } if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { lnk = _openl; - if (_caption.isEmpty()) { + if (_caption.isEmpty() && parent->getMedia() == this) { int32 fullRight = skipx + width, fullBottom = skipy + height; bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage); if (inDate) { @@ -3409,8 +3417,10 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b // date if (_caption.isEmpty()) { - int32 fullRight = skipx + width, fullBottom = skipy + height; - parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); + 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); @@ -3763,7 +3773,7 @@ void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x } if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; - if (_caption.isEmpty()) { + if (_caption.isEmpty() && parent->getMedia() == this) { int32 fullRight = skipx + width, fullBottom = skipy + height; bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage); if (inDate) { @@ -3863,8 +3873,10 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b // date if (_caption.isEmpty()) { - int32 fullRight = skipx + width, fullBottom = skipy + height; - parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); + 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); @@ -4658,7 +4670,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo } updateStatusText(parent); } - bool radial = !animating && isRadialAnimation(ms); + bool radial = isRadialAnimation(ms); if (bubble) { skipx = st::mediaPadding.left(); @@ -4682,7 +4694,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - if (!animating) { + if (!animating || radial) { 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) { @@ -4706,9 +4718,9 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo } style::sprite icon; - if (!_data->already().isEmpty()) { + if (!_data->already().isEmpty() && !radial) { icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); - } else if (_data->loader) { + } else if (_data->loader || radial) { icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); } else { icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); @@ -4719,17 +4731,21 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo _animation->radial.draw(p, rinner, 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, w), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); - p.setFont(st::normalFont); - p.setPen(st::white); - p.drawTextLeft(statusX, statusY, w, _statusText, statusW - 2 * st::msgDateImgPadding.x()); + if (!animating) { + 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, w), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + p.setFont(st::normalFont); + p.setPen(st::white); + p.drawTextLeft(statusX, statusY, w, _statusText, statusW - 2 * st::msgDateImgPadding.x()); - // date - int32 fullRight = skipx + width, fullBottom = skipy + height; - parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); + // date + if (parent->getMedia() == this) { + int32 fullRight = skipx + width, fullBottom = skipy + height; + parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); + } + } } } @@ -4904,10 +4920,12 @@ void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _savel; } - int32 fullRight = skipx + width, fullBottom = skipy + height; - bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage); - if (inDate) { - state = HistoryInDateCursorState; + 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; } @@ -4921,16 +4939,17 @@ ImagePtr HistoryGif::replyPreview() { return _data->makeReplyPreview(); } -void HistoryGif::play(HistoryItem *parent) { +bool HistoryGif::playInline(HistoryItem *parent) { if (_gif) { - stop(parent); + stopInline(parent); } else { _gif = new ClipReader(_data->location(), _data->data); App::regGifItem(_gif, parent); } + return true; } -void HistoryGif::stop(HistoryItem *parent) { +void HistoryGif::stopInline(HistoryItem *parent) { App::unregGifItem(_gif); delete _gif; _gif = 0; @@ -5026,15 +5045,17 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, } } - parent->drawInfo(p, usex + usew, _height, usex * 2 + usew, 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; - 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); + } } } @@ -5101,9 +5122,11 @@ void HistorySticker::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 return; } } - bool inDate = parent->pointInTime(usex + usew, _height, x, y, InfoDisplayOverImage); - if (inDate) { - state = HistoryInDateCursorState; + if (parent->getMedia() == this) { + bool inDate = parent->pointInTime(usex + usew, _height, x, y, InfoDisplayOverImage); + if (inDate) { + state = HistoryInDateCursorState; + } } } @@ -5308,292 +5331,278 @@ void HistoryContact::updateFrom(const MTPMessageMedia &media, HistoryItem *paren } } +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); } else { - _docThumbWidth = st::mediaThumbSize; + _attach = new HistoryDocument(_data->doc); } + } else if (_data->photo) { + _attach = new HistoryPhoto(_data->photo); } - _docName = documentName(data->doc); - _docSize = data->doc->song() ? formatDurationAndSizeText(data->doc->song()->duration, data->doc->size) : formatSizeText(data->doc->size); - _docNameWidth = st::normalFont->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::msgMaxWidth); - _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); } + _maxw += st::msgPadding.left() + st::webPageLeft + st::msgPadding.right(); + _minh += st::msgPadding.bottom(); + if (_asArticle) { + _minh += st::msgDateFont->height; + } + w = _maxw; _height = _minh; } +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::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; int32 width = w, height = _height, skipx = 0, skipy = 0; - 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 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()); - } - } 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()); - } - } - 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 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.fillRect(0, 0, st::webPageBar, _height - bottomSkip, bar->b); - p.save(); - p.translate(st::webPageLeft, 0); - - width -= st::webPageLeft; + 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; + } + + QRect bar(rtlrect(st::msgPadding.left(), 0, st::webPageBar, _height - bshift, w)); + 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)); if (full) { - pix = data->photo->medium->pixSingle(_pixw, _pixh, pixwidth, pixheight); + pix = _data->photo->medium->pixSingle(_pixw, articleThumbHeight(_data->photo, _pixw), pw, _pixh); } else { - pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh, pixwidth, pixheight); + pix = _data->photo->thumb->pixBlurredSingle(_pixw, articleThumbHeight(_data->photo, _pixw), pw, _pixh); } - p.drawPixmap(width - pixwidth, 0, pix); + p.drawPixmapLeft(lshift + width - pw, 0, w, pix); if (selected) { - App::roundRect(p, width - pixwidth, 0, pixwidth, pixheight, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + App::roundRect(p, rtlrect(lshift + width - pw, 0, pw, _pixh, w), 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, w, (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, w, _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, w, _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, ms); - int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); + p.save(); + p.translate(lshift - bubble.left(), tshift - bubble.top()); - 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; - - 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); - } - } - - if (selected) { - App::roundRect(p, 0, 0, pixwidth, pixheight, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); - } - - if (data->type == WebPageVideo) { - if (data->siteName == qstr("YouTube")) { + _attach->draw(p, parent, r.translated(bubble.left() - lshift, bubble.top() - tshift), selected, ms); + int32 pixwidth = _attach->currentWidth(), pixheight = _attach->height(); + + 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); @@ -5608,212 +5617,111 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, const QRect &r, 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.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 == FileDownloadFailed || data->doc->status == FileUploadFailed) { - 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 == FileDownloadFailed || data->doc->status == FileUploadFailed) { - 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::mediaMusicOutImg : st::mediaMusicInImg)); - } - } - 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::normalFont->f); - p.setPen(st::black->c); - if (twidth < _docNameWidth) { - p.drawText(tleft, st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(_docName, twidth)); - } else { - p.drawText(tleft, st::mediaNameTop + st::normalFont->ascent, _docName); - } - - style::color status(outbg ? (selected ? st::mediaOutFgSelected : st::mediaOutFg) : (selected ? st::mediaInFgSelected : st::mediaInFg)); - p.setPen(status->p); - - p.drawText(tleft, st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->descent, statusText); - } + p.restore(); } - - p.restore(); } int32 HistoryWebPage::resize(int32 width, const HistoryItem *parent) { - if (data->pendingTill) { + if (_data->pendingTill) { w = width; _height = _minh; return _height; } w = width; - width -= st::webPageLeft; + width -= st::msgPadding.left() + st::webPageLeft + st::msgPadding.right(); + + int32 linesMax = 5; + int32 siteNameLines = _siteNameWidth ? 1 : 0, siteNameHeight = _siteNameWidth ? _lineHeight : 0; 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; - } - } - _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())); + _pixh = linesMax * _lineHeight; + do { + _pixw = articleThumbWidth(_data->photo, _pixh); + int32 wleft = width - st::webPagePhotoDelta - qMax(_pixw, int16(_lineHeight)); - 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 = siteNameHeight; + + if (_title.isEmpty()) { + _titleLines = 0; + } else { + if (_title.countHeight(wleft) < 2 * st::webPageTitleFont->height) { + _titleLines = 1; + } else { + _titleLines = 2; + } + _height += _titleLines * _lineHeight; } - _height = animated.h / cIntRetinaFactor(); - if (animated.w / cIntRetinaFactor() > w) { - _height = (w * _height / (animated.w / cIntRetinaFactor())); - if (_height <= 0) _height = 1; + + int32 descriptionHeight = _description.countHeight(wleft); + if (descriptionHeight < (linesMax - siteNameLines - _titleLines) * st::webPageDescriptionFont->height) { + _descriptionLines = (descriptionHeight / st::webPageDescriptionFont->height); + } else { + _descriptionLines = (linesMax - siteNameLines - _titleLines); } - _height += st::webPagePhotoSkip; - if (w + parent->skipBlockWidth() > width) { - _height += (st::msgDateFont->height - st::msgDateDelta.y()); + _height += _descriptionLines * _lineHeight; + + if (_height >= _pixh) { + break; } - } else { - _height = st::mediaThumbSize; - } + + _pixh -= _lineHeight; + } while (_pixh > _lineHeight); + _height += st::msgDateFont->height; } else { - _height = 0; - } + _height = siteNameHeight; - if (!_asArticle) { - if (!data->siteName.isEmpty()) { - _height += st::webPageTitleFont->height; + if (_title.isEmpty()) { + _titleLines = 0; + } else { + if (_title.countHeight(width) < 2 * st::webPageTitleFont->height) { + _titleLines = 1; + } else { + _titleLines = 2; + } + _height += _titleLines * _lineHeight; } - if (!_title.isEmpty()) { - _height += qMin(_title.countHeight(width), st::webPageTitleFont->height * 2); + + 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 (!_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 (_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::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 { @@ -5833,72 +5741,45 @@ bool HistoryWebPage::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 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; + if (width < st::msgPadding.left() + st::msgPadding.right() + 1) return; + int32 height = _height, skipx = 0, skipy = 0; - width -= st::webPageLeft; - x -= st::webPageLeft; + 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; + } if (_asArticle) { - int32 pixwidth = st::webPagePhotoSize, pixheight = st::webPagePhotoSize; - if (x >= width - pixwidth && x < width && y >= 0 && y < pixheight) { + int32 pw = qMax(_pixw, int16(_lineHeight)); + if (rtlrect(lshift + width - pw, 0, pw, _pixh, w).contains(x, y)) { lnk = _openl; return; } + width -= pw + st::webPagePhotoDelta; } - int32 articleLines = 5; + int32 tshift = 0; if (_siteNameWidth) { - y -= st::webPageTitleFont->height; - --articleLines; + tshift += _lineHeight; } - 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 (_titleLines) { + tshift += _titleLines * _lineHeight; } - 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) { + if (_descriptionLines) { + if (y >= tshift && y < tshift + _descriptionLines * _lineHeight) { bool inText = false; - _description.getState(lnk, inText, x, y, availw); + _description.getStateLeft(lnk, inText, x - lshift, y - tshift, width, w); state = inText ? HistoryInTextCursorState : HistoryDefaultCursorState; return; } - y -= desch; + tshift += _descriptionLines * _lineHeight; } - 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; - } + if (_attach) { + if (tshift) tshift += st::webPagePhotoSkip; + + if (x >= lshift && x < lshift + width && y >= tshift && y < _height - st::msgPadding.bottom()) { + _attach->getState(lnk, state, x - lshift + bubble.left(), y - tshift + bubble.top(), parent); } } } @@ -5908,7 +5789,7 @@ HistoryMedia *HistoryWebPage::clone() const { } ImagePtr HistoryWebPage::replyPreview() { - return data->photo ? data->photo->makeReplyPreview() : (data->doc ? data->doc->makeReplyPreview() : ImagePtr()); + return _data->photo ? _data->photo->makeReplyPreview() : (_data->doc ? _data->doc->makeReplyPreview() : ImagePtr()); } namespace { @@ -6446,8 +6327,10 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, const QRect & 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, skipx * 2 + width, 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) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 414bf0ae7..71c456b6d 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1176,6 +1176,16 @@ public: } virtual HistoryMedia *clone() const = 0; + virtual DocumentData *getDocument() { + return 0; + } + + virtual bool playInline(HistoryItem *item) { + return false; + } + virtual void stopInline(HistoryItem *item) { + } + virtual void regItem(HistoryItem *item) { } @@ -1204,6 +1214,9 @@ public: } 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; } @@ -1225,6 +1238,7 @@ class HistoryPhoto : public HistoryMedia { public: HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent); + HistoryPhoto(PhotoData *photo); HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0); void init(); @@ -1459,6 +1473,9 @@ public: bool customInfoLayout() const { return false; } + QMargins bubbleMargins() const { + return st::msgPadding; + } protected: @@ -1507,7 +1524,7 @@ public: void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; HistoryMedia *clone() const; - DocumentData *document() { + DocumentData *getDocument() { return _data; } @@ -1530,6 +1547,9 @@ public: 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(); } @@ -1586,9 +1606,11 @@ public: void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; HistoryMedia *clone() const; - DocumentData *document() { + DocumentData *getDocument() { return _data; } + bool playInline(HistoryItem *item); + void stopInline(HistoryItem *item); void regItem(HistoryItem *item); void unregItem(HistoryItem *item); @@ -1613,9 +1635,6 @@ public: return true; } - void play(HistoryItem *parent); - void stop(HistoryItem *parent); - ~HistoryGif(); protected: @@ -1760,11 +1779,15 @@ class HistoryWebPage : public HistoryMedia { public: HistoryWebPage(WebPageData *data); + HistoryWebPage(const HistoryWebPage &other); void initDimensions(const HistoryItem *parent); + void linkOver(HistoryItem *parent, const TextLinkPtr &lnk); + void linkOut(HistoryItem *parent, const TextLinkPtr &lnk); + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; bool isDisplayed() const { - return !data->pendingTill; + return !_data->pendingTill; } int32 resize(int32 width, const HistoryItem *parent); HistoryMediaType type() const { @@ -1776,21 +1799,32 @@ public: void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; HistoryMedia *clone() const; + DocumentData *getDocument() { + return _attach ? _attach->getDocument() : 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(); + return _attach ? _attach->animating() : false; } WebPageData *webpage() { - return data; + return _data; } bool needsBubble(const HistoryItem *parent) const { @@ -1801,17 +1835,18 @@ public: } 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; }; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 435c08501..859de6b4f 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -5363,14 +5363,7 @@ void HistoryWidget::onDocumentUploaded(const FullMsgId &newId, const MTPInputFil if (!MTP::authedId()) return; HistoryMessage *item = dynamic_cast(App::histItemById(newId)); if (item) { - DocumentData *document = 0; - if (HistoryDocument *media = dynamic_cast(item->getMedia())) { - document = media->document(); - } else if (HistoryGif *media = dynamic_cast(item->getMedia())) { - document = media->document(); - } else if (HistorySticker *media = dynamic_cast(item->getMedia())) { - document = media->document(); - } + DocumentData *document = item->getMedia() ? item->getMedia()->getDocument() : 0; if (document) { uint64 randomId = MTP::nonce(); App::historyRegRandom(randomId, newId); @@ -5394,14 +5387,7 @@ void HistoryWidget::onThumbDocumentUploaded(const FullMsgId &newId, const MTPInp if (!MTP::authedId()) return; HistoryMessage *item = dynamic_cast(App::histItemById(newId)); if (item) { - DocumentData *document = 0; - if (HistoryDocument *media = dynamic_cast(item->getMedia())) { - document = media->document(); - } else if (HistoryGif *media = dynamic_cast(item->getMedia())) { - document = media->document(); - } else if (HistorySticker *media = dynamic_cast(item->getMedia())) { - document = media->document(); - } + DocumentData *document = item->getMedia() ? item->getMedia()->getDocument() : 0; if (document) { uint64 randomId = MTP::nonce(); App::historyRegRandom(randomId, newId); @@ -5463,17 +5449,7 @@ void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { if (!MTP::authedId()) return; if (HistoryItem *item = App::histItemById(newId)) { HistoryMedia *media = item->getMedia(); - DocumentData *doc = 0; - if (media) { - HistoryMediaType type = media->type(); - if (type == MediaTypeDocument) { - doc = static_cast(item->getMedia())->document(); - } else if (type == MediaTypeGif) { - doc = static_cast(item->getMedia())->document(); - } else if (type == MediaTypeSticker) { - doc = static_cast(item->getMedia())->document(); - } - } + DocumentData *doc = media ? media->getDocument() : 0; if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadFile, doc ? doc->uploadOffset : 0); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 25b89a349..a69741405 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1883,9 +1883,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { } else if (document->openOnSave > 0 && document->size < MediaViewImageSizeLimit) { const FileLocation &location(document->location(true)); if (location.accessEnable()) { - if (item && item->getMedia() && item->getMedia()->type() == MediaTypeGif) { - static_cast(item->getMedia())->play(item); - } else { + if (!item || !item->getMedia() || !item->getMedia()->playInline(item)) { QImageReader reader(location.name()); if (reader.canRead() && item) { App::wnd()->showDocument(document, item); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 380899f13..daec9dc77 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -346,7 +346,7 @@ void MediaView::updateControls() { _dateText = lng_mediaview_date_time(lt_date, d.date().toString(qsl("dd.MM.yy")), lt_time, d.time().toString(cTimeFormat())); } if (_from) { - _fromName.setText(st::mvFont, (_from->migrateTo() ? _from->migrateTo() : _from)->name); + _fromName.setText(st::mvFont, (_from->migrateTo() ? _from->migrateTo() : _from)->name, _textNameOptions); _nameNav = myrtlrect(st::mvTextLeft, height() - st::mvTextTop, qMin(_fromName.maxWidth(), width() / 3), st::mvFont->height); _dateNav = myrtlrect(st::mvTextLeft + _nameNav.width() + st::mvTextSkip, height() - st::mvTextTop, st::mvFont->width(_dateText), st::mvFont->height); } else { @@ -1513,8 +1513,8 @@ void MediaView::moveToNext(int32 delta) { if (item->getMedia()) { switch (item->getMedia()->type()) { case MediaTypePhoto: displayPhoto(static_cast(item->getMedia())->photo(), item); preloadData(delta); break; - case MediaTypeDocument: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; - case MediaTypeGif: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; + case MediaTypeDocument: displayDocument(static_cast(item->getMedia())->getDocument(), item); preloadData(delta); break; + case MediaTypeGif: displayDocument(static_cast(item->getMedia())->getDocument(), item); preloadData(delta); break; case MediaTypeSticker: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; } } else { @@ -1561,8 +1561,8 @@ void MediaView::preloadData(int32 delta) { if (HistoryMedia *media = item->getMedia()) { switch (media->type()) { case MediaTypePhoto: static_cast(media)->photo()->full->load(); break; - case MediaTypeDocument: static_cast(media)->document()->thumb->load(); break; - case MediaTypeGif: static_cast(media)->document()->thumb->load(); break; + case MediaTypeDocument: static_cast(media)->getDocument()->thumb->load(); break; + case MediaTypeGif: static_cast(media)->getDocument()->thumb->load(); break; case MediaTypeSticker: static_cast(media)->document()->sticker()->img->load(); break; } } @@ -1585,8 +1585,8 @@ void MediaView::preloadData(int32 delta) { if (HistoryMedia *media = item->getMedia()) { switch (media->type()) { case MediaTypePhoto: static_cast(media)->photo()->forget(); break; - case MediaTypeDocument: static_cast(media)->document()->forget(); break; - case MediaTypeGif: static_cast(media)->document()->forget(); break; + case MediaTypeDocument: static_cast(media)->getDocument()->forget(); break; + case MediaTypeGif: static_cast(media)->getDocument()->forget(); break; case MediaTypeSticker: static_cast(media)->document()->forget(); break; } } diff --git a/Telegram/SourceFiles/playerwidget.cpp b/Telegram/SourceFiles/playerwidget.cpp index 44adf5884..080e8cf38 100644 --- a/Telegram/SourceFiles/playerwidget.cpp +++ b/Telegram/SourceFiles/playerwidget.cpp @@ -323,11 +323,12 @@ void PlayerWidget::preloadNext() { } if (next) { if (HistoryDocument *document = static_cast(next->getMedia())) { - if (document->document()->location(true).isEmpty() && document->document()->data.isEmpty()) { - if (!document->document()->loader) { - DocumentOpenLink::doOpen(document->document()); - document->document()->openOnSave = 0; - document->document()->openOnSaveMsgId = FullMsgId(); + DocumentData *d = document->getDocument(); + if (d->location(true).isEmpty() && d->data.isEmpty()) { + if (!d->loader) { + DocumentOpenLink::doOpen(d); + d->openOnSave = 0; + d->openOnSaveMsgId = FullMsgId(); } } } @@ -337,7 +338,7 @@ void PlayerWidget::preloadNext() { void PlayerWidget::startPlay(const FullMsgId &msgId) { if (HistoryItem *item = App::histItemById(msgId)) { if (HistoryDocument *doc = static_cast(item->getMedia())) { - audioPlayer()->play(SongMsgId(doc->document(), item->fullId())); + audioPlayer()->play(SongMsgId(doc->getDocument(), item->fullId())); updateState(); } } diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index e906bb91b..eff4a02db 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -886,9 +886,7 @@ void DocumentOpenLink::doOpen(DocumentData *data) { if (App::main()) App::main()->documentPlayProgress(song); } } else if (data->size < MediaViewImageSizeLimit && location.accessEnable()) { - if (App::hoveredLinkItem() && App::hoveredLinkItem()->getMedia() && App::hoveredLinkItem()->getMedia()->type() == MediaTypeGif) { - static_cast(App::hoveredLinkItem()->getMedia())->play(App::hoveredLinkItem()); - } else { + if (!App::hoveredLinkItem() || !App::hoveredLinkItem()->getMedia() || !App::hoveredLinkItem()->getMedia()->playInline(App::hoveredLinkItem())) { QImageReader reader(location.name()); if (reader.canRead() && (App::hoveredLinkItem() || App::contextItem())) { App::wnd()->showDocument(data, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem()); diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index f9dc10b82..7315a44b7 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -1145,6 +1145,9 @@ struct DocumentData { SongData *song() { return (type == SongDocument) ? static_cast(_additional) : 0; } + bool isAnimation() { + return (type == AnimatedDocument) || !mime.compare(qstr("image/gif"), Qt::CaseInsensitive); + } bool isImage() const { return _isImage; } From f3065eb65491b2c53395edd47a3e9eed581b8961 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 19 Dec 2015 15:27:03 +0300 Subject: [PATCH 033/145] image links redesigned (removed all except locations) --- Telegram/Resources/style.txt | 2 +- Telegram/SourceFiles/history.cpp | 438 +++++++++---------------------- Telegram/SourceFiles/history.h | 9 +- 3 files changed, 120 insertions(+), 329 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 70acf8f2d..49d5bb475 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -2205,7 +2205,7 @@ usernameDefaultFg: #777; youtubeIcon: sprite(336px, 221px, 60px, 60px); vimeoIcon: sprite(336px, 283px, 60px, 60px); videoIcon: sprite(0px, 340px, 60px, 60px); -locationSize: size(320, 240); +locationSize: size(320px, 240px); boxOptionListPadding: margins(2px, 20px, 2px, 2px); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 6dcf3fcc2..f901b6fd9 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -3189,8 +3189,6 @@ void HistoryPhoto::init() { } void HistoryPhoto::initDimensions(const HistoryItem *parent) { - bool bubble = parent->hasBubble(); - if (_caption.hasSkipBlock()) { _caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight()); } @@ -3209,6 +3207,7 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) { } if (parent->toHistoryMessage()) { + bool bubble = parent->hasBubble(); w = tw; int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); int32 maxActualWidth = qMax(w, minWidth); @@ -5020,6 +5019,7 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, usex = width - usew; } } + if (rtl()) usex = width - usex - usew; if (!already && !hasdata && !data->loader && data->status == FileReady) { data->save(QString()); @@ -5051,6 +5051,7 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, 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; App::roundRect(p, rx, ry, rw, rh, selected ? App::msgServiceSelectBg() : App::msgServiceBg(), selected ? ServiceSelectedCorners : ServiceCorners); @@ -5595,10 +5596,13 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, const QRect &r, if (_attach) { if (tshift) tshift += st::webPagePhotoSkip; - p.save(); - p.translate(lshift - bubble.left(), tshift - bubble.top()); + int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top(); + if (rtl()) attachLeft = w - attachLeft - _attach->currentWidth(); - _attach->draw(p, parent, r.translated(bubble.left() - lshift, bubble.top() - tshift), selected, ms); + p.save(); + p.translate(attachLeft, attachTop); + + _attach->draw(p, parent, r.translated(-attachLeft, -attachTop), selected, ms); int32 pixwidth = _attach->currentWidth(), pixheight = _attach->height(); if (_data->type == WebPageVideo) { @@ -5779,7 +5783,9 @@ void HistoryWebPage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 if (tshift) tshift += st::webPagePhotoSkip; if (x >= lshift && x < lshift + width && y >= tshift && y < _height - st::msgPadding.bottom()) { - _attach->getState(lnk, state, x - lshift + bubble.left(), y - tshift + bubble.top(), parent); + int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top(); + if (rtl()) attachLeft = w - attachLeft - _attach->currentWidth(); + _attach->getState(lnk, state, x - attachLeft, y - attachTop, parent); } } } @@ -5793,11 +5799,6 @@ ImagePtr HistoryWebPage::replyPreview() { } 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; } @@ -5857,22 +5858,6 @@ 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; @@ -5947,138 +5932,6 @@ 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; } @@ -6155,43 +6008,17 @@ _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; + if (_data) { + switch (_data->type) { case GoogleMapsLink: return st::locationSize.width(); } } @@ -6199,11 +6026,8 @@ int32 HistoryImageLink::fullWidth() const { } int32 HistoryImageLink::fullHeight() const { - if (data) { - switch (data->type) { - case YouTubeLink: return 480; - case VimeoLink: return 480; - case InstagramLink: return 640; + if (_data) { + switch (_data->type) { case GoogleMapsLink: return st::locationSize.height(); } } @@ -6211,71 +6035,66 @@ int32 HistoryImageLink::fullHeight() const { } 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; - if (!_title.isEmpty() || !_description.isEmpty()) { + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); + _maxw = w = qMax(tw, int32(minWidth)); + _minh = qMax(th, int32(st::minPhotoSize)); + + if (bubble) { _maxw += st::mediaPadding.left() + st::mediaPadding.right(); 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, const QRect &r, bool selected, uint64 ms) const { + bool bubble = parent->hasBubble(); + if (w < st::msgPadding.left() + st::msgPadding.right() + 1) return; int32 width = w, height = _height, skipx = 0, skipy = 0; bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; - if (!_title.isEmpty() || !_description.isEmpty()) { + if (bubble) { skipx = st::mediaPadding.left(); + skipy = st::mediaPadding.top(); - 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, 0, 0, width, _height, bg, cors, &sh); - - int replyFrom = 0, fwdFrom = 0; - fwdFrom = st::msgPadding.top(); - skipy += fwdFrom; + if (!_title.isEmpty() || !_description.isEmpty()) { + if (!parent->toHistoryForwarded() && !parent->toHistoryReply()) { + skipy += st::msgPadding.top(); + } + } width -= st::mediaPadding.left() + st::mediaPadding.right(); + int32 textw = w - st::msgPadding.left() - st::msgPadding.right(); + p.setPen(st::black); 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); + _title.drawLeftElided(p, skipx + st::msgPadding.left(), skipy, textw, w, 2); + skipy += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height); } 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); + _description.drawLeftElided(p, skipx + st::msgPadding.left(), skipy, textw, w, 3); + skipy += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height); } if (!_title.isEmpty() || !_description.isEmpty()) { skipy += st::webPagePhotoSkip; @@ -6285,44 +6104,24 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, const QRect & 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)); - } - } - } if (selected) { App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } @@ -6334,12 +6133,14 @@ void HistoryImageLink::draw(Painter &p, const HistoryItem *parent, const QRect & } int32 HistoryImageLink::resize(int32 width, const HistoryItem *parent) { + bool bubble = parent->hasBubble(); + w = qMin(width, _maxw); - if (!_title.isEmpty() || !_description.isEmpty()) { + if (bubble) { w -= st::mediaPadding.left() + st::mediaPadding.right(); } - int32 tw = convertScale(fullWidth()), th = convertScale(fullHeight()); + int32 tw = fullWidth(), th = fullHeight(); if (tw > st::maxMediaSize) { th = (st::maxMediaSize * th) / tw; tw = st::maxMediaSize; @@ -6350,38 +6151,31 @@ int32 HistoryImageLink::resize(int32 width, const HistoryItem *parent) { } 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 (!_title.isEmpty() || !_description.isEmpty()) { + int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); + w = qMax(w, int32(minWidth)); + _height = qMax(_height, int32(st::minPhotoSize)); + if (bubble) { + w += st::mediaPadding.left() + st::mediaPadding.right(); if (!_title.isEmpty()) { - _height += qMin(_title.countHeight(w), st::webPageTitleFont->height * 2); + _height += qMin(_title.countHeight(w - st::msgPadding.left() - st::msgPadding.right()), st::webPageTitleFont->height * 2); } if (!_description.isEmpty()) { - _height += qMin(_description.countHeight(w), st::webPageDescriptionFont->height * 3); + _height += qMin(_description.countHeight(w - st::msgPadding.left() - st::msgPadding.right()), st::webPageDescriptionFont->height * 3); } + _height += st::mediaPadding.top() + st::mediaPadding.bottom(); if (!_title.isEmpty() || !_description.isEmpty()) { _height += st::webPagePhotoSkip; + if (!parent->toHistoryForwarded() && !parent->toHistoryReply()) { + _height += st::msgPadding.top(); + } } - _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"); + if (_data) { + switch (_data->type) { case GoogleMapsLink: return lang(lng_maps_point); } } @@ -6389,15 +6183,12 @@ const QString HistoryImageLink::inDialogsText() const { } 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(" ]"); + if (_data) { + switch (_data->type) { + case GoogleMapsLink: return qsl("[ ") + lang(lng_maps_point) + qsl(" : ") + _link->text() + qsl(" ]"); } } - return qsl("[ Link : ") + link->text() + qsl(" ]"); + return qsl("[ Link : ") + _link->text() + qsl(" ]"); } bool HistoryImageLink::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const { @@ -6406,21 +6197,38 @@ bool HistoryImageLink::hasPoint(int32 x, int32 y, const HistoryItem *parent, int } void HistoryImageLink::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width) const { + bool bubble = parent->hasBubble(); if (width < 0) width = w; bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; int skipx = 0, skipy = 0, height = _height; - int replyFrom = 0, fwdFrom = 0; - if (!_title.isEmpty() || !_description.isEmpty()) { + if (bubble) { skipx = st::mediaPadding.left(); - fwdFrom = st::msgPadding.top(); - skipy += fwdFrom; - height -= skipy + st::mediaPadding.bottom(); + 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 = w - st::msgPadding.left() - st::msgPadding.right(); + + if (!_title.isEmpty()) { + skipy += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height); + } + if (!_description.isEmpty()) { + skipy += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height); + } + if (!_title.isEmpty() || !_description.isEmpty()) { + skipy += st::webPagePhotoSkip; + } + height -= skipy + st::mediaPadding.bottom(); } - 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); @@ -6554,7 +6362,7 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media, QString ¤tTex 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); @@ -6565,19 +6373,10 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media, QString ¤tTex case mtpc_webPageExternal: LOG(("API Error: should not get webPageExternal in HistoryMessage::initMedia")); 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) { if (doc->sticker()) { _media = new HistorySticker(doc); @@ -7406,7 +7205,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) { @@ -7416,7 +7216,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); @@ -7425,31 +7225,27 @@ 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::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg))->p); + p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); } - replyToName.drawElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top(), w - st::msgReplyBarSkip - previewSkip); + replyToName.drawLeftElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top(), w - st::msgReplyBarSkip - previewSkip, w + 2 * x); HistoryMessage *replyToAsMsg = replyToMsg->toHistoryMessage(); if (likeService) { } else if ((replyToAsMsg && replyToAsMsg->emptyText()) || replyToMsg->serviceMsg()) { style::color date(outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg)); - p.setPen(date->p); + 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); + p.setFont(st::msgDateFont); style::color date(outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg)); - 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.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)); } } } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 71c456b6d..8b576d3bb 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1857,9 +1857,6 @@ void deinitImageLinkManager(); enum ImageLinkType { InvalidImageLink = 0, - YouTubeLink, - VimeoLink, - InstagramLink, GoogleMapsLink }; struct ImageLinkData { @@ -1867,7 +1864,6 @@ struct ImageLinkData { } QString id; - QString title, duration; ImagePtr thumb; ImageLinkType type; bool loading; @@ -1934,9 +1930,9 @@ public: } private: - ImageLinkData *data; + ImageLinkData *_data; Text _title, _description; - TextLinkPtr link; + TextLinkPtr _link; }; @@ -1949,7 +1945,6 @@ public: void initTime(); void initMedia(const MTPMessageMedia *media, QString ¤tText); - void initMediaFromText(QString ¤tText); void initMediaFromDocument(DocumentData *doc); void initDimensions(); void fromNameUpdated() const; From cd700b16c67dc983130baec1446823ab87194735 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 19 Dec 2015 17:37:28 +0300 Subject: [PATCH 034/145] files and contacts in PhotoSendBox redesigned --- Telegram/SourceFiles/boxes/photosendbox.cpp | 123 +++++++++++--------- Telegram/SourceFiles/boxes/photosendbox.h | 14 ++- Telegram/SourceFiles/gui/images.cpp | 72 +++++++----- Telegram/SourceFiles/gui/images.h | 2 + Telegram/SourceFiles/history.cpp | 2 +- Telegram/SourceFiles/localimageloader.cpp | 1 + Telegram/SourceFiles/localimageloader.h | 1 + Telegram/SourceFiles/structs.cpp | 13 ++- Telegram/SourceFiles/structs.h | 2 + 9 files changed, 134 insertions(+), 96 deletions(-) diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index c38770a69..6c7885291 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -29,17 +29,17 @@ 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) , _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())); @@ -79,30 +79,23 @@ PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxW _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, 0, true, false, true, st::msgFileThumbSize, st::msgFileThumbSize); } - _name = _file->filename; - _namew = st::normalFont->width(_name); - _size = formatSizeText(_file->filesize); - _textw = qMax(_namew, st::normalFont->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); } updateBoxSize(); _caption.setMaxLength(MaxPhotoCaption); @@ -115,16 +108,16 @@ PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxW } 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 +128,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::normalFont->width(_name); - _size = _phone; - _textw = qMax(_namew, st::normalFont->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(); @@ -165,8 +157,10 @@ 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()); + } else if (_thumbw) { + setMaxHeight(st::boxPhotoPadding.top() + st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom() + 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() + st::boxPhotoPadding.bottom() + st::boxButtonPadding.top() + _send.height() + st::boxButtonPadding.bottom()); } } @@ -191,35 +185,54 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) { } p.drawPixmap(_thumbx, st::boxPhotoPadding.top(), _thumb); } 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::mediaMusicOutImg); - } 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::normalFont); + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.drawEllipse(inner); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + + 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); - if (twidth < _namew) { - p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, st::normalFont->elided(_name, twidth)); - } else { - p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaNameTop + st::normalFont->ascent, _name); - } + _name.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width()); - p.setPen(st::mediaOutFg); - p.drawText(x + tleft, y + st::mediaPadding.top() + st::mediaThumbSize - st::mediaDetailsShift - st::normalFont->descent, _size); + style::color status(st::mediaOutFg); + p.setFont(st::normalFont); + p.setPen(status); + p.drawTextLeft(x + nameleft, y + statustop, width(), _status); } } diff --git a/Telegram/SourceFiles/boxes/photosendbox.h b/Telegram/SourceFiles/boxes/photosendbox.h index 597c4496a..3d62b7d6e 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.h +++ b/Telegram/SourceFiles/boxes/photosendbox.h @@ -65,16 +65,22 @@ private: void updateBoxSize(); FileLoadResultPtr _file; - int32 _thumbx, _thumby, _thumbw, _thumbh; - QString _name, _size; - int32 _namew, _textw; + + 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/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index 1ba8440fe..7a30eb6d5 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -396,6 +396,35 @@ QImage imageColored(const style::color &add, QImage img) { return img; } +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 || (w == img.width() && (h <= 0 || h == img.height()))) { + } else if (h <= 0) { + img = img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation); + } else { + img = img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation); + } + if (outerw > 0 && outerh > 0) { + outerw *= cIntRetinaFactor(); + outerh *= cIntRetinaFactor(); + if (outerw != w || outerh != h) { + img.setDevicePixelRatio(cRetinaFactor()); + QImage result(outerw, outerh, QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + { + QPainter p(&result); + if (w < outerw || h < outerh) { + p.fillRect(0, 0, result.width(), result.height(), st::black->b); + } + p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img); + } + img = result; + } + } + if (rounded) imageRound(img); + return QPixmap::fromImage(img, Qt::ColorOnly); +} + QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh) const { restore(); loaded(); @@ -403,41 +432,22 @@ QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool roun const QPixmap &p(pixData()); if (p.isNull()) return blank()->pix(); - bool n = isNull(); - QImage img = p.toImage(); - if (!n || !(outerw > 0 && outerh > 0)) { - if (blurred) img = imageBlur(img); - if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) { - } else if (h <= 0) { - img = img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation); - } else { - img = img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation); - } - } - if (outerw > 0 && outerh > 0) { + if (isNull() && outerw > 0 && outerh > 0) { outerw *= cIntRetinaFactor(); outerh *= cIntRetinaFactor(); - if (outerw != w || outerh != h || n) { - img.setDevicePixelRatio(cRetinaFactor()); - QImage result(outerw, outerh, QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(cRetinaFactor()); - if (n) { - QPainter p(&result); - p.fillRect(0, 0, result.width(), result.height(), st::black->b); - } else { - QPainter p(&result); - if (w < outerw || h < outerh || n) { - p.fillRect(0, 0, result.width(), result.height(), st::black->b); - } - if (!n) { - p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img); - } - } - img = result; + + QImage result(outerw, outerh, QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + + { + QPainter p(&result); + p.fillRect(0, 0, result.width(), result.height(), st::black); } + + if (rounded) imageRound(result); + return QPixmap::fromImage(result, Qt::ColorOnly); } - if (rounded) imageRound(img); - return QPixmap::fromImage(img, Qt::ColorOnly); + return imagePix(p.toImage(), w, h, smooth, blurred, rounded, outerw, outerh); } QPixmap Image::pixColoredNoCache(const style::color &add, int32 w, int32 h, bool smooth) const { diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index 92f7115bc..be2576bfd 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -49,6 +49,8 @@ inline bool operator!=(const StorageImageLocation &a, const StorageImageLocation return !(a == b); } +QPixmap imagePix(QImage img, int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh); + class Image { public: diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index f901b6fd9..d00e5c990 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -4412,7 +4412,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r p.setFont(st::semiboldFont); p.setPen(st::black); if (namewidth < _namew) { - p.drawTextLeft(nameleft, nametop, width, st::normalFont->elided(_name, namewidth)); + p.drawTextLeft(nameleft, nametop, width, st::semiboldFont->elided(_name, namewidth)); } else { p.drawTextLeft(nameleft, nametop, width, _name, _namew); } diff --git a/Telegram/SourceFiles/localimageloader.cpp b/Telegram/SourceFiles/localimageloader.cpp index e81e1ea34..64ae1e592 100644 --- a/Telegram/SourceFiles/localimageloader.cpp +++ b/Telegram/SourceFiles/localimageloader.cpp @@ -393,6 +393,7 @@ void FileLoadTask::process() { _result->content = _content; _result->filename = filename; + _result->filemime = filemime; _result->setFileData(filedata); _result->thumbId = thumbId; diff --git a/Telegram/SourceFiles/localimageloader.h b/Telegram/SourceFiles/localimageloader.h index 669095409..52a72a0a0 100644 --- a/Telegram/SourceFiles/localimageloader.h +++ b/Telegram/SourceFiles/localimageloader.h @@ -190,6 +190,7 @@ struct FileLoadResult { QByteArray content; QString filename; + QString filemime; int32 filesize; UploadFileParts fileparts; QByteArray filemd5; diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index eff4a02db..6d3afacc2 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -1080,12 +1080,10 @@ const FileLocation &DocumentData::location(bool check) { return _location; } -void DocumentData::recountIsImage() { - _isImage = false; - +bool fileIsImage(const QString &name, const QString &mime) { QString lowermime = mime.toLower(), namelower = name.toLower(); if (lowermime.startsWith(qstr("image/"))) { - _isImage = true; + return true; } else if (namelower.endsWith(qstr(".bmp")) || namelower.endsWith(qstr(".jpg")) || namelower.endsWith(qstr(".jpeg")) @@ -1096,8 +1094,13 @@ void DocumentData::recountIsImage() { || namelower.endsWith(qstr(".tif")) || namelower.endsWith(qstr(".psd")) || namelower.endsWith(qstr(".png"))) { - _isImage = true; + return true; } + return false; +} + +void DocumentData::recountIsImage() { + _isImage = fileIsImage(name, mime); } WebPageData::WebPageData(const WebPageId &id, WebPageType type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill) : id(id) diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 7315a44b7..8fe02f3f5 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -1070,6 +1070,8 @@ struct SongData : public DocumentAdditionalData { QString title, performer; }; +bool fileIsImage(const QString &name, const QString &mime); + struct DocumentData { DocumentData(const DocumentId &id, const uint64 &access = 0, int32 date = 0, const QVector &attributes = QVector(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0); void setattributes(const QVector &attributes); From 216b9ec52f3759ecf59211edb57bd69c029f2125 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 19 Dec 2015 21:09:24 +0300 Subject: [PATCH 035/145] overview redesign started (four out of six not working) --- Telegram/Resources/style.txt | 6 - Telegram/SourceFiles/art/sprite.png | Bin 181869 -> 179013 bytes Telegram/SourceFiles/art/sprite_200x.png | Bin 245738 -> 240472 bytes Telegram/SourceFiles/history.cpp | 2265 ++++++++++------------ Telegram/SourceFiles/history.h | 237 ++- Telegram/SourceFiles/mainwidget.cpp | 3 - Telegram/SourceFiles/overviewwidget.cpp | 534 +++-- Telegram/SourceFiles/overviewwidget.h | 20 +- 8 files changed, 1375 insertions(+), 1690 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 49d5bb475..4dddb8965 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1172,12 +1172,6 @@ mediaHeaderSkip: 5px; mediaThumbSize: 48px; mediaNameTop: 3px; mediaDetailsShift: 3px; -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); mediaInFg: msgInDateFg; mediaInFgSelected: msgInDateFgSelected; mediaOutFg: msgOutDateFg; diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index 91a9f8ac0e266a5218fd152a4867f8bcb7726a73..fd9350ad111973ba93e3ef68405a8d15af590066 100644 GIT binary patch delta 54988 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 CNI-%B delta 57866 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 diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index 53fa4b7928a60df78651aa4a4fa7a109c361c676..7a42854dc69f03d202078d071b4a4b2c8c5a2c32 100644 GIT binary patch delta 73494 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 delta 78801 zcma%iWmr^S^zIqDLmH%0>FzFnG>QT;bW4YT)XSHA0AYtKv(>^>4yprGOK>+$nR2#87uizeftN&UZnzC@E|u@tZp z;1ja8<`%Rxx8xQQwlL=ww-6KI78SA<7c#dH77((ruC7Mgz-9jb_+)Y(wip4w05|{t zeP$?xxE?|b`Llrbj3Q+$ef9vtgEBT$k9`QC$%jSOzczv0`LP;)+F(7R9oZL~NR(&O z!Ro4!qZ#KzNx8#nvd1;AA?2|C{{7cWES5OFakSqt!F*MH1pHJVNn9j9Ue;L;R88IA zyIC_2%r(w!ol5yWFhLP`9U5w z2HyoP@3r9b`z2L_Ac zH8(5o^(1dSQ-xY-tSH&0JB-TV3_Om}ar&^h3v9PHP|E1IEv~3IBGU&27Z>{`{qpH) z__#{jJEFGcM>46aqlA;r6Zfr}a1+ysrE#zFwrJc+1F3BLkrCwxKH+D+Jh*IVX!3Z> z#Mtheix-!8)Yzv_cjC+zumAqtQHJ=~p5K)AYEIko{mS<|SkT^u0MybBSJM@t@o6w^ zX!@|Y$X>mKr2%~qCtimIW$5wM9D|Zd#ZLROM6<<1WiK7>$nW1b)N;%H$?W9;E8!Fv z25H^&k5|2;_7jBxC=3gRZn_2rcFw0(uG;=vp;_S-UH3q)=uH!i?1+iMni$-(?1+q% zbf(IsYe-*52azFG*15{WE`3-bM+p;{)pcvH+IN-wRr_+v%U|2v6aDZpi1TYNCa>k0 zg$p*2%Gvt(gIz*m5q9wNgE@C7DPSk``Jhu91r4QChoia2GjH(3W_{6L%!OHk+twn0DWU+cChr{6+oHA7+zgz_j!% zhylk$jxZp>S0|vq>``i6@5OQ&x6#4_v*-Q!wBS@%cx}Laa6`Scpd!qwZ4!Z+tIG17)7F16yBrc~XsnQfj-q)B!k z@%<$J{yXz!S^UVWjPU#1Rr|Qy)B3E_`Dyy4+V$b{^k~JiaFpOa=AHk}eVy{&c9QS= z9^|+9*VMajsHF$q9elX5P={X4?{}{nIVV+pdhym?YGQ6(llX+GC0`SI#I|`J1o7_3_H55!Sy1*ys^IJ32Z{$w^7~ zT`#hWcjw46BVJ&Kbu?J^viKXH%s6WgS*)XqIaWC(m@lkC9h;~S5Zr*jn$m;im#(#% z9RvF}BdC59u+k6CCO=ZHUoa3g3MVI;lylPP?4%tn9-CFR#U*gN<-aMpAJ(;1UgP;< z;frQYkCN}%v6`AtyFdF^NirUzw=nXJKwTQKBecF_=R&`Hc~`yC@(AT>ujf7Iu9A$^ zpNc-dyGIhAN+yv`zBjh&P$H^Z4YZz@%)yMpv(uhV2v`nAekC2W?kA%H;BO4-#9Cgy zeOfE$m_+@im~LL$*3tJ4f~EW_zLk5^Psa+1fm|+>!49Hm58r}!oB}oEH|u1jZ~Wvq zNQXD8HFLBslV7Z>(@!IXDIQ!L(nD%tj zxw#F%xa&!;Cu&=-HrYMS^o~Lp_Eq|-;$#H&JGx^jU`@6oblmY#dnguB^SO&1&c54Q zhTH$%U6-_0VyvMBExSPLS0l|Y$9l`;#o&BQCp0Liz%al8 z)F<|&l;hyymb>1B>gUT4O@sD|eawL*-LPHN4P zXeu8*TPEQUnZ^YmH<_pD8|vi7NF3Cb#9G9-AgR-R|3M8OPz(Q?GspJ>na9m1x%0 zUj_GFp~8#j3-tgv7IaMBPaNAh;>X;eUq59|RhnpS;LXkmWyDX|`pQzt-QDR(+ZXM8 zm(%k-Li(G{4cXF%&G}C3ckwp@rCOJwIVO+K&KgMy$iff9w8|g#N+#|#OLa{zhUz0p zT0dMr7_|l7uP8ovEd@X4AzwPb^h|n@q2VneFZOKIFfFN@Wr#_3;-0A!*WZSST>GD^ ztSSooN~{I-A^BmLr}anyIs6lK!P}Ws>!L4T%v{XPSMUKDzPViG`#w6oVz zb+<9PuUi(|*qCvkV#y6b-D<9}RWL$i0(lq~f_pn~Y4qdw%r7w1DA%lCusb&=Ma9nL znJBRR4-Z~sye3RV z_k{qVS(vZG!soAU<%oW4YoL{M6TwWN2KsVjczAev(15ICygqH!xF6G+1R!fr;Z3x_ zbV-32vV%dR@&rYMgVdG%fK3WdLN|JN#k2ZPpFhuyEiBy0L7@wZTmv6Hj!igS931>a zgoSHEWj>%qyttYd&B<*}E7D+y~& z3xNfKuF`Lt7jR+7FbMsj_q<+fjQ|htcwEcQj)N?c$t}3M{#WXrLfGu_L`-|wZFG`q z6wG^4nPQkSy^(?OtxWf)FMNKEdP-gz;ANuqw*58k-?!TO)3DRc3)H6Ky|<`S*W>_- zlhvG1hBk@zl?-oC$>729jl$>lVxVyLEVQ z5XwS|V~Y}HZ74GDD*GCBdvwQB_O-tL5v^=sv&Hq_4f)&a@prY`hleVvs+jH`Fb_sj zoYF%uZ0MMne1yZr`ZnCd!oo(=Qxc+L31#mR(eH}jxjNZWs9fq6$&~jrO|EY@Q$0?3 z`qiS1I8vjm%jziT$UpP3yX=Hq0B=g>1Oin>d4%O(^&70 z?_tP}Z{$>6^0CPWETKjoZl$6}nbNy1uXH|&W1OU;nMuKaZ}cOpV(q8b7fZ0FM*biW z05vuB=;$aAAHnHNPfi|DBeb`yIW#oX*&D6I^zGX>25D*9{QUegQzwk#MA@5syhP54fzxQ<5|7052L`$bS#i^l&K z>x6ciD_hi|BV)kwE0g=;Sfn0P8g~Z&VV%>+FT;wH)BExOKR63RY;la``UJ$EkYT$B zGsjreyfLj)bB}iyF?&zGMqfW8--Q{tRW3<5{vX7$8HW0Y2;t6LrFjd;$1QRvOv&rnJ;(E@>>2|o# zpnH^>mZrd#%*fW`U)Nby#*;05dwL^Z`X;uqQR2_!BqKLBenUgU7nE=oo_#qYHr%Wy z8m%MU*Cek38+>j)Eu>t9n+w>-<@6$Dpdqaf!BdAa*2W%@U%$e4{dXdlpK?yV6TXq7 z&MHYCyy~8*xq@}SjAmIN3FuK(jW_b17>7am#7~EcL;@N(M-N45iQb)}L?KdUbb@AzAG0}_c{4VKif!ga*SxHL9ZeLuf#OUtUTFgB%;Rr+KXca9HkCB z&mXKtPC_HKU)SBN!S9xNDF!oz84oB<`2S)rRmgLY#_+V5{muDX!FP?y>vGF7dMI6L zAVpPeGnxP6h2rur8CISpbWw+~PTK+|0CqW&DacNOh>s+sr~9TZIvC2f+L2O%i;LTa zx_Q6Oe9=lUfZGf)8v-rvanb$tAe}ARx1W7{IsK!1LXyQJ+4nSj8;qiPwpegswgLZp zj5$y?0zh6U?zwx4YzS^aBjc#79ZE0|9Uc9ny#6T_pAaX14V_|9b!pS;>M8?_NC(I3 z357_8FsBkd(^CpOZ#4zSl(Xf_$BVJ6Z4Q&vdN7Z$_xvtm11}T4XL}Lvi?q2d^ zws^%F@~4C;UGl)DhE|jBSe&WV_b#5ZKaSztf7z|e{9Pwg4@R6Gb|d8)AJI^vct2m4 zxM9Y`sBO&@yooL6xU!QlZNm94xi68JpI#I&*f)s@#c4GD-%xy{H!C|0Zh8$ zNb=qo^i`7A53LUoH+9dDG@f3Uk63d6*PLo{2tt#e_r5-lJkoNl!t+ zIw0~O`CN+2gX5ce(ZrtL&5c9+^3nF|sVc6B>0r;ZGhsZu;@9V;#zTwJsK4n4-hJ-; zZZYQ62KCul-(R=RfBD}xUQ^zr7aXl?bINVY?|j7|iaq!prnuVRHz16fh8Q?j zUC)EjLE0KQS2fdS-w=2FmJT?_*8VkH;uT`Vn5JvABw_9tH4 z>aXv>kgE=X4!)wd2!-R%=se5OiX7H@fF-kfdU+`njOon1eL?~LrumW&Uwcy7{VwZ7 z`AHpqwA-MmEC~f*IJJTZ94p_cSlgZXL*Jsd5{S(#e^cKF);;L5teY;+tPMJXY8K@<#=okUcV-KsMA6}hGBvL z9tQ`f^;p-Bw_`h{#?-OlxlI9kycuPW4|nelL!&8I&fbC4rN>ajsy>Ry)^3BxR>Z{k z-)NI^Rcq@C?VeBYimrb}wOZGwY%_Qnm=)dBvi;c>R~~x5?pW%_C@}`tDn$j`J#;9O zglr3ZB%-Lwmo4RPRZ~)MJ;iXkEXma;%rN-@}UN)P5D< zK^#|h;JOE95$YDlc?TdS6qzdyF$>K+utal_62vXQ75Vg89SBJ*%>TD$bk)-uQ}L6~{Sc}=*UFp1gl0u0RmNp_>Z4xC^PWAGoP`lrQ6D8869TgKk~-A!qr1C%Vp^JwSD^v}ksMpHJcycvg~>EE zHGlm0;p)S!3K}GvcJBU<55hn(#0nMOL6v0A#e?TGnFv3o#8dk8#n=5gj6Bwg;cl8Z z{Y6K)p%C|NK~67{C@m8k2emUdZaxg|3#%0VXS`Gp^{%`<)r@)Z{1-lSeQble-y8$ z7jTyHzp9uJkG389+U_; zfO33d!dNCRC55!fR_@7@Cw%51r#mwTIsFW7D>UDflx#89K5$~B7<-=IXVG=bz50?% zgKI9}UXC?`(vn4ob2s{``s-C+5;NhIS>X>DCS*`#)@urBcjTJCsAKS>b)Ts`)bFpu zU;o>h$hi@#0TgrzFrp1*`SEyhN(1s-R_DBHmhA!C0!Qt3Pmx*xUbG*Ad@dVeiY(XTqiYTc0K8cLCB~tw;64BG)ov{({uZ-CtV}s?PU!qOxQ~b>AnR~=CBZFsnGQBWN_IBw66Llau z&(o3FIALwD#Pd#;mYmz;$U1Qvia4hgceh&DpRFD^u%Gzd$~*@f9v)WFO}dUNdJ>J6 zpMPg9K=)2NUne6#mXkB~(!tq=ZB4D<$S{wH!l({jH*?%cD0?>#oX8X;JCca zd?D6`9;rRO39iS#NNaGhu~)dIe#14@kDu9SC}Y4g<5WT`h^QX41u0Be8jxO@It+1y zQ>QKm+q(EaFzm@YwRa9)iPU?Yq4~2_*!>hrRHTfAk#OS;S>X*iqCIaF3pL+8-v4{T zKPY;A%X}lj5~rkc0t(Xx@WDbHnfr-YVg|hjfiL$(Vl-o6i23#-$lH!<^d!Rh9_Fn~ z^YB%{PGMe9QtI?tdw8Jl0XB>2Ik&%SORjNT!oX9KGp*v6hV)=bC%63jK~+^7_JJ>( z9fm6}0LNU0yq=wh%D0$drT^`w!kakbU6hfDC=6aZONrtmqY8+ajS>O|AdVc*$vmYIAUZiT-HuN>TDG4_jIdX}qy`lN| zXVY~QB5LBHYw0s<%pU4Ws1VI3J=6OI)}W{>?;QerIX(V{9B=Cxk(|_C527rZSmRhE zbY(l4dxAYm5eD)S7a37_4GFPv1SCnss!X933_`w$$?WTyezr2;mv<|pXlQ72IFG#$bqQI^-tO)%9*He^s9vZMtP_A!YQgmG*sd7; zu0wSj#|Xfwvtb54}9J(#tbq$lh4waNHN}Dv-BBU zKicp#B6dbT2l60Sv_bldi<#du@oc-5O-=JAg=&3U=yG!ItjG6vcSBSPDz5^KjA-5( zT^p&}Ju^x%*X+>}GdRgYCcwh_j!CJ}&Ai8E)u_=Q3)J&}N%}TQ*wMYkLDRqi7>z$X zp*%!}^)I6X{tdu@OmdqGy1q+j#X(BLJsATO-+9W?PO~!yIpygCwGw;IbLJ~OH#g_H z-bb8uFDxWVH48U9}q!WGvv>rR>&`r74Z!p9XqHkhf|3|ql`EHc;> zbtYyEw@~`8-jtq|7!%lu1|I?D>Kb*ivx|$bKl{%!PwMJKJUu;UYF%ExCIiv+nuwb` zQ>UUzJME?w@BIsUOon>HgFc`|y_q0~3>0f`^A^_+T|eDZ*PPHcY5a#UH-r zYH!+Bm88;fxwA)a7jSb>{y>ra@fqiU$pgcX)F*^1jdlhxRaypySx)8wA=vhiou+%i zFNRJJTnWPCwEf*kf?W0&yk&x>2OVMf2!T7P?|Cq5yGx;ydQ)lC0W~K z!B}ws$9e{h?0hmP)-(pDQV)yZzY&HY4BU<|tf$Tx#~-QOPK1ImTb}XQjN}?SyX4?m z3J5&q9hMLii&|Z^cq$wCl$)E|hQh)@h`Oe>c4fUkS{F+ z0jC?}6|>M1G+K5l4I;>NM5swbU>}e$%=kAYUz3iFho^)Neb%a_*+V|yg<0Sv|MgZ1 zG+XmP7F#ac5#decu8yGV&-^AXgHVK=nZ}uz9pZTdKHW&XVNqt^xCRsi75iKsJ`x?g zqji;$fck|zsD;0#4fVK?YUPmyJ}Ofl6Pw@WG}+Im%tdYql5=2frKqPz3R7YtzPY{4 ze>r>MCLtj)IyMHWG{F0*Cm}V@r?=ISgr_5riKnZp+xg6`prC-5oP6{6s)B32mv7J( zz%Za7udCo15WuS@k>ePsQoDg4t7quEz*Qd70S(!&u=u3Jeyta(Ua;cld44e|bNsva z2;)}l2f7BH%@B^fZc@Y{4Che^$?o+A^@;}GJ>d~=0u!9&T~?9Q2KdsIEwkH${l0>X zR2-(CI`9jnekzpTw^U&E=n2ii0-9Mu=7Auhbm+c!G3ksm>cE+q2 z8?^mf-paxgryRj7NCi^18L}{$zo)1CmOXfbIg%6{>-~KL1D(wuPWh~VlNR+xTN|K~0c5+yY$K4Fq8gm^|a+8gxx=DHLQ;bX!Q{KuebgY3`w%=Sl z>p{7qgN9!!12XN~8ah1-=sdc?_SD6)p;;}i?9{JT%+#R(jJ~d@(hwdvLpZH-n zOER_lb$s5$iz@=u;`XKV`UC&JN`}Vl@feZMp=yNKFU)-6o#S$oG8}r{=!1+!s3}bFCIE&-%a%4hn$8bNcwfzF;#h!pCiA1NqJ{gQV>; zM7`bdIgE!6t^TlMG49D6UMK0S(U!#vD?U6+EkJ+v4&@0}VtV?Ph%IdQ&rbsDN+y)h zZch5x!gGqL>n+UX!)^WIzRYt-&D)%tg9FUm>kHTaq~m)=q6~&6j-2gsQr;x_G zRx)`jXR>`NSE`C7fzu^@76JbdHP;m{C${D6%Ow-7(u5M>OE6@KD@(Q!<+phm5z3I*NPoD6qVdDVK6x0J=$LohN zr{xC&E$@753q{s5L$=_R*IKa8cLDWOm9FC-y;@P~ky9zMq~zp1U>Ai)X!g+YZuft> z{ON4d83A53r#wO3Hg#<^?tZr8pi}B+IlAN2At*zoPlh0`*iriB%WBq);eXXe+hgT=ZBuT*(a^AS_$(&l z`B2VpRny12ey}m{ru9}Izc|&wbm0alZKzoD;7L@O;J9KBuDUtFf+wahl4s2JmVG7$ zX_8*B#X*mn4jcE1GUTIIc;E4#UmH3Fk6;(?(Ee$*G`0 zZnts}85mYwR6dJ25RP_f1h-*M#I4$|vsbnLK5%c$@5M#m=|O~e9dXP4XW*Xr%FXU`}_ofm$rC#S6$aQRnyzE62jc(02Q@ne_y`rqle`0S-Ep(8$3q0sd_m_OReQsL#gV# zcY=BM-WSi+O7`A9)2xuzDwbm?(mmEj*+{?i1GY+=AE|;0E=1nw=p2`#%bWjx%&zg~ zgj2qUsW(HAl72MvU~4CmuZ^uUMZd)-epB)~x9!X}-WW);^qk2efcE{#!m^-@Xzz+9 z;rF|61be7)M5_G!{24;_pJp|5!2Zt8&Q3wG8#@b&*~R{BhKLijbs~-elhO(cUpBID zXZPsJvHW|>?O8geR?FGnv>Za`W$(Y?^dqo8{>YEBfU3Bm?qm0w3u&Ok(SwVB-IkK2 z(IvKw(~sgKF)i=xfFD$B0zv+h$S0A(?t~!&eboeraGS1f53QPOuAmnHE_ZYqNXzHR z%pB^aq~Jh(=ca+Xj`bFNWbu2+-_!s`F$3(y@5kWea0;+^{25H-(P+!EQB!$3s1JvGvpIZ z6lxX(GXsDHpKi_H9=EwhvgWXT|C7%Cbd%fV!@R+k>2f{$Bj1Y_(PiJWH}-Gd-6?42 z=7A-Hbe;We$=lMNmHD;O4%DJFQweqMC)ma&^Z!D8%fAj?)Fu;ycQD#v800U8~$VZn)o0`Miv$Yw`>_v!9@wwVxl~r`& z?|bENZoi0%f!yy64UpTFtH!S#v|IJF#{vVR0oI3&S*tMibDBG*)Z`rl@Hq?=R`{lb zooz2Ov}q6AA`TkiLpX@1XZq(ocJ0ag$Oa zUgi@_rJWF6rM}hXX;3GyZc@dFf+z>LcBiHXWuT>@9g;xGsYLvyYQylSL_d5!}z0mM1~3#*SxiaR=FTkHr+X>I0a$ZD&!T#S?qW^KLWWh1r~>W8TW3 zsl2vy;9qq5*v|LWl^R`^loH6j_$ny7D!OQf+ecTTbw43vqs=Eai5JusitDtCe$3v$ z;6O7%aP=PfvbpCUH)pyY!Zn4I5|G=7dPv{gbY7c){M$kCBd86>`qoVxAPWYt7}Vd+{yDjdXOflNN2YE@Es31IJdDQxNf4D9Bs_GR^n8Ibz2Zx<~>GxlMl})6I zyp{B&L3D!Cu&TqkUz>*bS>j-cHhgzP5ETlaQ#OiZY3A%3LvqKztbQT#&}JMYv1{zZ z1mHhYTHX-c>+B31=jS_R=9lX?oeSf!^Qc_56;yqyf53N&-(4Q97q+F3xD=Tki*Gsb zX_WQjCy2lk#TtGIaIVmpo3%RUscu^`6y&|%(k;d?cJKJk$mnHjAO`aXSW;=I*H`^zaukfdfE zHl%qVxepG3vOUZ1zmUWaFxt-}Oft9YeEZn%CDgeklY2Nq){#U!&A!E8sGFjic!awibMz`Ft4F53#p6TKQ;9PmNP59`G zt31T<_aY;_S<8u^m%@H1IgwnBTZ&c&?uYTWb#ra`_}&-VGd3UFqaV@acxJbx!z_rU zcRYh1Ak#K{7ED(5ANSk#3^M0woxy5v&h0pEz%Yvx^#h3>P+m{o6p-i1g57@m#s8=LGIDmKiZW3on*PS{Xd=$QVh~$n^d* zBXaiI?WZGTqOc0UL6XyfhX{u6x!vWbiol;dI9%3(C7F9aMzmsYMWNPP-jJ8%Q^Xh58>T^5meMm8K@t6&Y8T~RU7 zlTfu@tT6yzE@!rShmW4qSF_tGMdbH{3u6ZK^&yuMoLJ}kIzmR9XgV^bsVp;S%p5Fwt&;w+K`1Rw?&++a~p6Ns9kKvIVIkU_5ypy zo={$lK>)d+5u*N&eTO%}z#>%{t?Y%Fc#tfrOuY%hcHhHBg;|Y zV$v(H!LEn(Vn6*U`-uCcc(nc&|oN(x|SL(cMs4#yW+=Aw@LV~;<`&#TIO9$ z(+E4`bE)J6uhsRHongAYXqabn@JFQ=ghY5s5FdN+oJP}|@@0vh^m*3cl|_Dw9G~mY z(kueMB(IyJaoLqIqH&kI;Y3SMt410}E5c346;MJk8*eeFjhz&q6A#ce?!;u39(df3 zi<5M8S~(LY#iXjn#U`o3_p^JH!>uJfvK7Qf52HPsA%trlRM*WzPhhUq1PRygnppU- zI$D@Q_1#v&`oKQUEgo6N?I`lkZG1rC$2L|*glZv&Q8m>)4t^=kTJUhLiOJM*oTfJP z?(V4E$o#N{FH`Sqk5$n5qU$x&11LPaCuI%nnLOeM3Ina z@bn54V?wz}+xz@-SgkrrD8mA$fDZ))K@3PKJU~!+p8h072p+d61dtyHzQ2oqtgBh1 zRR|>pjYj5&%bxRkNogjDAndd!&!u%<4Emf=MD6A#RP^o{+w%^~*BiXqDQ@AMz02<1 z&DRR~9}Ib~zc^6(vyv*Zap*Pd>>nJTDXYI$p@hQ)l z{$xnEC)t+oVq>uFoBpy{2uhlT!caS2>NUYSYTx!_FZ z_>}QMqXWhoJ8(()022)o;U$EZ&yu*d{YSUG7DREoM&rW!(RL?|6$L*t!e;$iu4HoC z?=}@u&tb^&Bu1a7<#@#9?s~In;*-Vx_*a;b$N4xen{~No-N@Po{_Xb1RZQv5aoE;8 ze_2HOtGPx?i~^Dcymmxl6s9W$JQLLg>2QJe6bzOBaG!XP)h6@ZZNzEVi^G6{ra zbbBM@;2po0?F9TOi_s8RHP+H$9x~4hFzS=(psDdZf9>ZUAI46Vl?%VhQFX~#9-j>; zE^b}BIvGFR5FT<`WdFS@0RvZOMG-fm_M3_%&%M2C(3GW1Ny2zBx7hm>w-YB!2!}v0 z#rNVJ=qB%T9j8r{uPUL5-N4mMTAz@Zq;O&HyxT8k|7Ic4X%LnL`7N%y-Vq{bDewO* zPTZd`^2FubdE;FcvUi6|f;v;iM}7b-SF%=gLBlR>l|HNg4m&98(3kA8qn?u}LAkyQ zQ^@CFmGRmPp;~ipa@k-VX*rHucAmMvX-ru1^FMF8ENxzIu%^(wZ{b`;0p3b9Y3ZCh zcc>n#sTuj9sYt>rm=In*W63~k-7%eM{(r4Dgb+ENKI)i*<#x;JCICt(h?@Z-vBnlz zqgdS>Qi^7n__gG7PUFMnQH+%UgHSU%LS!JY_+SBouiwPOttZQAbxqH?UO?gLU#qw5 zgN(nOLH81baKaK+#!~7ou;L^)LVmpoB@rhzvAXc1M=bxUu{r0_esy?k*q3U&coh6&wfJX6@%M!!=AMAo&Yt&mZ-k= z!78t%nuLYxF7_xPw{-g5`x>#?|2!pH9ztCbo)#t{&xs?`FoG!xI~AWfIbp{oY=m%4 z=rDP#DA!`J|L0O74JZG6b~3&IHLZ%IsBh11k9Dh6m^tATB`M=vS_;sF4|*DlYS8$1>kV2ftpqa zud}wjgQgi}BvYolD4Yrabb^hP7R_z9dU_?!GSB(_um+e4FhO23`@@0(K2ojoye<}m zyr9L(yPe4Fm%K8s?{n3Giv5v|hT$cQVIp|}wjf+di+-7bj*cb6SiY%ERMrm2+s2z> zd(!NJnz9q3c$AS*ya-96#%f~+6iYFo-*}}e!w=HHewm)V-KjG>9vwma>I9u)miI(J zPg9>AhG@0U5yHF_;PF1r$lu$(c*rKAP@Fkfr@29@L&X+kqw(s$V(KTrY@Y@d9edbF zR*j&W^D8Bol;j)5$?h6SaN3bRRR!r5*|`sYIpWbqOtsE{U>6gB?>%@1B(I^wSRJJk zYWL?y0OEV&!g|>0j-B17hEa~Cef}EF{FisJacZvBs5uu`m;g_>M>Ub+^qA9q0|So_ zF%?e~?h~rkb^_oU84~~#NeZ~bj%DnMOH=sfv+nBlUOYZzt;X{r_xVqWWliRGUM7g5 zAmyDa3gRm`FmjjF%j9n}FU-z2hs^rYBG%IDrR(^6|SG`=@ zns_mz>h!92W!M=V+gS9=1iw((#|cztuN7sN8c8)X&pn5Q&-`6!U-(X$*WdWP(DBi; z^8Zs>9t}CBhMx;uqFf>3Lrx07AYFDr(OV67?dngcKF`?;gCnb97A$!v2AC96#OSdB zhgYyAr|15bu6Liewt3c*TnN&$;bJf*{IHy-&&%f23y^eZDEQcTy;pb$ZmIHW*B{2j z#;6^gjsOupkJOQ{>7k3NHS4AoE_f1244k(xVTbh@`DWX?t-N?lnBaCurpNzyOIJ1G zSHV@%boFsdh%1Ark@b3IduR6oq(*rp6ks9|DxDHI8*;aPrnLEq0;2hopr7UZfn2rF zR4IxtlmT*Wg!mftvuoZLVqwrk^ah4KL-4Y1U>G>S-~oBe@r4CjFr><~fMF$*?EwS% zUNj1n`6GFp3CT%gzDw*q@6b>{UgSyTWScZq_~(O}-X$-o#k>+z_%vz;?4V${seHb- ztuH7r@eX$j?l`Oh38$z{8lxx{eIKcFTFCXCN(xm1ydd!3 zsu){-uG%mA=^iV489_>g@(KCJhyn3Vux`e~#BU29wxX*bLcn0oHm#1&8 zry%nvG&cT`B8U>;MfHIh@bp=vZ=!%nK=r&$NHo3vU!~p(7PjtskDwpTItL8BbN!i@ z%=L5$Kzd0wx_zV7zTAqd2%O?GX?-)$LBH>jhZxPzBWB9J!@D&I{4enE(;YUhK(gi5 zo@ZmL3t{@*l4l-Bf_#n6>`x!FGu$dcWq;EO?&|1Em@;1o-O zPc7!Q7B(FIs%q^G^mCh-D7U$;6@1VjNL8y(BZdp?$qO&R_J%Es%8dnHfs9tSkniTs zzOG)NAN*UcN=HD-v(GQgGPc}Ats+;y8z(^%--E&y#6Oj~BAJ-d%G&bxXttBc| zd5-pKqPs9_BKn*r@A z%UkcwO?E)8$~WuV(oHDt%c@?Mu=2OQE6x99KEqwLTqof=@Sfd&Mxy4BrK*>Ixc}w{ z&D*Ar*5A5@3X+7hb?p~Lg>anxT@ieauU2TDJO9UOZpIt*`hN~4PkJe16v-39crfN^ zPC~ihc(Q||5qxZ8Mf+}RSA1;uC{W==CI`$0H~;LaUabpAMarS8Y!JGCZeQ#3@PYeq z{%Dz_AW2JR^T+o>4Y@Muk&9P1k^Ir}dF&><6PLK2-}p4H2ZjcmtJxR#!+x#X10O#- zKmPo62w>x#{=k_0JuQ2`!Vn(@yCvE_Q{%l3ZH>}@H6rnxuLmNACwKV0JqFMxbs`R$ zjTj}%tFSdqwYUh$#?qem82tRItz3;CWWn5muC-dXc<+gw5xF`d4wN!&{tT$;@jHC} z9BB62cNedp`H#yaNcsibs~P^tmJ!qXl*W)}?|gf^{8ZenyZM>gPbIEXSPFOV@E>P! zpQ_!i)>mZsJ>ncyuSl2+Hw(XWf9Ag!uj!hZU81Z0+;btVciS;kaU(;G2FN7(PU!}p z)KKJnuw2J5lDjZ?zngc#?-v5WfSOnN0cR*zB!j>7U6b|zDMII@^H>b_Bw?|Hf*aFB zl6HrGmi)ipqQFsL3l=+8xRD|dI6h)pq_LueHuMX!v@PH@4oH3VMPJPgGyWgy%io2# z_8;kG6;c?ue06=S&19ADroN>~TMbgI#4E%(i#YFJ`HfWm`g0Y2Sobf)5z;Ng18#%f z>DV6LUzF{(7ZjDuiw0hFVpr<1+_jnVWK?<%!%`+!szwdUef<4EEZ}^5pwF9|OG#l~ zmo}PAQxDVj&br@5jNae^pwYl1MlSwUrx*zJ!*}{@;Z^L^d z0@B?jEz%(k(h>p+(hbtx=>^hKq9R=)CEeYrC@I|_-QDo4egFO+-uF20gNU0IvnI}& zImgFmlv+vUwi|&4hDU0_XRR+p@Y}**Qf)?YZ4|Jk$PC+8qDUnky>lpGz8Z+#KgMvO z^V)=r;ydgh#NRzR`y$HLb~+E{nP5+zHI&yVnp5hRc$ZDdQex2 zR0iqhj5RmdpO-^WQ{G1U0$aNKpKnPJK6(f>q_@5R@{%31%X_5W!57JlqxZ(vJ?o$6 zm}3yxm0XcgahakYVRC^+VCN0Q`e}tMp4CqS&MWcA2o%nuT(&He%6}&8JaVJvci9Vr zO0lpnu^fP>HEX>wV|tHb(pFo?wJR|dV{tLtJ9=B+K^inEie_AbGv5eyqBpb+oi_gy zPZYG0noNVUGV>4EO%CgYpsJjn;#4*C_h4o0djG|9Ho?rcoYJb@6+fG4ZSq#~SL(%6 zjozOBcEtI|zI7!zn;X}tUb?QV^lg~JKy zwo{ui;uJFtsyCj!uM#k>sy$D!=P$bS1~ZE7U3@k39Au$eii}+$fCX%tsdJWl*y)<` zJ;Fqt)VgK&&%MNh#|9SKmv<5DqFeRdA{n`E1u2-jX?qVsM9lYc6a%+v3m|wa%dmxg zfn6~BxjqgUoa|j$zJ8U@PoCWF6lY5^$d$s4aE^9IHr;evu6=h`<|r<0B8Mw?3{^Yy>ic7U1u z$m50iN2srCz@JOSq$Bn-L?7U3CwML_jaZ5kCU+eJjA)UyG_p^;pRSWiN;-e*yAwLe z&zGuvDLsuK!|Exmcg`(-4c0U5-yPo+W?1a++~3NtTden5;2hjv3AC?QITl@Nz<125 z*W2GIy|T$sscvjled6t52$KOQ#HrCDWTR*9-C%%EddJMuM?HA-BtXy0?P%A1&k=sG z2{c~mspCe9Q;eWfoRR)umSi*Q0;%fNR4*}GxGwT^I=QegSEpuEPwnG-beb1bER1K%-3w;m@UC5 z{T0I8{h!oLgI|!+xt}B&{Z8_$uivHF71f*?8TS9cIWyfrxIX7TUkV*{zG{(fxVl2s zHJYs$-P!5BzqMH+qKg|^dkVeFe!^|9CtUXX5c2 zrdDmAyhPXZy)iHsWaRK7BxB%c>VNH2`H*7C=j=y7bu4EVcmC*Sb)MyZ{rv++9Zi?n zCa_D3O5%5}|1cdZQ&^+N?TI!}HG&$B8ooMS+PUsWd|&gbPtHrhQ4Xo3P82 zQAX-?VEwgOY1fLTjj>1=Q0Sj!klap-b{>hD(De`p0rEN0B55%IcRvFj{KzU{snwNIPN@tY)m^7+@C_g==mQ)=xu55)KG{TzGW zx$YO=?&c&YGJb-be5W5_#P@ZYzs8$OdKS#xk4-6jG7O6jehFVJ|67T`g0rFdh;3YRY0Y1HE-|BUy8RO0019Xz1Rs;Zz8;k z`W%*1e>@9`>P>X?lWv_{{Sk?=<9SMI{(AWDY?YDF2sHouk(m{7Ckpw-rmLMEvKQYe z+at_(&g-P*DouWWsPW0h)BNVULO%b8ahmF*Ibu&)8dwQRZLSYKX?rePn`jG~WIyfc zIhRk_-Y>;;=I~?LUmNVwb3e0578%T{)XQ3GyO5DAz*I<3&WG5)RP7mO-`0O*mL8%?p#3TNirxNPb0y zPHxYn3n!|Fi&uq}EMg2ytch1v?L`(`$GmayBECO~aH$PlsdhU__gK9^Xr%|GemCj$ z=lG`C-$7yN&9sRL5rHI9C(3Pc@d+6<#13mp_5tljk{2<3iw63FEIcptFHkY-oW8$? z4Yr-5pXyW%{yh}9n;G#?MZ@yBvSz4pQxoyE^GalUJNGhyo$q`;qM~7dV0Ha|{fB?6 zPHM*b!0|pP=1ddQ%p0{9AfbNOJ=@^`^I8n;FLiS}LxP!p>v-~}nCQg#i+syFD5Ro2 znh>tl*^#*5q0RT0yHA|PnqY}PO>bM=t`>flnoDZmrti0 zIW}n`jK1|bvOiVrW(_CDX0)~yuYp5QKR3emhz2wN@9hA!Z^iP>z4)A-hW%di4)>!a zb(h!gR+Ss^Oh&94c-gaux1f#V?e#i!oXoDE5By??hs+Sm>00xKYEw!0X%ceH$Z!0#L0)|swRCXM;l zvw=GR0C{;-ZNwFW{0u>a7I2tuvmr>>nPlN#8aWtx6y}^~%-oFOFh3oH))Z+b*pe<` z3v?@?IhAR;3x|K=K27|W=h(bmAo_hNkp-!3xUpjtXanVgWGXfj!AnzM%}|8?o(AMK z;^66(FAk?lnvqt_CP@sx3kY)XTg0sM@@=j>6vAsU$B|r?+GyjJ>f{zcd47-FnhzJn zF=h++b7#KL!us&{t-W<);pFH*I=iY{K*+O6N7mv#@i@Js)US0;=^} zwAq@r{rcN)@ux^>{U;n>9e5|AOHPDRY|mWwmgFn?7Q8LPQP0EKFUs%z)0&9j4kuiT z;eyTffCujvAHhc3-$F+P9=mv?yNewkU^7dysz?{p9lm}c2y6z~iL_**zs&tcoW6JY z04&RPw_6_aV3Tw2sbBx|a{U+W(+Mp@JC00V;O{{(f+!<4tYOZV5Vu z?~IAg*Rl7hMB_xh**>lee7y=H=nWXNkcFm+G#LF7A}wkAcD&{yQ}XJD_6K7;;(v1Aoc`QwTJtQQ1ut zI4PP>KH=Ee%8&_FMLY3^m7uC^tDa4Xh%8x~m?%U0i{1_07xcW16-uNRI70;^p1P*WcQttq&`PZmr<+ag;@W#}^{@X_>*nIjbf+6t@9#qFIkj~? z;V`H!Ghnti3R@HxOGrzrS9A+&rGL%|DGT zY1x8CqQ2C5`$d~sxQPBSO}ul$+S&*Vurnf0{GGxSHlfp|Qt5h&pC=M*i9#Gdr^QuUZ&%a*7*YAX z<`-F=LvX@iTIyW~&AaOvoV$0|YqKX^N>tXZ z2w|fd^uLhtcg-6a5eL1csWnblb$XYSon0GFWZ`(J_B#xL?Xk2Mn1(9Ww)Q@Ikjm2- z&%$=@do=y42|CmrUh#3fQ0eT8DoY@6+aT2DJ;Pl~o3?@}dD{RWhdE!Ecj9Gjsp3-k zT`D1oH0JHXbh$L$gEF=3jz`W*PKanKb8CLj^%!#kpvuX8;@chXwYvE-^JCE>f1!<2 zs1}c69n9qdo{C$O)wei*A-)<3n?KwOH>+8^tLrn=`k*VHKcf7opyb5+a_6U~k+|>C z+$lSiJo$_d>q<8L$Vzr6vq-~0Mu@ z7yJRzqE+t5pqlO(yMch}*?N(ykC~p z2gTuQRRL7j#4k2#*)H;p2gw|dv+Jt?x1xIy;O=y z^I!V1LjiD4k_I}Cn6!eqinUEo0c`>jx>w=Gw{xv6yrRlqJI*g;L&H9`aeXs-x%A2b zpWINv8bHHU?Jg|%8Iu&r|7_22$6tYn{&_iuJ>dfuuBx|6ls?}iw9TGk|)CX0CakGCEi< zBvf8~I{8|vu3tLFqcE*wiQ{S3t0t3;6^Dk0-dTvu1Z$dL4S8GqA~Zfb>AO2SwZJbb zbUpw;Y5w^3x*c6;jybX-Y8-Ml?@K5w_X zQ7%NyDdBYchoJKH*M?E10&TXOQf2H{jz;{^@8v^!BAr|n^;{#H{8_Uj(qu>Vuj1%u zxlLcJQkW4@zW|ccavixcRWIOi1mCVCaDFy@;BnveLi}ObK)kOY zq`h-IeqN%ISpXMP;*}dRg$Fs}f#k&2+ z*M2Etr4r8XekNPyl^aXF-)tiCw~5cYLKukF`iu9doQWUZ54wvNT7AEP`m@n_Gpuj_ zNMUTP3=*C_!BLN%l)o}F`)9HO@GTFqgALGXW52!kl(XcA#8JC|yy_&c9(UJUSEjtV zM!%mA_OyrWvCGv+(UGF$j5;gWl<1f45yTfu+bl>!IJCdz?Y18zJo>P6NaMS+gce`~ zzrYXJB%t6AScPF9z5V+hN)4w6I@9kIaDXT*+L#?|&d#_T8TAgvZ~QrHrDb0B*UCVq zs5U9M=ZAf==Nq+?*Lvwu)9F(up_TvO05{;iGu3^*wfJznKfRcB-N_*&0`Kyu0+<&>SwlC#R zreK+)o7I+uDcdmP{hvoZwjoDFhkeEU#GFa|OZOKlvbIEz>Cw&xzf%uks2M6PsNAtL zJrk??#O-f{yr;1LM%;A&9ECXyv0KWib2+Dw#$dUaqkWiPcdZG)+*$<8rCh2di1n9) z96DRkVGwc|6fN(%Ab^$u1!C;_km)lzdq}Dv105iD3N~*_5o>mO*TUMJe@F69rVOST zq;zXnkg53~U}krtCk~Ot;O)55>GZ;CJ3ny0Zc=<>>n`h{>rnFXZIZFj`c`O-q2mb! z9lnC4N}vAWo{^)DN5c>6>s-!v7cEsaQ`UOAB9M=wR07^bmxx}fn`@p7nS(8Y{tnL_O1fM4_L)jfKRCDKlNi3cXM z&fAoM!OLd^f3ark2eU&d!`FZH*of`R`u5o+CL-)1De}&xj^Yy29;mO*md_d8tdhza zdTKHZc#A}#*K5DXRz?#h_&up_u8a>Saw`eaEfDlkGf=g04)eIKwiF94@-1d)ZdUpG zmiQYZWo>*?OZabi8Fd@Jm-tOCgflz3iwgfjkv@49EC%iR;auqS(~*ODV=V`f7`)=D4AwJPQ8xw>~{o?YE(0(6|GFKRYRWc|~b z#OFFW8112Cn(T+MOmj9Jx2}Dcu<}#xsv$(l6igQHVa$1+x%bd);gz-kg0-0lArV)BO3&Z6dS&T zIeh5pooS{W$!)aZ0bLggqB#sqXr|%f-Nv#SU)ZdWLv^!{rrLH#CR&wO)tWJ&2ktnLfsm)-8U%6^zpH{1Q@>316)c%#J#y>4 zCXPZgkg)S@h?b+{>Oy{mNw>k}-J$eA3Kto+n8zL~VCXk^#i1FwVJv7Aue&0U#Ufov z0n9{6lTAAxXnIN$yS=RzyWKj=J=-H6y+sg+ihI<<7ciUJ|++~C`6z#Summg<{#cXy)wR; z<)(AHty(>lyFx*y0acBTow5J5Ddw3R(CPcOa*dN=lT}EgZTcQS zB4A;p&MwC*#qA$o{kZY7M>^^BYQ1ij`u<-d={`kx-u_CST&a04 ziaAV_Y6=vTKei!GJUkP`5Y69OIgMAka8ir%a|(AT0D*?RzpG^%=aGfX#`Ni4_87ze z-LA}#%T9A%vi1f!(%~#f*7;sv#cN1Tp{ij+KN=qx%5Y=$p9v6^vngSV{_m2|91`1) zR(!<%26{QXO#ASfF#9Q06Bi-5JmQnH6&h{=%Oa8vThXefNi%L%($}-;&CNE@ePa<}-_^;C z(cYsj*U_yL{_AJg-5gVUm2}X{u9pMLpOgUt{$tUqg5UMu-&|f zC@JwQaNM=S>N++(fr?D;d%{$Qh_KnRmNEag&JLtTBi|;?{;dJj5g{ru18LQS=HGYA zKN_BkFpFhc^8b-%;rr0_FrY2@j1jS~`Ly=i-dIk3C&?mM5gYD+C8F3D@TE1Xf+%@( z%v}P}#>dh9++EMo(Op(rsXOrVo3SzXVz}1OD=6<(!cI=nD+Z4Ldx!RupX|3nCd7`i zIiC4seSAv>NzfE)X5YJ=|4hh@RZSsc#_5lVi0UIke@Hmo20ppHCA}qZYmGzJ{q(Zt zLnq_d7*H{_s8`goOQkz41ejfWYQ$td!2&Y;nHm#H6A7hTF?iL;xcxfQzZ2mUjE@ ze8#bGqlEVVmL0^VefwJ$5H1Rl4!i-ihx+cre}6lZMOgoQ^lGylucFE+iBEwV?<=wL zgx_=i$(cM#tDP;1B1dR*L&&qFKD+#TZh;U>ckm;h=bRgF?#iw^duKLh0%`vzV*@u< zYwKZb1NWzl0bKbLoXP_V39(8_;Xgsq9R8Efp$OuxKB?e1o+Jhprg#PgM$=Z^(vcjs z@dDJbX0c!GYXj-?Od+r5d}|u&_j)Um*RIx780RjDyC$H_tA9(i_pc@CCI9!Z&`A2~ z_pKX|E;AR#uq(t2p@L!mtfM5?0Pu)$ISkzWG<+{2jcyBDbZvKDWO38qvaOz}`Dt(QthIOw=z}?mc32 z5Rh}xkF8sW+;}`wA$<5Zpzr>Pi>aJVYW;br%!kdhk>p(6;;&;Aj@NFEulS>Luknz? zLB+gPFfMnWaXH4KUl1RlVf!(`}1&dEq>}4+CJUb z-w%6T5Ug7qs?r;wlck0R8jX`6DO^ zzNx8c+hu3qW3F6Bkqq=je^{j0-(6~$KzbcVb2}|Z2aFBASn%arG|Z!tZAR5ounhnM z?e}8M&slIsNO)}DZUoe%u3FjJN&{FEf?o^q!29c*S3ahv zQ{&Q^4UpJN!5U#~M@|#IaD6|QZc^gsaF}Y|OM4`>7rbPl3z6koio`}0#tLPYU+Iu2 z0u}xJ)Wztw{jI49(IVd@m}qZrZ)IyMZE8vf+RJ)plmlC# zN$W^J2^k5hIc;ohA1CzVk&yK2WxRd+_GAC*3uR@ul(PKNQh9rOc0fuX@B0*xOBEG$ zeOLHw71|@~?0oc30kAt@VyuEQF}Jkzo&CDCwe<>o*pDASaA{~_0q59r(xWj$ToSI6 zdh#}L&TtIUt5nz`oBtN9X1TDP5t^85&WNC{41b%bMvg}}L`q4?CUP;5&A@S!NTRjs zUG<~(W#I7NzriCTBTM`kYiKw)@_+(JN=hm;!)_+(9Jm|x$5 zW`2|5spt>;2*1+TTMyL-y53(kS5Tso26+)Q|ha;$!<+&MJqCtPz|;REWIl?m&ctZ zpWNNuL0dXtPgtr<`Dxg+^SPaU7$)ZwUc5j9q)l<@=`rA8^YQa%6&KrgiGyE5 z3QS7`t^4K6mna(}y12<|2bsK4ll!~dY|8WCU}Vr8`O^VaB}*;J zYvgxcA1Ns*m?wY<=;?WSyN>Rbgd6MME`IAc8O?8NOcX$0TFdchl!-DSZUd24et|Iejy_Gs|+PbAd-{IhanhTE&aW+ax$j1ZXope*744ybcX!H#PYM zKNuPrK>|xw_{=ml_7!MA71A_8XQa8gx$Jq~TJiSR6lSky!x`I*_l@H5k2n8B3WZgU!I>ML=3zU2SDY znH0zd$lnNkifXKE>YN>^;>qf zK}#MhE32HoI-sHok>b9o)nJtcS_)V;K+uO$t{wG6JvXzun&`oU2Z)#YUn{TsOtS=` z3p6=5XRld=1GIEi6(KnF;o)I4ob%o95^NY=LQ}*Tv|KLezKJw7VskbBu6oZOOFg5( zl;NWbZf%pAFvcbEA&+h&A%o4*@yYpZo}Qdw_O9UIQBg&MUs%T&^jby+8F0{rj^?Rm z7jCa7vxDnL=m`ytFHnKs+)586z<2#3Ttg z(R$z&>+0&_Qd7r(R}C7yFU&7e4gb40g(@PW5;);(Y5tz-@qdb>kc{e1}ZEwFAr6>m>5h%aM9Ms1oq3E zm#CuooUXrOV{@||!Kn!(?gp@#L4sZfw2F#~Y{LEN_$1`fC_wcC`bN^n9Do=G;ThrM zBZZp$4qZM4j~Prd@ zm3gm?URqp|a##y07bhCWwrFR4k59wOQm+Tl*4gh;U0qz*dP-8Vv$Eje_X84(wu{4+ z-54#vXd$bjf(>>OT6+o%a!Cm`85tS<*=nbXrE`~xR@$KRN)LjRn!W}rWGq$06Pt*L zs7bI60wPZrho)ORp6iO;R-%KqqC;)TU2Bn9QGs9Lx4(Jn0~SmxEIj=d4(wV8xRJYHwdW(;Wiz5Ezpb&Myo&R>EUrza{34)!r=s6*)QS(7 z*q!t48cScMo2~Zdh@Z|F$i{EMn;ndZY;UhHej(fF;p&VMK()Aa0X2~@BzSTJ+`_&h z39c8YJd}=^sCT8m2D;R(ZmkojqcE!atos=y7nh^_Q%peRN^yAdnN!4|om#}+#Fm)u z@ne%ObzDsJ^qm%IXug)h$Vh<(%T_PUCwV_FZH+Dh8`!sc6sMwPM8mnQ`9+bDoSfXt zy7%kH4^dHFfQts=c3o3Ws9?ehdGkh76G8B_yDU{WwhSoxI#qmu-Nq*%03-xaCd{cO zq-Y^hK=Vr%Yw8tCi~^ZH^|yRgI0hUeHw7T%b92$ZeksmZ*w@o>a4031+@@*=hoEKt zt+>c_fccbB6OahZI&~JkOnElPK^&Q*TkLVrdJo+?#w}gN)j0+Jmpefj+%c8e_%=-I z_mHZyY=~7AIie0El>;R8ktql;*Tu)Ok+RFnox5-fW$2tYB1DbM&CR2{j!HBO<=Ot7 zk%-vGB_>+A4;S-0n+%EryMccS04io&mA!y4S^nV*7#ppxFWvBI>f+SHqDa&ANrZ} z!fb?8b}#3Mxwb?C0m%bhVjQZyHcNhgw8P)zQGwf~t?$P59Gm&sIL3=~DxEt+v93>j z#ERP&1LquDi%peU=KHtY*!jqVCm@q7q90c{JM$M_Z*jvTWnpJXFR+%MrrVGP$x;)L zN@tOF5`WM2VT?9=@uDxkjMZ!gsBJwi%pl<3=@G)u1h2oIDJUv-R9TNwdMM|;OtH{< z`_@+`^r@}MKbQKcld8Sn4XbQ1(_a!3!@=`!c-xF?w2ZW9@evq5z<^=_83TePV)a?p zcts6O&1u3f(xw4})uceZfcL)0tC9Ftr}A+kP)!LrKM!KP%h3eFBg1e!7yDgr(Qpp? zX1m*d#Xk+}lFipFe6!%(OO-P3vt3LNlaf4O=JD7c--KDsL99Vz&lSzVtr64Uk(?8Hax#}DWS8xsLnb0{!yn$nGw zA+a(@7R@_>@cHhY%$SgvY7UNffHT)EwQllV2@T#&I0jaovKq1Kb#3eI?(f=#ch?>f zGWehX;jibcb8iojZ7L?`C2H-jZ*d&_HUz7F5eSE72|oyy2T}Xm(>IMO{s+o!VcW%X z`hFw<_edW{pm-WN8`95>v#eao&1F6=BKU;EqsOGz-JG^{lB!NC$8_bCP4gFTmvo(R~hf`!McTn5C9Z`Jog7gsf zJa6=n_}zES!m!bsh9F?v=f_R+-PkO;lyLDno@Wbia&YkDldw`n%3lzXY#H4wlhgx_ zk}SY<9D`5tRI@1YazJFi=&|#Xw}+!&)9cC6)&!F&1Rg=(sweFKX8;Rl&t|qolB^F} zulI1}uJmv{T!1&BD^}V|S}wnVL18K56OWXs-i&rv&$s;zou4-pw6WCDnFwm;4>YHM%P7p68_*Hr8vSExv?7 zxfqV0B`A76c;YefxT_Qid3)SsVu_$fVXRudwC7rXK2-%$m>Vz!%;12v_G4 zo_Ht}*KXKL92io854YKJrsDAl3#Fs?$tdP*BjhEb{D`hnN2+xopXo zFaP9o6GnE2hll^?DX{Y7k8aK;?^-)K4XqYw`9Lrvt~x(u7AaZGuQ73wy-2Z?n*nlz zrfR**pZLh`xCMyumNP6lLO}s+>}Tz`7r_*cP24mA|Bm`O2Nx>dMtZFNig!FcJ++2g zedRO~_dj0yWBdJe!EaiQd7qVN0c=2w`+i@m;w9E%+b6w@q|DDBm@ss{@_67iJx2C% zGgybPcjwP+j50F{vjXNdVGlia-kk!?Gz=q$|Aptno%TRx+J|%ME}SB{+EVh|9)=Gu zg2g_(l*z^Z{#3^yib_dqoFq|P46q|+PglOR1YSQOdUv?}xalvdxrI+@XTn@Hz@K~W z=_$|y^lejSBZ#|zYV{DxI3u2R)r8C^={Z7K4Ka|B+}z#4oD*qUTv|RpLWJ%5^ED(i z^eVN@NuC9GE$)I{b^t&FA%w>t;d(`N_0MB!Z2%20fBP2eJ}oIJ0(kL}yhy<71YbaZ z11cd8EpE@o&h_Oh7C4N(bGcMrp#KRTQ!XJP;qOb}u$G&4f51vG@R<7Rx!~{lLf>ky z<$AC7ncbw*$F301soS&8oJz-c-(QlGi=}M6#cdMh8r~|zw)eSU=&!hXE-_J^5@`k_ zd|!BNjFUiS*aNXlIB4NxXmn{W68| z^6q+{y+y!zStc^%LOBC7#Z|>_O2}bW5a>L>#Qj~3r7QRmf!EA}p~xQ-M-&itJj)w( zI*QBb`_xyarLUaC^VTcvL$kXO8wikg$ID19opNfR5XRmq%@7AAH&4$-17je@g;}1M zowUr%mNFK#;CR7*dkEwU;HLl*p*IA+D!}c+KYTcfp!4*5gbBmt#%w@6y;y0-zS;gJ zsQ-mQRx%gUMFELPChUfdBj$jEBea!idwcu3wE1irG##CciU7x1?D9upNphv@Iy7(q zV$Gbe(9lre>;4ftpRL?HoiJi{UZSV;KHtQ)IC5VY%n(^YLW~rnml8Z@L}9;*@5OGp z_Y!+1PI!=K`}+AHkvZ%Lt8@m_UI6KWvMmpG)>Ox;N|4zpI6?cv>q#FokNby9%p=$KXg5_1v3%66y=7fO_@wmQ(PL;HmO2 z4B_(dU!Pg_C_LS%!~w=3ohX$vJXpXy_AOl{}_0k-msR?Ya9LVu)5I$8@r@KHr+y$+H1?mrAmODGh5x4XAD z#eHqpzEhu6LrUs>XUN&kV=(%l>zlLfGAQ{<757g*|Ir5kO!4DQ3gG`?a;j8_Eq8!< zc-Pl+cTThn49FmGBUaz~`t|F#qN3lcrxJmzJU)MYW}!?fUvks~^H(I|r%`pJk9kV! z*OCR!_7|Wd@G_x{u8@rID9?;NV^$Bpys4&R@3{Nr13LHvvHsib99nKD%dpfEJ|uf6jX(vNJGn$iX0J|=<=B8@%ln?< z@sRq&uyd8oOr@CD^>C)pK6nP7;i8WgSToPHV8e)$5^s|U9pW0#Kyu30jAdbEMWl!5 zQ@>PI@s*}WTZ>+*+0|xyJbU&Gkp%%B&^f(;z{Hxmjgm*Q za0I|#z!!RmY2A@K0Q-Qd2+KgD_#77(2r>jZyEFRH0Kor0_#lOdrDw_?%uwn<>PVxL zX8Mf4J6WDQIsDa}1Okr4MAF8yF`H@xY&JA8A;88!4~DFOaU|ea8oRqcdQtIOM2&?& zdwye`#yTDsIch9wZ{9p?HjD*70B~>+*gj{CHwDod;^1UuyYmSL!SH9VXJ*q?)(#4r z+w>=JC#z|-wu9+flv#~tY2l>2y9fNAKd73aK|+=P8XC7J6RSi~IyA`R7o|$WbiZpR z?D(Az+l&)L()ve%+zLO4LYg3$KALI+PLfCIlJ8|4jC}8Su|iQuA5Ft}i>3_K3Y2M$bc3>5{(#oL(NT&A0gmDtWx@}f7aH5PeKzET!~wAE3ytE*XSr4z^nMeJSVS z!o!*66@w4X3E)Lj&G!gAc4h_x91Hl|zY`NH0J8ve{Nd8ers3cX7McVCVa8^!(_)PW z88;b#-GC8jue7wnwzd#sU@`%y3J^8$8&ATgz5!-*ldM55On@J80odbvUS10H(j^m| z{42bntQAJXy~h5?ThG7)^2XZc>Du+vFdP%LdZV^2Uu&87&$VGb#i7L)OL_K2`+n=3 zZ`xTnqGWo=U?gzC6|-EiDiFhbfRR}w^k_50E8-Cm@$Y=8V2cDmH6k&#Y6sROFQ|Z^ za#m@`_8`~78{a-+C=1i3Yk+zRb}uBAl+QdGke0u^mJ1+MGiX+4Qg7cIB`fzgSS zz^x{gcz5Hp@n^E!45aB0TLdQpfB?WRi~vE(UgnJ=0>tR(=x4p-Q>?pS4h#Y;oGY*j ztEqQoG6T}NvAun)O=Ju3Xn-EfP?6^hTS0c3cAhf&u-S$baNt>WlYVQ0X)358QYKn8 z#i5}n0I75MD~zu9B4^}P4S?T_!N-J0M1VyDY}mAzyouL~?m_$m|E7n=H3=;@3rK1x zwCzf_332`F52RLhdsE0lt?R*X-O6gyL-RnGDS3Qb5UB3b)ooP$^6oAb3G(`g5==n0 z4!pp0oJ9=Sv40mEN?Qte*fhS*Zw%vUaQ5H9T7xlgKFPq&WhN(|AgD@xd>Z;6^Tz<@ zbh%(jtSO5dNPY+KfQxFdj)ixZgLD|n)5^O>QYtF@Q-x}Fk?6So)lQRFHI(AOcz~Vb zXz|9x#@05dX%=fjfR1NH^I=p0`yMe#IzAcDrGSW1`qiu9{Aw;gdO~z=`)R(gPv~GK zr7@WI2e7UxJ!BmMlAaY^BO^!Wc27^wS_1?oHEiMxcS>qvW5h?mjgF4{`N!3a<5b-Q zWdA5P%;*EQmrR8vm?v#FQXYQ9Xd8Vvl??mxDRKp(A7DOb)a(AYK7p8#RH98wtt3V7 z6x%7+2sOmXnl}JQp0M5e*Ww8|{Y?34#&=Fs`NKlP)!fuEIlvGh4!n9~@`wAY` z*}a@+vGD*y6!DV2b(tQ9yW_@8Kf#YchQ{1~~svVwcJqX(RjM5_cEb(pjRwl=ck{){@3#k8V;DlK}Mk^70Zu zn>`3qm-y2uhusyCaR>2q0rSyWIEFY6FILD2NUf$66986oZm8v!w+Wa^i|%R}y7u=E zA&i|Gg~BmYV`CVwJOC5D;6JIA%MJw7&Ft*hz|`2H(D3S-nixeDE)eN}2r^W_+4*co z2aK@t`6u#(i3tUy$Eq4U6jL0)dJM+WYZdo=%yp6*4%19R2dOmCoE{Pf7a75BtHh zAuGWi;B#ZvMS;Gl8Xqn&7*ei}3DkVC=!&&|6TY$rflJ0Bud%z`7uic2Z6i9A3u!i>UXZ zfk?=IgHc!g#%@_FI&cC>`enThc(Y-i1oM`VQ4J!a&qv3B5$y|aLYuKp-A6s3>BJ1L z_mF}`!Ow?SXxegM9KmLr&ig*jZ{}?s#Eq03L*73{xlo;{r5#7|>Yi3n!N3f_CIE~`Ld9jhCbnE zk9xWIekYGlnr@J8>Se*nV!J)zKu_stGg6yH+m`Nk3J{^6X9evt!L%POGQlc$1i$0A zT>xKV(_5HR9;cqV4Ru@}A6D2#^!Gsql38%W=l)etMnG0B4Q!iAy6C2wU`#U$A$ldY z32+79Q!7CA=u@4)zP0C269}tL^jD3*XuGxL0Ra~mm>G~@hlPXF z`;bI`as0!EX?bEoSr+gdDtXz`e-*t|_V>RBsa&)KbneRCCHhJ^>gE3JIyEH z8-77;lbFUszaG~wZoTl5)}M59#4@WIqna@OtGM?2w=$?ZiOnZNj4sHQk2RgGbAF+^ zo|pR*1PHGT4aHdhn_q9A+feNGQ-Zojz2;9ZJB@{A;VGX3B2}1p10^=P6F@^SJe!{5rq)xqEjWou#!)} zD+QzatoTaCSW~!6I`YvM&NxoiK!&OP)ixb?P2PW`JU4R^MmU)y5Bm0MX&h08n*NRJ z<+Rl)Ka=?=yUjNn7GnZw!wc;RPtV@$-yJ;ZB&ZIvHU?yf9wopvL~cuf;m-}-slprp z-snXY;11hECi!aq>m{t^sC>y9UWWNRu2GmPg%#vY6#zg&pU#RSCbqw|CFSB$F{;Wu zGd(>7vP3m<&p{Xk@@eLtdZ3o2oOcp9-}8powsfG@2Er0q1(?2Pek<@}8ZkP5uDXEQ zDr|{ckFdHjCLTnhT`}7h8-Vx#;R0|>s+jM%g(e61dsWk4>9A@A0l6Te zWu>K}C6`M(Ef)kJX^=41!(4cw zs@e;xF(_RQZn_5^BZK<4T-A&iV3nuD2!=;TgMg_jW7}5SPy$Gz#qUHDPZL3adV);N zB5+hdHJW=$9Tzj=ToA8*-V%FxWd#vc0A-@*_k$BC1282cO#R|WQdgIJAVVS+d|o8c z!tP@c5eksM0ufrU8Ve?Oz3+~j<-2+ANlI)D)Dx9a7c)dMqC>0`w&FI z^uwWFpjxiSkwytU^2_%yuJ;W16fpody{g{2a%Ep1;Yi z9)ocpNI++smU~T>gQ?+ag=1>0@tPB|AnT}{$ko0nTY!)r=@c*+1lYVuvCja;T2jkb z1vLlYpq8A%e}N+R&CMl7{z5T2X$E{rj)cZPkEwofX@a^H;QHgOkWx@Y{`|~*{Asn1 z3>gUy<|nE_dV1TRSV(^y7`%EF2#Uw*7&IwCMM9L*ztPcAf73HC88xcU>g^R7sEy7?#4VCaCe zFUpye0g`=Yaj!HrpZ%b#6QYbLP*qYw)bi}4*8?FCMesZNd3JJPf}P-ky&WC700aeu z8}wpgb7qfUeI-pcSdlLB?kv|T9P2D_F*7y&2uc(QB$xpH1CEM}Umark{8LoCQyMpr z?itMZXEF4#Fu45ZVb8-Hply5{kw_UB*D)nUadvj@1DWj~ z!Y;H^k6@ro5@LD!w32iHenYA5_>vL~xi#qse$+H!Pm5*?uvkRK50tQh)fyJGnmo#R z9RbP<{~uLf9ah!$MSJM(4rvgT?ve&+u;>(}q(wr+jkJP35%p^&<^1bF7L8~u`?<^sb~}audY4`;+u(;7IYclhEV(&3o0kn2h`2^EEMM-ld#$YlVqT_}QChT2 zTvVMEa+*Rn%^D9fkfmmRrcC4PVVBx$_TNc_%yOjBbB~&+nFk=TTHfRi%#;;iMhP!2 zQ9Tr$56vIR0j?;3`z{st{K;S^hXMCWk5A3iGMLG%BOf*!w6u+HBpKwy(Q>NuQIc?(ol&i?&r&1Y zcDgI~j6CK_;ehY**YT{2-@`A`uBubMTPXXhK!Do{6W6Psn_Dt1RUhn7rWXoU{;c&O z^=9(32(9o|Z1ucbJlG}l3oLg@TOWtHR&7Z2yl7xSnQG=RfBA*q!rSk+O}_gPd7Mt* z1XSor0g()=9AymcAM?lVad1R1M(2R})4-TDotO4kJc9Y!GTh-mIRShzOZK|9HvP2_ zVT<9T$B&syJF81e6BPS!=kkC#WHg8h;lRemc&PmMK_SxW61bR;pR0u>EkPAM^Fflb z=gVJ<*Q*IkIG2`q^8gWJ97_Q2g;dp0N{agCt+!Ukc+6z7Ocg#U_(K0lIw)~B#1Z^> z0$ZYBdF&FU5KsAkf3Mix-;LK}>mR0$@?$zaCJgpNZR2YpxN}thln7BT?8bVhML+xQ z@;wjG#h$f7%^`f)vvhY(2Fo(*e`5ZAV&FTYn!L~s0mm6-F)Ni8_7_b5-FOJhw9Cim zz%|0Pl@IoYi4rY|uTy1R>4Ggi#YsRQ*@!Jd73>WkUo9;Bh_!@E$bW@GnKc-z=J`Ku z2?ya?j;PEbO62o;S!6&&$~jfF^oBu%U1cAamP-Tk)YXmFzw;*Oqhhy4ze(FzQ@^R$ zR9rj8>K}Z@^B>C}S7o0Zy)`<_pg79@Ke~L#I>$3M_kzJoZf6Op8UI^8h4_#57LWeF zZ^DI}dyF+XzvM+4d|j5hu={Ch!`;KOOr$>MnOf$rIJ}Z$qjL>%=29?ENf!5?tcb7$ zCP#$JLiC!h&GR96&M?{mPL@N8N*9iWKOQMv2 za0LHMp3Y$7Zb=ovYLF>rE4|3A7u@l>E1vOSt9*1VUBW;2i|dnGMi?Adf~)+t;zXyf zD*MCLhiyY;ms}B#Uq-VPE0_8Q3RtJ59B;@OTPyS!BX~ZKi2nC9vvN8HSpo)BkM6qg ze^hlhYmM(rW#Vp83&3$8vMK`79-LCH`Rm#CVv zm~klIu4^|+TEXVua1(WCQAFSW4tJe4xM%jQTl&h{a?ULymXB>6fre^4w86I?x&=mO zkCdZ=DHABAZKQC-y|4aWiQ~9Wj@h8Ob{vGKJm=55s7oaPAaB3AEZ8Q$aXb;6V01*~&TX@!@72`TiKj|hl|FuWaPef@zERL?<2%HF zlj)dhRPg2F@W*PNYm#>o*w`fnoFcShpEMnh2$t{)HQ=rXA`A@w+htM8R7`THwHPBT zbNaX}qnDM2N_8uF27+ud!*TS_ri~L>-0GCxDy8w@?pa=BIER!bv`+BFKI%Xx_g!** z)&2E^u;uIv$^SMIE{)Y|mU~|W*kq^UB43BSw0_gEeCzzu?`W-M-`9;`T=f81QE)vt zV5H7z^_qIAK!{6fb4>NWDoK^PmDVetKx#tj<;nS(p3YmPbT3 zblE0q#t9C&|L^wAvc*KHSqB*MT$+8air0UQ;#z(BI?jJOI8fTD;Mk%pTk<`yTtPv` z!PGQ`qBoUU)kPJDUKp1qYbqw~eL$hfhu-vs*hgb@hBRc|KA#+41(zH)jUz$FEAnx#k8_$E%tX064oqOb@X>Ugf)YNfS4#Cbs+wybtnH;fV}nTs?)bbwAbF4|#}B6w?$j{8!P4+e`xa#r|9*J7}7Z zc4+Tm)!AiRK(`;rKdN#~4sZJJe`l0?s=Oc;+Z;y3w4 ztfXK$m6o3=PbDf6gPm~1Mx4NfIfz=EA+spGJtD;aj5`|nk_lW$D99grK4wA1;E2#Q z>H4(&cRE8bb%*@Z7TU#t+=?GIsB>-XUCkh|eKRog8A~twf+6KU1<))LfFSG-_OXDj z;xNN5?<)YsT`7 z4Zq}L_ijNmU01m#E{b#N+=Epf!_;lbC)oeV0yz|#;C?~WVY^!o6PdfSE>4WJX%M0C zf?ZI3IHrW}IM|x5_f9_^C}(J|!=63kDS5$`@&d$ZCeZe8mn#is&Ua|#UV7SibP$_b z#O@eefWRQsebAI+Hf2dPltFVqPjQ&h)zWH$k$?9uUMwP=eCdYj#9qJDN`f-=%)VS{ zi<)%xH!cGsvCiA2n>72*ac5ewgzdU(4V;>3Q5L!S%lf`w$Xy zV4$W32}5-f%^<9)r4`kf(R&takYG+F>~caB;(q)%EHhA?l4bAW6T*LwDdSaTB|V@T z(uY}ke67%6I}+qwfvcowAql(bz_sHv;I>&M?bz=WkNnN02wPNumx-C$TE3>uLoHxd zhAWoVJ!4QX+OtxCl{7d-Ga4pn8J0Bo2?iLU6X56m{TMkQ>45n_$teG;K0cmO!2CTS zl!GV!Eluw`_x2-5L|W@pthOu)IEw_6X!@~<%ZM`bI{whY%FwS0|@Wt z$cPWb8_*TKR6=pmXQID|-)f8cjas2n2x%f>h%TusS zknBVHlr#MJ)%JC?elaQYHF`we+?u(I^%PAPC4Q+8wsIT|+Hic=3(#C*XLkau>hEvH zvIR1Y!%#2uS1|8#Ma9>dk8KJlc`St$7mI9F{5bu@Ik*Irawp0!HYL_uy!rak32~EN zO-_iBqZUuphr=w{M+9*Wgu+P17TLc=3waJ@MK@Y$#`U^8Y^R{Z-R$!6A@(qhc1X@_ zaAZ)nj|x`Wbn*5bA24p(YqUp?+wm$xtxtBr#g+CrnEmUW}mI$x*s)D<4sWG-krzGFdo3k}!mE ziXS=dy)n;jWqA*aCMrrU}TMZZQI03TKt5xty z{(L7DA!TVHs_U$-*sD{&OHIQ%oc}%Ruf84*5MS6vMW5X z%XhDcbN^V}p)FrfueN8cvbFe#+S4a(Tl3Ckd`vx5-h_O*=3lOgS8p|rjmGZ8@w^c1 zUXw^@ukQRtYpi}RHkG2vp{aLF>R)1oOBxUmfWRm~$wO;^CwEsS2r>;86DyGb?QdQ1 zJL|BUtWcU-p-2+xi^4s}+LYW!UTT%NhI%P;n@MPKXR;+z1+OcQgqOeW`n~Y}VYUj@H4qX2NBfBRD=27K75w{McRbkVB~b`Vj5H~8 znA$>9pRWy?x7jBr?TOSgwmtUcj#BcX`r{*-CWjh(!a%z)RdvBeA%&lLSDEH#3T5>E z_t@;WyHai;Q;M>FB_HmI zZwEyz+M7hZd6lCHd@DF2f1CwOA5EnX1(xGV&~M&KeXyZLA+#8ytW7FFe6jbJ7c0 zMgizvzGedL*y`$0lb*PeZ#d6(VAT0ftoz7Feskrxih_j#s$zl4X6~Ck)qSc;PpT`u z#y7)`zZXA9{9&q|^5ky?=LlM*=J~ehjVZsG?BZgNFNca5HBvL<>6gYUWCq{eb#_AO z5v(WH8h(4|x2q3^T*1*;H<_i-I3*;L=7|fj@Au*?==-0ngo@k{{Z=(CC3=)%g5)eD zM9vI6IPIafsZ9AI`8U28a3^SaZnQB#Q~$|m7T~OAYOrUYA&lpaWlv4iQGv{7*n zErdDoDBUR8g20B58LuTblmq+*e?nC? zs-V7$cc{r(yKIAaiUb8W`7kFtS86{Ks~cALtG9SY6tjD%^JJ>6tc|UmT=#(gsSz=f zV?6#Nbiz(v;1l)jrlJuJ?D+UsJg+ik>ff#{QEeC2t+M_L(S&_BbWxC45Vw9#J#Fy~ zclS#cYaFHNP+aeApc0okt-gh&RuzMAoRKFF+7do6s5)&DuDgX3u0{3t$)zq{jv;+E z_>knyCZCxQYr0B`1mbBY$^5?lPy3tXqo0d)=Y&xIg9}R3se=19?hL=#Yr2kgmJqG# zWvg`0WXhA=k{YL2?Y|uZn4lky7LEwvtf8WQtaW8WuUT_8Me*5JTi z^bfaKXP*w}C2GZm25$ujoEe6AeBMCYkcSmYVUN zLm{G6BJ{@d>n?u$AUPT>poMm#!TWc#N}vNZ@^X-uWp;>MLobSVOKswmbo`@ZREwyc!88!wkd7? zA`Ec*8;)A`(#U7;p#XU4ZEv_7G2-ue&LuJ*wJsY>fyJ7~xg$`M#pa`U{Ka^u!(tI- zO53@p^S_^z2F5+ktO|-@6X~BeMeVUzD`A^b%pxLJbwkTEHwX%bviA~CcXlprD-#6E zHhcE|Z_VzW5L9ybkv!G`BGg~Rf5Afel`y3<7u2w5_&3rfyCh2`DZ3>30*h275l`} zVuUDF_W4rom99lQ@1MDJcV0#=*`}7@UJyNIkSQ9@2nx`>0~#7^UA)Isi;s^l0^0A` z*jT&fgruY}=pWV~PPlg8w<77oO=r?gWF#DM(_$k${JncR+)ia7J~tts%#0 zf92A|V90L8UVXOwy?t*K#>N?vm9He((H+gTmltw<3l)5E`hT7>9 zv74El+KYNiCz8fMkBNyZPT9-!a{Re?#7SpfbgLr4vodyg*khFoJl2o)vdSv@)+oAS zZ|RoZ{LH+&b$p++6!i5+SzF_a1GVEuCMJuU0l8O*!d2(?Sg5q0X5h!x)W~Hh1W*G+ z4yX1a5f5_?=uClL;l5mc<<0i8=uaQHyqy3iCqno;8LxRhBx2Id8Bop{;1Cib$Mh?p zuLuHpAWEZbxA|^0V;jh+6!zJ-Y5*h;bLB(dD#pb$W4Je4nIa*#`-cBsd z$kMW#Mn_#Dc4Ji3$MW7lqpd`tt+Sp1w9}AhrlcodXJ&wP_i13w-rio+W4bOr^{2*D z9(WKh-@I|&76sLPON(mOD2h?3Ru3*=xf~5N&+L~kZ)_h{SIfX1h3N&kJ2`pomv93k z74U<_M`XcJALlVVu`Wg%*Gri_G|r?GRHMd^p(drDSiL5Om7s%l&JO!&E26=Vt>6`u z&w5rCNI37_O>9ia-k(WIxz7GgX*9)|?ozRc-`(tG82lX*aCpe8Fp*_X5`l(A#gHPl z51}IpiDhKB#M2NC4p3x3vt(zda)t~@arp%WY6j#T(gTwPeZX|Nli!!UY1gb3)C($Y z)ojKyB*U=p#*uOJZ$Ex);zpKOhlWiFIHI4gGTa*)5Ma)t9c~4=-@eUKzk2D{9TgRo&Il3|v~VK_oG@HN z)axGS0e1wr+$E2U1rbDy3n^Q{}XKTGq8y_ymy!iI?y|9wS8Hqd=O9Rf{R!`RqZ zs|vAnmtb zAv;_T%2GlUG580VkQ6U(&CYVb7aWhdpcz`tzet+MGn;F#>8j-HdvO23`>5;ZybBGI zIW6Z-61_9ETUL!p8eBzlYLLDSK5LKU^_$^TXYmWGYm|T9t;#ZzHL$rFg-z?e8QMf4PO~u+`HC7aRmQXi~YiuN4_%LI-Fb%NrE^ ze0^UQ6p%n8@9Oyi=#;|-EmI8aOc!M|Ci1VF3>LOHo0W3wkyu6=SI6eZH$id@RZ}5- z7Tu#Gie57XPj6MbWfQ-QlwQmB&X^Xlc&M0z8;zTPOF7ljbBt^Kmlw@+fwaEAf~a0o zpYdCSDm!(I)@RNAILsIwqeayy)9P15@~+)hFT!5LLAuh`-(TDtxZsf;K$-&>Aoln=|B++tHEF1cg5WZpIP_E^hAl0@5unkCbi*1(_MwF^)>0k4ZRC07SJ($~qT>-)(Jeg?)-VSpGGGGIOu3 zXoGJPKQm1ctyfz4OmoXLi(kz-lu7uEWQ!Qz^!+iHhpH0uKW^}bnR#beAD{H=`WFm` z6;BBCTkyQP6xA6+n{bAgOIA-g=F_g^AoW%ct8C2<^=?JF-TRrvJPjmj)G|MH!!ui4 zyWfKE!H-wcy8UUdv@N2eUWW=YCNeTJs|Ezf78*}@;6Kv|2L}LPr<8f#&xd^mT#uaZ ztiZhpCt)bFlf)=FFL3O zH7^@ZS>B)7cVEl5JG7Hm?W1AQh?yzl>BvEZu4x#6l&rHgAH-~>55e9t4DxJ=@mtns z6uEbTT#%AB|46r$#Q){Mk=P1$rehEN{Yh8Np%-6sMqmMD&g#dOU%f?`Xt+`*VjC}R zNijqg+AMNUTudxAO`2Q$ETjtby0c+m^#$~hm@5`Wh14T>akTWpIz; zMap^}4*qXw0aWRJ1{%_%*8S_H+ZnCUl5*ZUQgw4TpB5Z zp)q4gRnV{7(f6kuniJ{pkqFWoP3w`R21E@|%inKDFk+)wa}8DbQ}~f7T5V+1ONcM=5uTU^I<8h$QP_$t-dU5I z7JK;BXDc>~eAdOA^AgzZR{1RyDQ`TNRe2vTWEfgaN%728APcXxJ4{V^X1D0e4LfeU zCpFt>CZgeCe~ndudw=#-G)qF&zKL(LXqsNmZM~Su46yZydKm?2AED}cU*N@EM&3v0 zoc_soefXix*@|&r7S`82hZkjE>I%yinhO%cOY!hX7zyJ>g0-ag%}o}#{0dhy-&A6B#8 zb6v)-uxvW3@1TVurJwz5TyBW1*?1Y!#m_OB3u>A40)|PPSGwBvVI2)hO$ERQN%b~z zdwJ=GaTdh+mi~T9&}e|86!=v2V*OTEF+#Jjuz=b}EcaltzaLd$jv*WlFPb?W&3oaE zHz8$~FX%Qj3Km3(4Jy4B+w3(X9}eJ0uaq7dzX>Xt`QaL?LC;sxz`oujiduDgubt7^U?40p2FHsGb9fF%uma{U!4XiqYMsr<54rev*=r*#WQ~-*r>K=Ot5N-=#RVyZL=U>P9+0eF`xy z*zs3_1pv{aCYa~|L-?|djgmP!{rPj~T^pI2b{??KZ!fIg`l@*JIcUgt&n$k$(>Vf%9pY!C)$R?0juV0R{-%Gz=2nIa=V@G{i8W)=WG_fs zeq4!88Xd5W=yF-6&qXM)!u5jnP!itoW^xIet6yOxFs8x5|Dz35Wiat$J#bk3@qS2V92c!}YI@K&%?}Ml)ZtfcS~?ko zJ#I8{AmZsgpqLq5dh$>;Tlwk$L9&oZR z8yU?Sp_hOC@~2i!x;IHMj~qDrY<;kMxHyDuwdOFfVf949dxkBqSVq40CluNHnw8kz z41R`v#!MUYm$~neT5g5^kn&9+z5xhCPSAUe%j$M^b1}Vl*&#KD_&Yvsj=~shK{4vc z(@)tW38)VggE>0^kd8li(+7-eRB&Zw<$$CoO#KWaB1fbPS6ZpxU$rE#*RPqpRdv?G zCx%5J)0SOtrC?#3&ZOffCVO~jKw@C{bYYu^NnIs;R8@Y}^AI6BB~ywiX&y2{Ncp$#*`yc+ups zBg>2MwsC$dQH;#`=7octO@IECy`5dnJC#waLzb@ALeRf*FlyzjYk7sBVbWY<@*7Xh zGS-)g-Gd^wZ8H=s>+nb-x?OuX@JWfO!3%})*w4k~t&P&%?#|9&om^}bBb1y~Ra;?{ z`mLj@5zfxeP@nBg1Ufo6KuQkPWL7*vDPES<5@lcikmAuooSk)yOzy(btFD++ewOxQ zn_rh_lma-MS>Mn51(NNbytBJMcr$Cz>z?XU*CHx>63@Dk$0G&$BIXa_nfR|%yOG71 zRs>JbpZDq$qZ8vkyE3kgrgyo}UOK&aTmY-yZe>sx11`)l=6Pon zUr!ftO*WeGXe(|QTqt)aA1eJ$9o%Q$z5kus;0x@R^~J*T@R0nzn?Gs9Iy8M)uk_8 zSQo4>x_=ATyF=Vzi<@04h!2l42d#K;6~e)_^K_*6IDnr3p?u6Q-Rb#`ETB)e7z9wQ zc%;=y;$>c5M{yCqM9)?peLTD1%bLDjqQ>jkif&R1gOf;;-o+#5a;9O?bAUFnW)5^s zELci7JiDDp2bUGK_p1kLosJMwD(v95Y!`G`x@l_U1!Gh6|Hxq30u(jzOW%oQQ>d`* z4W0n=-ZT|4)=00(OBsU*zh%ArpLZ0DJ!V4rzm(!}cbXO%LuL~901YgO5__fz@4>HM z+Y;3fiSPr3%2iz5AU1inFxA$yH?e4fRTl|0$IVmyf?ggwlN|ezSLN-j=~W#cJ{Ubz zEm|njD(`)~SP-*6)3>wVwRle<@S_Z;C9R8w<<bG78$XIiPJi^O~OKRl@Fm9V21;xa6az825K$SAX;d#-7y_3gE{1MXM&5{d9b zIbS@CRgh`#L%9^|bLc@| z`pG@8)AR+LvugMOu~x{dkeC5~m)F2;XIfuiV&T^e!KVsvaLpu&@KlsCioqo#KI2VQ z{YLBOI?=rp>z1o@;Gf}%@3 zfG4vfo}%+wRd|Lbn`=q|e-0mOP+GPNh43cgJ;3WP?P$WQi9$@#=SqIGMO7;p&WUK; zz8;5%$T~IcY(49Iyt$IO{Rrzq*2Np@E7fKRLVZ^`W_q4e1FF<;ZP+%#ORwTn9DS+6 zfZxuEli{qURo}Uuq;(Fov*MXYyc+mvh4lieX`9u7 zg8r~sBWm&VgN23`< zoLoXyXxbB)JWz+#>Dce2Rnz(>$k zsijpx^OCja%_7pHpTX!juY;CmauT@+gRjM^NKw>KqJOjoDI=}GS@j3Ml2Utq1he;z z+A{{as!Pa*&wt6w<4Zg@+abQo*v`fh&UP8)Zj-N?ggKVZoErajtFrZ|#rq@5Wz74z zbUZXI#fOi`=Dqp4Mgh~;`rg{s!oH-1U)a2L&M_p3cnusV#g4 zk$h>kc)wfMB`g>K;zF&D!J;P6Rs(uEs-#DmX6b8rs2e@&OQV9m-e1(q;(tOoP$RVG z6X?#(|HWP(3{912(ZI5|Fl`fv@90unCTu8^Ec1y~XXPbrhJ-N0MqvuqIwIGA1 zTlS3>IqLEinLHiGr~L{J?S&Og-bkSy`B7>CR-)UurQ^$In;g{-!VgWOnM67bTUrcu zN?BQ1Nta3?c!J{ws&bXKj+1lKz~5hHS-hvZ{K|L!$Is@V@VP(r<0zucuIX_E*4kb@ zmv+Q$;=f?`os8*U;yO#EeU{g0FSPpoz`OJrgP!L9EPoL+>GHkH4jU2bb0pytwe3C! z`ESlCn{yXo61aUuvhePX@jbs6w7R-V{u~n#n-3H|JAK7oH7!(iL(`qU6Q5Jg!yBY^ z45P!HegCfwV4jiK@$-rLlPJlb-V)?G)e6GvRymqbQ68;J&`rOUTqvpMwfCwcP5E)2 zvf#%n8hF@99`qdnlC=eErKdbV_@dd^*uatn^ONKOqyx-CzF}g4yNk<>pe!8k9F57l z&xz~tiE1snWxhCT=%evoYdbJqk5aaNPZ~Y1QD~5$RzU5aku|OLsHy$;tSglkIfK_B zS8N5OD8Oon7a7llp3*rEX&$gK!@;TaMO;)Uv3;+q)BXa2*rnw=(19}g3%z8enh9z0 z&a#XV-{)o#F?&d#3ELdgK-%6N=yXX7*2)e3CW3Wtj*FHnE-{Kx#Gz<^-(NyPqRCat z9_m4G4t{X@v9zIKdd^>F0USt`L@-vi-42)r8dyn(LvG&4rsjsY6}4`ZDE0{wQ(>S| zrmh7ZuG85?l!_K#BE~0e?uV_DQ>Vw;%+%D%y7S_wJtx=N@*Q*Y@ufXUNy(hhqT}p> zg6N&JsHmgIf{ZHX!5kY5k|E_3N$@|Nav2WtY#{`SAAb zr) z8|BlLp4Y~&$Pnk#Pw66-ARAY8R!t2SNj@iP8}<(R+#vVC_@SbpHphn?92_OljU#Yv zpxhP$V_e(mGs|_FVl~C#q|P1@@p?BgCD?Sk18?%$ ztB+;s)`vqx42YsFqt$yu+TiFrwABJ}_8&SWW19)RA!UQ4lliy-pzBBq_FZRo*jw%h zG?!wgV+ccnLqf29b2&Ck!3YmBcKI)hYLVoB>M(WNXtrV>S_!=q5PP^D<~-=8C}X!f zb{I6}-h7G7;i!Hx_+k+~`cwhZhX)@n#WB2%F=%azQb`PJWEF${S2)8%ik3F5lEB*G zuvoiCcg@BDf9i9oPr{!sE7UMEX6VJ#2$c(v zREWAJ$HO5Fd;ae6u(pi}yi)_jtiMWLXgG%J2-!9mBkB&X_Rty))^5!8OviyAu!281 z3NoD~DBM3*#KDirnwr(2UiZnR(r~;T;y_hrM8=SFi+I*H4{yY@Sv^Zfa9aDI+}7JBFRtV{!-lftu(46i~d7a7~19Q^@t<9|Q^0@yY9 zV&}hk!&KAR?g@31QvaK45dh*BM6W=*N#u(TnkR-JRzP_CH(pjDbtzOrzUeZJ_cWI zF>!Oq$o$n{omSM8B_-Z~xQk~)+{VhmeP*frQd2qGIO16saU9MFd~1WRc6JwwN z>RWKq1w#lh=2#*lv^{+F9w-_3p>{0|fHo&&X0B`?`eao4hR`wtbsG4KuNa|kySNx# zy&4LAuee|3*2RA2=sS1rz=~29;2v!RV|Co)tGrgdtSXVrV~1&?)&oxSoOB=k-Gz{C zl44}rER$_!VK+JEO#~_Yz)t)g+;*w;gvxxahJ%_S%2VL5y-+$njN#!xZIH&C+`vp^rGfC6EDdpDv+TV^fuOHjD(-pT@)b`#ok~1`~;4bv~*Le{#fVap6*u_ofuNM@!rM{@Bbt zI}t{`z`fOaHxN{~%^a`#%}g~S2!ewQiP&}%z2pZkWh{{1mY5fT+@f`TeSPVMH8(mU z_WpAw=<)%u{-8#i_SKH8s3_H=M~^Vywz9I5uXssay=1*}!QJQr^{vtPTxH*+75CbA zeUIS`Qy}KgYPe)xE#hptE_L(Tj%j}p+8TYDwEl=F2Np=Wo)lrQM`H8NZ%2(dL75Z2=_UVJ8sKE*jg~Gb1g*f2FlRb}&3&M>HZrM@xgdDK4uzU{Th-=H|I}JN{ zi_I(4>3vj=*u0>-{NlQuMLkh5tKW?33mC*W)M#`;E!DCq-fYvXGk;TMulVKYBdi6R zubNszoq`S**5kZ!h~JC(=!>K3FWZ0IRR8eVOGu+sY_eA8iAh|#(b$UBQ|B{J`{i!7 zJ63k_yG^~lp-%fTsxvxyZssmu1c{&YXYUUs&O@*3ByS_buV(I!SgipWU{};h#FmnZ zs>kKRQy3o1Y0UG9FD$yWA)%;g!@6K%hb#GPfbVAxhKD5O-~8*_gKRhWZoVhe7qhDg4b(by{H={>hyLaE#}hskDG; zK?y+aJBrj?*Y{Oi^se?K95XNa^ouO!z;g7iC*2(cp*0!+Zhd5_` zypWH(&Q_@x^WNd8;|AT+RtNXD77-+iU`Crm&WgyCe9w+#GjDVqE7MCR#KEVc8Ytu@ zY_if#5@IO-ygTbKbDhKVnLw*m-kZAT#Me2o{W4#xzI50$G2@+mQHm&+r3bY(P@e^&JUs%o4m{N{5NAmmFagMOSKNqbwS#Zw_7K; zkFiVmtYT-fikkg?oTKDJKj^ouUeCDlCTR+cpY!TZPYzA2?)}US$9z0(A;Qyf#7Tqu z5KmS8TQk`K`!0)RY-|2<`cZpDuLg2*(G8(L`ZN0lU+wl+>=VauW+FJNUZ2CyIIYtA z?(KQ-;@m!VyHBk4Ic%14y8XsGU!eV%x@pcB?50G@Fb!ix`s@DuNH?k99@I?-@As!r zw(zF?zV^M$R6u}INi^N?*lSMb96b8*^PiS#O#Lao5bb-v%;?yoOwhh(*e^^WbQ!uefn?j<2s{>so8nC+Gj6X^ ztP-M18;>yZ**-Lm2rwU5XkH>>#;W1C-4=EIP;ncow$!?A5~#Hxcezw#@a?%y?$asc z^!RqeUj16pBxKaL;p&nGazhT)tcHZjZa z=(FcpJA$IH*QYez4*c{^Icwk%qx{fV>ib@JBI)Yt zvV%D+BWn{?#m5mW?*0XJb#(!ir`^A~zDJlBY3k^F`ddByuDuwF_{g1sWD(wqo-VuW zc*Es8E-t^`C}n;9TG%J<1iYbb9A*?~RBmN=zqs{CT#3Uk;H8JV)WUCjv3bdmiR9>w zx%pkuxfbbL^!KIvbZPGU=|KsAnvDIKea`yM=VV?s@B|vh7?mH4O}jnDvZp?G?pzMe z6Eb`p@-uQ_C&<^-x+9OkVmiy_9c|jY9tgfNZ9YlDk&k`fj|rc;|o~p7gbd$J7WYp z0k0G~h8Ig4)tn1%-t-?YwxP`Mt1wUCY0cD4TEFXYCnNObmtOfgZmn>OX-Q zJ9J^79EBRk4eD=!45s`(sA&uP9=uE-slM}wm<-1lQU$vbEv;zEQhtMowN)6_{WMUc zLa%A9cX-w)4^L3;0&qx4ZfLx^)P3Zb@UGo522M&&9HS4GrIQvU3pY&}&!!KbJ#L7n z<(r?}{%N@ITe}*VYlzV0xlkGh)}OxhnLJ}P+Flotn6zhL(sAJtuJ5-owDwzIZ#mbaY`5;y z-DIn@PA#h3WSSYDbFOAI%a(_(`t%fiRdabw99FB1aj|t~8MrK`SGrH#`vwZ&6R|Ij z>A-Udx$;IG+Oz<*V21pFH4Z64PF`lwChMau)!bHSOWzuId_menCxpy(( zC$T`(&hJG=Dc$`kYnkRxG4gkZYPc>(kd#}_=vimvq^@i4WE+q`i(maPemSmsA+OXR#BmOLmr}Hx zbD^77Hg=DH-MPH0cu`FJ%11i9tT>ziAnVChxVxdhIxlVUSjJQf7(NuX=vneAZPx~z z%$P`#iA6PQ$fe@#VpiblCIr2c&qB4x?^`fgim>8Eb6hG|)~aghR3FyX16!J6MSS+B z4tI?Gs!}nuGkST?Lx+W5L-`!ekK4EQo1M>FW+87+8waqDa$2I4eoI%AF;v>8jk|^A z&E0VAtPaZlG^aT-g3~D7XTm8wj!Zq|dWLX2+2a341!wEu<*6>Ev2us{m)0bJ^ zPMZ-rd~i1|#q`3Y(BDPYzMls|pY^fcyOy?5@4F)!(NnLmJ$jx?{zM}ue|9J?ZujG5 zQ%f+4V4hAVmoWa+@SerSaxodBh=F2o0FR~r!J`V4j8E(HF3-8*c&azFo_%6ukY%p! zYH?zpeo()dd%boM9@)$@m&sD~@Ne27@p^Dp#{fy7-KR6wun|lr*