2016-09-27 14:20:49 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
|
|
|
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
It is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
2017-01-11 18:31:31 +00:00
|
|
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
2016-09-27 14:20:49 +00:00
|
|
|
*/
|
|
|
|
#include "history/history_message.h"
|
|
|
|
|
2017-04-13 08:27:10 +00:00
|
|
|
#include "lang/lang_keys.h"
|
2016-09-27 14:20:49 +00:00
|
|
|
#include "mainwidget.h"
|
|
|
|
#include "mainwindow.h"
|
|
|
|
#include "apiwrap.h"
|
|
|
|
#include "history/history_location_manager.h"
|
|
|
|
#include "history/history_service_layout.h"
|
2016-09-28 10:15:03 +00:00
|
|
|
#include "history/history_media_types.h"
|
2017-06-22 15:11:41 +00:00
|
|
|
#include "history/history_service.h"
|
2017-02-23 09:32:28 +00:00
|
|
|
#include "auth_session.h"
|
2017-07-11 17:11:06 +00:00
|
|
|
#include "boxes/share_box.h"
|
|
|
|
#include "boxes/confirm_box.h"
|
|
|
|
#include "ui/toast/toast.h"
|
|
|
|
#include "messenger.h"
|
2016-09-27 14:20:49 +00:00
|
|
|
#include "styles/style_dialogs.h"
|
2016-12-13 17:07:56 +00:00
|
|
|
#include "styles/style_widgets.h"
|
2016-09-30 12:52:03 +00:00
|
|
|
#include "styles/style_history.h"
|
2017-03-04 19:36:59 +00:00
|
|
|
#include "window/notifications_manager.h"
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
constexpr auto kPinnedMessageTextLimit = 16;
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
inline void initTextOptions() {
|
|
|
|
_historySrvOptions.dir = _textNameOptions.dir = _textDlgOptions.dir = cLangDir();
|
|
|
|
_textDlgOptions.maxw = st::dialogsWidthMax * 2;
|
|
|
|
}
|
|
|
|
|
2017-02-09 13:46:14 +00:00
|
|
|
style::color fromNameFg(int index) {
|
2017-06-21 08:53:14 +00:00
|
|
|
Expects(index >= 0 && index < 8);
|
2017-02-09 13:46:14 +00:00
|
|
|
style::color colors[] = {
|
|
|
|
st::historyPeer1NameFg,
|
|
|
|
st::historyPeer2NameFg,
|
|
|
|
st::historyPeer3NameFg,
|
|
|
|
st::historyPeer4NameFg,
|
|
|
|
st::historyPeer5NameFg,
|
|
|
|
st::historyPeer6NameFg,
|
|
|
|
st::historyPeer7NameFg,
|
|
|
|
st::historyPeer8NameFg,
|
|
|
|
};
|
|
|
|
return colors[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
style::color fromNameFgSelected(int index) {
|
2017-06-21 08:53:14 +00:00
|
|
|
Expects(index >= 0 && index < 8);
|
2017-02-09 13:46:14 +00:00
|
|
|
style::color colors[] = {
|
|
|
|
st::historyPeer1NameFgSelected,
|
|
|
|
st::historyPeer2NameFgSelected,
|
|
|
|
st::historyPeer3NameFgSelected,
|
|
|
|
st::historyPeer4NameFgSelected,
|
|
|
|
st::historyPeer5NameFgSelected,
|
|
|
|
st::historyPeer6NameFgSelected,
|
|
|
|
st::historyPeer7NameFgSelected,
|
|
|
|
st::historyPeer8NameFgSelected,
|
|
|
|
};
|
|
|
|
return colors[index];
|
|
|
|
}
|
|
|
|
|
2017-06-22 15:11:41 +00:00
|
|
|
MTPDmessage::Flags NewForwardedFlags(gsl::not_null<PeerData*> peer, int32 from, gsl::not_null<HistoryMessage*> fwd) {
|
|
|
|
auto result = NewMessageFlags(peer) | MTPDmessage::Flag::f_fwd_from;
|
2017-06-16 16:04:30 +00:00
|
|
|
if (from) {
|
|
|
|
result |= MTPDmessage::Flag::f_from_id;
|
|
|
|
}
|
|
|
|
if (fwd->Has<HistoryMessageVia>()) {
|
|
|
|
result |= MTPDmessage::Flag::f_via_bot_id;
|
|
|
|
}
|
|
|
|
if (auto channel = peer->asChannel()) {
|
|
|
|
if (auto media = fwd->getMedia()) {
|
|
|
|
if (media->type() == MediaTypeWebPage) {
|
|
|
|
// Drop web page if we're not allowed to send it.
|
|
|
|
if (channel->restrictedRights().is_embed_links()) {
|
|
|
|
result &= MTPDmessage::Flag::f_media;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (auto 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;
|
|
|
|
}
|
|
|
|
|
2017-07-11 17:11:06 +00:00
|
|
|
bool HasMediaItems(const SelectedItemSet &items) {
|
|
|
|
for_const (auto item, items) {
|
|
|
|
if (auto media = item->getMedia()) {
|
|
|
|
switch (media->type()) {
|
|
|
|
case MediaTypePhoto:
|
|
|
|
case MediaTypeVideo:
|
|
|
|
case MediaTypeFile:
|
|
|
|
case MediaTypeMusicFile:
|
|
|
|
case MediaTypeVoiceFile: return true;
|
|
|
|
case MediaTypeGif: return media->getDocument()->isRoundVideo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HasStickerItems(const SelectedItemSet &items) {
|
|
|
|
for_const (auto item, items) {
|
|
|
|
if (auto media = item->getMedia()) {
|
|
|
|
switch (media->type()) {
|
|
|
|
case MediaTypeSticker: return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HasGifItems(const SelectedItemSet &items) {
|
|
|
|
for_const (auto item, items) {
|
|
|
|
if (auto media = item->getMedia()) {
|
|
|
|
switch (media->type()) {
|
|
|
|
case MediaTypeGif: return !media->getDocument()->isRoundVideo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HasGameItems(const SelectedItemSet &items) {
|
|
|
|
for_const (auto item, items) {
|
|
|
|
if (auto media = item->getMedia()) {
|
|
|
|
switch (media->type()) {
|
|
|
|
case MediaTypeGame: return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HasInlineItems(const SelectedItemSet &items) {
|
|
|
|
for_const (auto item, items) {
|
|
|
|
if (item->viaBot()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
} // namespace
|
|
|
|
|
2017-07-11 17:11:06 +00:00
|
|
|
void FastShareMessage(gsl::not_null<HistoryItem*> item) {
|
|
|
|
struct ShareData {
|
|
|
|
ShareData(const FullMsgId &msgId) : msgId(msgId) {
|
|
|
|
}
|
|
|
|
FullMsgId msgId;
|
|
|
|
OrderedSet<mtpRequestId> requests;
|
|
|
|
};
|
|
|
|
auto data = MakeShared<ShareData>(item->fullId());
|
|
|
|
|
|
|
|
auto canCopyLink = item->hasDirectLink();
|
|
|
|
if (!canCopyLink) {
|
|
|
|
if (auto bot = item->getMessageBot()) {
|
|
|
|
if (auto media = item->getMedia()) {
|
|
|
|
canCopyLink = (media->type() == MediaTypeGame);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto copyCallback = [data]() {
|
|
|
|
if (auto main = App::main()) {
|
|
|
|
if (auto item = App::histItemById(data->msgId)) {
|
|
|
|
if (item->hasDirectLink()) {
|
|
|
|
QApplication::clipboard()->setText(item->directLink());
|
|
|
|
|
|
|
|
Ui::Toast::Show(lang(lng_channel_public_link_copied));
|
|
|
|
} else if (auto bot = item->getMessageBot()) {
|
|
|
|
if (auto media = item->getMedia()) {
|
|
|
|
if (media->type() == MediaTypeGame) {
|
|
|
|
auto shortName = static_cast<HistoryGame*>(media)->game()->shortName;
|
|
|
|
|
|
|
|
QApplication::clipboard()->setText(Messenger::Instance().createInternalLinkFull(bot->username + qsl("?game=") + shortName));
|
|
|
|
|
|
|
|
Ui::Toast::Show(lang(lng_share_game_link_copied));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
auto submitCallback = [data](const QVector<PeerData*> &result) {
|
|
|
|
if (!data->requests.empty()) {
|
|
|
|
return; // Share clicked already.
|
|
|
|
}
|
|
|
|
auto item = App::histItemById(data->msgId);
|
|
|
|
if (!item || result.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto items = SelectedItemSet();
|
|
|
|
auto restrictedSomewhere = false;
|
|
|
|
auto restrictedEverywhere = true;
|
|
|
|
auto firstError = QString();
|
|
|
|
items.insert(item->id, item);
|
|
|
|
for_const (auto peer, result) {
|
|
|
|
auto error = GetErrorTextForForward(peer, items);
|
|
|
|
if (!error.isEmpty()) {
|
|
|
|
if (firstError.isEmpty()) {
|
|
|
|
firstError = error;
|
|
|
|
}
|
|
|
|
restrictedSomewhere = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
restrictedEverywhere = false;
|
|
|
|
}
|
|
|
|
if (restrictedEverywhere) {
|
|
|
|
Ui::show(Box<InformBox>(firstError), KeepOtherLayers);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto doneCallback = [data](const MTPUpdates &updates, mtpRequestId requestId) {
|
|
|
|
if (auto main = App::main()) {
|
|
|
|
main->sentUpdatesReceived(updates);
|
|
|
|
}
|
|
|
|
data->requests.remove(requestId);
|
|
|
|
if (data->requests.empty()) {
|
|
|
|
Ui::Toast::Show(lang(lng_share_done));
|
|
|
|
Ui::hideLayer();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
auto sendFlags = MTPmessages_ForwardMessages::Flag::f_with_my_score;
|
|
|
|
MTPVector<MTPint> msgIds = MTP_vector<MTPint>(1, MTP_int(data->msgId.msg));
|
|
|
|
if (auto main = App::main()) {
|
|
|
|
for_const (auto peer, result) {
|
|
|
|
if (!GetErrorTextForForward(peer, items).isEmpty()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
MTPVector<MTPlong> random = MTP_vector<MTPlong>(1, rand_value<MTPlong>());
|
|
|
|
auto request = MTPmessages_ForwardMessages(MTP_flags(sendFlags), item->history()->peer->input, msgIds, random, peer->input);
|
|
|
|
auto callback = doneCallback;
|
|
|
|
auto requestId = MTP::send(request, rpcDone(std::move(callback)));
|
|
|
|
data->requests.insert(requestId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
auto filterCallback = [](PeerData *peer) {
|
|
|
|
if (peer->canWrite()) {
|
|
|
|
if (auto channel = peer->asChannel()) {
|
|
|
|
return !channel->isBroadcast();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
auto copyLinkCallback = canCopyLink ? base::lambda<void()>(std::move(copyCallback)) : base::lambda<void()>();
|
|
|
|
Ui::show(Box<ShareBox>(std::move(copyLinkCallback), std::move(submitCallback), std::move(filterCallback)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryInitMessages() {
|
|
|
|
initTextOptions();
|
|
|
|
}
|
|
|
|
|
2017-06-22 15:11:41 +00:00
|
|
|
base::lambda<void(ChannelData*, MsgId)> HistoryDependentItemCallback(const FullMsgId &msgId) {
|
|
|
|
return [dependent = msgId](ChannelData *channel, MsgId msgId) {
|
|
|
|
if (auto item = App::histItemById(dependent)) {
|
|
|
|
item->updateDependencyItem();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
MTPDmessage::Flags NewMessageFlags(gsl::not_null<PeerData*> peer) {
|
|
|
|
MTPDmessage::Flags result = 0;
|
|
|
|
if (!peer->isSelf()) {
|
|
|
|
result |= MTPDmessage::Flag::f_out;
|
|
|
|
//if (p->isChat() || (p->isUser() && !p->asUser()->botInfo)) {
|
|
|
|
// result |= MTPDmessage::Flag::f_unread;
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-07-11 17:11:06 +00:00
|
|
|
QString GetErrorTextForForward(gsl::not_null<PeerData*> peer, const SelectedItemSet &items) {
|
|
|
|
if (!peer->canWrite()) {
|
|
|
|
return lang(lng_forward_cant);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto megagroup = peer->asMegagroup()) {
|
|
|
|
if (megagroup->restrictedRights().is_send_media() && HasMediaItems(items)) {
|
|
|
|
return lang(lng_restricted_send_media);
|
|
|
|
} else if (megagroup->restrictedRights().is_send_stickers() && HasStickerItems(items)) {
|
|
|
|
return lang(lng_restricted_send_stickers);
|
|
|
|
} else if (megagroup->restrictedRights().is_send_gifs() && HasGifItems(items)) {
|
|
|
|
return lang(lng_restricted_send_gifs);
|
|
|
|
} else if (megagroup->restrictedRights().is_send_games() && HasGameItems(items)) {
|
|
|
|
return lang(lng_restricted_send_inline);
|
|
|
|
} else if (megagroup->restrictedRights().is_send_inline() && HasInlineItems(items)) {
|
|
|
|
return lang(lng_restricted_send_inline);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QString();
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessageVia::create(int32 userId) {
|
|
|
|
_bot = App::user(peerFromUser(userId));
|
|
|
|
_maxWidth = st::msgServiceNameFont->width(lng_inline_bot_via(lt_inline_bot, '@' + _bot->username));
|
2016-11-16 10:44:06 +00:00
|
|
|
_lnk = MakeShared<LambdaClickHandler>([bot = _bot] {
|
|
|
|
App::insertBotCommand('@' + bot->username);
|
|
|
|
});
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessageVia::resize(int32 availw) const {
|
|
|
|
if (availw < 0) {
|
|
|
|
_text = QString();
|
|
|
|
_width = 0;
|
|
|
|
} else {
|
|
|
|
_text = lng_inline_bot_via(lt_inline_bot, '@' + _bot->username);
|
|
|
|
if (availw < _maxWidth) {
|
|
|
|
_text = st::msgServiceNameFont->elided(_text, availw);
|
|
|
|
_width = st::msgServiceNameFont->width(_text);
|
|
|
|
} else if (_width < _maxWidth) {
|
|
|
|
_width = _maxWidth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-18 19:00:33 +00:00
|
|
|
void HistoryMessageSigned::create(UserData *from, const QString &date) {
|
|
|
|
auto time = qsl(", ") + date;
|
2017-06-21 08:53:14 +00:00
|
|
|
auto name = App::peerName(from);
|
|
|
|
auto timew = st::msgDateFont->width(time);
|
|
|
|
auto namew = st::msgDateFont->width(name);
|
2016-09-27 14:20:49 +00:00
|
|
|
if (timew + namew > st::maxSignatureSize) {
|
|
|
|
name = st::msgDateFont->elided(from->firstName, st::maxSignatureSize - timew);
|
|
|
|
}
|
2016-12-23 13:21:01 +00:00
|
|
|
_signature.setText(st::msgDateTextStyle, name + time, _textNameOptions);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int HistoryMessageSigned::maxWidth() const {
|
|
|
|
return _signature.maxWidth();
|
|
|
|
}
|
|
|
|
|
2017-07-18 19:00:33 +00:00
|
|
|
void HistoryMessageEdited::create(const QDateTime &editDate, const QString &date) {
|
2016-09-27 14:20:49 +00:00
|
|
|
_editDate = editDate;
|
2017-07-18 19:00:33 +00:00
|
|
|
_edited.setText(st::msgDateTextStyle, lang(lng_edited) + ' ' + date, _textNameOptions);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int HistoryMessageEdited::maxWidth() const {
|
|
|
|
return _edited.maxWidth();
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (_authorOriginal->isChannel()) {
|
|
|
|
text = lng_forwarded_channel(lt_channel, textcmdLink(1, text));
|
|
|
|
} else {
|
|
|
|
text = lng_forwarded(lt_user, textcmdLink(1, text));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TextParseOptions opts = { TextParseRichText, 0, 0, Qt::LayoutDirectionAuto };
|
2016-12-23 13:21:01 +00:00
|
|
|
_text.setText(st::fwdTextStyle, text, opts);
|
2016-11-16 10:44:06 +00:00
|
|
|
_text.setLink(1, (_originalId && _authorOriginal->isChannel()) ? goToMessageClickHandler(_authorOriginal, _originalId) : _authorOriginal->openLink());
|
2016-09-27 14:20:49 +00:00
|
|
|
if (via) {
|
|
|
|
_text.setLink(2, via->_lnk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2017-07-06 11:37:42 +00:00
|
|
|
replyToText.setText(st::messageTextStyle, TextUtilities::Clean(replyToMsg->inReplyText()), _textDlgOptions);
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
updateName();
|
|
|
|
|
2016-11-16 10:44:06 +00:00
|
|
|
replyToLnk = goToMessageClickHandler(replyToMsg);
|
2016-09-27 14:20:49 +00:00
|
|
|
if (!replyToMsg->Has<HistoryMessageForwarded>()) {
|
|
|
|
if (auto bot = replyToMsg->viaBot()) {
|
2017-03-05 13:39:10 +00:00
|
|
|
_replyToVia = std::make_unique<HistoryMessageVia>();
|
2016-09-27 14:20:49 +00:00
|
|
|
_replyToVia->create(peerToUser(bot->id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (force) {
|
|
|
|
replyToMsgId = 0;
|
|
|
|
}
|
|
|
|
if (force) {
|
|
|
|
holder->setPendingInitDimensions();
|
|
|
|
}
|
|
|
|
return (replyToMsg || !replyToMsgId);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessageReply::clearData(HistoryMessage *holder) {
|
|
|
|
_replyToVia = nullptr;
|
|
|
|
if (replyToMsg) {
|
|
|
|
App::historyUnregDependency(holder, replyToMsg);
|
|
|
|
replyToMsg = nullptr;
|
|
|
|
}
|
|
|
|
replyToMsgId = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryMessageReply::isNameUpdated() const {
|
|
|
|
if (replyToMsg && replyToMsg->author()->nameVersion > replyToVersion) {
|
|
|
|
updateName();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessageReply::updateName() const {
|
|
|
|
if (replyToMsg) {
|
|
|
|
QString name = (_replyToVia && replyToMsg->author()->isUser()) ? replyToMsg->author()->asUser()->firstName : App::peerName(replyToMsg->author());
|
2016-12-23 13:21:01 +00:00
|
|
|
replyToName.setText(st::fwdTextStyle, name, _textNameOptions);
|
2016-09-27 14:20:49 +00:00
|
|
|
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) {
|
|
|
|
clearData(holder);
|
|
|
|
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();
|
|
|
|
|
2016-12-23 13:21:01 +00:00
|
|
|
style::color bar = st::msgImgReplyBarColor;
|
2016-09-27 14:20:49 +00:00
|
|
|
if (flags & PaintInBubble) {
|
2016-12-23 13:21:01 +00:00
|
|
|
bar = (flags & PaintSelected) ? (outbg ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (outbg ? st::msgOutReplyBarColor : st::msgInReplyBarColor);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
QRect rbar(rtlrect(x + st::msgReplyBarPos.x(), y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), w + 2 * x));
|
2016-12-23 13:21:01 +00:00
|
|
|
p.fillRect(rbar, bar);
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
if (w > st::msgReplyBarSkip) {
|
|
|
|
if (replyToMsg) {
|
2017-07-18 16:47:56 +00:00
|
|
|
auto hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false;
|
|
|
|
if (hasPreview && w < st::msgReplyBarSkip + st::msgReplyBarSize.height()) {
|
|
|
|
hasPreview = false;
|
|
|
|
}
|
|
|
|
auto previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
if (hasPreview) {
|
|
|
|
ImagePtr replyPreview = replyToMsg->getMedia()->replyPreview();
|
|
|
|
if (!replyPreview->isNull()) {
|
2017-06-06 09:15:13 +00:00
|
|
|
auto to = rtlrect(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height(), w + 2 * x);
|
|
|
|
auto previewWidth = replyPreview->width() / cIntRetinaFactor();
|
|
|
|
auto previewHeight = replyPreview->height() / cIntRetinaFactor();
|
|
|
|
auto preview = replyPreview->pixSingle(previewWidth, previewHeight, to.width(), to.height(), ImageRoundRadius::Small, ImageRoundCorner::All, selected ? &st::msgStickerOverlay : nullptr);
|
|
|
|
p.drawPixmap(to.x(), to.y(), preview);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (w > st::msgReplyBarSkip + previewSkip) {
|
|
|
|
if (flags & PaintInBubble) {
|
|
|
|
p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
|
|
|
|
} else {
|
2016-10-31 12:29:26 +00:00
|
|
|
p.setPen(st::msgImgReplyBarColor);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-12-21 15:05:58 +00:00
|
|
|
auto replyToAsMsg = replyToMsg->toHistoryMessage();
|
2016-09-27 14:20:49 +00:00
|
|
|
if (!(flags & PaintInBubble)) {
|
|
|
|
} else if ((replyToAsMsg && replyToAsMsg->emptyText()) || replyToMsg->serviceMsg()) {
|
2017-02-09 13:46:14 +00:00
|
|
|
p.setPen(outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg));
|
2016-09-27 14:20:49 +00:00
|
|
|
} else {
|
2017-02-09 13:46:14 +00:00
|
|
|
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
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);
|
2016-11-03 10:33:57 +00:00
|
|
|
auto &date = outbg ? (selected ? st::msgOutDateFgSelected : st::msgOutDateFg) : (selected ? st::msgInDateFgSelected : st::msgInDateFg);
|
2016-12-21 15:05:58 +00:00
|
|
|
p.setPen((flags & PaintInBubble) ? date : st::msgDateImgFg);
|
2016-09-27 14:20:49 +00:00
|
|
|
p.drawTextLeft(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2, w + 2 * x, st::msgDateFont->elided(lang(replyToMsgId ? lng_profile_loading : lng_deleted_message), w - st::msgReplyBarSkip));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::KeyboardStyle::startPaint(Painter &p) const {
|
2016-12-21 15:05:58 +00:00
|
|
|
p.setPen(st::msgServiceFg);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2016-12-23 13:21:01 +00:00
|
|
|
const style::TextStyle &HistoryMessage::KeyboardStyle::textStyle() const {
|
|
|
|
return st::serviceTextStyle;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-21 21:38:31 +00:00
|
|
|
void HistoryMessage::KeyboardStyle::repaint(gsl::not_null<const HistoryItem*> item) const {
|
2016-09-27 14:20:49 +00:00
|
|
|
Ui::repaintHistoryItem(item);
|
|
|
|
}
|
|
|
|
|
2016-11-16 16:04:25 +00:00
|
|
|
int HistoryMessage::KeyboardStyle::buttonRadius() const {
|
|
|
|
return st::dateRadius;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::KeyboardStyle::paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const {
|
2016-11-03 10:33:57 +00:00
|
|
|
App::roundRect(p, rect, st::msgServiceBg, StickerCorners);
|
2016-09-27 14:20:49 +00:00
|
|
|
if (howMuchOver > 0) {
|
2016-10-31 12:29:26 +00:00
|
|
|
auto o = p.opacity();
|
|
|
|
p.setOpacity(o * howMuchOver);
|
2016-12-21 15:05:58 +00:00
|
|
|
App::roundRect(p, rect, st::msgBotKbOverBgAdd, BotKbOverCorners);
|
2016-09-27 14:20:49 +00:00
|
|
|
p.setOpacity(o);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-26 12:24:21 +00:00
|
|
|
void HistoryMessage::KeyboardStyle::paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const {
|
2016-09-27 14:20:49 +00:00
|
|
|
using Button = HistoryMessageReplyMarkup::Button;
|
2016-10-26 12:24:21 +00:00
|
|
|
auto getIcon = [](Button::Type type) -> const style::icon* {
|
|
|
|
switch (type) {
|
|
|
|
case Button::Type::Url: return &st::msgBotKbUrlIcon;
|
|
|
|
case Button::Type::SwitchInlineSame:
|
|
|
|
case Button::Type::SwitchInline: return &st::msgBotKbSwitchPmIcon;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
};
|
|
|
|
if (auto icon = getIcon(type)) {
|
|
|
|
icon->paint(p, rect.x() + rect.width() - icon->width() - st::msgBotKbIconPadding, rect.y() + st::msgBotKbIconPadding, outerWidth);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::KeyboardStyle::paintButtonLoading(Painter &p, const QRect &rect) const {
|
2016-09-30 12:52:03 +00:00
|
|
|
auto icon = &st::historySendingInvertedIcon;
|
|
|
|
icon->paint(p, rect.x() + rect.width() - icon->width() - st::msgBotKbIconPadding, rect.y() + rect.height() - icon->height() - st::msgBotKbIconPadding, rect.x() * 2 + rect.width());
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int HistoryMessage::KeyboardStyle::minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const {
|
|
|
|
using Button = HistoryMessageReplyMarkup::Button;
|
|
|
|
int result = 2 * buttonPadding(), iconWidth = 0;
|
|
|
|
switch (type) {
|
2016-10-26 12:24:21 +00:00
|
|
|
case Button::Type::Url: iconWidth = st::msgBotKbUrlIcon.width(); break;
|
2016-09-27 14:20:49 +00:00
|
|
|
case Button::Type::SwitchInlineSame:
|
2016-10-26 12:24:21 +00:00
|
|
|
case Button::Type::SwitchInline: iconWidth = st::msgBotKbSwitchPmIcon.width(); break;
|
2016-09-27 14:20:49 +00:00
|
|
|
case Button::Type::Callback:
|
2016-09-30 12:52:03 +00:00
|
|
|
case Button::Type::Game: iconWidth = st::historySendingInvertedIcon.width(); break;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
if (iconWidth > 0) {
|
|
|
|
result = std::max(result, 2 * iconWidth + 4 * int(st::msgBotKbIconPadding));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
HistoryMessage::HistoryMessage(gsl::not_null<History*> history, const MTPDmessage &msg)
|
2017-06-16 16:04:30 +00:00
|
|
|
: HistoryItem(history, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) {
|
2016-09-27 14:20:49 +00:00
|
|
|
CreateConfig config;
|
|
|
|
|
|
|
|
if (msg.has_fwd_from() && msg.vfwd_from.type() == mtpc_messageFwdHeader) {
|
2016-10-07 07:58:34 +00:00
|
|
|
auto &f = msg.vfwd_from.c_messageFwdHeader();
|
2017-06-20 22:05:38 +00:00
|
|
|
config.originalDate = ::date(f.vdate);
|
2016-09-27 14:20:49 +00:00
|
|
|
if (f.has_from_id() || f.has_channel_id()) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
2016-10-07 07:58:34 +00:00
|
|
|
if (msg.has_reply_markup()) config.mtpMarkup = &msg.vreply_markup;
|
2016-09-27 14:20:49 +00:00
|
|
|
if (msg.has_edit_date()) config.editDate = ::date(msg.vedit_date);
|
|
|
|
|
|
|
|
createComponents(config);
|
|
|
|
|
2016-09-28 11:38:35 +00:00
|
|
|
initMedia(msg.has_media() ? (&msg.vmedia) : nullptr);
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-07-06 11:37:42 +00:00
|
|
|
auto text = TextUtilities::Clean(qs(msg.vmessage));
|
|
|
|
auto entities = msg.has_entities() ? TextUtilities::EntitiesFromMTP(msg.ventities.v) : EntitiesInText();
|
2017-06-20 16:03:18 +00:00
|
|
|
setText({ text, entities });
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
HistoryMessage::HistoryMessage(gsl::not_null<History*> history, const MTPDmessageService &msg)
|
2017-06-16 16:04:30 +00:00
|
|
|
: HistoryItem(history, msg.vid.v, mtpCastFlags(msg.vflags.v), ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) {
|
2017-04-27 21:17:00 +00:00
|
|
|
CreateConfig config;
|
|
|
|
|
|
|
|
if (msg.has_reply_to_msg_id()) config.replyTo = msg.vreply_to_msg_id.v;
|
|
|
|
|
|
|
|
createComponents(config);
|
|
|
|
|
|
|
|
switch (msg.vaction.type()) {
|
|
|
|
case mtpc_messageActionPhoneCall: {
|
|
|
|
_media = std::make_unique<HistoryCall>(this, msg.vaction.c_messageActionPhoneCall());
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default: Unexpected("Service message action type in HistoryMessage.");
|
|
|
|
}
|
|
|
|
|
|
|
|
setText(TextWithEntities {});
|
|
|
|
}
|
|
|
|
|
2017-06-22 15:11:41 +00:00
|
|
|
HistoryMessage::HistoryMessage(gsl::not_null<History*> history, MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, gsl::not_null<HistoryMessage*> fwd)
|
|
|
|
: HistoryItem(history, id, NewForwardedFlags(history->peer, from, fwd) | flags, date, from) {
|
2016-09-27 14:20:49 +00:00
|
|
|
CreateConfig config;
|
|
|
|
|
2017-04-30 12:42:04 +00:00
|
|
|
if (fwd->Has<HistoryMessageForwarded>() || !fwd->history()->peer->isSelf()) {
|
|
|
|
// Server doesn't add "fwd_from" to non-forwarded messages from chat with yourself.
|
2017-06-20 22:05:38 +00:00
|
|
|
config.originalDate = fwd->dateOriginal();
|
2017-04-30 12:42:04 +00:00
|
|
|
config.authorIdOriginal = fwd->authorOriginal()->id;
|
|
|
|
config.fromIdOriginal = fwd->fromOriginal()->id;
|
|
|
|
if (fwd->authorOriginal()->isChannel()) {
|
|
|
|
config.originalId = fwd->idOriginal();
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
auto fwdViaBot = fwd->viaBot();
|
|
|
|
if (fwdViaBot) config.viaBotId = peerToUser(fwdViaBot->id);
|
|
|
|
int fwdViewsCount = fwd->viewsCount();
|
|
|
|
if (fwdViewsCount > 0) {
|
|
|
|
config.viewsCount = fwdViewsCount;
|
|
|
|
} else if (isPost()) {
|
|
|
|
config.viewsCount = 1;
|
|
|
|
}
|
|
|
|
|
2016-10-07 07:58:34 +00:00
|
|
|
// Copy inline keyboard when forwarding messages with a game.
|
|
|
|
auto mediaOriginal = fwd->getMedia();
|
2017-06-16 16:04:30 +00:00
|
|
|
auto mediaType = mediaOriginal ? mediaOriginal->type() : MediaTypeCount;
|
|
|
|
if (mediaOriginal && mediaType == MediaTypeGame) {
|
2016-10-07 07:58:34 +00:00
|
|
|
config.inlineMarkup = fwd->inlineReplyMarkup();
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
createComponents(config);
|
|
|
|
|
2017-06-16 16:04:30 +00:00
|
|
|
auto cloneMedia = [this, history, mediaType] {
|
|
|
|
if (mediaType == MediaTypeWebPage) {
|
|
|
|
if (auto channel = history->peer->asChannel()) {
|
|
|
|
if (channel->restrictedRights().is_embed_links()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (mediaType != MediaTypeCount);
|
|
|
|
};
|
|
|
|
if (cloneMedia()) {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = mediaOriginal->clone(this);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
setText(fwd->originalText());
|
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
HistoryMessage::HistoryMessage(gsl::not_null<History*> history, MsgId id, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities)
|
2016-09-27 14:20:49 +00:00
|
|
|
: HistoryItem(history, id, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
|
|
|
|
createComponentsHelper(flags, replyTo, viaBotId, MTPnullMarkup);
|
|
|
|
|
|
|
|
setText(textWithEntities);
|
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
HistoryMessage::HistoryMessage(gsl::not_null<History*> history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup)
|
2016-09-27 14:20:49 +00:00
|
|
|
: HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
|
|
|
|
createComponentsHelper(flags, replyTo, viaBotId, markup);
|
|
|
|
|
|
|
|
initMediaFromDocument(doc, caption);
|
|
|
|
setText(TextWithEntities());
|
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
HistoryMessage::HistoryMessage(gsl::not_null<History*> history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup)
|
2016-09-27 14:20:49 +00:00
|
|
|
: HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
|
|
|
|
createComponentsHelper(flags, replyTo, viaBotId, markup);
|
|
|
|
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryPhoto>(this, photo, caption);
|
2016-09-27 14:20:49 +00:00
|
|
|
setText(TextWithEntities());
|
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
HistoryMessage::HistoryMessage(gsl::not_null<History*> history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup)
|
2016-09-28 16:23:25 +00:00
|
|
|
: HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
|
|
|
|
createComponentsHelper(flags, replyTo, viaBotId, markup);
|
|
|
|
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryGame>(this, game);
|
2016-09-28 16:23:25 +00:00
|
|
|
setText(TextWithEntities());
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
void HistoryMessage::createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, const MTPReplyMarkup &markup) {
|
|
|
|
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-10-07 07:58:34 +00:00
|
|
|
if (flags & MTPDmessage::Flag::f_reply_markup) config.mtpMarkup = &markup;
|
2016-09-27 14:20:49 +00:00
|
|
|
if (isPost()) config.viewsCount = 1;
|
|
|
|
|
|
|
|
createComponents(config);
|
|
|
|
}
|
|
|
|
|
2016-09-28 10:15:03 +00:00
|
|
|
void HistoryMessage::updateMediaInBubbleState() {
|
2017-06-20 19:48:53 +00:00
|
|
|
auto mediaHasSomethingBelow = false;
|
|
|
|
auto mediaHasSomethingAbove = false;
|
|
|
|
auto getMediaHasSomethingAbove = [this] {
|
|
|
|
return displayFromName() || displayForwardedFrom() || Has<HistoryMessageReply>() || Has<HistoryMessageVia>();
|
|
|
|
};
|
2017-06-28 16:16:21 +00:00
|
|
|
auto entry = Get<HistoryMessageLogEntryOriginal>();
|
|
|
|
if (entry) {
|
2017-06-20 19:48:53 +00:00
|
|
|
mediaHasSomethingBelow = true;
|
|
|
|
mediaHasSomethingAbove = getMediaHasSomethingAbove();
|
|
|
|
auto entryState = (mediaHasSomethingAbove || !emptyText() || (_media && _media->isDisplayed())) ? MediaInBubbleState::Bottom : MediaInBubbleState::None;
|
|
|
|
entry->_page->setInBubbleState(entryState);
|
|
|
|
}
|
2016-09-28 10:15:03 +00:00
|
|
|
if (!_media) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!drawBubble()) {
|
|
|
|
_media->setInBubbleState(MediaInBubbleState::None);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-06-28 16:16:21 +00:00
|
|
|
if (!entry) {
|
|
|
|
mediaHasSomethingAbove = getMediaHasSomethingAbove();
|
|
|
|
}
|
2016-09-28 10:15:03 +00:00
|
|
|
if (!emptyText()) {
|
|
|
|
if (_media->isAboveMessage()) {
|
2017-06-20 19:48:53 +00:00
|
|
|
mediaHasSomethingBelow = true;
|
2016-09-28 10:15:03 +00:00
|
|
|
} else {
|
2017-06-20 19:48:53 +00:00
|
|
|
mediaHasSomethingAbove = true;
|
2016-09-28 10:15:03 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-20 19:48:53 +00:00
|
|
|
auto computeState = [mediaHasSomethingAbove, mediaHasSomethingBelow] {
|
|
|
|
if (mediaHasSomethingAbove) {
|
|
|
|
if (mediaHasSomethingBelow) {
|
2016-09-28 10:15:03 +00:00
|
|
|
return MediaInBubbleState::Middle;
|
|
|
|
}
|
|
|
|
return MediaInBubbleState::Bottom;
|
2017-06-20 19:48:53 +00:00
|
|
|
} else if (mediaHasSomethingBelow) {
|
2016-09-28 10:15:03 +00:00
|
|
|
return MediaInBubbleState::Top;
|
|
|
|
}
|
|
|
|
return MediaInBubbleState::None;
|
|
|
|
};
|
|
|
|
_media->setInBubbleState(computeState());
|
|
|
|
}
|
|
|
|
|
2016-10-07 07:58:34 +00:00
|
|
|
bool HistoryMessage::displayEditedBadge(bool hasViaBotOrInlineMarkup) const {
|
|
|
|
if (hasViaBotOrInlineMarkup) {
|
|
|
|
return false;
|
|
|
|
} else if (!(_flags & MTPDmessage::Flag::f_edit_date)) {
|
2016-09-27 14:20:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (auto fromUser = from()->asUser()) {
|
|
|
|
if (fromUser->botInfo) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-22 15:11:41 +00:00
|
|
|
bool HistoryMessage::uploading() const {
|
|
|
|
return _media && _media->uploading();
|
|
|
|
}
|
|
|
|
|
2017-07-11 17:11:06 +00:00
|
|
|
bool HistoryMessage::displayFastShare() const {
|
|
|
|
if (_history->peer->isChannel()) {
|
|
|
|
return !_history->peer->isMegagroup();
|
|
|
|
} else if (auto user = _history->peer->asUser()) {
|
|
|
|
if (user->botInfo && !out()) {
|
|
|
|
return _media && _media->allowsFastShare();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
void HistoryMessage::createComponents(const CreateConfig &config) {
|
|
|
|
uint64 mask = 0;
|
|
|
|
if (config.replyTo) {
|
|
|
|
mask |= HistoryMessageReply::Bit();
|
|
|
|
}
|
|
|
|
if (config.viaBotId) {
|
|
|
|
mask |= HistoryMessageVia::Bit();
|
|
|
|
}
|
|
|
|
if (config.viewsCount >= 0) {
|
|
|
|
mask |= HistoryMessageViews::Bit();
|
|
|
|
}
|
|
|
|
if (isPost() && _from->isUser()) {
|
|
|
|
mask |= HistoryMessageSigned::Bit();
|
|
|
|
}
|
2016-10-07 07:58:34 +00:00
|
|
|
auto hasViaBot = (config.viaBotId != 0);
|
|
|
|
auto hasInlineMarkup = [&config] {
|
|
|
|
if (config.mtpMarkup) {
|
|
|
|
return (config.mtpMarkup->type() == mtpc_replyInlineMarkup);
|
|
|
|
}
|
|
|
|
return (config.inlineMarkup != nullptr);
|
|
|
|
};
|
|
|
|
if (displayEditedBadge(hasViaBot || hasInlineMarkup())) {
|
2016-09-27 14:20:49 +00:00
|
|
|
mask |= HistoryMessageEdited::Bit();
|
|
|
|
}
|
|
|
|
if (config.authorIdOriginal && config.fromIdOriginal) {
|
|
|
|
mask |= HistoryMessageForwarded::Bit();
|
|
|
|
}
|
2016-10-07 07:58:34 +00:00
|
|
|
if (config.mtpMarkup) {
|
2016-09-27 14:20:49 +00:00
|
|
|
// optimization: don't create markup component for the case
|
|
|
|
// MTPDreplyKeyboardHide with flags = 0, assume it has f_zero flag
|
2016-10-07 07:58:34 +00:00
|
|
|
if (config.mtpMarkup->type() != mtpc_replyKeyboardHide || config.mtpMarkup->c_replyKeyboardHide().vflags.v != 0) {
|
2016-09-27 14:20:49 +00:00
|
|
|
mask |= HistoryMessageReplyMarkup::Bit();
|
|
|
|
}
|
2016-10-07 07:58:34 +00:00
|
|
|
} else if (config.inlineMarkup) {
|
|
|
|
mask |= HistoryMessageReplyMarkup::Bit();
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2016-10-07 07:58:34 +00:00
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
UpdateComponents(mask);
|
2016-10-07 07:58:34 +00:00
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
if (auto reply = Get<HistoryMessageReply>()) {
|
|
|
|
reply->replyToMsgId = config.replyTo;
|
|
|
|
if (!reply->updateData(this) && App::api()) {
|
2017-06-22 15:11:41 +00:00
|
|
|
App::api()->requestMessageData(history()->peer->asChannel(), reply->replyToMsgId, HistoryDependentItemCallback(fullId()));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (auto via = Get<HistoryMessageVia>()) {
|
|
|
|
via->create(config.viaBotId);
|
|
|
|
}
|
|
|
|
if (auto views = Get<HistoryMessageViews>()) {
|
|
|
|
views->_views = config.viewsCount;
|
|
|
|
}
|
|
|
|
if (auto edited = Get<HistoryMessageEdited>()) {
|
2017-07-18 19:00:33 +00:00
|
|
|
edited->create(config.editDate, date.toString(cTimeFormat()));
|
|
|
|
if (auto msgsigned = Get<HistoryMessageSigned>()) {
|
|
|
|
msgsigned->create(_from->asUser(), edited->_edited.originalText());
|
|
|
|
}
|
|
|
|
} else if (auto msgsigned = Get<HistoryMessageSigned>()) {
|
|
|
|
msgsigned->create(_from->asUser(), date.toString(cTimeFormat()));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-04-30 12:44:17 +00:00
|
|
|
if (auto forwarded = Get<HistoryMessageForwarded>()) {
|
2017-06-20 22:05:38 +00:00
|
|
|
forwarded->_originalDate = config.originalDate;
|
2017-04-30 12:44:17 +00:00
|
|
|
forwarded->_authorOriginal = App::peer(config.authorIdOriginal);
|
|
|
|
forwarded->_fromOriginal = App::peer(config.fromIdOriginal);
|
|
|
|
forwarded->_originalId = config.originalId;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
if (auto markup = Get<HistoryMessageReplyMarkup>()) {
|
2016-10-07 07:58:34 +00:00
|
|
|
if (config.mtpMarkup) {
|
|
|
|
markup->create(*config.mtpMarkup);
|
|
|
|
} else if (config.inlineMarkup) {
|
|
|
|
markup->create(*config.inlineMarkup);
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_has_switch_inline_button) {
|
|
|
|
_flags |= MTPDmessage_ClientFlag::f_has_switch_inline_button;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
initTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::initTime() {
|
|
|
|
if (auto msgsigned = Get<HistoryMessageSigned>()) {
|
|
|
|
_timeWidth = msgsigned->maxWidth();
|
|
|
|
} else if (auto edited = Get<HistoryMessageEdited>()) {
|
|
|
|
_timeWidth = edited->maxWidth();
|
|
|
|
} else {
|
|
|
|
_timeText = date.toString(cTimeFormat());
|
|
|
|
_timeWidth = st::msgDateFont->width(_timeText);
|
|
|
|
}
|
|
|
|
if (auto views = Get<HistoryMessageViews>()) {
|
|
|
|
views->_viewsText = (views->_views >= 0) ? formatViewsCount(views->_views) : QString();
|
|
|
|
views->_viewsWidth = views->_viewsText.isEmpty() ? 0 : st::msgDateFont->width(views->_viewsText);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-28 11:38:35 +00:00
|
|
|
void HistoryMessage::initMedia(const MTPMessageMedia *media) {
|
2016-09-27 14:20:49 +00:00
|
|
|
switch (media ? media->type() : mtpc_messageMediaEmpty) {
|
|
|
|
case mtpc_messageMediaContact: {
|
|
|
|
auto &d = media->c_messageMediaContact();
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryContact>(this, d.vuser_id.v, qs(d.vfirst_name), qs(d.vlast_name), qs(d.vphone_number));
|
2016-09-27 14:20:49 +00:00
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaGeo: {
|
|
|
|
auto &point = media->c_messageMediaGeo().vgeo;
|
|
|
|
if (point.type() == mtpc_geoPoint) {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryLocation>(this, LocationCoords(point.c_geoPoint()));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaVenue: {
|
|
|
|
auto &d = media->c_messageMediaVenue();
|
|
|
|
if (d.vgeo.type() == mtpc_geoPoint) {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryLocation>(this, LocationCoords(d.vgeo.c_geoPoint()), qs(d.vtitle), qs(d.vaddress));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaPhoto: {
|
|
|
|
auto &photo = media->c_messageMediaPhoto();
|
|
|
|
if (photo.vphoto.type() == mtpc_photo) {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryPhoto>(this, App::feedPhoto(photo.vphoto.c_photo()), qs(photo.vcaption));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaDocument: {
|
|
|
|
auto &document = media->c_messageMediaDocument().vdocument;
|
|
|
|
if (document.type() == mtpc_document) {
|
|
|
|
return initMediaFromDocument(App::feedDocument(document), qs(media->c_messageMediaDocument().vcaption));
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaWebPage: {
|
|
|
|
auto &d = media->c_messageMediaWebPage().vwebpage;
|
|
|
|
switch (d.type()) {
|
|
|
|
case mtpc_webPageEmpty: break;
|
|
|
|
case mtpc_webPagePending: {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryWebPage>(this, App::feedWebPage(d.c_webPagePending()));
|
2016-09-27 14:20:49 +00:00
|
|
|
} break;
|
|
|
|
case mtpc_webPage: {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryWebPage>(this, App::feedWebPage(d.c_webPage()));
|
2016-09-27 14:20:49 +00:00
|
|
|
} break;
|
2016-12-13 07:59:57 +00:00
|
|
|
case mtpc_webPageNotModified: LOG(("API Error: webPageNotModified is unexpected in message media.")); break;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaGame: {
|
|
|
|
auto &d = media->c_messageMediaGame().vgame;
|
|
|
|
if (d.type() == mtpc_game) {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryGame>(this, App::feedGame(d.c_game()));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
2017-03-05 13:39:10 +00:00
|
|
|
case mtpc_messageMediaInvoice: {
|
|
|
|
_media = std::make_unique<HistoryInvoice>(this, media->c_messageMediaInvoice());
|
2017-03-06 15:00:59 +00:00
|
|
|
if (static_cast<HistoryInvoice*>(getMedia())->getReceiptMsgId()) {
|
|
|
|
replaceBuyWithReceiptInMarkup();
|
|
|
|
}
|
2017-03-05 13:39:10 +00:00
|
|
|
} break;
|
2016-09-27 14:20:49 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-03-06 15:00:59 +00:00
|
|
|
void HistoryMessage::replaceBuyWithReceiptInMarkup() {
|
|
|
|
if (auto markup = inlineReplyMarkup()) {
|
|
|
|
for (auto &row : markup->rows) {
|
|
|
|
for (auto &button : row) {
|
|
|
|
if (button.type == HistoryMessageReplyMarkup::Button::Type::Buy) {
|
|
|
|
button.text = lang(lng_payments_receipt_button);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
void HistoryMessage::initMediaFromDocument(DocumentData *doc, const QString &caption) {
|
|
|
|
if (doc->sticker()) {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistorySticker>(this, doc);
|
2016-09-27 14:20:49 +00:00
|
|
|
} else if (doc->isAnimation()) {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryGif>(this, doc, caption);
|
2016-09-27 14:20:49 +00:00
|
|
|
} else if (doc->isVideo()) {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryVideo>(this, doc, caption);
|
2016-09-27 14:20:49 +00:00
|
|
|
} else {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media = std::make_unique<HistoryDocument>(this, doc, caption);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 HistoryMessage::plainMaxWidth() const {
|
|
|
|
return st::msgPadding.left() + _text.maxWidth() + st::msgPadding.right();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::initDimensions() {
|
2016-09-28 10:15:03 +00:00
|
|
|
updateMediaInBubbleState();
|
2016-09-27 14:20:49 +00:00
|
|
|
if (drawBubble()) {
|
2017-04-30 12:44:17 +00:00
|
|
|
auto forwarded = Get<HistoryMessageForwarded>();
|
2017-06-22 15:11:41 +00:00
|
|
|
auto reply = Get<HistoryMessageReply>();
|
2016-09-27 14:20:49 +00:00
|
|
|
auto via = Get<HistoryMessageVia>();
|
2017-06-22 15:11:41 +00:00
|
|
|
auto entry = Get<HistoryMessageLogEntryOriginal>();
|
2017-04-30 12:44:17 +00:00
|
|
|
if (forwarded) {
|
|
|
|
forwarded->create(via);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-06-22 15:11:41 +00:00
|
|
|
if (reply) {
|
|
|
|
reply->updateName();
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2016-09-28 10:15:03 +00:00
|
|
|
auto mediaDisplayed = false;
|
2016-09-27 14:20:49 +00:00
|
|
|
if (_media) {
|
2016-09-28 10:15:03 +00:00
|
|
|
mediaDisplayed = _media->isDisplayed();
|
2016-09-27 14:20:49 +00:00
|
|
|
_media->initDimensions();
|
|
|
|
}
|
2017-06-20 19:48:53 +00:00
|
|
|
if (entry) {
|
|
|
|
entry->_page->initDimensions();
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-06-22 15:11:41 +00:00
|
|
|
// Entry page is always a bubble bottom.
|
|
|
|
auto mediaOnBottom = (mediaDisplayed && _media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/);
|
|
|
|
auto mediaOnTop = (mediaDisplayed && _media->isBubbleTop()) || (entry && entry->_page->isBubbleTop());
|
|
|
|
|
2017-06-23 10:54:18 +00:00
|
|
|
if (mediaOnBottom) {
|
2017-06-22 15:11:41 +00:00
|
|
|
if (_text.hasSkipBlock()) {
|
|
|
|
_text.removeSkipBlock();
|
|
|
|
_textWidth = -1;
|
|
|
|
_textHeight = 0;
|
|
|
|
}
|
|
|
|
} else if (!_text.hasSkipBlock()) {
|
|
|
|
_text.setSkipBlock(skipBlockWidth(), skipBlockHeight());
|
|
|
|
_textWidth = -1;
|
|
|
|
_textHeight = 0;
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
_maxw = plainMaxWidth();
|
2016-09-28 10:15:03 +00:00
|
|
|
_minh = emptyText() ? 0 : _text.minHeight();
|
2017-06-22 15:11:41 +00:00
|
|
|
if (!mediaOnBottom) {
|
|
|
|
_minh += st::msgPadding.bottom();
|
|
|
|
if (mediaDisplayed) _minh += st::mediaInBubbleSkip;
|
|
|
|
}
|
|
|
|
if (!mediaOnTop) {
|
|
|
|
_minh += st::msgPadding.top();
|
|
|
|
if (mediaDisplayed) _minh += st::mediaInBubbleSkip;
|
|
|
|
if (entry) _minh += st::mediaInBubbleSkip;
|
|
|
|
}
|
2016-09-28 10:15:03 +00:00
|
|
|
if (mediaDisplayed) {
|
2017-06-22 15:11:41 +00:00
|
|
|
// Parts don't participate in maxWidth() in case of media message.
|
|
|
|
accumulate_max(_maxw, _media->maxWidth());
|
2016-09-27 14:20:49 +00:00
|
|
|
_minh += _media->minHeight();
|
2016-09-28 10:15:03 +00:00
|
|
|
} else {
|
2017-06-22 15:11:41 +00:00
|
|
|
// Count parts in maxWidth(), don't count them in minHeight().
|
|
|
|
// They will be added in resizeGetHeight() anyway.
|
2016-09-27 14:20:49 +00:00
|
|
|
if (displayFromName()) {
|
2016-09-28 10:15:03 +00:00
|
|
|
auto namew = st::msgPadding.left() + author()->nameText.maxWidth() + st::msgPadding.right();
|
2017-04-30 12:44:17 +00:00
|
|
|
if (via && !forwarded) {
|
2016-09-27 14:20:49 +00:00
|
|
|
namew += st::msgServiceFont->spacew + via->_maxWidth;
|
|
|
|
}
|
2017-06-22 15:11:41 +00:00
|
|
|
accumulate_max(_maxw, namew);
|
2017-04-30 12:44:17 +00:00
|
|
|
} else if (via && !forwarded) {
|
2017-06-22 15:11:41 +00:00
|
|
|
accumulate_max(_maxw, st::msgPadding.left() + via->_maxWidth + st::msgPadding.right());
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-04-30 12:44:17 +00:00
|
|
|
if (forwarded) {
|
2017-06-22 15:11:41 +00:00
|
|
|
auto namew = st::msgPadding.left() + forwarded->_text.maxWidth() + st::msgPadding.right();
|
2016-09-27 14:20:49 +00:00
|
|
|
if (via) {
|
2017-06-22 15:11:41 +00:00
|
|
|
namew += st::msgServiceFont->spacew + via->_maxWidth;
|
|
|
|
}
|
|
|
|
accumulate_max(_maxw, namew);
|
|
|
|
}
|
|
|
|
if (reply) {
|
|
|
|
auto 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-09-27 14:20:49 +00:00
|
|
|
}
|
2017-06-22 15:11:41 +00:00
|
|
|
accumulate_max(_maxw, replyw);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-06-20 19:48:53 +00:00
|
|
|
if (entry) {
|
|
|
|
accumulate_max(_maxw, entry->_page->maxWidth());
|
2017-07-18 18:59:53 +00:00
|
|
|
_minh += entry->_page->minHeight();
|
2017-06-20 19:48:53 +00:00
|
|
|
}
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
} else if (_media) {
|
|
|
|
_media->initDimensions();
|
|
|
|
_maxw = _media->maxWidth();
|
|
|
|
_minh = _media->minHeight();
|
|
|
|
} else {
|
|
|
|
_maxw = st::msgMinWidth;
|
|
|
|
_minh = 0;
|
|
|
|
}
|
|
|
|
if (auto markup = inlineReplyMarkup()) {
|
|
|
|
if (!markup->inlineKeyboard) {
|
2017-06-22 15:11:41 +00:00
|
|
|
markup->inlineKeyboard = std::make_unique<ReplyKeyboard>(this, std::make_unique<KeyboardStyle>(st::msgBotKbButton));
|
2016-09-27 14:20:49 +00: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 (!emptyText()) {
|
2017-06-22 15:11:41 +00:00
|
|
|
accumulate_max(_maxw, markup->inlineKeyboard->naturalWidth());
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-20 19:48:53 +00:00
|
|
|
bool HistoryMessage::drawBubble() const {
|
|
|
|
if (Has<HistoryMessageLogEntryOriginal>()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return _media ? (!emptyText() || _media->needsBubble()) : !isEmpty();
|
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
QRect HistoryMessage::countGeometry() const {
|
|
|
|
auto maxwidth = qMin(st::msgMaxWidth, _maxw);
|
2016-09-27 14:20:49 +00:00
|
|
|
if (_media && _media->currentWidth() < maxwidth) {
|
|
|
|
maxwidth = qMax(_media->currentWidth(), qMin(maxwidth, plainMaxWidth()));
|
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
auto contentLeft = (!isPost() && out() && !Adaptive::ChatWide()) ? st::msgMargin.right() : st::msgMargin.left();
|
2016-09-27 14:20:49 +00:00
|
|
|
if (hasFromPhoto()) {
|
2017-06-21 08:53:14 +00:00
|
|
|
contentLeft += st::msgPhotoSkip;
|
|
|
|
// } else if (!Adaptive::Wide() && !out() && !fromChannel() && st::msgPhotoSkip - (hmaxwidth - hwidth) > 0) {
|
|
|
|
// contentLeft += st::msgPhotoSkip - (hmaxwidth - hwidth);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
auto contentWidth = width() - st::msgMargin.left() - st::msgMargin.right();
|
|
|
|
if (contentWidth > maxwidth) {
|
2017-01-14 18:50:16 +00:00
|
|
|
if (!isPost() && out() && !Adaptive::ChatWide()) {
|
2017-06-21 08:53:14 +00:00
|
|
|
contentLeft += contentWidth - maxwidth;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-06-21 08:53:14 +00:00
|
|
|
contentWidth = maxwidth;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-06-21 08:53:14 +00:00
|
|
|
|
|
|
|
auto contentTop = marginTop();
|
|
|
|
return QRect(contentLeft, contentTop, contentWidth, _height - contentTop - marginBottom());
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::fromNameUpdated(int32 width) const {
|
|
|
|
_authorNameVersion = author()->nameVersion;
|
|
|
|
if (!Has<HistoryMessageForwarded>()) {
|
|
|
|
if (auto via = Get<HistoryMessageVia>()) {
|
|
|
|
via->resize(width - st::msgPadding.left() - st::msgPadding.right() - author()->nameText.maxWidth() - st::msgServiceFont->spacew);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::applyEdition(const MTPDmessage &message) {
|
|
|
|
int keyboardTop = -1;
|
|
|
|
if (!pendingResize()) {
|
|
|
|
if (auto keyboard = inlineReplyKeyboard()) {
|
|
|
|
int h = st::msgBotKbButton.margin + keyboard->naturalHeight();
|
|
|
|
keyboardTop = _height - h + st::msgBotKbButton.margin - marginBottom();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message.has_edit_date()) {
|
|
|
|
_flags |= MTPDmessage::Flag::f_edit_date;
|
2016-10-07 07:58:34 +00:00
|
|
|
auto hasViaBotId = Has<HistoryMessageVia>();
|
|
|
|
auto hasInlineMarkup = (inlineReplyMarkup() != nullptr);
|
|
|
|
if (displayEditedBadge(hasViaBotId || hasInlineMarkup)) {
|
2016-09-27 14:20:49 +00:00
|
|
|
if (!Has<HistoryMessageEdited>()) {
|
|
|
|
AddComponents(HistoryMessageEdited::Bit());
|
|
|
|
}
|
2017-07-18 19:00:33 +00:00
|
|
|
auto edited = Get<HistoryMessageEdited>();
|
|
|
|
edited->create(::date(message.vedit_date), date.toString(cTimeFormat()));
|
|
|
|
if (auto msgsigned = Get<HistoryMessageSigned>()) {
|
|
|
|
msgsigned->create(_from->asUser(), edited->_edited.originalText());
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
} else if (Has<HistoryMessageEdited>()) {
|
|
|
|
RemoveComponents(HistoryMessageEdited::Bit());
|
2017-07-18 19:00:33 +00:00
|
|
|
if (auto msgsigned = Get<HistoryMessageSigned>()) {
|
|
|
|
msgsigned->create(_from->asUser(), date.toString(cTimeFormat()));
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
initTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
TextWithEntities textWithEntities = { qs(message.vmessage), EntitiesInText() };
|
|
|
|
if (message.has_entities()) {
|
2017-07-06 11:37:42 +00:00
|
|
|
textWithEntities.entities = TextUtilities::EntitiesFromMTP(message.ventities.v);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
setText(textWithEntities);
|
|
|
|
setReplyMarkup(message.has_reply_markup() ? (&message.vreply_markup) : nullptr);
|
2017-03-06 15:00:59 +00:00
|
|
|
setMedia(message.has_media() ? (&message.vmedia) : nullptr);
|
2016-09-27 14:20:49 +00:00
|
|
|
setViewsCount(message.has_views() ? message.vviews.v : -1);
|
|
|
|
|
|
|
|
finishEdition(keyboardTop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::applyEdition(const MTPDmessageService &message) {
|
|
|
|
if (message.vaction.type() == mtpc_messageActionHistoryClear) {
|
|
|
|
applyEditionToEmpty();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::applyEditionToEmpty() {
|
|
|
|
setEmptyText();
|
|
|
|
setMedia(nullptr);
|
|
|
|
setReplyMarkup(nullptr);
|
|
|
|
setViewsCount(-1);
|
|
|
|
|
|
|
|
finishEditionToEmpty();
|
|
|
|
}
|
|
|
|
|
2017-06-22 15:11:41 +00:00
|
|
|
bool HistoryMessage::displayForwardedFrom() const {
|
|
|
|
if (auto forwarded = Get<HistoryMessageForwarded>()) {
|
|
|
|
return Has<HistoryMessageVia>()
|
|
|
|
|| !_media
|
|
|
|
|| !_media->isDisplayed()
|
|
|
|
|| !_media->hideForwardedFrom()
|
|
|
|
|| forwarded->_authorOriginal->isChannel();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
void HistoryMessage::updateMedia(const MTPMessageMedia *media) {
|
|
|
|
auto setMediaAllowed = [](HistoryMediaType type) {
|
|
|
|
return (type == MediaTypeWebPage || type == MediaTypeGame || type == MediaTypeLocation);
|
|
|
|
};
|
|
|
|
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 && !setMediaAllowed(_media->type())) {
|
|
|
|
_media->updateSentMedia(*media);
|
|
|
|
} else {
|
|
|
|
setMedia(media);
|
|
|
|
}
|
|
|
|
setPendingInitDimensions();
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 HistoryMessage::addToOverview(AddToOverviewMethod method) {
|
|
|
|
if (!indexInOverview()) return 0;
|
|
|
|
|
|
|
|
int32 result = 0;
|
2017-05-18 12:37:06 +00:00
|
|
|
if (auto media = getMedia()) {
|
|
|
|
result |= media->addToOverview(method);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
if (hasTextLinks()) {
|
|
|
|
if (history()->addToOverview(OverviewLinks, id, method)) {
|
|
|
|
result |= (1 << OverviewLinks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::eraseFromOverview() {
|
2017-05-18 12:37:06 +00:00
|
|
|
if (auto media = getMedia()) {
|
|
|
|
media->eraseFromOverview();
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
if (hasTextLinks()) {
|
|
|
|
history()->eraseFromOverview(OverviewLinks, id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TextWithEntities HistoryMessage::selectedText(TextSelection selection) const {
|
2017-06-24 10:11:29 +00:00
|
|
|
TextWithEntities logEntryOriginalResult;
|
|
|
|
auto textResult = _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection, ExpandLinksAll);
|
2017-06-23 19:28:42 +00:00
|
|
|
auto skipped = skipTextSelection(selection);
|
|
|
|
auto mediaDisplayed = (_media && _media->isDisplayed());
|
2017-06-24 10:11:29 +00:00
|
|
|
auto mediaResult = mediaDisplayed ? _media->selectedText(skipped) : TextWithEntities();
|
2017-06-23 19:28:42 +00:00
|
|
|
if (auto entry = Get<HistoryMessageLogEntryOriginal>()) {
|
|
|
|
logEntryOriginalResult = entry->_page->selectedText(mediaDisplayed ? _media->skipSelection(skipped) : skipped);
|
|
|
|
}
|
|
|
|
auto result = textResult;
|
|
|
|
if (result.text.isEmpty()) {
|
|
|
|
result = std::move(mediaResult);
|
|
|
|
} else if (!mediaResult.text.isEmpty()) {
|
|
|
|
result.text += qstr("\n\n");
|
2017-07-06 11:37:42 +00:00
|
|
|
TextUtilities::Append(result, std::move(mediaResult));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-06-23 19:28:42 +00:00
|
|
|
if (result.text.isEmpty()) {
|
|
|
|
result = std::move(logEntryOriginalResult);
|
|
|
|
} else if (!logEntryOriginalResult.text.isEmpty()) {
|
|
|
|
result.text += qstr("\n\n");
|
2017-07-06 11:37:42 +00:00
|
|
|
TextUtilities::Append(result, std::move(logEntryOriginalResult));
|
2017-06-23 19:28:42 +00:00
|
|
|
}
|
2017-07-18 16:48:10 +00:00
|
|
|
if (auto reply = Get<HistoryMessageReply>()) {
|
|
|
|
if (selection == FullSelection && reply->replyToMsg) {
|
|
|
|
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"));
|
|
|
|
TextUtilities::Append(wrapped, std::move(result));
|
|
|
|
result = wrapped;
|
|
|
|
}
|
|
|
|
}
|
2017-04-30 12:44:17 +00:00
|
|
|
if (auto forwarded = Get<HistoryMessageForwarded>()) {
|
2016-09-27 14:20:49 +00:00
|
|
|
if (selection == FullSelection) {
|
2017-04-30 12:44:17 +00:00
|
|
|
auto fwdinfo = forwarded->_text.originalTextWithEntities(AllTextSelection, ExpandLinksAll);
|
|
|
|
auto wrapped = TextWithEntities();
|
2016-09-27 14:20:49 +00:00
|
|
|
wrapped.text.reserve(fwdinfo.text.size() + 4 + result.text.size());
|
|
|
|
wrapped.entities.reserve(fwdinfo.entities.size() + result.entities.size());
|
|
|
|
wrapped.text.append('[');
|
2017-07-06 11:37:42 +00:00
|
|
|
TextUtilities::Append(wrapped, std::move(fwdinfo));
|
2016-09-27 14:20:49 +00:00
|
|
|
wrapped.text.append(qsl("]\n"));
|
2017-07-06 11:37:42 +00:00
|
|
|
TextUtilities::Append(wrapped, std::move(result));
|
2016-09-27 14:20:49 +00:00
|
|
|
result = wrapped;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::setMedia(const MTPMessageMedia *media) {
|
|
|
|
if (!_media && (!media || media->type() == mtpc_messageMediaEmpty)) return;
|
|
|
|
|
|
|
|
bool mediaRemovedSkipBlock = false;
|
|
|
|
if (_media) {
|
2016-09-29 16:15:44 +00:00
|
|
|
// Don't update Game media because we loose the consumed text of the message.
|
|
|
|
if (_media->type() == MediaTypeGame) return;
|
|
|
|
|
2016-09-28 11:38:35 +00:00
|
|
|
mediaRemovedSkipBlock = _media->isDisplayed() && _media->isBubbleBottom();
|
2017-03-05 13:39:10 +00:00
|
|
|
_media.reset();
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2016-09-28 11:38:35 +00:00
|
|
|
initMedia(media);
|
|
|
|
auto mediaDisplayed = _media && _media->isDisplayed();
|
|
|
|
if (mediaDisplayed && _media->isBubbleBottom() && !mediaRemovedSkipBlock) {
|
2016-09-27 14:20:49 +00:00
|
|
|
_text.removeSkipBlock();
|
|
|
|
_textWidth = -1;
|
|
|
|
_textHeight = 0;
|
2016-09-28 11:38:35 +00:00
|
|
|
} else if (mediaRemovedSkipBlock && (!mediaDisplayed || !_media->isBubbleBottom())) {
|
2016-09-27 14:20:49 +00:00
|
|
|
_text.setSkipBlock(skipBlockWidth(), skipBlockHeight());
|
|
|
|
_textWidth = -1;
|
|
|
|
_textHeight = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
|
|
|
|
for_const (auto &entity, textWithEntities.entities) {
|
|
|
|
auto type = entity.type();
|
|
|
|
if (type == EntityInTextUrl || type == EntityInTextCustomUrl || type == EntityInTextEmail) {
|
|
|
|
_flags |= MTPDmessage_ClientFlag::f_has_text_links;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-09-28 11:38:35 +00:00
|
|
|
|
|
|
|
auto mediaDisplayed = _media && _media->isDisplayed();
|
|
|
|
if (mediaDisplayed && _media->consumeMessageText(textWithEntities)) {
|
|
|
|
setEmptyText();
|
|
|
|
} else {
|
2017-06-23 19:28:42 +00:00
|
|
|
auto mediaOnBottom = (_media && _media->isDisplayed() && _media->isBubbleBottom()) || Has<HistoryMessageLogEntryOriginal>();
|
|
|
|
if (mediaOnBottom) {
|
2016-12-23 13:21:01 +00:00
|
|
|
_text.setMarkedText(st::messageTextStyle, textWithEntities, itemTextOptions(this));
|
2016-09-28 11:38:35 +00:00
|
|
|
} else {
|
2016-12-23 13:21:01 +00:00
|
|
|
_text.setMarkedText(st::messageTextStyle, { textWithEntities.text + skipBlock(), textWithEntities.entities }, itemTextOptions(this));
|
2016-09-28 11:38:35 +00:00
|
|
|
}
|
|
|
|
_textWidth = -1;
|
|
|
|
_textHeight = 0;
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::setEmptyText() {
|
2016-12-23 13:21:01 +00:00
|
|
|
_text.setMarkedText(st::messageTextStyle, { QString(), EntitiesInText() }, itemTextOptions(this));
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
_textWidth = -1;
|
|
|
|
_textHeight = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::setReplyMarkup(const MTPReplyMarkup *markup) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
bool changed = false;
|
|
|
|
if (Has<HistoryMessageReplyMarkup>()) {
|
|
|
|
RemoveComponents(HistoryMessageReplyMarkup::Bit());
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
if (!(_flags & MTPDmessage::Flag::f_reply_markup)) {
|
|
|
|
_flags |= MTPDmessage::Flag::f_reply_markup;
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
if (changed) {
|
|
|
|
setPendingInitDimensions();
|
|
|
|
|
|
|
|
Notify::replyMarkupUpdated(this);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!(_flags & MTPDmessage::Flag::f_reply_markup)) {
|
|
|
|
_flags |= MTPDmessage::Flag::f_reply_markup;
|
|
|
|
}
|
|
|
|
if (!Has<HistoryMessageReplyMarkup>()) {
|
|
|
|
AddComponents(HistoryMessageReplyMarkup::Bit());
|
|
|
|
}
|
|
|
|
Get<HistoryMessageReplyMarkup>()->create(*markup);
|
|
|
|
setPendingInitDimensions();
|
|
|
|
|
|
|
|
Notify::replyMarkupUpdated(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TextWithEntities HistoryMessage::originalText() const {
|
|
|
|
if (emptyText()) {
|
|
|
|
return { QString(), EntitiesInText() };
|
|
|
|
}
|
|
|
|
return _text.originalTextWithEntities();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryMessage::textHasLinks() const {
|
|
|
|
return emptyText() ? false : _text.hasLinks();
|
|
|
|
}
|
|
|
|
|
2016-09-30 12:52:03 +00:00
|
|
|
int HistoryMessage::infoWidth() const {
|
|
|
|
int result = _timeWidth;
|
|
|
|
if (auto views = Get<HistoryMessageViews>()) {
|
|
|
|
result += st::historyViewsSpace + views->_viewsWidth + st::historyViewsWidth;
|
|
|
|
} else if (id < 0 && history()->peer->isSelf()) {
|
|
|
|
result += st::historySendStateSpace;
|
|
|
|
}
|
|
|
|
if (out() && !isPost()) {
|
|
|
|
result += st::historySendStateSpace;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
int HistoryMessage::timeLeft() const {
|
|
|
|
int result = 0;
|
|
|
|
if (auto views = Get<HistoryMessageViews>()) {
|
|
|
|
result += st::historyViewsSpace + views->_viewsWidth + st::historyViewsWidth;
|
|
|
|
} else if (id < 0 && history()->peer->isSelf()) {
|
|
|
|
result += st::historySendStateSpace;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const {
|
|
|
|
p.setFont(st::msgDateFont);
|
|
|
|
|
|
|
|
bool outbg = out() && !isPost();
|
|
|
|
bool invertedsprites = (type == InfoDisplayOverImage || type == InfoDisplayOverBackground);
|
|
|
|
int32 infoRight = right, infoBottom = bottom;
|
|
|
|
switch (type) {
|
|
|
|
case InfoDisplayDefault:
|
2016-09-28 10:15:03 +00:00
|
|
|
infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
|
|
|
|
infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
|
|
|
|
p.setPen(selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
|
2016-09-27 14:20:49 +00:00
|
|
|
break;
|
|
|
|
case InfoDisplayOverImage:
|
2016-09-28 10:15:03 +00:00
|
|
|
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
|
|
|
|
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
|
2016-12-21 15:05:58 +00:00
|
|
|
p.setPen(st::msgDateImgFg);
|
2016-09-27 14:20:49 +00:00
|
|
|
break;
|
|
|
|
case InfoDisplayOverBackground:
|
2016-09-28 10:15:03 +00:00
|
|
|
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
|
|
|
|
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
|
2016-12-21 15:05:58 +00:00
|
|
|
p.setPen(st::msgServiceFg);
|
2016-09-27 14:20:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 infoW = HistoryMessage::infoWidth();
|
|
|
|
if (rtl()) infoRight = width - infoRight + infoW;
|
|
|
|
|
|
|
|
int32 dateX = infoRight - infoW;
|
|
|
|
int32 dateY = infoBottom - st::msgDateFont->height;
|
|
|
|
if (type == InfoDisplayOverImage) {
|
|
|
|
int32 dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y();
|
|
|
|
App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
|
|
|
|
} else if (type == InfoDisplayOverBackground) {
|
|
|
|
int32 dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y();
|
2016-12-21 15:05:58 +00:00
|
|
|
App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
dateX += HistoryMessage::timeLeft();
|
|
|
|
|
|
|
|
if (auto msgsigned = Get<HistoryMessageSigned>()) {
|
|
|
|
msgsigned->_signature.drawElided(p, dateX, dateY, _timeWidth);
|
|
|
|
} else if (auto edited = Get<HistoryMessageEdited>()) {
|
|
|
|
edited->_edited.drawElided(p, dateX, dateY, _timeWidth);
|
|
|
|
} else {
|
|
|
|
p.drawText(dateX, dateY + st::msgDateFont->ascent, _timeText);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto views = Get<HistoryMessageViews>()) {
|
2016-09-30 12:52:03 +00:00
|
|
|
auto icon = ([this, outbg, invertedsprites, selected] {
|
|
|
|
if (id > 0) {
|
|
|
|
if (outbg) {
|
|
|
|
return &(invertedsprites ? st::historyViewsInvertedIcon : (selected ? st::historyViewsOutSelectedIcon : st::historyViewsOutIcon));
|
|
|
|
}
|
|
|
|
return &(invertedsprites ? st::historyViewsInvertedIcon : (selected ? st::historyViewsInSelectedIcon : st::historyViewsInIcon));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2016-09-30 12:52:03 +00:00
|
|
|
return &(invertedsprites ? st::historyViewsSendingInvertedIcon : st::historyViewsSendingIcon);
|
|
|
|
})();
|
|
|
|
if (id > 0) {
|
|
|
|
icon->paint(p, infoRight - infoW, infoBottom + st::historyViewsTop, width);
|
|
|
|
p.drawText(infoRight - infoW + st::historyViewsWidth, infoBottom - st::msgDateFont->descent, views->_viewsText);
|
|
|
|
} else if (!outbg) { // sending outbg icon will be painted below
|
|
|
|
auto iconSkip = st::historyViewsSpace + views->_viewsWidth;
|
|
|
|
icon->paint(p, infoRight - infoW + iconSkip, infoBottom + st::historyViewsTop, width);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} else if (id < 0 && history()->peer->isSelf()) {
|
2016-09-30 12:52:03 +00:00
|
|
|
auto icon = &(invertedsprites ? st::historyViewsSendingInvertedIcon : st::historyViewsSendingIcon);
|
|
|
|
icon->paint(p, infoRight - infoW, infoBottom + st::historyViewsTop, width);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
if (outbg) {
|
2016-09-30 12:52:03 +00:00
|
|
|
auto icon = ([this, invertedsprites, selected] {
|
|
|
|
if (id > 0) {
|
|
|
|
if (unread()) {
|
|
|
|
return &(invertedsprites ? st::historySentInvertedIcon : (selected ? st::historySentSelectedIcon : st::historySentIcon));
|
|
|
|
}
|
|
|
|
return &(invertedsprites ? st::historyReceivedInvertedIcon : (selected ? st::historyReceivedSelectedIcon : st::historyReceivedIcon));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2016-09-30 12:52:03 +00:00
|
|
|
return &(invertedsprites ? st::historySendingInvertedIcon : st::historySendingIcon);
|
|
|
|
})();
|
|
|
|
icon->paint(p, QPoint(infoRight, infoBottom) + st::historySendStatePosition, width);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::setViewsCount(int32 count) {
|
|
|
|
auto views = Get<HistoryMessageViews>();
|
|
|
|
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) {
|
|
|
|
Ui::repaintHistoryItem(this);
|
|
|
|
} else {
|
|
|
|
if (_text.hasSkipBlock()) {
|
|
|
|
_text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight());
|
|
|
|
_textWidth = -1;
|
|
|
|
_textHeight = 0;
|
|
|
|
}
|
|
|
|
setPendingInitDimensions();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::setId(MsgId newId) {
|
|
|
|
bool wasPositive = (id > 0), positive = (newId > 0);
|
|
|
|
HistoryItem::setId(newId);
|
|
|
|
if (wasPositive == positive) {
|
|
|
|
Ui::repaintHistoryItem(this);
|
|
|
|
} else {
|
|
|
|
if (_text.hasSkipBlock()) {
|
|
|
|
_text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight());
|
|
|
|
_textWidth = -1;
|
|
|
|
_textHeight = 0;
|
|
|
|
}
|
|
|
|
setPendingInitDimensions();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
void HistoryMessage::draw(Painter &p, QRect clip, TextSelection selection, TimeMs ms) const {
|
2016-09-27 14:20:49 +00:00
|
|
|
bool outbg = out() && !isPost(), bubble = drawBubble(), selected = (selection == FullSelection);
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
auto g = countGeometry();
|
|
|
|
if (g.width() < 1) {
|
|
|
|
return;
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
auto dateh = 0;
|
2016-09-27 14:20:49 +00:00
|
|
|
if (auto date = Get<HistoryMessageDate>()) {
|
|
|
|
dateh = date->height();
|
|
|
|
}
|
|
|
|
if (auto unreadbar = Get<HistoryMessageUnreadBar>()) {
|
2017-06-21 08:53:14 +00:00
|
|
|
auto unreadbarh = unreadbar->height();
|
|
|
|
if (clip.intersects(QRect(0, dateh, width(), unreadbarh))) {
|
2016-09-27 14:20:49 +00:00
|
|
|
p.translate(0, dateh);
|
2017-06-21 08:53:14 +00:00
|
|
|
unreadbar->paint(p, 0, width());
|
2016-09-27 14:20:49 +00:00
|
|
|
p.translate(0, -dateh);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-01 19:20:33 +00:00
|
|
|
auto fullAnimMs = App::main() ? App::main()->animActiveTimeStart(this) : 0LL;
|
2016-09-27 14:20:49 +00:00
|
|
|
if (fullAnimMs > 0 && fullAnimMs <= ms) {
|
|
|
|
int animms = ms - fullAnimMs;
|
|
|
|
if (animms > st::activeFadeInDuration + st::activeFadeOutDuration) {
|
|
|
|
App::main()->stopAnimActive();
|
|
|
|
} else {
|
2017-02-18 11:28:50 +00:00
|
|
|
auto top = marginTop();
|
|
|
|
auto bottom = marginBottom();
|
|
|
|
auto fill = qMin(top, bottom);
|
|
|
|
auto skiptop = top - fill;
|
2017-06-21 08:53:14 +00:00
|
|
|
auto fillheight = fill + g.height() + fill;
|
2017-02-18 11:28:50 +00:00
|
|
|
|
|
|
|
auto dt = (animms > st::activeFadeInDuration) ? (1. - (animms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (animms / float64(st::activeFadeInDuration));
|
|
|
|
auto o = p.opacity();
|
2016-09-27 14:20:49 +00:00
|
|
|
p.setOpacity(o * dt);
|
2017-06-21 08:53:14 +00:00
|
|
|
p.fillRect(0, skiptop, width(), fillheight, st::defaultTextPalette.selectOverlay);
|
2016-09-27 14:20:49 +00:00
|
|
|
p.setOpacity(o);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-09 13:46:14 +00:00
|
|
|
p.setTextPalette(selected ? (outbg ? st::outTextPaletteSelected : st::inTextPaletteSelected) : (outbg ? st::outTextPalette : st::inTextPalette));
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-01-16 13:27:11 +00:00
|
|
|
auto keyboard = inlineReplyKeyboard();
|
|
|
|
if (keyboard) {
|
2017-06-21 08:53:14 +00:00
|
|
|
auto keyboardHeight = st::msgBotKbButton.margin + keyboard->naturalHeight();
|
|
|
|
g.setHeight(g.height() - keyboardHeight);
|
|
|
|
auto keyboardPosition = QPoint(g.left(), g.top() + g.height() + st::msgBotKbButton.margin);
|
|
|
|
p.translate(keyboardPosition);
|
|
|
|
keyboard->paint(p, g.width(), clip.translated(-keyboardPosition), ms);
|
|
|
|
p.translate(-keyboardPosition);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bubble) {
|
|
|
|
if (displayFromName() && author()->nameVersion > _authorNameVersion) {
|
2017-06-21 08:53:14 +00:00
|
|
|
fromNameUpdated(g.width());
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-20 19:48:53 +00:00
|
|
|
auto entry = Get<HistoryMessageLogEntryOriginal>();
|
2016-09-28 10:15:03 +00:00
|
|
|
auto mediaDisplayed = _media && _media->isDisplayed();
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-01-16 13:27:11 +00:00
|
|
|
auto skipTail = isAttachedToNext() || (_media && _media->skipBubbleTail()) || (keyboard != nullptr);
|
2017-05-24 12:20:50 +00:00
|
|
|
auto displayTail = skipTail ? RectPart::None : (outbg && !Adaptive::ChatWide()) ? RectPart::Right : RectPart::Left;
|
2017-06-21 08:53:14 +00:00
|
|
|
HistoryLayout::paintBubble(p, g, width(), selected, outbg, displayTail);
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-06-22 15:11:41 +00:00
|
|
|
// Entry page is always a bubble bottom.
|
|
|
|
auto mediaOnBottom = (mediaDisplayed && _media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/);
|
|
|
|
auto mediaOnTop = (mediaDisplayed && _media->isBubbleTop()) || (entry && entry->_page->isBubbleTop());
|
|
|
|
|
|
|
|
auto trect = g.marginsRemoved(st::msgPadding);
|
|
|
|
if (mediaOnBottom) {
|
|
|
|
trect.setHeight(trect.height() + st::msgPadding.bottom());
|
|
|
|
}
|
|
|
|
if (mediaOnTop) {
|
2016-09-28 10:15:03 +00:00
|
|
|
trect.setY(trect.y() - st::msgPadding.top());
|
|
|
|
} else {
|
|
|
|
paintFromName(p, trect, selected);
|
|
|
|
paintForwardedInfo(p, trect, selected);
|
|
|
|
paintReplyInfo(p, trect, selected);
|
|
|
|
paintViaBotIdInfo(p, trect, selected);
|
|
|
|
}
|
2017-06-20 19:48:53 +00:00
|
|
|
if (entry) {
|
|
|
|
trect.setHeight(trect.height() - entry->_page->height());
|
|
|
|
}
|
2017-06-22 15:11:41 +00:00
|
|
|
auto needDrawInfo = mediaOnBottom ? !(entry ? entry->_page->customInfoLayout() : _media->customInfoLayout()) : true;
|
2016-09-28 10:15:03 +00:00
|
|
|
if (mediaDisplayed) {
|
2016-09-27 14:20:49 +00:00
|
|
|
auto mediaAboveText = _media->isAboveMessage();
|
|
|
|
auto mediaHeight = _media->height();
|
2017-06-21 08:53:14 +00:00
|
|
|
auto mediaLeft = g.left();
|
2016-09-28 10:15:03 +00:00
|
|
|
auto mediaTop = mediaAboveText ? trect.y() : (trect.y() + trect.height() - mediaHeight);
|
2016-09-27 14:20:49 +00:00
|
|
|
if (!mediaAboveText) {
|
|
|
|
paintText(p, trect, selection);
|
|
|
|
}
|
|
|
|
p.translate(mediaLeft, mediaTop);
|
2017-06-23 19:28:42 +00:00
|
|
|
_media->draw(p, clip.translated(-mediaLeft, -mediaTop), skipTextSelection(selection), ms);
|
2016-09-27 14:20:49 +00:00
|
|
|
p.translate(-mediaLeft, -mediaTop);
|
|
|
|
|
|
|
|
if (mediaAboveText) {
|
|
|
|
trect.setY(trect.y() + mediaHeight);
|
|
|
|
paintText(p, trect, selection);
|
2017-06-22 15:11:41 +00:00
|
|
|
} else {
|
|
|
|
needDrawInfo = !_media->customInfoLayout();
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
paintText(p, trect, selection);
|
|
|
|
}
|
2017-06-20 19:48:53 +00:00
|
|
|
if (entry) {
|
2017-06-21 08:53:14 +00:00
|
|
|
auto entryLeft = g.left();
|
2017-06-20 19:48:53 +00:00
|
|
|
auto entryTop = trect.y() + trect.height();
|
|
|
|
p.translate(entryLeft, entryTop);
|
2017-06-23 19:28:42 +00:00
|
|
|
auto entrySelection = skipTextSelection(selection);
|
|
|
|
if (mediaDisplayed) {
|
|
|
|
entrySelection = _media->skipSelection(entrySelection);
|
|
|
|
}
|
|
|
|
entry->_page->draw(p, clip.translated(-entryLeft, -entryTop), entrySelection, ms);
|
2017-06-20 19:48:53 +00:00
|
|
|
p.translate(-entryLeft, -entryTop);
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
if (needDrawInfo) {
|
2017-06-21 08:53:14 +00:00
|
|
|
HistoryMessage::drawInfo(p, g.left() + g.width(), g.top() + g.height(), 2 * g.left() + g.width(), selected, InfoDisplayDefault);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-07-11 17:11:06 +00:00
|
|
|
if (displayFastShare()) {
|
|
|
|
auto fastShareLeft = g.left() + g.width() + st::historyFastShareLeft;
|
|
|
|
auto fastShareTop = g.top() + g.height() - st::historyFastShareBottom - st::historyFastShareSize;
|
|
|
|
drawFastShare(p, fastShareLeft, fastShareTop, width());
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
} else if (_media) {
|
2017-06-21 08:53:14 +00:00
|
|
|
p.translate(g.topLeft());
|
2017-06-23 19:28:42 +00:00
|
|
|
_media->draw(p, clip.translated(-g.topLeft()), skipTextSelection(selection), ms);
|
2017-06-21 08:53:14 +00:00
|
|
|
p.translate(-g.topLeft());
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2016-12-23 13:21:01 +00:00
|
|
|
p.restoreTextPalette();
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
auto reply = Get<HistoryMessageReply>();
|
|
|
|
if (reply && reply->isNameUpdated()) {
|
|
|
|
const_cast<HistoryMessage*>(this)->setPendingInitDimensions();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-11 17:11:06 +00:00
|
|
|
void HistoryMessage::drawFastShare(Painter &p, int left, int top, int outerWidth) const {
|
|
|
|
{
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
p.setBrush(st::msgServiceBg);
|
|
|
|
|
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
p.drawEllipse(rtlrect(left, top, st::historyFastShareSize, st::historyFastShareSize, outerWidth));
|
|
|
|
}
|
|
|
|
st::historyFastShareIcon.paint(p, left, top, outerWidth);
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
void HistoryMessage::paintFromName(Painter &p, QRect &trect, bool selected) const {
|
|
|
|
if (displayFromName()) {
|
|
|
|
p.setFont(st::msgNameFont);
|
|
|
|
if (isPost()) {
|
|
|
|
p.setPen(selected ? st::msgInServiceFgSelected : st::msgInServiceFg);
|
|
|
|
} else {
|
2017-02-09 13:46:14 +00:00
|
|
|
p.setPen(selected ? fromNameFgSelected(author()->colorIndex()) : fromNameFg(author()->colorIndex()));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
author()->nameText.drawElided(p, trect.left(), trect.top(), trect.width());
|
|
|
|
|
2017-04-30 12:44:17 +00:00
|
|
|
auto forwarded = Get<HistoryMessageForwarded>();
|
2016-09-27 14:20:49 +00:00
|
|
|
auto via = Get<HistoryMessageVia>();
|
2017-04-30 12:44:17 +00:00
|
|
|
if (via && !forwarded && trect.width() > author()->nameText.maxWidth() + st::msgServiceFont->spacew) {
|
2016-09-27 14:20:49 +00:00
|
|
|
bool outbg = out() && !isPost();
|
|
|
|
p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
|
|
|
|
p.drawText(trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew, trect.top() + st::msgServiceFont->ascent, via->_text);
|
|
|
|
}
|
|
|
|
trect.setY(trect.y() + st::msgNameFont->height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::paintForwardedInfo(Painter &p, QRect &trect, bool selected) const {
|
|
|
|
if (displayForwardedFrom()) {
|
|
|
|
style::font serviceFont(st::msgServiceFont), serviceName(st::msgServiceNameFont);
|
|
|
|
|
2017-02-09 13:46:14 +00:00
|
|
|
auto outbg = hasOutLayout();
|
|
|
|
p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
|
2016-09-27 14:20:49 +00:00
|
|
|
p.setFont(serviceFont);
|
|
|
|
|
2017-04-30 12:44:17 +00:00
|
|
|
auto forwarded = Get<HistoryMessageForwarded>();
|
|
|
|
auto breakEverywhere = (forwarded->_text.countHeight(trect.width()) > 2 * serviceFont->height);
|
2017-02-09 13:46:14 +00:00
|
|
|
p.setTextPalette(selected ? (outbg ? st::outFwdTextPaletteSelected : st::inFwdTextPaletteSelected) : (outbg ? st::outFwdTextPalette : st::inFwdTextPalette));
|
2017-04-30 12:44:17 +00:00
|
|
|
forwarded->_text.drawElided(p, trect.x(), trect.y(), trect.width(), 2, style::al_left, 0, -1, 0, breakEverywhere);
|
2017-02-09 13:46:14 +00:00
|
|
|
p.setTextPalette(selected ? (outbg ? st::outTextPaletteSelected : st::inTextPaletteSelected) : (outbg ? st::outTextPalette : st::inTextPalette));
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-04-30 12:44:17 +00:00
|
|
|
trect.setY(trect.y() + (((forwarded->_text.maxWidth() > trect.width()) ? 2 : 1) * serviceFont->height));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::paintReplyInfo(Painter &p, QRect &trect, bool selected) const {
|
|
|
|
if (auto reply = Get<HistoryMessageReply>()) {
|
|
|
|
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>()) {
|
|
|
|
if (auto via = Get<HistoryMessageVia>()) {
|
|
|
|
p.setFont(st::msgServiceNameFont);
|
|
|
|
p.setPen(selected ? (hasOutLayout() ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (hasOutLayout() ? st::msgOutServiceFg : st::msgInServiceFg));
|
2017-06-21 08:53:14 +00:00
|
|
|
p.drawTextLeft(trect.left(), trect.top(), width(), via->_text);
|
2016-09-27 14:20:49 +00:00
|
|
|
trect.setY(trect.y() + st::msgServiceNameFont->height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::paintText(Painter &p, QRect &trect, TextSelection selection) const {
|
2017-02-09 13:46:14 +00:00
|
|
|
auto outbg = out() && !isPost();
|
|
|
|
auto selected = (selection == FullSelection);
|
|
|
|
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
|
2016-09-27 14:20:49 +00:00
|
|
|
p.setFont(st::msgFont);
|
|
|
|
_text.draw(p, trect.x(), trect.y(), trect.width(), style::al_left, 0, -1, selection);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::dependencyItemRemoved(HistoryItem *dependency) {
|
|
|
|
if (auto reply = Get<HistoryMessageReply>()) {
|
|
|
|
reply->itemRemoved(this, dependency);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
int HistoryMessage::resizeContentGetHeight() {
|
|
|
|
int result = performResizeGetHeight();
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
int HistoryMessage::performResizeGetHeight() {
|
|
|
|
if (width() < st::msgMinWidth) return _height;
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
auto contentWidth = width() - (st::msgMargin.left() + st::msgMargin.right());
|
|
|
|
if (contentWidth < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
|
|
|
contentWidth = st::msgPadding.left() + st::msgPadding.right() + 1;
|
|
|
|
} else if (contentWidth > st::msgMaxWidth) {
|
|
|
|
contentWidth = st::msgMaxWidth;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
if (drawBubble()) {
|
2017-04-30 12:44:17 +00:00
|
|
|
auto forwarded = Get<HistoryMessageForwarded>();
|
2016-09-27 14:20:49 +00:00
|
|
|
auto reply = Get<HistoryMessageReply>();
|
|
|
|
auto via = Get<HistoryMessageVia>();
|
2017-06-20 19:48:53 +00:00
|
|
|
auto entry = Get<HistoryMessageLogEntryOriginal>();
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2016-09-28 10:15:03 +00:00
|
|
|
auto mediaDisplayed = false;
|
|
|
|
if (_media) {
|
|
|
|
mediaDisplayed = _media->isDisplayed();
|
|
|
|
}
|
2017-06-22 15:11:41 +00:00
|
|
|
|
|
|
|
// Entry page is always a bubble bottom.
|
|
|
|
auto mediaOnBottom = (mediaDisplayed && _media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/);
|
|
|
|
auto mediaOnTop = (mediaDisplayed && _media->isBubbleTop()) || (entry && entry->_page->isBubbleTop());
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
if (contentWidth >= _maxw) {
|
2016-09-27 14:20:49 +00:00
|
|
|
_height = _minh;
|
2017-06-20 19:48:53 +00:00
|
|
|
if (mediaDisplayed) {
|
|
|
|
_media->resizeGetHeight(_maxw);
|
2017-07-23 09:31:44 +00:00
|
|
|
if (entry) {
|
|
|
|
_height += entry->_page->resizeGetHeight(countGeometry().width());
|
|
|
|
}
|
|
|
|
} else if (entry) {
|
|
|
|
// In case of text-only message it is counted in _minh already.
|
|
|
|
entry->_page->resizeGetHeight(countGeometry().width());
|
2017-06-20 19:48:53 +00:00
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
} else {
|
|
|
|
if (emptyText()) {
|
|
|
|
_height = 0;
|
|
|
|
} else {
|
2017-06-21 08:53:14 +00:00
|
|
|
auto textWidth = qMax(contentWidth - st::msgPadding.left() - st::msgPadding.right(), 1);
|
2016-09-27 14:20:49 +00:00
|
|
|
if (textWidth != _textWidth) {
|
|
|
|
_textWidth = textWidth;
|
|
|
|
_textHeight = _text.countHeight(textWidth);
|
|
|
|
}
|
2016-09-28 10:15:03 +00:00
|
|
|
_height = _textHeight;
|
|
|
|
}
|
2017-06-22 15:11:41 +00:00
|
|
|
if (!mediaOnBottom) {
|
|
|
|
_height += st::msgPadding.bottom();
|
|
|
|
if (mediaDisplayed) _height += st::mediaInBubbleSkip;
|
|
|
|
}
|
|
|
|
if (!mediaOnTop) {
|
|
|
|
_height += st::msgPadding.top();
|
|
|
|
if (mediaDisplayed) _height += st::mediaInBubbleSkip;
|
|
|
|
if (entry) _height += st::mediaInBubbleSkip;
|
|
|
|
}
|
2016-09-28 10:15:03 +00:00
|
|
|
if (mediaDisplayed) {
|
2017-06-21 08:53:14 +00:00
|
|
|
_height += _media->resizeGetHeight(contentWidth);
|
2017-07-18 18:59:53 +00:00
|
|
|
if (entry) {
|
|
|
|
_height += entry->_page->resizeGetHeight(countGeometry().width());
|
|
|
|
}
|
|
|
|
} else if (entry) {
|
2017-06-21 08:53:14 +00:00
|
|
|
_height += entry->_page->resizeGetHeight(contentWidth);
|
2017-06-20 19:48:53 +00:00
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (displayFromName()) {
|
2017-07-18 18:59:53 +00:00
|
|
|
fromNameUpdated(countGeometry().width());
|
2016-09-27 14:20:49 +00:00
|
|
|
_height += st::msgNameFont->height;
|
2017-04-30 12:44:17 +00:00
|
|
|
} else if (via && !forwarded) {
|
2017-07-18 18:59:53 +00:00
|
|
|
via->resize(countGeometry().width() - st::msgPadding.left() - st::msgPadding.right());
|
2016-09-27 14:20:49 +00:00
|
|
|
_height += st::msgNameFont->height;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (displayForwardedFrom()) {
|
2017-07-18 18:59:53 +00:00
|
|
|
auto fwdheight = ((forwarded->_text.maxWidth() > (countGeometry().width() - st::msgPadding.left() - st::msgPadding.right())) ? 2 : 1) * st::semiboldFont->height;
|
2016-09-27 14:20:49 +00:00
|
|
|
_height += fwdheight;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reply) {
|
2017-07-18 18:59:53 +00:00
|
|
|
reply->resize(countGeometry().width() - st::msgPadding.left() - st::msgPadding.right());
|
2016-09-27 14:20:49 +00:00
|
|
|
_height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
|
|
|
|
}
|
|
|
|
} else if (_media) {
|
2017-06-21 08:53:14 +00:00
|
|
|
_height = _media->resizeGetHeight(contentWidth);
|
2016-09-27 14:20:49 +00:00
|
|
|
} else {
|
|
|
|
_height = 0;
|
|
|
|
}
|
|
|
|
if (auto keyboard = inlineReplyKeyboard()) {
|
2017-06-21 08:53:14 +00:00
|
|
|
auto g = countGeometry();
|
|
|
|
auto keyboardHeight = st::msgBotKbButton.margin + keyboard->naturalHeight();
|
|
|
|
_height += keyboardHeight;
|
|
|
|
keyboard->resize(g.width(), keyboardHeight - st::msgBotKbButton.margin);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_height += marginTop() + marginBottom();
|
|
|
|
return _height;
|
|
|
|
}
|
|
|
|
|
2017-06-21 21:38:31 +00:00
|
|
|
bool HistoryMessage::hasPoint(QPoint point) const {
|
2017-06-21 08:53:14 +00:00
|
|
|
auto g = countGeometry();
|
|
|
|
if (g.width() < 1) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
if (drawBubble()) {
|
2017-06-21 21:38:31 +00:00
|
|
|
return g.contains(point);
|
2016-09-27 14:20:49 +00:00
|
|
|
} else if (_media) {
|
2017-06-21 21:38:31 +00:00
|
|
|
return _media->hasPoint(point - g.topLeft());
|
2016-09-27 14:20:49 +00:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-21 21:38:31 +00:00
|
|
|
bool HistoryMessage::pointInTime(int right, int bottom, QPoint point, InfoDisplayType type) const {
|
|
|
|
auto infoRight = right;
|
|
|
|
auto infoBottom = bottom;
|
2016-09-27 14:20:49 +00:00
|
|
|
switch (type) {
|
|
|
|
case InfoDisplayDefault:
|
2016-09-28 10:15:03 +00:00
|
|
|
infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
|
|
|
|
infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
|
2016-09-27 14:20:49 +00:00
|
|
|
break;
|
|
|
|
case InfoDisplayOverImage:
|
2016-09-28 10:15:03 +00:00
|
|
|
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
|
|
|
|
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
|
2016-09-27 14:20:49 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-06-21 21:38:31 +00:00
|
|
|
auto dateX = infoRight - HistoryMessage::infoWidth() + HistoryMessage::timeLeft();
|
|
|
|
auto dateY = infoBottom - st::msgDateFont->height;
|
|
|
|
return QRect(dateX, dateY, HistoryMessage::timeWidth(), st::msgDateFont->height).contains(point);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-21 21:38:31 +00:00
|
|
|
HistoryTextState HistoryMessage::getState(QPoint point, HistoryStateRequest request) const {
|
2016-09-27 14:20:49 +00:00
|
|
|
HistoryTextState result;
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
auto g = countGeometry();
|
|
|
|
if (g.width() < 1) {
|
|
|
|
return result;
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
auto keyboard = inlineReplyKeyboard();
|
2017-06-21 08:53:14 +00:00
|
|
|
auto keyboardHeight = 0;
|
2016-09-27 14:20:49 +00:00
|
|
|
if (keyboard) {
|
2017-06-21 08:53:14 +00:00
|
|
|
keyboardHeight = keyboard->naturalHeight();
|
|
|
|
g.setHeight(g.height() - st::msgBotKbButton.margin - keyboardHeight);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (drawBubble()) {
|
2017-06-23 10:54:18 +00:00
|
|
|
auto entry = Get<HistoryMessageLogEntryOriginal>();
|
2016-09-28 10:15:03 +00:00
|
|
|
auto mediaDisplayed = _media && _media->isDisplayed();
|
2017-06-23 10:54:18 +00:00
|
|
|
|
|
|
|
// Entry page is always a bubble bottom.
|
|
|
|
auto mediaOnBottom = (mediaDisplayed && _media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/);
|
|
|
|
auto mediaOnTop = (mediaDisplayed && _media->isBubbleTop()) || (entry && entry->_page->isBubbleTop());
|
|
|
|
|
|
|
|
auto trect = g.marginsRemoved(st::msgPadding);
|
|
|
|
if (mediaOnBottom) {
|
|
|
|
trect.setHeight(trect.height() + st::msgPadding.bottom());
|
|
|
|
}
|
|
|
|
if (mediaOnTop) {
|
2016-09-28 10:15:03 +00:00
|
|
|
trect.setY(trect.y() - st::msgPadding.top());
|
|
|
|
} else {
|
2017-06-21 21:38:31 +00:00
|
|
|
if (getStateFromName(point, trect, &result)) return result;
|
|
|
|
if (getStateForwardedInfo(point, trect, &result, request)) return result;
|
|
|
|
if (getStateReplyInfo(point, trect, &result)) return result;
|
|
|
|
if (getStateViaBotIdInfo(point, trect, &result)) return result;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-06-23 10:54:18 +00:00
|
|
|
if (entry) {
|
|
|
|
auto entryHeight = entry->_page->height();
|
|
|
|
trect.setHeight(trect.height() - entryHeight);
|
|
|
|
auto entryLeft = g.left();
|
|
|
|
auto entryTop = trect.y() + trect.height();
|
|
|
|
if (point.y() >= entryTop && point.y() < entryTop + entryHeight) {
|
|
|
|
result = entry->_page->getState(point - QPoint(entryLeft, entryTop), request);
|
2017-06-23 19:28:42 +00:00
|
|
|
result.symbol += _text.length() + (mediaDisplayed ? _media->fullSelectionLength() : 0);
|
2017-06-23 10:54:18 +00:00
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-23 10:54:18 +00:00
|
|
|
auto needDateCheck = mediaOnBottom ? !(entry ? entry->_page->customInfoLayout() : _media->customInfoLayout()) : true;
|
2016-09-27 14:20:49 +00:00
|
|
|
if (mediaDisplayed) {
|
2016-09-28 10:15:03 +00:00
|
|
|
auto mediaAboveText = _media->isAboveMessage();
|
|
|
|
auto mediaHeight = _media->height();
|
|
|
|
auto mediaLeft = trect.x() - st::msgPadding.left();
|
|
|
|
auto mediaTop = mediaAboveText ? trect.y() : (trect.y() + trect.height() - mediaHeight);
|
|
|
|
|
2017-06-21 21:38:31 +00:00
|
|
|
if (point.y() >= mediaTop && point.y() < mediaTop + mediaHeight) {
|
|
|
|
result = _media->getState(point - QPoint(mediaLeft, mediaTop), request);
|
2016-09-27 14:20:49 +00:00
|
|
|
result.symbol += _text.length();
|
2016-09-28 10:15:03 +00:00
|
|
|
} else {
|
|
|
|
if (mediaAboveText) {
|
|
|
|
trect.setY(trect.y() + mediaHeight);
|
|
|
|
}
|
2017-06-23 10:54:18 +00:00
|
|
|
if (trect.contains(point)) {
|
|
|
|
getStateText(point, trect, &result, request);
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-06-23 10:54:18 +00:00
|
|
|
} else if (trect.contains(point)) {
|
2017-06-21 21:38:31 +00:00
|
|
|
getStateText(point, trect, &result, request);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2016-09-28 10:15:03 +00:00
|
|
|
if (needDateCheck) {
|
2017-06-21 21:38:31 +00:00
|
|
|
if (HistoryMessage::pointInTime(g.left() + g.width(), g.top() + g.height(), point, InfoDisplayDefault)) {
|
2016-09-28 10:15:03 +00:00
|
|
|
result.cursor = HistoryInDateCursorState;
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-07-11 17:11:06 +00:00
|
|
|
if (displayFastShare()) {
|
|
|
|
auto fastShareLeft = g.left() + g.width() + st::historyFastShareLeft;
|
|
|
|
auto fastShareTop = g.top() + g.height() - st::historyFastShareBottom - st::historyFastShareSize;
|
|
|
|
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
|
|
|
|
result.link = fastShareLink();
|
|
|
|
}
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
} else if (_media) {
|
2017-06-21 21:38:31 +00:00
|
|
|
result = _media->getState(point - g.topLeft(), request);
|
2016-09-27 14:20:49 +00:00
|
|
|
result.symbol += _text.length();
|
|
|
|
}
|
|
|
|
|
2017-06-24 10:32:30 +00:00
|
|
|
if (keyboard && !isLogEntry()) {
|
2017-06-21 08:53:14 +00:00
|
|
|
auto keyboardTop = g.top() + g.height() + st::msgBotKbButton.margin;
|
2017-06-21 21:38:31 +00:00
|
|
|
if (QRect(g.left(), keyboardTop, g.width(), keyboardHeight).contains(point)) {
|
2017-06-24 10:32:30 +00:00
|
|
|
result.link = keyboard->getState(point - QPoint(g.left(), keyboardTop));
|
2016-09-27 14:20:49 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-07-11 17:11:06 +00:00
|
|
|
ClickHandlerPtr HistoryMessage::fastShareLink() const {
|
|
|
|
if (!_fastShareLink) {
|
|
|
|
_fastShareLink = MakeShared<LambdaClickHandler>([id = fullId()] {
|
|
|
|
if (auto item = App::histItemById(id)) {
|
|
|
|
FastShareMessage(item->toHistoryMessage());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return _fastShareLink;
|
|
|
|
}
|
|
|
|
|
2017-02-10 22:37:37 +00:00
|
|
|
// Forward to _media.
|
2017-06-21 21:38:31 +00:00
|
|
|
void HistoryMessage::updatePressed(QPoint point) {
|
2017-02-10 22:37:37 +00:00
|
|
|
if (!_media) return;
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
auto g = countGeometry();
|
2017-02-10 22:37:37 +00:00
|
|
|
auto keyboard = inlineReplyKeyboard();
|
|
|
|
if (keyboard) {
|
2017-06-21 08:53:14 +00:00
|
|
|
auto keyboardHeight = st::msgBotKbButton.margin + keyboard->naturalHeight();
|
|
|
|
g.setHeight(g.height() - keyboardHeight);
|
2017-02-10 22:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (drawBubble()) {
|
|
|
|
auto mediaDisplayed = _media && _media->isDisplayed();
|
|
|
|
auto top = marginTop();
|
2017-06-21 08:53:14 +00:00
|
|
|
auto trect = g.marginsAdded(-st::msgPadding);
|
2017-02-10 22:37:37 +00:00
|
|
|
if (mediaDisplayed && _media->isBubbleTop()) {
|
|
|
|
trect.setY(trect.y() - st::msgPadding.top());
|
|
|
|
} else {
|
|
|
|
if (displayFromName()) trect.setTop(trect.top() + st::msgNameFont->height);
|
|
|
|
if (displayForwardedFrom()) {
|
2017-04-30 12:44:17 +00:00
|
|
|
auto forwarded = Get<HistoryMessageForwarded>();
|
|
|
|
auto fwdheight = ((forwarded->_text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height;
|
2017-02-10 22:37:37 +00:00
|
|
|
trect.setTop(trect.top() + fwdheight);
|
|
|
|
}
|
|
|
|
if (Get<HistoryMessageReply>()) {
|
|
|
|
auto h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
|
|
|
|
trect.setTop(trect.top() + h);
|
|
|
|
}
|
|
|
|
if (!displayFromName() && !Has<HistoryMessageForwarded>()) {
|
|
|
|
if (auto via = Get<HistoryMessageVia>()) {
|
|
|
|
trect.setTop(trect.top() + st::msgNameFont->height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mediaDisplayed && _media->isBubbleBottom()) {
|
|
|
|
trect.setHeight(trect.height() + st::msgPadding.bottom());
|
|
|
|
}
|
|
|
|
|
|
|
|
auto needDateCheck = true;
|
|
|
|
if (mediaDisplayed) {
|
|
|
|
auto mediaAboveText = _media->isAboveMessage();
|
|
|
|
auto mediaHeight = _media->height();
|
|
|
|
auto mediaLeft = trect.x() - st::msgPadding.left();
|
|
|
|
auto mediaTop = mediaAboveText ? trect.y() : (trect.y() + trect.height() - mediaHeight);
|
2017-06-21 21:38:31 +00:00
|
|
|
_media->updatePressed(point - QPoint(mediaLeft, mediaTop));
|
2017-02-10 22:37:37 +00:00
|
|
|
}
|
|
|
|
} else {
|
2017-06-21 21:38:31 +00:00
|
|
|
_media->updatePressed(point - g.topLeft());
|
2017-02-10 22:37:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-21 21:38:31 +00:00
|
|
|
bool HistoryMessage::getStateFromName(QPoint point, QRect &trect, HistoryTextState *outResult) const {
|
2016-09-28 10:15:03 +00:00
|
|
|
if (displayFromName()) {
|
2017-06-21 21:38:31 +00:00
|
|
|
if (point.y() >= trect.top() && point.y() < trect.top() + st::msgNameFont->height) {
|
|
|
|
if (point.x() >= trect.left() && point.x() < trect.left() + trect.width() && point.x() < trect.left() + author()->nameText.maxWidth()) {
|
2016-09-28 10:15:03 +00:00
|
|
|
outResult->link = author()->openLink();
|
|
|
|
return true;
|
|
|
|
}
|
2017-04-30 12:44:17 +00:00
|
|
|
auto forwarded = Get<HistoryMessageForwarded>();
|
2016-09-28 10:15:03 +00:00
|
|
|
auto via = Get<HistoryMessageVia>();
|
2017-06-21 21:38:31 +00:00
|
|
|
if (via && !forwarded && point.x() >= trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew && point.x() < trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew + via->_width) {
|
2016-09-28 10:15:03 +00:00
|
|
|
outResult->link = via->_lnk;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
trect.setTop(trect.top() + st::msgNameFont->height);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-21 21:38:31 +00:00
|
|
|
bool HistoryMessage::getStateForwardedInfo(QPoint point, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const {
|
2016-09-28 10:15:03 +00:00
|
|
|
if (displayForwardedFrom()) {
|
2017-04-30 12:44:17 +00:00
|
|
|
auto forwarded = Get<HistoryMessageForwarded>();
|
|
|
|
auto fwdheight = ((forwarded->_text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height;
|
2017-06-21 21:38:31 +00:00
|
|
|
if (point.y() >= trect.top() && point.y() < trect.top() + fwdheight) {
|
2017-04-30 12:44:17 +00:00
|
|
|
auto breakEverywhere = (forwarded->_text.countHeight(trect.width()) > 2 * st::semiboldFont->height);
|
2016-09-28 10:15:03 +00:00
|
|
|
auto textRequest = request.forText();
|
|
|
|
if (breakEverywhere) {
|
|
|
|
textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
|
|
|
|
}
|
2017-06-21 21:38:31 +00:00
|
|
|
*outResult = forwarded->_text.getState(point - trect.topLeft(), trect.width(), textRequest);
|
2016-09-28 10:15:03 +00:00
|
|
|
outResult->symbol = 0;
|
|
|
|
outResult->afterSymbol = false;
|
|
|
|
if (breakEverywhere) {
|
|
|
|
outResult->cursor = HistoryInForwardedCursorState;
|
|
|
|
} else {
|
|
|
|
outResult->cursor = HistoryDefaultCursorState;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
trect.setTop(trect.top() + fwdheight);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-21 21:38:31 +00:00
|
|
|
bool HistoryMessage::getStateReplyInfo(QPoint point, QRect &trect, HistoryTextState *outResult) const {
|
2016-09-28 10:15:03 +00:00
|
|
|
if (auto reply = Get<HistoryMessageReply>()) {
|
|
|
|
int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
|
2017-06-21 21:38:31 +00:00
|
|
|
if (point.y() >= trect.top() && point.y() < trect.top() + h) {
|
|
|
|
if (reply->replyToMsg && QRect(trect.x(), trect.y() + st::msgReplyPadding.top(), trect.width(), st::msgReplyBarSize.height()).contains(point)) {
|
2016-09-28 10:15:03 +00:00
|
|
|
outResult->link = reply->replyToLink();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
trect.setTop(trect.top() + h);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-21 21:38:31 +00:00
|
|
|
bool HistoryMessage::getStateViaBotIdInfo(QPoint point, QRect &trect, HistoryTextState *outResult) const {
|
2016-09-28 10:15:03 +00:00
|
|
|
if (!displayFromName() && !Has<HistoryMessageForwarded>()) {
|
|
|
|
if (auto via = Get<HistoryMessageVia>()) {
|
2017-06-21 21:38:31 +00:00
|
|
|
if (QRect(trect.x(), trect.y(), via->_width, st::msgNameFont->height).contains(point)) {
|
2016-09-28 10:15:03 +00:00
|
|
|
outResult->link = via->_lnk;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
trect.setTop(trect.top() + st::msgNameFont->height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-21 21:38:31 +00:00
|
|
|
bool HistoryMessage::getStateText(QPoint point, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const {
|
|
|
|
if (trect.contains(point)) {
|
|
|
|
*outResult = _text.getState(point - trect.topLeft(), trect.width(), request.forText());
|
2016-09-28 10:15:03 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
TextSelection HistoryMessage::adjustSelection(TextSelection selection, TextSelectType type) const {
|
2017-06-23 19:28:42 +00:00
|
|
|
auto result = _text.adjustSelection(selection, type);
|
|
|
|
auto beforeMediaLength = _text.length();
|
|
|
|
if (selection.to <= beforeMediaLength) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
auto mediaDisplayed = _media && _media->isDisplayed();
|
|
|
|
if (mediaDisplayed) {
|
|
|
|
auto mediaSelection = unskipTextSelection(_media->adjustSelection(skipTextSelection(selection), type));
|
|
|
|
if (selection.from >= beforeMediaLength) {
|
|
|
|
result = mediaSelection;
|
|
|
|
} else {
|
|
|
|
result.to = mediaSelection.to;
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-06-23 19:28:42 +00:00
|
|
|
auto beforeEntryLength = beforeMediaLength + (mediaDisplayed ? _media->fullSelectionLength() : 0);
|
|
|
|
if (selection.to <= beforeEntryLength) {
|
|
|
|
return result;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-06-23 19:28:42 +00:00
|
|
|
if (auto entry = Get<HistoryMessageLogEntryOriginal>()) {
|
|
|
|
auto entrySelection = mediaDisplayed ? _media->skipSelection(skipTextSelection(selection)) : skipTextSelection(selection);
|
|
|
|
auto logEntryOriginalSelection = entry->_page->adjustSelection(entrySelection, type);
|
|
|
|
if (mediaDisplayed) {
|
|
|
|
logEntryOriginalSelection = _media->unskipSelection(logEntryOriginalSelection);
|
|
|
|
}
|
|
|
|
logEntryOriginalSelection = unskipTextSelection(logEntryOriginalSelection);
|
|
|
|
if (selection.from >= beforeEntryLength) {
|
|
|
|
result = logEntryOriginalSelection;
|
|
|
|
} else {
|
|
|
|
result.to = logEntryOriginalSelection.to;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-22 15:11:41 +00:00
|
|
|
void HistoryMessage::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
|
|
|
if (_media) _media->clickHandlerActiveChanged(p, active);
|
|
|
|
HistoryItem::clickHandlerActiveChanged(p, active);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
|
|
|
|
if (_media) _media->clickHandlerPressedChanged(p, pressed);
|
|
|
|
HistoryItem::clickHandlerPressedChanged(p, pressed);
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
QString HistoryMessage::notificationHeader() const {
|
|
|
|
return (!_history->peer->isUser() && !isPost()) ? from()->name : QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryMessage::displayFromPhoto() const {
|
2016-11-18 16:27:47 +00:00
|
|
|
return hasFromPhoto() && !isAttachedToNext();
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryMessage::hasFromPhoto() const {
|
2017-01-14 18:50:16 +00:00
|
|
|
return (Adaptive::ChatWide() || (!out() && !history()->peer->isUser())) && !isPost() && !isEmpty();
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
HistoryMessage::~HistoryMessage() {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media.reset();
|
2016-09-27 14:20:49 +00:00
|
|
|
if (auto reply = Get<HistoryMessageReply>()) {
|
|
|
|
reply->clearData(this);
|
|
|
|
}
|
|
|
|
}
|