2014-05-30 12:53:19 +04:00
/*
This file is part of Telegram Desktop ,
2014-12-01 13:47:38 +03:00
the official desktop version of Telegram messaging app , see https : //telegram.org
2014-05-30 12:53:19 +04:00
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 .
2015-10-03 16:16:42 +03:00
In addition , as a special exception , the copyright holders give permission
to link the code of portions of this program with the OpenSSL library .
2014-05-30 12:53:19 +04:00
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
2016-02-08 13:56:18 +03:00
Copyright ( c ) 2014 - 2016 John Preston , https : //desktop.telegram.org
2014-05-30 12:53:19 +04:00
*/
# include "stdafx.h"
2016-04-09 22:45:55 +04:00
# include "history.h"
2016-04-13 00:31:28 +03:00
# include "core/click_handler_types.h"
2016-04-09 22:45:55 +04:00
# include "dialogs/dialogs_indexed_list.h"
2016-06-07 22:59:39 +03:00
# include "styles/style_dialogs.h"
2016-06-09 14:51:24 +03:00
# include "history/history_service_layout.h"
2016-06-09 17:31:10 +03:00
# include "data/data_drafts.h"
2016-06-24 13:37:29 +03:00
# include "media/media_clip_reader.h"
2014-05-30 12:53:19 +04:00
# include "lang.h"
# include "mainwidget.h"
# include "application.h"
# include "fileuploader.h"
2016-04-13 00:31:28 +03:00
# include "mainwindow.h"
2016-04-07 22:05:28 +04:00
# include "ui/filedialog.h"
2015-12-13 01:29:33 +03:00
# include "boxes/addcontactbox.h"
2015-12-16 18:04:02 +03:00
# include "boxes/confirmbox.h"
2016-07-05 20:44:02 +03:00
# include "media/media_audio.h"
2014-11-22 12:45:04 +03:00
# include "localstorage.h"
2016-04-13 00:31:28 +03:00
# include "apiwrap.h"
# include "window/top_bar_widget.h"
# include "playerwidget.h"
2016-06-03 10:20:24 +03:00
# include "observer_peer.h"
2014-09-04 11:33:44 +04:00
2014-08-22 18:55:23 +04:00
namespace {
2014-05-30 12:53:19 +04:00
2016-04-10 18:53:01 +04:00
TextParseOptions _historySrvOptions = {
2016-06-14 19:26:41 +03:00
TextParseLinks | TextParseMentions | TextParseHashtags /* | TextParseMultiline*/ | TextParseRichText , // flags
2016-04-10 18:53:01 +04:00
0 , // maxw
0 , // maxh
Qt : : LayoutDirectionAuto , // lang-dependent
} ;
TextParseOptions _webpageTitleOptions = {
TextParseMultiline | TextParseRichText , // flags
0 , // maxw
0 , // maxh
Qt : : LayoutDirectionAuto , // dir
} ;
TextParseOptions _webpageDescriptionOptions = {
TextParseLinks | TextParseMultiline | TextParseRichText , // flags
0 , // maxw
0 , // maxh
Qt : : LayoutDirectionAuto , // dir
} ;
TextParseOptions _twitterDescriptionOptions = {
TextParseLinks | TextParseMentions | TextTwitterMentions | TextParseHashtags | TextTwitterHashtags | TextParseMultiline | TextParseRichText , // flags
0 , // maxw
0 , // maxh
Qt : : LayoutDirectionAuto , // dir
} ;
TextParseOptions _instagramDescriptionOptions = {
TextParseLinks | TextParseMentions | TextInstagramMentions | TextParseHashtags | TextInstagramHashtags | TextParseMultiline | TextParseRichText , // flags
0 , // maxw
0 , // maxh
Qt : : LayoutDirectionAuto , // dir
} ;
2014-10-10 16:46:20 +04:00
2016-04-10 18:53:01 +04:00
inline void _initTextOptions ( ) {
_historySrvOptions . dir = _textNameOptions . dir = _textDlgOptions . dir = cLangDir ( ) ;
2016-06-07 22:59:39 +03:00
_textDlgOptions . maxw = st : : dialogsWidthMax * 2 ;
2016-04-10 18:53:01 +04:00
_webpageTitleOptions . maxw = st : : msgMaxWidth - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) - st : : webPageLeft ;
_webpageTitleOptions . maxh = st : : webPageTitleFont - > height * 2 ;
_webpageDescriptionOptions . maxw = st : : msgMaxWidth - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) - st : : webPageLeft ;
_webpageDescriptionOptions . maxh = st : : webPageDescriptionFont - > height * 3 ;
}
inline const TextParseOptions & itemTextOptions ( HistoryItem * item ) {
return itemTextOptions ( item - > history ( ) , item - > author ( ) ) ;
}
inline const TextParseOptions & itemTextNoMonoOptions ( const HistoryItem * item ) {
return itemTextNoMonoOptions ( item - > history ( ) , item - > author ( ) ) ;
}
bool needReSetInlineResultDocument ( const MTPMessageMedia & media , DocumentData * existing ) {
if ( media . type ( ) = = mtpc_messageMediaDocument ) {
if ( DocumentData * document = App : : feedDocument ( media . c_messageMediaDocument ( ) . vdocument ) ) {
if ( document = = existing ) {
return false ;
} else {
document - > collectLocalData ( existing ) ;
}
}
2015-08-24 13:53:04 +03:00
}
2016-04-10 18:53:01 +04:00
return true ;
2015-08-24 13:53:04 +03:00
}
2016-05-20 19:01:06 +03:00
MediaOverviewType messageMediaToOverviewType ( HistoryMedia * media ) {
switch ( media - > type ( ) ) {
case MediaTypePhoto : return OverviewPhotos ;
case MediaTypeVideo : return OverviewVideos ;
case MediaTypeFile : return OverviewFiles ;
case MediaTypeMusicFile : return media - > getDocument ( ) - > isMusic ( ) ? OverviewMusicFiles : OverviewFiles ;
case MediaTypeVoiceFile : return OverviewVoiceFiles ;
case MediaTypeGif : return media - > getDocument ( ) - > isGifv ( ) ? OverviewCount : OverviewFiles ;
default : break ;
}
return OverviewCount ;
}
MediaOverviewType serviceMediaToOverviewType ( HistoryMedia * media ) {
switch ( media - > type ( ) ) {
case MediaTypePhoto : return OverviewChatPhotos ;
default : break ;
}
return OverviewCount ;
}
2016-04-10 18:53:01 +04:00
} // namespace
2014-05-30 12:53:19 +04:00
void historyInit ( ) {
_initTextOptions ( ) ;
}
2016-04-08 18:16:52 +04:00
History : : History ( const PeerId & peerId )
: peer ( App : : peer ( peerId ) )
2016-06-07 22:59:39 +03:00
, lastItemTextCache ( st : : dialogsTextWidthMin )
, typingText ( st : : dialogsTextWidthMin )
, cloudDraftTextCache ( st : : dialogsTextWidthMin )
2016-04-11 14:59:01 +04:00
, _mute ( isNotifyMuted ( peer - > notify ) ) {
2016-05-20 19:01:06 +03:00
if ( peer - > isUser ( ) & & peer - > asUser ( ) - > botInfo ) {
2015-09-08 20:59:36 +03:00
outboxReadBefore = INT_MAX ;
}
2016-03-23 14:47:32 +03:00
for ( auto & countData : overviewCountData ) {
countData = - 1 ; // not loaded yet
2014-08-15 15:19:32 +04:00
}
2014-05-30 12:53:19 +04:00
}
2015-06-24 20:24:48 +03:00
void History : : clearLastKeyboard ( ) {
2015-11-24 19:19:18 +03:00
if ( lastKeyboardId ) {
if ( lastKeyboardId = = lastKeyboardHiddenId ) {
lastKeyboardHiddenId = 0 ;
}
lastKeyboardId = 0 ;
2016-07-19 14:37:01 +03:00
if ( auto main = App : : main ( ) ) {
main - > updateBotKeyboard ( this ) ;
}
2015-11-24 19:19:18 +03:00
}
2015-06-24 20:24:48 +03:00
lastKeyboardInited = true ;
lastKeyboardFrom = 0 ;
}
2016-03-23 14:47:32 +03:00
bool History : : canHaveFromPhotos ( ) const {
if ( peer - > isUser ( ) & & ! Adaptive : : Wide ( ) ) {
return false ;
2016-05-27 19:47:46 +03:00
} else if ( isChannel ( ) & & ! peer - > isMegagroup ( ) ) {
2016-03-23 14:47:32 +03:00
return false ;
}
return true ;
}
2016-03-19 19:55:15 +03:00
void History : : setHasPendingResizedItems ( ) {
_flags | = Flag : : f_has_pending_resized_items ;
Global : : RefHandleHistoryUpdate ( ) . call ( ) ;
}
2016-06-09 17:31:10 +03:00
void History : : setLocalDraft ( std_ : : unique_ptr < Data : : Draft > & & draft ) {
_localDraft = std_ : : move ( draft ) ;
}
void History : : takeLocalDraft ( History * from ) {
if ( auto & draft = from - > _localDraft ) {
if ( ! draft - > textWithTags . text . isEmpty ( ) & & ! _localDraft ) {
_localDraft = std_ : : move ( draft ) ;
// Edit and reply to drafts can't migrate.
// Cloud drafts do not migrate automatically.
_localDraft - > msgId = 0 ;
}
from - > clearLocalDraft ( ) ;
}
}
2016-06-03 21:24:27 +03:00
void History : : createLocalDraftFromCloud ( ) {
auto draft = cloudDraft ( ) ;
2016-06-09 17:31:10 +03:00
if ( Data : : draftIsNull ( draft ) | | ! draft - > date . isValid ( ) ) return ;
2016-06-03 21:24:27 +03:00
auto existing = localDraft ( ) ;
2016-06-09 17:31:10 +03:00
if ( Data : : draftIsNull ( existing ) | | ! existing - > date . isValid ( ) | | draft - > date > = existing - > date ) {
2016-06-03 21:24:27 +03:00
if ( ! existing ) {
2016-06-09 17:31:10 +03:00
setLocalDraft ( std_ : : make_unique < Data : : Draft > ( draft - > textWithTags , draft - > msgId , draft - > cursor , draft - > previewCancelled ) ) ;
2016-06-03 21:24:27 +03:00
existing = localDraft ( ) ;
} else if ( existing ! = draft ) {
existing - > textWithTags = draft - > textWithTags ;
existing - > msgId = draft - > msgId ;
existing - > cursor = draft - > cursor ;
existing - > previewCancelled = draft - > previewCancelled ;
}
existing - > date = draft - > date ;
}
}
2016-06-09 17:31:10 +03:00
void History : : setCloudDraft ( std_ : : unique_ptr < Data : : Draft > & & draft ) {
_cloudDraft = std_ : : move ( draft ) ;
cloudDraftTextCache . clear ( ) ;
}
Data : : Draft * History : : createCloudDraft ( Data : : Draft * fromDraft ) {
if ( Data : : draftIsNull ( fromDraft ) ) {
setCloudDraft ( std_ : : make_unique < Data : : Draft > ( TextWithTags ( ) , 0 , MessageCursor ( ) , false ) ) ;
2016-06-03 21:24:27 +03:00
cloudDraft ( ) - > date = QDateTime ( ) ;
} else {
auto existing = cloudDraft ( ) ;
if ( ! existing ) {
2016-06-09 17:31:10 +03:00
setCloudDraft ( std_ : : make_unique < Data : : Draft > ( fromDraft - > textWithTags , fromDraft - > msgId , fromDraft - > cursor , fromDraft - > previewCancelled ) ) ;
2016-06-03 21:24:27 +03:00
existing = cloudDraft ( ) ;
} else if ( existing ! = fromDraft ) {
existing - > textWithTags = fromDraft - > textWithTags ;
existing - > msgId = fromDraft - > msgId ;
existing - > cursor = fromDraft - > cursor ;
existing - > previewCancelled = fromDraft - > previewCancelled ;
}
existing - > date = : : date ( myunixtime ( ) ) ;
}
cloudDraftTextCache . clear ( ) ;
updateChatListSortPosition ( ) ;
return cloudDraft ( ) ;
}
2016-06-09 17:31:10 +03:00
void History : : setEditDraft ( std_ : : unique_ptr < Data : : Draft > & & draft ) {
_editDraft = std_ : : move ( draft ) ;
}
void History : : clearLocalDraft ( ) {
_localDraft = nullptr ;
}
2016-06-03 21:24:27 +03:00
void History : : clearCloudDraft ( ) {
if ( _cloudDraft ) {
_cloudDraft = nullptr ;
cloudDraftTextCache . clear ( ) ;
updateChatListSortPosition ( ) ;
}
}
2016-06-09 17:31:10 +03:00
void History : : clearEditDraft ( ) {
_editDraft = nullptr ;
}
void History : : draftSavedToCloud ( ) {
updateChatListEntry ( ) ;
if ( App : : main ( ) ) App : : main ( ) - > writeDrafts ( this ) ;
}
2016-01-05 12:52:40 +08:00
bool History : : updateTyping ( uint64 ms , bool force ) {
2014-05-30 12:53:19 +04:00
bool changed = force ;
for ( TypingUsers : : iterator i = typing . begin ( ) , e = typing . end ( ) ; i ! = e ; ) {
if ( ms > = i . value ( ) ) {
i = typing . erase ( i ) ;
changed = true ;
} else {
+ + i ;
}
}
2015-08-31 18:05:57 +03:00
for ( SendActionUsers : : iterator i = sendActions . begin ( ) ; i ! = sendActions . cend ( ) ; ) {
2015-08-01 11:33:00 +03:00
if ( ms > = i . value ( ) . until ) {
i = sendActions . erase ( i ) ;
changed = true ;
} else {
+ + i ;
}
}
2014-05-30 12:53:19 +04:00
if ( changed ) {
QString newTypingStr ;
int32 cnt = typing . size ( ) ;
if ( cnt > 2 ) {
2014-12-18 21:40:49 +03:00
newTypingStr = lng_many_typing ( lt_count , cnt ) ;
2014-05-30 12:53:19 +04:00
} else if ( cnt > 1 ) {
2014-12-18 21:40:49 +03:00
newTypingStr = lng_users_typing ( lt_user , typing . begin ( ) . key ( ) - > firstName , lt_second_user , ( typing . end ( ) - 1 ) . key ( ) - > firstName ) ;
2014-05-30 12:53:19 +04:00
} else if ( cnt ) {
2015-09-03 13:48:40 +03:00
newTypingStr = peer - > isUser ( ) ? lang ( lng_typing ) : lng_user_typing ( lt_user , typing . begin ( ) . key ( ) - > firstName ) ;
2015-08-01 11:33:00 +03:00
} else if ( ! sendActions . isEmpty ( ) ) {
switch ( sendActions . begin ( ) . value ( ) . type ) {
2015-09-03 13:48:40 +03:00
case SendActionRecordVideo : newTypingStr = peer - > isUser ( ) ? lang ( lng_send_action_record_video ) : lng_user_action_record_video ( lt_user , sendActions . begin ( ) . key ( ) - > firstName ) ; break ;
case SendActionUploadVideo : newTypingStr = peer - > isUser ( ) ? lang ( lng_send_action_upload_video ) : lng_user_action_upload_video ( lt_user , sendActions . begin ( ) . key ( ) - > firstName ) ; break ;
2016-02-12 19:35:06 +03:00
case SendActionRecordVoice : newTypingStr = peer - > isUser ( ) ? lang ( lng_send_action_record_audio ) : lng_user_action_record_audio ( lt_user , sendActions . begin ( ) . key ( ) - > firstName ) ; break ;
case SendActionUploadVoice : newTypingStr = peer - > isUser ( ) ? lang ( lng_send_action_upload_audio ) : lng_user_action_upload_audio ( lt_user , sendActions . begin ( ) . key ( ) - > firstName ) ; break ;
2015-09-03 13:48:40 +03:00
case SendActionUploadPhoto : newTypingStr = peer - > isUser ( ) ? lang ( lng_send_action_upload_photo ) : lng_user_action_upload_photo ( lt_user , sendActions . begin ( ) . key ( ) - > firstName ) ; break ;
case SendActionUploadFile : newTypingStr = peer - > isUser ( ) ? lang ( lng_send_action_upload_file ) : lng_user_action_upload_file ( lt_user , sendActions . begin ( ) . key ( ) - > firstName ) ; break ;
case SendActionChooseLocation : newTypingStr = peer - > isUser ( ) ? lang ( lng_send_action_geo_location ) : lng_user_action_geo_location ( lt_user , sendActions . begin ( ) . key ( ) - > firstName ) ; break ;
case SendActionChooseContact : newTypingStr = peer - > isUser ( ) ? lang ( lng_send_action_choose_contact ) : lng_user_action_choose_contact ( lt_user , sendActions . begin ( ) . key ( ) - > firstName ) ; break ;
2015-08-01 11:33:00 +03:00
}
2014-05-30 12:53:19 +04:00
}
if ( ! newTypingStr . isEmpty ( ) ) {
newTypingStr + = qsl ( " ... " ) ;
}
if ( typingStr ! = newTypingStr ) {
2016-06-07 22:59:39 +03:00
typingText . setText ( st : : dialogsTextFont , ( typingStr = newTypingStr ) , _textNameOptions ) ;
2014-05-30 12:53:19 +04:00
}
}
if ( ! typingStr . isEmpty ( ) ) {
2016-01-05 12:52:40 +08:00
if ( typingText . lastDots ( typingDots % 4 ) ) {
2014-05-30 12:53:19 +04:00
changed = true ;
}
}
2015-10-03 13:09:09 +03:00
if ( changed & & App : : main ( ) ) {
2016-02-28 14:58:30 +03:00
updateChatListEntry ( ) ;
2015-10-03 13:09:09 +03:00
if ( App : : main ( ) - > historyPeer ( ) = = peer ) {
App : : main ( ) - > topBar ( ) - > update ( ) ;
}
}
2014-05-30 12:53:19 +04:00
return changed ;
}
2016-03-29 21:03:53 +03:00
ChannelHistory : : ChannelHistory ( const PeerId & peer ) : History ( peer )
, _joinedMessage ( nullptr ) {
2015-09-19 12:13:21 +03:00
}
2015-09-21 23:57:42 +03:00
void ChannelHistory : : getRangeDifference ( ) {
MsgId fromId = 0 , toId = 0 ;
for ( int32 blockIndex = 0 , blocksCount = blocks . size ( ) ; blockIndex < blocksCount ; + + blockIndex ) {
HistoryBlock * block = blocks . at ( blockIndex ) ;
for ( int32 itemIndex = 0 , itemsCount = block - > items . size ( ) ; itemIndex < itemsCount ; + + itemIndex ) {
HistoryItem * item = block - > items . at ( itemIndex ) ;
if ( item - > type ( ) = = HistoryItemMsg & & item - > id > 0 ) {
fromId = item - > id ;
break ;
}
}
if ( fromId ) break ;
}
if ( ! fromId ) return ;
for ( int32 blockIndex = blocks . size ( ) ; blockIndex > 0 ; ) {
HistoryBlock * block = blocks . at ( - - blockIndex ) ;
for ( int32 itemIndex = block - > items . size ( ) ; itemIndex > 0 ; ) {
HistoryItem * item = block - > items . at ( - - itemIndex ) ;
if ( item - > type ( ) = = HistoryItemMsg & & item - > id > 0 ) {
toId = item - > id ;
break ;
}
}
if ( toId ) break ;
}
if ( fromId > 0 & & peer - > asChannel ( ) - > pts ( ) > 0 ) {
if ( _rangeDifferenceRequestId ) {
MTP : : cancel ( _rangeDifferenceRequestId ) ;
}
_rangeDifferenceFromId = fromId ;
_rangeDifferenceToId = toId ;
MTP_LOG ( 0 , ( " getChannelDifference { good - after channelDifferenceTooLong was received, validating history part }%1 " ) . arg ( cTestMode ( ) ? " TESTMODE " : " " ) ) ;
getRangeDifferenceNext ( peer - > asChannel ( ) - > pts ( ) ) ;
}
}
void ChannelHistory : : getRangeDifferenceNext ( int32 pts ) {
if ( ! App : : main ( ) | | _rangeDifferenceToId < _rangeDifferenceFromId ) return ;
int32 limit = _rangeDifferenceToId + 1 - _rangeDifferenceFromId ;
2016-03-19 19:55:15 +03:00
_rangeDifferenceRequestId = MTP : : send ( MTPupdates_GetChannelDifference ( peer - > asChannel ( ) - > inputChannel , MTP_channelMessagesFilter ( MTP_flags ( MTPDchannelMessagesFilter : : Flags ( 0 ) ) , MTP_vector < MTPMessageRange > ( 1 , MTP_messageRange ( MTP_int ( _rangeDifferenceFromId ) , MTP_int ( _rangeDifferenceToId ) ) ) ) , MTP_int ( pts ) , MTP_int ( limit ) ) , App : : main ( ) - > rpcDone ( & MainWidget : : gotRangeDifference , peer - > asChannel ( ) ) ) ;
2015-09-21 23:57:42 +03:00
}
HistoryJoined * ChannelHistory : : insertJoinedMessage ( bool unread ) {
2015-11-20 21:24:44 +03:00
if ( _joinedMessage | | ! peer - > asChannel ( ) - > amIn ( ) | | ( peer - > isMegagroup ( ) & & peer - > asChannel ( ) - > mgInfo - > joinedMessageFound ) ) {
2015-11-09 12:51:22 +03:00
return _joinedMessage ;
}
2015-09-21 23:57:42 +03:00
2016-03-19 21:32:17 +03:00
UserData * inviter = ( peer - > asChannel ( ) - > inviter > 0 ) ? App : : userLoaded ( peer - > asChannel ( ) - > inviter ) : nullptr ;
if ( ! inviter ) return nullptr ;
2015-09-21 23:57:42 +03:00
2016-03-19 19:55:15 +03:00
MTPDmessage : : Flags flags = 0 ;
2016-03-19 21:32:17 +03:00
if ( peerToUser ( inviter - > id ) = = MTP : : authedId ( ) ) {
unread = false ;
2016-05-20 19:01:06 +03:00
//} else if (unread) {
// flags |= MTPDmessage::Flag::f_unread;
2016-03-19 19:55:15 +03:00
}
2016-03-19 21:32:17 +03:00
2015-09-21 23:57:42 +03:00
QDateTime inviteDate = peer - > asChannel ( ) - > inviteDate ;
if ( unread ) _maxReadMessageDate = inviteDate ;
if ( isEmpty ( ) ) {
2016-03-18 22:05:08 +03:00
_joinedMessage = HistoryJoined : : create ( this , inviteDate , inviter , flags ) ;
2016-03-19 21:32:17 +03:00
addNewItem ( _joinedMessage , unread ) ;
2015-09-21 23:57:42 +03:00
return _joinedMessage ;
}
2016-03-19 21:32:17 +03:00
2016-03-22 18:23:34 +03:00
for ( int32 blockIndex = blocks . size ( ) ; blockIndex > 0 ; ) {
2015-09-21 23:57:42 +03:00
HistoryBlock * block = blocks . at ( - - blockIndex ) ;
for ( int32 itemIndex = block - > items . size ( ) ; itemIndex > 0 ; ) {
HistoryItem * item = block - > items . at ( - - itemIndex ) ;
HistoryItemType type = item - > type ( ) ;
2016-05-27 19:47:46 +03:00
if ( type = = HistoryItemMsg ) {
2016-06-11 22:44:55 +03:00
// Due to a server bug sometimes inviteDate is less (before) than the
// first message in the megagroup (message about migration), let us
// ignore that and think, that the inviteDate is always greater-or-equal.
if ( item - > isGroupMigrate ( ) & & peer - > isMegagroup ( ) & & peer - > migrateFrom ( ) ) {
peer - > asChannel ( ) - > mgInfo - > joinedMessageFound = true ;
return nullptr ;
}
2015-09-21 23:57:42 +03:00
if ( item - > date < = inviteDate ) {
+ + itemIndex ;
2016-03-18 22:05:08 +03:00
_joinedMessage = HistoryJoined : : create ( this , inviteDate , inviter , flags ) ;
2016-03-21 21:40:00 +03:00
addNewInTheMiddle ( _joinedMessage , blockIndex , itemIndex ) ;
2015-09-25 10:47:32 +03:00
if ( lastMsgDate . isNull ( ) | | inviteDate > = lastMsgDate ) {
2015-09-21 23:57:42 +03:00
setLastMessage ( _joinedMessage ) ;
if ( unread ) {
newItemAdded ( _joinedMessage ) ;
}
}
return _joinedMessage ;
}
}
}
}
2016-03-31 14:37:58 +04:00
startBuildingFrontBlock ( ) ;
2015-09-21 23:57:42 +03:00
2016-03-18 22:05:08 +03:00
_joinedMessage = HistoryJoined : : create ( this , inviteDate , inviter , flags ) ;
2016-03-31 14:37:58 +04:00
addItemToBlock ( _joinedMessage ) ;
2016-03-19 21:32:17 +03:00
2016-03-31 14:37:58 +04:00
finishBuildingFrontBlock ( ) ;
2016-03-19 21:32:17 +03:00
2015-09-21 23:57:42 +03:00
return _joinedMessage ;
}
2015-09-25 10:47:32 +03:00
void ChannelHistory : : checkJoinedMessage ( bool createUnread ) {
2015-11-20 21:24:44 +03:00
if ( _joinedMessage | | peer - > asChannel ( ) - > inviter < = 0 ) {
2015-11-09 12:51:22 +03:00
return ;
}
2015-09-21 23:57:42 +03:00
if ( isEmpty ( ) ) {
if ( loadedAtTop ( ) & & loadedAtBottom ( ) ) {
2015-09-25 10:47:32 +03:00
if ( insertJoinedMessage ( createUnread ) ) {
2015-11-20 21:24:44 +03:00
if ( ! _joinedMessage - > detached ( ) ) {
setLastMessage ( _joinedMessage ) ;
}
2015-09-21 23:57:42 +03:00
}
return ;
}
}
QDateTime inviteDate = peer - > asChannel ( ) - > inviteDate ;
QDateTime firstDate , lastDate ;
2016-03-22 18:23:34 +03:00
for ( int blockIndex = 0 , blocksCount = blocks . size ( ) ; blockIndex < blocksCount ; + + blockIndex ) {
2015-09-21 23:57:42 +03:00
HistoryBlock * block = blocks . at ( blockIndex ) ;
2016-03-22 18:23:34 +03:00
int itemIndex = 0 , itemsCount = block - > items . size ( ) ;
2015-09-21 23:57:42 +03:00
for ( ; itemIndex < itemsCount ; + + itemIndex ) {
HistoryItem * item = block - > items . at ( itemIndex ) ;
HistoryItemType type = item - > type ( ) ;
2016-05-27 19:47:46 +03:00
if ( type = = HistoryItemMsg ) {
2015-09-21 23:57:42 +03:00
firstDate = item - > date ;
break ;
}
}
if ( itemIndex < itemsCount ) break ;
}
2016-03-22 18:23:34 +03:00
for ( int blockIndex = blocks . size ( ) ; blockIndex > 0 ; ) {
2015-09-21 23:57:42 +03:00
HistoryBlock * block = blocks . at ( - - blockIndex ) ;
2016-03-22 18:23:34 +03:00
int itemIndex = block - > items . size ( ) ;
2015-09-21 23:57:42 +03:00
for ( ; itemIndex > 0 ; ) {
HistoryItem * item = block - > items . at ( - - itemIndex ) ;
HistoryItemType type = item - > type ( ) ;
2016-05-27 19:47:46 +03:00
if ( type = = HistoryItemMsg ) {
2015-09-21 23:57:42 +03:00
lastDate = item - > date ;
+ + itemIndex ;
break ;
}
}
if ( itemIndex ) break ;
}
if ( ! firstDate . isNull ( ) & & ! lastDate . isNull ( ) & & ( firstDate < = inviteDate | | loadedAtTop ( ) ) & & ( lastDate > inviteDate | | loadedAtBottom ( ) ) ) {
2015-09-25 10:47:32 +03:00
bool willBeLastMsg = ( inviteDate > = lastDate ) ;
if ( insertJoinedMessage ( createUnread & & willBeLastMsg ) & & willBeLastMsg ) {
2015-11-20 21:24:44 +03:00
if ( ! _joinedMessage - > detached ( ) ) {
setLastMessage ( _joinedMessage ) ;
}
2015-09-21 23:57:42 +03:00
}
}
}
void ChannelHistory : : checkMaxReadMessageDate ( ) {
if ( _maxReadMessageDate . isValid ( ) ) return ;
2016-03-22 18:23:34 +03:00
for ( int blockIndex = blocks . size ( ) ; blockIndex > 0 ; ) {
2015-09-21 23:57:42 +03:00
HistoryBlock * block = blocks . at ( - - blockIndex ) ;
2016-03-22 18:23:34 +03:00
for ( int itemIndex = block - > items . size ( ) ; itemIndex > 0 ; ) {
2015-09-21 23:57:42 +03:00
HistoryItem * item = block - > items . at ( - - itemIndex ) ;
2016-05-27 19:47:46 +03:00
if ( ! item - > unread ( ) ) {
2015-09-21 23:57:42 +03:00
_maxReadMessageDate = item - > date ;
2015-11-20 21:24:44 +03:00
if ( item - > isGroupMigrate ( ) & & isMegagroup ( ) & & peer - > migrateFrom ( ) ) {
_maxReadMessageDate = date ( MTP_int ( peer - > asChannel ( ) - > date + 1 ) ) ; // no report spam panel
}
2015-09-21 23:57:42 +03:00
return ;
}
}
}
2015-11-20 21:24:44 +03:00
if ( loadedAtTop ( ) & & ( ! isMegagroup ( ) | | ! isEmpty ( ) ) ) {
2015-09-21 23:57:42 +03:00
_maxReadMessageDate = date ( MTP_int ( peer - > asChannel ( ) - > date ) ) ;
}
}
const QDateTime & ChannelHistory : : maxReadMessageDate ( ) {
return _maxReadMessageDate ;
}
2015-09-20 11:55:41 +03:00
HistoryItem * ChannelHistory : : addNewChannelMessage ( const MTPMessage & msg , NewMessageType type ) {
if ( type = = NewMessageExisting ) return addToHistory ( msg ) ;
2016-05-27 19:47:46 +03:00
return addNewToBlocks ( msg , type ) ;
2015-09-20 11:55:41 +03:00
}
HistoryItem * ChannelHistory : : addNewToBlocks ( const MTPMessage & msg , NewMessageType type ) {
if ( ! loadedAtBottom ( ) ) {
HistoryItem * item = addToHistory ( msg ) ;
2016-05-27 19:47:46 +03:00
if ( item ) {
2015-09-20 11:55:41 +03:00
setLastMessage ( item ) ;
if ( type = = NewMessageUnread ) {
2015-10-27 20:29:39 -04:00
newItemAdded ( item ) ;
2015-09-20 11:55:41 +03:00
}
}
return item ;
}
2016-03-19 21:32:17 +03:00
return addNewToLastBlock ( msg , type ) ;
2015-09-20 11:55:41 +03:00
}
2016-04-14 22:24:42 +03:00
void ChannelHistory : : cleared ( bool leaveItems ) {
_joinedMessage = nullptr ;
2015-09-19 12:13:21 +03:00
}
HistoryItem * ChannelHistory : : findPrevItem ( HistoryItem * item ) const {
2016-03-21 21:40:00 +03:00
if ( item - > detached ( ) ) return nullptr ;
2015-09-19 12:13:21 +03:00
2016-03-22 18:23:34 +03:00
int itemIndex = item - > indexInBlock ( ) ;
int blockIndex = item - > block ( ) - > indexInHistory ( ) ;
2015-09-19 12:13:21 +03:00
for ( + + blockIndex , + + itemIndex ; blockIndex > 0 ; ) {
- - blockIndex ;
HistoryBlock * block = blocks . at ( blockIndex ) ;
if ( ! itemIndex ) itemIndex = block - > items . size ( ) ;
for ( ; itemIndex > 0 ; ) {
- - itemIndex ;
if ( block - > items . at ( itemIndex ) - > type ( ) = = HistoryItemMsg ) {
return block - > items . at ( itemIndex ) ;
}
}
}
2016-03-21 21:40:00 +03:00
return nullptr ;
2015-09-19 12:13:21 +03:00
}
void ChannelHistory : : messageDetached ( HistoryItem * msg ) {
2016-05-27 19:47:46 +03:00
if ( _joinedMessage = = msg ) {
2016-03-21 21:40:00 +03:00
_joinedMessage = nullptr ;
2015-09-19 12:13:21 +03:00
}
}
2016-03-15 13:37:56 +03:00
ChannelHistory : : ~ ChannelHistory ( ) {
// all items must be destroyed before ChannelHistory is destroyed
// or they will call history()->asChannelHistory() -> undefined behaviour
clearOnDestroy ( ) ;
}
2015-09-19 12:13:21 +03:00
History * Histories : : find ( const PeerId & peerId ) {
Map : : const_iterator i = map . constFind ( peerId ) ;
return ( i = = map . cend ( ) ) ? 0 : i . value ( ) ;
}
2016-09-25 22:04:02 +03:00
History * Histories : : findOrInsert ( const PeerId & peerId ) {
auto i = map . constFind ( peerId ) ;
if ( i = = map . cend ( ) ) {
auto history = peerIsChannel ( peerId ) ? static_cast < History * > ( new ChannelHistory ( peerId ) ) : ( new History ( peerId ) ) ;
i = map . insert ( peerId , history ) ;
}
return i . value ( ) ;
}
2016-05-20 19:01:06 +03:00
History * Histories : : findOrInsert ( const PeerId & peerId , int32 unreadCount , int32 maxInboxRead , int32 maxOutboxRead ) {
auto i = map . constFind ( peerId ) ;
2015-09-19 12:13:21 +03:00
if ( i = = map . cend ( ) ) {
2016-05-31 12:46:31 +03:00
auto history = peerIsChannel ( peerId ) ? static_cast < History * > ( new ChannelHistory ( peerId ) ) : ( new History ( peerId ) ) ;
i = map . insert ( peerId , history ) ;
history - > setUnreadCount ( unreadCount ) ;
history - > inboxReadBefore = maxInboxRead + 1 ;
history - > outboxReadBefore = maxOutboxRead + 1 ;
} else {
auto history = i . value ( ) ;
2016-09-25 22:04:02 +03:00
if ( unreadCount > history - > unreadCount ( ) ) {
2016-05-31 12:46:31 +03:00
history - > setUnreadCount ( unreadCount ) ;
}
2016-09-25 22:04:02 +03:00
accumulate_max ( history - > inboxReadBefore , maxInboxRead + 1 ) ;
2016-05-31 12:46:31 +03:00
accumulate_max ( history - > outboxReadBefore , maxOutboxRead + 1 ) ;
2015-09-19 12:13:21 +03:00
}
return i . value ( ) ;
}
2014-05-30 12:53:19 +04:00
void Histories : : clear ( ) {
App : : historyClearMsgs ( ) ;
2016-04-14 22:24:42 +03:00
Map temp ;
std : : swap ( temp , map ) ;
for_const ( auto history , temp ) {
delete history ;
2014-05-30 12:53:19 +04:00
}
2016-03-31 18:06:40 +04:00
2016-03-26 09:41:23 +03:00
_unreadFull = _unreadMuted = 0 ;
2016-04-14 22:24:42 +03:00
Notify : : unreadCounterUpdated ( ) ;
2014-05-30 12:53:19 +04:00
App : : historyClearItems ( ) ;
typing . clear ( ) ;
}
2016-06-22 21:41:13 +03:00
void Histories : : regSendAction ( History * history , UserData * user , const MTPSendMessageAction & action , TimeId when ) {
2015-08-01 11:33:00 +03:00
if ( action . type ( ) = = mtpc_sendMessageCancelAction ) {
history - > unregTyping ( user ) ;
return ;
}
2016-01-05 12:52:40 +08:00
uint64 ms = getms ( ) ;
2015-08-01 11:33:00 +03:00
switch ( action . type ( ) ) {
case mtpc_sendMessageTypingAction : history - > typing [ user ] = ms + 6000 ; break ;
case mtpc_sendMessageRecordVideoAction : history - > sendActions . insert ( user , SendAction ( SendActionRecordVideo , ms + 6000 ) ) ; break ;
case mtpc_sendMessageUploadVideoAction : history - > sendActions . insert ( user , SendAction ( SendActionUploadVideo , ms + 6000 , action . c_sendMessageUploadVideoAction ( ) . vprogress . v ) ) ; break ;
2016-02-12 19:35:06 +03:00
case mtpc_sendMessageRecordAudioAction : history - > sendActions . insert ( user , SendAction ( SendActionRecordVoice , ms + 6000 ) ) ; break ;
case mtpc_sendMessageUploadAudioAction : history - > sendActions . insert ( user , SendAction ( SendActionUploadVoice , ms + 6000 , action . c_sendMessageUploadAudioAction ( ) . vprogress . v ) ) ; break ;
2015-08-01 11:33:00 +03:00
case mtpc_sendMessageUploadPhotoAction : history - > sendActions . insert ( user , SendAction ( SendActionUploadPhoto , ms + 6000 , action . c_sendMessageUploadPhotoAction ( ) . vprogress . v ) ) ; break ;
case mtpc_sendMessageUploadDocumentAction : history - > sendActions . insert ( user , SendAction ( SendActionUploadFile , ms + 6000 , action . c_sendMessageUploadDocumentAction ( ) . vprogress . v ) ) ; break ;
case mtpc_sendMessageGeoLocationAction : history - > sendActions . insert ( user , SendAction ( SendActionChooseLocation , ms + 6000 ) ) ; break ;
case mtpc_sendMessageChooseContactAction : history - > sendActions . insert ( user , SendAction ( SendActionChooseContact , ms + 6000 ) ) ; break ;
default : return ;
}
2014-12-12 19:27:03 +03:00
2016-06-22 21:41:13 +03:00
user - > madeAction ( when ) ;
2015-06-10 15:48:26 +03:00
2014-12-12 19:27:03 +03:00
TypingHistories : : const_iterator i = typing . find ( history ) ;
if ( i = = typing . cend ( ) ) {
typing . insert ( history , ms ) ;
2016-01-05 12:52:40 +08:00
history - > typingDots = 0 ;
_a_typings . start ( ) ;
2014-12-12 19:27:03 +03:00
}
2016-01-05 12:52:40 +08:00
history - > updateTyping ( ms , true ) ;
2014-12-12 19:27:03 +03:00
}
2015-12-08 15:33:37 +03:00
void Histories : : step_typings ( uint64 ms , bool timer ) {
2014-12-12 19:27:03 +03:00
for ( TypingHistories : : iterator i = typing . begin ( ) , e = typing . end ( ) ; i ! = e ; ) {
2016-01-05 12:52:40 +08:00
i . key ( ) - > typingDots = ( ms - i . value ( ) ) / 150 ;
i . key ( ) - > updateTyping ( ms ) ;
2015-08-01 11:33:00 +03:00
if ( i . key ( ) - > typing . isEmpty ( ) & & i . key ( ) - > sendActions . isEmpty ( ) ) {
2014-12-12 19:27:03 +03:00
i = typing . erase ( i ) ;
} else {
+ + i ;
}
}
2015-12-08 15:33:37 +03:00
if ( typing . isEmpty ( ) ) {
_a_typings . stop ( ) ;
}
2014-12-12 19:27:03 +03:00
}
2014-12-18 21:40:49 +03:00
void Histories : : remove ( const PeerId & peer ) {
2015-09-19 12:13:21 +03:00
Map : : iterator i = map . find ( peer ) ;
if ( i ! = map . cend ( ) ) {
typing . remove ( i . value ( ) ) ;
delete i . value ( ) ;
map . erase ( i ) ;
2014-12-18 21:40:49 +03:00
}
}
2016-04-08 18:16:52 +04:00
namespace {
void checkForSwitchInlineButton ( HistoryItem * item ) {
if ( item - > out ( ) | | ! item - > hasSwitchInlineButton ( ) ) {
return ;
}
if ( UserData * user = item - > history ( ) - > peer - > asUser ( ) ) {
if ( ! user - > botInfo | | ! user - > botInfo - > inlineReturnPeerId ) {
return ;
}
if ( auto markup = item - > Get < HistoryMessageReplyMarkup > ( ) ) {
2016-09-03 17:27:22 -04:00
for_const ( auto & row , markup - > rows ) {
for_const ( auto & button , row ) {
2016-09-02 12:11:23 -04:00
if ( button . type = = HistoryMessageReplyMarkup : : Button : : Type : : SwitchInline ) {
2016-04-08 18:16:52 +04:00
Notify : : switchInlineBotButtonReceived ( QString : : fromUtf8 ( button . data ) ) ;
return ;
}
}
}
}
}
}
} // namespace
2015-09-21 23:57:42 +03:00
HistoryItem * Histories : : addNewMessage ( const MTPMessage & msg , NewMessageType type ) {
2016-08-12 19:28:10 +03:00
auto peer = peerFromMessage ( msg ) ;
2016-04-08 18:16:52 +04:00
if ( ! peer ) return nullptr ;
2014-05-30 12:53:19 +04:00
2016-08-12 19:28:10 +03:00
auto result = App : : history ( peer ) - > addNewMessage ( msg , type ) ;
2016-04-08 18:16:52 +04:00
if ( result & & type = = NewMessageUnread ) {
checkForSwitchInlineButton ( result ) ;
}
return result ;
2014-05-30 12:53:19 +04:00
}
2014-07-04 15:12:54 +04:00
2016-08-26 22:49:18 -06:00
int Histories : : unreadBadge ( ) const {
return _unreadFull - ( Global : : IncludeMuted ( ) ? 0 : _unreadMuted ) ;
}
bool Histories : : unreadOnlyMuted ( ) const {
return Global : : IncludeMuted ( ) ? ( _unreadMuted > = _unreadFull ) : false ;
}
2016-03-19 21:32:17 +03:00
HistoryItem * History : : createItem ( const MTPMessage & msg , bool applyServiceAction , bool detachExistingItem ) {
2015-04-30 16:53:36 +03:00
MsgId msgId = 0 ;
switch ( msg . type ( ) ) {
case mtpc_messageEmpty : msgId = msg . c_messageEmpty ( ) . vid . v ; break ;
case mtpc_message : msgId = msg . c_message ( ) . vid . v ; break ;
case mtpc_messageService : msgId = msg . c_messageService ( ) . vid . v ; break ;
}
2016-03-15 13:37:56 +03:00
if ( ! msgId ) return nullptr ;
2015-04-30 16:53:36 +03:00
2015-12-23 15:55:32 +03:00
HistoryItem * result = App : : histItemById ( channelId ( ) , msgId ) ;
if ( result ) {
2016-03-19 21:32:17 +03:00
if ( ! result - > detached ( ) & & detachExistingItem ) {
result - > detach ( ) ;
2015-05-01 02:05:19 +03:00
}
2015-12-28 00:37:48 +03:00
if ( msg . type ( ) = = mtpc_message ) {
2016-01-09 15:11:23 +08:00
result - > updateMedia ( msg . c_message ( ) . has_media ( ) ? ( & msg . c_message ( ) . vmedia ) : 0 ) ;
2015-12-28 00:37:48 +03:00
if ( applyServiceAction ) {
App : : checkSavedGif ( result ) ;
2015-12-23 15:55:32 +03:00
}
2015-04-30 16:53:36 +03:00
}
2015-12-28 00:37:48 +03:00
return result ;
2015-04-30 16:53:36 +03:00
}
2014-05-30 12:53:19 +04:00
switch ( msg . type ( ) ) {
case mtpc_messageEmpty :
2016-03-25 14:29:45 +03:00
result = HistoryService : : create ( this , msg . c_messageEmpty ( ) . vid . v , date ( ) , lang ( lng_message_empty ) ) ;
2014-05-30 12:53:19 +04:00
break ;
2015-07-15 14:23:59 +03:00
case mtpc_message : {
2016-04-08 14:44:35 +04:00
const auto & m ( msg . c_message ( ) ) ;
2015-07-15 14:23:59 +03:00
int badMedia = 0 ; // 1 - unsupported, 2 - empty
2015-08-30 17:57:21 +03:00
if ( m . has_media ( ) ) switch ( m . vmedia . type ( ) ) {
2015-07-21 16:55:23 +02:00
case mtpc_messageMediaEmpty :
case mtpc_messageMediaContact : break ;
2015-07-15 14:23:59 +03:00
case mtpc_messageMediaGeo :
switch ( m . vmedia . c_messageMediaGeo ( ) . vgeo . type ( ) ) {
case mtpc_geoPoint : break ;
case mtpc_geoPointEmpty : badMedia = 2 ; break ;
default : badMedia = 1 ; break ;
}
break ;
case mtpc_messageMediaVenue :
switch ( m . vmedia . c_messageMediaVenue ( ) . vgeo . type ( ) ) {
case mtpc_geoPoint : break ;
case mtpc_geoPointEmpty : badMedia = 2 ; break ;
default : badMedia = 1 ; break ;
}
break ;
case mtpc_messageMediaPhoto :
switch ( m . vmedia . c_messageMediaPhoto ( ) . vphoto . type ( ) ) {
case mtpc_photo : break ;
case mtpc_photoEmpty : badMedia = 2 ; break ;
default : badMedia = 1 ; break ;
}
break ;
case mtpc_messageMediaDocument :
switch ( m . vmedia . c_messageMediaDocument ( ) . vdocument . type ( ) ) {
2015-12-28 13:28:00 +03:00
case mtpc_document : break ;
2015-07-15 14:23:59 +03:00
case mtpc_documentEmpty : badMedia = 2 ; break ;
default : badMedia = 1 ; break ;
}
break ;
case mtpc_messageMediaWebPage :
switch ( m . vmedia . c_messageMediaWebPage ( ) . vwebpage . type ( ) ) {
case mtpc_webPage :
case mtpc_webPageEmpty :
case mtpc_webPagePending : break ;
default : badMedia = 1 ; break ;
}
break ;
case mtpc_messageMediaUnsupported :
default : badMedia = 1 ; break ;
2015-03-13 16:01:25 +03:00
}
2016-01-07 17:49:54 +08:00
if ( badMedia = = 1 ) {
QString text ( lng_message_unsupported ( lt_link , qsl ( " https://desktop.telegram.org " ) ) ) ;
2016-04-30 20:04:14 +03:00
EntitiesInText entities ;
textParseEntities ( text , _historyTextNoMonoOptions . flags , & entities ) ;
2016-01-07 17:49:54 +08:00
entities . push_front ( EntityInText ( EntityInTextItalic , 0 , text . size ( ) ) ) ;
2016-05-06 20:33:48 +03:00
result = HistoryMessage : : create ( this , m . vid . v , m . vflags . v , m . vreply_to_msg_id . v , m . vvia_bot_id . v , date ( m . vdate ) , m . vfrom_id . v , { text , entities } ) ;
2016-01-03 19:05:22 +08:00
} else if ( badMedia ) {
2016-03-29 20:17:00 +03:00
result = HistoryService : : create ( this , m . vid . v , date ( m . vdate ) , lang ( lng_message_empty ) , m . vflags . v , m . has_from_id ( ) ? m . vfrom_id . v : 0 ) ;
2015-07-15 14:23:59 +03:00
} else {
2016-03-25 14:29:45 +03:00
result = HistoryMessage : : create ( this , m ) ;
2015-06-15 20:19:24 +03:00
}
2015-07-15 14:23:59 +03:00
} break ;
2014-05-30 12:53:19 +04:00
case mtpc_messageService : {
2016-04-08 14:44:35 +04:00
const auto & d ( msg . c_messageService ( ) ) ;
2016-03-25 14:29:45 +03:00
result = HistoryService : : create ( this , d ) ;
2014-05-30 12:53:19 +04:00
2015-09-20 11:55:41 +03:00
if ( applyServiceAction ) {
2016-04-08 14:44:35 +04:00
const auto & action ( d . vaction ) ;
2014-05-30 12:53:19 +04:00
switch ( d . vaction . type ( ) ) {
case mtpc_messageActionChatAddUser : {
2016-04-08 14:44:35 +04:00
const auto & d ( action . c_messageActionChatAddUser ( ) ) ;
2015-11-19 18:56:29 +03:00
if ( peer - > isMegagroup ( ) ) {
2016-04-08 14:44:35 +04:00
const auto & v ( d . vusers . c_vector ( ) . v ) ;
2015-11-19 18:56:29 +03:00
for ( int32 i = 0 , l = v . size ( ) ; i < l ; + + i ) {
if ( UserData * user = App : : userLoaded ( peerFromUser ( v . at ( i ) ) ) ) {
if ( peer - > asChannel ( ) - > mgInfo - > lastParticipants . indexOf ( user ) < 0 ) {
peer - > asChannel ( ) - > mgInfo - > lastParticipants . push_front ( user ) ;
2015-11-24 13:40:18 +03:00
peer - > asChannel ( ) - > mgInfo - > lastParticipantsStatus | = MegagroupInfo : : LastParticipantsAdminsOutdated ;
2016-06-03 10:20:24 +03:00
Notify : : peerUpdatedDelayed ( peer , Notify : : PeerUpdate : : Flag : : MembersChanged ) ;
2015-11-19 18:56:29 +03:00
}
2015-11-20 16:34:37 +03:00
if ( user - > botInfo ) {
2016-03-10 18:42:01 +03:00
peer - > asChannel ( ) - > mgInfo - > bots . insert ( user ) ;
2015-11-20 16:34:37 +03:00
if ( peer - > asChannel ( ) - > mgInfo - > botStatus ! = 0 & & peer - > asChannel ( ) - > mgInfo - > botStatus < 2 ) {
peer - > asChannel ( ) - > mgInfo - > botStatus = 2 ;
}
}
2015-11-19 18:56:29 +03:00
}
}
}
2014-05-30 12:53:19 +04:00
} break ;
2015-04-30 16:53:36 +03:00
case mtpc_messageActionChatJoinedByLink : {
2016-04-08 14:44:35 +04:00
const auto & d ( action . c_messageActionChatJoinedByLink ( ) ) ;
2015-11-19 18:56:29 +03:00
if ( peer - > isMegagroup ( ) ) {
2015-11-20 16:34:37 +03:00
if ( result - > from ( ) - > isUser ( ) ) {
if ( peer - > asChannel ( ) - > mgInfo - > lastParticipants . indexOf ( result - > from ( ) - > asUser ( ) ) < 0 ) {
peer - > asChannel ( ) - > mgInfo - > lastParticipants . push_front ( result - > from ( ) - > asUser ( ) ) ;
2016-06-03 10:20:24 +03:00
Notify : : peerUpdatedDelayed ( peer , Notify : : PeerUpdate : : Flag : : MembersChanged ) ;
2015-11-20 16:34:37 +03:00
}
if ( result - > from ( ) - > asUser ( ) - > botInfo ) {
2016-03-10 18:42:01 +03:00
peer - > asChannel ( ) - > mgInfo - > bots . insert ( result - > from ( ) - > asUser ( ) ) ;
2015-11-20 16:34:37 +03:00
if ( peer - > asChannel ( ) - > mgInfo - > botStatus ! = 0 & & peer - > asChannel ( ) - > mgInfo - > botStatus < 2 ) {
peer - > asChannel ( ) - > mgInfo - > botStatus = 2 ;
}
}
2015-11-19 18:56:29 +03:00
}
}
2015-04-30 16:53:36 +03:00
} break ;
2014-05-30 12:53:19 +04:00
case mtpc_messageActionChatDeletePhoto : {
ChatData * chat = peer - > asChat ( ) ;
if ( chat ) chat - > setPhoto ( MTP_chatPhotoEmpty ( ) ) ;
} break ;
case mtpc_messageActionChatDeleteUser : {
2016-04-08 14:44:35 +04:00
const auto & d ( action . c_messageActionChatDeleteUser ( ) ) ;
2015-11-20 16:34:37 +03:00
PeerId uid = peerFromUser ( d . vuser_id ) ;
if ( lastKeyboardFrom = = uid ) {
2015-06-24 20:24:48 +03:00
clearLastKeyboard ( ) ;
2015-06-18 20:24:54 +03:00
}
2015-11-20 16:34:37 +03:00
if ( peer - > isMegagroup ( ) ) {
2016-06-02 16:02:55 +03:00
if ( auto user = App : : userLoaded ( uid ) ) {
auto channel = peer - > asChannel ( ) ;
auto megagroupInfo = channel - > mgInfo ;
int32 index = megagroupInfo - > lastParticipants . indexOf ( user ) ;
2015-11-20 16:34:37 +03:00
if ( index > = 0 ) {
2016-06-02 16:02:55 +03:00
megagroupInfo - > lastParticipants . removeAt ( index ) ;
2016-06-03 10:20:24 +03:00
Notify : : peerUpdatedDelayed ( peer , Notify : : PeerUpdate : : Flag : : MembersChanged ) ;
2015-11-20 16:34:37 +03:00
}
2016-06-02 16:02:55 +03:00
if ( peer - > asChannel ( ) - > membersCount ( ) > 1 ) {
peer - > asChannel ( ) - > setMembersCount ( channel - > membersCount ( ) - 1 ) ;
2016-03-14 09:10:24 +03:00
} else {
2016-06-02 16:02:55 +03:00
megagroupInfo - > lastParticipantsStatus | = MegagroupInfo : : LastParticipantsCountOutdated ;
megagroupInfo - > lastParticipantsCount = 0 ;
2016-03-14 09:10:24 +03:00
}
2016-06-02 16:02:55 +03:00
if ( megagroupInfo - > lastAdmins . contains ( user ) ) {
megagroupInfo - > lastAdmins . remove ( user ) ;
if ( channel - > adminsCount ( ) > 1 ) {
channel - > setAdminsCount ( channel - > adminsCount ( ) - 1 ) ;
2016-03-14 09:10:24 +03:00
}
2016-06-03 10:20:24 +03:00
Notify : : peerUpdatedDelayed ( peer , Notify : : PeerUpdate : : Flag : : AdminsChanged ) ;
2016-03-14 09:10:24 +03:00
}
2016-06-02 16:02:55 +03:00
megagroupInfo - > bots . remove ( user ) ;
if ( megagroupInfo - > bots . isEmpty ( ) & & megagroupInfo - > botStatus > 0 ) {
megagroupInfo - > botStatus = - 1 ;
2015-11-20 16:34:37 +03:00
}
}
}
2014-05-30 12:53:19 +04:00
} break ;
case mtpc_messageActionChatEditPhoto : {
2016-04-08 14:44:35 +04:00
const auto & d ( action . c_messageActionChatEditPhoto ( ) ) ;
2014-05-30 12:53:19 +04:00
if ( d . vphoto . type ( ) = = mtpc_photo ) {
2016-04-08 14:44:35 +04:00
const auto & sizes ( d . vphoto . c_photo ( ) . vsizes . c_vector ( ) . v ) ;
2014-05-30 12:53:19 +04:00
if ( ! sizes . isEmpty ( ) ) {
2015-09-03 13:48:40 +03:00
PhotoData * photo = App : : feedPhoto ( d . vphoto . c_photo ( ) ) ;
if ( photo ) photo - > peer = peer ;
2016-04-08 14:44:35 +04:00
const auto & smallSize ( sizes . front ( ) ) , & bigSize ( sizes . back ( ) ) ;
2015-09-03 13:48:40 +03:00
const MTPFileLocation * smallLoc = 0 , * bigLoc = 0 ;
switch ( smallSize . type ( ) ) {
case mtpc_photoSize : smallLoc = & smallSize . c_photoSize ( ) . vlocation ; break ;
case mtpc_photoCachedSize : smallLoc = & smallSize . c_photoCachedSize ( ) . vlocation ; break ;
}
switch ( bigSize . type ( ) ) {
case mtpc_photoSize : bigLoc = & bigSize . c_photoSize ( ) . vlocation ; break ;
case mtpc_photoCachedSize : bigLoc = & bigSize . c_photoCachedSize ( ) . vlocation ; break ;
}
if ( smallLoc & & bigLoc ) {
if ( peer - > isChat ( ) ) {
peer - > asChat ( ) - > setPhoto ( MTP_chatPhoto ( * smallLoc , * bigLoc ) , photo ? photo - > id : 0 ) ;
} else if ( peer - > isChannel ( ) ) {
peer - > asChannel ( ) - > setPhoto ( MTP_chatPhoto ( * smallLoc , * bigLoc ) , photo ? photo - > id : 0 ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-18 13:18:30 +03:00
peer - > loadUserpic ( ) ;
2014-05-30 12:53:19 +04:00
}
}
}
} break ;
case mtpc_messageActionChatEditTitle : {
2016-05-25 15:09:05 +03:00
auto & d ( action . c_messageActionChatEditTitle ( ) ) ;
if ( auto chat = peer - > asChat ( ) ) {
chat - > setName ( qs ( d . vtitle ) ) ;
}
2014-05-30 12:53:19 +04:00
} break ;
2015-11-06 12:48:49 -05:00
case mtpc_messageActionChatMigrateTo : {
2016-03-19 19:55:15 +03:00
peer - > asChat ( ) - > flags | = MTPDchat : : Flag : : f_deactivated ;
2015-11-06 12:48:49 -05:00
2016-04-08 14:44:35 +04:00
//const auto &d(action.c_messageActionChatMigrateTo());
2016-03-25 14:29:45 +03:00
//PeerData *channel = App::channelLoaded(d.vchannel_id.v);
2015-11-06 12:48:49 -05:00
} break ;
case mtpc_messageActionChannelMigrateFrom : {
2016-04-08 14:44:35 +04:00
//const auto &d(action.c_messageActionChannelMigrateFrom());
2016-03-25 14:29:45 +03:00
//PeerData *chat = App::chatLoaded(d.vchat_id.v);
2015-11-06 12:48:49 -05:00
} break ;
2016-03-05 23:12:55 +02:00
case mtpc_messageActionPinMessage : {
if ( d . has_reply_to_msg_id ( ) & & result & & result - > history ( ) - > peer - > isMegagroup ( ) ) {
result - > history ( ) - > peer - > asChannel ( ) - > mgInfo - > pinnedMsgId = d . vreply_to_msg_id . v ;
if ( App : : main ( ) ) emit App : : main ( ) - > peerUpdated ( result - > history ( ) - > peer ) ;
}
} break ;
2014-05-30 12:53:19 +04:00
}
}
} break ;
}
2015-12-28 00:37:48 +03:00
if ( applyServiceAction ) {
App : : checkSavedGif ( result ) ;
}
2016-03-18 22:05:08 +03:00
return result ;
2014-05-30 12:53:19 +04:00
}
2016-03-19 19:55:15 +03:00
HistoryItem * History : : createItemForwarded ( MsgId id , MTPDmessage : : Flags flags , QDateTime date , int32 from , HistoryMessage * msg ) {
2016-03-18 22:05:08 +03:00
return HistoryMessage : : create ( this , id , flags , date , from , msg ) ;
2014-05-30 12:53:19 +04:00
}
2016-04-06 12:00:37 +04:00
HistoryItem * History : : createItemDocument ( MsgId id , MTPDmessage : : Flags flags , int32 viaBotId , MsgId replyTo , QDateTime date , int32 from , DocumentData * doc , const QString & caption , const MTPReplyMarkup & markup ) {
return HistoryMessage : : create ( this , id , flags , replyTo , viaBotId , date , from , doc , caption , markup ) ;
2015-12-31 13:34:43 +08:00
}
2016-04-06 12:00:37 +04:00
HistoryItem * History : : createItemPhoto ( MsgId id , MTPDmessage : : Flags flags , int32 viaBotId , MsgId replyTo , QDateTime date , int32 from , PhotoData * photo , const QString & caption , const MTPReplyMarkup & markup ) {
return HistoryMessage : : create ( this , id , flags , replyTo , viaBotId , date , from , photo , caption , markup ) ;
2014-05-30 12:53:19 +04:00
}
2015-01-02 17:55:24 +03:00
2016-03-29 20:17:00 +03:00
HistoryItem * History : : addNewService ( MsgId msgId , QDateTime date , const QString & text , MTPDmessage : : Flags flags , bool newMsg ) {
return addNewItem ( HistoryService : : create ( this , msgId , date , text , flags ) , newMsg ) ;
2014-05-30 12:53:19 +04:00
}
2015-09-21 23:57:42 +03:00
HistoryItem * History : : addNewMessage ( const MTPMessage & msg , NewMessageType type ) {
2015-09-20 11:55:41 +03:00
if ( isChannel ( ) ) return asChannelHistory ( ) - > addNewChannelMessage ( msg , type ) ;
if ( type = = NewMessageExisting ) return addToHistory ( msg ) ;
2015-11-13 18:14:33 +03:00
if ( ! loadedAtBottom ( ) | | peer - > migrateTo ( ) ) {
2015-09-20 11:55:41 +03:00
HistoryItem * item = addToHistory ( msg ) ;
if ( item ) {
setLastMessage ( item ) ;
if ( type = = NewMessageUnread ) {
2015-10-27 20:29:39 -04:00
newItemAdded ( item ) ;
2015-09-20 11:55:41 +03:00
}
}
return item ;
}
2016-03-19 21:32:17 +03:00
return addNewToLastBlock ( msg , type ) ;
}
HistoryItem * History : : addNewToLastBlock ( const MTPMessage & msg , NewMessageType type ) {
bool applyServiceAction = ( type = = NewMessageUnread ) , detachExistingItem = ( type ! = NewMessageLast ) ;
HistoryItem * item = createItem ( msg , applyServiceAction , detachExistingItem ) ;
if ( ! item | | ! item - > detached ( ) ) {
return item ;
2015-12-25 00:27:45 +03:00
}
2016-03-19 21:32:17 +03:00
return addNewItem ( item , ( type = = NewMessageUnread ) ) ;
2014-05-30 12:53:19 +04:00
}
2015-09-21 23:57:42 +03:00
HistoryItem * History : : addToHistory ( const MTPMessage & msg ) {
2016-03-19 21:32:17 +03:00
return createItem ( msg , false , false ) ;
2014-07-04 15:12:54 +04:00
}
2016-03-19 19:55:15 +03:00
HistoryItem * History : : addNewForwarded ( MsgId id , MTPDmessage : : Flags flags , QDateTime date , int32 from , HistoryMessage * item ) {
2016-03-19 21:32:17 +03:00
return addNewItem ( createItemForwarded ( id , flags , date , from , item ) , true ) ;
2014-05-30 12:53:19 +04:00
}
2016-04-06 12:00:37 +04:00
HistoryItem * History : : addNewDocument ( MsgId id , MTPDmessage : : Flags flags , int32 viaBotId , MsgId replyTo , QDateTime date , int32 from , DocumentData * doc , const QString & caption , const MTPReplyMarkup & markup ) {
return addNewItem ( createItemDocument ( id , flags , viaBotId , replyTo , date , from , doc , caption , markup ) , true ) ;
2015-12-31 13:34:43 +08:00
}
2016-04-06 12:00:37 +04:00
HistoryItem * History : : addNewPhoto ( MsgId id , MTPDmessage : : Flags flags , int32 viaBotId , MsgId replyTo , QDateTime date , int32 from , PhotoData * photo , const QString & caption , const MTPReplyMarkup & markup ) {
return addNewItem ( createItemPhoto ( id , flags , viaBotId , replyTo , date , from , photo , caption , markup ) , true ) ;
2014-05-30 12:53:19 +04:00
}
2016-01-03 09:43:42 +08:00
bool History : : addToOverview ( MediaOverviewType type , MsgId msgId , AddToOverviewMethod method ) {
bool adding = false ;
switch ( method ) {
case AddToOverviewNew :
case AddToOverviewFront : adding = ( overviewIds [ type ] . constFind ( msgId ) = = overviewIds [ type ] . cend ( ) ) ; break ;
case AddToOverviewBack : adding = ( overviewCountData [ type ] ! = 0 ) ; break ;
}
if ( ! adding ) return false ;
overviewIds [ type ] . insert ( msgId , NullType ( ) ) ;
switch ( method ) {
case AddToOverviewNew :
case AddToOverviewBack : overview [ type ] . push_back ( msgId ) ; break ;
case AddToOverviewFront : overview [ type ] . push_front ( msgId ) ; break ;
}
if ( method = = AddToOverviewNew ) {
2015-11-18 16:11:56 +03:00
if ( overviewCountData [ type ] > 0 ) {
+ + overviewCountData [ type ] ;
}
2015-08-28 18:15:56 +03:00
if ( App : : wnd ( ) ) App : : wnd ( ) - > mediaOverviewUpdated ( peer , type ) ;
}
2016-01-03 09:43:42 +08:00
return true ;
2015-08-28 18:15:56 +03:00
}
2016-01-03 09:43:42 +08:00
void History : : eraseFromOverview ( MediaOverviewType type , MsgId msgId ) {
if ( overviewIds [ type ] . isEmpty ( ) ) return ;
History : : MediaOverviewIds : : iterator i = overviewIds [ type ] . find ( msgId ) ;
if ( i = = overviewIds [ type ] . cend ( ) ) return ;
overviewIds [ type ] . erase ( i ) ;
for ( History : : MediaOverview : : iterator i = overview [ type ] . begin ( ) , e = overview [ type ] . end ( ) ; i ! = e ; + + i ) {
if ( ( * i ) = = msgId ) {
overview [ type ] . erase ( i ) ;
if ( overviewCountData [ type ] > 0 ) {
- - overviewCountData [ type ] ;
}
break ;
}
2015-08-28 18:15:56 +03:00
}
2016-01-03 09:43:42 +08:00
if ( App : : wnd ( ) ) App : : wnd ( ) - > mediaOverviewUpdated ( peer , type ) ;
2015-08-28 18:15:56 +03:00
}
2016-03-19 21:32:17 +03:00
HistoryItem * History : : addNewItem ( HistoryItem * adding , bool newMsg ) {
2016-03-31 14:37:58 +04:00
t_assert ( ! isBuildingFrontBlock ( ) ) ;
addItemToBlock ( adding ) ;
2016-03-19 21:32:17 +03:00
2015-08-07 15:11:50 +03:00
setLastMessage ( adding ) ;
2014-05-30 12:53:19 +04:00
if ( newMsg ) {
2015-10-27 20:29:39 -04:00
newItemAdded ( adding ) ;
2014-07-04 15:12:54 +04:00
}
2015-08-07 15:11:50 +03:00
2016-01-03 09:43:42 +08:00
adding - > addToOverview ( AddToOverviewNew ) ;
2015-06-15 20:19:24 +03:00
if ( adding - > from ( ) - > id ) {
2015-11-09 12:51:22 +03:00
if ( adding - > from ( ) - > isUser ( ) ) {
QList < UserData * > * lastAuthors = 0 ;
if ( peer - > isChat ( ) ) {
lastAuthors = & peer - > asChat ( ) - > lastAuthors ;
2015-11-19 18:56:29 +03:00
} else if ( peer - > isMegagroup ( ) ) {
2015-11-09 12:51:22 +03:00
lastAuthors = & peer - > asChannel ( ) - > mgInfo - > lastParticipants ;
2015-11-20 16:34:37 +03:00
if ( adding - > from ( ) - > asUser ( ) - > botInfo ) {
2016-03-10 18:42:01 +03:00
peer - > asChannel ( ) - > mgInfo - > bots . insert ( adding - > from ( ) - > asUser ( ) ) ;
2015-11-20 16:34:37 +03:00
if ( peer - > asChannel ( ) - > mgInfo - > botStatus ! = 0 & & peer - > asChannel ( ) - > mgInfo - > botStatus < 2 ) {
peer - > asChannel ( ) - > mgInfo - > botStatus = 2 ;
}
}
2015-11-09 12:51:22 +03:00
}
if ( lastAuthors ) {
int prev = lastAuthors - > indexOf ( adding - > from ( ) - > asUser ( ) ) ;
if ( prev > 0 ) {
lastAuthors - > removeAt ( prev ) ;
2015-11-24 13:40:18 +03:00
} else if ( prev < 0 & & peer - > isMegagroup ( ) ) { // nothing is outdated if just reordering
peer - > asChannel ( ) - > mgInfo - > lastParticipantsStatus | = MegagroupInfo : : LastParticipantsAdminsOutdated ;
2015-11-09 12:51:22 +03:00
}
if ( prev ) {
lastAuthors - > push_front ( adding - > from ( ) - > asUser ( ) ) ;
}
2016-06-03 10:20:24 +03:00
if ( peer - > isMegagroup ( ) ) {
Notify : : peerUpdatedDelayed ( peer , Notify : : PeerUpdate : : Flag : : MembersChanged ) ;
}
2015-06-15 20:19:24 +03:00
}
}
2016-03-28 15:51:22 +03:00
if ( adding - > definesReplyKeyboard ( ) ) {
MTPDreplyKeyboardMarkup : : Flags markupFlags = adding - > replyKeyboardFlags ( ) ;
2016-03-19 19:55:15 +03:00
if ( ! ( markupFlags & MTPDreplyKeyboardMarkup : : Flag : : f_selective ) | | adding - > mentionsMe ( ) ) {
2016-03-10 18:42:01 +03:00
OrderedSet < PeerData * > * markupSenders = 0 ;
2015-09-21 23:57:42 +03:00
if ( peer - > isChat ( ) ) {
2015-11-09 12:51:22 +03:00
markupSenders = & peer - > asChat ( ) - > markupSenders ;
} else if ( peer - > isMegagroup ( ) ) {
markupSenders = & peer - > asChannel ( ) - > mgInfo - > markupSenders ;
}
if ( markupSenders ) {
2016-03-10 18:42:01 +03:00
markupSenders - > insert ( adding - > from ( ) ) ;
2015-06-22 11:51:39 +03:00
}
2016-03-19 19:55:15 +03:00
if ( markupFlags & MTPDreplyKeyboardMarkup_ClientFlag : : f_zero ) { // zero markup means replyKeyboardHide
2015-11-09 12:51:22 +03:00
if ( lastKeyboardFrom = = adding - > from ( ) - > id | | ( ! lastKeyboardInited & & ! peer - > isChat ( ) & & ! peer - > isMegagroup ( ) & & ! adding - > out ( ) ) ) {
2015-06-24 20:24:48 +03:00
clearLastKeyboard ( ) ;
2015-06-22 11:51:39 +03:00
}
} else {
2015-11-09 12:51:22 +03:00
bool botNotInChat = false ;
if ( peer - > isChat ( ) ) {
botNotInChat = adding - > from ( ) - > isUser ( ) & & ( ! peer - > canWrite ( ) | | ! peer - > asChat ( ) - > participants . isEmpty ( ) ) & & ! peer - > asChat ( ) - > participants . contains ( adding - > from ( ) - > asUser ( ) ) ;
} else if ( peer - > isMegagroup ( ) ) {
2015-11-24 19:19:18 +03:00
botNotInChat = adding - > from ( ) - > isUser ( ) & & ( ! peer - > canWrite ( ) | | peer - > asChannel ( ) - > mgInfo - > botStatus ! = 0 ) & & ! peer - > asChannel ( ) - > mgInfo - > bots . contains ( adding - > from ( ) - > asUser ( ) ) ;
2015-11-09 12:51:22 +03:00
}
if ( botNotInChat ) {
clearLastKeyboard ( ) ;
} else {
lastKeyboardInited = true ;
lastKeyboardId = adding - > id ;
lastKeyboardFrom = adding - > from ( ) - > id ;
lastKeyboardUsed = false ;
}
2015-06-17 22:43:03 +03:00
}
}
2015-03-19 12:18:19 +03:00
}
}
2015-09-21 23:57:42 +03:00
2014-07-04 15:12:54 +04:00
return adding ;
}
2014-10-22 22:39:03 +04:00
void History : : unregTyping ( UserData * from ) {
2015-08-01 11:33:00 +03:00
uint64 updateAtMs = 0 ;
2014-10-22 22:39:03 +04:00
TypingUsers : : iterator i = typing . find ( from ) ;
if ( i ! = typing . end ( ) ) {
2016-01-05 12:52:40 +08:00
updateAtMs = getms ( ) ;
2015-08-01 11:33:00 +03:00
i . value ( ) = updateAtMs ;
}
SendActionUsers : : iterator j = sendActions . find ( from ) ;
if ( j ! = sendActions . end ( ) ) {
2016-01-05 12:52:40 +08:00
if ( ! updateAtMs ) updateAtMs = getms ( ) ;
2015-08-01 11:33:00 +03:00
j . value ( ) . until = updateAtMs ;
}
if ( updateAtMs ) {
2016-01-05 12:52:40 +08:00
updateTyping ( updateAtMs , true ) ;
2014-10-22 22:39:03 +04:00
}
}
2014-07-04 15:12:54 +04:00
void History : : newItemAdded ( HistoryItem * item ) {
App : : checkImageCacheSize ( ) ;
2015-09-04 16:01:31 +03:00
if ( item - > from ( ) & & item - > from ( ) - > isUser ( ) ) {
2016-02-17 19:37:21 +03:00
if ( item - > from ( ) = = item - > author ( ) ) {
unregTyping ( item - > from ( ) - > asUser ( ) ) ;
}
2016-06-22 21:41:13 +03:00
MTPint itemServerTime ;
toServerTime ( item - > date . toTime_t ( ) , itemServerTime ) ;
item - > from ( ) - > asUser ( ) - > madeAction ( itemServerTime . v ) ;
2014-05-30 12:53:19 +04:00
}
2014-07-04 15:12:54 +04:00
if ( item - > out ( ) ) {
2016-03-18 22:05:08 +03:00
if ( unreadBar ) unreadBar - > destroyUnreadBar ( ) ;
2015-10-27 20:29:39 -04:00
if ( ! item - > unread ( ) ) {
outboxRead ( item ) ;
}
2014-07-04 15:12:54 +04:00
} else if ( item - > unread ( ) ) {
2015-09-23 20:43:08 +03:00
bool skip = false ;
if ( ! isChannel ( ) | | peer - > asChannel ( ) - > amIn ( ) ) {
notifies . push_back ( item ) ;
App : : main ( ) - > newUnreadMsg ( this , item ) ;
}
2015-11-13 18:14:33 +03:00
} else if ( ! item - > isGroupMigrate ( ) | | ! peer - > isMegagroup ( ) ) {
2015-10-27 20:29:39 -04:00
inboxRead ( item ) ;
2014-07-04 15:12:54 +04:00
}
2014-05-30 12:53:19 +04:00
}
2016-03-31 14:37:58 +04:00
HistoryBlock * History : : prepareBlockForAddingItem ( ) {
if ( isBuildingFrontBlock ( ) ) {
2016-03-31 15:18:21 +04:00
if ( _buildingFrontBlock - > block ) {
return _buildingFrontBlock - > block ;
2016-03-31 14:37:58 +04:00
}
2016-03-31 15:18:21 +04:00
HistoryBlock * result = _buildingFrontBlock - > block = new HistoryBlock ( this ) ;
if ( _buildingFrontBlock - > expectedItemsCount > 0 ) {
result - > items . reserve ( _buildingFrontBlock - > expectedItemsCount + 1 ) ;
2016-03-31 14:37:58 +04:00
}
result - > setIndexInHistory ( 0 ) ;
blocks . push_front ( result ) ;
for ( int i = 1 , l = blocks . size ( ) ; i < l ; + + i ) {
blocks . at ( i ) - > setIndexInHistory ( i ) ;
}
return result ;
}
bool addNewBlock = blocks . isEmpty ( ) | | ( blocks . back ( ) - > items . size ( ) > = MessagesPerPage ) ;
if ( ! addNewBlock ) {
return blocks . back ( ) ;
}
HistoryBlock * result = new HistoryBlock ( this ) ;
result - > setIndexInHistory ( blocks . size ( ) ) ;
blocks . push_back ( result ) ;
result - > items . reserve ( MessagesPerPage ) ;
return result ;
} ;
void History : : addItemToBlock ( HistoryItem * item ) {
t_assert ( item ! = nullptr ) ;
t_assert ( item - > detached ( ) ) ;
HistoryBlock * block = prepareBlockForAddingItem ( ) ;
2016-03-18 22:05:08 +03:00
item - > attachToBlock ( block , block - > items . size ( ) ) ;
2015-09-19 12:13:21 +03:00
block - > items . push_back ( item ) ;
2016-03-19 19:55:15 +03:00
item - > previousItemChanged ( ) ;
2015-09-19 12:13:21 +03:00
2016-03-31 15:18:21 +04:00
if ( isBuildingFrontBlock ( ) & & _buildingFrontBlock - > expectedItemsCount > 0 ) {
- - _buildingFrontBlock - > expectedItemsCount ;
2015-09-19 12:13:21 +03:00
}
}
2016-05-27 19:47:46 +03:00
void History : : addOlderSlice ( const QVector < MTPMessage > & slice ) {
2014-07-04 15:12:54 +04:00
if ( slice . isEmpty ( ) ) {
oldLoaded = true ;
2016-05-27 19:47:46 +03:00
if ( isChannel ( ) ) {
asChannelHistory ( ) - > checkJoinedMessage ( ) ;
asChannelHistory ( ) - > checkMaxReadMessageDate ( ) ;
2015-09-21 23:57:42 +03:00
}
2016-05-27 19:47:46 +03:00
return ;
2014-07-04 15:12:54 +04:00
}
2014-05-30 12:53:19 +04:00
2016-05-27 19:47:46 +03:00
startBuildingFrontBlock ( slice . size ( ) ) ;
2016-03-19 21:32:17 +03:00
2016-03-15 13:37:56 +03:00
for ( auto i = slice . cend ( ) , e = slice . cbegin ( ) ; i ! = e ; ) {
2015-09-19 12:13:21 +03:00
- - i ;
2016-03-19 21:32:17 +03:00
HistoryItem * adding = createItem ( * i , false , true ) ;
2015-09-19 12:13:21 +03:00
if ( ! adding ) continue ;
2016-03-31 14:37:58 +04:00
addItemToBlock ( adding ) ;
2015-09-19 12:13:21 +03:00
}
2016-03-31 14:37:58 +04:00
HistoryBlock * block = finishBuildingFrontBlock ( ) ;
if ( ! block ) {
// If no items were added it means we've loaded everything old.
2016-03-19 21:32:17 +03:00
oldLoaded = true ;
} else if ( loadedAtBottom ( ) ) { // add photos to overview and authors to lastAuthors / lastParticipants
bool channel = isChannel ( ) ;
int32 mask = 0 ;
QList < UserData * > * lastAuthors = 0 ;
OrderedSet < PeerData * > * markupSenders = 0 ;
if ( peer - > isChat ( ) ) {
lastAuthors = & peer - > asChat ( ) - > lastAuthors ;
markupSenders = & peer - > asChat ( ) - > markupSenders ;
} else if ( peer - > isMegagroup ( ) ) {
lastAuthors = & peer - > asChannel ( ) - > mgInfo - > lastParticipants ;
markupSenders = & peer - > asChannel ( ) - > mgInfo - > markupSenders ;
}
for ( int32 i = block - > items . size ( ) ; i > 0 ; - - i ) {
HistoryItem * item = block - > items [ i - 1 ] ;
mask | = item - > addToOverview ( AddToOverviewFront ) ;
if ( item - > from ( ) - > id ) {
if ( lastAuthors ) { // chats
if ( item - > from ( ) - > isUser ( ) ) {
if ( ! lastAuthors - > contains ( item - > from ( ) - > asUser ( ) ) ) {
lastAuthors - > push_back ( item - > from ( ) - > asUser ( ) ) ;
if ( peer - > isMegagroup ( ) ) {
peer - > asChannel ( ) - > mgInfo - > lastParticipantsStatus | = MegagroupInfo : : LastParticipantsAdminsOutdated ;
2016-06-03 10:20:24 +03:00
Notify : : peerUpdatedDelayed ( peer , Notify : : PeerUpdate : : Flag : : MembersChanged ) ;
2015-11-24 13:40:18 +03:00
}
2015-06-17 22:43:03 +03:00
}
2015-11-09 12:51:22 +03:00
}
2016-02-17 19:37:21 +03:00
}
2016-03-19 21:32:17 +03:00
}
if ( item - > author ( ) - > id ) {
if ( markupSenders ) { // chats with bots
2016-03-28 15:51:22 +03:00
if ( ! lastKeyboardInited & & item - > definesReplyKeyboard ( ) & & ! item - > out ( ) ) {
MTPDreplyKeyboardMarkup : : Flags markupFlags = item - > replyKeyboardFlags ( ) ;
2016-03-19 21:32:17 +03:00
if ( ! ( markupFlags & MTPDreplyKeyboardMarkup : : Flag : : f_selective ) | | item - > mentionsMe ( ) ) {
bool wasKeyboardHide = markupSenders - > contains ( item - > author ( ) ) ;
if ( ! wasKeyboardHide ) {
markupSenders - > insert ( item - > author ( ) ) ;
}
if ( ! ( markupFlags & MTPDreplyKeyboardMarkup_ClientFlag : : f_zero ) ) {
if ( ! lastKeyboardInited ) {
bool botNotInChat = false ;
if ( peer - > isChat ( ) ) {
botNotInChat = ( ! peer - > canWrite ( ) | | ! peer - > asChat ( ) - > participants . isEmpty ( ) ) & & item - > author ( ) - > isUser ( ) & & ! peer - > asChat ( ) - > participants . contains ( item - > author ( ) - > asUser ( ) ) ;
} else if ( peer - > isMegagroup ( ) ) {
botNotInChat = ( ! peer - > canWrite ( ) | | peer - > asChannel ( ) - > mgInfo - > botStatus ! = 0 ) & & item - > author ( ) - > isUser ( ) & & ! peer - > asChannel ( ) - > mgInfo - > bots . contains ( item - > author ( ) - > asUser ( ) ) ;
}
if ( wasKeyboardHide | | botNotInChat ) {
clearLastKeyboard ( ) ;
} else {
lastKeyboardInited = true ;
lastKeyboardId = item - > id ;
lastKeyboardFrom = item - > author ( ) - > id ;
lastKeyboardUsed = false ;
2015-06-17 22:43:03 +03:00
}
}
}
}
2016-03-19 21:32:17 +03:00
}
2016-03-28 15:51:22 +03:00
} else if ( ! lastKeyboardInited & & item - > definesReplyKeyboard ( ) & & ! item - > out ( ) ) { // conversations with bots
MTPDreplyKeyboardMarkup : : Flags markupFlags = item - > replyKeyboardFlags ( ) ;
2016-03-19 21:32:17 +03:00
if ( ! ( markupFlags & MTPDreplyKeyboardMarkup : : Flag : : f_selective ) | | item - > mentionsMe ( ) ) {
if ( markupFlags & MTPDreplyKeyboardMarkup_ClientFlag : : f_zero ) {
clearLastKeyboard ( ) ;
} else {
lastKeyboardInited = true ;
lastKeyboardId = item - > id ;
lastKeyboardFrom = item - > author ( ) - > id ;
lastKeyboardUsed = false ;
2015-06-15 20:19:24 +03:00
}
}
}
2014-08-11 13:03:45 +04:00
}
}
2016-03-19 21:32:17 +03:00
for ( int32 t = 0 ; t < OverviewCount ; + + t ) {
if ( ( mask & ( 1 < < t ) ) & & App : : wnd ( ) ) App : : wnd ( ) - > mediaOverviewUpdated ( peer , MediaOverviewType ( t ) ) ;
}
}
2015-09-21 23:57:42 +03:00
if ( isChannel ( ) ) {
asChannelHistory ( ) - > checkJoinedMessage ( ) ;
asChannelHistory ( ) - > checkMaxReadMessageDate ( ) ;
}
2016-04-11 11:43:40 +04:00
checkLastMsg ( ) ;
2014-05-30 12:53:19 +04:00
}
2016-05-27 19:47:46 +03:00
void History : : addNewerSlice ( const QVector < MTPMessage > & slice ) {
2015-09-24 19:05:06 +03:00
bool wasEmpty = isEmpty ( ) , wasLoadedAtBottom = loadedAtBottom ( ) ;
2014-07-04 15:12:54 +04:00
if ( slice . isEmpty ( ) ) {
newLoaded = true ;
2016-04-11 11:43:40 +04:00
if ( ! lastMsg ) {
setLastMessage ( lastImportantMessage ( ) ) ;
}
2014-07-04 15:12:54 +04:00
}
2016-03-31 14:37:58 +04:00
t_assert ( ! isBuildingFrontBlock ( ) ) ;
2016-05-27 19:47:46 +03:00
if ( ! slice . isEmpty ( ) ) {
2016-03-31 14:37:58 +04:00
bool atLeastOneAdded = false ;
2016-03-15 13:37:56 +03:00
for ( auto i = slice . cend ( ) , e = slice . cbegin ( ) ; i ! = e ; ) {
2015-09-24 19:05:06 +03:00
- - i ;
2016-03-19 21:32:17 +03:00
HistoryItem * adding = createItem ( * i , false , true ) ;
2015-09-24 19:05:06 +03:00
if ( ! adding ) continue ;
2014-07-04 15:12:54 +04:00
2016-03-31 14:37:58 +04:00
addItemToBlock ( adding ) ;
atLeastOneAdded = true ;
2015-09-24 19:05:06 +03:00
}
2015-09-19 12:13:21 +03:00
2016-03-31 14:37:58 +04:00
if ( ! atLeastOneAdded ) {
2015-09-24 19:05:06 +03:00
newLoaded = true ;
setLastMessage ( lastImportantMessage ( ) ) ;
}
2014-07-04 15:12:54 +04:00
}
2015-12-23 17:18:42 +03:00
2016-08-14 22:15:45 +03:00
if ( ! wasLoadedAtBottom ) {
checkAddAllToOverview ( ) ;
2014-08-11 13:03:45 +04:00
}
2015-09-21 23:57:42 +03:00
if ( isChannel ( ) ) asChannelHistory ( ) - > checkJoinedMessage ( ) ;
2016-04-11 11:43:40 +04:00
checkLastMsg ( ) ;
}
void History : : checkLastMsg ( ) {
if ( lastMsg ) {
2016-05-27 19:47:46 +03:00
if ( ! newLoaded & & ! lastMsg - > detached ( ) ) {
2016-04-11 11:43:40 +04:00
newLoaded = true ;
2016-08-14 22:15:45 +03:00
checkAddAllToOverview ( ) ;
2016-04-11 11:43:40 +04:00
}
} else if ( newLoaded ) {
setLastMessage ( lastImportantMessage ( ) ) ;
}
2014-07-04 15:12:54 +04:00
}
2016-08-14 22:15:45 +03:00
void History : : checkAddAllToOverview ( ) {
if ( ! loadedAtBottom ( ) ) {
return ;
}
int32 mask = 0 ;
for ( int32 i = 0 ; i < OverviewCount ; + + i ) {
if ( overviewCountData [ i ] = = 0 ) continue ; // all loaded
if ( ! overview [ i ] . isEmpty ( ) | | ! overviewIds [ i ] . isEmpty ( ) ) {
overview [ i ] . clear ( ) ;
overviewIds [ i ] . clear ( ) ;
mask | = ( 1 < < i ) ;
}
}
for_const ( HistoryBlock * block , blocks ) {
for_const ( HistoryItem * item , block - > items ) {
mask | = item - > addToOverview ( AddToOverviewBack ) ;
}
}
for ( int32 t = 0 ; t < OverviewCount ; + + t ) {
if ( ( mask & ( 1 < < t ) ) & & App : : wnd ( ) ) App : : wnd ( ) - > mediaOverviewUpdated ( peer , MediaOverviewType ( t ) ) ;
}
}
2016-03-22 18:23:34 +03:00
int History : : countUnread ( MsgId upTo ) {
int result = 0 ;
2016-03-15 13:37:56 +03:00
for ( auto i = blocks . cend ( ) , e = blocks . cbegin ( ) ; i ! = e ; ) {
2015-09-06 13:17:09 +03:00
- - i ;
2016-03-15 13:37:56 +03:00
for ( auto j = ( * i ) - > items . cend ( ) , en = ( * i ) - > items . cbegin ( ) ; j ! = en ; ) {
2015-09-06 13:17:09 +03:00
- - j ;
if ( ( * j ) - > id > 0 & & ( * j ) - > id < = upTo ) {
break ;
} else if ( ! ( * j ) - > out ( ) & & ( * j ) - > unread ( ) & & ( * j ) - > id > upTo ) {
+ + result ;
}
}
}
return result ;
}
2015-09-19 12:13:21 +03:00
void History : : updateShowFrom ( ) {
if ( showFrom ) return ;
2016-03-15 13:37:56 +03:00
for ( auto i = blocks . cend ( ) ; i ! = blocks . cbegin ( ) ; ) {
2015-09-19 12:13:21 +03:00
- - i ;
2016-03-15 13:37:56 +03:00
for ( auto j = ( * i ) - > items . cend ( ) ; j ! = ( * i ) - > items . cbegin ( ) ; ) {
2015-09-19 12:13:21 +03:00
- - j ;
2015-10-01 17:05:05 +03:00
if ( ( * j ) - > type ( ) = = HistoryItemMsg & & ( * j ) - > id > 0 & & ( ! ( * j ) - > out ( ) | | ! showFrom ) ) {
2015-09-19 12:13:21 +03:00
if ( ( * j ) - > id > = inboxReadBefore ) {
showFrom = * j ;
} else {
return ;
}
}
}
}
}
2015-09-06 13:17:09 +03:00
MsgId History : : inboxRead ( MsgId upTo ) {
2015-09-25 10:47:32 +03:00
if ( upTo < 0 ) return upTo ;
2016-04-11 14:59:01 +04:00
if ( unreadCount ( ) ) {
2015-03-13 16:01:25 +03:00
if ( upTo & & loadedAtBottom ( ) ) App : : main ( ) - > historyToDown ( this ) ;
2015-09-06 13:17:09 +03:00
setUnreadCount ( upTo ? countUnread ( upTo ) : 0 ) ;
2014-05-30 12:53:19 +04:00
}
2015-09-06 13:17:09 +03:00
if ( ! upTo ) upTo = msgIdForRead ( ) ;
2016-05-20 19:01:06 +03:00
accumulate_max ( inboxReadBefore , upTo + 1 ) ;
2015-09-06 13:17:09 +03:00
2016-02-28 14:58:30 +03:00
updateChatListEntry ( ) ;
if ( peer - > migrateTo ( ) ) {
2016-09-25 22:04:02 +03:00
if ( auto migrateTo = App : : historyLoaded ( peer - > migrateTo ( ) - > id ) ) {
migrateTo - > updateChatListEntry ( ) ;
2015-11-13 18:14:33 +03:00
}
}
2015-10-03 13:09:09 +03:00
2016-04-23 14:40:42 +03:00
showFrom = nullptr ;
2014-07-06 07:32:21 +04:00
App : : wnd ( ) - > notifyClear ( this ) ;
2014-07-04 15:12:54 +04:00
clearNotifications ( ) ;
2015-09-06 13:17:09 +03:00
return upTo ;
2014-05-30 12:53:19 +04:00
}
2015-09-06 13:17:09 +03:00
MsgId History : : inboxRead ( HistoryItem * wasRead ) {
2015-03-13 16:01:25 +03:00
return inboxRead ( wasRead ? wasRead - > id : 0 ) ;
}
2015-09-06 13:17:09 +03:00
MsgId History : : outboxRead ( int32 upTo ) {
2015-09-25 10:47:32 +03:00
if ( upTo < 0 ) return upTo ;
2015-09-06 13:17:09 +03:00
if ( ! upTo ) upTo = msgIdForRead ( ) ;
2016-05-20 19:01:06 +03:00
accumulate_max ( outboxReadBefore , upTo + 1 ) ;
2015-09-06 13:17:09 +03:00
return upTo ;
2014-05-30 12:53:19 +04:00
}
2015-09-06 13:17:09 +03:00
MsgId History : : outboxRead ( HistoryItem * wasRead ) {
2015-03-13 16:01:25 +03:00
return outboxRead ( wasRead ? wasRead - > id : 0 ) ;
}
2015-09-20 11:55:41 +03:00
HistoryItem * History : : lastImportantMessage ( ) const {
2016-03-22 18:23:34 +03:00
if ( isEmpty ( ) ) {
return nullptr ;
}
bool importantOnly = isChannel ( ) & & ! isMegagroup ( ) ;
for ( int blockIndex = blocks . size ( ) ; blockIndex > 0 ; ) {
2015-09-20 11:55:41 +03:00
HistoryBlock * block = blocks . at ( - - blockIndex ) ;
2016-03-22 18:23:34 +03:00
for ( int itemIndex = block - > items . size ( ) ; itemIndex > 0 ; ) {
2015-09-20 11:55:41 +03:00
HistoryItem * item = block - > items . at ( - - itemIndex ) ;
2016-05-27 19:47:46 +03:00
if ( item - > type ( ) = = HistoryItemMsg ) {
2015-09-20 11:55:41 +03:00
return item ;
}
}
}
2016-03-22 18:23:34 +03:00
return nullptr ;
2015-09-20 11:55:41 +03:00
}
2016-04-14 22:24:42 +03:00
void History : : setUnreadCount ( int newUnreadCount ) {
2016-04-11 14:59:01 +04:00
if ( _unreadCount ! = newUnreadCount ) {
2015-09-07 10:52:37 +03:00
if ( newUnreadCount = = 1 ) {
2015-09-20 11:55:41 +03:00
if ( loadedAtBottom ( ) ) showFrom = lastImportantMessage ( ) ;
2015-09-07 10:52:37 +03:00
inboxReadBefore = qMax ( inboxReadBefore , msgIdForRead ( ) ) ;
2014-05-30 12:53:19 +04:00
} else if ( ! newUnreadCount ) {
2016-03-26 09:41:23 +03:00
showFrom = nullptr ;
2015-09-07 10:52:37 +03:00
inboxReadBefore = qMax ( inboxReadBefore , msgIdForRead ( ) + 1 ) ;
2016-06-03 15:45:33 +03:00
} else {
if ( ! showFrom & & ! unreadBar & & loadedAtBottom ( ) ) updateShowFrom ( ) ;
2014-05-30 12:53:19 +04:00
}
2016-04-11 14:59:01 +04:00
if ( inChatList ( Dialogs : : Mode : : All ) ) {
App : : histories ( ) . unreadIncrement ( newUnreadCount - _unreadCount , mute ( ) ) ;
2016-08-26 22:49:18 -06:00
if ( ! mute ( ) | | Global : : IncludeMuted ( ) ) {
2016-04-14 22:24:42 +03:00
Notify : : unreadCounterUpdated ( ) ;
2016-02-28 14:58:30 +03:00
}
}
2016-04-11 14:59:01 +04:00
_unreadCount = newUnreadCount ;
2016-06-03 15:45:33 +03:00
if ( auto main = App : : main ( ) ) {
main - > unreadCountChanged ( this ) ;
}
2015-11-13 18:14:33 +03:00
if ( unreadBar ) {
2016-04-11 14:59:01 +04:00
int32 count = _unreadCount ;
2015-11-13 18:14:33 +03:00
if ( peer - > migrateTo ( ) ) {
if ( History * h = App : : historyLoaded ( peer - > migrateTo ( ) - > id ) ) {
2016-04-11 14:59:01 +04:00
count + = h - > unreadCount ( ) ;
2015-11-13 18:14:33 +03:00
}
}
2016-03-18 22:05:08 +03:00
if ( count > 0 ) {
unreadBar - > setUnreadBarCount ( count ) ;
} else {
unreadBar - > setUnreadBarFreezed ( ) ;
}
2015-11-13 18:14:33 +03:00
}
2014-05-30 12:53:19 +04:00
}
}
void History : : setMute ( bool newMute ) {
2016-04-11 14:59:01 +04:00
if ( _mute ! = newMute ) {
_mute = newMute ;
if ( inChatList ( Dialogs : : Mode : : All ) ) {
if ( _unreadCount ) {
App : : histories ( ) . unreadMuteChanged ( _unreadCount , newMute ) ;
2016-04-14 22:24:42 +03:00
Notify : : unreadCounterUpdated ( ) ;
2016-04-11 14:59:01 +04:00
}
Notify : : historyMuteUpdated ( this ) ;
2016-02-28 14:58:30 +03:00
}
updateChatListEntry ( ) ;
2014-05-30 12:53:19 +04:00
}
}
2016-03-22 18:23:34 +03:00
void History : : getNextShowFrom ( HistoryBlock * block , int i ) {
2014-05-30 12:53:19 +04:00
if ( i > = 0 ) {
2016-03-22 18:23:34 +03:00
int l = block - > items . size ( ) ;
2014-05-30 12:53:19 +04:00
for ( + + i ; i < l ; + + i ) {
2016-03-21 21:40:00 +03:00
if ( block - > items . at ( i ) - > type ( ) = = HistoryItemMsg ) {
showFrom = block - > items . at ( i ) ;
2014-05-30 12:53:19 +04:00
return ;
}
}
}
2016-03-22 18:23:34 +03:00
for ( int j = block - > indexInHistory ( ) + 1 , s = blocks . size ( ) ; j < s ; + + j ) {
2016-03-21 21:40:00 +03:00
block = blocks . at ( j ) ;
for_const ( HistoryItem * item , block - > items ) {
if ( item - > type ( ) = = HistoryItemMsg ) {
showFrom = item ;
return ;
}
}
}
showFrom = nullptr ;
}
void History : : countScrollState ( int top ) {
countScrollTopItem ( top ) ;
if ( scrollTopItem ) {
scrollTopOffset = ( top - scrollTopItem - > block ( ) - > y - scrollTopItem - > y ) ;
}
}
void History : : countScrollTopItem ( int top ) {
if ( isEmpty ( ) ) {
forgetScrollState ( ) ;
return ;
}
int itemIndex = 0 , blockIndex = 0 , itemTop = 0 ;
if ( scrollTopItem & & ! scrollTopItem - > detached ( ) ) {
itemIndex = scrollTopItem - > indexInBlock ( ) ;
blockIndex = scrollTopItem - > block ( ) - > indexInHistory ( ) ;
itemTop = blocks . at ( blockIndex ) - > y + scrollTopItem - > y ;
}
if ( itemTop > top ) {
// go backward through history while we don't find an item that starts above
do {
HistoryBlock * block = blocks . at ( blockIndex ) ;
for ( - - itemIndex ; itemIndex > = 0 ; - - itemIndex ) {
HistoryItem * item = block - > items . at ( itemIndex ) ;
itemTop = block - > y + item - > y ;
if ( itemTop < = top ) {
scrollTopItem = item ;
2014-05-30 12:53:19 +04:00
return ;
}
}
2016-03-21 21:40:00 +03:00
if ( - - blockIndex > = 0 ) {
itemIndex = blocks . at ( blockIndex ) - > items . size ( ) ;
} else {
break ;
}
} while ( true ) ;
scrollTopItem = blocks . front ( ) - > items . front ( ) ;
} else {
// go forward through history while we don't find the last item that starts above
for ( int blocksCount = blocks . size ( ) ; blockIndex < blocksCount ; + + blockIndex ) {
HistoryBlock * block = blocks . at ( blockIndex ) ;
for ( int itemsCount = block - > items . size ( ) ; itemIndex < itemsCount ; + + itemIndex ) {
HistoryItem * item = block - > items . at ( itemIndex ) ;
itemTop = block - > y + item - > y ;
if ( itemTop > top ) {
t_assert ( itemIndex > 0 | | blockIndex > 0 ) ;
if ( itemIndex > 0 ) {
scrollTopItem = block - > items . at ( itemIndex - 1 ) ;
} else {
scrollTopItem = blocks . at ( blockIndex - 1 ) - > items . back ( ) ;
}
return ;
}
}
itemIndex = 0 ;
2014-05-30 12:53:19 +04:00
}
2016-03-21 21:40:00 +03:00
scrollTopItem = blocks . back ( ) - > items . back ( ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-21 21:40:00 +03:00
}
void History : : getNextScrollTopItem ( HistoryBlock * block , int32 i ) {
+ + i ;
if ( i > 0 & & i < block - > items . size ( ) ) {
scrollTopItem = block - > items . at ( i ) ;
return ;
}
int j = block - > indexInHistory ( ) + 1 ;
if ( j > 0 & & j < blocks . size ( ) ) {
scrollTopItem = blocks . at ( j ) - > items . front ( ) ;
return ;
}
scrollTopItem = nullptr ;
2014-05-30 12:53:19 +04:00
}
void History : : addUnreadBar ( ) {
2016-04-11 14:59:01 +04:00
if ( unreadBar | | ! showFrom | | showFrom - > detached ( ) | | ! unreadCount ( ) ) return ;
2015-12-31 23:27:21 +08:00
2016-04-11 14:59:01 +04:00
int32 count = unreadCount ( ) ;
2015-11-13 18:14:33 +03:00
if ( peer - > migrateTo ( ) ) {
if ( History * h = App : : historyLoaded ( peer - > migrateTo ( ) - > id ) ) {
2016-04-11 14:59:01 +04:00
count + = h - > unreadCount ( ) ;
2015-11-13 18:14:33 +03:00
}
}
2016-03-18 22:05:08 +03:00
showFrom - > setUnreadBarCount ( count ) ;
unreadBar = showFrom ;
}
void History : : destroyUnreadBar ( ) {
if ( unreadBar ) {
unreadBar - > destroyUnreadBar ( ) ;
2015-09-19 12:13:21 +03:00
}
}
2014-05-30 12:53:19 +04:00
2015-09-19 12:13:21 +03:00
HistoryItem * History : : addNewInTheMiddle ( HistoryItem * newItem , int32 blockIndex , int32 itemIndex ) {
2016-03-21 21:40:00 +03:00
t_assert ( blockIndex > = 0 ) ;
t_assert ( blockIndex < blocks . size ( ) ) ;
t_assert ( itemIndex > = 0 ) ;
t_assert ( itemIndex < = blocks . at ( blockIndex ) - > items . size ( ) ) ;
2014-05-30 12:53:19 +04:00
2015-09-19 12:13:21 +03:00
HistoryBlock * block = blocks . at ( blockIndex ) ;
2016-03-18 22:05:08 +03:00
newItem - > attachToBlock ( block , itemIndex ) ;
2015-09-19 12:13:21 +03:00
block - > items . insert ( itemIndex , newItem ) ;
2016-03-19 19:55:15 +03:00
newItem - > previousItemChanged ( ) ;
2016-03-18 22:05:08 +03:00
for ( int i = itemIndex + 1 , l = block - > items . size ( ) ; i < l ; + + i ) {
block - > items . at ( i ) - > setIndexInBlock ( i ) ;
}
2016-03-21 21:40:00 +03:00
if ( itemIndex + 1 < block - > items . size ( ) ) {
block - > items . at ( itemIndex + 1 ) - > previousItemChanged ( ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-21 21:40:00 +03:00
2015-09-19 12:13:21 +03:00
return newItem ;
2014-05-30 12:53:19 +04:00
}
2016-03-31 14:37:58 +04:00
void History : : startBuildingFrontBlock ( int expectedItemsCount ) {
t_assert ( ! isBuildingFrontBlock ( ) ) ;
t_assert ( expectedItemsCount > 0 ) ;
2016-03-31 15:18:21 +04:00
_buildingFrontBlock . reset ( new BuildingBlock ( ) ) ;
_buildingFrontBlock - > expectedItemsCount = expectedItemsCount ;
2016-03-31 14:37:58 +04:00
}
HistoryBlock * History : : finishBuildingFrontBlock ( ) {
t_assert ( isBuildingFrontBlock ( ) ) ;
// Some checks if there was some message history already
2016-03-31 15:18:21 +04:00
HistoryBlock * block = _buildingFrontBlock - > block ;
2016-03-31 14:37:58 +04:00
if ( block & & blocks . size ( ) > 1 ) {
HistoryItem * last = block - > items . back ( ) ; // ... item, item, item, last ], [ first, item, item ...
HistoryItem * first = blocks . at ( 1 ) - > items . front ( ) ;
// we've added a new front block, so previous item for
// the old first item of a first block was changed
first - > previousItemChanged ( ) ;
2016-03-22 12:51:20 +03:00
}
2016-03-31 14:37:58 +04:00
2016-04-10 23:20:48 +04:00
_buildingFrontBlock = nullptr ;
2016-03-31 14:37:58 +04:00
return block ;
2016-03-22 12:51:20 +03:00
}
2014-07-04 15:12:54 +04:00
void History : : clearNotifications ( ) {
notifies . clear ( ) ;
}
bool History : : loadedAtBottom ( ) const {
return newLoaded ;
}
bool History : : loadedAtTop ( ) const {
return oldLoaded ;
}
2016-05-27 19:47:46 +03:00
bool History : : isReadyFor ( MsgId msgId ) {
2015-11-13 18:14:33 +03:00
if ( msgId < 0 & & - msgId < ServerMaxMsgId & & peer - > migrateFrom ( ) ) { // old group history
2016-05-27 19:47:46 +03:00
return App : : history ( peer - > migrateFrom ( ) - > id ) - > isReadyFor ( - msgId ) ;
2015-11-13 18:14:33 +03:00
}
2015-07-17 22:17:37 +03:00
if ( msgId = = ShowAtTheEndMsgId ) {
return loadedAtBottom ( ) ;
2015-09-19 12:13:21 +03:00
}
if ( msgId = = ShowAtUnreadMsgId ) {
2015-11-13 18:14:33 +03:00
if ( peer - > migrateFrom ( ) ) { // old group history
if ( History * h = App : : historyLoaded ( peer - > migrateFrom ( ) - > id ) ) {
2016-04-11 14:59:01 +04:00
if ( h - > unreadCount ( ) ) {
2016-05-27 19:47:46 +03:00
return h - > isReadyFor ( msgId ) ;
2015-11-13 18:14:33 +03:00
}
}
}
2016-04-11 14:59:01 +04:00
if ( unreadCount ( ) ) {
2015-09-19 12:13:21 +03:00
if ( ! isEmpty ( ) ) {
return ( loadedAtTop ( ) | | minMsgId ( ) < = inboxReadBefore ) & & ( loadedAtBottom ( ) | | maxMsgId ( ) > = inboxReadBefore ) ;
2015-09-08 16:34:22 +03:00
}
2015-09-19 12:13:21 +03:00
return false ;
2015-09-08 16:34:22 +03:00
}
2015-09-19 12:13:21 +03:00
return loadedAtBottom ( ) ;
2015-07-17 22:17:37 +03:00
}
2015-09-19 12:13:21 +03:00
HistoryItem * item = App : : histItemById ( channelId ( ) , msgId ) ;
return item & & ( item - > history ( ) = = this ) & & ! item - > detached ( ) ;
2015-07-17 22:17:37 +03:00
}
2016-05-27 19:47:46 +03:00
void History : : getReadyFor ( MsgId msgId ) {
2015-11-13 18:14:33 +03:00
if ( msgId < 0 & & - msgId < ServerMaxMsgId & & peer - > migrateFrom ( ) ) {
History * h = App : : history ( peer - > migrateFrom ( ) - > id ) ;
2016-05-27 19:47:46 +03:00
h - > getReadyFor ( - msgId ) ;
2015-11-13 18:14:33 +03:00
if ( h - > isEmpty ( ) ) {
clear ( true ) ;
}
return ;
}
if ( msgId = = ShowAtUnreadMsgId & & peer - > migrateFrom ( ) ) {
if ( History * h = App : : historyLoaded ( peer - > migrateFrom ( ) - > id ) ) {
2016-04-11 14:59:01 +04:00
if ( h - > unreadCount ( ) ) {
2015-11-13 18:14:33 +03:00
clear ( true ) ;
2016-05-27 19:47:46 +03:00
h - > getReadyFor ( msgId ) ;
2015-11-13 18:14:33 +03:00
return ;
}
}
}
2016-05-27 19:47:46 +03:00
if ( ! isReadyFor ( msgId ) ) {
2015-07-17 22:17:37 +03:00
clear ( true ) ;
2016-06-02 16:02:55 +03:00
2016-03-21 21:40:00 +03:00
if ( msgId = = ShowAtTheEndMsgId ) {
newLoaded = true ;
}
2015-07-17 22:17:37 +03:00
}
}
2015-09-13 11:41:27 +03:00
void History : : setNotLoadedAtBottom ( ) {
newLoaded = false ;
}
2015-09-06 13:17:09 +03:00
namespace {
uint32 _dialogsPosToTopShift = 0x80000000UL ;
}
inline uint64 dialogPosFromDate ( const QDateTime & date ) {
2016-06-21 18:58:07 +03:00
if ( date . isNull ( ) ) return 0 ;
2015-09-06 13:17:09 +03:00
return ( uint64 ( date . toTime_t ( ) ) < < 32 ) | ( + + _dialogsPosToTopShift ) ;
}
2015-09-21 23:57:42 +03:00
void History : : setLastMessage ( HistoryItem * msg ) {
2015-08-07 15:11:50 +03:00
if ( msg ) {
if ( ! lastMsg ) Local : : removeSavedPeer ( peer ) ;
lastMsg = msg ;
2016-02-28 14:58:30 +03:00
setChatsListDate ( msg - > date ) ;
2015-08-07 15:11:50 +03:00
} else {
lastMsg = 0 ;
2016-06-21 18:58:07 +03:00
updateChatListEntry ( ) ;
2015-08-07 15:11:50 +03:00
}
}
2016-06-03 21:24:27 +03:00
bool History : : needUpdateInChatList ( ) const {
if ( inChatList ( Dialogs : : Mode : : All ) ) {
return true ;
} else if ( peer - > migrateTo ( ) ) {
return false ;
2015-11-13 18:14:33 +03:00
}
2016-06-03 21:24:27 +03:00
return ( ! peer - > isChannel ( ) | | peer - > asChannel ( ) - > amIn ( ) ) ;
}
void History : : setChatsListDate ( const QDateTime & date ) {
bool updateDialog = needUpdateInChatList ( ) ;
2015-09-21 23:57:42 +03:00
if ( ! lastMsgDate . isNull ( ) & & lastMsgDate > = date ) {
2016-04-11 14:59:01 +04:00
if ( ! updateDialog | | ! inChatList ( Dialogs : : Mode : : All ) ) {
2015-09-21 23:57:42 +03:00
return ;
}
}
2015-09-06 13:17:09 +03:00
lastMsgDate = date ;
2016-06-03 21:24:27 +03:00
updateChatListSortPosition ( ) ;
}
void History : : updateChatListSortPosition ( ) {
auto chatListDate = [ this ] ( ) {
if ( auto draft = cloudDraft ( ) ) {
2016-06-21 18:58:07 +03:00
if ( ! Data : : draftIsNull ( draft ) & & draft - > date > lastMsgDate ) {
2016-06-03 21:24:27 +03:00
return draft - > date ;
}
}
return lastMsgDate ;
} ;
_sortKeyInChatList = dialogPosFromDate ( chatListDate ( ) ) ;
2016-06-21 18:58:07 +03:00
if ( auto m = App : : main ( ) ) {
if ( needUpdateInChatList ( ) ) {
if ( _sortKeyInChatList ) {
m - > createDialog ( this ) ;
updateChatListEntry ( ) ;
} else {
m - > deleteConversation ( peer , false ) ;
}
}
2015-09-06 13:17:09 +03:00
}
}
2014-07-04 15:12:54 +04:00
void History : : fixLastMessage ( bool wasAtBottom ) {
2015-09-21 23:57:42 +03:00
setLastMessage ( wasAtBottom ? lastImportantMessage ( ) : 0 ) ;
2014-07-04 15:12:54 +04:00
}
MsgId History : : minMsgId ( ) const {
2016-03-15 13:37:56 +03:00
for_const ( const HistoryBlock * block , blocks ) {
for_const ( const HistoryItem * item , block - > items ) {
if ( item - > id > 0 ) {
return item - > id ;
2014-05-30 12:53:19 +04:00
}
}
}
2014-07-04 15:12:54 +04:00
return 0 ;
2014-05-30 12:53:19 +04:00
}
2014-07-04 15:12:54 +04:00
MsgId History : : maxMsgId ( ) const {
2016-03-15 13:37:56 +03:00
for ( auto i = blocks . cend ( ) , e = blocks . cbegin ( ) ; i ! = e ; ) {
2014-07-04 15:12:54 +04:00
- - i ;
2016-03-15 13:37:56 +03:00
for ( auto j = ( * i ) - > items . cend ( ) , en = ( * i ) - > items . cbegin ( ) ; j ! = en ; ) {
2014-07-04 15:12:54 +04:00
- - j ;
if ( ( * j ) - > id > 0 ) {
return ( * j ) - > id ;
}
}
}
return 0 ;
2014-05-30 12:53:19 +04:00
}
2015-09-06 13:17:09 +03:00
MsgId History : : msgIdForRead ( ) const {
MsgId result = ( lastMsg & & lastMsg - > id > 0 ) ? lastMsg - > id : 0 ;
if ( loadedAtBottom ( ) ) result = qMax ( result , maxMsgId ( ) ) ;
return result ;
}
2016-03-21 21:40:00 +03:00
int History : : resizeGetHeight ( int newWidth ) {
2016-04-08 13:20:10 +04:00
bool resizeAllItems = ( _flags & Flag : : f_pending_resize ) | | ( width ! = newWidth ) ;
2016-03-21 21:40:00 +03:00
if ( ! resizeAllItems & & ! hasPendingResizedItems ( ) ) {
return height ;
}
2016-03-19 19:55:15 +03:00
_flags & = ~ ( Flag : : f_pending_resize | Flag : : f_has_pending_resized_items ) ;
2016-03-21 21:40:00 +03:00
width = newWidth ;
int y = 0 ;
for_const ( HistoryBlock * block , blocks ) {
block - > y = y ;
y + = block - > resizeGetHeight ( newWidth , resizeAllItems ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-21 21:40:00 +03:00
height = y ;
2014-05-30 12:53:19 +04:00
return height ;
}
2015-09-19 12:13:21 +03:00
ChannelHistory * History : : asChannelHistory ( ) {
return isChannel ( ) ? static_cast < ChannelHistory * > ( this ) : 0 ;
}
const ChannelHistory * History : : asChannelHistory ( ) const {
return isChannel ( ) ? static_cast < const ChannelHistory * > ( this ) : 0 ;
}
2016-06-03 15:45:33 +03:00
bool History : : isDisplayedEmpty ( ) const {
2016-06-08 13:24:20 +03:00
return isEmpty ( ) | | ( ( blocks . size ( ) = = 1 ) & & blocks . front ( ) - > items . size ( ) = = 1 & & blocks . front ( ) - > items . front ( ) - > isEmpty ( ) ) ;
2016-06-03 15:45:33 +03:00
}
2014-07-04 15:12:54 +04:00
void History : : clear ( bool leaveItems ) {
if ( unreadBar ) {
2016-03-18 22:05:08 +03:00
unreadBar = nullptr ;
2014-07-04 15:12:54 +04:00
}
if ( showFrom ) {
2016-03-18 22:05:08 +03:00
showFrom = nullptr ;
2014-07-04 15:12:54 +04:00
}
2016-04-23 14:40:42 +03:00
if ( lastSentMsg ) {
lastSentMsg = nullptr ;
}
2016-03-21 21:40:00 +03:00
if ( scrollTopItem ) {
forgetScrollState ( ) ;
}
2015-08-04 18:01:47 +03:00
if ( ! leaveItems ) {
2016-03-18 22:05:08 +03:00
setLastMessage ( nullptr ) ;
2016-04-14 22:24:42 +03:00
notifies . clear ( ) ;
auto & pending = Global : : RefPendingRepaintItems ( ) ;
for ( auto i = pending . begin ( ) ; i ! = pending . end ( ) ; ) {
if ( ( * i ) - > history ( ) = = this ) {
i = pending . erase ( i ) ;
} else {
+ + i ;
}
}
2015-08-04 18:01:47 +03:00
}
2014-08-15 15:19:32 +04:00
for ( int32 i = 0 ; i < OverviewCount ; + + i ) {
2015-09-19 12:13:21 +03:00
if ( ! overview [ i ] . isEmpty ( ) | | ! overviewIds [ i ] . isEmpty ( ) ) {
2015-08-04 18:01:47 +03:00
if ( leaveItems ) {
2015-11-18 16:11:56 +03:00
if ( overviewCountData [ i ] = = 0 ) {
overviewCountData [ i ] = overview [ i ] . size ( ) ;
}
2015-08-04 18:01:47 +03:00
} else {
2015-11-18 16:11:56 +03:00
overviewCountData [ i ] = - 1 ; // not loaded yet
2015-08-04 18:01:47 +03:00
}
2015-09-19 12:13:21 +03:00
overview [ i ] . clear ( ) ;
overviewIds [ i ] . clear ( ) ;
2016-03-02 20:34:42 +02:00
if ( App : : wnd ( ) & & ! App : : quitting ( ) ) App : : wnd ( ) - > mediaOverviewUpdated ( peer , MediaOverviewType ( i ) ) ;
2015-07-03 11:47:16 +03:00
}
2014-08-15 15:19:32 +04:00
}
2016-03-15 13:37:56 +03:00
clearBlocks ( leaveItems ) ;
2015-06-17 22:43:03 +03:00
if ( leaveItems ) {
lastKeyboardInited = false ;
} else {
2014-07-04 15:12:54 +04:00
setUnreadCount ( 0 ) ;
2016-04-14 22:24:42 +03:00
if ( peer - > isMegagroup ( ) ) {
peer - > asChannel ( ) - > mgInfo - > pinnedMsgId = 0 ;
}
clearLastKeyboard ( ) ;
2014-07-04 15:12:54 +04:00
}
2016-03-21 21:40:00 +03:00
setPendingResize ( ) ;
newLoaded = oldLoaded = false ;
forgetScrollState ( ) ;
2015-09-03 13:48:40 +03:00
if ( peer - > isChat ( ) ) {
2015-06-17 22:43:03 +03:00
peer - > asChat ( ) - > lastAuthors . clear ( ) ;
peer - > asChat ( ) - > markupSenders . clear ( ) ;
2015-09-21 23:57:42 +03:00
} else if ( isChannel ( ) ) {
2016-04-14 22:24:42 +03:00
asChannelHistory ( ) - > cleared ( leaveItems ) ;
2015-11-09 12:51:22 +03:00
if ( isMegagroup ( ) ) {
peer - > asChannel ( ) - > mgInfo - > markupSenders . clear ( ) ;
}
2015-06-17 22:43:03 +03:00
}
if ( leaveItems & & App : : main ( ) ) App : : main ( ) - > historyCleared ( this ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-15 13:37:56 +03:00
void History : : clearBlocks ( bool leaveItems ) {
Blocks lst ;
std : : swap ( lst , blocks ) ;
for_const ( HistoryBlock * block , lst ) {
if ( leaveItems ) {
block - > clear ( true ) ;
}
delete block ;
}
}
void History : : clearOnDestroy ( ) {
clearBlocks ( false ) ;
}
2016-04-11 15:14:54 +04:00
History : : PositionInChatListChange History : : adjustByPosInChatList ( Dialogs : : Mode list , Dialogs : : IndexedList * indexed ) {
2016-04-09 22:45:55 +04:00
t_assert ( indexed ! = nullptr ) ;
2016-04-11 14:59:01 +04:00
Dialogs : : Row * lnk = mainChatListLink ( list ) ;
2016-04-11 15:14:54 +04:00
int32 movedFrom = lnk - > pos ( ) ;
2016-04-11 14:59:01 +04:00
indexed - > adjustByPos ( chatListLinks ( list ) ) ;
2016-04-11 15:14:54 +04:00
int32 movedTo = lnk - > pos ( ) ;
return { movedFrom , movedTo } ;
2016-02-28 14:58:30 +03:00
}
2016-04-11 14:59:01 +04:00
int History : : posInChatList ( Dialogs : : Mode list ) const {
return mainChatListLink ( list ) - > pos ( ) ;
2016-04-09 22:45:55 +04:00
}
2016-04-11 14:59:01 +04:00
Dialogs : : Row * History : : addToChatList ( Dialogs : : Mode list , Dialogs : : IndexedList * indexed ) {
2016-04-09 22:45:55 +04:00
t_assert ( indexed ! = nullptr ) ;
2016-04-11 14:59:01 +04:00
if ( ! inChatList ( list ) ) {
chatListLinks ( list ) = indexed - > addToEnd ( this ) ;
if ( list = = Dialogs : : Mode : : All & & unreadCount ( ) ) {
App : : histories ( ) . unreadIncrement ( unreadCount ( ) , mute ( ) ) ;
2016-04-14 22:24:42 +03:00
Notify : : unreadCounterUpdated ( ) ;
2016-02-28 14:58:30 +03:00
}
}
2016-04-11 14:59:01 +04:00
return mainChatListLink ( list ) ;
2016-02-28 14:58:30 +03:00
}
2016-04-11 14:59:01 +04:00
void History : : removeFromChatList ( Dialogs : : Mode list , Dialogs : : IndexedList * indexed ) {
2016-04-09 22:45:55 +04:00
t_assert ( indexed ! = nullptr ) ;
2016-04-11 14:59:01 +04:00
if ( inChatList ( list ) ) {
2016-04-09 22:45:55 +04:00
indexed - > del ( peer ) ;
2016-04-11 14:59:01 +04:00
chatListLinks ( list ) . clear ( ) ;
if ( list = = Dialogs : : Mode : : All & & unreadCount ( ) ) {
App : : histories ( ) . unreadIncrement ( - unreadCount ( ) , mute ( ) ) ;
2016-04-14 22:24:42 +03:00
Notify : : unreadCounterUpdated ( ) ;
2016-02-28 14:58:30 +03:00
}
}
}
2016-04-11 14:59:01 +04:00
void History : : removeChatListEntryByLetter ( Dialogs : : Mode list , QChar letter ) {
2016-02-28 14:58:30 +03:00
t_assert ( letter ! = 0 ) ;
2016-04-11 14:59:01 +04:00
if ( inChatList ( list ) ) {
chatListLinks ( list ) . remove ( letter ) ;
2016-02-28 14:58:30 +03:00
}
}
2016-04-11 14:59:01 +04:00
void History : : addChatListEntryByLetter ( Dialogs : : Mode list , QChar letter , Dialogs : : Row * row ) {
2016-02-28 14:58:30 +03:00
t_assert ( letter ! = 0 ) ;
2016-04-11 14:59:01 +04:00
if ( inChatList ( list ) ) {
chatListLinks ( list ) . insert ( letter , row ) ;
2016-02-28 14:58:30 +03:00
}
}
void History : : updateChatListEntry ( ) const {
if ( MainWidget * m = App : : main ( ) ) {
2016-04-11 14:59:01 +04:00
if ( inChatList ( Dialogs : : Mode : : All ) ) {
m - > dlgUpdated ( Dialogs : : Mode : : All , mainChatListLink ( Dialogs : : Mode : : All ) ) ;
if ( inChatList ( Dialogs : : Mode : : Important ) ) {
m - > dlgUpdated ( Dialogs : : Mode : : Important , mainChatListLink ( Dialogs : : Mode : : Important ) ) ;
}
2016-02-28 14:58:30 +03:00
}
}
}
2015-11-18 16:11:56 +03:00
void History : : overviewSliceDone ( int32 overviewIndex , const MTPmessages_Messages & result , bool onlyCounts ) {
const QVector < MTPMessage > * v = 0 ;
switch ( result . type ( ) ) {
case mtpc_messages_messages : {
2016-05-27 19:47:46 +03:00
auto & d ( result . c_messages_messages ( ) ) ;
2015-11-18 16:11:56 +03:00
App : : feedUsers ( d . vusers ) ;
App : : feedChats ( d . vchats ) ;
v = & d . vmessages . c_vector ( ) . v ;
overviewCountData [ overviewIndex ] = 0 ;
} break ;
case mtpc_messages_messagesSlice : {
2016-05-27 19:47:46 +03:00
auto & d ( result . c_messages_messagesSlice ( ) ) ;
2015-11-18 16:11:56 +03:00
App : : feedUsers ( d . vusers ) ;
App : : feedChats ( d . vchats ) ;
overviewCountData [ overviewIndex ] = d . vcount . v ;
v = & d . vmessages . c_vector ( ) . v ;
} break ;
case mtpc_messages_channelMessages : {
2016-05-27 19:47:46 +03:00
auto & d ( result . c_messages_channelMessages ( ) ) ;
2015-11-18 16:11:56 +03:00
if ( peer - > isChannel ( ) ) {
peer - > asChannel ( ) - > ptsReceived ( d . vpts . v ) ;
} else {
2016-01-11 23:43:29 +08:00
LOG ( ( " API Error: received messages.channelMessages when no channel was passed! (History::overviewSliceDone, onlyCounts %1) " ) . arg ( Logs : : b ( onlyCounts ) ) ) ;
2015-11-18 16:11:56 +03:00
}
App : : feedUsers ( d . vusers ) ;
App : : feedChats ( d . vchats ) ;
overviewCountData [ overviewIndex ] = d . vcount . v ;
v = & d . vmessages . c_vector ( ) . v ;
} break ;
default : return ;
}
if ( ! onlyCounts & & v - > isEmpty ( ) ) {
overviewCountData [ overviewIndex ] = 0 ;
} else if ( overviewCountData [ overviewIndex ] > 0 ) {
for ( History : : MediaOverviewIds : : const_iterator i = overviewIds [ overviewIndex ] . cbegin ( ) , e = overviewIds [ overviewIndex ] . cend ( ) ; i ! = e ; + + i ) {
if ( i . key ( ) < 0 ) {
+ + overviewCountData [ overviewIndex ] ;
} else {
break ;
}
}
}
for ( QVector < MTPMessage > : : const_iterator i = v - > cbegin ( ) , e = v - > cend ( ) ; i ! = e ; + + i ) {
HistoryItem * item = App : : histories ( ) . addNewMessage ( * i , NewMessageExisting ) ;
if ( item & & overviewIds [ overviewIndex ] . constFind ( item - > id ) = = overviewIds [ overviewIndex ] . cend ( ) ) {
overviewIds [ overviewIndex ] . insert ( item - > id , NullType ( ) ) ;
overview [ overviewIndex ] . push_front ( item - > id ) ;
}
}
}
void History : : changeMsgId ( MsgId oldId , MsgId newId ) {
for ( int32 i = 0 ; i < OverviewCount ; + + i ) {
History : : MediaOverviewIds : : iterator j = overviewIds [ i ] . find ( oldId ) ;
if ( j ! = overviewIds [ i ] . cend ( ) ) {
overviewIds [ i ] . erase ( j ) ;
int32 index = overview [ i ] . indexOf ( oldId ) ;
if ( overviewIds [ i ] . constFind ( newId ) = = overviewIds [ i ] . cend ( ) ) {
overviewIds [ i ] . insert ( newId , NullType ( ) ) ;
if ( index > = 0 ) {
overview [ i ] [ index ] = newId ;
} else {
overview [ i ] . push_back ( newId ) ;
}
} else if ( index > = 0 ) {
overview [ i ] . removeAt ( index ) ;
}
}
}
}
2014-05-30 12:53:19 +04:00
void History : : removeBlock ( HistoryBlock * block ) {
2016-03-25 19:39:58 +03:00
t_assert ( block - > items . isEmpty ( ) ) ;
2016-03-31 15:18:21 +04:00
if ( _buildingFrontBlock & & block = = _buildingFrontBlock - > block ) {
_buildingFrontBlock - > block = nullptr ;
2016-03-31 14:37:58 +04:00
}
2016-03-21 21:40:00 +03:00
int index = block - > indexInHistory ( ) ;
blocks . removeAt ( index ) ;
for ( int i = index , l = blocks . size ( ) ; i < l ; + + i ) {
blocks . at ( i ) - > setIndexInHistory ( i ) ;
}
if ( index < blocks . size ( ) ) {
blocks . at ( index ) - > items . front ( ) - > previousItemChanged ( ) ;
2014-05-30 12:53:19 +04:00
}
}
2016-02-25 13:32:31 +03:00
History : : ~ History ( ) {
2016-03-15 13:37:56 +03:00
clearOnDestroy ( ) ;
2016-02-25 13:32:31 +03:00
}
2016-03-21 21:40:00 +03:00
int HistoryBlock : : resizeGetHeight ( int newWidth , bool resizeAllItems ) {
int y = 0 ;
for_const ( HistoryItem * item , items ) {
2014-05-30 12:53:19 +04:00
item - > y = y ;
2016-03-21 21:40:00 +03:00
if ( resizeAllItems | | item - > pendingResize ( ) ) {
y + = item - > resizeGetHeight ( newWidth ) ;
2015-09-10 13:30:59 +03:00
} else {
y + = item - > height ( ) ;
}
2014-05-30 12:53:19 +04:00
}
height = y ;
return height ;
}
2014-07-04 15:12:54 +04:00
void HistoryBlock : : clear ( bool leaveItems ) {
2016-03-15 13:37:56 +03:00
Items lst ;
std : : swap ( lst , items ) ;
2014-07-04 15:12:54 +04:00
if ( leaveItems ) {
2016-03-15 13:37:56 +03:00
for_const ( HistoryItem * item , lst ) {
item - > detachFast ( ) ;
2014-07-04 15:12:54 +04:00
}
} else {
2016-03-15 13:37:56 +03:00
for_const ( HistoryItem * item , lst ) {
delete item ;
2014-07-04 15:12:54 +04:00
}
2014-05-30 12:53:19 +04:00
}
}
void HistoryBlock : : removeItem ( HistoryItem * item ) {
2016-03-21 21:40:00 +03:00
t_assert ( item - > block ( ) = = this ) ;
2016-05-27 19:47:46 +03:00
int blockIndex = indexInHistory ( ) ;
2016-03-25 19:39:58 +03:00
int itemIndex = item - > indexInBlock ( ) ;
2014-05-30 12:53:19 +04:00
if ( history - > showFrom = = item ) {
2016-03-21 21:40:00 +03:00
history - > getNextShowFrom ( this , itemIndex ) ;
2014-05-30 12:53:19 +04:00
}
2016-04-23 14:40:42 +03:00
if ( history - > lastSentMsg = = item ) {
history - > lastSentMsg = nullptr ;
}
2016-03-18 22:05:08 +03:00
if ( history - > unreadBar = = item ) {
history - > unreadBar = nullptr ;
}
2016-03-21 21:40:00 +03:00
if ( history - > scrollTopItem = = item ) {
history - > getNextScrollTopItem ( this , itemIndex ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-26 09:41:23 +03:00
item - > detachFast ( ) ;
2016-03-21 21:40:00 +03:00
items . remove ( itemIndex ) ;
for ( int i = itemIndex , l = items . size ( ) ; i < l ; + + i ) {
items . at ( i ) - > setIndexInBlock ( i ) ;
}
2016-03-26 09:41:23 +03:00
if ( items . isEmpty ( ) ) {
history - > removeBlock ( this ) ;
} else if ( itemIndex < items . size ( ) ) {
2016-03-21 21:40:00 +03:00
items . at ( itemIndex ) - > previousItemChanged ( ) ;
2016-03-26 09:41:23 +03:00
} else if ( blockIndex + 1 < history - > blocks . size ( ) ) {
history - > blocks . at ( blockIndex + 1 ) - > items . front ( ) - > previousItemChanged ( ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-18 22:05:08 +03:00
2016-03-21 21:40:00 +03:00
if ( items . isEmpty ( ) ) {
2016-03-26 09:41:23 +03:00
delete this ;
2014-05-30 12:53:19 +04:00
}
}
2016-04-06 19:32:05 +04:00
class ReplyMarkupClickHandler : public LeftButtonClickHandler {
public :
2016-06-15 08:36:59 +03:00
ReplyMarkupClickHandler ( const HistoryItem * item , int row , int col ) : _itemId ( item - > fullId ( ) ) , _row ( row ) , _col ( col ) {
2016-04-06 19:32:05 +04:00
}
QString tooltip ( ) const override {
2016-04-07 14:35:09 +04:00
return _fullDisplayed ? QString ( ) : buttonText ( ) ;
2016-04-06 19:32:05 +04:00
}
void setFullDisplayed ( bool full ) {
_fullDisplayed = full ;
}
2016-05-19 18:02:07 +03:00
// Copy to clipboard support.
void copyToClipboard ( ) const override {
if ( auto button = getButton ( ) ) {
2016-09-02 12:11:23 -04:00
if ( button - > type = = HistoryMessageReplyMarkup : : Button : : Type : : Url ) {
2016-05-19 18:02:07 +03:00
auto url = QString : : fromUtf8 ( button - > data ) ;
if ( ! url . isEmpty ( ) ) {
QApplication : : clipboard ( ) - > setText ( url ) ;
}
}
}
}
QString copyToClipboardContextItemText ( ) const override {
if ( auto button = getButton ( ) ) {
2016-09-02 12:11:23 -04:00
if ( button - > type = = HistoryMessageReplyMarkup : : Button : : Type : : Url ) {
2016-05-19 18:02:07 +03:00
return lang ( lng_context_copy_link ) ;
}
}
return QString ( ) ;
}
2016-04-06 19:32:05 +04:00
// Finds the corresponding button in the items markup struct.
// If the button is not found it returns nullptr.
// Note: it is possible that we will point to the different button
// than the one was used when constructing the handler, but not a big deal.
const HistoryMessageReplyMarkup : : Button * getButton ( ) const {
2016-06-15 08:36:59 +03:00
if ( auto item = App : : histItemById ( _itemId ) ) {
if ( auto markup = item - > Get < HistoryMessageReplyMarkup > ( ) ) {
if ( _row < markup - > rows . size ( ) ) {
2016-09-03 17:27:22 -04:00
auto & row = markup - > rows . at ( _row ) ;
2016-06-15 08:36:59 +03:00
if ( _col < row . size ( ) ) {
return & row . at ( _col ) ;
}
2016-03-29 20:17:00 +03:00
}
}
}
2016-04-06 19:32:05 +04:00
return nullptr ;
2016-03-29 20:17:00 +03:00
}
2016-04-06 19:32:05 +04:00
2016-06-15 08:36:59 +03:00
// We hold only FullMsgId, not HistoryItem*, because all click handlers
// are activated async and the item may be already destroyed.
void setMessageId ( const FullMsgId & msgId ) {
_itemId = msgId ;
}
2016-04-06 21:02:22 +04:00
protected :
void onClickImpl ( ) const override {
2016-06-15 08:36:59 +03:00
if ( auto item = App : : histItemById ( _itemId ) ) {
App : : activateBotCommand ( item , _row , _col ) ;
}
2016-04-06 21:02:22 +04:00
}
private :
2016-06-15 08:36:59 +03:00
FullMsgId _itemId ;
2016-04-06 21:02:22 +04:00
int _row , _col ;
bool _fullDisplayed = true ;
2016-04-06 19:32:05 +04:00
// Returns the full text of the corresponding button.
2016-04-07 14:35:09 +04:00
QString buttonText ( ) const {
2016-04-06 19:32:05 +04:00
if ( auto button = getButton ( ) ) {
return button - > text ;
}
return QString ( ) ;
}
} ;
2016-03-29 20:17:00 +03:00
2016-03-28 20:15:17 +03:00
ReplyKeyboard : : ReplyKeyboard ( const HistoryItem * item , StylePtr & & s )
: _item ( item )
, _a_selected ( animation ( this , & ReplyKeyboard : : step_selected ) )
, _st ( std_ : : forward < StylePtr > ( s ) ) {
2016-04-08 13:20:10 +04:00
if ( auto markup = item - > Get < HistoryMessageReplyMarkup > ( ) ) {
2016-03-28 20:15:17 +03:00
_rows . reserve ( markup - > rows . size ( ) ) ;
for ( int i = 0 , l = markup - > rows . size ( ) ; i ! = l ; + + i ) {
2016-09-03 17:27:22 -04:00
auto & row = markup - > rows . at ( i ) ;
2016-03-28 20:15:17 +03:00
int s = row . size ( ) ;
ButtonRow newRow ( s , Button ( ) ) ;
for ( int j = 0 ; j ! = s ; + + j ) {
2016-09-03 17:27:22 -04:00
auto & button = newRow [ j ] ;
auto str = row . at ( j ) . text ;
2016-03-30 20:42:01 +04:00
button . type = row . at ( j ) . type ;
2016-09-03 17:27:22 -04:00
button . link = MakeShared < ReplyMarkupClickHandler > ( item , i , j ) ;
2016-03-28 20:15:17 +03:00
button . text . setText ( _st - > textFont ( ) , textOneLine ( str ) , _textPlainOptions ) ;
button . characters = str . isEmpty ( ) ? 1 : str . size ( ) ;
}
_rows . push_back ( newRow ) ;
}
}
}
2016-06-15 08:36:59 +03:00
void ReplyKeyboard : : updateMessageId ( ) {
auto msgId = _item - > fullId ( ) ;
for_const ( auto & row , _rows ) {
for_const ( auto & button , row ) {
button . link - > setMessageId ( msgId ) ;
}
}
}
2016-03-28 20:15:17 +03:00
void ReplyKeyboard : : resize ( int width , int height ) {
_width = width ;
2016-04-08 13:20:10 +04:00
auto markup = _item - > Get < HistoryMessageReplyMarkup > ( ) ;
2016-03-28 20:15:17 +03:00
float64 y = 0 , buttonHeight = _rows . isEmpty ( ) ? _st - > buttonHeight ( ) : ( float64 ( height + _st - > buttonSkip ( ) ) / _rows . size ( ) ) ;
2016-09-03 17:27:22 -04:00
for ( auto & row : _rows ) {
2016-03-28 20:15:17 +03:00
int s = row . size ( ) ;
2016-06-26 18:37:47 +03:00
int widthForButtons = _width - ( ( s - 1 ) * _st - > buttonSkip ( ) ) ;
int widthForText = widthForButtons ;
2016-04-11 00:59:07 +04:00
int widthOfText = 0 ;
2016-06-26 18:37:47 +03:00
int maxMinButtonWidth = 0 ;
2016-09-03 17:27:22 -04:00
for_const ( auto & button , row ) {
2016-03-28 20:15:17 +03:00
widthOfText + = qMax ( button . text . maxWidth ( ) , 1 ) ;
2016-06-26 18:37:47 +03:00
int minButtonWidth = _st - > minButtonWidth ( button . type ) ;
widthForText - = minButtonWidth ;
accumulate_max ( maxMinButtonWidth , minButtonWidth ) ;
2016-03-28 20:15:17 +03:00
}
2016-04-11 00:59:07 +04:00
bool exact = ( widthForText = = widthOfText ) ;
2016-06-26 18:37:47 +03:00
bool enough = ( widthForButtons - s * maxMinButtonWidth ) > = widthOfText ;
2016-03-28 20:15:17 +03:00
2016-04-11 00:59:07 +04:00
float64 x = 0 ;
2016-03-28 20:15:17 +03:00
for ( Button & button : row ) {
2016-04-11 00:59:07 +04:00
int buttonw = qMax ( button . text . maxWidth ( ) , 1 ) ;
2016-06-26 18:37:47 +03:00
float64 textw = buttonw , minw = _st - > minButtonWidth ( button . type ) ;
float64 w = textw ;
if ( exact ) {
w + = minw ;
} else if ( enough ) {
w = ( widthForButtons / float64 ( s ) ) ;
textw = w - minw ;
} else {
textw = ( widthForText / float64 ( s ) ) ;
w = minw + textw ;
accumulate_max ( w , 2 * float64 ( _st - > buttonPadding ( ) ) ) ;
}
2016-03-28 20:15:17 +03:00
2016-04-01 14:23:40 +04:00
int rectx = static_cast < int > ( std : : floor ( x ) ) ;
int rectw = static_cast < int > ( std : : floor ( x + w ) ) - rectx ;
button . rect = QRect ( rectx , qRound ( y ) , rectw , qRound ( buttonHeight - _st - > buttonSkip ( ) ) ) ;
2016-03-30 20:42:01 +04:00
if ( rtl ( ) ) button . rect . setX ( _width - button . rect . x ( ) - button . rect . width ( ) ) ;
2016-03-28 20:15:17 +03:00
x + = w + _st - > buttonSkip ( ) ;
2016-04-11 00:59:07 +04:00
button . link - > setFullDisplayed ( textw > = buttonw ) ;
2016-03-28 20:15:17 +03:00
}
y + = buttonHeight ;
}
}
bool ReplyKeyboard : : isEnoughSpace ( int width , const style : : botKeyboardButton & st ) const {
2016-09-03 17:27:22 -04:00
for_const ( auto & row , _rows ) {
2016-03-28 20:15:17 +03:00
int s = row . size ( ) ;
int widthLeft = width - ( ( s - 1 ) * st . margin + s * 2 * st . padding ) ;
2016-09-03 17:27:22 -04:00
for_const ( auto & button , row ) {
2016-03-28 20:15:17 +03:00
widthLeft - = qMax ( button . text . maxWidth ( ) , 1 ) ;
if ( widthLeft < 0 ) {
if ( row . size ( ) > 3 ) {
return false ;
} else {
break ;
}
}
}
}
return true ;
}
void ReplyKeyboard : : setStyle ( StylePtr & & st ) {
_st = std_ : : move ( st ) ;
}
2016-03-30 20:42:01 +04:00
int ReplyKeyboard : : naturalWidth ( ) const {
2016-04-14 15:00:44 +03:00
auto result = 0 ;
2016-06-26 18:37:47 +03:00
for_const ( auto & row , _rows ) {
auto maxMinButtonWidth = 0 ;
for_const ( auto & button , row ) {
accumulate_max ( maxMinButtonWidth , _st - > minButtonWidth ( button . type ) ) ;
}
2016-04-14 15:00:44 +03:00
auto rowMaxButtonWidth = 0 ;
2016-06-26 18:37:47 +03:00
for_const ( auto & button , row ) {
accumulate_max ( rowMaxButtonWidth , qMax ( button . text . maxWidth ( ) , 1 ) + maxMinButtonWidth ) ;
2016-03-30 20:42:01 +04:00
}
2016-04-14 15:00:44 +03:00
auto rowSize = row . size ( ) ;
accumulate_max ( result , rowSize * rowMaxButtonWidth + ( rowSize - 1 ) * _st - > buttonSkip ( ) ) ;
2016-03-30 20:42:01 +04:00
}
return result ;
}
2016-03-28 20:15:17 +03:00
int ReplyKeyboard : : naturalHeight ( ) const {
return ( _rows . size ( ) - 1 ) * _st - > buttonSkip ( ) + _rows . size ( ) * _st - > buttonHeight ( ) ;
}
void ReplyKeyboard : : paint ( Painter & p , const QRect & clip ) const {
2016-04-10 23:20:48 +04:00
t_assert ( _st ! = nullptr ) ;
2016-03-28 20:15:17 +03:00
t_assert ( _width > 0 ) ;
_st - > startPaint ( p ) ;
2016-09-03 17:27:22 -04:00
for_const ( auto & row , _rows ) {
for_const ( auto & button , row ) {
2016-03-28 20:15:17 +03:00
QRect rect ( button . rect ) ;
if ( rect . y ( ) > = clip . y ( ) + clip . height ( ) ) return ;
if ( rect . y ( ) + rect . height ( ) < clip . y ( ) ) continue ;
2016-03-30 20:42:01 +04:00
// just ignore the buttons that didn't layout well
if ( rect . x ( ) + rect . width ( ) > _width ) break ;
2016-03-28 20:15:17 +03:00
2016-03-30 20:42:01 +04:00
_st - > paintButton ( p , button ) ;
2016-03-28 20:15:17 +03:00
}
}
}
2016-04-13 21:29:32 +03:00
ClickHandlerPtr ReplyKeyboard : : getState ( int x , int y ) const {
2016-03-28 20:15:17 +03:00
t_assert ( _width > 0 ) ;
2016-09-03 17:27:22 -04:00
for_const ( auto & row , _rows ) {
for_const ( auto & button , row ) {
2016-03-28 20:15:17 +03:00
QRect rect ( button . rect ) ;
2016-03-30 20:42:01 +04:00
// just ignore the buttons that didn't layout well
if ( rect . x ( ) + rect . width ( ) > _width ) break ;
2016-03-28 20:15:17 +03:00
if ( rect . contains ( x , y ) ) {
2016-04-13 21:29:32 +03:00
return button . link ;
2016-03-28 20:15:17 +03:00
}
}
}
2016-04-13 21:29:32 +03:00
return ClickHandlerPtr ( ) ;
2016-03-28 20:15:17 +03:00
}
2016-03-29 20:17:00 +03:00
void ReplyKeyboard : : clickHandlerActiveChanged ( const ClickHandlerPtr & p , bool active ) {
2016-03-30 12:03:29 +04:00
if ( ! p ) return ;
bool startAnimation = false ;
for ( int i = 0 , rows = _rows . size ( ) ; i ! = rows ; + + i ) {
2016-09-03 17:27:22 -04:00
auto & row = _rows . at ( i ) ;
2016-03-30 12:03:29 +04:00
for ( int j = 0 , cols = row . size ( ) ; j ! = cols ; + + j ) {
if ( row . at ( j ) . link = = p ) {
bool startAnimation = _animations . isEmpty ( ) ;
int indexForAnimation = i * MatrixRowShift + j + 1 ;
if ( ! active ) {
indexForAnimation = - indexForAnimation ;
}
_animations . remove ( - indexForAnimation ) ;
if ( ! _animations . contains ( indexForAnimation ) ) {
_animations . insert ( indexForAnimation , getms ( ) ) ;
}
if ( startAnimation & & ! _a_selected . animating ( ) ) {
_a_selected . start ( ) ;
}
return ;
2016-03-28 20:15:17 +03:00
}
}
2016-03-30 12:03:29 +04:00
}
2016-03-28 20:15:17 +03:00
}
2016-03-29 20:17:00 +03:00
void ReplyKeyboard : : clickHandlerPressedChanged ( const ClickHandlerPtr & p , bool pressed ) {
2016-03-30 12:03:29 +04:00
_st - > repaint ( _item ) ;
2016-03-28 20:15:17 +03:00
}
void ReplyKeyboard : : 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 ( ms - i . value ( ) ) / st : : botKbDuration ;
if ( dt > = 1 ) {
_rows [ row ] [ col ] . howMuchOver = ( i . key ( ) > 0 ) ? 1 : 0 ;
i = _animations . erase ( i ) ;
} else {
_rows [ row ] [ col ] . howMuchOver = ( i . key ( ) > 0 ) ? dt : ( 1 - dt ) ;
+ + i ;
}
}
if ( timer ) _st - > repaint ( _item ) ;
if ( _animations . isEmpty ( ) ) {
_a_selected . stop ( ) ;
}
}
void ReplyKeyboard : : clearSelection ( ) {
for ( auto i = _animations . cbegin ( ) , e = _animations . cend ( ) ; i ! = e ; + + i ) {
int index = qAbs ( i . key ( ) ) - 1 , row = ( index / MatrixRowShift ) , col = index % MatrixRowShift ;
_rows [ row ] [ col ] . howMuchOver = 0 ;
}
_animations . clear ( ) ;
_a_selected . stop ( ) ;
}
2016-03-30 20:42:01 +04:00
void ReplyKeyboard : : Style : : paintButton ( Painter & p , const ReplyKeyboard : : Button & button ) const {
const QRect & rect = button . rect ;
bool pressed = ClickHandler : : showAsPressed ( button . link ) ;
paintButtonBg ( p , rect , pressed , button . howMuchOver ) ;
paintButtonIcon ( p , rect , button . type ) ;
2016-09-02 12:11:23 -04:00
if ( button . type = = HistoryMessageReplyMarkup : : Button : : Type : : Callback
| | button . type = = HistoryMessageReplyMarkup : : Button : : Type : : Game ) {
if ( auto data = button . link - > getButton ( ) ) {
2016-04-06 21:02:22 +04:00
if ( data - > requestId ) {
paintButtonLoading ( p , rect ) ;
}
}
}
2016-03-28 20:15:17 +03:00
int tx = rect . x ( ) , tw = rect . width ( ) ;
2016-04-11 00:59:07 +04:00
if ( tw > = st : : botKbFont - > elidew + _st - > padding * 2 ) {
2016-03-28 20:15:17 +03:00
tx + = _st - > padding ;
tw - = _st - > padding * 2 ;
} else if ( tw > st : : botKbFont - > elidew ) {
tx + = ( tw - st : : botKbFont - > elidew ) / 2 ;
tw = st : : botKbFont - > elidew ;
}
2016-03-29 20:17:00 +03:00
int textTop = rect . y ( ) + ( pressed ? _st - > downTextTop : _st - > textTop ) ;
2016-03-30 20:42:01 +04:00
button . text . drawElided ( p , tx , textTop + ( ( rect . height ( ) - _st - > height ) / 2 ) , tw , 1 , style : : al_top ) ;
2016-03-28 20:15:17 +03:00
}
2016-04-06 12:00:37 +04:00
void HistoryMessageReplyMarkup : : createFromButtonRows ( const QVector < MTPKeyboardButtonRow > & v ) {
if ( v . isEmpty ( ) ) {
rows . clear ( ) ;
return ;
}
rows . reserve ( v . size ( ) ) ;
2016-09-03 17:27:22 -04:00
for_const ( auto & row , v ) {
2016-04-06 12:00:37 +04:00
switch ( row . type ( ) ) {
case mtpc_keyboardButtonRow : {
2016-09-03 17:27:22 -04:00
auto & r = row . c_keyboardButtonRow ( ) ;
auto & b = r . vbuttons . c_vector ( ) . v ;
2016-04-06 12:00:37 +04:00
if ( ! b . isEmpty ( ) ) {
ButtonRow buttonRow ;
buttonRow . reserve ( b . size ( ) ) ;
2016-04-08 14:44:35 +04:00
for_const ( const auto & button , b ) {
2016-04-06 12:00:37 +04:00
switch ( button . type ( ) ) {
case mtpc_keyboardButton : {
2016-09-02 12:11:23 -04:00
buttonRow . push_back ( { Button : : Type : : Default , qs ( button . c_keyboardButton ( ) . vtext ) , QByteArray ( ) , 0 } ) ;
2016-04-06 12:00:37 +04:00
} break ;
case mtpc_keyboardButtonCallback : {
2016-09-02 12:11:23 -04:00
auto & buttonData = button . c_keyboardButtonCallback ( ) ;
buttonRow . push_back ( { Button : : Type : : Callback , qs ( buttonData . vtext ) , qba ( buttonData . vdata ) , 0 } ) ;
2016-04-06 12:00:37 +04:00
} break ;
case mtpc_keyboardButtonRequestGeoLocation : {
2016-09-02 12:11:23 -04:00
buttonRow . push_back ( { Button : : Type : : RequestLocation , qs ( button . c_keyboardButtonRequestGeoLocation ( ) . vtext ) , QByteArray ( ) , 0 } ) ;
2016-04-06 12:00:37 +04:00
} break ;
case mtpc_keyboardButtonRequestPhone : {
2016-09-02 12:11:23 -04:00
buttonRow . push_back ( { Button : : Type : : RequestPhone , qs ( button . c_keyboardButtonRequestPhone ( ) . vtext ) , QByteArray ( ) , 0 } ) ;
2016-04-06 12:00:37 +04:00
} break ;
case mtpc_keyboardButtonUrl : {
2016-09-02 12:11:23 -04:00
auto & buttonData = button . c_keyboardButtonUrl ( ) ;
buttonRow . push_back ( { Button : : Type : : Url , qs ( buttonData . vtext ) , qba ( buttonData . vurl ) , 0 } ) ;
2016-04-06 12:00:37 +04:00
} break ;
2016-04-07 14:03:10 +04:00
case mtpc_keyboardButtonSwitchInline : {
2016-08-12 19:28:10 +03:00
auto & buttonData = button . c_keyboardButtonSwitchInline ( ) ;
2016-09-02 12:11:23 -04:00
auto buttonType = buttonData . is_same_peer ( ) ? Button : : Type : : SwitchInlineSame : Button : : Type : : SwitchInline ;
2016-08-12 19:28:10 +03:00
buttonRow . push_back ( { buttonType , qs ( buttonData . vtext ) , qba ( buttonData . vquery ) , 0 } ) ;
2016-09-02 12:11:23 -04:00
if ( buttonType = = Button : : Type : : SwitchInline ) {
2016-08-12 19:28:10 +03:00
// Optimization flag.
// Fast check on all new messages if there is a switch button to auto-click it.
flags | = MTPDreplyKeyboardMarkup_ClientFlag : : f_has_switch_inline_button ;
}
2016-04-07 14:03:10 +04:00
} break ;
2016-09-02 12:11:23 -04:00
case mtpc_keyboardButtonGame : {
auto & buttonData = button . c_keyboardButtonGame ( ) ;
2016-09-19 13:18:21 +03:00
auto title = qs ( buttonData . vgame_title ) ;
auto start = qs ( buttonData . vstart_param ) ;
auto charIsGoodForStartParam = [ ] ( QChar ch ) {
return ( ch > = ' A ' & & ch < = ' Z ' ) | | ( ch > = ' a ' & & ch < = ' z ' ) | | ( ch > = ' 0 ' & & ch < = ' 9 ' ) | | ( ch = = ' _ ' ) | | ( ch = = ' - ' ) ;
} ;
for ( auto & ch : start ) {
if ( ! charIsGoodForStartParam ( ch ) ) {
ch = ' _ ' ;
}
}
auto strData = QString : : number ( buttonData . vgame_id . v ) + ' , ' + start + ' , ' + title ;
2016-09-02 12:11:23 -04:00
buttonRow . push_back ( { Button : : Type : : Game , qs ( buttonData . vtext ) , strData . toUtf8 ( ) , 0 } ) ;
} break ;
2016-04-06 12:00:37 +04:00
}
}
if ( ! buttonRow . isEmpty ( ) ) rows . push_back ( buttonRow ) ;
}
} break ;
}
}
}
2016-03-28 15:51:22 +03:00
void HistoryMessageReplyMarkup : : create ( const MTPReplyMarkup & markup ) {
2016-04-01 14:23:40 +04:00
flags = 0 ;
rows . clear ( ) ;
2016-04-10 23:20:48 +04:00
inlineKeyboard = nullptr ;
2016-04-01 14:23:40 +04:00
2016-03-28 15:51:22 +03:00
switch ( markup . type ( ) ) {
case mtpc_replyKeyboardMarkup : {
2016-04-08 14:44:35 +04:00
const auto & d ( markup . c_replyKeyboardMarkup ( ) ) ;
2016-03-28 15:51:22 +03:00
flags = d . vflags . v ;
2016-04-06 12:00:37 +04:00
createFromButtonRows ( d . vrows . c_vector ( ) . v ) ;
} break ;
case mtpc_replyInlineMarkup : {
2016-04-08 14:44:35 +04:00
const auto & d ( markup . c_replyInlineMarkup ( ) ) ;
2016-04-06 12:00:37 +04:00
flags = MTPDreplyKeyboardMarkup : : Flags ( 0 ) | MTPDreplyKeyboardMarkup_ClientFlag : : f_inline ;
createFromButtonRows ( d . vrows . c_vector ( ) . v ) ;
2016-03-28 15:51:22 +03:00
} break ;
case mtpc_replyKeyboardHide : {
2016-04-08 14:44:35 +04:00
const auto & d ( markup . c_replyKeyboardHide ( ) ) ;
2016-03-28 15:51:22 +03:00
flags = mtpCastFlags ( d . vflags ) | MTPDreplyKeyboardMarkup_ClientFlag : : f_zero ;
} break ;
case mtpc_replyKeyboardForceReply : {
2016-04-08 14:44:35 +04:00
const auto & d ( markup . c_replyKeyboardForceReply ( ) ) ;
2016-03-28 15:51:22 +03:00
flags = mtpCastFlags ( d . vflags ) | MTPDreplyKeyboardMarkup_ClientFlag : : f_force_reply ;
} break ;
}
}
2016-03-10 13:15:21 +03:00
void HistoryDependentItemCallback : : call ( ChannelData * channel , MsgId msgId ) const {
if ( HistoryItem * item = App : : histItemById ( _dependent ) ) {
item - > updateDependencyItem ( ) ;
}
}
2016-03-18 22:05:08 +03:00
void HistoryMessageUnreadBar : : init ( int count ) {
if ( _freezed ) return ;
_text = lng_unread_bar ( lt_count , count ) ;
_width = st : : semiboldFont - > width ( _text ) ;
}
2016-03-31 18:06:40 +04:00
int HistoryMessageUnreadBar : : height ( ) {
return st : : unreadBarHeight + st : : unreadBarMargin ;
}
int HistoryMessageUnreadBar : : marginTop ( ) {
return st : : lineWidth + st : : unreadBarMargin ;
2016-03-19 19:55:15 +03:00
}
void HistoryMessageUnreadBar : : paint ( Painter & p , int y , int w ) const {
2016-03-31 18:06:40 +04:00
p . fillRect ( 0 , y + marginTop ( ) , w , height ( ) - marginTop ( ) - st : : lineWidth , st : : unreadBarBG ) ;
p . fillRect ( 0 , y + height ( ) - st : : lineWidth , w , st : : lineWidth , st : : unreadBarBorder ) ;
2016-03-19 19:55:15 +03:00
p . setFont ( st : : unreadBarFont ) ;
p . setPen ( st : : unreadBarColor ) ;
2016-03-21 21:40:00 +03:00
int left = st : : msgServiceMargin . left ( ) ;
int maxwidth = w ;
if ( Adaptive : : Wide ( ) ) {
maxwidth = qMin ( maxwidth , int32 ( st : : msgMaxWidth + 2 * st : : msgPhotoSkip + 2 * st : : msgMargin . left ( ) ) ) ;
}
w = maxwidth ;
2016-03-31 18:06:40 +04:00
p . drawText ( ( w - _width ) / 2 , y + marginTop ( ) + ( st : : unreadBarHeight - 2 * st : : lineWidth - st : : unreadBarFont - > height ) / 2 + st : : unreadBarFont - > ascent , _text ) ;
2016-03-19 19:55:15 +03:00
}
2016-03-18 22:05:08 +03:00
void HistoryMessageDate : : init ( const QDateTime & date ) {
_text = langDayOfMonthFull ( date . date ( ) ) ;
2016-03-19 19:55:15 +03:00
_width = st : : msgServiceFont - > width ( _text ) ;
}
int HistoryMessageDate : : height ( ) const {
return st : : msgServiceMargin . top ( ) + st : : msgServicePadding . top ( ) + st : : msgServiceFont - > height + st : : msgServicePadding . bottom ( ) + st : : msgServiceMargin . bottom ( ) ;
}
void HistoryMessageDate : : paint ( Painter & p , int y , int w ) const {
2016-06-10 13:21:09 +03:00
HistoryLayout : : ServiceMessagePainter : : paintDate ( p , _text , _width , y , w ) ;
2016-03-18 22:05:08 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryMediaPtr : : reset ( HistoryMedia * p ) {
2016-03-29 20:17:00 +03:00
if ( _p ) {
2016-04-10 18:53:01 +04:00
_p - > detachFromParent ( ) ;
2016-03-29 20:17:00 +03:00
delete _p ;
}
_p = p ;
if ( _p ) {
2016-04-10 18:53:01 +04:00
_p - > attachToParent ( ) ;
2016-03-29 20:17:00 +03:00
}
}
2016-04-14 14:00:23 +03:00
namespace internal {
TextSelection unshiftSelection ( TextSelection selection , const Text & byText ) {
if ( selection = = FullSelection ) {
return selection ;
}
return : : unshiftSelection ( selection , byText ) ;
}
TextSelection shiftSelection ( TextSelection selection , const Text & byText ) {
if ( selection = = FullSelection ) {
return selection ;
}
return : : shiftSelection ( selection , byText ) ;
}
} // namespace internal
2016-03-19 19:55:15 +03:00
HistoryItem : : HistoryItem ( History * history , MsgId msgId , MTPDmessage : : Flags flags , QDateTime msgDate , int32 from ) : HistoryElem ( )
2016-03-18 22:05:08 +03:00
, y ( 0 )
2014-06-16 13:31:10 +04:00
, id ( msgId )
, date ( msgDate )
2015-09-04 16:01:31 +03:00
, _from ( from ? App : : user ( from ) : history - > peer )
2014-06-16 13:31:10 +04:00
, _history ( history )
2016-03-19 19:55:15 +03:00
, _flags ( flags | MTPDmessage_ClientFlag : : f_pending_init_dimensions | MTPDmessage_ClientFlag : : f_pending_resize )
2016-02-17 19:37:21 +03:00
, _authorNameVersion ( author ( ) - > nameVersion ) {
2014-05-30 12:53:19 +04:00
}
2016-03-18 22:05:08 +03:00
void HistoryItem : : finishCreate ( ) {
App : : historyRegItem ( this ) ;
}
2016-06-13 21:42:25 +03:00
void HistoryItem : : finishEdition ( int oldKeyboardTop ) {
setPendingInitDimensions ( ) ;
if ( App : : main ( ) ) {
App : : main ( ) - > dlgUpdated ( history ( ) , id ) ;
}
// invalidate cache for drawInDialog
if ( history ( ) - > textCachedFor = = this ) {
history ( ) - > textCachedFor = nullptr ;
}
if ( oldKeyboardTop > = 0 ) {
if ( auto keyboard = Get < HistoryMessageReplyMarkup > ( ) ) {
keyboard - > oldTop = oldKeyboardTop ;
}
}
App : : historyUpdateDependent ( this ) ;
}
void HistoryItem : : finishEditionToEmpty ( ) {
recountDisplayDate ( ) ;
finishEdition ( - 1 ) ;
_history - > removeNotification ( this ) ;
if ( history ( ) - > isChannel ( ) ) {
if ( history ( ) - > peer - > isMegagroup ( ) & & history ( ) - > peer - > asChannel ( ) - > mgInfo - > pinnedMsgId = = id ) {
history ( ) - > peer - > asChannel ( ) - > mgInfo - > pinnedMsgId = 0 ;
}
}
if ( history ( ) - > lastKeyboardId = = id ) {
history ( ) - > clearLastKeyboard ( ) ;
}
if ( ( ! out ( ) | | isPost ( ) ) & & unread ( ) & & history ( ) - > unreadCount ( ) > 0 ) {
history ( ) - > setUnreadCount ( history ( ) - > unreadCount ( ) - 1 ) ;
}
if ( auto next = nextItem ( ) ) {
next - > previousItemChanged ( ) ;
}
}
2016-03-29 20:17:00 +03:00
void HistoryItem : : clickHandlerActiveChanged ( const ClickHandlerPtr & p , bool active ) {
2016-04-08 13:20:10 +04:00
if ( auto markup = Get < HistoryMessageReplyMarkup > ( ) ) {
2016-03-29 20:17:00 +03:00
if ( markup - > inlineKeyboard ) {
markup - > inlineKeyboard - > clickHandlerActiveChanged ( p , active ) ;
}
}
App : : hoveredLinkItem ( active ? this : nullptr ) ;
Ui : : repaintHistoryItem ( this ) ;
}
void HistoryItem : : clickHandlerPressedChanged ( const ClickHandlerPtr & p , bool pressed ) {
2016-04-08 13:20:10 +04:00
if ( auto markup = Get < HistoryMessageReplyMarkup > ( ) ) {
2016-03-29 20:17:00 +03:00
if ( markup - > inlineKeyboard ) {
markup - > inlineKeyboard - > clickHandlerPressedChanged ( p , pressed ) ;
}
}
App : : pressedLinkItem ( pressed ? this : nullptr ) ;
Ui : : repaintHistoryItem ( this ) ;
}
2014-08-11 13:03:45 +04:00
void HistoryItem : : destroy ( ) {
2016-04-14 22:24:42 +03:00
// All this must be done for all items manually in History::clear(false)!
2016-05-20 19:01:06 +03:00
eraseFromOverview ( ) ;
2014-08-11 13:03:45 +04:00
bool wasAtBottom = history ( ) - > loadedAtBottom ( ) ;
_history - > removeNotification ( this ) ;
detach ( ) ;
2015-09-19 12:13:21 +03:00
if ( history ( ) - > isChannel ( ) ) {
2016-03-10 18:42:01 +03:00
if ( history ( ) - > peer - > isMegagroup ( ) & & history ( ) - > peer - > asChannel ( ) - > mgInfo - > pinnedMsgId = = id ) {
history ( ) - > peer - > asChannel ( ) - > mgInfo - > pinnedMsgId = 0 ;
}
2015-09-19 12:13:21 +03:00
}
2015-06-15 20:19:24 +03:00
if ( history ( ) - > lastMsg = = this ) {
2014-08-11 13:03:45 +04:00
history ( ) - > fixLastMessage ( wasAtBottom ) ;
}
2015-10-03 13:09:09 +03:00
if ( history ( ) - > lastKeyboardId = = id ) {
2015-11-24 19:19:18 +03:00
history ( ) - > clearLastKeyboard ( ) ;
2015-10-03 13:09:09 +03:00
}
2016-04-11 14:59:01 +04:00
if ( ( ! out ( ) | | isPost ( ) ) & & unread ( ) & & history ( ) - > unreadCount ( ) > 0 ) {
history ( ) - > setUnreadCount ( history ( ) - > unreadCount ( ) - 1 ) ;
2016-03-26 09:41:23 +03:00
}
2016-03-31 18:06:40 +04:00
Global : : RefPendingRepaintItems ( ) . remove ( this ) ;
2014-08-11 13:03:45 +04:00
delete this ;
}
2014-07-04 15:12:54 +04:00
void HistoryItem : : detach ( ) {
2016-03-21 21:40:00 +03:00
if ( detached ( ) ) return ;
2016-03-19 19:55:15 +03:00
if ( _history - > isChannel ( ) ) {
_history - > asChannelHistory ( ) - > messageDetached ( this ) ;
2014-07-04 15:12:54 +04:00
}
2016-03-21 21:40:00 +03:00
_block - > removeItem ( this ) ;
App : : historyItemDetached ( this ) ;
2016-03-19 19:55:15 +03:00
2016-03-21 21:40:00 +03:00
_history - > setPendingResize ( ) ;
2014-07-04 15:12:54 +04:00
}
void HistoryItem : : detachFast ( ) {
2016-03-21 21:40:00 +03:00
_block = nullptr ;
2016-03-18 22:05:08 +03:00
_indexInBlock = - 1 ;
}
void HistoryItem : : previousItemChanged ( ) {
2016-06-10 13:21:09 +03:00
recountDisplayDate ( ) ;
2016-03-21 21:40:00 +03:00
recountAttachToPrevious ( ) ;
}
void HistoryItem : : recountAttachToPrevious ( ) {
bool attach = false ;
2016-03-25 14:29:45 +03:00
if ( ! isPost ( ) & & ! Has < HistoryMessageDate > ( ) & & ! Has < HistoryMessageUnreadBar > ( ) ) {
2016-06-13 21:42:25 +03:00
if ( auto previos = previousItem ( ) ) {
attach = ! previos - > isPost ( )
& & ! previos - > serviceMsg ( )
& & ! previos - > isEmpty ( )
& & previos - > from ( ) = = from ( )
& & ( qAbs ( previos - > date . secsTo ( date ) ) < AttachMessageToPreviousSecondsDelta ) ;
2016-03-21 21:40:00 +03:00
}
}
if ( attach & & ! ( _flags & MTPDmessage_ClientFlag : : f_attach_to_previous ) ) {
_flags | = MTPDmessage_ClientFlag : : f_attach_to_previous ;
setPendingInitDimensions ( ) ;
} else if ( ! attach & & ( _flags & MTPDmessage_ClientFlag : : f_attach_to_previous ) ) {
_flags & = ~ MTPDmessage_ClientFlag : : f_attach_to_previous ;
setPendingInitDimensions ( ) ;
}
2014-07-04 15:12:54 +04:00
}
2015-11-18 16:11:56 +03:00
void HistoryItem : : setId ( MsgId newId ) {
history ( ) - > changeMsgId ( id , newId ) ;
id = newId ;
2016-06-15 08:36:59 +03:00
// We don't need to call Notify::replyMarkupUpdated(this) and update keyboard
// in history widget, because it can't exist for an outgoing message.
// Only inline keyboards can be in outgoing messages.
if ( auto markup = inlineReplyMarkup ( ) ) {
if ( markup - > inlineKeyboard ) {
markup - > inlineKeyboard - > updateMessageId ( ) ;
}
}
2015-11-18 16:11:56 +03:00
}
2016-02-21 15:30:16 +03:00
bool HistoryItem : : canEdit ( const QDateTime & cur ) const {
2016-07-31 10:29:15 +01:00
auto messageToMyself = ( peerToUser ( _history - > peer - > id ) = = MTP : : authedId ( ) ) ;
auto messageTooOld = messageToMyself ? false : ( date . secsTo ( cur ) > = Global : : EditTimeLimit ( ) ) ;
if ( id < 0 | | messageTooOld ) return false ;
2016-02-21 15:30:16 +03:00
2016-05-06 20:33:48 +03:00
if ( auto msg = toHistoryMessage ( ) ) {
2016-03-25 14:29:45 +03:00
if ( msg - > Has < HistoryMessageVia > ( ) | | msg - > Has < HistoryMessageForwarded > ( ) ) return false ;
2016-02-21 15:30:16 +03:00
if ( HistoryMedia * media = msg - > getMedia ( ) ) {
2016-05-06 20:33:48 +03:00
auto type = media - > type ( ) ;
if ( type ! = MediaTypePhoto & &
type ! = MediaTypeVideo & &
type ! = MediaTypeFile & &
type ! = MediaTypeGif & &
type ! = MediaTypeMusicFile & &
type ! = MediaTypeVoiceFile & &
type ! = MediaTypeWebPage ) {
2016-02-21 15:30:16 +03:00
return false ;
}
}
if ( isPost ( ) ) {
2016-05-09 15:03:06 +03:00
auto channel = _history - > peer - > asChannel ( ) ;
2016-02-21 15:30:16 +03:00
return ( channel - > amCreator ( ) | | ( channel - > amEditor ( ) & & out ( ) ) ) ;
}
2016-07-31 10:29:15 +01:00
return out ( ) | | messageToMyself ;
2016-02-21 15:30:16 +03:00
}
return false ;
}
2016-05-20 19:01:06 +03:00
bool HistoryItem : : unread ( ) const {
// Messages from myself are always read.
if ( history ( ) - > peer - > isSelf ( ) ) return false ;
if ( out ( ) ) {
// Outgoing messages in converted chats are always read.
if ( history ( ) - > peer - > migrateTo ( ) ) return false ;
if ( id > 0 ) {
if ( id < history ( ) - > outboxReadBefore ) return false ;
2016-06-27 19:25:21 +03:00
if ( auto user = history ( ) - > peer - > asUser ( ) ) {
if ( user - > botInfo ) return false ;
} else if ( auto channel = history ( ) - > peer - > asChannel ( ) ) {
2016-05-20 19:01:06 +03:00
if ( ! channel - > isMegagroup ( ) ) return false ;
}
}
return true ;
}
if ( id > 0 ) {
if ( id < history ( ) - > inboxReadBefore ) return false ;
return true ;
}
return ( _flags & MTPDmessage_ClientFlag : : f_clientside_unread ) ;
}
2016-03-18 22:05:08 +03:00
void HistoryItem : : destroyUnreadBar ( ) {
2016-03-25 14:29:45 +03:00
if ( Has < HistoryMessageUnreadBar > ( ) ) {
RemoveComponents ( HistoryMessageUnreadBar : : Bit ( ) ) ;
2016-03-19 19:55:15 +03:00
setPendingInitDimensions ( ) ;
2016-03-18 22:05:08 +03:00
if ( _history - > unreadBar = = this ) {
_history - > unreadBar = nullptr ;
}
2016-03-21 21:40:00 +03:00
recountAttachToPrevious ( ) ;
2016-03-18 22:05:08 +03:00
}
}
void HistoryItem : : setUnreadBarCount ( int count ) {
if ( count > 0 ) {
HistoryMessageUnreadBar * bar ;
2016-03-25 14:29:45 +03:00
if ( ! Has < HistoryMessageUnreadBar > ( ) ) {
AddComponents ( HistoryMessageUnreadBar : : Bit ( ) ) ;
2016-03-19 19:55:15 +03:00
setPendingInitDimensions ( ) ;
2016-03-21 21:40:00 +03:00
recountAttachToPrevious ( ) ;
2016-03-18 22:05:08 +03:00
bar = Get < HistoryMessageUnreadBar > ( ) ;
} else {
bar = Get < HistoryMessageUnreadBar > ( ) ;
if ( bar - > _freezed ) {
return ;
}
Global : : RefPendingRepaintItems ( ) . insert ( this ) ;
}
bar - > init ( count ) ;
} else {
destroyUnreadBar ( ) ;
}
}
void HistoryItem : : setUnreadBarFreezed ( ) {
2016-04-08 13:20:10 +04:00
if ( auto bar = Get < HistoryMessageUnreadBar > ( ) ) {
2016-03-18 22:05:08 +03:00
bar - > _freezed = true ;
}
}
2016-06-24 13:37:29 +03:00
void HistoryItem : : clipCallback ( Media : : Clip : : Notification notification ) {
using namespace Media : : Clip ;
2015-12-29 00:20:04 +03:00
HistoryMedia * media = getMedia ( ) ;
if ( ! media ) return ;
2015-12-31 23:27:21 +08:00
2016-06-24 13:37:29 +03:00
Reader * reader = media ? media - > getClipReader ( ) : 0 ;
2015-12-29 00:20:04 +03:00
if ( ! reader ) return ;
switch ( notification ) {
2016-06-24 13:37:29 +03:00
case NotificationReinit : {
2015-12-29 00:20:04 +03:00
bool stopped = false ;
2016-07-13 14:24:31 +03:00
if ( reader - > autoPausedGif ( ) ) {
2015-12-29 00:20:04 +03:00
if ( MainWidget * m = App : : main ( ) ) {
if ( ! m - > isItemVisible ( this ) ) { // stop animation if it is not visible
2016-04-10 18:53:01 +04:00
media - > stopInline ( ) ;
2015-12-29 00:20:04 +03:00
if ( DocumentData * document = media - > getDocument ( ) ) { // forget data from memory
document - > forget ( ) ;
}
stopped = true ;
}
}
}
if ( ! stopped ) {
2016-03-19 19:55:15 +03:00
setPendingInitDimensions ( ) ;
2015-12-29 00:20:04 +03:00
Notify : : historyItemLayoutChanged ( this ) ;
}
} break ;
2016-06-24 13:37:29 +03:00
case NotificationRepaint : {
2015-12-29 00:20:04 +03:00
if ( ! reader - > currentDisplayed ( ) ) {
Ui : : repaintHistoryItem ( this ) ;
}
} break ;
}
}
2016-06-10 13:21:09 +03:00
void HistoryItem : : recountDisplayDate ( ) {
bool displayingDate = ( [ this ] ( ) {
if ( isEmpty ( ) ) return false ;
2016-06-13 21:42:25 +03:00
if ( auto previous = previousItem ( ) ) {
return previous - > isEmpty ( ) | | ( previous - > date . date ( ) ! = date . date ( ) ) ;
2016-06-10 13:21:09 +03:00
}
return true ;
} ) ( ) ;
2016-06-08 22:14:17 +03:00
2016-06-10 13:21:09 +03:00
if ( displayingDate & & ! Has < HistoryMessageDate > ( ) ) {
AddComponents ( HistoryMessageDate : : Bit ( ) ) ;
Get < HistoryMessageDate > ( ) - > init ( date ) ;
setPendingInitDimensions ( ) ;
} else if ( ! displayingDate & & Has < HistoryMessageDate > ( ) ) {
RemoveComponents ( HistoryMessageDate : : Bit ( ) ) ;
setPendingInitDimensions ( ) ;
2016-06-08 22:14:17 +03:00
}
}
2016-06-22 16:39:54 +03:00
QString HistoryItem : : notificationText ( ) const {
auto getText = [ this ] ( ) {
if ( emptyText ( ) ) {
return _media ? _media - > notificationText ( ) : QString ( ) ;
}
return _text . originalText ( ) ;
} ;
auto result = getText ( ) ;
if ( result . size ( ) > 0xFF ) result = result . mid ( 0 , 0xFF ) + qsl ( " ... " ) ;
return result ;
}
QString HistoryItem : : inDialogsText ( ) const {
auto getText = [ this ] ( ) {
if ( emptyText ( ) ) {
return _media ? _media - > inDialogsText ( ) : QString ( ) ;
}
return textClean ( _text . originalText ( ) ) ;
} ;
auto plainText = getText ( ) ;
if ( ( ! _history - > peer - > isUser ( ) | | out ( ) ) & & ! isPost ( ) & & ! isEmpty ( ) ) {
auto fromText = author ( ) - > isSelf ( ) ? lang ( lng_from_you ) : author ( ) - > shortName ( ) ;
auto fromWrapped = textcmdLink ( 1 , lng_dialogs_text_from_wrapped ( lt_from , textClean ( fromText ) ) ) ;
return lng_dialogs_text_with_from ( lt_from_part , fromWrapped , lt_message , plainText ) ;
}
return plainText ;
}
void HistoryItem : : drawInDialog ( Painter & p , const QRect & r , bool act , const HistoryItem * & cacheFor , Text & cache ) const {
if ( cacheFor ! = this ) {
cacheFor = this ;
cache . setText ( st : : dialogsTextFont , inDialogsText ( ) , _textDlgOptions ) ;
}
if ( r . width ( ) ) {
textstyleSet ( & ( act ? st : : dialogsTextStyleActive : st : : dialogsTextStyle ) ) ;
p . setFont ( st : : dialogsTextFont ) ;
p . setPen ( act ? st : : dialogsTextFgActive : st : : dialogsTextFg ) ;
cache . drawElided ( p , r . left ( ) , r . top ( ) , r . width ( ) , r . height ( ) / st : : dialogsTextFont - > height ) ;
textstyleRestore ( ) ;
}
}
2014-05-30 12:53:19 +04:00
HistoryItem : : ~ HistoryItem ( ) {
App : : historyUnregItem ( this ) ;
2015-12-28 20:23:27 +03:00
if ( id < 0 & & App : : uploader ( ) ) {
App : : uploader ( ) - > cancel ( fullId ( ) ) ;
2014-05-30 12:53:19 +04:00
}
}
2016-05-12 19:05:20 +03:00
RadialAnimation : : RadialAnimation ( AnimationCallbacks & & callbacks )
2015-12-22 15:49:42 +03:00
: _firstStart ( 0 )
2015-12-11 21:11:38 +03:00
, _lastStart ( 0 )
, _lastTime ( 0 )
, _opacity ( 0 )
, a_arcEnd ( 0 , 0 )
2015-12-13 01:29:33 +03:00
, a_arcStart ( 0 , FullArcLength )
2016-05-12 19:05:20 +03:00
, _animation ( std_ : : move ( callbacks ) ) {
2015-12-11 21:11:38 +03:00
}
void RadialAnimation : : start ( float64 prg ) {
_firstStart = _lastStart = _lastTime = getms ( ) ;
2015-12-13 01:29:33 +03:00
int32 iprg = qRound ( qMax ( prg , 0.0001 ) * AlmostFullArcLength ) , iprgstrict = qRound ( prg * AlmostFullArcLength ) ;
a_arcEnd = anim : : ivalue ( iprgstrict , iprg ) ;
2015-12-11 21:11:38 +03:00
_animation . start ( ) ;
}
void RadialAnimation : : update ( float64 prg , bool finished , uint64 ms ) {
2015-12-13 01:29:33 +03:00
int32 iprg = qRound ( qMax ( prg , 0.0001 ) * AlmostFullArcLength ) ;
if ( iprg ! = a_arcEnd . to ( ) ) {
a_arcEnd . start ( iprg ) ;
2015-12-11 21:11:38 +03:00
_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 ;
2016-04-01 00:36:46 +04:00
a_arcStart . update ( fromstart - std : : floor ( fromstart ) , anim : : linear ) ;
2015-12-11 21:11:38 +03:00
}
void RadialAnimation : : stop ( ) {
_firstStart = _lastStart = _lastTime = 0 ;
2015-12-13 01:29:33 +03:00
a_arcEnd = anim : : ivalue ( 0 , 0 ) ;
2015-12-11 21:11:38 +03:00
_animation . stop ( ) ;
}
2015-12-13 01:29:33 +03:00
void RadialAnimation : : step ( uint64 ms ) {
_animation . step ( ms ) ;
}
2015-12-11 21:11:38 +03:00
2015-12-22 15:49:42 +03:00
void RadialAnimation : : draw ( Painter & p , const QRect & inner , int32 thickness , const style : : color & color ) {
2015-12-11 21:11:38 +03:00
float64 o = p . opacity ( ) ;
p . setOpacity ( o * _opacity ) ;
QPen pen ( color - > p ) , was ( p . pen ( ) ) ;
2015-12-22 15:49:42 +03:00
pen . setWidth ( thickness ) ;
2015-12-11 21:11:38 +03:00
p . setPen ( pen ) ;
2015-12-13 01:29:33 +03:00
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 ) ;
2015-12-11 21:11:38 +03:00
p . setPen ( was ) ;
p . setOpacity ( o ) ;
}
2016-06-22 16:39:54 +03:00
QString HistoryMedia : : inDialogsText ( ) const {
auto result = notificationText ( ) ;
return result . isEmpty ( ) ? QString ( ) : textcmdLink ( 1 , textClean ( result ) ) ;
}
2015-12-25 16:09:14 +03:00
namespace {
2016-04-14 14:00:23 +03:00
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 if ( VoiceData * voice = document - > voice ( ) ) {
result = qMax ( result , st : : normalFont - > width ( formatPlayedText ( voice - > duration , voice - > duration ) ) ) ;
result = qMax ( result , st : : normalFont - > width ( formatDurationAndSizeText ( voice - > duration , document - > size ) ) ) ;
} else if ( document - > isVideo ( ) ) {
result = qMax ( result , st : : normalFont - > width ( formatDurationAndSizeText ( document - > duration ( ) , document - > size ) ) ) ;
} else {
result = qMax ( result , st : : normalFont - > width ( formatSizeText ( document - > size ) ) ) ;
2015-12-25 16:09:14 +03:00
}
2016-04-14 14:00:23 +03:00
return result ;
}
2015-12-25 16:09:14 +03:00
2016-04-14 14:00:23 +03:00
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 ;
}
2016-05-06 20:33:48 +03:00
TextWithEntities captionedSelectedText ( const QString & attachType , const Text & caption , TextSelection selection ) {
2016-04-14 14:00:23 +03:00
if ( selection ! = FullSelection ) {
2016-05-06 20:33:48 +03:00
return caption . originalTextWithEntities ( selection , ExpandLinksAll ) ;
2015-12-25 16:09:14 +03:00
}
2016-05-06 20:33:48 +03:00
TextWithEntities result , original ;
if ( ! caption . isEmpty ( ) ) {
original = caption . originalTextWithEntities ( AllTextSelection , ExpandLinksAll ) ;
}
result . text . reserve ( 5 + attachType . size ( ) + original . text . size ( ) ) ;
result . text . append ( qstr ( " [ " ) ) . append ( attachType ) . append ( qstr ( " ] " ) ) ;
2016-04-14 14:00:23 +03:00
if ( ! caption . isEmpty ( ) ) {
2016-05-06 20:33:48 +03:00
result . text . append ( qstr ( " \n " ) ) ;
appendTextWithEntities ( result , std_ : : move ( original ) ) ;
2016-04-14 14:00:23 +03:00
}
return result ;
2015-12-25 16:09:14 +03:00
}
2016-05-06 20:33:48 +03:00
2016-06-22 16:39:54 +03:00
QString captionedNotificationText ( const QString & attachType , const Text & caption ) {
if ( caption . isEmpty ( ) ) {
return attachType ;
}
auto captionText = caption . originalText ( ) ;
auto attachTypeWrapped = lng_dialogs_text_media_wrapped ( lt_media , attachType ) ;
return lng_dialogs_text_media ( lt_media_part , attachTypeWrapped , lt_caption , captionText ) ;
}
QString captionedInDialogsText ( const QString & attachType , const Text & caption ) {
if ( caption . isEmpty ( ) ) {
return textcmdLink ( 1 , textClean ( attachType ) ) ;
}
auto captionText = textClean ( caption . originalText ( ) ) ;
auto attachTypeWrapped = textcmdLink ( 1 , lng_dialogs_text_media_wrapped ( lt_media , textClean ( attachType ) ) ) ;
return lng_dialogs_text_media ( lt_media_part , attachTypeWrapped , lt_caption , captionText ) ;
}
2016-04-14 14:00:23 +03:00
} // namespace
2015-12-25 16:09:14 +03:00
2016-04-10 18:53:01 +04:00
void HistoryFileMedia : : clickHandlerActiveChanged ( const ClickHandlerPtr & p , bool active ) {
2016-03-29 20:17:00 +03:00
if ( p = = _savel | | p = = _cancell ) {
if ( active & & ! dataLoaded ( ) ) {
2016-04-10 18:53:01 +04:00
ensureAnimation ( ) ;
2016-03-29 20:17:00 +03:00
_animation - > a_thumbOver . start ( 1 ) ;
_animation - > _a_thumbOver . start ( ) ;
} else if ( ! active & & _animation ) {
_animation - > a_thumbOver . start ( 0 ) ;
_animation - > _a_thumbOver . start ( ) ;
}
2015-12-25 16:09:14 +03:00
}
}
2016-04-10 18:53:01 +04:00
void HistoryFileMedia : : clickHandlerPressedChanged ( const ClickHandlerPtr & p , bool pressed ) {
Ui : : repaintHistoryItem ( _parent ) ;
2015-12-25 16:09:14 +03:00
}
2016-03-29 20:17:00 +03:00
void HistoryFileMedia : : setLinks ( ClickHandlerPtr & & openl , ClickHandlerPtr & & savel , ClickHandlerPtr & & cancell ) {
_openl = std_ : : move ( openl ) ;
_savel = std_ : : move ( savel ) ;
_cancell = std_ : : move ( cancell ) ;
2015-12-25 16:09:14 +03:00
}
void HistoryFileMedia : : setStatusSize ( int32 newSize , int32 fullSize , int32 duration , qint64 realDuration ) const {
_statusSize = newSize ;
if ( _statusSize = = FileStatusSizeReady ) {
_statusText = ( duration > = 0 ) ? formatDurationAndSizeText ( duration , fullSize ) : ( duration < - 1 ? formatGifAndSizeText ( fullSize ) : formatSizeText ( fullSize ) ) ;
} else if ( _statusSize = = FileStatusSizeLoaded ) {
_statusText = ( duration > = 0 ) ? formatDurationText ( duration ) : ( duration < - 1 ? qsl ( " GIF " ) : formatSizeText ( fullSize ) ) ;
} else if ( _statusSize = = FileStatusSizeFailed ) {
_statusText = lang ( lng_attach_failed ) ;
} else if ( _statusSize > = 0 ) {
_statusText = formatDownloadText ( _statusSize , fullSize ) ;
} else {
_statusText = formatPlayedText ( - _statusSize - 1 , realDuration ) ;
}
}
2016-04-10 18:53:01 +04:00
void HistoryFileMedia : : step_thumbOver ( float64 ms , bool timer ) {
2015-12-25 16:09:14 +03:00
float64 dt = ms / st : : msgFileOverDuration ;
if ( dt > = 1 ) {
_animation - > a_thumbOver . finish ( ) ;
_animation - > _a_thumbOver . stop ( ) ;
checkAnimationFinished ( ) ;
2015-12-28 00:37:48 +03:00
} else if ( ! timer ) {
2015-12-25 16:09:14 +03:00
_animation - > a_thumbOver . update ( dt , anim : : linear ) ;
}
if ( timer ) {
2016-04-10 18:53:01 +04:00
Ui : : repaintHistoryItem ( _parent ) ;
2015-12-25 16:09:14 +03:00
}
}
2016-04-10 18:53:01 +04:00
void HistoryFileMedia : : step_radial ( uint64 ms , bool timer ) {
2015-12-25 16:09:14 +03:00
if ( timer ) {
2016-04-10 18:53:01 +04:00
Ui : : repaintHistoryItem ( _parent ) ;
2015-12-28 00:37:48 +03:00
} else {
_animation - > radial . update ( dataProgress ( ) , dataFinished ( ) , ms ) ;
if ( ! _animation - > radial . animating ( ) ) {
checkAnimationFinished ( ) ;
}
2015-12-25 16:09:14 +03:00
}
}
2016-04-10 18:53:01 +04:00
void HistoryFileMedia : : ensureAnimation ( ) const {
2015-12-25 16:09:14 +03:00
if ( ! _animation ) {
_animation = new AnimationData (
2016-04-10 18:53:01 +04:00
animation ( const_cast < HistoryFileMedia * > ( this ) , & HistoryFileMedia : : step_thumbOver ) ,
animation ( const_cast < HistoryFileMedia * > ( this ) , & HistoryFileMedia : : step_radial ) ) ;
2015-12-25 16:09:14 +03:00
}
}
void HistoryFileMedia : : checkAnimationFinished ( ) {
if ( _animation & & ! _animation - > _a_thumbOver . animating ( ) & & ! _animation - > radial . animating ( ) ) {
if ( dataLoaded ( ) ) {
delete _animation ;
_animation = 0 ;
}
}
}
HistoryFileMedia : : ~ HistoryFileMedia ( ) {
2015-12-29 00:20:04 +03:00
deleteAndMark ( _animation ) ;
2015-12-25 16:09:14 +03:00
}
2016-04-10 18:53:01 +04:00
HistoryPhoto : : HistoryPhoto ( HistoryItem * parent , PhotoData * photo , const QString & caption ) : HistoryFileMedia ( parent )
2015-12-31 13:34:43 +08:00
, _data ( photo )
2015-12-13 18:21:20 +03:00
, _caption ( st : : minPhotoSize - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) {
2016-03-29 20:17:00 +03:00
setLinks ( MakeShared < PhotoOpenClickHandler > ( _data ) , MakeShared < PhotoSaveClickHandler > ( _data ) , MakeShared < PhotoCancelClickHandler > ( _data ) ) ;
2015-04-30 16:53:36 +03:00
if ( ! caption . isEmpty ( ) ) {
2016-04-10 18:53:01 +04:00
_caption . setText ( st : : msgFont , caption + _parent - > skipBlock ( ) , itemTextNoMonoOptions ( _parent ) ) ;
2015-04-30 16:53:36 +03:00
}
2014-08-11 13:03:45 +04:00
init ( ) ;
}
2016-04-10 18:53:01 +04:00
HistoryPhoto : : HistoryPhoto ( HistoryItem * parent , PeerData * chat , const MTPDphoto & photo , int32 width ) : HistoryFileMedia ( parent )
2016-03-29 20:17:00 +03:00
, _data ( App : : feedPhoto ( photo ) ) {
setLinks ( MakeShared < PhotoOpenClickHandler > ( _data , chat ) , MakeShared < PhotoSaveClickHandler > ( _data , chat ) , MakeShared < PhotoCancelClickHandler > ( _data , chat ) ) ;
2015-12-24 22:26:28 +03:00
2015-12-19 21:09:24 +03:00
_width = width ;
2014-08-11 13:03:45 +04:00
init ( ) ;
}
2016-04-10 18:53:01 +04:00
HistoryPhoto : : HistoryPhoto ( HistoryItem * parent , const HistoryPhoto & other ) : HistoryFileMedia ( parent )
2015-12-24 22:26:28 +03:00
, _data ( other . _data )
, _pixw ( other . _pixw )
2015-12-24 23:29:33 +03:00
, _pixh ( other . _pixh )
, _caption ( other . _caption ) {
2016-03-29 20:17:00 +03:00
setLinks ( MakeShared < PhotoOpenClickHandler > ( _data ) , MakeShared < PhotoSaveClickHandler > ( _data ) , MakeShared < PhotoCancelClickHandler > ( _data ) ) ;
2015-12-24 22:26:28 +03:00
init ( ) ;
}
2014-08-11 13:03:45 +04:00
void HistoryPhoto : : init ( ) {
2015-12-09 21:06:20 +03:00
_data - > thumb - > load ( ) ;
2014-10-10 16:46:20 +04:00
}
2016-04-10 18:53:01 +04:00
void HistoryPhoto : : initDimensions ( ) {
2015-12-09 21:06:20 +03:00
if ( _caption . hasSkipBlock ( ) ) {
2016-04-10 18:53:01 +04:00
_caption . setSkipBlock ( _parent - > skipBlockWidth ( ) , _parent - > skipBlockHeight ( ) ) ;
2015-12-09 21:06:20 +03:00
}
int32 tw = convertScale ( _data - > full - > width ( ) ) , th = convertScale ( _data - > full - > height ( ) ) ;
2014-05-30 12:53:19 +04:00
if ( ! tw | | ! th ) {
tw = th = 1 ;
}
2015-12-09 21:06:20 +03:00
if ( tw > st : : maxMediaSize ) {
th = ( st : : maxMediaSize * th ) / tw ;
tw = st : : maxMediaSize ;
}
if ( th > st : : maxMediaSize ) {
tw = ( st : : maxMediaSize * tw ) / th ;
th = st : : maxMediaSize ;
}
2016-04-10 18:53:01 +04:00
if ( _parent - > toHistoryMessage ( ) ) {
bool bubble = _parent - > hasBubble ( ) ;
2015-12-19 21:09:24 +03:00
2016-04-10 18:53:01 +04:00
int32 minWidth = qMax ( st : : minPhotoSize , _parent - > infoWidth ( ) + 2 * ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
2015-12-19 21:09:24 +03:00
int32 maxActualWidth = qMax ( tw , minWidth ) ;
2015-12-13 18:21:20 +03:00
_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 ( ) ;
}
}
2014-11-18 15:41:33 +03:00
} else {
2015-12-19 21:09:24 +03:00
_maxw = _minh = _width ;
2015-03-19 12:18:19 +03:00
}
2014-05-30 12:53:19 +04:00
}
2016-04-10 18:53:01 +04:00
int HistoryPhoto : : resizeGetHeight ( int width ) {
bool bubble = _parent - > hasBubble ( ) ;
2014-11-12 23:18:00 +03:00
2016-04-07 14:03:10 +04:00
int tw = convertScale ( _data - > full - > width ( ) ) , th = convertScale ( _data - > full - > height ( ) ) ;
2014-11-18 15:41:33 +03:00
if ( tw > st : : maxMediaSize ) {
th = ( st : : maxMediaSize * th ) / tw ;
tw = st : : maxMediaSize ;
}
2014-11-18 15:59:16 +03:00
if ( th > st : : maxMediaSize ) {
tw = ( st : : maxMediaSize * tw ) / th ;
th = st : : maxMediaSize ;
}
2015-12-09 21:06:20 +03:00
_pixw = qMin ( width , _maxw ) ;
if ( bubble ) {
_pixw - = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
}
_pixh = th ;
if ( tw > _pixw ) {
_pixh = ( _pixw * _pixh / tw ) ;
2014-11-12 23:18:00 +03:00
} else {
2015-12-09 21:06:20 +03:00
_pixw = tw ;
2014-11-12 23:18:00 +03:00
}
2015-12-09 21:06:20 +03:00
if ( _pixh > width ) {
_pixw = ( _pixw * width ) / _pixh ;
_pixh = width ;
2014-11-12 23:18:00 +03:00
}
2015-12-09 21:06:20 +03:00
if ( _pixw < 1 ) _pixw = 1 ;
if ( _pixh < 1 ) _pixh = 1 ;
2015-12-13 18:21:20 +03:00
2016-04-10 18:53:01 +04:00
int minWidth = qMax ( st : : minPhotoSize , _parent - > infoWidth ( ) + 2 * ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
2015-12-19 21:09:24 +03:00
_width = qMax ( _pixw , int16 ( minWidth ) ) ;
2015-12-09 21:06:20 +03:00
_height = qMax ( _pixh , int16 ( st : : minPhotoSize ) ) ;
if ( bubble ) {
2015-12-19 21:09:24 +03:00
_width + = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
2015-12-09 21:06:20 +03:00
_height + = st : : mediaPadding . top ( ) + st : : mediaPadding . bottom ( ) ;
2015-04-30 16:53:36 +03:00
if ( ! _caption . isEmpty ( ) ) {
2016-04-07 14:03:10 +04:00
int captionw = _width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ;
2015-12-09 21:06:20 +03:00
_height + = st : : mediaCaptionSkip + _caption . countHeight ( captionw ) + st : : msgPadding . bottom ( ) ;
2015-04-30 16:53:36 +03:00
}
2015-03-19 12:18:19 +03:00
}
2014-05-30 12:53:19 +04:00
return _height ;
}
2016-04-13 21:29:32 +03:00
void HistoryPhoto : : draw ( Painter & p , const QRect & r , TextSelection selection , uint64 ms ) const {
2015-12-19 21:09:24 +03:00
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return ;
2015-12-24 22:26:28 +03:00
2016-04-10 18:53:01 +04:00
_data - > automaticLoad ( _parent ) ;
2016-04-13 21:29:32 +03:00
bool selected = ( selection = = FullSelection ) ;
2015-12-24 22:26:28 +03:00
bool loaded = _data - > loaded ( ) , displayLoading = _data - > displayLoading ( ) ;
2016-04-10 18:53:01 +04:00
bool notChild = ( _parent - > getMedia ( ) = = this ) ;
2016-04-07 14:03:10 +04:00
int skipx = 0 , skipy = 0 , width = _width , height = _height ;
2016-04-10 18:53:01 +04:00
bool bubble = _parent - > hasBubble ( ) ;
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , outbg = out & & ! isPost ;
2014-05-30 12:53:19 +04:00
2016-04-07 14:03:10 +04:00
int captionw = width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ;
2015-12-19 21:09:24 +03:00
2015-12-24 22:26:28 +03:00
if ( displayLoading ) {
2016-04-10 18:53:01 +04:00
ensureAnimation ( ) ;
2015-12-24 22:26:28 +03:00
if ( ! _animation - > radial . animating ( ) ) {
_animation - > radial . start ( _data - > progress ( ) ) ;
}
}
bool radial = isRadialAnimation ( ms ) ;
2015-12-19 21:09:24 +03:00
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 ) ;
}
2016-07-19 13:54:43 +03:00
auto inWebPage = ( _parent - > getMedia ( ) ! = this ) ;
auto roundRadius = inWebPage ? ImageRoundRadius : : Small : ImageRoundRadius : : Large ;
2015-12-19 21:09:24 +03:00
QPixmap pix ;
2015-12-24 22:26:28 +03:00
if ( loaded ) {
2016-07-19 13:54:43 +03:00
pix = _data - > full - > pixSingle ( roundRadius , _pixw , _pixh , width , height ) ;
2015-12-19 21:09:24 +03:00
} else {
2016-07-19 13:54:43 +03:00
pix = _data - > thumb - > pixBlurredSingle ( roundRadius , _pixw , _pixh , width , height ) ;
2015-12-19 21:09:24 +03:00
}
2015-12-24 22:26:28 +03:00
QRect rthumb ( rtlrect ( skipx , skipy , width , height , _width ) ) ;
p . drawPixmap ( rthumb . topLeft ( ) , pix ) ;
if ( selected ) {
2016-07-07 15:35:14 +03:00
App : : roundRect ( p , rthumb , textstyleCurrent ( ) - > selectOverlay , SelectedOverlayLargeCorners ) ;
2015-12-24 22:26:28 +03:00
}
2015-05-20 22:28:24 +03:00
2015-12-24 22:26:28 +03:00
if ( notChild & & ( radial | | ( ! loaded & & ! _data - > loading ( ) ) ) ) {
float64 radialOpacity = ( radial & & loaded & & ! _data - > uploading ( ) ) ? _animation - > radial . opacity ( ) : 1 ;
QRect inner ( rthumb . x ( ) + ( rthumb . width ( ) - st : : msgFileSize ) / 2 , rthumb . y ( ) + ( rthumb . height ( ) - st : : msgFileSize ) / 2 , st : : msgFileSize , st : : msgFileSize ) ;
p . setPen ( Qt : : NoPen ) ;
if ( selected ) {
p . setBrush ( st : : msgDateImgBgSelected ) ;
2015-12-28 00:37:48 +03:00
} else if ( isThumbAnimation ( ms ) ) {
2015-12-24 22:26:28 +03:00
float64 over = _animation - > a_thumbOver . current ( ) ;
p . setOpacity ( ( st : : msgDateImgBg - > c . alphaF ( ) * ( 1 - over ) ) + ( st : : msgDateImgBgOver - > c . alphaF ( ) * over ) ) ;
p . setBrush ( st : : black ) ;
} else {
2016-03-29 20:17:00 +03:00
bool over = ClickHandler : : showAsActive ( _data - > loading ( ) ? _cancell : _savel ) ;
2015-12-24 22:26:28 +03:00
p . setBrush ( over ? st : : msgDateImgBgOver : st : : msgDateImgBg ) ;
}
2015-12-19 21:09:24 +03:00
2015-12-24 22:26:28 +03:00
p . setOpacity ( radialOpacity * p . opacity ( ) ) ;
2015-12-19 21:09:24 +03:00
2015-12-24 22:26:28 +03:00
p . setRenderHint ( QPainter : : HighQualityAntialiasing ) ;
p . drawEllipse ( inner ) ;
p . setRenderHint ( QPainter : : HighQualityAntialiasing , false ) ;
2015-12-19 21:09:24 +03:00
2015-12-24 22:26:28 +03:00
p . setOpacity ( radial ? _animation - > radial . opacity ( ) : 1 ) ;
p . setOpacity ( radialOpacity ) ;
style : : sprite icon ;
if ( radial | | _data - > loading ( ) ) {
2016-01-04 19:28:41 +08:00
DelayedStorageImage * delayed = _data - > full - > toDelayedStorageImage ( ) ;
if ( ! delayed | | ! delayed - > location ( ) . isNull ( ) ) {
icon = ( selected ? st : : msgFileInCancelSelected : st : : msgFileInCancel ) ;
}
2015-12-24 22:26:28 +03:00
} else {
icon = ( selected ? st : : msgFileInDownloadSelected : st : : msgFileInDownload ) ;
}
2016-01-04 19:28:41 +08:00
if ( ! icon . isEmpty ( ) ) {
p . drawSpriteCenter ( inner , icon ) ;
}
2015-12-24 22:26:28 +03:00
if ( radial ) {
p . setOpacity ( 1 ) ;
QRect rinner ( inner . marginsRemoved ( QMargins ( st : : msgFileRadialLine , st : : msgFileRadialLine , st : : msgFileRadialLine , st : : msgFileRadialLine ) ) ) ;
_animation - > radial . draw ( p , rinner , st : : msgFileRadialLine , selected ? st : : msgInBgSelected : st : : msgInBg ) ;
}
2015-12-19 21:09:24 +03:00
}
// date
if ( _caption . isEmpty ( ) ) {
2016-04-10 18:53:01 +04:00
if ( notChild & & ( _data - > uploading ( ) | | App : : hoveredItem ( ) = = _parent ) ) {
2015-12-19 21:09:24 +03:00
int32 fullRight = skipx + width , fullBottom = skipy + height ;
2016-04-10 18:53:01 +04:00
_parent - > drawInfo ( p , fullRight , fullBottom , 2 * skipx + width , selected , InfoDisplayOverImage ) ;
2015-12-19 21:09:24 +03:00
}
} else {
p . setPen ( st : : black ) ;
2016-04-13 21:29:32 +03:00
_caption . draw ( p , st : : msgPadding . left ( ) , skipy + height + st : : mediaPadding . bottom ( ) + st : : mediaCaptionSkip , captionw , style : : al_left , 0 , - 1 , selection ) ;
2015-12-19 21:09:24 +03:00
}
2014-05-30 12:53:19 +04:00
}
2016-04-13 21:29:32 +03:00
HistoryTextState HistoryPhoto : : getState ( int x , int y , HistoryStateRequest request ) const {
HistoryTextState result ;
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return result ;
2016-04-07 14:03:10 +04:00
int skipx = 0 , skipy = 0 , width = _width , height = _height ;
2016-04-10 18:53:01 +04:00
bool bubble = _parent - > hasBubble ( ) ;
2015-12-09 21:06:20 +03:00
if ( bubble ) {
2015-03-19 12:18:19 +03:00
skipx = st : : mediaPadding . left ( ) ;
2015-12-09 21:06:20 +03:00
skipy = st : : mediaPadding . top ( ) ;
2015-04-30 16:53:36 +03:00
if ( ! _caption . isEmpty ( ) ) {
2016-04-07 14:03:10 +04:00
int captionw = width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ;
2015-12-09 21:06:20 +03:00
height - = _caption . countHeight ( captionw ) + st : : msgPadding . bottom ( ) ;
if ( x > = st : : msgPadding . left ( ) & & y > = height & & x < st : : msgPadding . left ( ) + captionw & & y < _height ) {
2016-04-13 21:29:32 +03:00
result = _caption . getState ( x - st : : msgPadding . left ( ) , y - height , captionw , request . forText ( ) ) ;
return result ;
2015-04-30 16:53:36 +03:00
}
2015-12-09 21:06:20 +03:00
height - = st : : mediaCaptionSkip ;
2015-04-30 16:53:36 +03:00
}
2015-12-09 21:06:20 +03:00
width - = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
height - = skipy + st : : mediaPadding . bottom ( ) ;
2015-03-19 12:18:19 +03:00
}
2015-08-31 17:27:20 +03:00
if ( x > = skipx & & y > = skipy & & x < skipx + width & & y < skipy + height ) {
2015-12-24 22:26:28 +03:00
if ( _data - > uploading ( ) ) {
2016-04-13 21:29:32 +03:00
result . link = _cancell ;
2016-01-04 19:28:41 +08:00
} else if ( _data - > loaded ( ) ) {
2016-04-13 21:29:32 +03:00
result . link = _openl ;
2016-01-04 19:28:41 +08:00
} else if ( _data - > loading ( ) ) {
DelayedStorageImage * delayed = _data - > full - > toDelayedStorageImage ( ) ;
if ( ! delayed | | ! delayed - > location ( ) . isNull ( ) ) {
2016-04-13 21:29:32 +03:00
result . link = _cancell ;
2016-01-04 19:28:41 +08:00
}
2015-12-24 22:26:28 +03:00
} else {
2016-04-13 21:29:32 +03:00
result . link = _savel ;
2015-12-24 22:26:28 +03:00
}
2016-04-10 18:53:01 +04:00
if ( _caption . isEmpty ( ) & & _parent - > getMedia ( ) = = this ) {
2015-12-09 21:06:20 +03:00
int32 fullRight = skipx + width , fullBottom = skipy + height ;
2016-04-10 18:53:01 +04:00
bool inDate = _parent - > pointInTime ( fullRight , fullBottom , x , y , InfoDisplayOverImage ) ;
2015-08-31 17:27:20 +03:00
if ( inDate ) {
2016-04-13 21:29:32 +03:00
result . cursor = HistoryInDateCursorState ;
2015-08-31 17:27:20 +03:00
}
}
2016-04-13 21:29:32 +03:00
return result ;
2014-05-30 12:53:19 +04:00
}
2016-04-13 21:29:32 +03:00
return result ;
2014-05-30 12:53:19 +04:00
}
2016-04-10 18:53:01 +04:00
void HistoryPhoto : : updateSentMedia ( const MTPMessageMedia & media ) {
2014-11-22 12:45:04 +03:00
if ( media . type ( ) = = mtpc_messageMediaPhoto ) {
2016-04-08 14:44:35 +04:00
const auto & photo ( media . c_messageMediaPhoto ( ) . vphoto ) ;
2015-12-24 22:26:28 +03:00
App : : feedPhoto ( photo , _data ) ;
2014-11-22 12:45:04 +03:00
if ( photo . type ( ) = = mtpc_photo ) {
2016-04-08 14:44:35 +04:00
const auto & sizes ( photo . c_photo ( ) . vsizes . c_vector ( ) . v ) ;
2015-12-24 22:26:28 +03:00
int32 max = 0 ;
const MTPDfileLocation * maxLocation = 0 ;
for ( int32 i = 0 , l = sizes . size ( ) ; i < l ; + + i ) {
2014-11-22 12:45:04 +03:00
char size = 0 ;
const MTPFileLocation * loc = 0 ;
2015-12-24 22:26:28 +03:00
switch ( sizes . at ( i ) . type ( ) ) {
2014-11-22 12:45:04 +03:00
case mtpc_photoSize : {
2015-12-24 22:26:28 +03:00
const string & s ( sizes . at ( i ) . c_photoSize ( ) . vtype . c_string ( ) . v ) ;
loc = & sizes . at ( i ) . c_photoSize ( ) . vlocation ;
2014-11-22 12:45:04 +03:00
if ( s . size ( ) ) size = s [ 0 ] ;
} break ;
case mtpc_photoCachedSize : {
2015-12-24 22:26:28 +03:00
const string & s ( sizes . at ( i ) . c_photoCachedSize ( ) . vtype . c_string ( ) . v ) ;
loc = & sizes . at ( i ) . c_photoCachedSize ( ) . vlocation ;
2014-11-22 12:45:04 +03:00
if ( s . size ( ) ) size = s [ 0 ] ;
} break ;
}
if ( ! loc | | loc - > type ( ) ! = mtpc_fileLocation ) continue ;
if ( size = = ' s ' ) {
2015-12-09 21:06:20 +03:00
Local : : writeImage ( storageKey ( loc - > c_fileLocation ( ) ) , _data - > thumb ) ;
2014-11-22 12:45:04 +03:00
} else if ( size = = ' m ' ) {
2015-12-09 21:06:20 +03:00
Local : : writeImage ( storageKey ( loc - > c_fileLocation ( ) ) , _data - > medium ) ;
2015-12-24 22:26:28 +03:00
} else if ( size = = ' x ' & & max < 1 ) {
max = 1 ;
maxLocation = & loc - > c_fileLocation ( ) ;
} else if ( size = = ' y ' & & max < 2 ) {
max = 2 ;
maxLocation = & loc - > c_fileLocation ( ) ;
//} else if (size == 'w' && max < 3) {
// max = 3;
// maxLocation = &loc->c_fileLocation();
2014-11-22 12:45:04 +03:00
}
}
2015-12-24 22:26:28 +03:00
if ( maxLocation ) {
Local : : writeImage ( storageKey ( * maxLocation ) , _data - > full ) ;
}
2014-11-22 12:45:04 +03:00
}
}
}
2016-04-10 18:53:01 +04:00
bool HistoryPhoto : : needReSetInlineResultMedia ( const MTPMessageMedia & media ) {
if ( media . type ( ) = = mtpc_messageMediaPhoto ) {
if ( PhotoData * existing = App : : feedPhoto ( media . c_messageMediaPhoto ( ) . vphoto ) ) {
if ( existing = = _data ) {
return false ;
} else {
// collect data
}
}
}
return false ;
2015-12-25 16:09:14 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryPhoto : : attachToParent ( ) {
App : : regPhotoItem ( _data , _parent ) ;
}
void HistoryPhoto : : detachFromParent ( ) {
App : : unregPhotoItem ( _data , _parent ) ;
2015-12-25 16:09:14 +03:00
}
2016-06-22 16:39:54 +03:00
QString HistoryPhoto : : notificationText ( ) const {
return captionedNotificationText ( lang ( lng_in_dlg_photo ) , _caption ) ;
}
2016-04-14 14:00:23 +03:00
QString HistoryPhoto : : inDialogsText ( ) const {
2016-06-22 16:39:54 +03:00
return captionedInDialogsText ( lang ( lng_in_dlg_photo ) , _caption ) ;
2015-12-19 21:09:24 +03:00
}
2014-05-30 12:53:19 +04:00
2016-05-06 20:33:48 +03:00
TextWithEntities HistoryPhoto : : selectedText ( TextSelection selection ) const {
2016-04-14 14:00:23 +03:00
return captionedSelectedText ( lang ( lng_in_dlg_photo ) , _caption , selection ) ;
2014-05-30 12:53:19 +04:00
}
2015-03-19 12:18:19 +03:00
ImagePtr HistoryPhoto : : replyPreview ( ) {
2015-12-09 21:06:20 +03:00
return _data - > makeReplyPreview ( ) ;
2015-03-19 12:18:19 +03:00
}
2016-04-10 18:53:01 +04:00
HistoryVideo : : HistoryVideo ( HistoryItem * parent , DocumentData * document , const QString & caption ) : HistoryFileMedia ( parent )
2016-02-12 21:18:32 +03:00
, _data ( document )
2015-12-24 23:29:33 +03:00
, _thumbw ( 1 )
, _caption ( st : : minPhotoSize - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) {
2015-12-13 18:21:20 +03:00
if ( ! caption . isEmpty ( ) ) {
2016-04-10 18:53:01 +04:00
_caption . setText ( st : : msgFont , caption + _parent - > skipBlock ( ) , itemTextNoMonoOptions ( _parent ) ) ;
2015-12-13 18:21:20 +03:00
}
2015-12-13 01:29:33 +03:00
2016-03-29 20:17:00 +03:00
setDocumentLinks ( _data ) ;
2015-12-13 01:29:33 +03:00
2015-12-13 18:21:20 +03:00
setStatusSize ( FileStatusSizeReady ) ;
_data - > thumb - > load ( ) ;
}
2016-04-10 18:53:01 +04:00
HistoryVideo : : HistoryVideo ( HistoryItem * parent , const HistoryVideo & other ) : HistoryFileMedia ( parent )
2015-12-17 20:31:28 +03:00
, _data ( other . _data )
2015-12-24 23:29:33 +03:00
, _thumbw ( other . _thumbw )
, _caption ( other . _caption ) {
2016-03-29 20:17:00 +03:00
setDocumentLinks ( _data ) ;
2015-12-17 20:31:28 +03:00
setStatusSize ( other . _statusSize ) ;
}
2016-04-10 18:53:01 +04:00
void HistoryVideo : : initDimensions ( ) {
bool bubble = _parent - > hasBubble ( ) ;
2015-12-13 18:21:20 +03:00
if ( _caption . hasSkipBlock ( ) ) {
2016-04-10 18:53:01 +04:00
_caption . setSkipBlock ( _parent - > skipBlockWidth ( ) , _parent - > skipBlockHeight ( ) ) ;
2015-12-13 01:29:33 +03:00
}
2015-12-13 18:21:20 +03:00
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 ( ) ;
}
2015-12-19 21:09:24 +03:00
_thumbw = qMax ( tw , 1 ) ;
2016-04-10 18:53:01 +04:00
int32 minWidth = qMax ( st : : minPhotoSize , _parent - > infoWidth ( ) + 2 * ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
2016-02-12 21:18:32 +03:00
minWidth = qMax ( minWidth , documentMaxStatusWidth ( _data ) + 2 * int32 ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
_maxw = qMax ( _thumbw , int32 ( minWidth ) ) ;
2015-12-13 18:21:20 +03:00
_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 ( ) ;
2015-12-13 01:29:33 +03:00
}
}
2015-12-13 18:21:20 +03:00
}
2015-12-13 01:29:33 +03:00
2016-04-10 18:53:01 +04:00
int HistoryVideo : : resizeGetHeight ( int width ) {
bool bubble = _parent - > hasBubble ( ) ;
2015-12-13 18:21:20 +03:00
2016-04-07 14:03:10 +04:00
int tw = convertScale ( _data - > thumb - > width ( ) ) , th = convertScale ( _data - > thumb - > height ( ) ) ;
2015-12-13 18:21:20 +03:00
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 ;
}
2016-03-14 08:31:33 +03:00
_thumbw = qMax ( tw , 1 ) ;
2016-04-10 18:53:01 +04:00
int minWidth = qMax ( st : : minPhotoSize , _parent - > infoWidth ( ) + 2 * ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
2016-04-07 14:03:10 +04:00
minWidth = qMax ( minWidth , documentMaxStatusWidth ( _data ) + 2 * int ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
_width = qMax ( _thumbw , int ( minWidth ) ) ;
_height = qMax ( th , int ( st : : minPhotoSize ) ) ;
2015-12-13 18:21:20 +03:00
if ( bubble ) {
2015-12-19 21:09:24 +03:00
_width + = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
_height + = st : : mediaPadding . top ( ) + st : : mediaPadding . bottom ( ) ;
2015-12-13 18:21:20 +03:00
if ( ! _caption . isEmpty ( ) ) {
2016-04-07 14:03:10 +04:00
int captionw = _width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ;
2015-12-19 21:09:24 +03:00
_height + = st : : mediaCaptionSkip + _caption . countHeight ( captionw ) + st : : msgPadding . bottom ( ) ;
2015-12-13 18:21:20 +03:00
}
}
2015-12-19 21:09:24 +03:00
return _height ;
2015-12-13 18:21:20 +03:00
}
2016-04-13 21:29:32 +03:00
void HistoryVideo : : draw ( Painter & p , const QRect & r , TextSelection selection , uint64 ms ) const {
2015-12-19 21:09:24 +03:00
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return ;
2015-12-24 22:26:28 +03:00
2016-04-10 18:53:01 +04:00
_data - > automaticLoad ( _parent ) ;
2015-12-24 22:26:28 +03:00
bool loaded = _data - > loaded ( ) , displayLoading = _data - > displayLoading ( ) ;
2016-04-13 21:29:32 +03:00
bool selected = ( selection = = FullSelection ) ;
2015-12-24 22:26:28 +03:00
2016-04-07 14:03:10 +04:00
int skipx = 0 , skipy = 0 , width = _width , height = _height ;
2016-04-10 18:53:01 +04:00
bool bubble = _parent - > hasBubble ( ) ;
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , outbg = out & & ! isPost ;
2015-12-19 21:09:24 +03:00
2016-04-07 14:03:10 +04:00
int captionw = width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ;
2015-12-13 18:21:20 +03:00
2015-12-24 22:26:28 +03:00
if ( displayLoading ) {
2016-04-10 18:53:01 +04:00
ensureAnimation ( ) ;
2015-12-13 18:21:20 +03:00
if ( ! _animation - > radial . animating ( ) ) {
_animation - > radial . start ( _data - > progress ( ) ) ;
}
}
2016-04-10 18:53:01 +04:00
updateStatusText ( ) ;
2015-12-13 18:21:20 +03:00
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 {
2015-12-19 21:09:24 +03:00
App : : roundShadow ( p , 0 , 0 , width , height , selected ? st : : msgInShadowSelected : st : : msgInShadow , selected ? InSelectedShadowCorners : InShadowCorners ) ;
2015-12-13 18:21:20 +03:00
}
2015-12-19 21:09:24 +03:00
QRect rthumb ( rtlrect ( skipx , skipy , width , height , _width ) ) ;
2016-07-07 15:35:14 +03:00
p . drawPixmap ( rthumb . topLeft ( ) , _data - > thumb - > pixBlurredSingle ( ImageRoundRadius : : Large , _thumbw , 0 , width , height ) ) ;
2015-12-13 18:21:20 +03:00
if ( selected ) {
2016-07-07 15:35:14 +03:00
App : : roundRect ( p , rthumb , textstyleCurrent ( ) - > selectOverlay , SelectedOverlayLargeCorners ) ;
2015-12-13 18:21:20 +03:00
}
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 ) ;
2015-12-28 00:37:48 +03:00
} else if ( isThumbAnimation ( ms ) ) {
2015-12-13 18:21:20 +03:00
float64 over = _animation - > a_thumbOver . current ( ) ;
p . setOpacity ( ( st : : msgDateImgBg - > c . alphaF ( ) * ( 1 - over ) ) + ( st : : msgDateImgBgOver - > c . alphaF ( ) * over ) ) ;
p . setBrush ( st : : black ) ;
} else {
2016-03-29 20:17:00 +03:00
bool over = ClickHandler : : showAsActive ( _data - > loading ( ) ? _cancell : _savel ) ;
2015-12-13 18:21:20 +03:00
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 ;
2015-12-24 22:26:28 +03:00
if ( loaded ) {
2015-12-13 18:21:20 +03:00
icon = ( selected ? st : : msgFileInPlaySelected : st : : msgFileInPlay ) ;
2015-12-24 22:26:28 +03:00
} else if ( radial | | _data - > loading ( ) ) {
2015-12-13 18:21:20 +03:00
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 ) ) ) ;
2015-12-22 15:49:42 +03:00
_animation - > radial . draw ( p , rinner , st : : msgFileRadialLine , selected ? st : : msgInBgSelected : st : : msgInBg ) ;
2015-12-13 18:21:20 +03:00
}
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 ( ) ;
2015-12-19 21:09:24 +03:00
App : : roundRect ( p , rtlrect ( statusX - st : : msgDateImgPadding . x ( ) , statusY - st : : msgDateImgPadding . y ( ) , statusW , statusH , _width ) , selected ? st : : msgDateImgBgSelected : st : : msgDateImgBg , selected ? DateSelectedCorners : DateCorners ) ;
2015-12-13 18:21:20 +03:00
p . setFont ( st : : normalFont ) ;
p . setPen ( st : : white ) ;
2015-12-19 21:09:24 +03:00
p . drawTextLeft ( statusX , statusY , _width , _statusText , statusW - 2 * st : : msgDateImgPadding . x ( ) ) ;
2015-12-13 18:21:20 +03:00
// date
if ( _caption . isEmpty ( ) ) {
2016-04-10 18:53:01 +04:00
if ( _parent - > getMedia ( ) = = this ) {
2015-12-19 00:36:16 +03:00
int32 fullRight = skipx + width , fullBottom = skipy + height ;
2016-04-10 18:53:01 +04:00
_parent - > drawInfo ( p , fullRight , fullBottom , 2 * skipx + width , selected , InfoDisplayOverImage ) ;
2015-12-19 00:36:16 +03:00
}
2015-12-13 18:21:20 +03:00
} else {
p . setPen ( st : : black ) ;
2016-04-13 21:29:32 +03:00
_caption . draw ( p , st : : msgPadding . left ( ) , skipy + height + st : : mediaPadding . bottom ( ) + st : : mediaCaptionSkip , captionw , style : : al_left , 0 , - 1 , selection ) ;
2015-12-13 18:21:20 +03:00
}
}
2016-04-13 21:29:32 +03:00
HistoryTextState HistoryVideo : : getState ( int x , int y , HistoryStateRequest request ) const {
HistoryTextState result ;
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return result ;
2015-12-24 22:26:28 +03:00
bool loaded = _data - > loaded ( ) ;
2015-12-19 21:09:24 +03:00
int32 skipx = 0 , skipy = 0 , width = _width , height = _height ;
2016-04-10 18:53:01 +04:00
bool bubble = _parent - > hasBubble ( ) ;
2015-12-13 18:21:20 +03:00
if ( bubble ) {
2015-12-19 21:09:24 +03:00
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 ) {
2016-04-14 14:00:23 +03:00
result = _caption . getState ( x - st : : msgPadding . left ( ) , y - height , captionw , request . forText ( ) ) ;
2015-12-19 21:09:24 +03:00
}
height - = st : : mediaCaptionSkip ;
}
2015-12-13 18:21:20 +03:00
width - = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
2015-12-19 21:09:24 +03:00
height - = skipy + st : : mediaPadding . bottom ( ) ;
2015-12-13 18:21:20 +03:00
}
2015-12-19 21:09:24 +03:00
if ( x > = skipx & & y > = skipy & & x < skipx + width & & y < skipy + height ) {
2016-04-13 21:29:32 +03:00
result . link = loaded ? _openl : ( _data - > loading ( ) ? _cancell : _savel ) ;
2016-04-10 18:53:01 +04:00
if ( _caption . isEmpty ( ) & & _parent - > getMedia ( ) = = this ) {
2015-12-19 21:09:24 +03:00
int32 fullRight = skipx + width , fullBottom = skipy + height ;
2016-04-10 18:53:01 +04:00
bool inDate = _parent - > pointInTime ( fullRight , fullBottom , x , y , InfoDisplayOverImage ) ;
2015-12-19 21:09:24 +03:00
if ( inDate ) {
2016-04-13 21:29:32 +03:00
result . cursor = HistoryInDateCursorState ;
2015-12-19 21:09:24 +03:00
}
2015-12-13 18:21:20 +03:00
}
2016-04-13 21:29:32 +03:00
return result ;
2015-12-13 18:21:20 +03:00
}
2016-04-13 21:29:32 +03:00
return result ;
2015-12-19 21:09:24 +03:00
}
void HistoryVideo : : setStatusSize ( int32 newSize ) const {
2016-02-12 21:18:32 +03:00
HistoryFileMedia : : setStatusSize ( newSize , _data - > size , _data - > duration ( ) , 0 ) ;
2015-12-19 21:09:24 +03:00
}
2016-06-22 16:39:54 +03:00
QString HistoryVideo : : notificationText ( ) const {
return captionedNotificationText ( lang ( lng_in_dlg_video ) , _caption ) ;
}
2016-04-14 14:00:23 +03:00
QString HistoryVideo : : inDialogsText ( ) const {
2016-06-22 16:39:54 +03:00
return captionedInDialogsText ( lang ( lng_in_dlg_video ) , _caption ) ;
2015-12-19 21:09:24 +03:00
}
2016-05-06 20:33:48 +03:00
TextWithEntities HistoryVideo : : selectedText ( TextSelection selection ) const {
2016-04-14 14:00:23 +03:00
return captionedSelectedText ( lang ( lng_in_dlg_video ) , _caption , selection ) ;
2015-12-19 21:09:24 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryVideo : : updateStatusText ( ) const {
2015-12-19 21:09:24 +03:00
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 ;
2015-12-24 22:26:28 +03:00
} else if ( _data - > loading ( ) ) {
2016-01-02 08:33:26 +08:00
statusSize = _data - > loadOffset ( ) ;
2016-03-16 14:54:37 +03:00
} else if ( _data - > loaded ( ) ) {
2015-12-19 21:09:24 +03:00
statusSize = FileStatusSizeLoaded ;
} else {
statusSize = FileStatusSizeReady ;
}
if ( statusSize ! = _statusSize ) {
setStatusSize ( statusSize ) ;
}
}
2016-04-10 18:53:01 +04:00
void HistoryVideo : : attachToParent ( ) {
App : : regDocumentItem ( _data , _parent ) ;
}
void HistoryVideo : : detachFromParent ( ) {
App : : unregDocumentItem ( _data , _parent ) ;
2015-12-19 21:09:24 +03:00
}
2016-04-10 18:53:01 +04:00
bool HistoryVideo : : needReSetInlineResultMedia ( const MTPMessageMedia & media ) {
return needReSetInlineResultDocument ( media , _data ) ;
2015-12-13 18:21:20 +03:00
}
ImagePtr HistoryVideo : : replyPreview ( ) {
if ( _data - > replyPreview - > isNull ( ) & & ! _data - > thumb - > isNull ( ) ) {
if ( _data - > thumb - > loaded ( ) ) {
2016-02-15 14:23:45 +03:00
int w = convertScale ( _data - > thumb - > width ( ) ) , h = convertScale ( _data - > thumb - > height ( ) ) ;
2015-12-13 18:21:20 +03:00
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 ( ) ;
}
2015-12-13 01:29:33 +03:00
}
2015-12-13 18:21:20 +03:00
return _data - > replyPreview ;
2015-12-13 01:29:33 +03:00
}
2016-02-12 19:35:06 +03:00
HistoryDocumentVoicePlayback : : HistoryDocumentVoicePlayback ( const HistoryDocument * that )
: _position ( 0 )
, a_progress ( 0. , 0. )
, _a_progress ( animation ( const_cast < HistoryDocument * > ( that ) , & HistoryDocument : : step_voiceProgress ) ) {
2015-12-17 20:31:28 +03:00
}
2016-02-12 19:35:06 +03:00
void HistoryDocumentVoice : : ensurePlayback ( const HistoryDocument * that ) const {
if ( ! _playback ) {
_playback = new HistoryDocumentVoicePlayback ( that ) ;
}
2015-12-13 01:29:33 +03:00
}
2016-02-12 19:35:06 +03:00
void HistoryDocumentVoice : : checkPlaybackFinished ( ) const {
if ( _playback & & ! _playback - > _a_progress . animating ( ) ) {
delete _playback ;
2016-03-25 14:29:45 +03:00
_playback = nullptr ;
2014-09-04 11:33:44 +04:00
}
2016-02-12 19:35:06 +03:00
}
2015-06-02 14:22:00 +03:00
2016-04-10 18:53:01 +04:00
HistoryDocument : : HistoryDocument ( HistoryItem * parent , DocumentData * document , const QString & caption ) : HistoryFileMedia ( parent )
2016-02-12 19:35:06 +03:00
, _data ( document ) {
2016-03-28 15:51:22 +03:00
createComponents ( ! caption . isEmpty ( ) ) ;
2016-04-08 13:20:10 +04:00
if ( auto named = Get < HistoryDocumentNamed > ( ) ) {
2016-02-12 19:35:06 +03:00
named - > _name = documentName ( _data ) ;
named - > _namew = st : : semiboldFont - > width ( named - > _name ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-29 20:17:00 +03:00
setDocumentLinks ( _data ) ;
2014-05-30 12:53:19 +04:00
2016-02-12 19:35:06 +03:00
setStatusSize ( FileStatusSizeReady ) ;
2015-12-13 01:29:33 +03:00
2016-04-08 13:20:10 +04:00
if ( auto captioned = Get < HistoryDocumentCaptioned > ( ) ) {
2016-04-10 18:53:01 +04:00
captioned - > _caption . setText ( st : : msgFont , caption + _parent - > skipBlock ( ) , itemTextNoMonoOptions ( _parent ) ) ;
2015-12-13 01:29:33 +03:00
}
2016-02-12 19:35:06 +03:00
}
2015-12-13 01:29:33 +03:00
2016-04-10 18:53:01 +04:00
HistoryDocument : : HistoryDocument ( HistoryItem * parent , const HistoryDocument & other ) : HistoryFileMedia ( parent )
2016-03-25 14:29:45 +03:00
, Composer ( )
2016-02-12 19:35:06 +03:00
, _data ( other . _data ) {
2016-04-08 13:20:10 +04:00
auto captioned = other . Get < HistoryDocumentCaptioned > ( ) ;
2016-03-28 15:51:22 +03:00
createComponents ( captioned ! = 0 ) ;
2016-04-08 13:20:10 +04:00
if ( auto named = Get < HistoryDocumentNamed > ( ) ) {
if ( auto othernamed = other . Get < HistoryDocumentNamed > ( ) ) {
2016-03-25 14:29:45 +03:00
named - > _name = othernamed - > _name ;
named - > _namew = othernamed - > _namew ;
2016-02-12 19:35:06 +03:00
} else {
named - > _name = documentName ( _data ) ;
named - > _namew = st : : semiboldFont - > width ( named - > _name ) ;
2015-04-30 16:53:36 +03:00
}
}
2014-05-30 12:53:19 +04:00
2016-03-29 20:17:00 +03:00
setDocumentLinks ( _data ) ;
2015-12-19 21:09:24 +03:00
2016-02-12 19:35:06 +03:00
setStatusSize ( other . _statusSize ) ;
2015-12-19 21:09:24 +03:00
2016-02-12 19:35:06 +03:00
if ( captioned ) {
Get < HistoryDocumentCaptioned > ( ) - > _caption = captioned - > _caption ;
2015-12-19 21:09:24 +03:00
}
2014-05-30 12:53:19 +04:00
}
2016-03-28 15:51:22 +03:00
void HistoryDocument : : createComponents ( bool caption ) {
2016-02-17 19:37:21 +03:00
uint64 mask = 0 ;
2016-02-12 19:35:06 +03:00
if ( _data - > voice ( ) ) {
2016-02-17 19:37:21 +03:00
mask | = HistoryDocumentVoice : : Bit ( ) ;
2016-02-12 19:35:06 +03:00
} else {
2016-02-17 19:37:21 +03:00
mask | = HistoryDocumentNamed : : Bit ( ) ;
2016-02-12 19:35:06 +03:00
if ( ! _data - > song ( ) & & ! _data - > thumb - > isNull ( ) & & _data - > thumb - > width ( ) & & _data - > thumb - > height ( ) ) {
mask | = HistoryDocumentThumbed : : Bit ( ) ;
2015-12-19 21:09:24 +03:00
}
2014-05-30 12:53:19 +04:00
}
2016-02-21 15:30:16 +03:00
if ( caption ) {
mask | = HistoryDocumentCaptioned : : Bit ( ) ;
}
2016-03-25 14:29:45 +03:00
UpdateComponents ( mask ) ;
2016-04-08 13:20:10 +04:00
if ( auto thumbed = Get < HistoryDocumentThumbed > ( ) ) {
2016-03-29 20:17:00 +03:00
thumbed - > _linksavel . reset ( new DocumentSaveClickHandler ( _data ) ) ;
thumbed - > _linkcancell . reset ( new DocumentCancelClickHandler ( _data ) ) ;
2015-12-28 13:28:00 +03:00
}
2014-05-30 12:53:19 +04:00
}
2016-04-10 18:53:01 +04:00
void HistoryDocument : : initDimensions ( ) {
2016-04-08 13:20:10 +04:00
auto captioned = Get < HistoryDocumentCaptioned > ( ) ;
2016-02-12 19:35:06 +03:00
if ( captioned & & captioned - > _caption . hasSkipBlock ( ) ) {
2016-04-10 18:53:01 +04:00
captioned - > _caption . setSkipBlock ( _parent - > skipBlockWidth ( ) , _parent - > skipBlockHeight ( ) ) ;
2015-12-28 13:28:00 +03:00
}
2016-04-08 13:20:10 +04:00
auto thumbed = Get < HistoryDocumentThumbed > ( ) ;
2016-02-12 19:35:06 +03:00
if ( thumbed ) {
2016-01-09 15:11:23 +08:00
_data - > thumb - > load ( ) ;
2016-02-15 14:23:45 +03:00
int32 tw = convertScale ( _data - > thumb - > width ( ) ) , th = convertScale ( _data - > thumb - > height ( ) ) ;
2016-01-09 15:11:23 +08:00
if ( tw > th ) {
2016-02-12 19:35:06 +03:00
thumbed - > _thumbw = ( tw * st : : msgFileThumbSize ) / th ;
2016-01-09 15:11:23 +08:00
} else {
2016-02-12 19:35:06 +03:00
thumbed - > _thumbw = st : : msgFileThumbSize ;
2016-01-09 15:11:23 +08:00
}
}
2015-12-08 22:07:50 +03:00
_maxw = st : : msgFileMinWidth ;
int32 tleft = 0 , tright = 0 ;
2016-02-12 19:35:06 +03:00
if ( thumbed ) {
2015-12-08 22:07:50 +03:00
tleft = st : : msgFileThumbPadding . left ( ) + st : : msgFileThumbSize + st : : msgFileThumbPadding . right ( ) ;
tright = st : : msgFileThumbPadding . left ( ) ;
_maxw = qMax ( _maxw , tleft + documentMaxStatusWidth ( _data ) + tright ) ;
2014-10-10 16:46:20 +04:00
} else {
2015-12-08 22:07:50 +03:00
tleft = st : : msgFilePadding . left ( ) + st : : msgFileSize + st : : msgFilePadding . right ( ) ;
tright = st : : msgFileThumbPadding . left ( ) ;
2016-02-12 19:35:06 +03:00
int32 unread = _data - > voice ( ) ? ( st : : mediaUnreadSkip + st : : mediaUnreadSize ) : 0 ;
2016-04-10 18:53:01 +04:00
_maxw = qMax ( _maxw , tleft + documentMaxStatusWidth ( _data ) + unread + _parent - > skipBlockWidth ( ) + st : : msgPadding . right ( ) ) ;
2014-05-30 12:53:19 +04:00
}
2015-12-08 22:07:50 +03:00
2016-04-08 13:20:10 +04:00
if ( auto named = Get < HistoryDocumentNamed > ( ) ) {
2016-02-12 19:35:06 +03:00
_maxw = qMax ( tleft + named - > _namew + tright , _maxw ) ;
_maxw = qMin ( _maxw , int ( st : : msgMaxWidth ) ) ;
}
2015-12-11 21:11:38 +03:00
2016-02-12 19:35:06 +03:00
if ( thumbed ) {
2015-12-19 21:09:24 +03:00
_minh = st : : msgFileThumbPadding . top ( ) + st : : msgFileThumbSize + st : : msgFileThumbPadding . bottom ( ) ;
2016-04-10 18:53:01 +04:00
if ( ! captioned & & _parent - > Has < HistoryMessageSigned > ( ) ) {
2016-03-24 18:31:11 +03:00
_minh + = st : : msgDateFont - > height - st : : msgDateDelta . y ( ) ;
}
2015-12-11 21:11:38 +03:00
} else {
2015-12-19 21:09:24 +03:00
_minh = st : : msgFilePadding . top ( ) + st : : msgFileSize + st : : msgFilePadding . bottom ( ) ;
2015-12-11 21:11:38 +03:00
}
2015-12-28 13:28:00 +03:00
2016-02-12 19:35:06 +03:00
if ( captioned ) {
_minh + = captioned - > _caption . countHeight ( _maxw - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) + st : : msgPadding . bottom ( ) ;
2015-12-28 13:28:00 +03:00
} else {
2016-02-12 19:35:06 +03:00
_height = _minh ;
2015-12-28 13:28:00 +03:00
}
}
2016-04-10 18:53:01 +04:00
int HistoryDocument : : resizeGetHeight ( int width ) {
2016-04-08 13:20:10 +04:00
auto captioned = Get < HistoryDocumentCaptioned > ( ) ;
2016-02-12 19:35:06 +03:00
if ( ! captioned ) {
2016-04-10 18:53:01 +04:00
return HistoryFileMedia : : resizeGetHeight ( width ) ;
2015-12-28 13:28:00 +03:00
}
_width = qMin ( width , _maxw ) ;
2016-02-12 19:35:06 +03:00
if ( Get < HistoryDocumentThumbed > ( ) ) {
2015-12-28 13:28:00 +03:00
_height = st : : msgFileThumbPadding . top ( ) + st : : msgFileThumbSize + st : : msgFileThumbPadding . bottom ( ) ;
} else {
_height = st : : msgFilePadding . top ( ) + st : : msgFileSize + st : : msgFilePadding . bottom ( ) ;
}
2016-02-12 19:35:06 +03:00
_height + = captioned - > _caption . countHeight ( _width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) + st : : msgPadding . bottom ( ) ;
2015-12-28 13:28:00 +03:00
return _height ;
2014-05-30 12:53:19 +04:00
}
2016-04-13 21:29:32 +03:00
void HistoryDocument : : draw ( Painter & p , const QRect & r , TextSelection selection , uint64 ms ) const {
2015-12-19 21:09:24 +03:00
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return ;
2014-05-30 12:53:19 +04:00
2016-04-10 18:53:01 +04:00
_data - > automaticLoad ( _parent ) ;
2015-12-24 22:26:28 +03:00
bool loaded = _data - > loaded ( ) , displayLoading = _data - > displayLoading ( ) ;
2016-04-13 21:29:32 +03:00
bool selected = ( selection = = FullSelection ) ;
2015-12-24 22:26:28 +03:00
2016-04-07 14:03:10 +04:00
int captionw = _width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ;
2015-12-28 13:28:00 +03:00
2016-04-10 18:53:01 +04:00
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , outbg = out & & ! isPost ;
2015-03-19 12:18:19 +03:00
2015-12-24 22:26:28 +03:00
if ( displayLoading ) {
2016-04-10 18:53:01 +04:00
ensureAnimation ( ) ;
2015-12-11 21:11:38 +03:00
if ( ! _animation - > radial . animating ( ) ) {
_animation - > radial . start ( _data - > progress ( ) ) ;
}
}
2016-04-10 18:53:01 +04:00
bool showPause = updateStatusText ( ) ;
2015-12-13 01:29:33 +03:00
bool radial = isRadialAnimation ( ms ) ;
2014-05-30 12:53:19 +04:00
2016-04-07 14:03:10 +04:00
int nameleft = 0 , nametop = 0 , nameright = 0 , statustop = 0 , linktop = 0 , bottom = 0 ;
2016-04-08 13:20:10 +04:00
if ( auto thumbed = Get < HistoryDocumentThumbed > ( ) ) {
2015-12-10 16:37:45 +03:00
nameleft = st : : msgFileThumbPadding . left ( ) + st : : msgFileThumbSize + st : : msgFileThumbPadding . right ( ) ;
nametop = st : : msgFileThumbNameTop ;
nameright = st : : msgFileThumbPadding . left ( ) ;
statustop = st : : msgFileThumbStatusTop ;
linktop = st : : msgFileThumbLinkTop ;
2015-12-28 13:28:00 +03:00
bottom = st : : msgFileThumbPadding . top ( ) + st : : msgFileThumbSize + st : : msgFileThumbPadding . bottom ( ) ;
2015-12-31 23:27:21 +08:00
2015-12-19 21:09:24 +03:00
QRect rthumb ( rtlrect ( st : : msgFileThumbPadding . left ( ) , st : : msgFileThumbPadding . top ( ) , st : : msgFileThumbSize , st : : msgFileThumbSize , _width ) ) ;
2016-07-15 18:58:52 +03:00
QPixmap thumb = loaded ? _data - > thumb - > pixSingle ( ImageRoundRadius : : Large , thumbed - > _thumbw , 0 , st : : msgFileThumbSize , st : : msgFileThumbSize ) : _data - > thumb - > pixBlurredSingle ( ImageRoundRadius : : Small , thumbed - > _thumbw , 0 , st : : msgFileThumbSize , st : : msgFileThumbSize ) ;
2016-01-09 15:11:23 +08:00
p . drawPixmap ( rthumb . topLeft ( ) , thumb ) ;
2015-12-10 16:37:45 +03:00
if ( selected ) {
2016-07-15 18:58:52 +03:00
App : : roundRect ( p , rthumb , textstyleCurrent ( ) - > selectOverlay , SelectedOverlayLargeCorners ) ;
2015-12-10 16:37:45 +03:00
}
2014-05-30 12:53:19 +04:00
2015-12-24 22:26:28 +03:00
if ( radial | | ( ! loaded & & ! _data - > loading ( ) ) ) {
2015-12-25 18:21:18 +03:00
float64 radialOpacity = ( radial & & loaded & & ! _data - > uploading ( ) ) ? _animation - > radial . opacity ( ) : 1 ;
2015-12-10 16:37:45 +03:00
QRect inner ( rthumb . x ( ) + ( rthumb . width ( ) - st : : msgFileSize ) / 2 , rthumb . y ( ) + ( rthumb . height ( ) - st : : msgFileSize ) / 2 , st : : msgFileSize , st : : msgFileSize ) ;
p . setPen ( Qt : : NoPen ) ;
2015-12-11 21:11:38 +03:00
if ( selected ) {
p . setBrush ( st : : msgDateImgBgSelected ) ;
2015-12-28 00:37:48 +03:00
} else if ( isThumbAnimation ( ms ) ) {
2015-12-11 21:11:38 +03:00
float64 over = _animation - > a_thumbOver . current ( ) ;
p . setOpacity ( ( st : : msgDateImgBg - > c . alphaF ( ) * ( 1 - over ) ) + ( st : : msgDateImgBgOver - > c . alphaF ( ) * over ) ) ;
p . setBrush ( st : : black ) ;
} else {
2016-03-29 20:17:00 +03:00
bool over = ClickHandler : : showAsActive ( _data - > loading ( ) ? _cancell : _savel ) ;
2015-12-11 21:11:38 +03:00
p . setBrush ( over ? st : : msgDateImgBgOver : st : : msgDateImgBg ) ;
}
2015-12-24 22:26:28 +03:00
p . setOpacity ( radialOpacity * p . opacity ( ) ) ;
2014-05-30 12:53:19 +04:00
2015-12-11 21:11:38 +03:00
p . setRenderHint ( QPainter : : HighQualityAntialiasing ) ;
p . drawEllipse ( inner ) ;
2015-12-10 16:37:45 +03:00
p . setRenderHint ( QPainter : : HighQualityAntialiasing , false ) ;
2014-05-30 12:53:19 +04:00
2015-12-24 22:26:28 +03:00
p . setOpacity ( radialOpacity ) ;
2015-12-10 16:37:45 +03:00
style : : sprite icon ;
2015-12-24 22:26:28 +03:00
if ( radial | | _data - > loading ( ) ) {
2015-12-10 16:37:45 +03:00
icon = ( selected ? st : : msgFileInCancelSelected : st : : msgFileInCancel ) ;
} else {
icon = ( selected ? st : : msgFileInDownloadSelected : st : : msgFileInDownload ) ;
}
2015-12-24 22:26:28 +03:00
p . setOpacity ( ( radial & & loaded ) ? _animation - > radial . opacity ( ) : 1 ) ;
2015-12-10 16:37:45 +03:00
p . drawSpriteCenter ( inner , icon ) ;
2015-12-11 21:11:38 +03:00
if ( radial ) {
p . setOpacity ( 1 ) ;
QRect rinner ( inner . marginsRemoved ( QMargins ( st : : msgFileRadialLine , st : : msgFileRadialLine , st : : msgFileRadialLine , st : : msgFileRadialLine ) ) ) ;
2015-12-22 15:49:42 +03:00
_animation - > radial . draw ( p , rinner , st : : msgFileRadialLine , selected ? st : : msgInBgSelected : st : : msgInBg ) ;
2015-12-11 21:11:38 +03:00
}
2015-12-10 16:37:45 +03:00
}
2014-05-30 12:53:19 +04:00
2015-12-11 10:44:53 +03:00
if ( _data - > status ! = FileUploadFailed ) {
2016-03-29 20:17:00 +03:00
const ClickHandlerPtr & lnk ( ( _data - > loading ( ) | | _data - > status = = FileUploading ) ? thumbed - > _linkcancell : thumbed - > _linksavel ) ;
bool over = ClickHandler : : showAsActive ( lnk ) ;
2015-12-11 10:44:53 +03:00
p . setFont ( over ? st : : semiboldFont - > underline ( ) : st : : semiboldFont ) ;
p . setPen ( outbg ? ( selected ? st : : msgFileThumbLinkOutFgSelected : st : : msgFileThumbLinkOutFg ) : ( selected ? st : : msgFileThumbLinkInFgSelected : st : : msgFileThumbLinkInFg ) ) ;
2016-02-12 19:35:06 +03:00
p . drawTextLeft ( nameleft , linktop , _width , thumbed - > _link , thumbed - > _linkw ) ;
2015-12-11 10:44:53 +03:00
}
2015-12-10 16:37:45 +03:00
} else {
nameleft = st : : msgFilePadding . left ( ) + st : : msgFileSize + st : : msgFilePadding . right ( ) ;
nametop = st : : msgFileNameTop ;
nameright = st : : msgFilePadding . left ( ) ;
statustop = st : : msgFileStatusTop ;
2015-12-28 13:28:00 +03:00
bottom = st : : msgFilePadding . top ( ) + st : : msgFileSize + st : : msgFilePadding . bottom ( ) ;
2015-12-10 16:37:45 +03:00
2015-12-19 21:09:24 +03:00
QRect inner ( rtlrect ( st : : msgFilePadding . left ( ) , st : : msgFilePadding . top ( ) , st : : msgFileSize , st : : msgFileSize , _width ) ) ;
2015-12-10 16:37:45 +03:00
p . setPen ( Qt : : NoPen ) ;
2015-12-11 21:11:38 +03:00
if ( selected ) {
p . setBrush ( outbg ? st : : msgFileOutBgSelected : st : : msgFileInBgSelected ) ;
2015-12-28 00:37:48 +03:00
} else if ( isThumbAnimation ( ms ) ) {
2015-12-11 21:11:38 +03:00
float64 over = _animation - > a_thumbOver . current ( ) ;
p . setBrush ( style : : interpolate ( outbg ? st : : msgFileOutBg : st : : msgFileInBg , outbg ? st : : msgFileOutBgOver : st : : msgFileInBgOver , over ) ) ;
} else {
2016-03-29 20:17:00 +03:00
bool over = ClickHandler : : showAsActive ( _data - > loading ( ) ? _cancell : _savel ) ;
2015-12-11 21:11:38 +03:00
p . setBrush ( outbg ? ( over ? st : : msgFileOutBgOver : st : : msgFileOutBg ) : ( over ? st : : msgFileInBgOver : st : : msgFileInBg ) ) ;
}
2015-12-10 16:37:45 +03:00
2015-12-11 21:11:38 +03:00
p . setRenderHint ( QPainter : : HighQualityAntialiasing ) ;
p . drawEllipse ( inner ) ;
2015-12-10 16:37:45 +03:00
p . setRenderHint ( QPainter : : HighQualityAntialiasing , false ) ;
2015-12-11 21:11:38 +03:00
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 ) ) ;
2015-12-22 15:49:42 +03:00
_animation - > radial . draw ( p , rinner , st : : msgFileRadialLine , bg ) ;
2015-12-11 21:11:38 +03:00
}
2015-12-10 16:37:45 +03:00
style : : sprite icon ;
if ( showPause ) {
icon = outbg ? ( selected ? st : : msgFileOutPauseSelected : st : : msgFileOutPause ) : ( selected ? st : : msgFileInPauseSelected : st : : msgFileInPause ) ;
2015-12-24 22:26:28 +03:00
} else if ( radial | | _data - > loading ( ) ) {
icon = outbg ? ( selected ? st : : msgFileOutCancelSelected : st : : msgFileOutCancel ) : ( selected ? st : : msgFileInCancelSelected : st : : msgFileInCancel ) ;
} else if ( loaded ) {
2016-02-12 19:35:06 +03:00
if ( _data - > song ( ) | | _data - > voice ( ) ) {
2015-12-10 16:37:45 +03:00
icon = outbg ? ( selected ? st : : msgFileOutPlaySelected : st : : msgFileOutPlay ) : ( selected ? st : : msgFileInPlaySelected : st : : msgFileInPlay ) ;
} else if ( _data - > isImage ( ) ) {
icon = outbg ? ( selected ? st : : msgFileOutImageSelected : st : : msgFileOutImage ) : ( selected ? st : : msgFileInImageSelected : st : : msgFileInImage ) ;
} else {
icon = outbg ? ( selected ? st : : msgFileOutFileSelected : st : : msgFileOutFile ) : ( selected ? st : : msgFileInFileSelected : st : : msgFileInFile ) ;
}
} else {
icon = outbg ? ( selected ? st : : msgFileOutDownloadSelected : st : : msgFileOutDownload ) : ( selected ? st : : msgFileInDownloadSelected : st : : msgFileInDownload ) ;
}
p . drawSpriteCenter ( inner , icon ) ;
}
2015-12-19 21:09:24 +03:00
int32 namewidth = _width - nameleft - nameright ;
2015-12-10 16:37:45 +03:00
2016-04-08 13:20:10 +04:00
if ( auto voice = Get < HistoryDocumentVoice > ( ) ) {
2016-02-12 19:35:06 +03:00
const VoiceWaveform * wf = 0 ;
uchar norm_value = 0 ;
if ( _data - > voice ( ) ) {
wf = & _data - > voice ( ) - > waveform ;
if ( wf - > isEmpty ( ) ) {
wf = 0 ;
if ( loaded ) {
Local : : countVoiceWaveform ( _data ) ;
}
} else if ( wf - > at ( 0 ) < 0 ) {
wf = 0 ;
} else {
norm_value = _data - > voice ( ) - > wavemax ;
}
}
float64 prg = voice - > _playback ? voice - > _playback - > a_progress . current ( ) : 0 ;
// rescale waveform by going in waveform.size * bar_count 1D grid
2016-02-14 21:29:17 +03:00
style : : color active ( outbg ? ( selected ? st : : msgWaveformOutActiveSelected : st : : msgWaveformOutActive ) : ( selected ? st : : msgWaveformInActiveSelected : st : : msgWaveformInActive ) ) ;
style : : color inactive ( outbg ? ( selected ? st : : msgWaveformOutInactiveSelected : st : : msgWaveformOutInactive ) : ( selected ? st : : msgWaveformInInactiveSelected : st : : msgWaveformInInactive ) ) ;
2016-02-12 19:35:06 +03:00
int32 wf_size = wf ? wf - > size ( ) : WaveformSamplesCount , availw = int32 ( namewidth + st : : msgWaveformSkip ) , activew = qRound ( availw * prg ) ;
2016-04-10 18:53:01 +04:00
if ( ! outbg & & ! voice - > _playback & & _parent - > isMediaUnread ( ) ) {
2016-02-14 21:29:17 +03:00
activew = availw ;
}
2016-02-12 19:35:06 +03:00
int32 bar_count = qMin ( availw / int32 ( st : : msgWaveformBar + st : : msgWaveformSkip ) , wf_size ) ;
uchar max_value = 0 ;
int32 max_delta = st : : msgWaveformMax - st : : msgWaveformMin , bottom = st : : msgFilePadding . top ( ) + st : : msgWaveformMax ;
p . setPen ( Qt : : NoPen ) ;
2016-02-14 22:46:01 +03:00
for ( int32 i = 0 , bar_x = 0 , sum_i = 0 ; i < wf_size ; + + i ) {
2016-02-12 19:35:06 +03:00
uchar value = wf ? wf - > at ( i ) : 0 ;
if ( sum_i + bar_count > = wf_size ) { // draw bar
sum_i = sum_i + bar_count - wf_size ;
if ( sum_i < ( bar_count + 1 ) / 2 ) {
if ( max_value < value ) max_value = value ;
}
int32 bar_value = ( ( max_value * max_delta ) + ( ( norm_value + 1 ) / 2 ) ) / ( norm_value + 1 ) ;
if ( bar_x > = activew ) {
p . fillRect ( nameleft + bar_x , bottom - bar_value , st : : msgWaveformBar , st : : msgWaveformMin + bar_value , inactive ) ;
} else if ( bar_x + st : : msgWaveformBar < = activew ) {
p . fillRect ( nameleft + bar_x , bottom - bar_value , st : : msgWaveformBar , st : : msgWaveformMin + bar_value , active ) ;
} else {
p . fillRect ( nameleft + bar_x , bottom - bar_value , activew - bar_x , st : : msgWaveformMin + bar_value , active ) ;
p . fillRect ( nameleft + activew , bottom - bar_value , st : : msgWaveformBar - ( activew - bar_x ) , st : : msgWaveformMin + bar_value , inactive ) ;
}
bar_x + = st : : msgWaveformBar + st : : msgWaveformSkip ;
if ( sum_i < ( bar_count + 1 ) / 2 ) {
max_value = 0 ;
} else {
max_value = value ;
}
} else {
if ( max_value < value ) max_value = value ;
sum_i + = bar_count ;
}
}
2016-04-08 13:20:10 +04:00
} else if ( auto named = Get < HistoryDocumentNamed > ( ) ) {
2016-02-12 19:35:06 +03:00
p . setFont ( st : : semiboldFont ) ;
p . setPen ( st : : black ) ;
if ( namewidth < named - > _namew ) {
p . drawTextLeft ( nameleft , nametop , _width , st : : semiboldFont - > elided ( named - > _name , namewidth ) ) ;
} else {
p . drawTextLeft ( nameleft , nametop , _width , named - > _name , named - > _namew ) ;
}
2015-12-10 16:37:45 +03:00
}
2015-12-11 21:11:38 +03:00
style : : color status ( outbg ? ( selected ? st : : mediaOutFgSelected : st : : mediaOutFg ) : ( selected ? st : : mediaInFgSelected : st : : mediaInFg ) ) ;
2015-12-10 16:37:45 +03:00
p . setFont ( st : : normalFont ) ;
p . setPen ( status ) ;
2015-12-19 21:09:24 +03:00
p . drawTextLeft ( nameleft , statustop , _width , _statusText ) ;
2015-12-28 13:28:00 +03:00
2016-04-10 18:53:01 +04:00
if ( _parent - > isMediaUnread ( ) ) {
2016-02-12 19:35:06 +03:00
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 ) ;
}
}
2016-04-08 13:20:10 +04:00
if ( auto captioned = Get < HistoryDocumentCaptioned > ( ) ) {
2015-12-28 13:28:00 +03:00
p . setPen ( st : : black ) ;
2016-04-13 21:29:32 +03:00
captioned - > _caption . draw ( p , st : : msgPadding . left ( ) , bottom , captionw , style : : al_left , 0 , - 1 , selection ) ;
2015-12-28 13:28:00 +03:00
}
2014-05-30 12:53:19 +04:00
}
2016-04-13 21:29:32 +03:00
HistoryTextState HistoryDocument : : getState ( int x , int y , HistoryStateRequest request ) const {
HistoryTextState result ;
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return result ;
2015-12-19 21:09:24 +03:00
2016-04-10 18:53:01 +04:00
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , outbg = out & & ! isPost ;
2015-12-23 22:23:14 +03:00
bool loaded = _data - > loaded ( ) ;
2015-07-03 13:48:28 +03:00
2016-04-10 18:53:01 +04:00
bool showPause = updateStatusText ( ) ;
2015-07-03 13:48:28 +03:00
2015-12-28 13:28:00 +03:00
int32 nameleft = 0 , nametop = 0 , nameright = 0 , statustop = 0 , linktop = 0 , bottom = 0 ;
2016-04-08 13:20:10 +04:00
if ( auto thumbed = Get < HistoryDocumentThumbed > ( ) ) {
2015-12-19 21:09:24 +03:00
nameleft = st : : msgFileThumbPadding . left ( ) + st : : msgFileThumbSize + st : : msgFileThumbPadding . right ( ) ;
linktop = st : : msgFileThumbLinkTop ;
2015-12-28 13:28:00 +03:00
bottom = st : : msgFileThumbPadding . top ( ) + st : : msgFileThumbSize + st : : msgFileThumbPadding . bottom ( ) ;
2015-12-10 16:37:45 +03:00
2015-12-19 21:09:24 +03:00
QRect rthumb ( rtlrect ( st : : msgFileThumbPadding . left ( ) , st : : msgFileThumbPadding . top ( ) , st : : msgFileThumbSize , st : : msgFileThumbSize , _width ) ) ;
2015-07-03 13:48:28 +03:00
2015-12-24 22:26:28 +03:00
if ( ( _data - > loading ( ) | | _data - > uploading ( ) | | ! loaded ) & & rthumb . contains ( x , y ) ) {
2016-04-13 21:29:32 +03:00
result . link = ( _data - > loading ( ) | | _data - > uploading ( ) ) ? _cancell : _savel ;
return result ;
2015-12-19 21:09:24 +03:00
}
2015-07-03 13:48:28 +03:00
2015-12-19 21:09:24 +03:00
if ( _data - > status ! = FileUploadFailed ) {
2016-02-12 19:35:06 +03:00
if ( rtlrect ( nameleft , linktop , thumbed - > _linkw , st : : semiboldFont - > height , _width ) . contains ( x , y ) ) {
2016-04-13 21:29:32 +03:00
result . link = ( _data - > loading ( ) | | _data - > uploading ( ) ) ? thumbed - > _linkcancell : thumbed - > _linksavel ;
return result ;
2015-12-19 21:09:24 +03:00
}
}
2015-07-03 13:48:28 +03:00
} else {
2015-12-28 13:28:00 +03:00
bottom = st : : msgFilePadding . top ( ) + st : : msgFileSize + st : : msgFilePadding . bottom ( ) ;
2015-12-19 21:09:24 +03:00
QRect inner ( rtlrect ( st : : msgFilePadding . left ( ) , st : : msgFilePadding . top ( ) , st : : msgFileSize , st : : msgFileSize , _width ) ) ;
2015-12-24 22:26:28 +03:00
if ( ( _data - > loading ( ) | | _data - > uploading ( ) | | ! loaded ) & & inner . contains ( x , y ) ) {
2016-04-13 21:29:32 +03:00
result . link = ( _data - > loading ( ) | | _data - > uploading ( ) ) ? _cancell : _savel ;
return result ;
2015-12-19 21:09:24 +03:00
}
2015-07-03 13:48:28 +03:00
}
2015-12-28 13:28:00 +03:00
int32 height = _height ;
2016-04-08 13:20:10 +04:00
if ( auto captioned = Get < HistoryDocumentCaptioned > ( ) ) {
2015-12-28 13:28:00 +03:00
if ( y > = bottom ) {
2016-04-13 21:29:32 +03:00
result = captioned - > _caption . getState ( x - st : : msgPadding . left ( ) , y - bottom , _width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) , request . forText ( ) ) ;
return result ;
2015-12-28 13:28:00 +03:00
}
2016-02-12 19:35:06 +03:00
height - = captioned - > _caption . countHeight ( _width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) + st : : msgPadding . bottom ( ) ;
2015-12-28 13:28:00 +03:00
}
2016-04-09 09:57:55 +04:00
if ( x > = 0 & & y > = 0 & & x < _width & & y < height & & ! _data - > loading ( ) & & ! _data - > uploading ( ) & & _data - > isValid ( ) ) {
2016-04-13 21:29:32 +03:00
result . link = _openl ;
return result ;
2015-07-03 13:48:28 +03:00
}
2016-04-13 21:29:32 +03:00
return result ;
2015-07-03 13:48:28 +03:00
}
2016-06-22 16:39:54 +03:00
QString HistoryDocument : : notificationText ( ) const {
QString result ;
buildStringRepresentation ( [ & result ] ( const QString & type , const QString & fileName , const Text & caption ) {
result = captionedNotificationText ( fileName . isEmpty ( ) ? type : fileName , caption ) ;
} ) ;
return result ;
}
2016-04-14 14:00:23 +03:00
QString HistoryDocument : : inDialogsText ( ) const {
2016-02-12 19:35:06 +03:00
QString result ;
2016-06-22 16:39:54 +03:00
buildStringRepresentation ( [ & result ] ( const QString & type , const QString & fileName , const Text & caption ) {
result = captionedInDialogsText ( fileName . isEmpty ( ) ? type : fileName , caption ) ;
} ) ;
2016-02-12 19:35:06 +03:00
return result ;
2014-05-30 12:53:19 +04:00
}
2016-05-06 20:33:48 +03:00
TextWithEntities HistoryDocument : : selectedText ( TextSelection selection ) const {
2016-06-22 16:39:54 +03:00
TextWithEntities result ;
buildStringRepresentation ( [ & result , selection ] ( const QString & type , const QString & fileName , const Text & caption ) {
auto fullType = type ;
if ( ! fileName . isEmpty ( ) ) {
fullType . append ( qstr ( " : " ) ) . append ( fileName ) ;
}
result = captionedSelectedText ( fullType , caption , selection ) ;
} ) ;
return result ;
}
template < typename Callback >
void HistoryDocument : : buildStringRepresentation ( Callback callback ) const {
2016-04-14 14:00:23 +03:00
const Text emptyCaption ;
const Text * caption = & emptyCaption ;
if ( auto captioned = Get < HistoryDocumentCaptioned > ( ) ) {
caption = & captioned - > _caption ;
}
QString attachType = lang ( lng_in_dlg_file ) ;
2016-03-25 14:29:45 +03:00
if ( Has < HistoryDocumentVoice > ( ) ) {
2016-04-14 14:00:23 +03:00
attachType = lang ( lng_in_dlg_audio ) ;
2016-03-25 14:29:45 +03:00
} else if ( _data - > song ( ) ) {
2016-04-14 14:00:23 +03:00
attachType = lang ( lng_in_dlg_audio_file ) ;
2016-03-25 14:29:45 +03:00
}
2016-06-22 16:39:54 +03:00
QString attachFileName ;
2016-04-08 13:20:10 +04:00
if ( auto named = Get < HistoryDocumentNamed > ( ) ) {
2016-02-12 19:35:06 +03:00
if ( ! named - > _name . isEmpty ( ) ) {
2016-06-22 16:39:54 +03:00
attachFileName = named - > _name ;
2016-02-12 19:35:06 +03:00
}
}
2016-06-22 16:39:54 +03:00
return callback ( attachType , attachFileName , * caption ) ;
2014-11-22 12:45:04 +03:00
}
2015-12-19 21:09:24 +03:00
void HistoryDocument : : setStatusSize ( int32 newSize , qint64 realDuration ) const {
2016-02-12 19:35:06 +03:00
int32 duration = _data - > song ( ) ? _data - > song ( ) - > duration : ( _data - > voice ( ) ? _data - > voice ( ) - > duration : - 1 ) ;
HistoryFileMedia : : setStatusSize ( newSize , _data - > size , duration , realDuration ) ;
2016-04-08 13:20:10 +04:00
if ( auto thumbed = Get < HistoryDocumentThumbed > ( ) ) {
2016-02-12 19:35:06 +03:00
if ( _statusSize = = FileStatusSizeReady ) {
thumbed - > _link = lang ( lng_media_download ) . toUpper ( ) ;
} else if ( _statusSize = = FileStatusSizeLoaded ) {
thumbed - > _link = lang ( lng_media_open_with ) . toUpper ( ) ;
} else if ( _statusSize = = FileStatusSizeFailed ) {
thumbed - > _link = lang ( lng_media_download ) . toUpper ( ) ;
} else if ( _statusSize > = 0 ) {
thumbed - > _link = lang ( lng_media_cancel ) . toUpper ( ) ;
} else {
thumbed - > _link = lang ( lng_media_open_with ) . toUpper ( ) ;
}
thumbed - > _linkw = st : : semiboldFont - > width ( thumbed - > _link ) ;
2014-10-10 16:46:20 +04:00
}
}
2016-04-10 18:53:01 +04:00
bool HistoryDocument : : updateStatusText ( ) const {
2015-12-19 21:09:24 +03:00
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 ;
2015-12-24 22:26:28 +03:00
} else if ( _data - > loading ( ) ) {
statusSize = _data - > loadOffset ( ) ;
2015-12-23 22:23:14 +03:00
} else if ( _data - > loaded ( ) ) {
2016-07-10 16:02:22 +03:00
statusSize = FileStatusSizeLoaded ;
if ( audioPlayer ( ) ) {
if ( _data - > voice ( ) ) {
AudioMsgId playing ;
auto playbackState = audioPlayer ( ) - > currentState ( & playing , AudioMsgId : : Type : : Voice ) ;
if ( playing = = AudioMsgId ( _data , _parent - > fullId ( ) ) & & ! ( playbackState . state & AudioPlayerStoppedMask ) & & playbackState . state ! = AudioPlayerFinishing ) {
if ( auto voice = Get < HistoryDocumentVoice > ( ) ) {
bool was = voice - > _playback ;
voice - > ensurePlayback ( this ) ;
if ( ! was | | playbackState . position ! = voice - > _playback - > _position ) {
float64 prg = playbackState . duration ? snap ( float64 ( playbackState . position ) / playbackState . duration , 0. , 1. ) : 0. ;
if ( voice - > _playback - > _position < playbackState . position ) {
voice - > _playback - > a_progress . start ( prg ) ;
} else {
voice - > _playback - > a_progress = anim : : fvalue ( 0. , prg ) ;
}
voice - > _playback - > _position = playbackState . position ;
voice - > _playback - > _a_progress . start ( ) ;
2016-02-12 19:35:06 +03:00
}
}
2016-07-10 16:02:22 +03:00
statusSize = - 1 - ( playbackState . position / ( playbackState . frequency ? playbackState . frequency : AudioVoiceMsgFrequency ) ) ;
realDuration = playbackState . duration / ( playbackState . frequency ? playbackState . frequency : AudioVoiceMsgFrequency ) ;
showPause = ( playbackState . state = = AudioPlayerPlaying | | playbackState . state = = AudioPlayerResuming | | playbackState . state = = AudioPlayerStarting ) ;
} else {
if ( auto voice = Get < HistoryDocumentVoice > ( ) ) {
voice - > checkPlaybackFinished ( ) ;
}
}
} else if ( _data - > song ( ) ) {
AudioMsgId playing ;
auto playbackState = audioPlayer ( ) - > currentState ( & playing , AudioMsgId : : Type : : Song ) ;
if ( playing = = AudioMsgId ( _data , _parent - > fullId ( ) ) & & ! ( playbackState . state & AudioPlayerStoppedMask ) & & playbackState . state ! = AudioPlayerFinishing ) {
statusSize = - 1 - ( playbackState . position / ( playbackState . frequency ? playbackState . frequency : AudioVoiceMsgFrequency ) ) ;
realDuration = playbackState . duration / ( playbackState . frequency ? playbackState . frequency : AudioVoiceMsgFrequency ) ;
showPause = ( playbackState . state = = AudioPlayerPlaying | | playbackState . state = = AudioPlayerResuming | | playbackState . state = = AudioPlayerStarting ) ;
} else {
}
if ( ! showPause & & ( playing = = AudioMsgId ( _data , _parent - > fullId ( ) ) ) & & App : : main ( ) & & App : : main ( ) - > player ( ) - > seekingSong ( playing ) ) {
showPause = true ;
2016-02-12 19:35:06 +03:00
}
}
2015-08-31 17:27:20 +03:00
}
2015-12-11 10:44:53 +03:00
} else {
2015-12-19 21:09:24 +03:00
statusSize = FileStatusSizeReady ;
2015-12-11 10:44:53 +03:00
}
2015-12-19 21:09:24 +03:00
if ( statusSize ! = _statusSize ) {
setStatusSize ( statusSize , realDuration ) ;
2014-05-30 12:53:19 +04:00
}
2015-12-19 21:09:24 +03:00
return showPause ;
}
2016-02-12 19:35:06 +03:00
void HistoryDocument : : step_voiceProgress ( float64 ms , bool timer ) {
2016-04-08 13:20:10 +04:00
if ( auto voice = Get < HistoryDocumentVoice > ( ) ) {
2016-02-12 19:35:06 +03:00
if ( voice - > _playback ) {
float64 dt = ms / ( 2 * AudioVoiceMsgUpdateView ) ;
if ( dt > = 1 ) {
voice - > _playback - > _a_progress . stop ( ) ;
voice - > _playback - > a_progress . finish ( ) ;
} else {
voice - > _playback - > a_progress . update ( qMin ( dt , 1. ) , anim : : linear ) ;
}
if ( timer ) Ui : : repaintHistoryItem ( _parent ) ;
}
}
}
2016-04-10 18:53:01 +04:00
void HistoryDocument : : attachToParent ( ) {
App : : regDocumentItem ( _data , _parent ) ;
2014-05-30 12:53:19 +04:00
}
2016-04-10 18:53:01 +04:00
void HistoryDocument : : detachFromParent ( ) {
App : : unregDocumentItem ( _data , _parent ) ;
2015-12-19 21:09:24 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryDocument : : updateSentMedia ( const MTPMessageMedia & media ) {
2015-12-19 21:09:24 +03:00
if ( media . type ( ) = = mtpc_messageMediaDocument ) {
App : : feedDocument ( media . c_messageMediaDocument ( ) . vdocument , _data ) ;
2016-02-12 19:35:06 +03:00
if ( ! _data - > data ( ) . isEmpty ( ) ) {
if ( _data - > voice ( ) ) {
2016-02-12 21:18:32 +03:00
Local : : writeAudio ( _data - > mediaKey ( ) , _data - > data ( ) ) ;
2016-02-12 19:35:06 +03:00
} else {
2016-02-12 21:18:32 +03:00
Local : : writeStickerImage ( _data - > mediaKey ( ) , _data - > data ( ) ) ;
2016-02-12 19:35:06 +03:00
}
}
2015-12-19 21:09:24 +03:00
}
2014-05-30 12:53:19 +04:00
}
2016-04-10 18:53:01 +04:00
bool HistoryDocument : : needReSetInlineResultMedia ( const MTPMessageMedia & media ) {
return needReSetInlineResultDocument ( media , _data ) ;
}
2015-03-19 12:18:19 +03:00
ImagePtr HistoryDocument : : replyPreview ( ) {
2015-12-08 22:07:50 +03:00
return _data - > makeReplyPreview ( ) ;
}
2016-04-10 18:53:01 +04:00
HistoryGif : : HistoryGif ( HistoryItem * parent , DocumentData * document , const QString & caption ) : HistoryFileMedia ( parent )
2015-12-08 22:07:50 +03:00
, _data ( document )
2015-12-13 20:05:32 +03:00
, _thumbw ( 1 )
2015-12-15 17:50:51 +03:00
, _thumbh ( 1 )
2015-12-28 13:28:00 +03:00
, _caption ( st : : minPhotoSize - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) )
2016-03-25 14:29:45 +03:00
, _gif ( nullptr ) {
2016-03-29 20:17:00 +03:00
setDocumentLinks ( _data , true ) ;
2015-12-13 20:05:32 +03:00
setStatusSize ( FileStatusSizeReady ) ;
2015-12-28 13:28:00 +03:00
if ( ! caption . isEmpty ( ) ) {
2016-04-10 18:53:01 +04:00
_caption . setText ( st : : msgFont , caption + _parent - > skipBlock ( ) , itemTextNoMonoOptions ( _parent ) ) ;
2015-12-28 13:28:00 +03:00
}
2015-12-08 22:07:50 +03:00
_data - > thumb - > load ( ) ;
}
2016-04-10 18:53:01 +04:00
HistoryGif : : HistoryGif ( HistoryItem * parent , const HistoryGif & other ) : HistoryFileMedia ( parent )
2015-12-17 20:31:28 +03:00
, _data ( other . _data )
, _thumbw ( other . _thumbw )
, _thumbh ( other . _thumbh )
2016-01-25 13:22:58 +03:00
, _caption ( other . _caption )
2016-03-25 14:29:45 +03:00
, _gif ( nullptr ) {
2016-03-29 20:17:00 +03:00
setDocumentLinks ( _data , true ) ;
2015-12-17 20:31:28 +03:00
setStatusSize ( other . _statusSize ) ;
}
2016-04-10 18:53:01 +04:00
void HistoryGif : : initDimensions ( ) {
2015-12-28 13:28:00 +03:00
if ( _caption . hasSkipBlock ( ) ) {
2016-04-10 18:53:01 +04:00
_caption . setSkipBlock ( _parent - > skipBlockWidth ( ) , _parent - > skipBlockHeight ( ) ) ;
2015-12-28 13:28:00 +03:00
}
2016-04-10 18:53:01 +04:00
bool bubble = _parent - > hasBubble ( ) ;
2015-12-13 20:05:32 +03:00
int32 tw = 0 , th = 0 ;
2016-06-24 13:37:29 +03:00
if ( gif ( ) & & _gif - > state ( ) = = Media : : Clip : : State : : Error ) {
2015-12-25 16:10:13 +03:00
if ( ! _gif - > autoplay ( ) ) {
Ui : : showLayer ( new InformBox ( lang ( lng_gif_error ) ) ) ;
}
2015-12-16 18:04:02 +03:00
App : : unregGifItem ( _gif ) ;
delete _gif ;
2016-06-24 13:37:29 +03:00
_gif = Media : : Clip : : BadReader ;
2015-12-16 18:04:02 +03:00
}
2015-12-25 16:10:13 +03:00
if ( gif ( ) & & _gif - > ready ( ) ) {
2015-12-15 17:50:51 +03:00
tw = convertScale ( _gif - > width ( ) ) ;
th = convertScale ( _gif - > height ( ) ) ;
2015-12-08 22:07:50 +03:00
} else {
2015-12-13 20:05:32 +03:00
tw = convertScale ( _data - > dimensions . width ( ) ) , th = convertScale ( _data - > dimensions . height ( ) ) ;
if ( ! tw | | ! th ) {
tw = convertScale ( _data - > thumb - > width ( ) ) ;
th = convertScale ( _data - > thumb - > height ( ) ) ;
}
}
2015-12-16 18:04:02 +03:00
if ( tw > st : : maxGifSize ) {
th = ( st : : maxGifSize * th ) / tw ;
tw = st : : maxGifSize ;
2015-12-13 20:05:32 +03:00
}
2015-12-16 18:04:02 +03:00
if ( th > st : : maxGifSize ) {
tw = ( st : : maxGifSize * tw ) / th ;
th = st : : maxGifSize ;
2015-12-13 20:05:32 +03:00
}
if ( ! tw | | ! th ) {
tw = th = 1 ;
}
_thumbw = tw ;
_thumbh = th ;
2015-12-15 17:50:51 +03:00
_maxw = qMax ( tw , int32 ( st : : minPhotoSize ) ) ;
_minh = qMax ( th , int32 ( st : : minPhotoSize ) ) ;
2016-04-10 18:53:01 +04:00
_maxw = qMax ( _maxw , _parent - > infoWidth ( ) + 2 * int32 ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
2015-12-25 16:10:13 +03:00
if ( ! gif ( ) | | ! _gif - > ready ( ) ) {
2015-12-15 17:50:51 +03:00
_maxw = qMax ( _maxw , gifMaxStatusWidth ( _data ) + 2 * int32 ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
2015-12-13 20:05:32 +03:00
}
if ( bubble ) {
_maxw + = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
_minh + = st : : mediaPadding . top ( ) + st : : mediaPadding . bottom ( ) ;
2015-12-28 13:28:00 +03:00
if ( ! _caption . isEmpty ( ) ) {
_minh + = st : : mediaCaptionSkip + _caption . countHeight ( _maxw - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) + st : : msgPadding . bottom ( ) ;
}
2015-12-08 22:07:50 +03:00
}
2015-12-19 21:09:24 +03:00
}
2016-04-10 18:53:01 +04:00
int HistoryGif : : resizeGetHeight ( int width ) {
bool bubble = _parent - > hasBubble ( ) ;
2015-12-19 21:09:24 +03:00
2016-04-07 14:03:10 +04:00
int tw = 0 , th = 0 ;
2015-12-25 16:10:13 +03:00
if ( gif ( ) & & _gif - > ready ( ) ) {
2015-12-19 21:09:24 +03:00
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 ) ) ;
2016-04-10 18:53:01 +04:00
_width = qMax ( _width , _parent - > infoWidth ( ) + 2 * int32 ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
2015-12-25 16:10:13 +03:00
if ( gif ( ) & & _gif - > ready ( ) ) {
2015-12-19 21:09:24 +03:00
if ( ! _gif - > started ( ) ) {
2016-07-19 13:54:43 +03:00
auto inWebPage = ( _parent - > getMedia ( ) ! = this ) ;
auto roundRadius = inWebPage ? ImageRoundRadius : : Small : ImageRoundRadius : : Large ;
_gif - > start ( _thumbw , _thumbh , _width , _height , roundRadius ) ;
2015-12-19 21:09:24 +03:00
}
} else {
_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 ( ) ;
2015-12-28 13:28:00 +03:00
if ( ! _caption . isEmpty ( ) ) {
_height + = st : : mediaCaptionSkip + _caption . countHeight ( _width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) + st : : msgPadding . bottom ( ) ;
}
2015-12-19 21:09:24 +03:00
}
return _height ;
2015-12-08 22:07:50 +03:00
}
2016-04-13 21:29:32 +03:00
void HistoryGif : : draw ( Painter & p , const QRect & r , TextSelection selection , uint64 ms ) const {
2015-12-19 21:09:24 +03:00
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return ;
2015-12-08 22:07:50 +03:00
2016-04-10 18:53:01 +04:00
_data - > automaticLoad ( _parent ) ;
bool loaded = _data - > loaded ( ) , displayLoading = ( _parent - > id < 0 ) | | _data - > displayLoading ( ) ;
2016-04-13 21:29:32 +03:00
bool selected = ( selection = = FullSelection ) ;
2016-06-24 13:37:29 +03:00
if ( loaded & & ! gif ( ) & & _gif ! = Media : : Clip : : BadReader & & cAutoPlayGif ( ) ) {
2016-04-10 18:53:01 +04:00
Ui : : autoplayMediaInlineAsync ( _parent - > fullId ( ) ) ;
2015-12-23 19:48:44 +03:00
}
2015-12-24 22:26:28 +03:00
int32 skipx = 0 , skipy = 0 , width = _width , height = _height ;
2016-04-10 18:53:01 +04:00
bool bubble = _parent - > hasBubble ( ) ;
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , outbg = out & & ! isPost ;
2015-12-24 22:26:28 +03:00
2015-12-28 13:28:00 +03:00
int32 captionw = width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ;
2015-12-25 16:10:13 +03:00
bool animating = ( gif ( ) & & _gif - > started ( ) ) ;
2015-12-13 20:05:32 +03:00
2016-04-10 18:53:01 +04:00
if ( ! animating | | _parent - > id < 0 ) {
2015-12-24 22:26:28 +03:00
if ( displayLoading ) {
2016-04-10 18:53:01 +04:00
ensureAnimation ( ) ;
2015-12-13 20:05:32 +03:00
if ( ! _animation - > radial . animating ( ) ) {
2015-12-31 23:27:21 +08:00
_animation - > radial . start ( dataProgress ( ) ) ;
2015-12-13 20:05:32 +03:00
}
2015-12-08 22:07:50 +03:00
}
2016-04-10 18:53:01 +04:00
updateStatusText ( ) ;
2015-12-13 20:05:32 +03:00
}
2015-12-19 00:36:16 +03:00
bool radial = isRadialAnimation ( ms ) ;
2015-12-13 20:05:32 +03:00
if ( bubble ) {
skipx = st : : mediaPadding . left ( ) ;
skipy = st : : mediaPadding . top ( ) ;
width - = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
height - = skipy + st : : mediaPadding . bottom ( ) ;
2015-12-28 13:28:00 +03:00
if ( ! _caption . isEmpty ( ) ) {
height - = st : : mediaCaptionSkip + _caption . countHeight ( captionw ) + st : : msgPadding . bottom ( ) ;
}
2015-12-13 20:05:32 +03:00
} else {
App : : roundShadow ( p , 0 , 0 , width , _height , selected ? st : : msgInShadowSelected : st : : msgInShadow , selected ? InSelectedShadowCorners : InShadowCorners ) ;
}
2015-12-19 21:09:24 +03:00
QRect rthumb ( rtlrect ( skipx , skipy , width , height , _width ) ) ;
2015-12-08 22:07:50 +03:00
2015-12-13 20:05:32 +03:00
if ( animating ) {
2015-12-31 03:09:20 +08:00
p . drawPixmap ( rthumb . topLeft ( ) , _gif - > current ( _thumbw , _thumbh , width , height , ( Ui : : isLayerShown ( ) | | Ui : : isMediaViewShown ( ) | | Ui : : isInlineItemBeingChosen ( ) ) ? 0 : ms ) ) ;
2015-12-13 20:05:32 +03:00
} else {
2016-07-07 15:35:14 +03:00
p . drawPixmap ( rthumb . topLeft ( ) , _data - > thumb - > pixBlurredSingle ( ImageRoundRadius : : Large , _thumbw , _thumbh , width , height ) ) ;
2015-12-13 20:05:32 +03:00
}
if ( selected ) {
2016-07-07 15:35:14 +03:00
App : : roundRect ( p , rthumb , textstyleCurrent ( ) - > selectOverlay , SelectedOverlayLargeCorners ) ;
2015-12-13 20:05:32 +03:00
}
2015-12-08 22:07:50 +03:00
2016-06-24 13:37:29 +03:00
if ( radial | | ( ! _gif & & ( ( ! loaded & & ! _data - > loading ( ) ) | | ! cAutoPlayGif ( ) ) ) | | ( _gif = = Media : : Clip : : BadReader ) ) {
2016-04-10 18:53:01 +04:00
float64 radialOpacity = ( radial & & loaded & & _parent - > id > 0 ) ? _animation - > radial . opacity ( ) : 1 ;
2015-12-13 20:05:32 +03:00
QRect inner ( rthumb . x ( ) + ( rthumb . width ( ) - st : : msgFileSize ) / 2 , rthumb . y ( ) + ( rthumb . height ( ) - st : : msgFileSize ) / 2 , st : : msgFileSize , st : : msgFileSize ) ;
p . setPen ( Qt : : NoPen ) ;
2015-12-08 22:07:50 +03:00
if ( selected ) {
2015-12-13 20:05:32 +03:00
p . setBrush ( st : : msgDateImgBgSelected ) ;
2015-12-28 00:37:48 +03:00
} else if ( isThumbAnimation ( ms ) ) {
2015-12-13 20:05:32 +03:00
float64 over = _animation - > a_thumbOver . current ( ) ;
p . setOpacity ( ( st : : msgDateImgBg - > c . alphaF ( ) * ( 1 - over ) ) + ( st : : msgDateImgBgOver - > c . alphaF ( ) * over ) ) ;
p . setBrush ( st : : black ) ;
} else {
2016-03-29 20:17:00 +03:00
bool over = ClickHandler : : showAsActive ( _data - > loading ( ) ? _cancell : _savel ) ;
2015-12-13 20:05:32 +03:00
p . setBrush ( over ? st : : msgDateImgBgOver : st : : msgDateImgBg ) ;
2015-12-08 22:07:50 +03:00
}
2015-12-24 22:26:28 +03:00
p . setOpacity ( radialOpacity * p . opacity ( ) ) ;
2015-12-13 20:05:32 +03:00
p . setRenderHint ( QPainter : : HighQualityAntialiasing ) ;
p . drawEllipse ( inner ) ;
p . setRenderHint ( QPainter : : HighQualityAntialiasing , false ) ;
2015-12-24 22:26:28 +03:00
p . setOpacity ( radialOpacity ) ;
2015-12-13 20:05:32 +03:00
style : : sprite icon ;
2015-12-23 19:48:44 +03:00
if ( _data - > loaded ( ) & & ! radial ) {
2015-12-13 20:05:32 +03:00
icon = ( selected ? st : : msgFileInPlaySelected : st : : msgFileInPlay ) ;
2015-12-24 22:26:28 +03:00
} else if ( radial | | _data - > loading ( ) ) {
2016-04-10 18:53:01 +04:00
if ( _parent - > id > 0 | | _data - > uploading ( ) ) {
2016-01-04 19:28:41 +08:00
icon = ( selected ? st : : msgFileInCancelSelected : st : : msgFileInCancel ) ;
}
2015-12-13 20:05:32 +03:00
} else {
icon = ( selected ? st : : msgFileInDownloadSelected : st : : msgFileInDownload ) ;
}
2016-01-04 19:28:41 +08:00
if ( ! icon . isEmpty ( ) ) {
p . drawSpriteCenter ( inner , icon ) ;
}
2015-12-13 20:05:32 +03:00
if ( radial ) {
2015-12-24 22:26:28 +03:00
p . setOpacity ( 1 ) ;
2015-12-13 20:05:32 +03:00
QRect rinner ( inner . marginsRemoved ( QMargins ( st : : msgFileRadialLine , st : : msgFileRadialLine , st : : msgFileRadialLine , st : : msgFileRadialLine ) ) ) ;
2015-12-22 15:49:42 +03:00
_animation - > radial . draw ( p , rinner , st : : msgFileRadialLine , selected ? st : : msgInBgSelected : st : : msgInBg ) ;
2015-12-13 20:05:32 +03:00
}
2016-04-10 18:53:01 +04:00
if ( ! animating | | _parent - > id < 0 ) {
2015-12-19 00:36:16 +03:00
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 ( ) ;
2015-12-19 21:09:24 +03:00
App : : roundRect ( p , rtlrect ( statusX - st : : msgDateImgPadding . x ( ) , statusY - st : : msgDateImgPadding . y ( ) , statusW , statusH , _width ) , selected ? st : : msgDateImgBgSelected : st : : msgDateImgBg , selected ? DateSelectedCorners : DateCorners ) ;
2015-12-19 00:36:16 +03:00
p . setFont ( st : : normalFont ) ;
p . setPen ( st : : white ) ;
2015-12-19 21:09:24 +03:00
p . drawTextLeft ( statusX , statusY , _width , _statusText , statusW - 2 * st : : msgDateImgPadding . x ( ) ) ;
2015-12-19 00:36:16 +03:00
}
2015-12-08 22:07:50 +03:00
}
2015-12-28 13:28:00 +03:00
if ( ! _caption . isEmpty ( ) ) {
p . setPen ( st : : black ) ;
2016-04-13 21:29:32 +03:00
_caption . draw ( p , st : : msgPadding . left ( ) , skipy + height + st : : mediaPadding . bottom ( ) + st : : mediaCaptionSkip , captionw , style : : al_left , 0 , - 1 , selection ) ;
2016-04-10 18:53:01 +04:00
} else if ( _parent - > getMedia ( ) = = this & & ( _data - > uploading ( ) | | App : : hoveredItem ( ) = = _parent ) ) {
2016-01-05 14:59:57 +08:00
int32 fullRight = skipx + width , fullBottom = skipy + height ;
2016-04-10 18:53:01 +04:00
_parent - > drawInfo ( p , fullRight , fullBottom , 2 * skipx + width , selected , InfoDisplayOverImage ) ;
2015-12-28 13:28:00 +03:00
}
2015-12-13 20:05:32 +03:00
}
2015-12-08 22:07:50 +03:00
2016-04-13 21:29:32 +03:00
HistoryTextState HistoryGif : : getState ( int x , int y , HistoryStateRequest request ) const {
HistoryTextState result ;
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return result ;
2015-12-19 21:09:24 +03:00
int32 skipx = 0 , skipy = 0 , width = _width , height = _height ;
2016-04-10 18:53:01 +04:00
bool bubble = _parent - > hasBubble ( ) ;
2015-12-19 21:09:24 +03:00
if ( bubble ) {
skipx = st : : mediaPadding . left ( ) ;
skipy = st : : mediaPadding . top ( ) ;
2015-12-28 13:28:00 +03:00
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 ) {
2016-04-13 21:29:32 +03:00
result = _caption . getState ( x - st : : msgPadding . left ( ) , y - height , captionw , request . forText ( ) ) ;
return result ;
2015-12-28 13:28:00 +03:00
}
height - = st : : mediaCaptionSkip ;
}
2015-12-19 21:09:24 +03:00
width - = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
height - = skipy + st : : mediaPadding . bottom ( ) ;
}
if ( x > = skipx & & y > = skipy & & x < skipx + width & & y < skipy + height ) {
2015-12-24 22:26:28 +03:00
if ( _data - > uploading ( ) ) {
2016-04-13 21:29:32 +03:00
result . link = _cancell ;
2015-12-28 00:37:48 +03:00
} else if ( ! gif ( ) | | ! cAutoPlayGif ( ) ) {
2016-04-13 21:29:32 +03:00
result . link = _data - > loaded ( ) ? _openl : ( _data - > loading ( ) ? _cancell : _savel ) ;
2015-12-19 21:09:24 +03:00
}
2016-04-10 18:53:01 +04:00
if ( _parent - > getMedia ( ) = = this ) {
2015-12-19 21:09:24 +03:00
int32 fullRight = skipx + width , fullBottom = skipy + height ;
2016-04-10 18:53:01 +04:00
bool inDate = _parent - > pointInTime ( fullRight , fullBottom , x , y , InfoDisplayOverImage ) ;
2015-12-19 21:09:24 +03:00
if ( inDate ) {
2016-04-13 21:29:32 +03:00
result . cursor = HistoryInDateCursorState ;
2015-12-19 21:09:24 +03:00
}
}
2016-04-13 21:29:32 +03:00
return result ;
2015-12-19 21:09:24 +03:00
}
2016-04-13 21:29:32 +03:00
return result ;
2015-12-19 21:09:24 +03:00
}
2016-06-22 16:39:54 +03:00
QString HistoryGif : : notificationText ( ) const {
return captionedNotificationText ( qsl ( " GIF " ) , _caption ) ;
}
2016-04-14 14:00:23 +03:00
QString HistoryGif : : inDialogsText ( ) const {
2016-06-22 16:39:54 +03:00
return captionedInDialogsText ( qsl ( " GIF " ) , _caption ) ;
2015-12-19 21:09:24 +03:00
}
2016-05-06 20:33:48 +03:00
TextWithEntities HistoryGif : : selectedText ( TextSelection selection ) const {
2016-04-14 14:00:23 +03:00
return captionedSelectedText ( qsl ( " GIF " ) , _caption , selection ) ;
2015-12-19 21:09:24 +03:00
}
2015-12-13 20:05:32 +03:00
void HistoryGif : : setStatusSize ( int32 newSize ) const {
HistoryFileMedia : : setStatusSize ( newSize , _data - > size , - 2 , 0 ) ;
}
2016-04-10 18:53:01 +04:00
void HistoryGif : : updateStatusText ( ) const {
2015-12-13 20:05:32 +03:00
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 ;
2015-12-24 22:26:28 +03:00
} else if ( _data - > loading ( ) ) {
statusSize = _data - > loadOffset ( ) ;
2015-12-23 19:48:44 +03:00
} else if ( _data - > loaded ( ) ) {
2015-12-13 20:05:32 +03:00
statusSize = FileStatusSizeLoaded ;
} else {
statusSize = FileStatusSizeReady ;
}
if ( statusSize ! = _statusSize ) {
setStatusSize ( statusSize ) ;
}
2015-12-08 22:07:50 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryGif : : attachToParent ( ) {
App : : regDocumentItem ( _data , _parent ) ;
2015-12-08 22:07:50 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryGif : : detachFromParent ( ) {
App : : unregDocumentItem ( _data , _parent ) ;
2015-12-08 22:07:50 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryGif : : updateSentMedia ( const MTPMessageMedia & media ) {
2015-12-08 22:07:50 +03:00
if ( media . type ( ) = = mtpc_messageMediaDocument ) {
App : : feedDocument ( media . c_messageMediaDocument ( ) . vdocument , _data ) ;
}
}
2016-04-10 18:53:01 +04:00
bool HistoryGif : : needReSetInlineResultMedia ( const MTPMessageMedia & media ) {
return needReSetInlineResultDocument ( media , _data ) ;
}
2015-12-08 22:07:50 +03:00
ImagePtr HistoryGif : : replyPreview ( ) {
return _data - > makeReplyPreview ( ) ;
2015-03-19 12:18:19 +03:00
}
2016-04-10 18:53:01 +04:00
bool HistoryGif : : playInline ( bool autoplay ) {
2015-12-25 16:10:13 +03:00
if ( gif ( ) ) {
2016-04-10 18:53:01 +04:00
stopInline ( ) ;
2016-04-20 15:27:38 +03:00
} else if ( _data - > loaded ( DocumentData : : FilePathResolveChecked ) ) {
2015-12-28 00:37:48 +03:00
if ( ! cAutoPlayGif ( ) ) {
App : : stopGifItems ( ) ;
}
2016-06-24 13:37:29 +03:00
_gif = new Media : : Clip : : Reader ( _data - > location ( ) , _data - > data ( ) , func ( _parent , & HistoryItem : : clipCallback ) ) ;
2016-04-10 18:53:01 +04:00
App : : regGifItem ( _gif , _parent ) ;
2016-03-19 19:55:15 +03:00
if ( gif ( ) ) _gif - > setAutoplay ( ) ;
2015-12-15 17:50:51 +03:00
}
2015-12-19 00:36:16 +03:00
return true ;
2015-12-15 17:50:51 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryGif : : stopInline ( ) {
2015-12-25 16:10:13 +03:00
if ( gif ( ) ) {
App : : unregGifItem ( _gif ) ;
delete _gif ;
_gif = 0 ;
}
2015-12-16 16:35:15 +03:00
2016-04-10 18:53:01 +04:00
_parent - > setPendingInitDimensions ( ) ;
Notify : : historyItemLayoutChanged ( _parent ) ;
2015-12-16 16:35:15 +03:00
}
2015-12-15 17:50:51 +03:00
HistoryGif : : ~ HistoryGif ( ) {
2015-12-25 16:10:13 +03:00
if ( gif ( ) ) {
2015-12-15 17:50:51 +03:00
App : : unregGifItem ( _gif ) ;
2015-12-29 00:20:04 +03:00
deleteAndMark ( _gif ) ;
2015-12-15 17:50:51 +03:00
}
}
2015-12-31 23:27:21 +08:00
float64 HistoryGif : : dataProgress ( ) const {
return ( _data - > uploading ( ) | | ! _parent | | _parent - > id > 0 ) ? _data - > progress ( ) : 0 ;
}
bool HistoryGif : : dataFinished ( ) const {
return ( ! _parent | | _parent - > id > 0 ) ? ( ! _data - > loading ( ) & & ! _data - > uploading ( ) ) : false ;
}
bool HistoryGif : : dataLoaded ( ) const {
return ( ! _parent | | _parent - > id > 0 ) ? _data - > loaded ( ) : false ;
}
2016-04-06 16:02:00 +04:00
namespace {
class StickerClickHandler : public LeftButtonClickHandler {
public :
StickerClickHandler ( const HistoryItem * item ) : _item ( item ) {
}
protected :
void onClickImpl ( ) const override {
2016-06-26 18:37:47 +03:00
if ( auto media = _item - > getMedia ( ) ) {
if ( auto document = media - > getDocument ( ) ) {
if ( auto sticker = document - > sticker ( ) ) {
2016-04-06 16:02:00 +04:00
if ( sticker - > set . type ( ) ! = mtpc_inputStickerSetEmpty & & App : : main ( ) ) {
App : : main ( ) - > stickersBox ( sticker - > set ) ;
}
}
}
}
}
private :
const HistoryItem * _item ;
} ;
} // namespace
2016-04-10 18:53:01 +04:00
HistorySticker : : HistorySticker ( HistoryItem * parent , DocumentData * document ) : HistoryMedia ( parent )
2015-12-23 15:19:32 +03:00
, _pixw ( 1 )
, _pixh ( 1 )
2016-01-09 19:24:16 +08:00
, _data ( document )
, _emoji ( _data - > sticker ( ) - > alt ) {
2015-12-23 15:19:32 +03:00
_data - > thumb - > load ( ) ;
2016-06-26 18:37:47 +03:00
if ( auto e = emojiFromText ( _emoji ) ) {
2016-01-09 19:24:16 +08:00
_emoji = emojiString ( e ) ;
2015-07-01 00:27:56 +03:00
}
2015-01-05 23:17:33 +03:00
}
2016-04-10 18:53:01 +04:00
void HistorySticker : : initDimensions ( ) {
2016-06-26 18:37:47 +03:00
auto sticker = _data - > sticker ( ) ;
if ( ! _packLink & & sticker & & sticker - > set . type ( ) ! = mtpc_inputStickerSetEmpty ) {
2016-04-10 18:53:01 +04:00
_packLink = ClickHandlerPtr ( new StickerClickHandler ( _parent ) ) ;
2016-04-06 16:02:00 +04:00
}
2015-12-23 15:19:32 +03:00
_pixw = _data - > dimensions . width ( ) ;
_pixh = _data - > dimensions . height ( ) ;
if ( _pixw > st : : maxStickerSize ) {
_pixh = ( st : : maxStickerSize * _pixh ) / _pixw ;
_pixw = st : : maxStickerSize ;
}
if ( _pixh > st : : maxStickerSize ) {
_pixw = ( st : : maxStickerSize * _pixw ) / _pixh ;
_pixh = st : : maxStickerSize ;
}
if ( _pixw < 1 ) _pixw = 1 ;
if ( _pixh < 1 ) _pixh = 1 ;
_maxw = qMax ( _pixw , int16 ( st : : minPhotoSize ) ) ;
_minh = qMax ( _pixh , int16 ( st : : minPhotoSize ) ) ;
2016-06-08 21:46:37 +03:00
if ( _parent - > getMedia ( ) = = this ) {
_maxw + = additionalWidth ( ) ;
}
2016-06-08 19:40:33 +03:00
2015-03-19 12:18:19 +03:00
_height = _minh ;
2014-12-23 02:11:37 +03:00
}
2016-04-10 18:53:01 +04:00
int HistorySticker : : resizeGetHeight ( int width ) { // return new height
2016-06-08 21:46:37 +03:00
_width = qMin ( width , _maxw ) ;
2016-04-10 18:53:01 +04:00
if ( _parent - > getMedia ( ) = = this ) {
auto via = _parent - > Get < HistoryMessageVia > ( ) ;
auto reply = _parent - > Get < HistoryMessageReply > ( ) ;
2016-04-07 14:03:10 +04:00
if ( via | | reply ) {
2016-06-08 21:46:37 +03:00
int usew = _maxw - additionalWidth ( via , reply ) ;
int availw = _width - usew - st : : msgReplyPadding . left ( ) - st : : msgReplyPadding . left ( ) - st : : msgReplyPadding . left ( ) ;
2016-04-07 14:03:10 +04:00
if ( via ) {
via - > resize ( availw ) ;
}
if ( reply ) {
reply - > resize ( availw ) ;
}
}
2016-01-03 17:46:30 +08:00
}
return _height ;
}
2016-04-13 21:29:32 +03:00
void HistorySticker : : draw ( Painter & p , const QRect & r , TextSelection selection , uint64 ms ) const {
2016-06-26 18:37:47 +03:00
auto sticker = _data - > sticker ( ) ;
if ( ! sticker ) return ;
2015-12-19 21:09:24 +03:00
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return ;
2014-12-23 02:11:37 +03:00
2015-12-24 22:26:28 +03:00
_data - > checkSticker ( ) ;
2015-12-23 19:48:44 +03:00
bool loaded = _data - > loaded ( ) ;
2016-04-13 21:29:32 +03:00
bool selected = ( selection = = FullSelection ) ;
2015-12-24 22:26:28 +03:00
2016-04-10 18:53:01 +04:00
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , childmedia = ( _parent - > getMedia ( ) ! = this ) ;
2015-09-13 20:27:29 +03:00
2016-04-07 14:03:10 +04:00
int usew = _maxw , usex = 0 ;
2016-04-10 18:53:01 +04:00
auto via = childmedia ? nullptr : _parent - > Get < HistoryMessageVia > ( ) ;
auto reply = childmedia ? nullptr : _parent - > Get < HistoryMessageReply > ( ) ;
2016-04-07 14:03:10 +04:00
if ( via | | reply ) {
2016-06-08 21:46:37 +03:00
usew - = additionalWidth ( via , reply ) ;
2016-02-17 19:37:21 +03:00
if ( isPost ) {
2015-09-13 20:27:29 +03:00
} else if ( out ) {
2015-12-19 21:09:24 +03:00
usex = _width - usew ;
2015-03-19 12:18:19 +03:00
}
}
2015-12-19 21:09:24 +03:00
if ( rtl ( ) ) usex = _width - usex - usew ;
2015-03-19 12:18:19 +03:00
2014-12-23 02:11:37 +03:00
if ( selected ) {
2016-06-26 18:37:47 +03:00
if ( sticker - > img - > isNull ( ) ) {
2015-12-23 15:19:32 +03:00
p . drawPixmap ( QPoint ( usex + ( usew - _pixw ) / 2 , ( _minh - _pixh ) / 2 ) , _data - > thumb - > pixBlurredColored ( st : : msgStickerOverlay , _pixw , _pixh ) ) ;
2015-01-05 23:17:33 +03:00
} else {
2016-06-26 18:37:47 +03:00
p . drawPixmap ( QPoint ( usex + ( usew - _pixw ) / 2 , ( _minh - _pixh ) / 2 ) , sticker - > img - > pixColored ( st : : msgStickerOverlay , _pixw , _pixh ) ) ;
2015-01-05 23:17:33 +03:00
}
} else {
2016-06-26 18:37:47 +03:00
if ( sticker - > img - > isNull ( ) ) {
2015-12-23 15:19:32 +03:00
p . drawPixmap ( QPoint ( usex + ( usew - _pixw ) / 2 , ( _minh - _pixh ) / 2 ) , _data - > thumb - > pixBlurred ( _pixw , _pixh ) ) ;
2015-01-05 23:17:33 +03:00
} else {
2016-06-26 18:37:47 +03:00
p . drawPixmap ( QPoint ( usex + ( usew - _pixw ) / 2 , ( _minh - _pixh ) / 2 ) , sticker - > img - > pix ( _pixw , _pixh ) ) ;
2015-01-05 23:17:33 +03:00
}
2014-12-23 02:11:37 +03:00
}
2016-04-07 14:03:10 +04:00
if ( ! childmedia ) {
2016-04-10 18:53:01 +04:00
_parent - > drawInfo ( p , usex + usew , _height , usex * 2 + usew , selected , InfoDisplayOverBackground ) ;
2015-03-19 12:18:19 +03:00
2016-04-07 14:03:10 +04:00
if ( via | | reply ) {
int rectw = _width - usew - st : : msgReplyPadding . left ( ) ;
int recth = st : : msgReplyPadding . top ( ) + st : : msgReplyPadding . bottom ( ) ;
if ( via ) {
recth + = st : : msgServiceNameFont - > height + ( reply ? st : : msgReplyPadding . top ( ) : 0 ) ;
}
if ( reply ) {
recth + = st : : msgReplyBarSize . height ( ) ;
}
int rectx = isPost ? ( usew + st : : msgReplyPadding . left ( ) ) : ( out ? 0 : ( usew + st : : msgReplyPadding . left ( ) ) ) ;
int recty = _height - recth ;
if ( rtl ( ) ) rectx = _width - rectx - rectw ;
// Make the bottom of the rect at the same level as the bottom of the info rect.
recty - = st : : msgDateImgDelta ;
2016-07-08 13:06:41 +03:00
App : : roundRect ( p , rectx , recty , rectw , recth , selected ? App : : msgServiceSelectBg ( ) : App : : msgServiceBg ( ) , selected ? StickerSelectedCorners : StickerCorners ) ;
2016-04-07 14:03:10 +04:00
rectx + = st : : msgReplyPadding . left ( ) ;
rectw - = st : : msgReplyPadding . left ( ) + st : : msgReplyPadding . right ( ) ;
if ( via ) {
p . drawTextLeft ( rectx , recty + st : : msgReplyPadding . top ( ) , 2 * rectx + rectw , via - > _text ) ;
int skip = st : : msgServiceNameFont - > height + ( reply ? st : : msgReplyPadding . top ( ) : 0 ) ;
recty + = skip ;
}
if ( reply ) {
HistoryMessageReply : : PaintFlags flags = 0 ;
if ( selected ) {
flags | = HistoryMessageReply : : PaintSelected ;
}
2016-04-10 18:53:01 +04:00
reply - > paint ( p , _parent , rectx , recty , rectw , flags ) ;
2016-03-25 14:29:45 +03:00
}
2015-12-19 00:36:16 +03:00
}
2015-03-19 12:18:19 +03:00
}
}
2016-04-13 21:29:32 +03:00
HistoryTextState HistorySticker : : getState ( int x , int y , HistoryStateRequest request ) const {
HistoryTextState result ;
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return result ;
2014-12-23 02:11:37 +03:00
2016-04-10 18:53:01 +04:00
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , childmedia = ( _parent - > getMedia ( ) ! = this ) ;
2014-12-23 02:11:37 +03:00
2016-04-07 14:03:10 +04:00
int usew = _maxw , usex = 0 ;
2016-04-10 18:53:01 +04:00
auto via = childmedia ? nullptr : _parent - > Get < HistoryMessageVia > ( ) ;
auto reply = childmedia ? nullptr : _parent - > Get < HistoryMessageReply > ( ) ;
2016-04-07 14:03:10 +04:00
if ( via | | reply ) {
2016-06-08 21:46:37 +03:00
usew - = additionalWidth ( via , reply ) ;
2016-02-17 19:37:21 +03:00
if ( isPost ) {
2015-12-19 21:09:24 +03:00
} else if ( out ) {
usex = _width - usew ;
}
}
if ( rtl ( ) ) usex = _width - usex - usew ;
2016-04-07 14:03:10 +04:00
if ( via | | reply ) {
int rectw = _width - usew - st : : msgReplyPadding . left ( ) ;
int recth = st : : msgReplyPadding . top ( ) + st : : msgReplyPadding . bottom ( ) ;
if ( via ) {
recth + = st : : msgServiceNameFont - > height + ( reply ? st : : msgReplyPadding . top ( ) : 0 ) ;
}
if ( reply ) {
recth + = st : : msgReplyBarSize . height ( ) ;
}
int rectx = isPost ? ( usew + st : : msgReplyPadding . left ( ) ) : ( out ? 0 : ( usew + st : : msgReplyPadding . left ( ) ) ) ;
int recty = _height - recth ;
if ( rtl ( ) ) rectx = _width - rectx - rectw ;
// Make the bottom of the rect at the same level as the bottom of the info rect.
recty - = st : : msgDateImgDelta ;
if ( via ) {
int viah = st : : msgReplyPadding . top ( ) + st : : msgServiceNameFont - > height + ( reply ? 0 : st : : msgReplyPadding . bottom ( ) ) ;
if ( x > = rectx & & y > = recty & & x < rectx + rectw & & y < recty + viah ) {
2016-04-13 21:29:32 +03:00
result . link = via - > _lnk ;
return result ;
2016-04-07 14:03:10 +04:00
}
int skip = st : : msgServiceNameFont - > height + ( reply ? 2 * st : : msgReplyPadding . top ( ) : 0 ) ;
recty + = skip ;
recth - = skip ;
}
if ( reply ) {
if ( x > = rectx & & y > = recty & & x < rectx + rectw & & y < recty + recth ) {
2016-04-13 21:29:32 +03:00
result . link = reply - > replyToLink ( ) ;
return result ;
2016-04-07 14:03:10 +04:00
}
2015-12-19 21:09:24 +03:00
}
}
2016-04-10 18:53:01 +04:00
if ( _parent - > getMedia ( ) = = this ) {
bool inDate = _parent - > pointInTime ( usex + usew , _height , x , y , InfoDisplayOverImage ) ;
2015-12-19 21:09:24 +03:00
if ( inDate ) {
2016-04-13 21:29:32 +03:00
result . cursor = HistoryInDateCursorState ;
2015-05-29 21:52:43 +03:00
}
2014-12-23 02:11:37 +03:00
}
2016-04-06 16:02:00 +04:00
int pixLeft = usex + ( usew - _pixw ) / 2 , pixTop = ( _minh - _pixh ) / 2 ;
if ( x > = pixLeft & & x < pixLeft + _pixw & & y > = pixTop & & y < pixTop + _pixh ) {
2016-04-13 21:29:32 +03:00
result . link = _packLink ;
return result ;
2016-04-06 16:02:00 +04:00
}
2016-04-13 21:29:32 +03:00
return result ;
2014-12-23 02:11:37 +03:00
}
2016-06-22 16:39:54 +03:00
QString HistorySticker : : toString ( ) const {
2015-01-05 23:17:33 +03:00
return _emoji . isEmpty ( ) ? lang ( lng_in_dlg_sticker ) : lng_in_dlg_sticker_emoji ( lt_emoji , _emoji ) ;
2014-12-23 02:11:37 +03:00
}
2016-06-22 16:39:54 +03:00
QString HistorySticker : : notificationText ( ) const {
return toString ( ) ;
}
2016-05-06 20:33:48 +03:00
TextWithEntities HistorySticker : : selectedText ( TextSelection selection ) const {
2016-04-14 14:00:23 +03:00
if ( selection ! = FullSelection ) {
2016-05-06 20:33:48 +03:00
return TextWithEntities ( ) ;
2016-04-14 14:00:23 +03:00
}
2016-06-22 16:39:54 +03:00
return { qsl ( " [ " ) + toString ( ) + qsl ( " ] " ) , EntitiesInText ( ) } ;
2014-12-23 02:11:37 +03:00
}
2016-04-10 18:53:01 +04:00
void HistorySticker : : attachToParent ( ) {
App : : regDocumentItem ( _data , _parent ) ;
2014-12-23 02:11:37 +03:00
}
2016-04-10 18:53:01 +04:00
void HistorySticker : : detachFromParent ( ) {
App : : unregDocumentItem ( _data , _parent ) ;
2015-12-19 21:09:24 +03:00
}
2015-08-31 17:27:20 +03:00
2016-04-10 18:53:01 +04:00
void HistorySticker : : updateSentMedia ( const MTPMessageMedia & media ) {
2015-12-19 21:09:24 +03:00
if ( media . type ( ) = = mtpc_messageMediaDocument ) {
2015-12-23 15:19:32 +03:00
App : : feedDocument ( media . c_messageMediaDocument ( ) . vdocument , _data ) ;
2015-12-24 22:26:28 +03:00
if ( ! _data - > data ( ) . isEmpty ( ) ) {
2016-02-12 21:18:32 +03:00
Local : : writeStickerImage ( _data - > mediaKey ( ) , _data - > data ( ) ) ;
2015-12-19 00:36:16 +03:00
}
2015-08-31 17:27:20 +03:00
}
2014-12-23 02:11:37 +03:00
}
2016-04-10 18:53:01 +04:00
bool HistorySticker : : needReSetInlineResultMedia ( const MTPMessageMedia & media ) {
return needReSetInlineResultDocument ( media , _data ) ;
}
2016-04-07 14:03:10 +04:00
int HistorySticker : : additionalWidth ( const HistoryMessageVia * via , const HistoryMessageReply * reply ) const {
int result = 0 ;
if ( via ) {
accumulate_max ( result , st : : msgReplyPadding . left ( ) + st : : msgReplyPadding . left ( ) + via - > _maxWidth + st : : msgReplyPadding . left ( ) ) ;
}
if ( reply ) {
accumulate_max ( result , st : : msgReplyPadding . left ( ) + reply - > replyToWidth ( ) ) ;
}
return result ;
}
2016-03-29 20:17:00 +03:00
void SendMessageClickHandler : : onClickImpl ( ) const {
2016-07-05 17:48:36 +03:00
Ui : : showPeerHistory ( peer ( ) - > id , ShowAtUnreadMsgId , Ui : : ShowWay : : Forward ) ;
2015-12-13 01:29:33 +03:00
}
2015-09-23 20:43:08 +03:00
2016-03-29 20:17:00 +03:00
void AddContactClickHandler : : onClickImpl ( ) const {
if ( HistoryItem * item = App : : histItemById ( peerToChannel ( peer ( ) ) , msgid ( ) ) ) {
if ( HistoryMedia * media = item - > getMedia ( ) ) {
if ( media - > type ( ) = = MediaTypeContact ) {
QString fname = static_cast < HistoryContact * > ( media ) - > fname ( ) ;
QString lname = static_cast < HistoryContact * > ( media ) - > lname ( ) ;
QString phone = static_cast < HistoryContact * > ( media ) - > phone ( ) ;
Ui : : showLayer ( new AddContactBox ( fname , lname , phone ) ) ;
2015-12-13 01:29:33 +03:00
}
}
2015-09-23 20:43:08 +03:00
}
}
2016-04-10 18:53:01 +04:00
HistoryContact : : HistoryContact ( HistoryItem * parent , int32 userId , const QString & first , const QString & last , const QString & phone ) : HistoryMedia ( parent )
2015-12-13 01:29:33 +03:00
, _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 ) ;
2015-09-23 20:43:08 +03:00
2015-12-13 01:29:33 +03:00
_phonew = st : : normalFont - > width ( _phone ) ;
}
2014-05-30 12:53:19 +04:00
2016-04-10 18:53:01 +04:00
void HistoryContact : : initDimensions ( ) {
2015-12-13 01:29:33 +03:00
_maxw = st : : msgFileMinWidth ;
2014-05-30 12:53:19 +04:00
2015-12-13 01:29:33 +03:00
_contact = _userId ? App : : userLoaded ( _userId ) : 0 ;
if ( _contact ) {
2016-03-18 13:18:30 +03:00
_contact - > loadUserpic ( ) ;
2014-05-30 12:53:19 +04:00
}
2015-12-13 01:29:33 +03:00
if ( _contact & & _contact - > contact > 0 ) {
2016-03-29 20:17:00 +03:00
_linkl . reset ( new SendMessageClickHandler ( _contact ) ) ;
2015-12-13 01:29:33 +03:00
_link = lang ( lng_profile_send_message ) . toUpper ( ) ;
} else if ( _userId ) {
2016-04-10 18:53:01 +04:00
_linkl . reset ( new AddContactClickHandler ( _parent - > history ( ) - > peer - > id , _parent - > id ) ) ;
2015-12-13 01:29:33 +03:00
_link = lang ( lng_profile_add_contact ) . toUpper ( ) ;
}
_linkw = _link . isEmpty ( ) ? 0 : st : : semiboldFont - > width ( _link ) ;
2014-05-30 12:53:19 +04:00
2015-12-13 01:29:33 +03:00
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 ( ) ;
2016-04-10 18:53:01 +04:00
_maxw = qMax ( _maxw , tleft + _phonew + _parent - > skipBlockWidth ( ) + st : : msgPadding . right ( ) ) ;
2014-05-30 12:53:19 +04:00
}
2015-12-13 01:29:33 +03:00
_maxw = qMax ( tleft + _name . maxWidth ( ) + tright , _maxw ) ;
_maxw = qMin ( _maxw , int ( st : : msgMaxWidth ) ) ;
if ( _userId ) {
2015-12-19 21:09:24 +03:00
_minh = st : : msgFileThumbPadding . top ( ) + st : : msgFileThumbSize + st : : msgFileThumbPadding . bottom ( ) ;
2016-05-14 20:28:35 +03:00
if ( _parent - > Has < HistoryMessageSigned > ( ) ) {
_minh + = st : : msgDateFont - > height - st : : msgDateDelta . y ( ) ;
}
2015-12-13 01:29:33 +03:00
} else {
2015-12-19 21:09:24 +03:00
_minh = st : : msgFilePadding . top ( ) + st : : msgFileSize + st : : msgFilePadding . bottom ( ) ;
2015-12-13 01:29:33 +03:00
}
2015-12-19 21:09:24 +03:00
_height = _minh ;
2014-05-30 12:53:19 +04:00
}
2016-04-13 21:29:32 +03:00
void HistoryContact : : draw ( Painter & p , const QRect & r , TextSelection selection , uint64 ms ) const {
2015-12-19 21:09:24 +03:00
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return ;
int32 skipx = 0 , skipy = 0 , width = _width , height = _height ;
2015-03-19 12:18:19 +03:00
2016-04-10 18:53:01 +04:00
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , outbg = out & & ! isPost ;
2016-04-13 21:29:32 +03:00
bool selected = ( selection = = FullSelection ) ;
2015-12-13 01:29:33 +03:00
2014-05-30 12:53:19 +04:00
if ( width > = _maxw ) {
width = _maxw ;
}
2015-12-13 01:29:33 +03:00
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 ;
2014-05-30 12:53:19 +04:00
2015-12-13 01:29:33 +03:00
QRect rthumb ( rtlrect ( st : : msgFileThumbPadding . left ( ) , st : : msgFileThumbPadding . top ( ) , st : : msgFileThumbSize , st : : msgFileThumbSize , width ) ) ;
2016-03-18 13:18:30 +03:00
if ( _contact ) {
_contact - > paintUserpic ( p , st : : msgFileThumbSize , rthumb . x ( ) , rthumb . y ( ) ) ;
2015-12-13 01:29:33 +03:00
} else {
2016-03-18 13:18:30 +03:00
p . drawPixmap ( rthumb . topLeft ( ) , userDefPhoto ( qAbs ( _userId ) % UserColorsCount ) - > pixCircled ( st : : msgFileThumbSize , st : : msgFileThumbSize ) ) ;
2015-12-13 01:29:33 +03:00
}
if ( selected ) {
2016-07-07 15:35:14 +03:00
App : : roundRect ( p , rthumb , textstyleCurrent ( ) - > selectOverlay , SelectedOverlaySmallCorners ) ;
2015-12-13 01:29:33 +03:00
}
2014-05-30 12:53:19 +04:00
2016-03-29 20:17:00 +03:00
bool over = ClickHandler : : showAsActive ( _linkl ) ;
2015-12-13 01:29:33 +03:00
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 ) ;
2014-05-30 12:53:19 +04:00
} else {
2015-12-13 01:29:33 +03:00
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 ) ) ;
2016-04-10 18:53:01 +04:00
p . drawPixmap ( inner . topLeft ( ) , userDefPhoto ( qAbs ( _parent - > id ) % UserColorsCount ) - > pixCircled ( st : : msgFileSize , st : : msgFileSize ) ) ;
2014-05-30 12:53:19 +04:00
}
2015-12-13 01:29:33 +03:00
int32 namewidth = width - nameleft - nameright ;
2014-05-30 12:53:19 +04:00
2015-12-13 01:29:33 +03:00
p . setFont ( st : : semiboldFont ) ;
p . setPen ( st : : black ) ;
_name . drawLeftElided ( p , nameleft , nametop , namewidth , width ) ;
2015-12-19 21:09:24 +03:00
2015-12-11 21:11:38 +03:00
style : : color status ( outbg ? ( selected ? st : : mediaOutFgSelected : st : : mediaOutFg ) : ( selected ? st : : mediaInFgSelected : st : : mediaInFg ) ) ;
2015-12-13 01:29:33 +03:00
p . setFont ( st : : normalFont ) ;
p . setPen ( status ) ;
p . drawTextLeft ( nameleft , statustop , width , _phone ) ;
2014-05-30 12:53:19 +04:00
}
2016-04-13 21:29:32 +03:00
HistoryTextState HistoryContact : : getState ( int x , int y , HistoryStateRequest request ) const {
HistoryTextState result ;
2016-04-10 18:53:01 +04:00
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , outbg = out & & ! isPost ;
2015-12-19 21:09:24 +03:00
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 ) ) {
2016-04-13 21:29:32 +03:00
result . link = _linkl ;
return result ;
2015-12-19 21:09:24 +03:00
}
}
if ( x > = 0 & & y > = 0 & & x < _width & & y < _height & & _contact ) {
2016-04-13 21:29:32 +03:00
result . link = _contact - > openLink ( ) ;
return result ;
2015-12-19 21:09:24 +03:00
}
2016-04-13 21:29:32 +03:00
return result ;
2015-12-19 21:09:24 +03:00
}
2016-06-22 16:39:54 +03:00
QString HistoryContact : : notificationText ( ) const {
2015-12-19 21:09:24 +03:00
return lang ( lng_in_dlg_contact ) ;
}
2016-05-06 20:33:48 +03:00
TextWithEntities HistoryContact : : selectedText ( TextSelection selection ) const {
2016-04-14 14:00:23 +03:00
if ( selection ! = FullSelection ) {
2016-05-06 20:33:48 +03:00
return TextWithEntities ( ) ;
2016-04-14 14:00:23 +03:00
}
2016-05-06 20:33:48 +03:00
return { qsl ( " [ " ) + lang ( lng_in_dlg_contact ) + qsl ( " ] \n " ) + _name . originalText ( ) + ' \n ' + _phone , EntitiesInText ( ) } ;
2015-12-19 21:09:24 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryContact : : attachToParent ( ) {
2015-12-19 21:09:24 +03:00
if ( _userId ) {
2016-04-10 18:53:01 +04:00
App : : regSharedContactItem ( _userId , _parent ) ;
2015-12-19 21:09:24 +03:00
}
}
2016-04-10 18:53:01 +04:00
void HistoryContact : : detachFromParent ( ) {
2015-12-19 21:09:24 +03:00
if ( _userId ) {
2016-04-10 18:53:01 +04:00
App : : unregSharedContactItem ( _userId , _parent ) ;
2015-12-19 21:09:24 +03:00
}
}
2016-04-10 18:53:01 +04:00
void HistoryContact : : updateSentMedia ( const MTPMessageMedia & media ) {
2014-08-22 18:55:23 +04:00
if ( media . type ( ) = = mtpc_messageMediaContact ) {
2015-12-13 01:29:33 +03:00
if ( _userId ! = media . c_messageMediaContact ( ) . vuser_id . v ) {
2016-04-10 18:53:01 +04:00
detachFromParent ( ) ;
2015-12-13 01:29:33 +03:00
_userId = media . c_messageMediaContact ( ) . vuser_id . v ;
2016-04-10 18:53:01 +04:00
attachToParent ( ) ;
2014-08-22 18:55:23 +04:00
}
}
}
2015-12-19 00:36:16 +03:00
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 ;
}
2016-04-10 18:53:01 +04:00
HistoryWebPage : : HistoryWebPage ( HistoryItem * parent , WebPageData * data ) : HistoryMedia ( parent )
2015-12-19 00:36:16 +03:00
, _data ( data )
2015-09-01 16:50:56 +03:00
, _openl ( 0 )
2016-04-10 18:53:01 +04:00
, _attach ( nullptr )
2015-04-04 23:01:34 +03:00
, _asArticle ( false )
, _title ( st : : msgMinWidth - st : : webPageLeft )
, _description ( st : : msgMinWidth - st : : webPageLeft )
, _siteNameWidth ( 0 )
, _durationWidth ( 0 )
2015-12-19 00:36:16 +03:00
, _pixw ( 0 )
, _pixh ( 0 ) {
}
2016-04-10 18:53:01 +04:00
HistoryWebPage : : HistoryWebPage ( HistoryItem * parent , const HistoryWebPage & other ) : HistoryMedia ( parent )
2015-12-19 00:36:16 +03:00
, _data ( other . _data )
, _openl ( 0 )
2016-04-10 18:53:01 +04:00
, _attach ( other . _attach ? other . _attach - > clone ( parent ) : nullptr )
2015-12-19 00:36:16 +03:00
, _asArticle ( other . _asArticle )
, _title ( other . _title )
, _description ( other . _description )
, _siteNameWidth ( other . _siteNameWidth )
, _durationWidth ( other . _durationWidth )
, _pixw ( other . _pixw )
, _pixh ( other . _pixh ) {
2015-04-04 23:01:34 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryWebPage : : initDimensions ( ) {
2015-12-19 00:36:16 +03:00
if ( _data - > pendingTill ) {
2015-05-20 22:28:24 +03:00
_maxw = _minh = _height = 0 ;
2015-04-04 23:01:34 +03:00
return ;
}
2015-12-19 00:36:16 +03:00
if ( ! _lineHeight ) _lineHeight = qMax ( st : : webPageTitleFont - > height , st : : webPageDescriptionFont - > height ) ;
2016-03-29 20:17:00 +03:00
if ( ! _openl & & ! _data - > url . isEmpty ( ) ) _openl . reset ( new UrlClickHandler ( _data - > url , true ) ) ;
2015-12-19 00:36:16 +03:00
// 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 ) ;
}
2016-04-08 13:20:10 +04:00
if ( ! _data - > document & & _data - > photo & & _data - > type ! = WebPagePhoto & & _data - > type ! = WebPageVideo ) {
2015-12-19 00:36:16 +03:00
if ( _data - > type = = WebPageProfile ) {
2015-04-04 23:01:34 +03:00
_asArticle = true ;
2015-12-19 00:36:16 +03:00
} else if ( _data - > siteName = = qstr ( " Twitter " ) | | _data - > siteName = = qstr ( " Facebook " ) ) {
2015-04-04 23:01:34 +03:00
_asArticle = false ;
} else {
_asArticle = true ;
}
2016-02-12 19:35:06 +03:00
if ( _asArticle & & _data - > description . isEmpty ( ) & & title . isEmpty ( ) & & _data - > siteName . isEmpty ( ) ) {
2015-12-19 00:36:16 +03:00
_asArticle = false ;
}
2015-04-04 23:01:34 +03:00
} else {
_asArticle = false ;
}
2015-09-01 16:50:56 +03:00
2015-12-19 00:36:16 +03:00
// init attach
if ( ! _asArticle & & ! _attach ) {
2016-04-08 13:20:10 +04:00
if ( _data - > document ) {
if ( _data - > document - > sticker ( ) ) {
2016-04-10 18:53:01 +04:00
_attach = new HistorySticker ( _parent , _data - > document ) ;
2016-04-08 13:20:10 +04:00
} else if ( _data - > document - > isAnimation ( ) ) {
2016-04-10 18:53:01 +04:00
_attach = new HistoryGif ( _parent , _data - > document , QString ( ) ) ;
2016-04-08 13:20:10 +04:00
} else if ( _data - > document - > isVideo ( ) ) {
2016-04-10 18:53:01 +04:00
_attach = new HistoryVideo ( _parent , _data - > document , QString ( ) ) ;
2015-09-01 16:50:56 +03:00
} else {
2016-04-10 18:53:01 +04:00
_attach = new HistoryDocument ( _parent , _data - > document , QString ( ) ) ;
2015-09-01 16:50:56 +03:00
}
2015-12-19 00:36:16 +03:00
} else if ( _data - > photo ) {
2016-04-10 18:53:01 +04:00
_attach = new HistoryPhoto ( _parent , _data - > photo , QString ( ) ) ;
2015-09-01 16:50:56 +03:00
}
2015-04-04 23:01:34 +03:00
}
2015-12-19 00:36:16 +03:00
// init strings
if ( _description . isEmpty ( ) & & ! _data - > description . isEmpty ( ) ) {
QString text = textClean ( _data - > description ) ;
if ( text . isEmpty ( ) ) {
_data - > description = QString ( ) ;
2015-04-04 23:01:34 +03:00
} else {
2015-12-19 00:36:16 +03:00
if ( ! _asArticle & & ! _attach ) {
2016-04-10 18:53:01 +04:00
text + = _parent - > skipBlock ( ) ;
2015-12-19 00:36:16 +03:00
}
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 ) ;
2015-04-04 23:01:34 +03:00
}
}
2015-12-19 00:36:16 +03:00
if ( _title . isEmpty ( ) & & ! title . isEmpty ( ) ) {
title = textOneLine ( textClean ( title ) ) ;
if ( title . isEmpty ( ) ) {
if ( _data - > title . isEmpty ( ) ) {
_data - > author = QString ( ) ;
} else {
_data - > title = QString ( ) ;
}
2015-04-04 23:01:34 +03:00
} else {
2015-12-19 00:36:16 +03:00
if ( ! _asArticle & & ! _attach & & _description . isEmpty ( ) ) {
2016-04-10 18:53:01 +04:00
title + = _parent - > skipBlock ( ) ;
2015-12-19 00:36:16 +03:00
}
_title . setText ( st : : webPageTitleFont , title , _webpageTitleOptions ) ;
2015-04-04 23:01:34 +03:00
}
}
2015-12-19 00:36:16 +03:00
if ( ! _siteNameWidth & & ! _data - > siteName . isEmpty ( ) ) {
_siteNameWidth = st : : webPageTitleFont - > width ( _data - > siteName ) ;
}
// init dimensions
int32 l = st : : msgPadding . left ( ) + st : : webPageLeft , r = st : : msgPadding . right ( ) ;
2016-04-10 18:53:01 +04:00
int32 skipBlockWidth = _parent - > skipBlockWidth ( ) ;
2015-12-19 00:36:16 +03:00
_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 ( ) ) {
2016-04-10 18:53:01 +04:00
_maxw = qMax ( _maxw , int32 ( _siteNameWidth + _parent - > skipBlockWidth ( ) ) ) ;
2015-04-04 23:01:34 +03:00
} else {
2015-12-19 00:36:16 +03:00
_maxw = qMax ( _maxw , int32 ( _siteNameWidth + articlePhotoMaxWidth ) ) ;
2015-04-04 23:01:34 +03:00
}
2015-12-19 00:36:16 +03:00
_minh + = _lineHeight ;
2015-04-04 23:01:34 +03:00
}
2015-12-19 00:36:16 +03:00
if ( ! _title . isEmpty ( ) ) {
_maxw = qMax ( _maxw , int32 ( _title . maxWidth ( ) + articlePhotoMaxWidth ) ) ;
_minh + = titleMinHeight ;
2015-04-04 23:01:34 +03:00
}
2015-12-19 00:36:16 +03:00
if ( ! _description . isEmpty ( ) ) {
_maxw = qMax ( _maxw , int32 ( _description . maxWidth ( ) + articlePhotoMaxWidth ) ) ;
_minh + = descriptionMinHeight ;
}
if ( _attach ) {
if ( _minh ) _minh + = st : : webPagePhotoSkip ;
2016-04-10 18:53:01 +04:00
_attach - > initDimensions ( ) ;
2015-12-19 00:36:16 +03:00
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 ) ;
2015-10-03 13:09:09 +03:00
_durationWidth = st : : msgDateFont - > width ( _duration ) ;
2015-04-04 23:01:34 +03:00
}
2015-12-19 00:36:16 +03:00
_maxw + = st : : msgPadding . left ( ) + st : : webPageLeft + st : : msgPadding . right ( ) ;
_minh + = st : : msgPadding . bottom ( ) ;
if ( _asArticle ) {
2016-04-10 18:53:01 +04:00
_minh = resizeGetHeight ( _maxw ) ; // hack
2015-12-28 00:37:48 +03:00
// _minh += st::msgDateFont->height;
2015-12-19 00:36:16 +03:00
}
}
2015-08-30 17:57:21 +03:00
2016-04-10 18:53:01 +04:00
int HistoryWebPage : : resizeGetHeight ( int width ) {
2015-12-19 00:36:16 +03:00
if ( _data - > pendingTill ) {
2015-12-19 21:09:24 +03:00
_width = width ;
2015-12-19 00:36:16 +03:00
_height = _minh ;
return _height ;
}
2015-08-30 17:57:21 +03:00
2015-12-19 21:09:24 +03:00
_width = qMin ( width , _maxw ) ;
2015-12-19 00:36:16 +03:00
width - = st : : msgPadding . left ( ) + st : : webPageLeft + st : : msgPadding . right ( ) ;
2015-08-30 17:57:21 +03:00
2015-12-19 00:36:16 +03:00
int32 linesMax = 5 ;
int32 siteNameLines = _siteNameWidth ? 1 : 0 , siteNameHeight = _siteNameWidth ? _lineHeight : 0 ;
if ( _asArticle ) {
_pixh = linesMax * _lineHeight ;
do {
_pixw = articleThumbWidth ( _data - > photo , _pixh ) ;
int32 wleft = width - st : : webPagePhotoDelta - qMax ( _pixw , int16 ( _lineHeight ) ) ;
_height = siteNameHeight ;
2015-08-30 17:57:21 +03:00
2015-12-19 00:36:16 +03:00
if ( _title . isEmpty ( ) ) {
_titleLines = 0 ;
} else {
if ( _title . countHeight ( wleft ) < 2 * st : : webPageTitleFont - > height ) {
_titleLines = 1 ;
2015-08-30 17:57:21 +03:00
} else {
2015-12-19 00:36:16 +03:00
_titleLines = 2 ;
2015-08-30 17:57:21 +03:00
}
2015-12-19 00:36:16 +03:00
_height + = _titleLines * _lineHeight ;
2015-08-30 17:57:21 +03:00
}
2015-12-19 00:36:16 +03:00
int32 descriptionHeight = _description . countHeight ( wleft ) ;
if ( descriptionHeight < ( linesMax - siteNameLines - _titleLines ) * st : : webPageDescriptionFont - > height ) {
_descriptionLines = ( descriptionHeight / st : : webPageDescriptionFont - > height ) ;
2015-08-30 17:57:21 +03:00
} else {
2015-12-19 00:36:16 +03:00
_descriptionLines = ( linesMax - siteNameLines - _titleLines ) ;
2015-08-30 17:57:21 +03:00
}
2015-12-19 00:36:16 +03:00
_height + = _descriptionLines * _lineHeight ;
2015-08-30 17:57:21 +03:00
2015-12-19 00:36:16 +03:00
if ( _height > = _pixh ) {
break ;
}
2015-04-04 23:01:34 +03:00
2015-12-19 00:36:16 +03:00
_pixh - = _lineHeight ;
} while ( _pixh > _lineHeight ) ;
_height + = st : : msgDateFont - > height ;
} else {
_height = siteNameHeight ;
2015-04-04 23:01:34 +03:00
2015-12-19 00:36:16 +03:00
if ( _title . isEmpty ( ) ) {
_titleLines = 0 ;
} else {
if ( _title . countHeight ( width ) < 2 * st : : webPageTitleFont - > height ) {
_titleLines = 1 ;
} else {
_titleLines = 2 ;
2015-04-04 23:01:34 +03:00
}
2015-12-19 00:36:16 +03:00
_height + = _titleLines * _lineHeight ;
2015-04-04 23:01:34 +03:00
}
2015-12-19 00:36:16 +03:00
if ( _description . isEmpty ( ) ) {
_descriptionLines = 0 ;
2015-04-04 23:01:34 +03:00
} else {
2015-12-19 21:09:24 +03:00
int32 descriptionHeight = _description . countHeight ( width ) ;
if ( descriptionHeight < ( linesMax - siteNameLines - _titleLines ) * st : : webPageDescriptionFont - > height ) {
_descriptionLines = ( descriptionHeight / st : : webPageDescriptionFont - > height ) ;
} else {
_descriptionLines = ( linesMax - siteNameLines - _titleLines ) ;
}
_height + = _descriptionLines * _lineHeight ;
}
if ( _attach ) {
if ( _height ) _height + = st : : webPagePhotoSkip ;
QMargins bubble ( _attach - > bubbleMargins ( ) ) ;
2016-04-10 18:53:01 +04:00
_attach - > resizeGetHeight ( width + bubble . left ( ) + bubble . right ( ) ) ;
2015-12-19 21:09:24 +03:00
_height + = _attach - > height ( ) - bubble . top ( ) - bubble . bottom ( ) ;
2016-04-10 18:53:01 +04:00
if ( _attach - > customInfoLayout ( ) & & _attach - > currentWidth ( ) + _parent - > skipBlockWidth ( ) > width + bubble . left ( ) + bubble . right ( ) ) {
2015-12-19 21:09:24 +03:00
_height + = st : : msgDateFont - > height ;
}
}
}
_height + = st : : msgPadding . bottom ( ) ;
return _height ;
}
2016-04-13 21:29:32 +03:00
void HistoryWebPage : : draw ( Painter & p , const QRect & r , TextSelection selection , uint64 ms ) const {
2015-12-19 21:09:24 +03:00
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return ;
int32 skipx = 0 , skipy = 0 , width = _width , height = _height ;
2016-04-10 18:53:01 +04:00
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , outbg = out & & ! isPost ;
2016-04-13 21:29:32 +03:00
bool selected = ( selection = = FullSelection ) ;
2015-12-19 21:09:24 +03:00
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 ( ) ) ;
2016-04-10 18:53:01 +04:00
if ( _asArticle | | ( _attach & & _attach - > customInfoLayout ( ) & & _attach - > currentWidth ( ) + _parent - > skipBlockWidth ( ) > width + bubble . left ( ) + bubble . right ( ) ) ) {
2015-12-19 21:09:24 +03:00
bshift + = st : : msgDateFont - > height ;
}
2015-12-31 23:27:21 +08:00
2015-12-19 21:09:24 +03:00
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 ;
2015-12-30 08:15:22 +08:00
int32 pw = qMax ( _pixw , int16 ( _lineHeight ) ) , ph = _pixh ;
int32 pixw = _pixw , pixh = articleThumbHeight ( _data - > photo , _pixw ) ;
int32 maxw = convertScale ( _data - > photo - > medium - > width ( ) ) , maxh = convertScale ( _data - > photo - > medium - > height ( ) ) ;
if ( pixw * ph ! = pixh * pw ) {
float64 coef = ( pixw * ph > pixh * pw ) ? qMin ( ph / float64 ( pixh ) , maxh / float64 ( pixh ) ) : qMin ( pw / float64 ( pixw ) , maxw / float64 ( pixw ) ) ;
pixh = qRound ( pixh * coef ) ;
pixw = qRound ( pixw * coef ) ;
}
2015-12-19 21:09:24 +03:00
if ( full ) {
2016-07-07 15:35:14 +03:00
pix = _data - > photo - > medium - > pixSingle ( ImageRoundRadius : : Small , pixw , pixh , pw , ph ) ;
2015-12-19 21:09:24 +03:00
} else {
2016-07-07 15:35:14 +03:00
pix = _data - > photo - > thumb - > pixBlurredSingle ( ImageRoundRadius : : Small , pixw , pixh , pw , ph ) ;
2015-08-30 17:57:21 +03:00
}
2015-12-19 21:09:24 +03:00
p . drawPixmapLeft ( lshift + width - pw , 0 , _width , pix ) ;
if ( selected ) {
2016-07-07 15:35:14 +03:00
App : : roundRect ( p , rtlrect ( lshift + width - pw , 0 , pw , _pixh , _width ) , textstyleCurrent ( ) - > selectOverlay , SelectedOverlaySmallCorners ) ;
2015-04-04 23:01:34 +03:00
}
2015-12-19 21:09:24 +03:00
width - = pw + st : : webPagePhotoDelta ;
2015-04-04 23:01:34 +03:00
}
2015-12-19 21:09:24 +03:00
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 ( ) ) {
2016-04-10 18:53:01 +04:00
endskip = _parent - > skipBlockWidth ( ) ;
2015-12-19 21:09:24 +03:00
}
2016-04-13 21:29:32 +03:00
_title . drawLeftElided ( p , lshift , tshift , width , _width , _titleLines , style : : al_left , 0 , - 1 , endskip , false , selection ) ;
2015-12-19 21:09:24 +03:00
tshift + = _titleLines * _lineHeight ;
}
if ( _descriptionLines ) {
p . setPen ( st : : black ) ;
int32 endskip = 0 ;
if ( _description . hasSkipBlock ( ) ) {
2016-04-10 18:53:01 +04:00
endskip = _parent - > skipBlockWidth ( ) ;
2015-12-19 21:09:24 +03:00
}
2016-04-13 21:29:32 +03:00
_description . drawLeftElided ( p , lshift , tshift , width , _width , _descriptionLines , style : : al_left , 0 , - 1 , endskip , false , toDescriptionSelection ( selection ) ) ;
2015-12-19 21:09:24 +03:00
tshift + = _descriptionLines * _lineHeight ;
}
if ( _attach ) {
if ( tshift ) tshift + = st : : webPagePhotoSkip ;
2015-04-04 23:01:34 +03:00
2015-12-19 21:09:24 +03:00
int32 attachLeft = lshift - bubble . left ( ) , attachTop = tshift - bubble . top ( ) ;
if ( rtl ( ) ) attachLeft = _width - attachLeft - _attach - > currentWidth ( ) ;
2015-04-04 23:01:34 +03:00
2015-12-19 21:09:24 +03:00
p . save ( ) ;
p . translate ( attachLeft , attachTop ) ;
2015-04-04 23:01:34 +03:00
2016-04-13 21:29:32 +03:00
auto attachSelection = selected ? FullSelection : TextSelection { 0 , 0 } ;
_attach - > draw ( p , r . translated ( - attachLeft , - attachTop ) , attachSelection , ms ) ;
2015-12-19 21:09:24 +03:00
int32 pixwidth = _attach - > currentWidth ( ) , pixheight = _attach - > height ( ) ;
2015-12-31 23:27:21 +08:00
2016-05-19 18:02:07 +03:00
if ( _data - > type = = WebPageVideo & & _attach - > type ( ) = = MediaTypePhoto ) {
2015-12-19 21:09:24 +03:00
if ( _data - > siteName = = qstr ( " YouTube " ) ) {
2016-04-21 20:57:29 +03:00
p . drawSprite ( QPoint ( ( pixwidth - st : : youtubeIcon . pxWidth ( ) ) / 2 , ( pixheight - st : : youtubeIcon . pxHeight ( ) ) / 2 ) , st : : youtubeIcon ) ;
2015-12-19 21:09:24 +03:00
} else {
2016-04-21 20:57:29 +03:00
p . drawSprite ( QPoint ( ( pixwidth - st : : videoIcon . pxWidth ( ) ) / 2 , ( pixheight - st : : videoIcon . pxHeight ( ) ) / 2 ) , st : : videoIcon ) ;
2015-12-19 21:09:24 +03:00
}
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 ;
2015-04-04 23:01:34 +03:00
2015-12-19 21:09:24 +03:00
App : : roundRect ( p , dateX , dateY , dateW , dateH , selected ? st : : msgDateImgBgSelected : st : : msgDateImgBg , selected ? DateSelectedCorners : DateCorners ) ;
2015-04-04 23:01:34 +03:00
2015-12-19 21:09:24 +03:00
p . setFont ( st : : msgDateFont ) ;
p . setPen ( st : : msgDateImgColor ) ;
p . drawTextLeft ( dateX + st : : msgDateImgPadding . x ( ) , dateY + st : : msgDateImgPadding . y ( ) , pixwidth , _duration ) ;
}
}
2015-04-04 23:01:34 +03:00
2015-12-19 21:09:24 +03:00
p . restore ( ) ;
}
2015-04-04 23:01:34 +03:00
}
2016-04-13 21:29:32 +03:00
HistoryTextState HistoryWebPage : : getState ( int x , int y , HistoryStateRequest request ) const {
HistoryTextState result ;
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return result ;
2015-12-19 21:09:24 +03:00
int32 skipx = 0 , skipy = 0 , width = _width , height = _height ;
2015-04-04 23:01:34 +03:00
2015-12-19 00:36:16 +03:00
int32 lshift = st : : msgPadding . left ( ) + st : : webPageLeft , rshift = st : : msgPadding . right ( ) , bshift = st : : msgPadding . bottom ( ) ;
width - = lshift + rshift ;
QMargins bubble ( _attach ? _attach - > bubbleMargins ( ) : QMargins ( ) ) ;
2016-04-10 18:53:01 +04:00
if ( _asArticle | | ( _attach & & _attach - > customInfoLayout ( ) & & _attach - > currentWidth ( ) + _parent - > skipBlockWidth ( ) > width + bubble . left ( ) + bubble . right ( ) ) ) {
2015-12-19 00:36:16 +03:00
bshift + = st : : msgDateFont - > height ;
}
2015-04-04 23:01:34 +03:00
2016-04-13 21:29:32 +03:00
bool inThumb = false ;
2015-04-08 02:03:32 +03:00
if ( _asArticle ) {
2015-12-19 00:36:16 +03:00
int32 pw = qMax ( _pixw , int16 ( _lineHeight ) ) ;
2015-12-19 21:09:24 +03:00
if ( rtlrect ( lshift + width - pw , 0 , pw , _pixh , _width ) . contains ( x , y ) ) {
2016-04-13 21:29:32 +03:00
inThumb = true ;
2015-04-04 23:01:34 +03:00
}
2015-12-19 00:36:16 +03:00
width - = pw + st : : webPagePhotoDelta ;
2015-04-08 02:03:32 +03:00
}
2016-04-13 21:29:32 +03:00
int tshift = 0 , symbolAdd = 0 ;
2015-04-08 02:03:32 +03:00
if ( _siteNameWidth ) {
2015-12-19 00:36:16 +03:00
tshift + = _lineHeight ;
2015-04-08 02:03:32 +03:00
}
2015-12-19 00:36:16 +03:00
if ( _titleLines ) {
2016-04-13 21:29:32 +03:00
if ( y > = tshift & & y < tshift + _titleLines * _lineHeight ) {
Text : : StateRequestElided titleRequest = request . forText ( ) ;
titleRequest . lines = _titleLines ;
result = _title . getStateElidedLeft ( x - lshift , y - tshift , width , _width , titleRequest ) ;
} else if ( y > = tshift + _titleLines * _lineHeight ) {
symbolAdd + = _title . length ( ) ;
}
2015-12-19 00:36:16 +03:00
tshift + = _titleLines * _lineHeight ;
2015-04-08 02:03:32 +03:00
}
2015-12-19 00:36:16 +03:00
if ( _descriptionLines ) {
if ( y > = tshift & & y < tshift + _descriptionLines * _lineHeight ) {
2016-04-13 21:29:32 +03:00
Text : : StateRequestElided descriptionRequest = request . forText ( ) ;
descriptionRequest . lines = _descriptionLines ;
result = _description . getStateElidedLeft ( x - lshift , y - tshift , width , _width , descriptionRequest ) ;
} else if ( y > = tshift + _descriptionLines * _lineHeight ) {
symbolAdd + = _description . length ( ) ;
2015-04-08 02:03:32 +03:00
}
2015-12-19 00:36:16 +03:00
tshift + = _descriptionLines * _lineHeight ;
2015-04-08 02:03:32 +03:00
}
2016-04-13 21:29:32 +03:00
if ( inThumb ) {
result . link = _openl ;
} else if ( _attach ) {
2015-12-19 00:36:16 +03:00
if ( tshift ) tshift + = st : : webPagePhotoSkip ;
if ( x > = lshift & & x < lshift + width & & y > = tshift & & y < _height - st : : msgPadding . bottom ( ) ) {
2015-12-19 15:27:03 +03:00
int32 attachLeft = lshift - bubble . left ( ) , attachTop = tshift - bubble . top ( ) ;
2015-12-19 21:09:24 +03:00
if ( rtl ( ) ) attachLeft = _width - attachLeft - _attach - > currentWidth ( ) ;
2016-04-13 21:29:32 +03:00
result = _attach - > getState ( x - attachLeft , y - attachTop , request ) ;
if ( result . link & & ! _data - > document & & _data - > photo ) {
2015-12-23 14:13:08 +03:00
if ( _data - > type = = WebPageProfile | | _data - > type = = WebPageVideo ) {
2016-04-13 21:29:32 +03:00
result . link = _openl ;
2015-12-23 14:13:08 +03:00
} else if ( _data - > type = = WebPagePhoto | | _data - > siteName = = qstr ( " Twitter " ) | | _data - > siteName = = qstr ( " Facebook " ) ) {
// leave photo link
} else {
2016-04-13 21:29:32 +03:00
result . link = _openl ;
2015-12-23 14:13:08 +03:00
}
}
2015-04-04 23:01:34 +03:00
}
}
2016-04-13 21:29:32 +03:00
result . symbol + = symbolAdd ;
return result ;
}
TextSelection HistoryWebPage : : adjustSelection ( TextSelection selection , TextSelectType type ) const {
if ( ! _descriptionLines | | selection . to < = _title . length ( ) ) {
return _title . adjustSelection ( selection , type ) ;
}
auto descriptionSelection = _description . adjustSelection ( toDescriptionSelection ( selection ) , type ) ;
if ( selection . from > = _title . length ( ) ) {
return fromDescriptionSelection ( descriptionSelection ) ;
}
auto titleSelection = _title . adjustSelection ( selection , type ) ;
return { titleSelection . from , fromDescriptionSelection ( descriptionSelection ) . to } ;
2015-04-04 23:01:34 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryWebPage : : clickHandlerActiveChanged ( const ClickHandlerPtr & p , bool active ) {
2015-12-19 21:09:24 +03:00
if ( _attach ) {
2016-04-10 18:53:01 +04:00
_attach - > clickHandlerActiveChanged ( p , active ) ;
2015-12-19 21:09:24 +03:00
}
}
2016-04-10 18:53:01 +04:00
void HistoryWebPage : : clickHandlerPressedChanged ( const ClickHandlerPtr & p , bool pressed ) {
2015-12-19 21:09:24 +03:00
if ( _attach ) {
2016-04-10 18:53:01 +04:00
_attach - > clickHandlerPressedChanged ( p , pressed ) ;
2015-12-19 21:09:24 +03:00
}
}
2016-04-10 18:53:01 +04:00
void HistoryWebPage : : attachToParent ( ) {
App : : regWebPageItem ( _data , _parent ) ;
if ( _attach ) _attach - > attachToParent ( ) ;
2015-12-19 21:09:24 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryWebPage : : detachFromParent ( ) {
App : : unregWebPageItem ( _data , _parent ) ;
if ( _attach ) _attach - > detachFromParent ( ) ;
2015-12-19 21:09:24 +03:00
}
2016-05-06 20:33:48 +03:00
TextWithEntities HistoryWebPage : : selectedText ( TextSelection selection ) const {
2016-04-14 14:00:23 +03:00
if ( selection = = FullSelection ) {
2016-05-06 20:33:48 +03:00
return TextWithEntities ( ) ;
2016-04-14 14:00:23 +03:00
}
2016-05-06 20:33:48 +03:00
auto titleResult = _title . originalTextWithEntities ( selection , ExpandLinksAll ) ;
auto descriptionResult = _description . originalTextWithEntities ( toDescriptionSelection ( selection ) , ExpandLinksAll ) ;
if ( titleResult . text . isEmpty ( ) ) {
2016-04-14 14:00:23 +03:00
return descriptionResult ;
2016-05-06 20:33:48 +03:00
} else if ( descriptionResult . text . isEmpty ( ) ) {
2016-04-14 14:00:23 +03:00
return titleResult ;
}
2016-05-06 20:33:48 +03:00
titleResult . text + = ' \n ' ;
appendTextWithEntities ( titleResult , std_ : : move ( descriptionResult ) ) ;
return titleResult ;
2015-04-04 23:01:34 +03:00
}
ImagePtr HistoryWebPage : : replyPreview ( ) {
2015-12-28 00:37:48 +03:00
return _attach ? _attach - > replyPreview ( ) : ( _data - > photo ? _data - > photo - > makeReplyPreview ( ) : ImagePtr ( ) ) ;
}
HistoryWebPage : : ~ HistoryWebPage ( ) {
2015-12-29 00:20:04 +03:00
deleteAndMark ( _attach ) ;
2015-04-04 23:01:34 +03:00
}
2015-12-31 23:28:54 +08:00
namespace {
2016-07-13 20:34:57 +03:00
LocationManager * locationManager = nullptr ;
2015-12-31 23:28:54 +08:00
}
2016-02-28 16:54:04 +03:00
void LocationManager : : init ( ) {
2015-12-31 23:28:54 +08:00
if ( manager ) delete manager ;
manager = new QNetworkAccessManager ( ) ;
App : : setProxySettings ( * manager ) ;
connect ( manager , SIGNAL ( authenticationRequired ( QNetworkReply * , QAuthenticator * ) ) , this , SLOT ( onFailed ( QNetworkReply * ) ) ) ;
2016-09-15 14:08:45 +03:00
# ifndef OS_MAC_OLD
2015-12-31 23:28:54 +08:00
connect ( manager , SIGNAL ( sslErrors ( QNetworkReply * , const QList < QSslError > & ) ) , this , SLOT ( onFailed ( QNetworkReply * ) ) ) ;
2016-09-15 14:08:45 +03:00
# endif // OS_MAC_OLD
2015-12-31 23:28:54 +08:00
connect ( manager , SIGNAL ( finished ( QNetworkReply * ) ) , this , SLOT ( onFinished ( QNetworkReply * ) ) ) ;
2016-07-13 20:34:57 +03:00
if ( black ) {
delete black - > v ( ) ;
delete black ;
}
2015-12-31 23:28:54 +08:00
QImage b ( cIntRetinaFactor ( ) , cIntRetinaFactor ( ) , QImage : : Format_ARGB32_Premultiplied ) ;
{
QPainter p ( & b ) ;
p . fillRect ( QRect ( 0 , 0 , cIntRetinaFactor ( ) , cIntRetinaFactor ( ) ) , st : : white - > b ) ;
}
2016-07-13 20:34:57 +03:00
QPixmap p = App : : pixmapFromImageInPlace ( std_ : : move ( b ) ) ;
2015-12-31 23:28:54 +08:00
p . setDevicePixelRatio ( cRetinaFactor ( ) ) ;
black = new ImagePtr ( p , " PNG " ) ;
}
2016-02-28 16:54:04 +03:00
void LocationManager : : reinit ( ) {
2015-12-31 23:28:54 +08:00
if ( manager ) App : : setProxySettings ( * manager ) ;
}
2016-02-28 16:54:04 +03:00
void LocationManager : : deinit ( ) {
2015-12-31 23:28:54 +08:00
if ( manager ) {
delete manager ;
2016-07-13 20:34:57 +03:00
manager = nullptr ;
2015-12-31 23:28:54 +08:00
}
if ( black ) {
2016-07-13 20:34:57 +03:00
delete black - > v ( ) ;
2015-12-31 23:28:54 +08:00
delete black ;
2016-07-13 20:34:57 +03:00
black = nullptr ;
2015-12-31 23:28:54 +08:00
}
dataLoadings . clear ( ) ;
imageLoadings . clear ( ) ;
}
void initImageLinkManager ( ) {
2016-07-13 20:34:57 +03:00
if ( ! locationManager ) {
locationManager = new LocationManager ( ) ;
locationManager - > init ( ) ;
}
2015-12-31 23:28:54 +08:00
}
void reinitImageLinkManager ( ) {
2016-07-13 20:34:57 +03:00
if ( locationManager ) {
locationManager - > reinit ( ) ;
}
2015-12-31 23:28:54 +08:00
}
void deinitImageLinkManager ( ) {
2016-07-13 20:34:57 +03:00
if ( locationManager ) {
locationManager - > deinit ( ) ;
delete locationManager ;
locationManager = nullptr ;
}
2015-12-31 23:28:54 +08:00
}
2016-02-28 16:54:04 +03:00
void LocationManager : : getData ( LocationData * data ) {
2015-12-31 23:28:54 +08:00
if ( ! manager ) {
DEBUG_LOG ( ( " App Error: getting image link data without manager init! " ) ) ;
return failed ( data ) ;
}
2016-02-28 16:54:04 +03:00
int32 w = st : : locationSize . width ( ) , h = st : : locationSize . height ( ) ;
int32 zoom = 13 , scale = 1 ;
if ( cScale ( ) = = dbisTwo | | cRetina ( ) ) {
scale = 2 ;
} else {
w = convertScale ( w ) ;
h = convertScale ( h ) ;
2015-12-31 23:28:54 +08:00
}
2016-02-28 16:54:04 +03:00
QString coords = qsl ( " %1,%2 " ) . arg ( data - > coords . lat ) . arg ( data - > coords . lon ) ;
QString url = qsl ( " https://maps.googleapis.com/maps/api/staticmap?center= " ) + coords + qsl ( " &zoom=%1&size=%2x%3&maptype=roadmap&scale=%4&markers=color:red|size:big| " ) . arg ( zoom ) . arg ( w ) . arg ( h ) . arg ( scale ) + coords + qsl ( " &sensor=false " ) ;
QNetworkReply * reply = manager - > get ( QNetworkRequest ( QUrl ( url ) ) ) ;
imageLoadings [ reply ] = data ;
2015-12-31 23:28:54 +08:00
}
2016-02-28 16:54:04 +03:00
void LocationManager : : onFinished ( QNetworkReply * reply ) {
2015-12-31 23:28:54 +08:00
if ( ! manager ) return ;
if ( reply - > error ( ) ! = QNetworkReply : : NoError ) return onFailed ( reply ) ;
QVariant statusCode = reply - > attribute ( QNetworkRequest : : HttpStatusCodeAttribute ) ;
if ( statusCode . isValid ( ) ) {
int status = statusCode . toInt ( ) ;
if ( status = = 301 | | status = = 302 ) {
QString loc = reply - > header ( QNetworkRequest : : LocationHeader ) . toString ( ) ;
if ( ! loc . isEmpty ( ) ) {
2016-02-28 16:54:04 +03:00
QMap < QNetworkReply * , LocationData * > : : iterator i = dataLoadings . find ( reply ) ;
2015-12-31 23:28:54 +08:00
if ( i ! = dataLoadings . cend ( ) ) {
2016-02-28 16:54:04 +03:00
LocationData * d = i . value ( ) ;
2015-12-31 23:28:54 +08:00
if ( serverRedirects . constFind ( d ) = = serverRedirects . cend ( ) ) {
serverRedirects . insert ( d , 1 ) ;
} else if ( + + serverRedirects [ d ] > MaxHttpRedirects ) {
DEBUG_LOG ( ( " Network Error: Too many HTTP redirects in onFinished() for image link: %1 " ) . arg ( loc ) ) ;
return onFailed ( reply ) ;
}
dataLoadings . erase ( i ) ;
dataLoadings . insert ( manager - > get ( QNetworkRequest ( loc ) ) , d ) ;
return ;
} else if ( ( i = imageLoadings . find ( reply ) ) ! = imageLoadings . cend ( ) ) {
2016-02-28 16:54:04 +03:00
LocationData * d = i . value ( ) ;
2015-12-31 23:28:54 +08:00
if ( serverRedirects . constFind ( d ) = = serverRedirects . cend ( ) ) {
serverRedirects . insert ( d , 1 ) ;
} else if ( + + serverRedirects [ d ] > MaxHttpRedirects ) {
DEBUG_LOG ( ( " Network Error: Too many HTTP redirects in onFinished() for image link: %1 " ) . arg ( loc ) ) ;
return onFailed ( reply ) ;
}
imageLoadings . erase ( i ) ;
imageLoadings . insert ( manager - > get ( QNetworkRequest ( loc ) ) , d ) ;
return ;
}
}
}
if ( status ! = 200 ) {
DEBUG_LOG ( ( " Network Error: Bad HTTP status received in onFinished() for image link: %1 " ) . arg ( status ) ) ;
return onFailed ( reply ) ;
}
}
2016-02-28 16:54:04 +03:00
LocationData * d = 0 ;
QMap < QNetworkReply * , LocationData * > : : iterator i = dataLoadings . find ( reply ) ;
2015-12-31 23:28:54 +08:00
if ( i ! = dataLoadings . cend ( ) ) {
d = i . value ( ) ;
dataLoadings . erase ( i ) ;
QJsonParseError e ;
QJsonDocument doc = QJsonDocument : : fromJson ( reply - > readAll ( ) , & e ) ;
if ( e . error ! = QJsonParseError : : NoError ) {
DEBUG_LOG ( ( " JSON Error: Bad json received in onFinished() for image link " ) ) ;
return onFailed ( reply ) ;
}
2016-02-28 16:54:04 +03:00
failed ( d ) ;
2015-12-31 23:28:54 +08:00
if ( App : : main ( ) ) App : : main ( ) - > update ( ) ;
} else {
i = imageLoadings . find ( reply ) ;
if ( i ! = imageLoadings . cend ( ) ) {
d = i . value ( ) ;
imageLoadings . erase ( i ) ;
QPixmap thumb ;
QByteArray format ;
QByteArray data ( reply - > readAll ( ) ) ;
{
QBuffer buffer ( & data ) ;
QImageReader reader ( & buffer ) ;
2016-08-29 23:24:16 -06:00
# ifndef OS_MAC_OLD
2015-12-31 23:28:54 +08:00
reader . setAutoTransform ( true ) ;
2016-08-29 23:24:16 -06:00
# endif // OS_MAC_OLD
2015-12-31 23:28:54 +08:00
thumb = QPixmap : : fromImageReader ( & reader , Qt : : ColorOnly ) ;
format = reader . format ( ) ;
thumb . setDevicePixelRatio ( cRetinaFactor ( ) ) ;
if ( format . isEmpty ( ) ) format = QByteArray ( " JPG " ) ;
}
d - > loading = false ;
d - > thumb = thumb . isNull ( ) ? ( * black ) : ImagePtr ( thumb , format ) ;
serverRedirects . remove ( d ) ;
if ( App : : main ( ) ) App : : main ( ) - > update ( ) ;
}
}
}
2016-02-28 16:54:04 +03:00
void LocationManager : : onFailed ( QNetworkReply * reply ) {
2015-12-31 23:28:54 +08:00
if ( ! manager ) return ;
2016-02-28 16:54:04 +03:00
LocationData * d = 0 ;
QMap < QNetworkReply * , LocationData * > : : iterator i = dataLoadings . find ( reply ) ;
2015-12-31 23:28:54 +08:00
if ( i ! = dataLoadings . cend ( ) ) {
d = i . value ( ) ;
dataLoadings . erase ( i ) ;
} else {
i = imageLoadings . find ( reply ) ;
if ( i ! = imageLoadings . cend ( ) ) {
d = i . value ( ) ;
imageLoadings . erase ( i ) ;
}
}
2016-02-28 16:54:04 +03:00
DEBUG_LOG ( ( " Network Error: failed to get data for image link %1,%2 error %3 " ) . arg ( d ? d - > coords . lat : 0 ) . arg ( d ? d - > coords . lon : 0 ) . arg ( reply - > errorString ( ) ) ) ;
2015-12-31 23:28:54 +08:00
if ( d ) {
failed ( d ) ;
}
}
2016-02-28 16:54:04 +03:00
void LocationManager : : failed ( LocationData * data ) {
2015-12-31 23:28:54 +08:00
data - > loading = false ;
data - > thumb = * black ;
serverRedirects . remove ( data ) ;
}
2016-02-28 16:54:04 +03:00
void LocationData : : load ( ) {
2015-12-31 23:28:54 +08:00
if ( ! thumb - > isNull ( ) ) return thumb - > load ( false , false ) ;
if ( loading ) return ;
loading = true ;
2016-07-13 20:34:57 +03:00
if ( locationManager ) {
locationManager - > getData ( this ) ;
}
2015-12-31 23:28:54 +08:00
}
2016-04-10 18:53:01 +04:00
HistoryLocation : : HistoryLocation ( HistoryItem * parent , const LocationCoords & coords , const QString & title , const QString & description ) : HistoryMedia ( parent )
, _data ( App : : location ( coords ) )
2016-04-02 18:06:36 +04:00
, _title ( st : : msgMinWidth )
2016-04-10 18:53:01 +04:00
, _description ( st : : msgMinWidth )
, _link ( new LocationClickHandler ( coords ) ) {
2015-04-30 16:53:36 +03:00
if ( ! title . isEmpty ( ) ) {
_title . setText ( st : : webPageTitleFont , textClean ( title ) , _webpageTitleOptions ) ;
}
if ( ! description . isEmpty ( ) ) {
_description . setText ( st : : webPageDescriptionFont , textClean ( description ) , _webpageDescriptionOptions ) ;
}
2016-04-10 18:53:01 +04:00
}
2015-04-30 16:53:36 +03:00
2016-04-10 18:53:01 +04:00
HistoryLocation : : HistoryLocation ( HistoryItem * parent , const HistoryLocation & other ) : HistoryMedia ( parent )
, _data ( other . _data )
, _title ( other . _title )
, _description ( other . _description )
, _link ( new LocationClickHandler ( _data - > coords ) ) {
2014-11-12 23:18:00 +03:00
}
2016-04-10 18:53:01 +04:00
void HistoryLocation : : initDimensions ( ) {
bool bubble = _parent - > hasBubble ( ) ;
2015-12-19 15:27:03 +03:00
int32 tw = fullWidth ( ) , th = fullHeight ( ) ;
if ( tw > st : : maxMediaSize ) {
th = ( st : : maxMediaSize * th ) / tw ;
tw = st : : maxMediaSize ;
2014-11-12 23:18:00 +03:00
}
2016-04-10 18:53:01 +04:00
int32 minWidth = qMax ( st : : minPhotoSize , _parent - > infoWidth ( ) + 2 * ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
2015-12-19 21:09:24 +03:00
_maxw = qMax ( tw , int32 ( minWidth ) ) ;
2015-12-19 15:27:03 +03:00
_minh = qMax ( th , int32 ( st : : minPhotoSize ) ) ;
if ( bubble ) {
2015-03-19 12:18:19 +03:00
_maxw + = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
2015-04-30 16:53:36 +03:00
if ( ! _title . isEmpty ( ) ) {
2015-12-19 15:27:03 +03:00
_minh + = qMin ( _title . countHeight ( _maxw - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) , 2 * st : : webPageTitleFont - > height ) ;
2015-04-30 16:53:36 +03:00
}
if ( ! _description . isEmpty ( ) ) {
2015-12-19 15:27:03 +03:00
_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 ) ;
2015-04-30 16:53:36 +03:00
}
2015-12-19 15:27:03 +03:00
_minh + = st : : mediaPadding . top ( ) + st : : mediaPadding . bottom ( ) ;
2015-04-30 16:53:36 +03:00
if ( ! _title . isEmpty ( ) | | ! _description . isEmpty ( ) ) {
_minh + = st : : webPagePhotoSkip ;
2016-04-10 18:53:01 +04:00
if ( ! _parent - > Has < HistoryMessageForwarded > ( ) & & ! _parent - > Has < HistoryMessageReply > ( ) ) {
2015-12-19 15:27:03 +03:00
_minh + = st : : msgPadding . top ( ) ;
}
2015-04-30 16:53:36 +03:00
}
2015-03-19 12:18:19 +03:00
}
2014-11-12 23:18:00 +03:00
}
2016-04-10 18:53:01 +04:00
int HistoryLocation : : resizeGetHeight ( int width ) {
bool bubble = _parent - > hasBubble ( ) ;
2015-12-19 15:27:03 +03:00
2015-12-19 21:09:24 +03:00
_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 ;
}
2016-04-10 18:53:01 +04:00
int32 minWidth = qMax ( st : : minPhotoSize , _parent - > infoWidth ( ) + 2 * ( st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ) ) ;
2015-12-19 21:09:24 +03:00
_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 ;
2016-04-10 18:53:01 +04:00
if ( ! _parent - > Has < HistoryMessageForwarded > ( ) & & ! _parent - > Has < HistoryMessageReply > ( ) ) {
2015-12-19 21:09:24 +03:00
_height + = st : : msgPadding . top ( ) ;
}
}
}
return _height ;
}
2015-12-11 21:11:38 +03:00
2016-04-13 21:29:32 +03:00
void HistoryLocation : : draw ( Painter & p , const QRect & r , TextSelection selection , uint64 ms ) const {
2015-12-19 21:09:24 +03:00
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return ;
int32 skipx = 0 , skipy = 0 , width = _width , height = _height ;
2016-04-10 18:53:01 +04:00
bool bubble = _parent - > hasBubble ( ) ;
bool out = _parent - > out ( ) , isPost = _parent - > isPost ( ) , outbg = out & & ! isPost ;
2016-04-13 21:29:32 +03:00
bool selected = ( selection = = FullSelection ) ;
2015-05-20 22:28:24 +03:00
2015-12-19 15:27:03 +03:00
if ( bubble ) {
2015-03-19 12:18:19 +03:00
skipx = st : : mediaPadding . left ( ) ;
2015-12-19 15:27:03 +03:00
skipy = st : : mediaPadding . top ( ) ;
2015-03-19 12:18:19 +03:00
2015-12-19 15:27:03 +03:00
if ( ! _title . isEmpty ( ) | | ! _description . isEmpty ( ) ) {
2016-04-10 18:53:01 +04:00
if ( ! _parent - > Has < HistoryMessageForwarded > ( ) & & ! _parent - > Has < HistoryMessageReply > ( ) ) {
2015-12-19 15:27:03 +03:00
skipy + = st : : msgPadding . top ( ) ;
}
}
2015-12-09 22:09:29 +03:00
2015-03-19 12:18:19 +03:00
width - = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
2015-12-19 21:09:24 +03:00
int32 textw = _width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ;
2015-04-30 16:53:36 +03:00
2015-12-19 15:27:03 +03:00
p . setPen ( st : : black ) ;
2015-04-30 16:53:36 +03:00
if ( ! _title . isEmpty ( ) ) {
2016-04-13 21:29:32 +03:00
_title . drawLeftElided ( p , skipx + st : : msgPadding . left ( ) , skipy , textw , _width , 2 , style : : al_left , 0 , - 1 , 0 , false , selection ) ;
2015-12-19 15:27:03 +03:00
skipy + = qMin ( _title . countHeight ( textw ) , 2 * st : : webPageTitleFont - > height ) ;
2015-04-30 16:53:36 +03:00
}
if ( ! _description . isEmpty ( ) ) {
2016-04-13 21:29:32 +03:00
_description . drawLeftElided ( p , skipx + st : : msgPadding . left ( ) , skipy , textw , _width , 3 , style : : al_left , 0 , - 1 , 0 , false , toDescriptionSelection ( selection ) ) ;
2015-12-19 15:27:03 +03:00
skipy + = qMin ( _description . countHeight ( textw ) , 3 * st : : webPageDescriptionFont - > height ) ;
2015-04-30 16:53:36 +03:00
}
if ( ! _title . isEmpty ( ) | | ! _description . isEmpty ( ) ) {
skipy + = st : : webPagePhotoSkip ;
}
2015-03-19 12:18:19 +03:00
height - = skipy + st : : mediaPadding . bottom ( ) ;
2015-05-20 22:28:24 +03:00
} else {
2015-12-19 21:09:24 +03:00
App : : roundShadow ( p , 0 , 0 , width , height , selected ? st : : msgInShadowSelected : st : : msgInShadow , selected ? InSelectedShadowCorners : InShadowCorners ) ;
2015-03-19 12:18:19 +03:00
}
2014-11-12 23:18:00 +03:00
2015-12-19 15:27:03 +03:00
_data - > load ( ) ;
2014-11-12 23:18:00 +03:00
QPixmap toDraw ;
2015-12-19 15:27:03 +03:00
if ( _data & & ! _data - > thumb - > isNull ( ) ) {
int32 w = _data - > thumb - > width ( ) , h = _data - > thumb - > height ( ) ;
2015-05-20 22:28:24 +03:00
QPixmap pix ;
2015-12-19 15:27:03 +03:00
if ( width * h = = height * w | | ( w = = fullWidth ( ) & & h = = fullHeight ( ) ) ) {
2016-07-07 15:35:14 +03:00
pix = _data - > thumb - > pixSingle ( ImageRoundRadius : : Large , width , height , width , height ) ;
2015-05-20 22:50:05 +03:00
} else if ( width * h > height * w ) {
int32 nw = height * w / h ;
2016-07-07 15:35:14 +03:00
pix = _data - > thumb - > pixSingle ( ImageRoundRadius : : Large , nw , height , width , height ) ;
2014-11-12 23:18:00 +03:00
} else {
2015-05-20 22:50:05 +03:00
int32 nh = width * h / w ;
2016-07-07 15:35:14 +03:00
pix = _data - > thumb - > pixSingle ( ImageRoundRadius : : Large , width , nh , width , height ) ;
2014-11-12 23:18:00 +03:00
}
2015-05-20 22:28:24 +03:00
p . drawPixmap ( QPoint ( skipx , skipy ) , pix ) ;
2014-11-12 23:18:00 +03:00
} else {
2016-01-09 15:11:23 +08:00
App : : roundRect ( p , skipx , skipy , width , height , st : : white , MessageInCorners ) ;
2014-11-12 23:18:00 +03:00
}
if ( selected ) {
2016-07-07 15:35:14 +03:00
App : : roundRect ( p , skipx , skipy , width , height , textstyleCurrent ( ) - > selectOverlay , SelectedOverlayLargeCorners ) ;
2014-11-12 23:18:00 +03:00
}
2016-04-10 18:53:01 +04:00
if ( _parent - > getMedia ( ) = = this ) {
2015-12-19 00:36:16 +03:00
int32 fullRight = skipx + width , fullBottom = _height - ( skipx ? st : : mediaPadding . bottom ( ) : 0 ) ;
2016-04-10 18:53:01 +04:00
_parent - > drawInfo ( p , fullRight , fullBottom , skipx * 2 + width , selected , InfoDisplayOverImage ) ;
2015-12-19 00:36:16 +03:00
}
2014-11-12 23:18:00 +03:00
}
2016-04-13 21:29:32 +03:00
HistoryTextState HistoryLocation : : getState ( int x , int y , HistoryStateRequest request ) const {
HistoryTextState result ;
2016-04-14 14:00:23 +03:00
auto symbolAdd = 0 ;
2016-04-13 21:29:32 +03:00
if ( _width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) return result ;
2015-12-19 21:09:24 +03:00
int32 skipx = 0 , skipy = 0 , width = _width , height = _height ;
2016-04-10 18:53:01 +04:00
bool bubble = _parent - > hasBubble ( ) ;
2015-09-13 20:27:29 +03:00
2015-12-19 15:27:03 +03:00
if ( bubble ) {
2015-03-19 12:18:19 +03:00
skipx = st : : mediaPadding . left ( ) ;
2015-12-19 15:27:03 +03:00
skipy = st : : mediaPadding . top ( ) ;
if ( ! _title . isEmpty ( ) | | ! _description . isEmpty ( ) ) {
2016-04-10 18:53:01 +04:00
if ( ! _parent - > Has < HistoryMessageForwarded > ( ) & & ! _parent - > Has < HistoryMessageReply > ( ) ) {
2015-12-19 15:27:03 +03:00
skipy + = st : : msgPadding . top ( ) ;
}
}
2015-03-19 12:18:19 +03:00
width - = st : : mediaPadding . left ( ) + st : : mediaPadding . right ( ) ;
2015-12-19 21:09:24 +03:00
int32 textw = _width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ;
2015-12-19 15:27:03 +03:00
if ( ! _title . isEmpty ( ) ) {
2016-04-13 21:29:32 +03:00
auto titleh = qMin ( _title . countHeight ( textw ) , 2 * st : : webPageTitleFont - > height ) ;
if ( y > = skipy & & y < skipy + titleh ) {
result = _title . getStateLeft ( x - skipx - st : : msgPadding . left ( ) , y - skipy , textw , _width , request . forText ( ) ) ;
return result ;
2016-04-14 14:00:23 +03:00
} else if ( y > = skipy + titleh ) {
symbolAdd + = _title . length ( ) ;
2016-04-13 21:29:32 +03:00
}
skipy + = titleh ;
2015-12-19 15:27:03 +03:00
}
if ( ! _description . isEmpty ( ) ) {
2016-04-13 21:29:32 +03:00
auto descriptionh = qMin ( _description . countHeight ( textw ) , 3 * st : : webPageDescriptionFont - > height ) ;
if ( y > = skipy & & y < skipy + descriptionh ) {
result = _description . getStateLeft ( x - skipx - st : : msgPadding . left ( ) , y - skipy , textw , _width , request . forText ( ) ) ;
2016-04-14 14:00:23 +03:00
} else if ( y > = skipy + descriptionh ) {
symbolAdd + = _description . length ( ) ;
2016-04-13 21:29:32 +03:00
}
skipy + = descriptionh ;
2015-12-19 15:27:03 +03:00
}
if ( ! _title . isEmpty ( ) | | ! _description . isEmpty ( ) ) {
skipy + = st : : webPagePhotoSkip ;
}
height - = skipy + st : : mediaPadding . bottom ( ) ;
2015-03-19 12:18:19 +03:00
}
2015-12-19 15:27:03 +03:00
if ( x > = skipx & & y > = skipy & & x < skipx + width & & y < skipy + height & & _data ) {
2016-04-13 21:29:32 +03:00
result . link = _link ;
2015-08-31 17:27:20 +03:00
2015-10-23 21:24:05 +02:00
int32 fullRight = skipx + width , fullBottom = _height - ( skipx ? st : : mediaPadding . bottom ( ) : 0 ) ;
2016-04-10 18:53:01 +04:00
bool inDate = _parent - > pointInTime ( fullRight , fullBottom , x , y , InfoDisplayOverImage ) ;
2015-08-31 17:27:20 +03:00
if ( inDate ) {
2016-04-13 21:29:32 +03:00
result . cursor = HistoryInDateCursorState ;
2015-08-31 17:27:20 +03:00
}
2016-04-13 21:29:32 +03:00
}
2016-04-14 14:00:23 +03:00
result . symbol + = symbolAdd ;
2016-04-13 21:29:32 +03:00
return result ;
}
TextSelection HistoryLocation : : adjustSelection ( TextSelection selection , TextSelectType type ) const {
if ( _description . isEmpty ( ) | | selection . to < = _title . length ( ) ) {
return _title . adjustSelection ( selection , type ) ;
2014-11-12 23:18:00 +03:00
}
2016-04-13 21:29:32 +03:00
auto descriptionSelection = _description . adjustSelection ( toDescriptionSelection ( selection ) , type ) ;
if ( selection . from > = _title . length ( ) ) {
return fromDescriptionSelection ( descriptionSelection ) ;
}
auto titleSelection = _title . adjustSelection ( selection , type ) ;
return { titleSelection . from , fromDescriptionSelection ( descriptionSelection ) . to } ;
2014-11-12 23:18:00 +03:00
}
2016-06-22 16:39:54 +03:00
QString HistoryLocation : : notificationText ( ) const {
return captionedNotificationText ( lang ( lng_maps_point ) , _title ) ;
}
2016-04-14 14:00:23 +03:00
QString HistoryLocation : : inDialogsText ( ) const {
2016-06-22 16:39:54 +03:00
return captionedInDialogsText ( lang ( lng_maps_point ) , _title ) ;
2015-12-19 21:09:24 +03:00
}
2016-05-06 20:33:48 +03:00
TextWithEntities HistoryLocation : : selectedText ( TextSelection selection ) const {
2016-04-14 14:00:23 +03:00
if ( selection = = FullSelection ) {
2016-05-06 20:33:48 +03:00
TextWithEntities result = { qsl ( " [ " ) + lang ( lng_maps_point ) + qsl ( " ] \n " ) , EntitiesInText ( ) } ;
2016-04-14 14:00:23 +03:00
auto info = selectedText ( AllTextSelection ) ;
2016-05-06 20:33:48 +03:00
if ( ! info . text . isEmpty ( ) ) {
appendTextWithEntities ( result , std_ : : move ( info ) ) ;
result . text . append ( ' \n ' ) ;
}
result . text + = _link - > dragText ( ) ;
return result ;
2016-04-14 14:00:23 +03:00
}
2016-05-06 20:33:48 +03:00
auto titleResult = _title . originalTextWithEntities ( selection ) ;
auto descriptionResult = _description . originalTextWithEntities ( toDescriptionSelection ( selection ) ) ;
if ( titleResult . text . isEmpty ( ) ) {
2016-04-14 14:00:23 +03:00
return descriptionResult ;
2016-05-06 20:33:48 +03:00
} else if ( descriptionResult . text . isEmpty ( ) ) {
2016-04-14 14:00:23 +03:00
return titleResult ;
}
2016-05-06 20:33:48 +03:00
titleResult . text + = ' \n ' ;
appendTextWithEntities ( titleResult , std_ : : move ( descriptionResult ) ) ;
return titleResult ;
2015-12-19 21:09:24 +03:00
}
2016-02-28 16:54:04 +03:00
int32 HistoryLocation : : fullWidth ( ) const {
return st : : locationSize . width ( ) ;
2015-12-19 21:09:24 +03:00
}
2016-02-28 16:54:04 +03:00
int32 HistoryLocation : : fullHeight ( ) const {
return st : : locationSize . height ( ) ;
2014-11-12 23:18:00 +03:00
}
2016-03-29 20:17:00 +03:00
void ViaInlineBotClickHandler : : onClickImpl ( ) const {
2015-12-31 23:27:21 +08:00
App : : insertBotCommand ( ' @ ' + _bot - > username ) ;
}
2016-02-17 19:37:21 +03:00
void HistoryMessageVia : : create ( int32 userId ) {
2016-02-18 22:12:50 +03:00
_bot = App : : user ( peerFromUser ( userId ) ) ;
_maxWidth = st : : msgServiceNameFont - > width ( lng_inline_bot_via ( lt_inline_bot , ' @ ' + _bot - > username ) ) ;
2016-03-29 20:17:00 +03:00
_lnk . reset ( new ViaInlineBotClickHandler ( _bot ) ) ;
2015-12-31 23:27:21 +08:00
}
2016-02-17 19:37:21 +03:00
void HistoryMessageVia : : resize ( int32 availw ) const {
2016-01-07 17:49:54 +08:00
if ( availw < 0 ) {
2016-02-18 22:12:50 +03:00
_text = QString ( ) ;
_width = 0 ;
2016-01-07 17:49:54 +08:00
} else {
2016-02-18 22:12:50 +03:00
_text = lng_inline_bot_via ( lt_inline_bot , ' @ ' + _bot - > username ) ;
if ( availw < _maxWidth ) {
_text = st : : msgServiceNameFont - > elided ( _text , availw ) ;
_width = st : : msgServiceNameFont - > width ( _text ) ;
} else if ( _width < _maxWidth ) {
_width = _maxWidth ;
2016-01-03 17:46:30 +08:00
}
2015-12-31 23:27:21 +08:00
}
}
2016-02-17 19:37:21 +03:00
void HistoryMessageSigned : : create ( UserData * from , const QDateTime & date ) {
QString time = qsl ( " , " ) + date . toString ( cTimeFormat ( ) ) , name = App : : peerName ( from ) ;
int32 timew = st : : msgDateFont - > width ( time ) , namew = st : : msgDateFont - > width ( name ) ;
if ( timew + namew > st : : maxSignatureSize ) {
name = st : : msgDateFont - > elided ( from - > firstName , st : : maxSignatureSize - timew ) ;
}
_signature . setText ( st : : msgDateFont , name + time , _textNameOptions ) ;
}
2016-03-25 14:29:45 +03:00
int HistoryMessageSigned : : maxWidth ( ) const {
2016-02-17 19:37:21 +03:00
return _signature . maxWidth ( ) ;
}
2016-04-23 14:40:42 +03:00
void HistoryMessageEdited : : create ( const QDateTime & editDate , const QDateTime & date ) {
_editDate = editDate ;
QString time = date . toString ( cTimeFormat ( ) ) ;
2016-05-09 15:03:06 +03:00
_edited . setText ( st : : msgDateFont , lang ( lng_edited ) + ' ' + time , _textNameOptions ) ;
2016-04-23 14:40:42 +03:00
}
int HistoryMessageEdited : : maxWidth ( ) const {
return _edited . maxWidth ( ) ;
}
2016-02-19 14:53:49 +03:00
void HistoryMessageForwarded : : create ( const HistoryMessageVia * via ) const {
QString text ;
if ( _authorOriginal ! = _fromOriginal ) {
text = lng_forwarded_signed ( lt_channel , App : : peerName ( _authorOriginal ) , lt_user , App : : peerName ( _fromOriginal ) ) ;
} else {
text = App : : peerName ( _authorOriginal ) ;
}
if ( via ) {
2016-02-25 19:19:54 +03:00
if ( _authorOriginal - > isChannel ( ) ) {
text = lng_forwarded_channel_via ( lt_channel , textcmdLink ( 1 , text ) , lt_inline_bot , textcmdLink ( 2 , ' @ ' + via - > _bot - > username ) ) ;
} else {
text = lng_forwarded_via ( lt_user , textcmdLink ( 1 , text ) , lt_inline_bot , textcmdLink ( 2 , ' @ ' + via - > _bot - > username ) ) ;
}
2016-02-19 14:53:49 +03:00
} else {
2016-02-25 19:19:54 +03:00
if ( _authorOriginal - > isChannel ( ) ) {
text = lng_forwarded_channel ( lt_channel , textcmdLink ( 1 , text ) ) ;
} else {
text = lng_forwarded ( lt_user , textcmdLink ( 1 , text ) ) ;
}
2016-02-19 14:53:49 +03:00
}
TextParseOptions opts = { TextParseRichText , 0 , 0 , Qt : : LayoutDirectionAuto } ;
textstyleSet ( & st : : inFwdTextStyle ) ;
_text . setText ( st : : msgServiceNameFont , text , opts ) ;
textstyleRestore ( ) ;
2016-03-29 20:17:00 +03:00
_text . setLink ( 1 , ( _originalId & & _authorOriginal - > isChannel ( ) ) ? ClickHandlerPtr ( new GoToMessageClickHandler ( _authorOriginal - > id , _originalId ) ) : _authorOriginal - > openLink ( ) ) ;
2016-02-19 14:53:49 +03:00
if ( via ) {
_text . setLink ( 2 , via - > _lnk ) ;
}
2016-02-18 22:12:50 +03:00
}
2016-03-25 14:29:45 +03:00
bool HistoryMessageReply : : updateData ( HistoryMessage * holder , bool force ) {
if ( ! force ) {
if ( replyToMsg | | ! replyToMsgId ) {
return true ;
}
}
if ( ! replyToMsg ) {
replyToMsg = App : : histItemById ( holder - > channelId ( ) , replyToMsgId ) ;
if ( replyToMsg ) {
App : : historyRegDependency ( holder , replyToMsg ) ;
}
}
if ( replyToMsg ) {
2016-06-22 16:39:54 +03:00
replyToText . setText ( st : : msgFont , textClean ( replyToMsg - > inReplyText ( ) ) , _textDlgOptions ) ;
2016-03-25 14:29:45 +03:00
updateName ( ) ;
2016-03-29 20:17:00 +03:00
replyToLnk . reset ( new GoToMessageClickHandler ( replyToMsg - > history ( ) - > peer - > id , replyToMsg - > id ) ) ;
2016-03-25 14:29:45 +03:00
if ( ! replyToMsg - > Has < HistoryMessageForwarded > ( ) ) {
2016-09-19 13:18:21 +03:00
if ( auto bot = replyToMsg - > viaBot ( ) ) {
2016-03-28 15:51:22 +03:00
_replyToVia . reset ( new HistoryMessageVia ( ) ) ;
2016-03-25 14:29:45 +03:00
_replyToVia - > create ( peerToUser ( bot - > id ) ) ;
}
}
} else if ( force ) {
replyToMsgId = 0 ;
}
if ( force ) {
holder - > setPendingInitDimensions ( ) ;
}
return ( replyToMsg | | ! replyToMsgId ) ;
}
void HistoryMessageReply : : clearData ( HistoryMessage * holder ) {
2016-04-10 23:20:48 +04:00
_replyToVia = nullptr ;
2016-03-25 14:29:45 +03:00
if ( replyToMsg ) {
App : : historyUnregDependency ( holder , replyToMsg ) ;
replyToMsg = nullptr ;
}
replyToMsgId = 0 ;
}
2016-06-08 21:46:37 +03:00
bool HistoryMessageReply : : isNameUpdated ( ) const {
2016-03-25 14:29:45 +03:00
if ( replyToMsg & & replyToMsg - > author ( ) - > nameVersion > replyToVersion ) {
updateName ( ) ;
2016-06-08 21:46:37 +03:00
return true ;
2016-03-25 14:29:45 +03:00
}
2016-06-08 21:46:37 +03:00
return false ;
2016-03-25 14:29:45 +03:00
}
void HistoryMessageReply : : updateName ( ) const {
if ( replyToMsg ) {
QString name = ( _replyToVia & & replyToMsg - > author ( ) - > isUser ( ) ) ? replyToMsg - > author ( ) - > asUser ( ) - > firstName : App : : peerName ( replyToMsg - > author ( ) ) ;
replyToName . setText ( st : : msgServiceNameFont , name , _textNameOptions ) ;
replyToVersion = replyToMsg - > author ( ) - > nameVersion ;
bool hasPreview = replyToMsg - > getMedia ( ) ? replyToMsg - > getMedia ( ) - > hasReplyPreview ( ) : false ;
int32 previewSkip = hasPreview ? ( st : : msgReplyBarSize . height ( ) + st : : msgReplyBarSkip - st : : msgReplyBarSize . width ( ) - st : : msgReplyBarPos . x ( ) ) : 0 ;
int32 w = replyToName . maxWidth ( ) ;
if ( _replyToVia ) {
w + = st : : msgServiceFont - > spacew + _replyToVia - > _maxWidth ;
}
_maxReplyWidth = previewSkip + qMax ( w , qMin ( replyToText . maxWidth ( ) , int32 ( st : : maxSignatureSize ) ) ) ;
} else {
_maxReplyWidth = st : : msgDateFont - > width ( lang ( replyToMsgId ? lng_profile_loading : lng_deleted_message ) ) ;
}
_maxReplyWidth = st : : msgReplyPadding . left ( ) + st : : msgReplyBarSkip + _maxReplyWidth + st : : msgReplyPadding . right ( ) ;
}
void HistoryMessageReply : : resize ( int width ) const {
if ( _replyToVia ) {
bool hasPreview = replyToMsg - > getMedia ( ) ? replyToMsg - > getMedia ( ) - > hasReplyPreview ( ) : false ;
int previewSkip = hasPreview ? ( st : : msgReplyBarSize . height ( ) + st : : msgReplyBarSkip - st : : msgReplyBarSize . width ( ) - st : : msgReplyBarPos . x ( ) ) : 0 ;
_replyToVia - > resize ( width - st : : msgReplyBarSkip - previewSkip - replyToName . maxWidth ( ) - st : : msgServiceFont - > spacew ) ;
}
}
void HistoryMessageReply : : itemRemoved ( HistoryMessage * holder , HistoryItem * removed ) {
if ( replyToMsg = = removed ) {
2016-03-25 23:46:35 +03:00
clearData ( holder ) ;
2016-03-25 14:29:45 +03:00
holder - > setPendingInitDimensions ( ) ;
}
}
void HistoryMessageReply : : paint ( Painter & p , const HistoryItem * holder , int x , int y , int w , PaintFlags flags ) const {
bool selected = ( flags & PaintSelected ) , outbg = holder - > hasOutLayout ( ) ;
style : : color bar ;
if ( flags & PaintInBubble ) {
bar = ( ( flags & PaintSelected ) ? ( outbg ? st : : msgOutReplyBarSelColor : st : : msgInReplyBarSelColor ) : ( outbg ? st : : msgOutReplyBarColor : st : : msgInReplyBarColor ) ) ;
} else {
bar = st : : white ;
}
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 ) {
bool hasPreview = replyToMsg - > getMedia ( ) ? replyToMsg - > getMedia ( ) - > hasReplyPreview ( ) : false ;
int previewSkip = hasPreview ? ( st : : msgReplyBarSize . height ( ) + st : : msgReplyBarSkip - st : : msgReplyBarSize . width ( ) - st : : msgReplyBarPos . x ( ) ) : 0 ;
if ( hasPreview ) {
ImagePtr replyPreview = replyToMsg - > getMedia ( ) - > replyPreview ( ) ;
if ( ! replyPreview - > isNull ( ) ) {
QRect to ( rtlrect ( x + st : : msgReplyBarSkip , y + st : : msgReplyPadding . top ( ) + st : : msgReplyBarPos . y ( ) , st : : msgReplyBarSize . height ( ) , st : : msgReplyBarSize . height ( ) , w + 2 * x ) ) ;
2016-07-07 15:35:14 +03:00
p . drawPixmap ( to . x ( ) , to . y ( ) , replyPreview - > pixSingle ( ImageRoundRadius : : Small , replyPreview - > width ( ) / cIntRetinaFactor ( ) , replyPreview - > height ( ) / cIntRetinaFactor ( ) , to . width ( ) , to . height ( ) ) ) ;
2016-03-25 14:29:45 +03:00
if ( selected ) {
2016-07-07 15:35:14 +03:00
App : : roundRect ( p , to , textstyleCurrent ( ) - > selectOverlay , SelectedOverlaySmallCorners ) ;
2016-03-25 14:29:45 +03:00
}
}
}
if ( w > st : : msgReplyBarSkip + previewSkip ) {
if ( flags & PaintInBubble ) {
p . setPen ( selected ? ( outbg ? st : : msgOutServiceFgSelected : st : : msgInServiceFgSelected ) : ( outbg ? st : : msgOutServiceFg : st : : msgInServiceFg ) ) ;
} else {
p . setPen ( st : : white ) ;
}
replyToName . drawLeftElided ( p , x + st : : msgReplyBarSkip + previewSkip , y + st : : msgReplyPadding . top ( ) , w - st : : msgReplyBarSkip - previewSkip , w + 2 * x ) ;
if ( _replyToVia & & w > st : : msgReplyBarSkip + previewSkip + replyToName . maxWidth ( ) + st : : msgServiceFont - > spacew ) {
p . setFont ( st : : msgServiceFont ) ;
p . drawText ( x + st : : msgReplyBarSkip + previewSkip + replyToName . maxWidth ( ) + st : : msgServiceFont - > spacew , y + st : : msgReplyPadding . top ( ) + st : : msgServiceFont - > ascent , _replyToVia - > _text ) ;
}
HistoryMessage * replyToAsMsg = replyToMsg - > toHistoryMessage ( ) ;
if ( ! ( flags & PaintInBubble ) ) {
} else if ( ( replyToAsMsg & & replyToAsMsg - > emptyText ( ) ) | | replyToMsg - > serviceMsg ( ) ) {
style : : color date ( outbg ? ( selected ? st : : msgOutDateFgSelected : st : : msgOutDateFg ) : ( selected ? st : : msgInDateFgSelected : st : : msgInDateFg ) ) ;
p . setPen ( date ) ;
} else {
p . setPen ( st : : msgColor ) ;
}
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 ) ;
style : : color date ( outbg ? ( selected ? st : : msgOutDateFgSelected : st : : msgOutDateFg ) : ( selected ? st : : msgInDateFgSelected : st : : msgInDateFg ) ) ;
p . setPen ( ( flags & PaintInBubble ) ? date : st : : white ) ;
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 ) ) ;
}
}
}
2016-03-28 20:15:17 +03:00
void HistoryMessage : : KeyboardStyle : : startPaint ( Painter & p ) const {
p . setPen ( st : : msgServiceColor ) ;
}
style : : font HistoryMessage : : KeyboardStyle : : textFont ( ) const {
return st : : msgServiceFont ;
}
void HistoryMessage : : KeyboardStyle : : repaint ( const HistoryItem * item ) const {
Ui : : repaintHistoryItem ( item ) ;
}
void HistoryMessage : : KeyboardStyle : : paintButtonBg ( Painter & p , const QRect & rect , bool down , float64 howMuchOver ) const {
2016-07-08 13:06:41 +03:00
App : : roundRect ( p , rect , App : : msgServiceBg ( ) , StickerCorners ) ;
2016-03-30 12:03:29 +04:00
if ( down ) {
howMuchOver = 1. ;
}
if ( howMuchOver > 0 ) {
2016-03-30 20:42:01 +04:00
float64 o = p . opacity ( ) ;
p . setOpacity ( o * ( howMuchOver * st : : msgBotKbOverOpacity ) ) ;
2016-03-30 12:03:29 +04:00
App : : roundRect ( p , rect , st : : white , WhiteCorners ) ;
2016-03-30 20:42:01 +04:00
p . setOpacity ( o ) ;
}
}
void HistoryMessage : : KeyboardStyle : : paintButtonIcon ( Painter & p , const QRect & rect , HistoryMessageReplyMarkup : : Button : : Type type ) const {
2016-04-08 18:16:52 +04:00
using Button = HistoryMessageReplyMarkup : : Button ;
2016-03-30 20:42:01 +04:00
style : : sprite sprite ;
switch ( type ) {
2016-09-02 12:11:23 -04:00
case Button : : Type : : Url : sprite = st : : msgBotKbUrlIcon ; break ;
// case Button::Type::RequestPhone: sprite = st::msgBotKbRequestPhoneIcon; break;
// case Button::Type::RequestLocation: sprite = st::msgBotKbRequestLocationIcon; break;
case Button : : Type : : SwitchInlineSame :
case Button : : Type : : SwitchInline : sprite = st : : msgBotKbSwitchPmIcon ; break ;
2016-03-30 20:42:01 +04:00
}
if ( ! sprite . isEmpty ( ) ) {
p . drawSprite ( rect . x ( ) + rect . width ( ) - sprite . pxWidth ( ) - st : : msgBotKbIconPadding , rect . y ( ) + st : : msgBotKbIconPadding , sprite ) ;
}
}
2016-04-06 21:02:22 +04:00
void HistoryMessage : : KeyboardStyle : : paintButtonLoading ( Painter & p , const QRect & rect ) const {
style : : sprite sprite = st : : msgInvSendingImg ;
p . drawSprite ( rect . x ( ) + rect . width ( ) - sprite . pxWidth ( ) - st : : msgBotKbIconPadding , rect . y ( ) + rect . height ( ) - sprite . pxHeight ( ) - st : : msgBotKbIconPadding , sprite ) ;
}
2016-03-30 20:42:01 +04:00
int HistoryMessage : : KeyboardStyle : : minButtonWidth ( HistoryMessageReplyMarkup : : Button : : Type type ) const {
2016-04-08 18:16:52 +04:00
using Button = HistoryMessageReplyMarkup : : Button ;
2016-03-30 20:42:01 +04:00
int result = 2 * buttonPadding ( ) , iconWidth = 0 ;
switch ( type ) {
2016-09-02 12:11:23 -04:00
case Button : : Type : : Url : iconWidth = st : : msgBotKbUrlIcon . pxWidth ( ) ; break ;
//case Button::Type::RequestPhone: iconWidth = st::msgBotKbRequestPhoneIcon.pxWidth(); break;
//case Button::Type::RequestLocation: iconWidth = st::msgBotKbRequestLocationIcon.pxWidth(); break;
case Button : : Type : : SwitchInlineSame :
case Button : : Type : : SwitchInline : iconWidth = st : : msgBotKbSwitchPmIcon . pxWidth ( ) ; break ;
case Button : : Type : : Callback :
case Button : : Type : : Game : iconWidth = st : : msgInvSendingImg . pxWidth ( ) ; break ;
2016-03-30 12:03:29 +04:00
}
2016-03-30 20:42:01 +04:00
if ( iconWidth > 0 ) {
2016-04-11 00:59:07 +04:00
result = std : : max ( result , 2 * iconWidth + 4 * int ( st : : msgBotKbIconPadding ) ) ;
2016-03-30 20:42:01 +04:00
}
return result ;
2016-03-28 20:15:17 +03:00
}
2016-03-25 14:29:45 +03:00
HistoryMessage : : HistoryMessage ( History * history , const MTPDmessage & msg )
: HistoryItem ( history , msg . vid . v , msg . vflags . v , : : date ( msg . vdate ) , msg . has_from_id ( ) ? msg . vfrom_id . v : 0 ) {
2016-03-28 15:51:22 +03:00
CreateConfig config ;
2016-02-18 22:12:50 +03:00
if ( msg . has_fwd_from ( ) & & msg . vfwd_from . type ( ) = = mtpc_messageFwdHeader ) {
2016-04-08 14:44:35 +04:00
const auto & f ( msg . vfwd_from . c_messageFwdHeader ( ) ) ;
2016-02-18 22:12:50 +03:00
if ( f . has_from_id ( ) | | f . has_channel_id ( ) ) {
2016-03-28 15:51:22 +03:00
config . authorIdOriginal = f . has_channel_id ( ) ? peerFromChannel ( f . vchannel_id ) : peerFromUser ( f . vfrom_id ) ;
config . fromIdOriginal = f . has_from_id ( ) ? peerFromUser ( f . vfrom_id ) : peerFromChannel ( f . vchannel_id ) ;
if ( f . has_channel_post ( ) ) config . originalId = f . vchannel_post . v ;
2016-02-18 22:12:50 +03:00
}
}
2016-03-28 15:51:22 +03:00
if ( msg . has_reply_to_msg_id ( ) ) config . replyTo = msg . vreply_to_msg_id . v ;
if ( msg . has_via_bot_id ( ) ) config . viaBotId = msg . vvia_bot_id . v ;
if ( msg . has_views ( ) ) config . viewsCount = msg . vviews . v ;
if ( msg . has_reply_markup ( ) ) config . markup = & msg . vreply_markup ;
2016-04-23 14:40:42 +03:00
if ( msg . has_edit_date ( ) ) config . editDate = : : date ( msg . vedit_date ) ;
2016-03-28 15:51:22 +03:00
createComponents ( config ) ;
2016-02-18 22:12:50 +03:00
2014-05-30 12:53:19 +04:00
QString text ( textClean ( qs ( msg . vmessage ) ) ) ;
2016-04-29 15:00:48 +03:00
initMedia ( msg . has_media ( ) ? ( & msg . vmedia ) : nullptr , text ) ;
2016-05-06 20:33:48 +03:00
TextWithEntities textWithEntities = { text , EntitiesInText ( ) } ;
if ( msg . has_entities ( ) ) {
textWithEntities . entities = entitiesFromMTP ( msg . ventities . c_vector ( ) . v ) ;
}
setText ( textWithEntities ) ;
2014-05-30 12:53:19 +04:00
}
2016-05-09 17:42:10 +03:00
namespace {
MTPDmessage : : Flags newForwardedFlags ( PeerData * p , int32 from , HistoryMessage * fwd ) {
MTPDmessage : : Flags result = newMessageFlags ( p ) | MTPDmessage : : Flag : : f_fwd_from ;
if ( from ) {
result | = MTPDmessage : : Flag : : f_from_id ;
}
if ( fwd - > Has < HistoryMessageVia > ( ) ) {
result | = MTPDmessage : : Flag : : f_via_bot_id ;
}
if ( ! p - > isChannel ( ) ) {
if ( HistoryMedia * media = fwd - > getMedia ( ) ) {
if ( media - > type ( ) = = MediaTypeVoiceFile ) {
result | = MTPDmessage : : Flag : : f_media_unread ;
// } else if (media->type() == MediaTypeVideo) {
// result |= MTPDmessage::flag_media_unread;
}
}
}
if ( fwd - > hasViews ( ) ) {
result | = MTPDmessage : : Flag : : f_views ;
}
return result ;
}
} // namespace
2016-03-19 19:55:15 +03:00
HistoryMessage : : HistoryMessage ( History * history , MsgId id , MTPDmessage : : Flags flags , QDateTime date , int32 from , HistoryMessage * fwd )
2016-03-22 22:43:47 +03:00
: HistoryItem ( history , id , newForwardedFlags ( history - > peer , from , fwd ) | flags , date , from ) {
2016-03-28 15:51:22 +03:00
CreateConfig config ;
2016-04-09 15:08:19 +04:00
config . authorIdOriginal = fwd - > authorOriginal ( ) - > id ;
config . fromIdOriginal = fwd - > fromOriginal ( ) - > id ;
if ( fwd - > authorOriginal ( ) - > isChannel ( ) ) {
config . originalId = fwd - > id ;
}
2016-09-19 13:18:21 +03:00
auto fwdViaBot = fwd - > viaBot ( ) ;
2016-03-28 15:51:22 +03:00
if ( fwdViaBot ) config . viaBotId = peerToUser ( fwdViaBot - > id ) ;
int fwdViewsCount = fwd - > viewsCount ( ) ;
if ( fwdViewsCount > 0 ) {
config . viewsCount = fwdViewsCount ;
} else if ( isPost ( ) ) {
config . viewsCount = 1 ;
}
createComponents ( config ) ;
2016-02-18 22:12:50 +03:00
if ( HistoryMedia * mediaOriginal = fwd - > getMedia ( ) ) {
2016-04-10 18:53:01 +04:00
_media . reset ( mediaOriginal - > clone ( this ) ) ;
2014-05-30 12:53:19 +04:00
}
2016-05-06 20:33:48 +03:00
setText ( fwd - > originalText ( ) ) ;
2016-02-18 22:12:50 +03:00
}
2016-05-06 20:33:48 +03:00
HistoryMessage : : HistoryMessage ( History * history , MsgId id , MTPDmessage : : Flags flags , MsgId replyTo , int32 viaBotId , QDateTime date , int32 from , const TextWithEntities & textWithEntities )
2016-03-25 14:29:45 +03:00
: HistoryItem ( history , id , flags , date , ( flags & MTPDmessage : : Flag : : f_from_id ) ? from : 0 ) {
2016-04-06 12:00:37 +04:00
createComponentsHelper ( flags , replyTo , viaBotId , MTPnullMarkup ) ;
2016-02-18 22:12:50 +03:00
2016-05-06 20:33:48 +03:00
setText ( textWithEntities ) ;
2015-01-02 17:55:24 +03:00
}
2016-04-06 12:00:37 +04:00
HistoryMessage : : HistoryMessage ( History * history , MsgId msgId , MTPDmessage : : Flags flags , MsgId replyTo , int32 viaBotId , QDateTime date , int32 from , DocumentData * doc , const QString & caption , const MTPReplyMarkup & markup )
2016-03-25 14:29:45 +03:00
: HistoryItem ( history , msgId , flags , date , ( flags & MTPDmessage : : Flag : : f_from_id ) ? from : 0 ) {
2016-04-06 12:00:37 +04:00
createComponentsHelper ( flags , replyTo , viaBotId , markup ) ;
2016-02-18 22:12:50 +03:00
2015-12-28 13:28:00 +03:00
initMediaFromDocument ( doc , caption ) ;
2016-05-06 20:33:48 +03:00
setText ( TextWithEntities ( ) ) ;
2014-05-30 12:53:19 +04:00
}
2016-04-06 12:00:37 +04:00
HistoryMessage : : HistoryMessage ( History * history , MsgId msgId , MTPDmessage : : Flags flags , MsgId replyTo , int32 viaBotId , QDateTime date , int32 from , PhotoData * photo , const QString & caption , const MTPReplyMarkup & markup )
2016-03-25 14:29:45 +03:00
: HistoryItem ( history , msgId , flags , date , ( flags & MTPDmessage : : Flag : : f_from_id ) ? from : 0 ) {
2016-04-06 12:00:37 +04:00
createComponentsHelper ( flags , replyTo , viaBotId , markup ) ;
2016-02-18 22:12:50 +03:00
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryPhoto ( this , photo , caption ) ) ;
2016-05-06 20:33:48 +03:00
setText ( TextWithEntities ( ) ) ;
2015-12-31 13:34:43 +08:00
}
2016-04-06 12:00:37 +04:00
void HistoryMessage : : createComponentsHelper ( MTPDmessage : : Flags flags , MsgId replyTo , int32 viaBotId , const MTPReplyMarkup & markup ) {
2016-03-28 15:51:22 +03:00
CreateConfig config ;
if ( flags & MTPDmessage : : Flag : : f_via_bot_id ) config . viaBotId = viaBotId ;
if ( flags & MTPDmessage : : Flag : : f_reply_to_msg_id ) config . replyTo = replyTo ;
2016-04-06 12:00:37 +04:00
if ( flags & MTPDmessage : : Flag : : f_reply_markup ) config . markup = & markup ;
2016-03-28 15:51:22 +03:00
if ( isPost ( ) ) config . viewsCount = 1 ;
createComponents ( config ) ;
2016-03-25 14:29:45 +03:00
}
2016-06-08 21:46:37 +03:00
bool HistoryMessage : : displayEditedBadge ( bool hasViaBot ) const {
if ( ! ( _flags & MTPDmessage : : Flag : : f_edit_date ) ) {
return false ;
}
if ( auto fromUser = from ( ) - > asUser ( ) ) {
if ( fromUser - > botInfo ) {
return false ;
}
}
if ( hasViaBot ) {
return false ;
}
return true ;
}
2016-03-28 15:51:22 +03:00
void HistoryMessage : : createComponents ( const CreateConfig & config ) {
2016-02-17 19:37:21 +03:00
uint64 mask = 0 ;
2016-03-28 15:51:22 +03:00
if ( config . replyTo ) {
2016-03-25 14:29:45 +03:00
mask | = HistoryMessageReply : : Bit ( ) ;
}
2016-03-28 15:51:22 +03:00
if ( config . viaBotId ) {
2016-02-17 19:37:21 +03:00
mask | = HistoryMessageVia : : Bit ( ) ;
}
2016-03-28 15:51:22 +03:00
if ( config . viewsCount > = 0 ) {
2016-02-17 19:37:21 +03:00
mask | = HistoryMessageViews : : Bit ( ) ;
}
if ( isPost ( ) & & _from - > isUser ( ) ) {
mask | = HistoryMessageSigned : : Bit ( ) ;
}
2016-06-08 21:46:37 +03:00
if ( displayEditedBadge ( config . viaBotId ! = 0 ) ) {
2016-04-23 14:40:42 +03:00
mask | = HistoryMessageEdited : : Bit ( ) ;
}
2016-03-28 15:51:22 +03:00
if ( config . authorIdOriginal & & config . fromIdOriginal ) {
2016-02-18 22:12:50 +03:00
mask | = HistoryMessageForwarded : : Bit ( ) ;
}
2016-03-28 15:51:22 +03:00
if ( config . markup ) {
// optimization: don't create markup component for the case
// MTPDreplyKeyboardHide with flags = 0, assume it has f_zero flag
if ( config . markup - > type ( ) ! = mtpc_replyKeyboardHide | | config . markup - > c_replyKeyboardHide ( ) . vflags . v ! = 0 ) {
mask | = HistoryMessageReplyMarkup : : Bit ( ) ;
}
}
2016-03-25 14:29:45 +03:00
UpdateComponents ( mask ) ;
2016-04-08 13:20:10 +04:00
if ( auto reply = Get < HistoryMessageReply > ( ) ) {
2016-03-28 15:51:22 +03:00
reply - > replyToMsgId = config . replyTo ;
2016-03-25 14:29:45 +03:00
if ( ! reply - > updateData ( this ) & & App : : api ( ) ) {
2016-05-09 13:07:25 +03:00
App : : api ( ) - > requestMessageData ( history ( ) - > peer - > asChannel ( ) , reply - > replyToMsgId , std_ : : make_unique < HistoryDependentItemCallback > ( fullId ( ) ) ) ;
2016-03-25 14:29:45 +03:00
}
}
2016-04-08 13:20:10 +04:00
if ( auto via = Get < HistoryMessageVia > ( ) ) {
2016-03-28 15:51:22 +03:00
via - > create ( config . viaBotId ) ;
2016-02-17 19:37:21 +03:00
}
2016-04-08 13:20:10 +04:00
if ( auto views = Get < HistoryMessageViews > ( ) ) {
2016-03-28 15:51:22 +03:00
views - > _views = config . viewsCount ;
2016-02-17 19:37:21 +03:00
}
2016-04-08 13:20:10 +04:00
if ( auto msgsigned = Get < HistoryMessageSigned > ( ) ) {
2016-02-17 19:37:21 +03:00
msgsigned - > create ( _from - > asUser ( ) , date ) ;
}
2016-04-23 14:40:42 +03:00
if ( auto edited = Get < HistoryMessageEdited > ( ) ) {
edited - > create ( config . editDate , date ) ;
}
2016-04-08 13:20:10 +04:00
if ( auto fwd = Get < HistoryMessageForwarded > ( ) ) {
2016-03-28 15:51:22 +03:00
fwd - > _authorOriginal = App : : peer ( config . authorIdOriginal ) ;
fwd - > _fromOriginal = App : : peer ( config . fromIdOriginal ) ;
fwd - > _originalId = config . originalId ;
}
2016-04-08 13:20:10 +04:00
if ( auto markup = Get < HistoryMessageReplyMarkup > ( ) ) {
2016-03-28 15:51:22 +03:00
markup - > create ( * config . markup ) ;
2016-04-08 18:16:52 +04:00
if ( markup - > flags & MTPDreplyKeyboardMarkup_ClientFlag : : f_has_switch_inline_button ) {
_flags | = MTPDmessage_ClientFlag : : f_has_switch_inline_button ;
}
2016-02-18 22:12:50 +03:00
}
2016-02-17 19:37:21 +03:00
initTime ( ) ;
}
2015-09-21 23:57:42 +03:00
QString formatViewsCount ( int32 views ) {
if ( views > 999999 ) {
views / = 100000 ;
if ( views % 10 ) {
return QString : : number ( views / 10 ) + ' . ' + QString : : number ( views % 10 ) + ' M ' ;
}
return QString : : number ( views / 10 ) + ' M ' ;
} else if ( views > 9999 ) {
views / = 100 ;
if ( views % 10 ) {
return QString : : number ( views / 10 ) + ' . ' + QString : : number ( views % 10 ) + ' K ' ;
}
return QString : : number ( views / 10 ) + ' K ' ;
} else if ( views > 0 ) {
return QString : : number ( views ) ;
}
return qsl ( " 1 " ) ;
}
2015-04-30 16:53:36 +03:00
void HistoryMessage : : initTime ( ) {
2016-04-08 13:20:10 +04:00
if ( auto msgsigned = Get < HistoryMessageSigned > ( ) ) {
2016-02-17 19:37:21 +03:00
_timeWidth = msgsigned - > maxWidth ( ) ;
2016-04-23 14:40:42 +03:00
} else if ( auto edited = Get < HistoryMessageEdited > ( ) ) {
_timeWidth = edited - > maxWidth ( ) ;
2016-02-17 19:37:21 +03:00
} else {
_timeText = date . toString ( cTimeFormat ( ) ) ;
_timeWidth = st : : msgDateFont - > width ( _timeText ) ;
}
2016-04-08 13:20:10 +04:00
if ( auto views = Get < HistoryMessageViews > ( ) ) {
2016-02-17 19:37:21 +03:00
views - > _viewsText = ( views - > _views > = 0 ) ? formatViewsCount ( views - > _views ) : QString ( ) ;
views - > _viewsWidth = views - > _viewsText . isEmpty ( ) ? 0 : st : : msgDateFont - > width ( views - > _viewsText ) ;
}
2015-04-30 16:53:36 +03:00
}
2015-08-30 17:57:21 +03:00
void HistoryMessage : : initMedia ( const MTPMessageMedia * media , QString & currentText ) {
switch ( media ? media - > type ( ) : mtpc_messageMediaEmpty ) {
2014-05-30 12:53:19 +04:00
case mtpc_messageMediaContact : {
2016-04-08 14:44:35 +04:00
const auto & d ( media - > c_messageMediaContact ( ) ) ;
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryContact ( this , d . vuser_id . v , qs ( d . vfirst_name ) , qs ( d . vlast_name ) , qs ( d . vphone_number ) ) ) ;
2014-05-30 12:53:19 +04:00
} break ;
case mtpc_messageMediaGeo : {
2016-04-08 14:44:35 +04:00
const auto & point ( media - > c_messageMediaGeo ( ) . vgeo ) ;
2014-05-30 12:53:19 +04:00
if ( point . type ( ) = = mtpc_geoPoint ) {
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryLocation ( this , LocationCoords ( point . c_geoPoint ( ) ) ) ) ;
2014-05-30 12:53:19 +04:00
}
} break ;
2015-04-30 16:53:36 +03:00
case mtpc_messageMediaVenue : {
2016-04-08 14:44:35 +04:00
const auto & d ( media - > c_messageMediaVenue ( ) ) ;
2015-04-30 16:53:36 +03:00
if ( d . vgeo . type ( ) = = mtpc_geoPoint ) {
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryLocation ( this , LocationCoords ( d . vgeo . c_geoPoint ( ) ) , qs ( d . vtitle ) , qs ( d . vaddress ) ) ) ;
2015-04-30 16:53:36 +03:00
}
} break ;
2014-05-30 12:53:19 +04:00
case mtpc_messageMediaPhoto : {
2016-04-08 14:44:35 +04:00
const auto & photo ( media - > c_messageMediaPhoto ( ) ) ;
2015-04-30 16:53:36 +03:00
if ( photo . vphoto . type ( ) = = mtpc_photo ) {
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryPhoto ( this , App : : feedPhoto ( photo . vphoto . c_photo ( ) ) , qs ( photo . vcaption ) ) ) ;
2014-05-30 12:53:19 +04:00
}
} break ;
case mtpc_messageMediaDocument : {
2016-04-08 14:44:35 +04:00
const auto & document ( media - > c_messageMediaDocument ( ) . vdocument ) ;
2014-05-30 12:53:19 +04:00
if ( document . type ( ) = = mtpc_document ) {
2015-12-28 13:28:00 +03:00
return initMediaFromDocument ( App : : feedDocument ( document ) , qs ( media - > c_messageMediaDocument ( ) . vcaption ) ) ;
2014-05-30 12:53:19 +04:00
}
} break ;
2015-04-04 23:01:34 +03:00
case mtpc_messageMediaWebPage : {
2016-04-08 14:44:35 +04:00
const auto & d ( media - > c_messageMediaWebPage ( ) . vwebpage ) ;
2015-04-04 23:01:34 +03:00
switch ( d . type ( ) ) {
2015-12-19 15:27:03 +03:00
case mtpc_webPageEmpty : break ;
2015-04-04 23:01:34 +03:00
case mtpc_webPagePending : {
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryWebPage ( this , App : : feedWebPage ( d . c_webPagePending ( ) ) ) ) ;
2015-04-04 23:01:34 +03:00
} break ;
case mtpc_webPage : {
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryWebPage ( this , App : : feedWebPage ( d . c_webPage ( ) ) ) ) ;
2015-04-04 23:01:34 +03:00
} break ;
}
} break ;
2014-05-30 12:53:19 +04:00
} ;
}
2015-12-28 13:28:00 +03:00
void HistoryMessage : : initMediaFromDocument ( DocumentData * doc , const QString & caption ) {
2015-07-01 00:07:05 +03:00
if ( doc - > sticker ( ) ) {
2016-04-10 18:53:01 +04:00
_media . reset ( new HistorySticker ( this , doc ) ) ;
2015-12-21 16:14:29 +03:00
} else if ( doc - > isAnimation ( ) ) {
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryGif ( this , doc , caption ) ) ;
2016-02-12 21:18:32 +03:00
} else if ( doc - > isVideo ( ) ) {
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryVideo ( this , doc , caption ) ) ;
2015-01-02 17:55:24 +03:00
} else {
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryDocument ( this , doc , caption ) ) ;
2015-01-02 17:55:24 +03:00
}
}
2015-12-09 21:06:20 +03:00
int32 HistoryMessage : : plainMaxWidth ( ) const {
return st : : msgPadding . left ( ) + _text . maxWidth ( ) + st : : msgPadding . right ( ) ;
}
2015-09-10 13:30:59 +03:00
void HistoryMessage : : initDimensions ( ) {
2016-06-08 21:46:37 +03:00
auto reply = Get < HistoryMessageReply > ( ) ;
if ( reply ) {
reply - > updateName ( ) ;
}
2015-12-08 22:07:50 +03:00
if ( drawBubble ( ) ) {
2016-03-25 14:29:45 +03:00
auto fwd = Get < HistoryMessageForwarded > ( ) ;
auto via = Get < HistoryMessageVia > ( ) ;
2016-02-18 22:12:50 +03:00
if ( fwd ) {
2016-02-19 14:53:49 +03:00
fwd - > create ( via ) ;
2016-02-18 22:12:50 +03:00
}
2015-04-04 23:01:34 +03:00
if ( _media ) {
2016-04-10 18:53:01 +04:00
_media - > initDimensions ( ) ;
2015-12-09 21:06:20 +03:00
if ( _media - > isDisplayed ( ) ) {
if ( _text . hasSkipBlock ( ) ) {
_text . removeSkipBlock ( ) ;
2016-05-04 19:46:24 +03:00
_textWidth = - 1 ;
2015-12-09 21:06:20 +03:00
_textHeight = 0 ;
}
} else if ( ! _text . hasSkipBlock ( ) ) {
2015-09-15 11:50:54 +03:00
_text . setSkipBlock ( skipBlockWidth ( ) , skipBlockHeight ( ) ) ;
2016-05-04 19:46:24 +03:00
_textWidth = - 1 ;
2015-08-24 13:53:04 +03:00
_textHeight = 0 ;
2015-05-20 22:28:24 +03:00
}
2015-04-04 23:01:34 +03:00
}
2015-12-28 00:37:48 +03:00
_maxw = plainMaxWidth ( ) ;
if ( _text . isEmpty ( ) ) {
_minh = 0 ;
} else {
2016-08-14 22:56:26 +03:00
_minh = st : : msgPadding . top ( ) + _text . minHeight ( ) + st : : msgPadding . bottom ( ) ;
2015-12-28 00:37:48 +03:00
}
if ( _media & & _media - > isDisplayed ( ) ) {
int32 maxw = _media - > maxWidth ( ) ;
if ( maxw > _maxw ) _maxw = maxw ;
_minh + = _media - > minHeight ( ) ;
}
2016-01-09 15:11:23 +08:00
if ( ! _media ) {
if ( displayFromName ( ) ) {
2016-02-17 19:37:21 +03:00
int32 namew = st : : msgPadding . left ( ) + author ( ) - > nameText . maxWidth ( ) + st : : msgPadding . right ( ) ;
2016-02-18 22:12:50 +03:00
if ( via & & ! fwd ) {
namew + = st : : msgServiceFont - > spacew + via - > _maxWidth ;
2016-01-09 15:11:23 +08:00
}
if ( namew > _maxw ) _maxw = namew ;
2016-02-18 22:12:50 +03:00
} else if ( via & & ! fwd ) {
if ( st : : msgPadding . left ( ) + via - > _maxWidth + st : : msgPadding . right ( ) > _maxw ) {
_maxw = st : : msgPadding . left ( ) + via - > _maxWidth + st : : msgPadding . right ( ) ;
2016-01-09 15:11:23 +08:00
}
2015-12-31 23:27:21 +08:00
}
2016-02-18 22:12:50 +03:00
if ( fwd ) {
2016-02-19 14:53:49 +03:00
int32 _namew = st : : msgPadding . left ( ) + fwd - > _text . maxWidth ( ) + st : : msgPadding . right ( ) ;
2016-02-18 22:12:50 +03:00
if ( via ) {
_namew + = st : : msgServiceFont - > spacew + via - > _maxWidth ;
}
if ( _namew > _maxw ) _maxw = _namew ;
}
2015-12-31 23:27:21 +08:00
}
2016-06-13 21:42:25 +03:00
} else if ( _media ) {
2016-04-10 18:53:01 +04:00
_media - > initDimensions ( ) ;
2015-12-08 22:07:50 +03:00
_maxw = _media - > maxWidth ( ) ;
_minh = _media - > minHeight ( ) ;
2016-06-13 21:42:25 +03:00
} else {
_maxw = st : : msgMinWidth ;
_minh = 0 ;
2014-05-30 12:53:19 +04:00
}
2016-06-08 21:46:37 +03:00
if ( reply & & ! _text . isEmpty ( ) ) {
int replyw = st : : msgPadding . left ( ) + reply - > _maxReplyWidth - st : : msgReplyPadding . left ( ) - st : : msgReplyPadding . right ( ) + st : : msgPadding . right ( ) ;
if ( reply - > _replyToVia ) {
replyw + = st : : msgServiceFont - > spacew + reply - > _replyToVia - > _maxWidth ;
2016-03-25 14:29:45 +03:00
}
2016-06-08 21:46:37 +03:00
if ( replyw > _maxw ) _maxw = replyw ;
2016-03-25 14:29:45 +03:00
}
2016-06-15 08:36:59 +03:00
if ( auto markup = inlineReplyMarkup ( ) ) {
2016-03-28 20:15:17 +03:00
if ( ! markup - > inlineKeyboard ) {
2016-04-10 23:20:48 +04:00
markup - > inlineKeyboard . reset ( new ReplyKeyboard ( this , std_ : : make_unique < KeyboardStyle > ( st : : msgBotKbButton ) ) ) ;
2016-03-28 20:15:17 +03:00
}
2016-03-30 20:42:01 +04:00
// if we have a text bubble we can resize it to fit the keyboard
// but if we have only media we don't do that
if ( ! _text . isEmpty ( ) ) {
_maxw = qMax ( _maxw , markup - > inlineKeyboard - > naturalWidth ( ) ) ;
}
2016-03-28 15:51:22 +03:00
}
2014-05-30 12:53:19 +04:00
}
2015-12-09 21:06:20 +03:00
void HistoryMessage : : countPositionAndSize ( int32 & left , int32 & width ) const {
2016-02-22 10:01:10 +03:00
int32 maxwidth = qMin ( int ( st : : msgMaxWidth ) , _maxw ) , hwidth = _history - > width ;
2016-02-08 17:54:55 +03:00
if ( _media & & _media - > currentWidth ( ) < maxwidth ) {
maxwidth = qMax ( _media - > currentWidth ( ) , qMin ( maxwidth , plainMaxWidth ( ) ) ) ;
2015-12-09 21:06:20 +03:00
}
2016-02-22 11:11:53 +03:00
left = ( ! isPost ( ) & & out ( ) & & ! Adaptive : : Wide ( ) ) ? st : : msgMargin . right ( ) : st : : msgMargin . left ( ) ;
2016-03-21 21:40:00 +03:00
if ( hasFromPhoto ( ) ) {
2016-02-09 19:05:08 +03:00
left + = st : : msgPhotoSkip ;
2016-02-12 19:35:06 +03:00
// } else if (!Adaptive::Wide() && !out() && !fromChannel() && st::msgPhotoSkip - (hmaxwidth - hwidth) > 0) {
// left += st::msgPhotoSkip - (hmaxwidth - hwidth);
2015-12-09 21:06:20 +03:00
}
2016-02-07 18:38:49 +03:00
width = hwidth - st : : msgMargin . left ( ) - st : : msgMargin . right ( ) ;
2016-02-08 17:54:55 +03:00
if ( width > maxwidth ) {
2016-02-17 19:37:21 +03:00
if ( ! isPost ( ) & & out ( ) & & ! Adaptive : : Wide ( ) ) {
2016-02-08 17:54:55 +03:00
left + = width - maxwidth ;
2015-12-09 21:06:20 +03:00
}
2016-02-08 17:54:55 +03:00
width = maxwidth ;
2015-12-09 21:06:20 +03:00
}
}
2016-01-09 15:11:23 +08:00
void HistoryMessage : : fromNameUpdated ( int32 width ) const {
2016-02-17 19:37:21 +03:00
_authorNameVersion = author ( ) - > nameVersion ;
2016-03-25 14:29:45 +03:00
if ( ! Has < HistoryMessageForwarded > ( ) ) {
2016-04-08 13:20:10 +04:00
if ( auto via = Get < HistoryMessageVia > ( ) ) {
2016-02-18 22:12:50 +03:00
via - > resize ( width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) - author ( ) - > nameText . maxWidth ( ) - st : : msgServiceFont - > spacew ) ;
2016-01-03 17:46:30 +08:00
}
2015-12-09 21:06:20 +03:00
}
2014-05-30 12:53:19 +04:00
}
2016-04-01 14:23:40 +04:00
void HistoryMessage : : applyEdition ( const MTPDmessage & message ) {
2016-04-08 13:20:10 +04:00
int keyboardTop = - 1 ;
if ( ! pendingResize ( ) ) {
if ( auto keyboard = inlineReplyKeyboard ( ) ) {
int h = st : : msgBotKbButton . margin + keyboard - > naturalHeight ( ) ;
keyboardTop = _height - h + st : : msgBotKbButton . margin - marginBottom ( ) ;
}
}
2016-04-23 14:40:42 +03:00
if ( message . has_edit_date ( ) ) {
_flags | = MTPDmessage : : Flag : : f_edit_date ;
2016-06-08 21:46:37 +03:00
if ( displayEditedBadge ( Has < HistoryMessageVia > ( ) ) ) {
if ( ! Has < HistoryMessageEdited > ( ) ) {
AddComponents ( HistoryMessageEdited : : Bit ( ) ) ;
}
Get < HistoryMessageEdited > ( ) - > create ( : : date ( message . vedit_date ) , date ) ;
} else if ( Has < HistoryMessageEdited > ( ) ) {
RemoveComponents ( HistoryMessageEdited : : Bit ( ) ) ;
2016-04-23 14:40:42 +03:00
}
initTime ( ) ;
}
2016-05-06 20:33:48 +03:00
TextWithEntities textWithEntities = { qs ( message . vmessage ) , EntitiesInText ( ) } ;
if ( message . has_entities ( ) ) {
textWithEntities . entities = entitiesFromMTP ( message . ventities . c_vector ( ) . v ) ;
}
setText ( textWithEntities ) ;
2016-04-01 14:23:40 +04:00
setMedia ( message . has_media ( ) ? ( & message . vmedia ) : nullptr ) ;
setReplyMarkup ( message . has_reply_markup ( ) ? ( & message . vreply_markup ) : nullptr ) ;
setViewsCount ( message . has_views ( ) ? message . vviews . v : - 1 ) ;
2016-06-13 21:42:25 +03:00
finishEdition ( keyboardTop ) ;
}
2016-04-08 13:20:10 +04:00
2016-09-23 21:26:53 +03:00
void HistoryMessage : : applyEdition ( const MTPDmessageService & message ) {
if ( message . vaction . type ( ) = = mtpc_messageActionHistoryClear ) {
applyEditionToEmpty ( ) ;
}
}
2016-06-13 21:42:25 +03:00
void HistoryMessage : : applyEditionToEmpty ( ) {
setEmptyText ( ) ;
setMedia ( nullptr ) ;
setReplyMarkup ( nullptr ) ;
setViewsCount ( - 1 ) ;
2016-05-04 19:46:24 +03:00
2016-06-13 21:42:25 +03:00
finishEditionToEmpty ( ) ;
2016-04-01 14:23:40 +04:00
}
2016-04-06 12:00:37 +04:00
void HistoryMessage : : updateMedia ( const MTPMessageMedia * media ) {
2016-04-10 18:53:01 +04:00
if ( _flags & MTPDmessage_ClientFlag : : f_from_inline_bot ) {
bool needReSet = true ;
if ( media & & _media ) {
needReSet = _media - > needReSetInlineResultMedia ( * media ) ;
}
if ( needReSet ) {
setMedia ( media ) ;
}
_flags & = ~ MTPDmessage_ClientFlag : : f_from_inline_bot ;
} else if ( media & & _media & & _media - > type ( ) ! = MediaTypeWebPage ) {
_media - > updateSentMedia ( * media ) ;
2016-04-06 12:00:37 +04:00
} else {
setMedia ( media ) ;
}
setPendingInitDimensions ( ) ;
}
2016-01-03 09:43:42 +08:00
int32 HistoryMessage : : addToOverview ( AddToOverviewMethod method ) {
if ( ! indexInOverview ( ) ) return 0 ;
int32 result = 0 ;
2016-03-29 20:17:00 +03:00
if ( HistoryMedia * media = getMedia ( ) ) {
2016-05-20 19:01:06 +03:00
MediaOverviewType type = messageMediaToOverviewType ( media ) ;
2016-01-03 09:43:42 +08:00
if ( type ! = OverviewCount ) {
if ( history ( ) - > addToOverview ( type , id , method ) ) {
result | = ( 1 < < type ) ;
}
}
}
if ( hasTextLinks ( ) ) {
if ( history ( ) - > addToOverview ( OverviewLinks , id , method ) ) {
result | = ( 1 < < OverviewLinks ) ;
}
}
return result ;
}
void HistoryMessage : : eraseFromOverview ( ) {
2016-03-29 20:17:00 +03:00
if ( HistoryMedia * media = getMedia ( ) ) {
2016-05-20 19:01:06 +03:00
MediaOverviewType type = messageMediaToOverviewType ( media ) ;
2016-01-03 09:43:42 +08:00
if ( type ! = OverviewCount ) {
history ( ) - > eraseFromOverview ( type , id ) ;
}
}
if ( hasTextLinks ( ) ) {
history ( ) - > eraseFromOverview ( OverviewLinks , id ) ;
}
}
2016-05-06 20:33:48 +03:00
TextWithEntities HistoryMessage : : selectedText ( TextSelection selection ) const {
TextWithEntities result , textResult , mediaResult ;
2016-04-14 14:00:23 +03:00
if ( selection = = FullSelection ) {
2016-05-06 20:33:48 +03:00
textResult = _text . originalTextWithEntities ( AllTextSelection , ExpandLinksAll ) ;
2016-04-14 14:00:23 +03:00
} else {
2016-05-06 20:33:48 +03:00
textResult = _text . originalTextWithEntities ( selection , ExpandLinksAll ) ;
2016-04-14 14:00:23 +03:00
}
if ( _media ) {
mediaResult = _media - > selectedText ( toMediaSelection ( selection ) ) ;
}
2016-05-06 20:33:48 +03:00
if ( textResult . text . isEmpty ( ) ) {
2016-04-14 14:00:23 +03:00
result = mediaResult ;
2016-05-06 20:33:48 +03:00
} else if ( mediaResult . text . isEmpty ( ) ) {
2016-04-14 14:00:23 +03:00
result = textResult ;
2016-02-18 22:12:50 +03:00
} else {
2016-05-06 20:33:48 +03:00
result . text = textResult . text + qstr ( " \n \n " ) ;
result . entities = textResult . entities ;
appendTextWithEntities ( result , std_ : : move ( mediaResult ) ) ;
2016-02-18 22:12:50 +03:00
}
2016-04-08 13:20:10 +04:00
if ( auto fwd = Get < HistoryMessageForwarded > ( ) ) {
2016-02-18 22:12:50 +03:00
if ( selection = = FullSelection ) {
2016-05-06 20:33:48 +03:00
auto fwdinfo = fwd - > _text . originalTextWithEntities ( AllTextSelection , ExpandLinksAll ) ;
TextWithEntities wrapped ;
wrapped . text . reserve ( fwdinfo . text . size ( ) + 4 + result . text . size ( ) ) ;
wrapped . entities . reserve ( fwdinfo . entities . size ( ) + result . entities . size ( ) ) ;
wrapped . text . append ( ' [ ' ) ;
appendTextWithEntities ( wrapped , std_ : : move ( fwdinfo ) ) ;
wrapped . text . append ( qsl ( " ] \n " ) ) ;
appendTextWithEntities ( wrapped , std_ : : move ( result ) ) ;
2016-02-18 22:12:50 +03:00
result = wrapped ;
}
2014-05-30 12:53:19 +04:00
}
2016-04-08 13:20:10 +04:00
if ( auto reply = Get < HistoryMessageReply > ( ) ) {
2016-03-25 14:29:45 +03:00
if ( selection = = FullSelection & & reply - > replyToMsg ) {
2016-05-06 20:33:48 +03:00
TextWithEntities wrapped ;
wrapped . text . reserve ( lang ( lng_in_reply_to ) . size ( ) + reply - > replyToMsg - > author ( ) - > name . size ( ) + 4 + result . text . size ( ) ) ;
wrapped . text . append ( ' [ ' ) . append ( lang ( lng_in_reply_to ) ) . append ( ' ' ) . append ( reply - > replyToMsg - > author ( ) - > name ) . append ( qsl ( " ] \n " ) ) ;
appendTextWithEntities ( wrapped , std_ : : move ( result ) ) ;
2016-03-25 14:29:45 +03:00
result = wrapped ;
}
}
2016-02-18 22:12:50 +03:00
return result ;
2015-08-24 13:53:04 +03:00
}
2016-01-09 15:11:23 +08:00
void HistoryMessage : : setMedia ( const MTPMessageMedia * media ) {
2016-04-07 14:35:09 +04:00
if ( ! _media & & ( ! media | | media - > type ( ) = = mtpc_messageMediaEmpty ) ) return ;
2015-08-24 13:53:04 +03:00
2015-05-20 22:28:24 +03:00
bool mediaWasDisplayed = false ;
2015-04-08 02:03:32 +03:00
if ( _media ) {
2015-05-20 22:28:24 +03:00
mediaWasDisplayed = _media - > isDisplayed ( ) ;
2016-04-10 18:53:01 +04:00
_media . clear ( ) ;
2015-04-08 02:03:32 +03:00
}
2015-04-05 11:40:56 +03:00
QString t ;
initMedia ( media , t ) ;
2015-05-20 22:28:24 +03:00
if ( _media & & _media - > isDisplayed ( ) & & ! mediaWasDisplayed ) {
2015-08-24 13:53:04 +03:00
_text . removeSkipBlock ( ) ;
2016-05-04 19:46:24 +03:00
_textWidth = - 1 ;
2015-08-24 13:53:04 +03:00
_textHeight = 0 ;
2015-05-20 22:28:24 +03:00
} else if ( mediaWasDisplayed & & ( ! _media | | ! _media - > isDisplayed ( ) ) ) {
2015-09-15 11:50:54 +03:00
_text . setSkipBlock ( skipBlockWidth ( ) , skipBlockHeight ( ) ) ;
2016-05-04 19:46:24 +03:00
_textWidth = - 1 ;
2015-08-24 13:53:04 +03:00
_textHeight = 0 ;
2015-04-04 23:01:34 +03:00
}
}
2016-05-06 20:33:48 +03:00
void HistoryMessage : : setText ( const TextWithEntities & textWithEntities ) {
2016-02-17 19:37:21 +03:00
textstyleSet ( & ( ( out ( ) & & ! isPost ( ) ) ? st : : outTextStyle : st : : inTextStyle ) ) ;
2015-12-08 22:07:50 +03:00
if ( _media & & _media - > isDisplayed ( ) ) {
2016-05-06 20:33:48 +03:00
_text . setMarkedText ( st : : msgFont , textWithEntities , itemTextOptions ( this ) ) ;
2015-12-08 22:07:50 +03:00
} else {
2016-05-06 20:33:48 +03:00
_text . setMarkedText ( st : : msgFont , { textWithEntities . text + skipBlock ( ) , textWithEntities . entities } , itemTextOptions ( this ) ) ;
2015-12-08 22:07:50 +03:00
}
textstyleRestore ( ) ;
2016-05-06 20:33:48 +03:00
for_const ( auto & entity , textWithEntities . entities ) {
2016-04-29 15:00:48 +03:00
auto type = entity . type ( ) ;
if ( type = = EntityInTextUrl | | type = = EntityInTextCustomUrl | | type = = EntityInTextEmail ) {
2016-03-19 19:55:15 +03:00
_flags | = MTPDmessage_ClientFlag : : f_has_text_links ;
2016-01-03 09:43:42 +08:00
break ;
2015-08-24 13:53:04 +03:00
}
}
2016-05-04 19:46:24 +03:00
_textWidth = - 1 ;
2015-12-08 22:07:50 +03:00
_textHeight = 0 ;
2015-08-24 13:53:04 +03:00
}
2016-06-13 21:42:25 +03:00
void HistoryMessage : : setEmptyText ( ) {
textstyleSet ( & ( ( out ( ) & & ! isPost ( ) ) ? st : : outTextStyle : st : : inTextStyle ) ) ;
_text . setMarkedText ( st : : msgFont , { QString ( ) , EntitiesInText ( ) } , itemTextOptions ( this ) ) ;
textstyleRestore ( ) ;
_textWidth = - 1 ;
_textHeight = 0 ;
}
2016-04-01 14:23:40 +04:00
void HistoryMessage : : setReplyMarkup ( const MTPReplyMarkup * markup ) {
2016-04-10 15:41:14 +04:00
if ( ! markup ) {
if ( _flags & MTPDmessage : : Flag : : f_reply_markup ) {
_flags & = ~ MTPDmessage : : Flag : : f_reply_markup ;
if ( Has < HistoryMessageReplyMarkup > ( ) ) {
RemoveComponents ( HistoryMessageReplyMarkup : : Bit ( ) ) ;
}
setPendingInitDimensions ( ) ;
Notify : : replyMarkupUpdated ( this ) ;
}
return ;
}
2016-04-01 14:23:40 +04:00
// optimization: don't create markup component for the case
// MTPDreplyKeyboardHide with flags = 0, assume it has f_zero flag
if ( markup - > type ( ) = = mtpc_replyKeyboardHide & & markup - > c_replyKeyboardHide ( ) . vflags . v = = 0 ) {
2016-04-10 15:41:14 +04:00
bool changed = false ;
2016-04-01 14:23:40 +04:00
if ( Has < HistoryMessageReplyMarkup > ( ) ) {
RemoveComponents ( HistoryMessageReplyMarkup : : Bit ( ) ) ;
2016-04-10 15:41:14 +04:00
changed = true ;
}
if ( ! ( _flags & MTPDmessage : : Flag : : f_reply_markup ) ) {
_flags | = MTPDmessage : : Flag : : f_reply_markup ;
changed = true ;
}
if ( changed ) {
2016-04-01 14:23:40 +04:00
setPendingInitDimensions ( ) ;
2016-04-01 19:32:26 +04:00
Notify : : replyMarkupUpdated ( this ) ;
2016-04-01 14:23:40 +04:00
}
} else {
2016-04-10 15:41:14 +04:00
if ( ! ( _flags & MTPDmessage : : Flag : : f_reply_markup ) ) {
_flags | = MTPDmessage : : Flag : : f_reply_markup ;
}
2016-04-01 14:23:40 +04:00
if ( ! Has < HistoryMessageReplyMarkup > ( ) ) {
AddComponents ( HistoryMessageReplyMarkup : : Bit ( ) ) ;
}
Get < HistoryMessageReplyMarkup > ( ) - > create ( * markup ) ;
setPendingInitDimensions ( ) ;
2016-04-01 19:32:26 +04:00
Notify : : replyMarkupUpdated ( this ) ;
2016-04-01 14:23:40 +04:00
}
}
2016-05-06 20:33:48 +03:00
TextWithEntities HistoryMessage : : originalText ( ) const {
if ( emptyText ( ) ) {
return { QString ( ) , EntitiesInText ( ) } ;
}
return _text . originalTextWithEntities ( ) ;
2015-08-28 18:15:56 +03:00
}
2016-05-06 20:33:48 +03:00
bool HistoryMessage : : textHasLinks ( ) const {
2015-12-09 22:09:29 +03:00
return emptyText ( ) ? false : _text . hasLinks ( ) ;
2015-09-02 00:33:44 +03:00
}
2015-12-11 21:11:38 +03:00
void HistoryMessage : : drawInfo ( Painter & p , int32 right , int32 bottom , int32 width , bool selected , InfoDisplayType type ) const {
p . setFont ( st : : msgDateFont ) ;
2015-09-15 11:50:54 +03:00
2016-03-31 18:06:40 +04:00
bool outbg = out ( ) & & ! isPost ( ) ;
bool invertedsprites = ( type = = InfoDisplayOverImage | | type = = InfoDisplayOverBackground ) ;
2015-09-15 11:50:54 +03:00
int32 infoRight = right , infoBottom = bottom ;
switch ( type ) {
case InfoDisplayDefault :
infoRight - = st : : msgPadding . right ( ) - st : : msgDateDelta . x ( ) ;
infoBottom - = st : : msgPadding . bottom ( ) - st : : msgDateDelta . y ( ) ;
2016-03-31 18:06:40 +04:00
p . setPen ( selected ? ( outbg ? st : : msgOutDateFgSelected : st : : msgInDateFgSelected ) : ( outbg ? st : : msgOutDateFg : st : : msgInDateFg ) ) ;
2015-09-15 11:50:54 +03:00
break ;
case InfoDisplayOverImage :
infoRight - = st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ;
infoBottom - = st : : msgDateImgDelta + st : : msgDateImgPadding . y ( ) ;
2016-03-31 18:06:40 +04:00
p . setPen ( st : : msgDateImgColor ) ;
break ;
case InfoDisplayOverBackground :
infoRight - = st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ;
infoBottom - = st : : msgDateImgDelta + st : : msgDateImgPadding . y ( ) ;
p . setPen ( st : : msgServiceColor ) ;
2015-09-15 11:50:54 +03:00
break ;
}
int32 infoW = HistoryMessage : : infoWidth ( ) ;
2015-12-11 21:11:38 +03:00
if ( rtl ( ) ) infoRight = width - infoRight + infoW ;
2015-09-15 11:50:54 +03:00
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 ( ) ;
2015-12-11 21:11:38 +03:00
App : : roundRect ( p , dateX - st : : msgDateImgPadding . x ( ) , dateY - st : : msgDateImgPadding . y ( ) , dateW , dateH , selected ? st : : msgDateImgBgSelected : st : : msgDateImgBg , selected ? DateSelectedCorners : DateCorners ) ;
2016-03-31 18:06:40 +04:00
} else if ( type = = InfoDisplayOverBackground ) {
int32 dateW = infoW + 2 * st : : msgDateImgPadding . x ( ) , dateH = st : : msgDateFont - > height + 2 * st : : msgDateImgPadding . y ( ) ;
2016-07-08 13:06:41 +03:00
App : : roundRect ( p , dateX - st : : msgDateImgPadding . x ( ) , dateY - st : : msgDateImgPadding . y ( ) , dateW , dateH , selected ? App : : msgServiceSelectBg ( ) : App : : msgServiceBg ( ) , selected ? StickerSelectedCorners : StickerCorners ) ;
2015-09-15 11:50:54 +03:00
}
dateX + = HistoryMessage : : timeLeft ( ) ;
2016-04-08 13:20:10 +04:00
if ( auto msgsigned = Get < HistoryMessageSigned > ( ) ) {
2016-02-17 19:37:21 +03:00
msgsigned - > _signature . drawElided ( p , dateX , dateY , _timeWidth ) ;
2016-04-23 14:40:42 +03:00
} else if ( auto edited = Get < HistoryMessageEdited > ( ) ) {
edited - > _edited . drawElided ( p , dateX , dateY , _timeWidth ) ;
2016-02-17 19:37:21 +03:00
} else {
p . drawText ( dateX , dateY + st : : msgDateFont - > ascent , _timeText ) ;
}
2015-09-15 11:50:54 +03:00
QPoint iconPos ;
2016-04-21 20:57:29 +03:00
const style : : sprite * iconRect = nullptr ;
2016-04-08 13:20:10 +04:00
if ( auto views = Get < HistoryMessageViews > ( ) ) {
2015-09-15 11:50:54 +03:00
iconPos = QPoint ( infoRight - infoW + st : : msgViewsPos . x ( ) , infoBottom - st : : msgViewsImg . pxHeight ( ) + st : : msgViewsPos . y ( ) ) ;
if ( id > 0 ) {
2016-02-17 19:37:21 +03:00
if ( outbg ) {
2016-03-31 18:06:40 +04:00
iconRect = & ( invertedsprites ? st : : msgInvViewsImg : ( selected ? st : : msgSelectOutViewsImg : st : : msgOutViewsImg ) ) ;
2015-09-15 11:50:54 +03:00
} else {
2016-03-31 18:06:40 +04:00
iconRect = & ( invertedsprites ? st : : msgInvViewsImg : ( selected ? st : : msgSelectViewsImg : st : : msgViewsImg ) ) ;
2015-09-15 11:50:54 +03:00
}
2016-02-17 19:37:21 +03:00
p . drawText ( iconPos . x ( ) + st : : msgViewsImg . pxWidth ( ) + st : : msgDateCheckSpace , infoBottom - st : : msgDateFont - > descent , views - > _viewsText ) ;
2015-09-15 11:50:54 +03:00
} else {
2016-02-17 19:37:21 +03:00
iconPos . setX ( iconPos . x ( ) + st : : msgDateViewsSpace + views - > _viewsWidth ) ;
if ( outbg ) {
2016-03-31 18:06:40 +04:00
iconRect = & ( invertedsprites ? st : : msgInvSendingViewsImg : st : : msgSendingOutViewsImg ) ;
2015-09-15 11:50:54 +03:00
} else {
2016-03-31 18:06:40 +04:00
iconRect = & ( invertedsprites ? st : : msgInvSendingViewsImg : st : : msgSendingViewsImg ) ;
2015-09-15 11:50:54 +03:00
}
}
2016-04-21 20:57:29 +03:00
p . drawSprite ( iconPos , * iconRect ) ;
2015-10-27 20:29:39 -04:00
} else if ( id < 0 & & history ( ) - > peer - > isSelf ( ) ) {
iconPos = QPoint ( infoRight - infoW , infoBottom - st : : msgViewsImg . pxHeight ( ) + st : : msgViewsPos . y ( ) ) ;
2016-03-31 18:06:40 +04:00
iconRect = & ( invertedsprites ? st : : msgInvSendingViewsImg : st : : msgSendingViewsImg ) ;
2016-04-21 20:57:29 +03:00
p . drawSprite ( iconPos , * iconRect ) ;
2015-09-15 11:50:54 +03:00
}
2016-02-17 19:37:21 +03:00
if ( outbg ) {
2015-09-15 11:50:54 +03:00
iconPos = QPoint ( infoRight - st : : msgCheckImg . pxWidth ( ) + st : : msgCheckPos . x ( ) , infoBottom - st : : msgCheckImg . pxHeight ( ) + st : : msgCheckPos . y ( ) ) ;
if ( id > 0 ) {
if ( unread ( ) ) {
2016-03-31 18:06:40 +04:00
iconRect = & ( invertedsprites ? st : : msgInvCheckImg : ( selected ? st : : msgSelectCheckImg : st : : msgCheckImg ) ) ;
2015-09-15 11:50:54 +03:00
} else {
2016-03-31 18:06:40 +04:00
iconRect = & ( invertedsprites ? st : : msgInvDblCheckImg : ( selected ? st : : msgSelectDblCheckImg : st : : msgDblCheckImg ) ) ;
2015-09-15 11:50:54 +03:00
}
} else {
2016-03-31 18:06:40 +04:00
iconRect = & ( invertedsprites ? st : : msgInvSendingImg : st : : msgSendingImg ) ;
2015-09-15 11:50:54 +03:00
}
2016-04-21 20:57:29 +03:00
p . drawSprite ( iconPos , * iconRect ) ;
2015-09-15 11:50:54 +03:00
}
}
2016-03-19 19:55:15 +03:00
void HistoryMessage : : setViewsCount ( int32 count ) {
2016-04-08 13:20:10 +04:00
auto views = Get < HistoryMessageViews > ( ) ;
2016-02-17 19:37:21 +03:00
if ( ! views | | views - > _views = = count | | ( count > = 0 & & views - > _views > count ) ) return ;
int32 was = views - > _viewsWidth ;
views - > _views = count ;
views - > _viewsText = ( views - > _views > = 0 ) ? formatViewsCount ( views - > _views ) : QString ( ) ;
views - > _viewsWidth = views - > _viewsText . isEmpty ( ) ? 0 : st : : msgDateFont - > width ( views - > _viewsText ) ;
if ( was = = views - > _viewsWidth ) {
2015-12-28 00:37:48 +03:00
Ui : : repaintHistoryItem ( this ) ;
2015-09-15 11:50:54 +03:00
} else {
if ( _text . hasSkipBlock ( ) ) {
_text . setSkipBlock ( HistoryMessage : : skipBlockWidth ( ) , HistoryMessage : : skipBlockHeight ( ) ) ;
2016-05-04 19:46:24 +03:00
_textWidth = - 1 ;
2015-09-15 11:50:54 +03:00
_textHeight = 0 ;
}
2016-03-19 19:55:15 +03:00
setPendingInitDimensions ( ) ;
2015-09-15 11:50:54 +03:00
}
}
2015-10-27 20:29:39 -04:00
void HistoryMessage : : setId ( MsgId newId ) {
bool wasPositive = ( id > 0 ) , positive = ( newId > 0 ) ;
2015-11-18 16:11:56 +03:00
HistoryItem : : setId ( newId ) ;
2015-10-27 20:29:39 -04:00
if ( wasPositive = = positive ) {
2015-12-28 00:37:48 +03:00
Ui : : repaintHistoryItem ( this ) ;
2015-10-27 20:29:39 -04:00
} else {
if ( _text . hasSkipBlock ( ) ) {
_text . setSkipBlock ( HistoryMessage : : skipBlockWidth ( ) , HistoryMessage : : skipBlockHeight ( ) ) ;
2016-05-04 19:46:24 +03:00
_textWidth = - 1 ;
2015-10-27 20:29:39 -04:00
_textHeight = 0 ;
}
2016-03-19 19:55:15 +03:00
setPendingInitDimensions ( ) ;
2015-10-27 20:29:39 -04:00
}
}
2016-04-13 21:29:32 +03:00
void HistoryMessage : : draw ( Painter & p , const QRect & r , TextSelection selection , uint64 ms ) const {
2016-02-17 19:37:21 +03:00
bool outbg = out ( ) & & ! isPost ( ) , bubble = drawBubble ( ) , selected = ( selection = = FullSelection ) ;
2015-09-13 20:27:29 +03:00
2016-03-19 19:55:15 +03:00
int left = 0 , width = 0 , height = _height ;
countPositionAndSize ( left , width ) ;
if ( width < 1 ) return ;
int dateh = 0 , unreadbarh = 0 ;
2016-04-08 13:20:10 +04:00
if ( auto date = Get < HistoryMessageDate > ( ) ) {
2016-03-19 19:55:15 +03:00
dateh = date - > height ( ) ;
2016-06-10 13:21:09 +03:00
//if (r.intersects(QRect(0, 0, _history->width, dateh))) {
// date->paint(p, 0, _history->width);
//}
2016-03-19 19:55:15 +03:00
}
2016-04-08 13:20:10 +04:00
if ( auto unreadbar = Get < HistoryMessageUnreadBar > ( ) ) {
2016-03-19 19:55:15 +03:00
unreadbarh = unreadbar - > height ( ) ;
2016-04-09 17:56:40 +04:00
if ( r . intersects ( QRect ( 0 , dateh , _history - > width , unreadbarh ) ) ) {
p . translate ( 0 , dateh ) ;
unreadbar - > paint ( p , 0 , _history - > width ) ;
p . translate ( 0 , - dateh ) ;
}
2016-03-19 19:55:15 +03:00
}
2016-04-19 13:59:59 +03:00
uint64 fullAnimMs = App : : main ( ) ? App : : main ( ) - > animActiveTimeStart ( this ) : 0 ;
if ( fullAnimMs > 0 & & fullAnimMs < = ms ) {
int animms = ms - fullAnimMs ;
2015-12-11 21:11:38 +03:00
if ( animms > st : : activeFadeInDuration + st : : activeFadeOutDuration ) {
2015-07-17 22:17:37 +03:00
App : : main ( ) - > stopAnimActive ( ) ;
} else {
2016-03-22 18:23:34 +03:00
int skiph = marginTop ( ) - marginBottom ( ) ;
2015-12-11 21:11:38 +03:00
float64 dt = ( animms > st : : activeFadeInDuration ) ? ( 1 - ( animms - st : : activeFadeInDuration ) / float64 ( st : : activeFadeOutDuration ) ) : ( animms / float64 ( st : : activeFadeInDuration ) ) ;
2015-07-17 22:17:37 +03:00
float64 o = p . opacity ( ) ;
p . setOpacity ( o * dt ) ;
2016-03-21 21:40:00 +03:00
p . fillRect ( 0 , skiph , _history - > width , height - skiph , textstyleCurrent ( ) - > selectOverlay - > b ) ;
2015-07-17 22:17:37 +03:00
p . setOpacity ( o ) ;
2014-07-04 15:12:54 +04:00
}
}
2016-03-21 21:40:00 +03:00
textstyleSet ( & ( outbg ? st : : outTextStyle : st : : inTextStyle ) ) ;
2016-03-28 20:15:17 +03:00
if ( const ReplyKeyboard * keyboard = inlineReplyKeyboard ( ) ) {
int h = st : : msgBotKbButton . margin + keyboard - > naturalHeight ( ) ;
height - = h ;
2016-03-30 12:03:29 +04:00
int top = height + st : : msgBotKbButton . margin - marginBottom ( ) ;
2016-03-28 20:15:17 +03:00
p . translate ( left , top ) ;
keyboard - > paint ( p , r . translated ( - left , - top ) ) ;
p . translate ( - left , - top ) ;
2016-03-28 15:51:22 +03:00
}
2014-05-30 12:53:19 +04:00
2015-12-09 21:06:20 +03:00
if ( bubble ) {
2016-04-08 13:20:10 +04:00
auto fwd = Get < HistoryMessageForwarded > ( ) ;
auto via = Get < HistoryMessageVia > ( ) ;
2016-02-18 22:12:50 +03:00
if ( displayFromName ( ) & & author ( ) - > nameVersion > _authorNameVersion ) {
fromNameUpdated ( width ) ;
}
2016-03-21 21:40:00 +03:00
int32 top = marginTop ( ) ;
QRect r ( left , top , width , height - top - marginBottom ( ) ) ;
2014-05-30 12:53:19 +04:00
2015-12-11 21:11:38 +03:00
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 ) ) ;
2015-09-13 20:27:29 +03:00
RoundCorners cors ( selected ? ( outbg ? MessageOutSelectedCorners : MessageInSelectedCorners ) : ( outbg ? MessageOutCorners : MessageInCorners ) ) ;
2015-05-20 22:28:24 +03:00
App : : roundRect ( p , r , bg , cors , & sh ) ;
2014-05-30 12:53:19 +04:00
2015-09-03 13:48:40 +03:00
if ( displayFromName ( ) ) {
2015-12-31 23:27:21 +08:00
p . setFont ( st : : msgNameFont ) ;
2016-02-17 19:37:21 +03:00
if ( isPost ( ) ) {
2015-12-11 21:11:38 +03:00
p . setPen ( selected ? st : : msgInServiceFgSelected : st : : msgInServiceFg ) ;
2015-09-19 12:13:21 +03:00
} else {
2016-02-17 19:37:21 +03:00
p . setPen ( author ( ) - > color ) ;
2015-09-19 12:13:21 +03:00
}
2016-02-17 19:37:21 +03:00
author ( ) - > nameText . drawElided ( p , r . left ( ) + st : : msgPadding . left ( ) , r . top ( ) + st : : msgPadding . top ( ) , width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) ;
2016-02-18 22:12:50 +03:00
if ( via & & ! fwd & & width > st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + author ( ) - > nameText . maxWidth ( ) + st : : msgServiceFont - > spacew ) {
2016-01-03 17:46:30 +08:00
p . setPen ( selected ? ( outbg ? st : : msgOutServiceFgSelected : st : : msgInServiceFgSelected ) : ( outbg ? st : : msgOutServiceFg : st : : msgInServiceFg ) ) ;
2016-02-18 22:12:50 +03:00
p . drawText ( r . left ( ) + st : : msgPadding . left ( ) + author ( ) - > nameText . maxWidth ( ) + st : : msgServiceFont - > spacew , r . top ( ) + st : : msgPadding . top ( ) + st : : msgServiceFont - > ascent , via - > _text ) ;
2016-01-03 17:46:30 +08:00
}
2014-05-30 12:53:19 +04:00
r . setTop ( r . top ( ) + st : : msgNameFont - > height ) ;
}
2015-12-31 23:27:21 +08:00
2014-05-30 12:53:19 +04:00
QRect trect ( r . marginsAdded ( - st : : msgPadding ) ) ;
2016-03-25 14:29:45 +03:00
paintForwardedInfo ( p , trect , selected ) ;
paintReplyInfo ( p , trect , selected ) ;
paintViaBotIdInfo ( p , trect , selected ) ;
p . setPen ( st : : msgColor ) ;
p . setFont ( st : : msgFont ) ;
2016-04-13 21:29:32 +03:00
_text . draw ( p , trect . x ( ) , trect . y ( ) , trect . width ( ) , style : : al_left , 0 , - 1 , selection ) ;
2014-05-30 12:53:19 +04:00
2015-12-09 21:06:20 +03:00
if ( _media & & _media - > isDisplayed ( ) ) {
2016-03-21 21:40:00 +03:00
int32 top = height - marginBottom ( ) - _media - > height ( ) ;
2015-12-11 21:11:38 +03:00
p . translate ( left , top ) ;
2016-04-13 21:29:32 +03:00
_media - > draw ( p , r . translated ( - left , - top ) , toMediaSelection ( selection ) , ms ) ;
2016-03-19 19:55:15 +03:00
p . translate ( - left , - top ) ;
2015-12-09 21:06:20 +03:00
if ( ! _media - > customInfoLayout ( ) ) {
2015-12-11 21:11:38 +03:00
HistoryMessage : : drawInfo ( p , r . x ( ) + r . width ( ) , r . y ( ) + r . height ( ) , 2 * r . x ( ) + r . width ( ) , selected , InfoDisplayDefault ) ;
2015-12-09 21:06:20 +03:00
}
} else {
2015-12-11 21:11:38 +03:00
HistoryMessage : : drawInfo ( p , r . x ( ) + r . width ( ) , r . y ( ) + r . height ( ) , 2 * r . x ( ) + r . width ( ) , selected , InfoDisplayDefault ) ;
2015-04-04 23:01:34 +03:00
}
2016-06-13 21:42:25 +03:00
} else if ( _media ) {
2016-03-21 21:40:00 +03:00
int32 top = marginTop ( ) ;
2015-12-11 21:11:38 +03:00
p . translate ( left , top ) ;
2016-04-13 21:29:32 +03:00
_media - > draw ( p , r . translated ( - left , - top ) , toMediaSelection ( selection ) , ms ) ;
2016-03-19 19:55:15 +03:00
p . translate ( - left , - top ) ;
}
2015-11-23 18:34:38 +03:00
textstyleRestore ( ) ;
2016-06-08 21:46:37 +03:00
auto reply = Get < HistoryMessageReply > ( ) ;
if ( reply & & reply - > isNameUpdated ( ) ) {
const_cast < HistoryMessage * > ( this ) - > setPendingInitDimensions ( ) ;
}
2014-05-30 12:53:19 +04:00
}
2016-03-25 14:29:45 +03:00
void HistoryMessage : : paintForwardedInfo ( Painter & p , QRect & trect , bool selected ) const {
2016-02-18 22:12:50 +03:00
if ( displayForwardedFrom ( ) ) {
2016-02-19 14:53:49 +03:00
style : : font serviceFont ( st : : msgServiceFont ) , serviceName ( st : : msgServiceNameFont ) ;
2016-03-25 14:29:45 +03:00
p . setPen ( selected ? ( hasOutLayout ( ) ? st : : msgOutServiceFgSelected : st : : msgInServiceFgSelected ) : ( hasOutLayout ( ) ? st : : msgOutServiceFg : st : : msgInServiceFg ) ) ;
2016-02-19 14:53:49 +03:00
p . setFont ( serviceFont ) ;
2016-04-08 13:20:10 +04:00
auto fwd = Get < HistoryMessageForwarded > ( ) ;
2016-02-19 14:53:49 +03:00
bool breakEverywhere = ( fwd - > _text . countHeight ( trect . width ( ) ) > 2 * serviceFont - > height ) ;
2016-03-25 14:29:45 +03:00
textstyleSet ( & ( selected ? ( hasOutLayout ( ) ? st : : outFwdTextStyleSelected : st : : inFwdTextStyleSelected ) : ( hasOutLayout ( ) ? st : : outFwdTextStyle : st : : inFwdTextStyle ) ) ) ;
2016-02-19 14:53:49 +03:00
fwd - > _text . drawElided ( p , trect . x ( ) , trect . y ( ) , trect . width ( ) , 2 , style : : al_left , 0 , - 1 , 0 , breakEverywhere ) ;
2016-03-25 14:29:45 +03:00
textstyleSet ( & ( hasOutLayout ( ) ? st : : outTextStyle : st : : inTextStyle ) ) ;
2016-02-19 14:53:49 +03:00
trect . setY ( trect . y ( ) + ( ( ( fwd - > _text . maxWidth ( ) > trect . width ( ) ) ? 2 : 1 ) * serviceFont - > height ) ) ;
2015-12-31 23:27:21 +08:00
}
2016-03-25 14:29:45 +03:00
}
2015-12-31 23:27:21 +08:00
2016-03-25 14:29:45 +03:00
void HistoryMessage : : paintReplyInfo ( Painter & p , QRect & trect , bool selected ) const {
2016-04-08 13:20:10 +04:00
if ( auto reply = Get < HistoryMessageReply > ( ) ) {
2016-03-25 14:29:45 +03:00
int32 h = st : : msgReplyPadding . top ( ) + st : : msgReplyBarSize . height ( ) + st : : msgReplyPadding . bottom ( ) ;
HistoryMessageReply : : PaintFlags flags = HistoryMessageReply : : PaintInBubble ;
if ( selected ) {
flags | = HistoryMessageReply : : PaintSelected ;
}
reply - > paint ( p , this , trect . x ( ) , trect . y ( ) , trect . width ( ) , flags ) ;
trect . setY ( trect . y ( ) + h ) ;
}
}
void HistoryMessage : : paintViaBotIdInfo ( Painter & p , QRect & trect , bool selected ) const {
if ( ! displayFromName ( ) & & ! Has < HistoryMessageForwarded > ( ) ) {
2016-04-08 13:20:10 +04:00
if ( auto via = Get < HistoryMessageVia > ( ) ) {
2016-02-18 22:12:50 +03:00
p . setFont ( st : : msgServiceNameFont ) ;
2016-03-25 14:29:45 +03:00
p . setPen ( selected ? ( hasOutLayout ( ) ? st : : msgOutServiceFgSelected : st : : msgInServiceFgSelected ) : ( hasOutLayout ( ) ? st : : msgOutServiceFg : st : : msgInServiceFg ) ) ;
2016-02-18 22:12:50 +03:00
p . drawTextLeft ( trect . left ( ) , trect . top ( ) , _history - > width , via - > _text ) ;
trect . setY ( trect . y ( ) + st : : msgServiceNameFont - > height ) ;
}
}
2016-03-25 14:29:45 +03:00
}
2016-02-18 22:12:50 +03:00
2016-03-25 14:29:45 +03:00
void HistoryMessage : : dependencyItemRemoved ( HistoryItem * dependency ) {
2016-04-08 13:20:10 +04:00
if ( auto reply = Get < HistoryMessageReply > ( ) ) {
2016-03-25 14:29:45 +03:00
reply - > itemRemoved ( this , dependency ) ;
}
2014-05-30 12:53:19 +04:00
}
2016-03-21 21:40:00 +03:00
int HistoryMessage : : resizeGetHeight_ ( int width ) {
2016-04-08 13:20:10 +04:00
int result = performResizeGetHeight ( width ) ;
auto keyboard = inlineReplyKeyboard ( ) ;
if ( auto markup = Get < HistoryMessageReplyMarkup > ( ) ) {
int oldTop = markup - > oldTop ;
if ( oldTop > = 0 ) {
markup - > oldTop = - 1 ;
if ( keyboard ) {
int h = st : : msgBotKbButton . margin + keyboard - > naturalHeight ( ) ;
int keyboardTop = _height - h + st : : msgBotKbButton . margin - marginBottom ( ) ;
if ( keyboardTop ! = oldTop ) {
Notify : : inlineKeyboardMoved ( this , oldTop , keyboardTop ) ;
}
}
}
}
return result ;
}
int HistoryMessage : : performResizeGetHeight ( int width ) {
2015-08-24 13:53:04 +03:00
if ( width < st : : msgMinWidth ) return _height ;
2014-05-30 12:53:19 +04:00
width - = st : : msgMargin . left ( ) + st : : msgMargin . right ( ) ;
2016-01-02 07:43:41 +08:00
if ( width < st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ) {
width = st : : msgPadding . left ( ) + st : : msgPadding . right ( ) + 1 ;
} else if ( width > st : : msgMaxWidth ) {
width = st : : msgMaxWidth ;
}
2015-12-08 22:07:50 +03:00
if ( drawBubble ( ) ) {
2016-04-08 13:20:10 +04:00
auto fwd = Get < HistoryMessageForwarded > ( ) ;
auto reply = Get < HistoryMessageReply > ( ) ;
auto via = Get < HistoryMessageVia > ( ) ;
2016-02-18 22:12:50 +03:00
2015-12-09 21:06:20 +03:00
bool media = ( _media & & _media - > isDisplayed ( ) ) ;
2014-05-30 12:53:19 +04:00
if ( width > = _maxw ) {
_height = _minh ;
2016-04-10 18:53:01 +04:00
if ( media ) _media - > resizeGetHeight ( _maxw ) ;
2014-05-30 12:53:19 +04:00
} else {
2015-12-09 21:06:20 +03:00
if ( _text . isEmpty ( ) ) {
_height = 0 ;
} else {
int32 textWidth = qMax ( width - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) , 1 ) ;
if ( textWidth ! = _textWidth ) {
2016-02-17 19:37:21 +03:00
textstyleSet ( & ( ( out ( ) & & ! isPost ( ) ) ? st : : outTextStyle : st : : inTextStyle ) ) ;
2015-12-09 21:06:20 +03:00
_textWidth = textWidth ;
2016-08-14 22:56:26 +03:00
_textHeight = _text . countHeight ( textWidth ) ;
2015-12-09 21:06:20 +03:00
textstyleRestore ( ) ;
}
_height = st : : msgPadding . top ( ) + _textHeight + st : : msgPadding . bottom ( ) ;
}
2016-04-10 18:53:01 +04:00
if ( media ) _height + = _media - > resizeGetHeight ( width ) ;
2014-05-30 12:53:19 +04:00
}
2016-01-09 15:11:23 +08:00
2015-09-03 13:48:40 +03:00
if ( displayFromName ( ) ) {
2015-12-09 22:09:29 +03:00
if ( emptyText ( ) ) {
2015-12-09 21:06:20 +03:00
_height + = st : : msgPadding . top ( ) + st : : msgNameFont - > height + st : : mediaHeaderSkip ;
} else {
_height + = st : : msgNameFont - > height ;
}
2016-01-11 12:45:07 +08:00
int32 l = 0 , w = 0 ;
countPositionAndSize ( l , w ) ;
2016-01-09 15:11:23 +08:00
fromNameUpdated ( w ) ;
2016-02-18 22:12:50 +03:00
} else if ( via & & ! fwd ) {
2016-01-11 12:45:07 +08:00
int32 l = 0 , w = 0 ;
countPositionAndSize ( l , w ) ;
2016-02-18 22:12:50 +03:00
via - > resize ( w - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) ;
2015-12-31 23:27:21 +08:00
if ( emptyText ( ) & & ! displayFromName ( ) ) {
_height + = st : : msgPadding . top ( ) + st : : msgNameFont - > height + st : : mediaHeaderSkip ;
} else {
_height + = st : : msgNameFont - > height ;
}
}
2016-02-18 22:12:50 +03:00
if ( displayForwardedFrom ( ) ) {
2016-02-19 14:53:49 +03:00
int32 l = 0 , w = 0 ;
countPositionAndSize ( l , w ) ;
int32 fwdheight = ( ( fwd - > _text . maxWidth ( ) > ( w - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) ) ? 2 : 1 ) * st : : semiboldFont - > height ;
2016-02-18 22:12:50 +03:00
if ( emptyText ( ) & & ! displayFromName ( ) ) {
2016-02-19 14:53:49 +03:00
_height + = st : : msgPadding . top ( ) + fwdheight + st : : mediaHeaderSkip ;
2016-02-18 22:12:50 +03:00
} else {
2016-02-19 14:53:49 +03:00
_height + = fwdheight ;
2016-02-18 22:12:50 +03:00
}
}
2016-03-25 14:29:45 +03:00
if ( reply ) {
2016-04-20 15:27:38 +03:00
int32 l = 0 , w = 0 ;
countPositionAndSize ( l , w ) ;
2016-03-25 14:29:45 +03:00
if ( emptyText ( ) & & ! displayFromName ( ) & & ! Has < HistoryMessageVia > ( ) ) {
_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 ( ) ;
}
2016-04-20 15:27:38 +03:00
reply - > resize ( w - st : : msgPadding . left ( ) - st : : msgPadding . right ( ) ) ;
2016-03-25 14:29:45 +03:00
}
2016-06-13 21:42:25 +03:00
} else if ( _media ) {
2016-04-10 18:53:01 +04:00
_height = _media - > resizeGetHeight ( width ) ;
2016-06-13 21:42:25 +03:00
} else {
_height = 0 ;
2014-05-30 12:53:19 +04:00
}
2016-04-08 13:20:10 +04:00
if ( auto keyboard = inlineReplyKeyboard ( ) ) {
2016-03-30 20:42:01 +04:00
int32 l = 0 , w = 0 ;
countPositionAndSize ( l , w ) ;
2016-03-28 20:15:17 +03:00
int h = st : : msgBotKbButton . margin + keyboard - > naturalHeight ( ) ;
_height + = h ;
2016-03-30 20:42:01 +04:00
keyboard - > resize ( w , h - st : : msgBotKbButton . margin ) ;
2016-03-28 15:51:22 +03:00
}
2016-03-21 21:40:00 +03:00
_height + = marginTop ( ) + marginBottom ( ) ;
2014-05-30 12:53:19 +04:00
return _height ;
}
2016-04-10 18:53:01 +04:00
bool HistoryMessage : : hasPoint ( int x , int y ) const {
2016-03-19 19:55:15 +03:00
int left = 0 , width = 0 , height = _height ;
2015-12-09 21:06:20 +03:00
countPositionAndSize ( left , width ) ;
2014-05-30 12:53:19 +04:00
if ( width < 1 ) return false ;
2015-12-08 22:07:50 +03:00
if ( drawBubble ( ) ) {
2016-03-21 21:40:00 +03:00
int top = marginTop ( ) ;
QRect r ( left , top , width , height - top - marginBottom ( ) ) ;
2015-12-08 22:07:50 +03:00
return r . contains ( x , y ) ;
2016-06-13 21:42:25 +03:00
} else if ( _media ) {
2016-04-10 18:53:01 +04:00
return _media - > hasPoint ( x - left , y - marginTop ( ) ) ;
2016-06-13 21:42:25 +03:00
} else {
return false ;
2014-05-30 12:53:19 +04:00
}
}
2016-04-10 18:53:01 +04:00
bool HistoryMessage : : pointInTime ( int32 right , int32 bottom , int x , int y , InfoDisplayType type ) const {
2015-09-15 11:50:54 +03:00
int32 infoRight = right , infoBottom = bottom ;
switch ( type ) {
case InfoDisplayDefault :
infoRight - = st : : msgPadding . right ( ) - st : : msgDateDelta . x ( ) ;
infoBottom - = st : : msgPadding . bottom ( ) - st : : msgDateDelta . y ( ) ;
break ;
case InfoDisplayOverImage :
infoRight - = st : : msgDateImgDelta + st : : msgDateImgPadding . x ( ) ;
infoBottom - = st : : msgDateImgDelta + st : : msgDateImgPadding . y ( ) ;
break ;
}
int32 dateX = infoRight - HistoryMessage : : infoWidth ( ) + HistoryMessage : : timeLeft ( ) ;
int32 dateY = infoBottom - st : : msgDateFont - > height ;
return QRect ( dateX , dateY , HistoryMessage : : timeWidth ( ) , st : : msgDateFont - > height ) . contains ( x , y ) ;
}
2016-04-13 21:29:32 +03:00
HistoryTextState HistoryMessage : : getState ( int x , int y , HistoryStateRequest request ) const {
HistoryTextState result ;
2014-05-30 12:53:19 +04:00
2016-03-19 19:55:15 +03:00
int left = 0 , width = 0 , height = _height ;
2015-12-09 21:06:20 +03:00
countPositionAndSize ( left , width ) ;
2016-02-18 22:12:50 +03:00
2016-04-13 21:29:32 +03:00
if ( width < 1 ) return result ;
2014-05-30 12:53:19 +04:00
2016-04-13 21:29:32 +03:00
auto keyboard = inlineReplyKeyboard ( ) ;
if ( keyboard ) {
2016-03-28 20:15:17 +03:00
int h = st : : msgBotKbButton . margin + keyboard - > naturalHeight ( ) ;
height - = h ;
2016-03-28 15:51:22 +03:00
}
2015-12-08 22:07:50 +03:00
if ( drawBubble ( ) ) {
2016-04-08 13:20:10 +04:00
auto fwd = Get < HistoryMessageForwarded > ( ) ;
auto via = Get < HistoryMessageVia > ( ) ;
auto reply = Get < HistoryMessageReply > ( ) ;
2016-02-18 22:12:50 +03:00
2016-03-21 21:40:00 +03:00
int top = marginTop ( ) ;
QRect r ( left , top , width , height - top - marginBottom ( ) ) ;
2016-03-25 14:29:45 +03:00
QRect trect ( r . marginsAdded ( - st : : msgPadding ) ) ;
if ( displayFromName ( ) ) {
if ( y > = trect . top ( ) & & y < trect . top ( ) + st : : msgNameFont - > height ) {
if ( x > = trect . left ( ) & & x < trect . left ( ) + trect . width ( ) & & x < trect . left ( ) + author ( ) - > nameText . maxWidth ( ) ) {
2016-04-13 21:29:32 +03:00
result . link = author ( ) - > openLink ( ) ;
return result ;
2016-01-03 17:46:30 +08:00
}
2016-03-25 14:29:45 +03:00
if ( via & & ! fwd & & x > = trect . left ( ) + author ( ) - > nameText . maxWidth ( ) + st : : msgServiceFont - > spacew & & x < trect . left ( ) + author ( ) - > nameText . maxWidth ( ) + st : : msgServiceFont - > spacew + via - > _width ) {
2016-04-13 21:29:32 +03:00
result . link = via - > _lnk ;
return result ;
2016-01-03 17:46:30 +08:00
}
2015-12-08 22:07:50 +03:00
}
2016-03-25 14:29:45 +03:00
trect . setTop ( trect . top ( ) + st : : msgNameFont - > height ) ;
2014-05-30 12:53:19 +04:00
}
2016-02-18 22:12:50 +03:00
if ( displayForwardedFrom ( ) ) {
2016-02-19 14:53:49 +03:00
int32 fwdheight = ( ( fwd - > _text . maxWidth ( ) > trect . width ( ) ) ? 2 : 1 ) * st : : semiboldFont - > height ;
if ( y > = trect . top ( ) & & y < trect . top ( ) + fwdheight ) {
bool breakEverywhere = ( fwd - > _text . countHeight ( trect . width ( ) ) > 2 * st : : semiboldFont - > height ) ;
2016-04-13 21:29:32 +03:00
auto textRequest = request . forText ( ) ;
if ( breakEverywhere ) {
textRequest . flags | = Text : : StateRequest : : Flag : : BreakEverywhere ;
}
2016-02-19 14:53:49 +03:00
textstyleSet ( & st : : inFwdTextStyle ) ;
2016-04-13 21:29:32 +03:00
result = fwd - > _text . getState ( x - trect . left ( ) , y - trect . top ( ) , trect . width ( ) , textRequest ) ;
2016-02-19 14:53:49 +03:00
textstyleRestore ( ) ;
2016-04-13 21:29:32 +03:00
result . symbol = 0 ;
result . afterSymbol = false ;
2016-02-19 14:53:49 +03:00
if ( breakEverywhere ) {
2016-04-13 21:29:32 +03:00
result . cursor = HistoryInForwardedCursorState ;
} else {
result . cursor = HistoryDefaultCursorState ;
2016-02-18 22:12:50 +03:00
}
2016-04-13 21:29:32 +03:00
return result ;
2016-02-18 22:12:50 +03:00
}
2016-03-25 14:29:45 +03:00
trect . setTop ( trect . top ( ) + fwdheight ) ;
2016-02-18 22:12:50 +03:00
}
2016-03-25 14:29:45 +03:00
if ( reply ) {
int32 h = st : : msgReplyPadding . top ( ) + st : : msgReplyBarSize . height ( ) + st : : msgReplyPadding . bottom ( ) ;
if ( y > = trect . top ( ) & & y < trect . top ( ) + h ) {
if ( reply - > replyToMsg & & y > = trect . top ( ) + st : : msgReplyPadding . top ( ) & & y < trect . top ( ) + st : : msgReplyPadding . top ( ) + st : : msgReplyBarSize . height ( ) & & x > = trect . left ( ) & & x < trect . left ( ) + trect . width ( ) ) {
2016-04-13 21:29:32 +03:00
result . link = reply - > replyToLink ( ) ;
2016-03-25 14:29:45 +03:00
}
2016-04-13 21:29:32 +03:00
return result ;
2016-03-25 14:29:45 +03:00
}
trect . setTop ( trect . top ( ) + h ) ;
}
2016-06-07 22:59:39 +03:00
if ( via & & ! displayFromName ( ) & & ! displayForwardedFrom ( ) ) {
if ( x > = trect . left ( ) & & y > = trect . top ( ) & & y < trect . top ( ) + st : : msgNameFont - > height & & x < trect . left ( ) + via - > _width ) {
result . link = via - > _lnk ;
return result ;
}
trect . setTop ( trect . top ( ) + st : : msgNameFont - > height ) ;
}
2015-12-31 23:27:21 +08:00
2016-04-13 21:29:32 +03:00
bool inDate = false , mediaDisplayed = _media & & _media - > isDisplayed ( ) ;
if ( ! mediaDisplayed | | ! _media - > customInfoLayout ( ) ) {
2015-12-09 21:06:20 +03:00
inDate = HistoryMessage : : pointInTime ( r . x ( ) + r . width ( ) , r . y ( ) + r . height ( ) , x , y , InfoDisplayDefault ) ;
}
2016-04-13 21:29:32 +03:00
if ( mediaDisplayed ) {
trect . setBottom ( trect . bottom ( ) - _media - > height ( ) ) ;
if ( y > = r . bottom ( ) - _media - > height ( ) ) {
result = _media - > getState ( x - r . left ( ) , y - ( r . bottom ( ) - _media - > height ( ) ) , request ) ;
result . symbol + = _text . length ( ) ;
}
}
if ( ! mediaDisplayed | | ( y < r . bottom ( ) - _media - > height ( ) ) ) {
textstyleSet ( & ( ( out ( ) & & ! isPost ( ) ) ? st : : outTextStyle : st : : inTextStyle ) ) ;
result = _text . getState ( x - trect . x ( ) , y - trect . y ( ) , trect . width ( ) , request . forText ( ) ) ;
textstyleRestore ( ) ;
}
2016-03-25 14:29:45 +03:00
if ( inDate ) {
2016-04-13 21:29:32 +03:00
result . cursor = HistoryInDateCursorState ;
2016-03-25 14:29:45 +03:00
}
2016-06-13 21:42:25 +03:00
} else if ( _media ) {
2016-04-13 21:29:32 +03:00
result = _media - > getState ( x - left , y - marginTop ( ) , request ) ;
result . symbol + = _text . length ( ) ;
2015-06-27 16:02:00 +03:00
}
2014-05-30 12:53:19 +04:00
2016-04-13 21:29:32 +03:00
if ( keyboard ) {
int top = height + st : : msgBotKbButton . margin - marginBottom ( ) ;
if ( x > = left & & x < left + width & & y > = top & & y < _height - marginBottom ( ) ) {
result . link = keyboard - > getState ( x - left , y - top ) ;
return result ;
2015-12-08 22:07:50 +03:00
}
2016-04-13 21:29:32 +03:00
}
2015-12-08 22:07:50 +03:00
2016-04-13 21:29:32 +03:00
return result ;
}
TextSelection HistoryMessage : : adjustSelection ( TextSelection selection , TextSelectType type ) const {
if ( ! _media | | selection . to < = _text . length ( ) ) {
return _text . adjustSelection ( selection , type ) ;
2015-04-04 23:01:34 +03:00
}
2016-04-13 21:29:32 +03:00
auto mediaSelection = _media - > adjustSelection ( toMediaSelection ( selection ) , type ) ;
if ( selection . from > = _text . length ( ) ) {
return fromMediaSelection ( mediaSelection ) ;
}
auto textSelection = _text . adjustSelection ( selection , type ) ;
return { textSelection . from , fromMediaSelection ( mediaSelection ) . to } ;
2014-05-30 12:53:19 +04:00
}
2014-06-14 23:32:11 +04:00
QString HistoryMessage : : notificationHeader ( ) const {
2016-02-17 19:37:21 +03:00
return ( ! _history - > peer - > isUser ( ) & & ! isPost ( ) ) ? from ( ) - > name : QString ( ) ;
2014-06-14 23:32:11 +04:00
}
2016-03-22 22:43:47 +03:00
bool HistoryMessage : : displayFromPhoto ( ) const {
return hasFromPhoto ( ) & & ! isAttachedToPrevious ( ) ;
}
bool HistoryMessage : : hasFromPhoto ( ) const {
2016-06-13 21:42:25 +03:00
return ( Adaptive : : Wide ( ) | | ( ! out ( ) & & ! history ( ) - > peer - > isUser ( ) ) ) & & ! isPost ( ) & & ! isEmpty ( ) ;
2016-03-22 22:43:47 +03:00
}
2014-05-30 12:53:19 +04:00
HistoryMessage : : ~ HistoryMessage ( ) {
2016-04-10 18:53:01 +04:00
_media . clear ( ) ;
2016-04-08 13:20:10 +04:00
if ( auto reply = Get < HistoryMessageReply > ( ) ) {
2016-03-25 14:29:45 +03:00
reply - > clearData ( this ) ;
}
2014-05-30 12:53:19 +04:00
}
2016-03-25 14:29:45 +03:00
void HistoryService : : setMessageByAction ( const MTPmessageAction & action ) {
2016-09-03 17:27:22 -04:00
auto text = lang ( lng_message_empty ) ;
auto from = textcmdLink ( 1 , _from - > name ) ;
Links links ;
links . push_back ( MakeShared < PeerOpenClickHandler > ( _from ) ) ;
2014-12-18 21:40:49 +03:00
2014-05-30 12:53:19 +04:00
switch ( action . type ( ) ) {
case mtpc_messageActionChatAddUser : {
2016-09-03 17:27:22 -04:00
auto & d = action . c_messageActionChatAddUser ( ) ;
auto & v = d . vusers . c_vector ( ) . v ;
2015-11-13 18:14:33 +03:00
bool foundSelf = false ;
2016-09-03 17:27:22 -04:00
for ( int i = 0 , l = v . size ( ) ; i < l ; + + i ) {
2015-11-13 18:14:33 +03:00
if ( v . at ( i ) . v = = MTP : : authedId ( ) ) {
foundSelf = true ;
break ;
}
}
if ( v . size ( ) = = 1 ) {
2016-09-03 17:27:22 -04:00
auto u = App : : user ( peerFromUser ( v . at ( 0 ) ) ) ;
2015-11-13 18:14:33 +03:00
if ( u = = _from ) {
text = lng_action_user_joined ( lt_from , from ) ;
} else {
2016-03-29 20:17:00 +03:00
links . push_back ( MakeShared < PeerOpenClickHandler > ( u ) ) ;
2015-11-13 18:14:33 +03:00
text = lng_action_add_user ( lt_from , from , lt_user , textcmdLink ( 2 , u - > name ) ) ;
}
2015-11-16 19:04:37 +03:00
} else if ( v . isEmpty ( ) ) {
text = lng_action_add_user ( lt_from , from , lt_user , " somebody " ) ;
2014-12-18 21:40:49 +03:00
} else {
2016-09-03 17:27:22 -04:00
for ( int i = 0 , l = v . size ( ) ; i < l ; + + i ) {
auto u = App : : user ( peerFromUser ( v . at ( i ) ) ) ;
auto linkText = textcmdLink ( i + 2 , u - > name ) ;
2015-11-24 20:37:55 +03:00
if ( i = = 0 ) {
text = linkText ;
} else if ( i + 1 < l ) {
text = lng_action_add_users_and_one ( lt_accumulated , text , lt_user , linkText ) ;
} else {
text = lng_action_add_users_and_last ( lt_accumulated , text , lt_user , linkText ) ;
}
2016-03-29 20:17:00 +03:00
links . push_back ( MakeShared < PeerOpenClickHandler > ( u ) ) ;
2015-11-24 20:37:55 +03:00
}
text = lng_action_add_users_many ( lt_from , from , lt_users , text ) ;
2015-11-13 18:14:33 +03:00
}
2015-11-20 21:24:44 +03:00
if ( foundSelf ) {
if ( history ( ) - > peer - > isMegagroup ( ) ) {
history ( ) - > peer - > asChannel ( ) - > mgInfo - > joinedMessageFound = true ;
}
2014-05-30 12:53:19 +04:00
}
} break ;
2015-04-30 16:53:36 +03:00
case mtpc_messageActionChatJoinedByLink : {
2016-09-03 17:27:22 -04:00
auto & d = action . c_messageActionChatJoinedByLink ( ) ;
2016-03-29 20:17:00 +03:00
//if (true || peerFromUser(d.vinviter_id) == _from->id) {
2015-09-23 13:39:37 +03:00
text = lng_action_user_joined_by_link ( lt_from , from ) ;
2015-04-30 16:53:36 +03:00
//} else {
2016-03-29 20:17:00 +03:00
// UserData *u = App::user(App::peerFromUser(d.vinviter_id));
// links.push_back(MakeShared<PeerOpenClickHandler>(u));
// text = lng_action_user_joined_by_link_from(lt_from, from, lt_inviter, textcmdLink(2, u->name));
//}
2015-11-20 21:24:44 +03:00
if ( _from - > isSelf ( ) & & history ( ) - > peer - > isMegagroup ( ) ) {
history ( ) - > peer - > asChannel ( ) - > mgInfo - > joinedMessageFound = true ;
}
2015-04-30 16:53:36 +03:00
} break ;
2014-05-30 12:53:19 +04:00
case mtpc_messageActionChatCreate : {
2016-09-03 17:27:22 -04:00
auto & d = action . c_messageActionChatCreate ( ) ;
2014-12-18 21:40:49 +03:00
text = lng_action_created_chat ( lt_from , from , lt_title , textClean ( qs ( d . vtitle ) ) ) ;
2014-05-30 12:53:19 +04:00
} break ;
2015-09-04 16:01:31 +03:00
case mtpc_messageActionChannelCreate : {
2016-09-03 17:27:22 -04:00
auto & d = action . c_messageActionChannelCreate ( ) ;
2016-02-17 19:37:21 +03:00
if ( isPost ( ) ) {
2015-11-02 17:33:57 -05:00
text = lng_action_created_channel ( lt_title , textClean ( qs ( d . vtitle ) ) ) ;
} else {
text = lng_action_created_chat ( lt_from , from , lt_title , textClean ( qs ( d . vtitle ) ) ) ;
}
2015-09-04 16:01:31 +03:00
} break ;
2016-06-03 15:45:33 +03:00
case mtpc_messageActionHistoryClear : {
text = QString ( ) ;
} break ;
2014-05-30 12:53:19 +04:00
case mtpc_messageActionChatDeletePhoto : {
2016-02-17 19:37:21 +03:00
text = isPost ( ) ? lang ( lng_action_removed_photo_channel ) : lng_action_removed_photo ( lt_from , from ) ;
2014-05-30 12:53:19 +04:00
} break ;
case mtpc_messageActionChatDeleteUser : {
2016-09-03 17:27:22 -04:00
auto & d = action . c_messageActionChatDeleteUser ( ) ;
2015-09-03 13:48:40 +03:00
if ( peerFromUser ( d . vuser_id ) = = _from - > id ) {
2015-09-23 13:39:37 +03:00
text = lng_action_user_left ( lt_from , from ) ;
2014-12-18 21:40:49 +03:00
} else {
2016-09-03 17:27:22 -04:00
auto u = App : : user ( peerFromUser ( d . vuser_id ) ) ;
2016-03-29 20:17:00 +03:00
links . push_back ( MakeShared < PeerOpenClickHandler > ( u ) ) ;
2014-12-18 21:40:49 +03:00
text = lng_action_kick_user ( lt_from , from , lt_user , textcmdLink ( 2 , u - > name ) ) ;
2014-05-30 12:53:19 +04:00
}
} break ;
case mtpc_messageActionChatEditPhoto : {
2016-09-03 17:27:22 -04:00
auto & d = action . c_messageActionChatEditPhoto ( ) ;
2014-05-30 12:53:19 +04:00
if ( d . vphoto . type ( ) = = mtpc_photo ) {
2016-04-10 18:53:01 +04:00
_media . reset ( new HistoryPhoto ( this , history ( ) - > peer , d . vphoto . c_photo ( ) , st : : msgServicePhotoWidth ) ) ;
2014-05-30 12:53:19 +04:00
}
2016-02-17 19:37:21 +03:00
text = isPost ( ) ? lang ( lng_action_changed_photo_channel ) : lng_action_changed_photo ( lt_from , from ) ;
2014-05-30 12:53:19 +04:00
} break ;
case mtpc_messageActionChatEditTitle : {
2016-09-03 17:27:22 -04:00
auto & d = action . c_messageActionChatEditTitle ( ) ;
2016-02-17 19:37:21 +03:00
text = isPost ( ) ? lng_action_changed_title_channel ( lt_title , textClean ( qs ( d . vtitle ) ) ) : lng_action_changed_title ( lt_from , from , lt_title , textClean ( qs ( d . vtitle ) ) ) ;
2014-05-30 12:53:19 +04:00
} break ;
2015-11-06 12:48:49 -05:00
case mtpc_messageActionChatMigrateTo : {
2016-03-19 19:55:15 +03:00
_flags | = MTPDmessage_ClientFlag : : f_is_group_migrate ;
2016-09-03 17:27:22 -04:00
auto & d = action . c_messageActionChatMigrateTo ( ) ;
2016-03-25 14:29:45 +03:00
if ( true /*PeerData *channel = App::channelLoaded(d.vchannel_id.v)*/ ) {
2015-11-06 12:48:49 -05:00
text = lang ( lng_action_group_migrate ) ;
} else {
text = lang ( lng_contacts_loading ) ;
}
} break ;
case mtpc_messageActionChannelMigrateFrom : {
2016-03-19 19:55:15 +03:00
_flags | = MTPDmessage_ClientFlag : : f_is_group_migrate ;
2016-09-03 17:27:22 -04:00
auto & d = action . c_messageActionChannelMigrateFrom ( ) ;
2016-03-25 14:29:45 +03:00
if ( true /*PeerData *chat = App::chatLoaded(d.vchat_id.v)*/ ) {
2015-11-06 12:48:49 -05:00
text = lang ( lng_action_group_migrate ) ;
} else {
text = lang ( lng_contacts_loading ) ;
}
} break ;
2016-03-05 23:12:55 +02:00
case mtpc_messageActionPinMessage : {
2016-09-03 17:27:22 -04:00
preparePinnedText ( from , & text , & links ) ;
2016-03-05 23:12:55 +02:00
} break ;
2016-09-02 12:11:23 -04:00
case mtpc_messageActionGameScore : {
2016-09-03 17:27:22 -04:00
prepareGameScoreText ( from , & text , & links ) ;
2016-09-02 12:11:23 -04:00
} break ;
2014-12-18 21:40:49 +03:00
default : from = QString ( ) ; break ;
2014-05-30 12:53:19 +04:00
}
2016-09-03 17:27:22 -04:00
setServiceText ( text , links ) ;
for ( int i = 0 , count = links . size ( ) ; i ! = count ; + + i ) {
_text . setLink ( 1 + i , links . at ( i ) ) ;
2015-11-13 18:14:33 +03:00
}
2014-05-30 12:53:19 +04:00
}
2016-09-02 12:11:23 -04:00
bool HistoryService : : updateDependent ( bool force ) {
auto dependent = GetDependentData ( ) ;
t_assert ( dependent ! = nullptr ) ;
2016-03-05 23:12:55 +02:00
2016-03-11 15:20:58 +03:00
if ( ! force ) {
2016-09-02 12:11:23 -04:00
if ( ! dependent - > msgId | | dependent - > msg ) {
2016-03-11 15:20:58 +03:00
return true ;
}
}
2016-03-05 23:12:55 +02:00
2016-09-02 12:11:23 -04:00
if ( ! dependent - > lnk ) {
dependent - > lnk . reset ( new GoToMessageClickHandler ( history ( ) - > peer - > id , dependent - > msgId ) ) ;
2016-03-05 23:12:55 +02:00
}
2016-03-11 15:20:58 +03:00
bool gotDependencyItem = false ;
2016-09-02 12:11:23 -04:00
if ( ! dependent - > msg ) {
dependent - > msg = App : : histItemById ( channelId ( ) , dependent - > msgId ) ;
if ( dependent - > msg ) {
App : : historyRegDependency ( this , dependent - > msg ) ;
2016-03-11 15:20:58 +03:00
gotDependencyItem = true ;
}
}
2016-09-02 12:11:23 -04:00
if ( dependent - > msg ) {
updateDependentText ( ) ;
2016-03-05 23:12:55 +02:00
} else if ( force ) {
2016-09-02 12:11:23 -04:00
if ( dependent - > msgId > 0 ) {
dependent - > msgId = 0 ;
2016-03-11 15:20:58 +03:00
gotDependencyItem = true ;
}
2016-09-02 12:11:23 -04:00
updateDependentText ( ) ;
2016-03-05 23:12:55 +02:00
}
if ( force ) {
2016-03-11 15:20:58 +03:00
if ( gotDependencyItem & & App : : wnd ( ) ) {
App : : wnd ( ) - > notifySettingGot ( ) ;
}
2016-03-05 23:12:55 +02:00
}
2016-09-02 12:11:23 -04:00
return ( dependent - > msg | | ! dependent - > msgId ) ;
2016-03-05 23:12:55 +02:00
}
2016-09-03 17:27:22 -04:00
bool HistoryService : : preparePinnedText ( const QString & from , QString * outText , Links * outLinks ) {
2016-03-05 23:12:55 +02:00
bool result = false ;
2016-09-03 17:27:22 -04:00
QString text ;
2016-03-05 23:12:55 +02:00
2016-03-29 20:17:00 +03:00
ClickHandlerPtr second ;
2016-04-08 13:20:10 +04:00
auto pinned = Get < HistoryServicePinned > ( ) ;
2016-03-05 23:12:55 +02:00
if ( pinned & & pinned - > msg ) {
HistoryMedia * media = pinned - > msg - > getMedia ( ) ;
QString mediaText ;
switch ( media ? media - > type ( ) : MediaTypeCount ) {
case MediaTypePhoto : mediaText = lang ( lng_action_pinned_media_photo ) ; break ;
case MediaTypeVideo : mediaText = lang ( lng_action_pinned_media_video ) ; break ;
case MediaTypeContact : mediaText = lang ( lng_action_pinned_media_contact ) ; break ;
case MediaTypeFile : mediaText = lang ( lng_action_pinned_media_file ) ; break ;
case MediaTypeGif : mediaText = lang ( lng_action_pinned_media_gif ) ; break ;
case MediaTypeSticker : mediaText = lang ( lng_action_pinned_media_sticker ) ; break ;
case MediaTypeLocation : mediaText = lang ( lng_action_pinned_media_location ) ; break ;
2016-03-11 14:14:55 +03:00
case MediaTypeMusicFile : mediaText = lang ( lng_action_pinned_media_audio ) ; break ;
2016-03-05 23:12:55 +02:00
case MediaTypeVoiceFile : mediaText = lang ( lng_action_pinned_media_voice ) ; break ;
}
if ( mediaText . isEmpty ( ) ) {
2016-05-06 20:33:48 +03:00
QString original = pinned - > msg - > originalText ( ) . text ;
2016-03-05 23:12:55 +02:00
int32 cutat = 0 , limit = PinnedMessageTextLimit , size = original . size ( ) ;
for ( ; limit > 0 ; ) {
- - limit ;
if ( cutat > = size ) break ;
if ( original . at ( cutat ) . isLowSurrogate ( ) & & cutat + 1 < size & & original . at ( cutat + 1 ) . isHighSurrogate ( ) ) {
cutat + = 2 ;
} else {
+ + cutat ;
}
}
if ( ! limit & & cutat + 5 < size ) {
2016-03-24 18:07:13 +03:00
original = original . mid ( 0 , cutat ) + qstr ( " ... " ) ;
2016-03-05 23:12:55 +02:00
}
text = lng_action_pinned_message ( lt_from , from , lt_text , textcmdLink ( 2 , original ) ) ;
} else {
text = lng_action_pinned_media ( lt_from , from , lt_media , textcmdLink ( 2 , mediaText ) ) ;
}
2016-03-06 19:06:05 +02:00
second = pinned - > lnk ;
2016-03-05 23:12:55 +02:00
result = true ;
} else if ( pinned & & pinned - > msgId ) {
text = lng_action_pinned_media ( lt_from , from , lt_media , textcmdLink ( 2 , lang ( lng_contacts_loading ) ) ) ;
2016-03-06 19:06:05 +02:00
second = pinned - > lnk ;
2016-03-05 23:12:55 +02:00
result = true ;
} else {
text = lng_action_pinned_media ( lt_from , from , lt_media , lang ( lng_deleted_message ) ) ;
}
2016-09-03 17:27:22 -04:00
* outText = text ;
if ( second ) {
outLinks - > push_back ( second ) ;
2016-03-05 23:12:55 +02:00
}
return result ;
}
2016-09-03 17:27:22 -04:00
bool HistoryService : : prepareGameScoreText ( const QString & from , QString * outText , Links * outLinks ) {
2016-09-02 12:11:23 -04:00
bool result = false ;
2016-09-03 17:27:22 -04:00
QString text ;
2016-09-02 12:11:23 -04:00
2016-09-03 17:27:22 -04:00
ClickHandlerPtr second ;
2016-09-02 12:11:23 -04:00
auto gamescore = Get < HistoryServiceGameScore > ( ) ;
if ( gamescore & & gamescore - > msg ) {
2016-09-03 17:27:22 -04:00
auto getGameTitle = [ item = gamescore - > msg , & second ] ( ) - > QString {
2016-09-02 12:11:23 -04:00
if ( auto markup = item - > Get < HistoryMessageReplyMarkup > ( ) ) {
2016-09-03 17:27:22 -04:00
for ( int i = 0 , rowsCount = markup - > rows . size ( ) ; i ! = rowsCount ; + + i ) {
auto & row = markup - > rows [ i ] ;
for ( int j = 0 , buttonsCount = row . size ( ) ; j ! = buttonsCount ; + + j ) {
auto & button = row [ j ] ;
2016-09-02 12:11:23 -04:00
if ( button . type = = HistoryMessageReplyMarkup : : Button : : Type : : Game ) {
auto strData = QString : : fromUtf8 ( button . data ) ;
2016-09-03 17:27:22 -04:00
second = MakeShared < ReplyMarkupClickHandler > ( item , i , j ) ;
2016-09-19 13:18:21 +03:00
auto parts = strData . split ( ' , ' ) ;
t_assert ( parts . size ( ) > 2 ) ;
2016-09-23 19:40:25 +03:00
QString gameTitle ;
gameTitle . reserve ( strData . size ( ) - 2 ) ;
gameTitle . append ( parts [ 2 ] ) ;
for ( int i = 3 , count = parts . size ( ) ; i ! = count ; + + i ) {
gameTitle . append ( ' , ' ) . append ( parts [ i ] ) ;
}
return textcmdLink ( 2 , gameTitle ) ;
2016-09-02 12:11:23 -04:00
}
}
}
}
return lang ( lng_deleted_message ) ;
} ;
2016-09-20 16:03:42 +03:00
text = lng_action_game_score ( lt_from , from , lt_count , gamescore - > score , lt_game , getGameTitle ( ) ) ;
2016-09-02 12:11:23 -04:00
result = true ;
} else if ( gamescore & & gamescore - > msgId ) {
2016-09-20 16:03:42 +03:00
text = lng_action_game_score ( lt_from , from , lt_count , gamescore - > score , lt_game , lang ( lng_contacts_loading ) ) ;
2016-09-02 12:11:23 -04:00
result = true ;
} else {
2016-09-20 16:03:42 +03:00
text = lng_action_game_score ( lt_from , from , lt_count , gamescore - > score , lt_game , lang ( lng_deleted_message ) ) ;
2016-09-02 12:11:23 -04:00
}
2016-09-03 17:27:22 -04:00
* outText = text ;
if ( second ) {
outLinks - > push_back ( second ) ;
2016-09-02 12:11:23 -04:00
}
return result ;
}
2016-03-25 14:29:45 +03:00
HistoryService : : HistoryService ( History * history , const MTPDmessageService & msg ) :
2016-03-23 14:47:32 +03:00
HistoryItem ( history , msg . vid . v , mtpCastFlags ( msg . vflags . v ) , : : date ( msg . vdate ) , msg . has_from_id ( ) ? msg . vfrom_id . v : 0 ) {
2016-09-23 21:26:53 +03:00
createFromMtp ( msg ) ;
2014-12-18 21:40:49 +03:00
setMessageByAction ( msg . vaction ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-29 20:17:00 +03:00
HistoryService : : HistoryService ( History * history , MsgId msgId , QDateTime date , const QString & msg , MTPDmessage : : Flags flags , int32 from ) :
HistoryItem ( history , msgId , flags , date , from ) {
2016-09-03 17:27:22 -04:00
setServiceText ( msg , Links ( ) ) ;
2014-10-10 16:46:20 +04:00
}
2016-03-25 14:29:45 +03:00
void HistoryService : : initDimensions ( ) {
2014-05-30 12:53:19 +04:00
_maxw = _text . maxWidth ( ) + st : : msgServicePadding . left ( ) + st : : msgServicePadding . right ( ) ;
_minh = _text . minHeight ( ) ;
2016-04-10 18:53:01 +04:00
if ( _media ) _media - > initDimensions ( ) ;
2014-05-30 12:53:19 +04:00
}
2016-09-02 12:11:23 -04:00
bool HistoryService : : updateDependencyItem ( ) {
if ( GetDependentData ( ) ) {
return updateDependent ( true ) ;
}
return HistoryItem : : updateDependencyItem ( ) ;
}
2016-03-25 14:29:45 +03:00
void HistoryService : : countPositionAndSize ( int32 & left , int32 & width ) const {
2016-02-09 19:05:08 +03:00
left = st : : msgServiceMargin . left ( ) ;
2016-02-15 14:46:01 +03:00
int32 maxwidth = _history - > width ;
if ( Adaptive : : Wide ( ) ) {
2016-03-21 21:40:00 +03:00
maxwidth = qMin ( maxwidth , int32 ( st : : msgMaxWidth + 2 * st : : msgPhotoSkip + 2 * st : : msgMargin . left ( ) ) ) ;
2016-02-15 14:46:01 +03:00
}
width = maxwidth - st : : msgServiceMargin . left ( ) - st : : msgServiceMargin . left ( ) ;
2016-02-09 19:05:08 +03:00
}
2016-05-06 20:33:48 +03:00
TextWithEntities HistoryService : : selectedText ( TextSelection selection ) const {
return _text . originalTextWithEntities ( ( selection = = FullSelection ) ? AllTextSelection : selection ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-25 14:29:45 +03:00
QString HistoryService : : inDialogsText ( ) const {
2016-06-22 16:39:54 +03:00
return textcmdLink ( 1 , textClean ( notificationText ( ) ) ) ;
2015-03-19 12:18:19 +03:00
}
2016-03-25 14:29:45 +03:00
QString HistoryService : : inReplyText ( ) const {
2016-06-22 16:39:54 +03:00
QString result = HistoryService : : notificationText ( ) ;
2016-02-17 19:37:21 +03:00
return result . trimmed ( ) . startsWith ( author ( ) - > name ) ? result . trimmed ( ) . mid ( author ( ) - > name . size ( ) ) . trimmed ( ) : result ;
2015-03-19 12:18:19 +03:00
}
2016-09-03 17:27:22 -04:00
void HistoryService : : setServiceText ( const QString & text , const Links & links ) {
2015-11-23 18:34:38 +03:00
textstyleSet ( & st : : serviceTextStyle ) ;
2015-09-19 12:13:21 +03:00
_text . setText ( st : : msgServiceFont , text , _historySrvOptions ) ;
2015-11-23 18:34:38 +03:00
textstyleRestore ( ) ;
2016-09-03 17:27:22 -04:00
for ( int i = 0 , count = links . size ( ) ; i ! = count ; + + i ) {
_text . setLink ( 1 + i , links . at ( i ) ) ;
}
2016-05-04 19:46:24 +03:00
setPendingInitDimensions ( ) ;
_textWidth = - 1 ;
_textHeight = 0 ;
2015-09-19 12:13:21 +03:00
}
2016-04-13 21:29:32 +03:00
void HistoryService : : draw ( Painter & p , const QRect & r , TextSelection selection , uint64 ms ) const {
2016-06-09 14:51:24 +03:00
int height = _height - st : : msgServiceMargin . top ( ) - st : : msgServiceMargin . bottom ( ) ;
2016-03-21 21:40:00 +03:00
2016-06-13 13:42:55 +03:00
QRect clip ( r ) ;
2016-03-21 21:40:00 +03:00
int dateh = 0 , unreadbarh = 0 ;
2016-04-08 13:20:10 +04:00
if ( auto date = Get < HistoryMessageDate > ( ) ) {
2016-03-21 21:40:00 +03:00
dateh = date - > height ( ) ;
2016-06-13 13:42:55 +03:00
//if (clip.intersects(QRect(0, 0, _history->width, dateh))) {
2016-06-10 13:21:09 +03:00
// date->paint(p, 0, _history->width);
//}
2016-03-21 21:40:00 +03:00
p . translate ( 0 , dateh ) ;
2016-06-13 13:42:55 +03:00
clip . translate ( 0 , - dateh ) ;
2016-03-21 21:40:00 +03:00
height - = dateh ;
}
2016-04-08 13:20:10 +04:00
if ( auto unreadbar = Get < HistoryMessageUnreadBar > ( ) ) {
2016-03-21 21:40:00 +03:00
unreadbarh = unreadbar - > height ( ) ;
2016-06-13 13:42:55 +03:00
if ( clip . intersects ( QRect ( 0 , 0 , _history - > width , unreadbarh ) ) ) {
2016-04-09 17:56:40 +04:00
unreadbar - > paint ( p , 0 , _history - > width ) ;
}
2016-03-21 21:40:00 +03:00
p . translate ( 0 , unreadbarh ) ;
2016-06-13 13:42:55 +03:00
clip . translate ( 0 , - unreadbarh ) ;
2016-03-21 21:40:00 +03:00
height - = unreadbarh ;
}
2016-06-13 13:42:55 +03:00
HistoryLayout : : PaintContext context ( ms , clip , selection ) ;
2016-06-09 14:51:24 +03:00
HistoryLayout : : ServiceMessagePainter : : paint ( p , this , context , height ) ;
2016-03-21 21:40:00 +03:00
if ( int skiph = dateh + unreadbarh ) {
p . translate ( 0 , - skiph ) ;
}
2014-05-30 12:53:19 +04:00
}
2016-03-25 14:29:45 +03:00
int32 HistoryService : : resizeGetHeight_ ( int32 width ) {
2016-06-03 15:45:33 +03:00
_height = displayedDateHeight ( ) ;
if ( auto unreadbar = Get < HistoryMessageUnreadBar > ( ) ) {
_height + = unreadbar - > height ( ) ;
2016-02-15 14:46:01 +03:00
}
2014-05-30 12:53:19 +04:00
2016-06-03 15:45:33 +03:00
if ( _text . isEmpty ( ) ) {
_textHeight = 0 ;
2014-05-30 12:53:19 +04:00
} else {
2016-06-03 15:45:33 +03:00
int32 maxwidth = _history - > width ;
if ( Adaptive : : Wide ( ) ) {
maxwidth = qMin ( maxwidth , int32 ( st : : msgMaxWidth + 2 * st : : msgPhotoSkip + 2 * st : : msgMargin . left ( ) ) ) ;
}
if ( width > maxwidth ) width = maxwidth ;
width - = st : : msgServiceMargin . left ( ) + st : : msgServiceMargin . left ( ) ; // two small margins
if ( width < st : : msgServicePadding . left ( ) + st : : msgServicePadding . right ( ) + 1 ) width = st : : msgServicePadding . left ( ) + st : : msgServicePadding . right ( ) + 1 ;
2016-08-14 22:56:26 +03:00
int32 nwidth = qMax ( width - st : : msgServicePadding . left ( ) - st : : msgServicePadding . right ( ) , 0 ) ;
2016-06-03 15:45:33 +03:00
if ( nwidth ! = _textWidth ) {
_textWidth = nwidth ;
textstyleSet ( & st : : serviceTextStyle ) ;
_textHeight = _text . countHeight ( nwidth ) ;
textstyleRestore ( ) ;
}
if ( width > = _maxw ) {
_height + = _minh ;
} else {
_height + = _textHeight ;
}
_height + = st : : msgServicePadding . top ( ) + st : : msgServicePadding . bottom ( ) + st : : msgServiceMargin . top ( ) + st : : msgServiceMargin . bottom ( ) ;
if ( _media ) {
_height + = st : : msgServiceMargin . top ( ) + _media - > resizeGetHeight ( _media - > currentWidth ( ) ) ;
}
2016-03-21 21:40:00 +03:00
}
2016-06-03 15:45:33 +03:00
2014-05-30 12:53:19 +04:00
return _height ;
}
2016-04-10 18:53:01 +04:00
bool HistoryService : : hasPoint ( int x , int y ) const {
2016-03-21 21:40:00 +03:00
int left = 0 , width = 0 , height = _height - st : : msgServiceMargin . top ( ) - st : : msgServiceMargin . bottom ( ) ; // two small margins
2016-02-09 19:05:08 +03:00
countPositionAndSize ( left , width ) ;
2014-05-30 12:53:19 +04:00
if ( width < 1 ) return false ;
2016-03-21 21:40:00 +03:00
if ( int dateh = displayedDateHeight ( ) ) {
y - = dateh ;
height - = dateh ;
}
2016-04-08 13:20:10 +04:00
if ( auto unreadbar = Get < HistoryMessageUnreadBar > ( ) ) {
2016-03-21 21:40:00 +03:00
int unreadbarh = unreadbar - > height ( ) ;
y - = unreadbarh ;
height - = unreadbarh ;
}
2014-08-15 15:19:32 +04:00
if ( _media ) {
height - = st : : msgServiceMargin . top ( ) + _media - > height ( ) ;
2014-05-30 12:53:19 +04:00
}
return QRect ( left , st : : msgServiceMargin . top ( ) , width , height ) . contains ( x , y ) ;
}
2016-04-13 21:29:32 +03:00
HistoryTextState HistoryService : : getState ( int x , int y , HistoryStateRequest request ) const {
HistoryTextState result ;
2014-05-30 12:53:19 +04:00
2016-03-21 21:40:00 +03:00
int left = 0 , width = 0 , height = _height - st : : msgServiceMargin . top ( ) - st : : msgServiceMargin . bottom ( ) ; // two small margins
2016-02-09 19:05:08 +03:00
countPositionAndSize ( left , width ) ;
2016-04-13 21:29:32 +03:00
if ( width < 1 ) return result ;
2014-05-30 12:53:19 +04:00
2016-03-21 21:40:00 +03:00
if ( int dateh = displayedDateHeight ( ) ) {
y - = dateh ;
height - = dateh ;
}
2016-04-08 13:20:10 +04:00
if ( auto unreadbar = Get < HistoryMessageUnreadBar > ( ) ) {
2016-03-21 21:40:00 +03:00
int unreadbarh = unreadbar - > height ( ) ;
y - = unreadbarh ;
height - = unreadbarh ;
}
2014-08-15 15:19:32 +04:00
if ( _media ) {
height - = st : : msgServiceMargin . top ( ) + _media - > height ( ) ;
2014-05-30 12:53:19 +04:00
}
2016-09-02 12:11:23 -04:00
auto outer = QRect ( left , st : : msgServiceMargin . top ( ) , width , height ) ;
auto trect = outer . marginsAdded ( - st : : msgServicePadding ) ;
2014-05-30 12:53:19 +04:00
if ( trect . contains ( x , y ) ) {
2015-11-23 18:34:38 +03:00
textstyleSet ( & st : : serviceTextStyle ) ;
2016-04-13 21:29:32 +03:00
auto textRequest = request . forText ( ) ;
textRequest . align = style : : al_center ;
result = _text . getState ( x - trect . x ( ) , y - trect . y ( ) , trect . width ( ) , textRequest ) ;
2015-11-23 18:34:38 +03:00
textstyleRestore ( ) ;
2016-09-03 17:27:22 -04:00
if ( auto gamescore = Get < HistoryServiceGameScore > ( ) ) {
if ( ! result . link & & outer . contains ( x , y ) ) {
result . link = gamescore - > lnk ;
}
}
2015-06-27 16:02:00 +03:00
} else if ( _media ) {
2016-04-13 21:29:32 +03:00
result = _media - > getState ( x - st : : msgServiceMargin . left ( ) - ( width - _media - > maxWidth ( ) ) / 2 , y - st : : msgServiceMargin . top ( ) - height - st : : msgServiceMargin . top ( ) , request ) ;
2016-03-21 21:40:00 +03:00
}
2016-04-13 21:29:32 +03:00
return result ;
2014-05-30 12:53:19 +04:00
}
2016-09-23 21:26:53 +03:00
void HistoryService : : createFromMtp ( const MTPDmessageService & message ) {
if ( message . has_reply_to_msg_id ( ) ) {
if ( message . vaction . type ( ) = = mtpc_messageActionPinMessage ) {
UpdateComponents ( HistoryServicePinned : : Bit ( ) ) ;
} else if ( message . vaction . type ( ) = = mtpc_messageActionGameScore ) {
UpdateComponents ( HistoryServiceGameScore : : Bit ( ) ) ;
Get < HistoryServiceGameScore > ( ) - > score = message . vaction . c_messageActionGameScore ( ) . vscore . v ;
}
if ( auto dependent = GetDependentData ( ) ) {
dependent - > msgId = message . vreply_to_msg_id . v ;
if ( ! updateDependent ( ) & & App : : api ( ) ) {
App : : api ( ) - > requestMessageData ( history ( ) - > peer - > asChannel ( ) , dependent - > msgId , std_ : : make_unique < HistoryDependentItemCallback > ( fullId ( ) ) ) ;
}
}
}
setMessageByAction ( message . vaction ) ;
}
2016-06-13 21:42:25 +03:00
2016-09-23 21:26:53 +03:00
void HistoryService : : applyEdition ( const MTPDmessageService & message ) {
2016-09-02 12:11:23 -04:00
clearDependency ( ) ;
UpdateComponents ( 0 ) ;
2016-09-23 21:26:53 +03:00
createFromMtp ( message ) ;
if ( message . vaction . type ( ) = = mtpc_messageActionHistoryClear ) {
removeMedia ( ) ;
finishEditionToEmpty ( ) ;
} else {
finishEdition ( - 1 ) ;
}
2016-06-13 21:42:25 +03:00
}
void HistoryService : : removeMedia ( ) {
if ( ! _media ) return ;
bool mediaWasDisplayed = _media - > isDisplayed ( ) ;
_media . clear ( ) ;
if ( mediaWasDisplayed ) {
_textWidth = - 1 ;
_textHeight = 0 ;
}
}
2016-05-20 19:01:06 +03:00
int32 HistoryService : : addToOverview ( AddToOverviewMethod method ) {
if ( ! indexInOverview ( ) ) return 0 ;
int32 result = 0 ;
if ( auto media = getMedia ( ) ) {
MediaOverviewType type = serviceMediaToOverviewType ( media ) ;
if ( type ! = OverviewCount ) {
if ( history ( ) - > addToOverview ( type , id , method ) ) {
result | = ( 1 < < type ) ;
}
}
}
return result ;
}
void HistoryService : : eraseFromOverview ( ) {
if ( auto media = getMedia ( ) ) {
MediaOverviewType type = serviceMediaToOverviewType ( media ) ;
if ( type ! = OverviewCount ) {
history ( ) - > eraseFromOverview ( type , id ) ;
}
}
2014-08-11 13:03:45 +04:00
}
2016-09-03 17:27:22 -04:00
bool HistoryService : : updateDependentText ( ) {
auto result = false ;
auto from = textcmdLink ( 1 , _from - > name ) ;
QString text ;
Links links ;
links . push_back ( MakeShared < PeerOpenClickHandler > ( _from ) ) ;
if ( Has < HistoryServicePinned > ( ) ) {
result = preparePinnedText ( from , & text , & links ) ;
} else if ( Has < HistoryServiceGameScore > ( ) ) {
result = prepareGameScoreText ( from , & text , & links ) ;
} else {
return result ;
}
setServiceText ( text , links ) ;
if ( history ( ) - > textCachedFor = = this ) {
history ( ) - > textCachedFor = 0 ;
}
if ( App : : main ( ) ) {
App : : main ( ) - > dlgUpdated ( history ( ) , id ) ;
}
App : : historyUpdateDependent ( this ) ;
return result ;
}
2016-09-02 12:11:23 -04:00
void HistoryService : : clearDependency ( ) {
if ( auto dependent = GetDependentData ( ) ) {
if ( dependent - > msg ) {
App : : historyUnregDependency ( this , dependent - > msg ) ;
2016-03-14 08:31:33 +03:00
}
}
2016-09-02 12:11:23 -04:00
}
HistoryService : : ~ HistoryService ( ) {
clearDependency ( ) ;
2016-04-10 18:53:01 +04:00
_media . clear ( ) ;
2014-05-30 12:53:19 +04:00
}
2016-03-21 21:40:00 +03:00
HistoryJoined : : HistoryJoined ( History * history , const QDateTime & inviteDate , UserData * inviter , MTPDmessage : : Flags flags )
2016-03-25 14:29:45 +03:00
: HistoryService ( history , clientMsgId ( ) , inviteDate , QString ( ) , flags ) {
2016-09-03 17:27:22 -04:00
Links links ;
auto text = ( [ history , inviter , & links ] ( ) {
if ( peerToUser ( inviter - > id ) = = MTP : : authedId ( ) ) {
return lang ( history - > isMegagroup ( ) ? lng_action_you_joined_group : lng_action_you_joined ) ;
}
links . push_back ( MakeShared < PeerOpenClickHandler > ( inviter ) ) ;
if ( history - > isMegagroup ( ) ) {
return lng_action_add_you_group ( lt_from , textcmdLink ( 1 , inviter - > name ) ) ;
}
return lng_action_add_you ( lt_from , textcmdLink ( 1 , inviter - > name ) ) ;
} ) ( ) ;
setServiceText ( text , links ) ;
2015-09-21 23:57:42 +03:00
}
2016-03-29 20:17:00 +03:00
void GoToMessageClickHandler : : onClickImpl ( ) const {
if ( App : : main ( ) ) {
HistoryItem * current = App : : mousedItem ( ) ;
if ( current & & current - > history ( ) - > peer - > id = = peer ( ) ) {
App : : main ( ) - > pushReplyReturn ( current ) ;
}
2016-09-01 21:37:30 -04:00
Ui : : showPeerHistory ( peer ( ) , msgid ( ) , Ui : : ShowWay : : Forward ) ;
2016-03-29 20:17:00 +03:00
}
}
void CommentsClickHandler : : onClickImpl ( ) const {
if ( App : : main ( ) & & peerIsChannel ( peer ( ) ) ) {
Ui : : showPeerHistory ( peer ( ) , msgid ( ) ) ;
}
}