serverside links parsing used

This commit is contained in:
John Preston 2015-08-24 13:53:04 +03:00
parent 8dff205949
commit 4afa1aace0
8 changed files with 345 additions and 151 deletions

View file

@ -79,6 +79,9 @@ namespace {
typedef QMap<uint64, MsgId> RandomData;
RandomData randomData;
typedef QMap<uint64, QString> SentTextData;
SentTextData sentTextData;
HistoryItem *hoveredItem = 0, *pressedItem = 0, *hoveredLinkItem = 0, *pressedLinkItem = 0, *contextItem = 0, *mousedItem = 0;
QPixmap *sprite = 0, *emojis = 0, *emojisLarge = 0;
@ -1681,6 +1684,7 @@ namespace App {
void historyClearItems() {
historyClearMsgs();
randomData.clear();
sentTextData.clear();
mutedPeers.clear();
updatedPeers.clear();
cSetSavedPeers(SavedPeers());
@ -1756,6 +1760,18 @@ namespace App {
return 0;
}
void historyRegSentText(uint64 randomId, const QString &text) {
sentTextData.insert(randomId, text);
}
void historyUnregSentText(uint64 randomId) {
sentTextData.remove(randomId);
}
QString histSentTextByItem(uint64 randomId) {
return sentTextData.value(randomId);
}
void prepareCorners(RoundCorners index, int32 radius, const style::color &color, const style::color *shadow = 0, QImage *cors = 0) {
int32 r = radius * cIntRetinaFactor(), s = st::msgShadow * cIntRetinaFactor();
QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4];

View file

@ -188,6 +188,9 @@ namespace App {
void historyRegRandom(uint64 randomId, MsgId itemId);
void historyUnregRandom(uint64 randomId);
MsgId histItemByRandom(uint64 randomId);
void historyRegSentText(uint64 itemId, const QString &text);
void historyUnregSentText(uint64 itemId);
QString histSentTextByItem(uint64 itemId);
void hoveredItem(HistoryItem *item);
HistoryItem *hoveredItem();

View file

@ -146,8 +146,8 @@ inline bool emojiEdge(const QChar *ch) {
inline QString replaceEmojis(const QString &text) {
QString result;
LinkRanges lnkRanges = textParseLinks(text, TextParseLinks | TextParseMentions | TextParseHashtags);
int32 currentLink = 0, lnkCount = lnkRanges.size();
LinksInText links = textParseLinks(text, TextParseLinks | TextParseMentions | TextParseHashtags);
int32 currentLink = 0, lnkCount = links.size();
const QChar *emojiStart = text.unicode(), *emojiEnd = emojiStart, *e = text.cend();
bool canFindEmoji = true, consumePrevious = false;
for (const QChar *ch = emojiEnd; ch != e;) {
@ -157,14 +157,14 @@ inline QString replaceEmojis(const QString &text) {
emojiFind(ch, e, newEmojiEnd, emojiCode);
}
while (currentLink < lnkCount && ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len) {
while (currentLink < lnkCount && ch >= emojiStart + links[currentLink].offset + links[currentLink].length) {
++currentLink;
}
EmojiPtr emoji = emojiCode ? emojiGet(emojiCode) : 0;
if (emoji && emoji != TwoSymbolEmoji &&
(ch == emojiStart || !ch->isLetterOrNumber() || !(ch - 1)->isLetterOrNumber()) &&
(newEmojiEnd == e || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) &&
(currentLink >= lnkCount || (ch < lnkRanges[currentLink].from && newEmojiEnd <= lnkRanges[currentLink].from) || (ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len && newEmojiEnd > lnkRanges[currentLink].from + lnkRanges[currentLink].len))
(currentLink >= lnkCount || (ch < emojiStart + links[currentLink].offset && newEmojiEnd <= emojiStart + links[currentLink].offset) || (ch >= emojiStart + links[currentLink].offset + links[currentLink].length && newEmojiEnd > emojiStart + links[currentLink].offset + links[currentLink].length))
) {
// if (newEmojiEnd < e && newEmojiEnd->unicode() == ' ') ++newEmojiEnd;
if (result.isEmpty()) result.reserve(text.size());

View file

@ -328,13 +328,13 @@ public:
}
bool checkWaitedLink() {
if (waitingLink == linksEnd || ptr < waitingLink->from || links.size() >= 0x7FFF) {
if (waitingLink == linksEnd || ptr < start + waitingLink->offset || links.size() >= 0x7FFF) {
return true;
}
createBlock();
QString lnkUrl = QString(waitingLink->from, waitingLink->len), lnkText;
QString lnkUrl = QString(start + waitingLink->offset, waitingLink->length), lnkText;
int32 fullDisplayed;
getLinkData(lnkUrl, lnkText, fullDisplayed);
@ -342,7 +342,7 @@ public:
lnkIndex = 0x8000 + links.size();
_t->_text += lnkText;
ptr = waitingLink->from + waitingLink->len;
ptr = start + waitingLink->offset + waitingLink->length;
createBlock();
++waitingLink;
@ -519,8 +519,31 @@ public:
emoji = e;
}
TextParser(Text *t, const QString &text, const TextParseOptions &options) : _t(t), src(text),
rich(options.flags & TextParseRichText), multiline(options.flags & TextParseMultiline), maxLnkIndex(0), flags(0), lnkIndex(0), stopAfterWidth(QFIXED_MAX) {
TextParser(Text *t, const QString &text, const TextParseOptions &options) : _t(t),
src(text),
rich(options.flags & TextParseRichText),
multiline(options.flags & TextParseMultiline),
maxLnkIndex(0),
flags(0),
lnkIndex(0),
stopAfterWidth(QFIXED_MAX) {
if (options.flags & TextParseLinks) {
lnkRanges = textParseLinks(src, options.flags, rich);
}
parse(options);
}
TextParser(Text *t, const QString &text, const LinksInText &links, const TextParseOptions &options) : _t(t),
src(text),
rich(options.flags & TextParseRichText),
multiline(options.flags & TextParseMultiline),
maxLnkIndex(0),
flags(0),
lnkIndex(0),
stopAfterWidth(QFIXED_MAX) {
lnkRanges = links;
parse(options);
}
void parse(const TextParseOptions &options) {
int flags = options.flags;
if (options.maxw > 0 && options.maxh > 0) {
stopAfterWidth = ((options.maxh / _t->_font->height) + 1) * options.maxw;
@ -529,10 +552,6 @@ public:
start = src.constData();
end = start + src.size();
if (options.flags & TextParseLinks) {
lnkRanges = textParseLinks(src, options.flags, rich);
}
while (start != end && chIsTrimmed(*start, rich)) {
++start;
}
@ -552,8 +571,8 @@ public:
ch = chInt = 0;
lastSkipped = false;
lastSpace = true;
waitingLink = lnkRanges.isEmpty() ? 0 : lnkRanges.constData();
linksEnd = lnkRanges.isEmpty() ? 0 : waitingLink + lnkRanges.size();
waitingLink = lnkRanges.cbegin();
linksEnd = lnkRanges.cend();
for (ptr = start; ptr <= end; ++ptr) {
if (!checkWaitedLink()) {
break;
@ -615,8 +634,8 @@ private:
const QChar *start, *end, *ptr;
bool rich, multiline;
LinkRanges lnkRanges;
const LinkRange *waitingLink, *linksEnd;
LinksInText lnkRanges;
LinksInText::const_iterator waitingLink, linksEnd;
struct TextLinkData {
TextLinkData(const QString &url = QString(), int32 fullDisplayed = 1) : url(url), fullDisplayed(fullDisplayed) {
@ -2305,6 +2324,7 @@ void Text::setText(style::font font, const QString &text, const TextParseOptions
void Text::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) {
NewlineBlock *lastNewline = 0;
_maxWidth = _minHeight = 0;
int32 lineHeight = 0;
int32 result = 0, lastNewlineStart = 0;
QFixed _width = 0, last_rBearing = 0, last_rPadding = 0;
@ -2369,8 +2389,14 @@ void Text::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) {
}
}
void Text::setMarkedText(style::font font, const QString &text, const LinksInText &links) {
void Text::setMarkedText(style::font font, const QString &text, const LinksInText &links, const TextParseOptions &options) {
if (!_textStyle) _initDefault();
_font = font;
clean();
{
TextParser parser(this, text, links, options);
}
recountNaturalSize(true, options.dir);
}
void Text::setRichText(style::font font, const QString &text, TextParseOptions options, const TextCustomTagsMap &custom) {
@ -2460,16 +2486,80 @@ bool Text::hasLinks() const {
return !_links.isEmpty();
}
void Text::setSkipBlock(int32 width) {
void Text::setSkipBlock(int32 width, int32 height) {
if (!_blocks.isEmpty() && _blocks.back()->type() == TextBlockSkip) {
SkipBlock *block = static_cast<SkipBlock*>(_blocks.back());
if (block->width() == width && block->height() == height) return;
_text.resize(block->from());
_blocks.pop_back();
}
_text.push_back('_');
_blocks.push_back(new SkipBlock(_font, _text, _text.size() - 1, width, height, 0));
recountNaturalSize(false);
}
void Text::removeSkipBlock() {
if (!_blocks.isEmpty() && _blocks.back()->type() == TextBlockSkip) {
_text.resize(_blocks.back()->from());
_blocks.pop_back();
recountNaturalSize(false);
}
}
LinksInText Text::calcLinksInText() const {
return LinksInText();
LinksInText result;
int32 lnkFrom = 0, lnkIndex = 0, offset = 0;
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) {
int32 blockLnkIndex = (i == e) ? 0 : (*i)->lnkIndex();
int32 blockFrom = (i == e) ? _text.size() : (*i)->from();
if (blockLnkIndex != lnkIndex) {
if (lnkIndex) { // write link
const TextLinkPtr &lnk(_links.at(lnkIndex - 1));
const QString &url(lnk ? lnk->text() : QString());
int32 rangeFrom = lnkFrom, rangeTo = blockFrom;
if (rangeTo > rangeFrom) {
QStringRef r = _text.midRef(rangeFrom, rangeTo - rangeFrom);
if (url.isEmpty()) {
offset += r.size();
} else {
QUrl u(url);
if (r.size() <= 3 || _text.midRef(lnkFrom, r.size() - 3) == (u.isValid() ? u.toDisplayString() : url).midRef(0, r.size() - 3)) { // same link
if (url.at(0) == '@') {
result.push_back(LinkInText(LinkInTextMention, offset, url.size()));
} else if (url.at(0) == '#') {
result.push_back(LinkInText(LinkInTextHashtag, offset, url.size()));
} else if (url.at(0) == '/') {
result.push_back(LinkInText(LinkInTextBotCommand, offset, url.size()));
} else if (url.indexOf('@') > 0 && url.indexOf('/') <= 0) {
result.push_back(LinkInText(LinkInTextEmail, offset, url.size()));
} else {
result.push_back(LinkInText(LinkInTextUrl, offset, url.size()));
}
offset += url.size();
} else {
result.push_back(LinkInText(LinkInTextCustomUrl, offset, r.size(), url));
offset += r.size();
}
}
}
}
lnkIndex = blockLnkIndex;
lnkFrom = blockFrom;
}
if (i == e) break;
TextBlockType type = (*i)->type();
if (type == TextBlockSkip) continue;
if (!blockLnkIndex) {
int32 rangeFrom = (*i)->from(), rangeTo = uint16((*i)->from() + TextPainter::_blockLength(this, i, e));
if (rangeTo > rangeFrom) {
offset += rangeTo - rangeFrom;
}
}
}
return result;
}
int32 Text::countHeight(int32 w) const {
@ -4058,16 +4148,16 @@ QString textSearchKey(const QString &text) {
bool textSplit(QString &sendingText, QString &leftText, int32 limit) {
if (leftText.isEmpty() || !limit) return false;
LinkRanges lnkRanges = textParseLinks(leftText, TextParseLinks | TextParseMentions | TextParseHashtags);
int32 currentLink = 0, lnkCount = lnkRanges.size();
LinksInText links = textParseLinks(leftText, TextParseLinks | TextParseMentions | TextParseHashtags);
int32 currentLink = 0, lnkCount = links.size();
int32 s = 0, half = limit / 2, goodLevel = 0;
for (const QChar *start = leftText.constData(), *ch = start, *end = leftText.constEnd(), *good = ch; ch != end; ++ch, ++s) {
while (currentLink < lnkCount && ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len) {
while (currentLink < lnkCount && ch >= start + links[currentLink].offset + links[currentLink].length) {
++currentLink;
}
bool inLink = (currentLink < lnkCount) && (ch > lnkRanges[currentLink].from) && (ch < lnkRanges[currentLink].from + lnkRanges[currentLink].len);
bool inLink = (currentLink < lnkCount) && (ch > start + links[currentLink].offset) && (ch < start + links[currentLink].offset + links[currentLink].length);
if (s > half) {
if (inLink) {
if (!goodLevel) good = ch;
@ -4125,8 +4215,8 @@ bool textSplit(QString &sendingText, QString &leftText, int32 limit) {
return true;
}
LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some code is duplicated in flattextarea.cpp!
LinkRanges lnkRanges;
LinksInText textParseLinks(const QString &text, int32 flags, bool rich) { // some code is duplicated in flattextarea.cpp!
LinksInText result;
bool withHashtags = (flags & TextParseHashtags);
bool withMentions = (flags & TextParseMentions);
@ -4149,7 +4239,8 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch();
QRegularExpressionMatch mBotCommand = withBotCommands ? _reBotCommand.match(text, matchOffset) : QRegularExpressionMatch();
LinkRange link;
LinkInTextType lnkType = LinkInTextUrl;
int32 lnkOffset = 0, lnkLength = 0;
int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
domainEnd = mDomain.hasMatch() ? mDomain.capturedEnd() : INT_MAX,
explicitDomainOffset = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedStart() : INT_MAX,
@ -4214,9 +4305,9 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
continue;
}
}
link.from = start + mentionOffset;
link.len = start + mentionEnd - link.from;
lnkType = LinkInTextMention;
lnkOffset = mentionOffset;
lnkLength = mentionEnd - mentionOffset;
} else if (hashtagOffset < domainOffset && hashtagOffset < botCommandOffset) {
if (hashtagOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len);
@ -4226,8 +4317,9 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
}
}
link.from = start + hashtagOffset;
link.len = start + hashtagEnd - link.from;
lnkType = LinkInTextHashtag;
lnkOffset = hashtagOffset;
lnkLength = hashtagEnd - hashtagOffset;
} else if (botCommandOffset < domainOffset) {
if (botCommandOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len);
@ -4237,8 +4329,9 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
}
}
link.from = start + botCommandOffset;
link.len = start + botCommandEnd - link.from;
lnkType = LinkInTextBotCommand;
lnkOffset = botCommandOffset;
lnkLength = botCommandEnd - botCommandOffset;
} else {
if (domainOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len);
@ -4262,16 +4355,17 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
if (mailOffset < offset) {
mailOffset = offset;
}
link.from = start + mailOffset;
link.len = domainEnd - mailOffset;
lnkType = LinkInTextEmail;
lnkOffset = mailOffset;
lnkLength = domainEnd - mailOffset;
}
}
if (!link.from || !link.len) {
if (lnkType == LinkInTextUrl && !lnkLength) {
if (!isProtocolValid || !isTopDomainValid) {
matchOffset = domainEnd;
continue;
}
link.from = start + domainOffset;
lnkOffset = domainOffset;
QStack<const QChar*> parenth;
const QChar *domainEnd = start + mDomain.capturedEnd(), *p = domainEnd;
@ -4306,15 +4400,15 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
continue;
}
}
link.len = p - link.from;
lnkLength = (p - start) - lnkOffset;
}
}
lnkRanges.push_back(link);
result.push_back(LinkInText(lnkType, lnkOffset, lnkLength));
offset = matchOffset = (link.from - start) + link.len;
offset = matchOffset = lnkOffset + lnkLength;
}
return lnkRanges;
return result;
}
void emojiDraw(QPainter &p, EmojiPtr e, int x, int y) {

View file

@ -39,14 +39,57 @@ enum {
TextInstagramHashtags = 0x200,
};
struct LinkRange {
LinkRange() : from(0), len(0) {
}
const QChar *from;
int32 len;
enum LinkInTextType {
LinkInTextUrl,
LinkInTextCustomUrl,
LinkInTextEmail,
LinkInTextHashtag,
LinkInTextMention,
LinkInTextBotCommand,
};
typedef QVector<LinkRange> LinkRanges;
LinkRanges textParseLinks(const QString &text, int32 flags, bool rich = false);
struct LinkInText {
LinkInText(LinkInTextType type, int32 offset, int32 length, const QString &text = QString()) : type(type), offset(offset), length(length), text(text) {
}
LinkInTextType type;
int32 offset, length;
QString text;
};
typedef QList<LinkInText> LinksInText;
inline LinksInText linksFromMTP(const QVector<MTPMessageEntity> &entities) {
LinksInText result;
if (!entities.isEmpty()) {
result.reserve(entities.size());
for (int32 i = 0, l = entities.size(); i != l; ++i) {
const MTPMessageEntity &e(entities.at(i));
switch (e.type()) {
case mtpc_messageEntityUrl: { const MTPDmessageEntityUrl &d(e.c_messageEntityUrl()); result.push_back(LinkInText(LinkInTextUrl, d.voffset.v, d.vlength.v)); } break;
case mtpc_messageEntityTextUrl: { const MTPDmessageEntityTextUrl &d(e.c_messageEntityTextUrl()); result.push_back(LinkInText(LinkInTextCustomUrl, d.voffset.v, d.vlength.v, textClean(qs(d.vurl)))); } break;
case mtpc_messageEntityEmail: { const MTPDmessageEntityEmail &d(e.c_messageEntityEmail()); result.push_back(LinkInText(LinkInTextEmail, d.voffset.v, d.vlength.v)); } break;
case mtpc_messageEntityHashtag: { const MTPDmessageEntityHashtag &d(e.c_messageEntityHashtag()); result.push_back(LinkInText(LinkInTextHashtag, d.voffset.v, d.vlength.v)); } break;
case mtpc_messageEntityMention: { const MTPDmessageEntityMention &d(e.c_messageEntityMention()); result.push_back(LinkInText(LinkInTextMention, d.voffset.v, d.vlength.v)); } break;
case mtpc_messageEntityBotCommand: { const MTPDmessageEntityBotCommand &d(e.c_messageEntityBotCommand()); result.push_back(LinkInText(LinkInTextBotCommand, d.voffset.v, d.vlength.v)); } break;
}
}
}
return result;
}
inline MTPVector<MTPMessageEntity> linksToMTP(const LinksInText &links) {
MTPVector<MTPMessageEntity> result(MTP_vector<MTPMessageEntity>(0));
QVector<MTPMessageEntity> &v(result._vector().v);
for (int32 i = 0, s = links.size(); i != s; ++i) {
const LinkInText &l(links.at(i));
switch (l.type) {
case LinkInTextUrl: v.push_back(MTP_messageEntityUrl(MTP_int(l.offset), MTP_int(l.length))); break;
case LinkInTextCustomUrl: v.push_back(MTP_messageEntityTextUrl(MTP_int(l.offset), MTP_int(l.length), MTP_string(l.text))); break;
case LinkInTextEmail: v.push_back(MTP_messageEntityEmail(MTP_int(l.offset), MTP_int(l.length))); break;
case LinkInTextHashtag: v.push_back(MTP_messageEntityHashtag(MTP_int(l.offset), MTP_int(l.length))); break;
case LinkInTextMention: v.push_back(MTP_messageEntityMention(MTP_int(l.offset), MTP_int(l.length))); break;
case LinkInTextBotCommand: v.push_back(MTP_messageEntityBotCommand(MTP_int(l.offset), MTP_int(l.length))); break;
}
}
return result;
}
LinksInText textParseLinks(const QString &text, int32 flags, bool rich = false);
#include "gui/emoji_config.h"
@ -464,23 +507,6 @@ enum TextSelectType {
typedef QPair<QString, QString> TextCustomTag; // open str and close str
typedef QMap<QChar, TextCustomTag> TextCustomTagsMap;
enum LinkInTextType {
LinkInTextUrl,
LinkInTextCustomUrl,
LinkInTextEmail,
LinkInTextHashtag,
LinkInTextMention,
LinkInTextBotCommand,
};
struct LinkInText {
LinkInText(LinkInTextType type, int32 offset, int32 length, const QString &text = QString()) : type(type), offset(offset), length(length), text(text) {
}
LinkInTextType type;
int32 offset, length;
QString text;
};
typedef QList<LinkInText> LinksInText;
class Text {
public:
@ -492,7 +518,7 @@ public:
int32 countHeight(int32 width) const;
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions);
void setRichText(style::font font, const QString &text, TextParseOptions options = _defaultOptions, const TextCustomTagsMap &custom = TextCustomTagsMap());
void setMarkedText(style::font font, const QString &text, const LinksInText &links);
void setMarkedText(style::font font, const QString &text, const LinksInText &links, const TextParseOptions &options = _defaultOptions);
void setLink(uint16 lnkIndex, const TextLinkPtr &lnk);
bool hasLinks() const;
@ -500,7 +526,7 @@ public:
bool hasSkipBlock() const {
return _blocks.isEmpty() ? false : _blocks.back()->type() == TextBlockSkip;
}
void setSkipBlock(int32 width);
void setSkipBlock(int32 width, int32 height);
void removeSkipBlock();
LinksInText calcLinksInText() const;

View file

@ -110,15 +110,17 @@ namespace {
return item ? item->toHistoryForwarded() : 0;
}
inline const TextParseOptions &itemTextParseOptions(HistoryItem *item) {
History *h = item->history();
UserData *f = item->from();
if ((!h->peer->chat && h->peer->asUser()->botInfo) || (!f->chat && f->asUser()->botInfo) || (h->peer->chat && h->peer->asChat()->botStatus >= 0)) {
return _historyBotOptions;
}
return _historyTextOptions;
return itemTextParseOptions(item->history(), item->from());
}
}
const TextParseOptions &itemTextParseOptions(History *h, UserData *f) {
if ((!h->peer->chat && h->peer->asUser()->botInfo) || (!f->chat && f->asUser()->botInfo) || (h->peer->chat && h->peer->asChat()->botStatus >= 0)) {
return _historyBotOptions;
}
return _historyTextOptions;
}
void historyInit() {
_initTextOptions();
}
@ -1035,7 +1037,7 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
int32 addToH = 0, skip = 0;
if (!isEmpty()) {
addToH = -front()->height;
if (width) addToH = -front()->height;
pop_front(); // remove date block
}
HistoryItem *till = isEmpty() ? 0 : front()->front(), *prev = 0;
@ -1049,12 +1051,16 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
if (prev && prev->date.date() != adding->date.date()) {
HistoryItem *dayItem = createDayServiceMsg(this, block, adding->date);
block->push_back(dayItem);
dayItem->y = block->height;
block->height += dayItem->resize(width);
if (width) {
dayItem->y = block->height;
block->height += dayItem->resize(width);
}
}
block->push_back(adding);
adding->y = block->height;
block->height += adding->resize(width);
if (width) {
adding->y = block->height;
block->height += adding->resize(width);
}
setMsgCount(msgCount + 1);
prev = adding;
}
@ -1063,8 +1069,10 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
if (till && prev && prev->date.date() != till->date.date()) {
HistoryItem *dayItem = createDayServiceMsg(this, block, till->date);
block->push_back(dayItem);
dayItem->y = block->height;
block->height += dayItem->resize(width);
if (width) {
dayItem->y = block->height;
block->height += dayItem->resize(width);
}
}
if (block->size()) {
if (loadedAtBottom() && wasMsgCount < unreadCount && msgCount >= unreadCount) {
@ -1079,8 +1087,10 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
}
}
push_front(block);
addToH += block->height;
++skip;
if (width) {
addToH += block->height;
++skip;
}
if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors
int32 mask = 0;
@ -1167,16 +1177,18 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
HistoryBlock *dateBlock = new HistoryBlock(this);
HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, front()->front()->date);
dateBlock->push_back(dayItem);
int32 dh = dayItem->resize(width);
dateBlock->height = dh;
if (skip) {
front()->y += dh;
if (width) {
int32 dh = dayItem->resize(width);
dateBlock->height = dh;
if (skip) {
front()->y += dh;
}
addToH += dh;
++skip;
}
push_front(dateBlock); // date block
addToH += dh;
++skip;
}
if (addToH) {
if (width && addToH) {
for (iterator i = begin(), e = end(); i != e; ++i) {
if (skip) {
--skip;
@ -5026,7 +5038,7 @@ HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPD
QString text(textClean(qs(msg.vmessage)));
initTime();
initMedia(msg.vmedia, text);
initDimensions(text, msg.has_entities() ? linksFromMTP(msg.ventities.c_vector().v) : LinksInText());
setText(text, msg.has_entities() ? linksFromMTP(msg.ventities.c_vector().v) : LinksInText());
}
HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, const QString &msg, const LinksInText &links, const MTPMessageMedia &media) :
@ -5039,7 +5051,7 @@ HistoryItem(history, block, msgId, flags, date, from)
QString text(msg);
initTime();
initMedia(media, text);
initDimensions(text, links);
setText(text, links);
}
HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, const QString &msg, const LinksInText &links, HistoryMedia *fromMedia) :
@ -5054,7 +5066,7 @@ HistoryItem(history, block, msgId, flags, date, from)
_media = fromMedia->clone();
_media->regItem(this);
}
initDimensions(msg, links);
setText(msg, links);
}
HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime date, int32 from, DocumentData *doc) :
@ -5066,7 +5078,7 @@ HistoryItem(history, block, msgId, flags, date, from)
{
initTime();
initMediaFromDocument(doc);
initDimensions(QString(), LinksInText());
setText(QString(), LinksInText());
}
void HistoryMessage::initTime() {
@ -5154,22 +5166,6 @@ void HistoryMessage::initMediaFromDocument(DocumentData *doc) {
_media->regItem(this);
}
void HistoryMessage::initDimensions(const QString &text, const LinksInText &links) {
if (!_media || !text.isEmpty()) { // !justMedia()
if (_media && _media->isDisplayed()) {
_text.setMarkedText(st::msgFont, text, links, itemTextParseOptions(this));
} else {
_text.setMarkedText(st::msgFont, text + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), links, itemTextParseOptions(this));
}
for (int32 i = 0, l = links.size(); i != l; ++i) {
if (links.at(i).type == LinkInTextUrl || links.at(i).type == LinkInTextCustomUrl || links.at(i).type == LinkInTextEmail) {
_flags |= MTPDmessage_flag_HAS_TEXT_LINKS;
break;
}
}
}
}
void HistoryMessage::initDimensions(const HistoryItem *parent) {
if (justMedia()) {
_media->initDimensions(this);
@ -5182,19 +5178,13 @@ void HistoryMessage::initDimensions(const HistoryItem *parent) {
if (_media) {
_media->initDimensions(this);
if (_media->isDisplayed() && _text.hasSkipBlock()) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was, itemTextParseOptions(this)); // without date skip
_textWidth = 0;
_textHeight = 0;
}
_text.removeSkipBlock();
_textWidth = 0;
_textHeight = 0;
} else if (!_media->isDisplayed() && !_text.hasSkipBlock()) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), itemTextParseOptions(this)); // without date skip
_textWidth = 0;
_textHeight = 0;
}
_text.setSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y());
_textWidth = 0;
_textHeight = 0;
}
if (_media->isDisplayed()) {
int32 maxw = _media->maxWidth() + st::msgPadding.left() + st::msgPadding.right();
@ -5226,6 +5216,10 @@ QString HistoryMessage::selectedText(uint32 selection) const {
return _text.original(selectedFrom, selectedTo);
}
LinksInText HistoryMessage::textLinks() const {
return _text.calcLinksInText();
}
QString HistoryMessage::inDialogsText() const {
QString result = _media ? _media->inDialogsText() : QString();
return result.isEmpty() ? _text.original(0, 0xFFFF, false) : result;
@ -5237,6 +5231,7 @@ HistoryMedia *HistoryMessage::getMedia(bool inOverview) const {
void HistoryMessage::setMedia(const MTPmessageMedia &media) {
if ((!_media || _media->isImageLink()) && media.type() == mtpc_messageMediaEmpty) return;
bool mediaWasDisplayed = false;
if (_media) {
mediaWasDisplayed = _media->isDisplayed();
@ -5246,24 +5241,38 @@ void HistoryMessage::setMedia(const MTPmessageMedia &media) {
QString t;
initMedia(media, t);
if (_media && _media->isDisplayed() && !mediaWasDisplayed) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was, itemTextParseOptions(this)); // without date skip
_textWidth = 0;
_textHeight = 0;
}
_text.removeSkipBlock();
_textWidth = 0;
_textHeight = 0;
} else if (mediaWasDisplayed && (!_media || !_media->isDisplayed())) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), itemTextParseOptions(this)); // without date skip
_textWidth = 0;
_textHeight = 0;
}
_text.setSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y());
_textWidth = 0;
_textHeight = 0;
}
initDimensions(0);
if (App::main()) App::main()->itemResized(this);
}
void HistoryMessage::setText(const QString &text, const LinksInText &links) {
if (!_media || !text.isEmpty()) { // !justMedia()
if (_media && _media->isDisplayed()) {
_text.setMarkedText(st::msgFont, text, links, itemTextParseOptions(this));
} else {
_text.setMarkedText(st::msgFont, text + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), links, itemTextParseOptions(this));
}
if (id > 0) {
for (int32 i = 0, l = links.size(); i != l; ++i) {
if (links.at(i).type == LinkInTextUrl || links.at(i).type == LinkInTextCustomUrl || links.at(i).type == LinkInTextEmail) {
_flags |= MTPDmessage_flag_HAS_TEXT_LINKS;
break;
}
}
}
_textWidth = 0;
_textHeight = 0;
}
}
void HistoryMessage::draw(QPainter &p, uint32 selection) const {
textstyleSet(&(out() ? st::outTextStyle : st::inTextStyle));
@ -5368,6 +5377,8 @@ void HistoryMessage::drawMessageText(QPainter &p, const QRect &trect, uint32 sel
}
int32 HistoryMessage::resize(int32 width, bool dontRecountText, const HistoryItem *parent) {
if (width < st::msgMinWidth) return _height;
width -= st::msgMargin.left() + st::msgMargin.right();
if (justMedia()) {
_height = _media->resize(width, dontRecountText, this);
@ -5573,7 +5584,7 @@ HistoryMessage::~HistoryMessage() {
}
}
HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const MTPDmessage &msg) : HistoryMessage(history, block, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.vfrom_id.v, textClean(qs(msg.vmessage)), msg.vmedia)
HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const MTPDmessage &msg) : HistoryMessage(history, block, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.vfrom_id.v, textClean(qs(msg.vmessage)), msg.has_entities() ? linksFromMTP(msg.ventities.c_vector().v) : LinksInText(), msg.vmedia)
, fwdDate(::date(msg.vfwd_date))
, fwdFrom(App::user(msg.vfwd_from_id.v))
, fwdFromVersion(fwdFrom->nameVersion)
@ -5582,7 +5593,7 @@ HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const
fwdNameUpdated();
}
HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, HistoryMessage *msg) : HistoryMessage(history, block, id, ((history->peer->input.type() != mtpc_inputPeerSelf) ? (MTPDmessage_flag_out | MTPDmessage_flag_unread) : 0) | (msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage_flag_media_unread : 0), ::date(unixtime()), MTP::authedId(), msg->justMedia() ? QString() : msg->HistoryMessage::selectedText(FullItemSel), msg->getMedia())
HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, HistoryMessage *msg) : HistoryMessage(history, block, id, ((history->peer->input.type() != mtpc_inputPeerSelf) ? (MTPDmessage_flag_out | MTPDmessage_flag_unread) : 0) | (msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage_flag_media_unread : 0), ::date(unixtime()), MTP::authedId(), msg->justMedia() ? QString() : msg->HistoryMessage::selectedText(FullItemSel), msg->HistoryMessage::textLinks(), msg->getMedia())
, fwdDate(msg->dateForwarded())
, fwdFrom(msg->fromForwarded())
, fwdFromVersion(fwdFrom->nameVersion)
@ -5769,7 +5780,7 @@ void HistoryForwarded::getSymbol(uint16 &symbol, bool &after, bool &upon, int32
return HistoryMessage::getSymbol(symbol, after, upon, x, y);
}
HistoryReply::HistoryReply(History *history, HistoryBlock *block, const MTPDmessage &msg) : HistoryMessage(history, block, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.vfrom_id.v, textClean(qs(msg.vmessage)), msg.vmedia)
HistoryReply::HistoryReply(History *history, HistoryBlock *block, const MTPDmessage &msg) : HistoryMessage(history, block, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.vfrom_id.v, textClean(qs(msg.vmessage)), msg.has_entities() ? linksFromMTP(msg.ventities.c_vector().v) : LinksInText(), msg.vmedia)
, replyToMsgId(msg.vreply_to_msg_id.v)
, replyToMsg(0)
, replyToVersion(0)

View file

@ -780,6 +780,8 @@ public:
}
virtual void setMedia(const MTPmessageMedia &media) {
}
virtual void setText(const QString &text, const LinksInText &links) {
}
virtual QString time() const {
return QString();
}
@ -1288,7 +1290,6 @@ public:
void initMediaFromText(QString &currentText);
void initMediaFromDocument(DocumentData *doc);
void initDimensions(const HistoryItem *parent = 0);
void initDimensions(const QString &text, const LinksInText &links);
void fromNameUpdated() const;
bool justMedia() const {
@ -1322,9 +1323,11 @@ public:
}
QString selectedText(uint32 selection) const;
LinksInText textLinks() const;
QString inDialogsText() const;
HistoryMedia *getMedia(bool inOverview = false) const;
void setMedia(const MTPmessageMedia &media);
void setText(const QString &text, const LinksInText &links);
QString time() const {
return _time;
@ -1559,3 +1562,5 @@ protected:
QString text;
bool freezed;
};
const TextParseOptions &itemTextParseOptions(History *h, UserData *f);

View file

@ -982,6 +982,12 @@ DialogsIndexed &MainWidget::dialogsList() {
return dialogs.dialogsList();
}
QString cleanMessage(const QString &text) {
QString result = text.trimmed();
// clean bad symbols
return result;
}
void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId replyTo, WebPageId webPageId) {
saveRecentHashtags(text);
QString sendingText, leftText = text;
@ -990,7 +996,10 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
MsgId newId = clientMsgId();
uint64 randomId = MTP::nonce<uint64>();
sendingText = cleanMessage(sendingText);
App::historyRegRandom(randomId, newId);
App::historyRegSentText(randomId, sendingText);
MTPstring msgText(MTP_string(sendingText));
int32 flags = newMessageFlags(hist->peer); // unread, out
@ -1006,8 +1015,9 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
WebPageData *page = App::webPage(webPageId);
media = MTP_messageMediaWebPage(MTP_webPagePending(MTP_long(page->id), MTP_int(page->pendingTill)));
}
MTPVector<MTPMessageEntity> localEntities = linksToMTP(textParseLinks(sendingText, itemTextParseOptions(hist, App::self()).flags));
hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTPint(), MTPint(), MTP_int(replyTo), MTP_int(unixtime()), msgText, media, MTPnullMarkup, MTPnullEntities));
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_int(sendFlags), hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId), MTPnullMarkup, MTPnullEntities), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_int(sendFlags), hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId), MTPnullMarkup, localEntities), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
}
finishForwarding(hist);
@ -2221,8 +2231,21 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
switch (result.type()) {
case mtpc_messages_sentMessage: {
const MTPDmessages_sentMessage &d(result.c_messages_sentMessage());
if (randomId) feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
HistoryItem *item = 0;
if (randomId) {
QString text = App::histSentTextByItem(randomId);
feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
LinksInText links(linksFromMTP(d.ventities.c_vector().v));
if (!text.isEmpty() && !links.isEmpty()) {
item = App::histItemById(d.vid.v);
if (item) {
item->setText(text, links);
item->initDimensions(0);
itemResized(item);
}
}
}
if (updInited) {
if (!updPtsUpdated(d.vpts.v, d.vpts_count.v)) {
@ -2231,7 +2254,9 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
}
}
HistoryItem *item = App::histItemById(d.vid.v);
if (!item) {
item = App::histItemById(d.vid.v);
}
if (item) {
item->setMedia(d.vmedia);
}
@ -2240,7 +2265,20 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
case mtpc_messages_sentMessageLink: {
const MTPDmessages_sentMessageLink &d(result.c_messages_sentMessageLink());
if (randomId) feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
HistoryItem *item = 0;
if (randomId) {
//QString text = App::histSentTextByItem(randomId);
feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
//LinksInText links(linksFromMTP(d.ventities.c_vector().v));
//if (!text.isEmpty() && !links.isEmpty()) {
// item = App::histItemById(d.vid.v);
// if (item) {
// item->setText(text, links);
// item->initDimensions(0);
// itemResized(item);
// }
//}
}
if (updInited) {
if (!updPtsUpdated(d.vpts.v, d.vpts_count.v)) {
@ -2250,11 +2288,11 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
}
App::feedUserLinks(d.vlinks);
if (d.vmedia.type() != mtpc_messageMediaEmpty) {
HistoryItem *item = App::histItemById(d.vid.v);
if (item) {
item->setMedia(d.vmedia);
}
if (!item) {
item = App::histItemById(d.vid.v);
}
if (item) {
item->setMedia(d.vmedia);
}
} break;
};
@ -3434,6 +3472,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
}
App::historyUnregRandom(d.vrandom_id.v);
}
App::historyUnregSentText(d.vrandom_id.v);
} break;
case mtpc_updateReadMessagesContents: {