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; typedef QMap<uint64, MsgId> RandomData;
RandomData randomData; RandomData randomData;
typedef QMap<uint64, QString> SentTextData;
SentTextData sentTextData;
HistoryItem *hoveredItem = 0, *pressedItem = 0, *hoveredLinkItem = 0, *pressedLinkItem = 0, *contextItem = 0, *mousedItem = 0; HistoryItem *hoveredItem = 0, *pressedItem = 0, *hoveredLinkItem = 0, *pressedLinkItem = 0, *contextItem = 0, *mousedItem = 0;
QPixmap *sprite = 0, *emojis = 0, *emojisLarge = 0; QPixmap *sprite = 0, *emojis = 0, *emojisLarge = 0;
@ -1681,6 +1684,7 @@ namespace App {
void historyClearItems() { void historyClearItems() {
historyClearMsgs(); historyClearMsgs();
randomData.clear(); randomData.clear();
sentTextData.clear();
mutedPeers.clear(); mutedPeers.clear();
updatedPeers.clear(); updatedPeers.clear();
cSetSavedPeers(SavedPeers()); cSetSavedPeers(SavedPeers());
@ -1756,6 +1760,18 @@ namespace App {
return 0; 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) { 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(); int32 r = radius * cIntRetinaFactor(), s = st::msgShadow * cIntRetinaFactor();
QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4]; 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 historyRegRandom(uint64 randomId, MsgId itemId);
void historyUnregRandom(uint64 randomId); void historyUnregRandom(uint64 randomId);
MsgId histItemByRandom(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); void hoveredItem(HistoryItem *item);
HistoryItem *hoveredItem(); HistoryItem *hoveredItem();

View File

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

View File

@ -328,13 +328,13 @@ public:
} }
bool checkWaitedLink() { bool checkWaitedLink() {
if (waitingLink == linksEnd || ptr < waitingLink->from || links.size() >= 0x7FFF) { if (waitingLink == linksEnd || ptr < start + waitingLink->offset || links.size() >= 0x7FFF) {
return true; return true;
} }
createBlock(); createBlock();
QString lnkUrl = QString(waitingLink->from, waitingLink->len), lnkText; QString lnkUrl = QString(start + waitingLink->offset, waitingLink->length), lnkText;
int32 fullDisplayed; int32 fullDisplayed;
getLinkData(lnkUrl, lnkText, fullDisplayed); getLinkData(lnkUrl, lnkText, fullDisplayed);
@ -342,7 +342,7 @@ public:
lnkIndex = 0x8000 + links.size(); lnkIndex = 0x8000 + links.size();
_t->_text += lnkText; _t->_text += lnkText;
ptr = waitingLink->from + waitingLink->len; ptr = start + waitingLink->offset + waitingLink->length;
createBlock(); createBlock();
++waitingLink; ++waitingLink;
@ -519,8 +519,31 @@ public:
emoji = e; emoji = e;
} }
TextParser(Text *t, const QString &text, const TextParseOptions &options) : _t(t), src(text), TextParser(Text *t, const QString &text, const TextParseOptions &options) : _t(t),
rich(options.flags & TextParseRichText), multiline(options.flags & TextParseMultiline), maxLnkIndex(0), flags(0), lnkIndex(0), stopAfterWidth(QFIXED_MAX) { 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; int flags = options.flags;
if (options.maxw > 0 && options.maxh > 0) { if (options.maxw > 0 && options.maxh > 0) {
stopAfterWidth = ((options.maxh / _t->_font->height) + 1) * options.maxw; stopAfterWidth = ((options.maxh / _t->_font->height) + 1) * options.maxw;
@ -529,10 +552,6 @@ public:
start = src.constData(); start = src.constData();
end = start + src.size(); end = start + src.size();
if (options.flags & TextParseLinks) {
lnkRanges = textParseLinks(src, options.flags, rich);
}
while (start != end && chIsTrimmed(*start, rich)) { while (start != end && chIsTrimmed(*start, rich)) {
++start; ++start;
} }
@ -552,8 +571,8 @@ public:
ch = chInt = 0; ch = chInt = 0;
lastSkipped = false; lastSkipped = false;
lastSpace = true; lastSpace = true;
waitingLink = lnkRanges.isEmpty() ? 0 : lnkRanges.constData(); waitingLink = lnkRanges.cbegin();
linksEnd = lnkRanges.isEmpty() ? 0 : waitingLink + lnkRanges.size(); linksEnd = lnkRanges.cend();
for (ptr = start; ptr <= end; ++ptr) { for (ptr = start; ptr <= end; ++ptr) {
if (!checkWaitedLink()) { if (!checkWaitedLink()) {
break; break;
@ -615,8 +634,8 @@ private:
const QChar *start, *end, *ptr; const QChar *start, *end, *ptr;
bool rich, multiline; bool rich, multiline;
LinkRanges lnkRanges; LinksInText lnkRanges;
const LinkRange *waitingLink, *linksEnd; LinksInText::const_iterator waitingLink, linksEnd;
struct TextLinkData { struct TextLinkData {
TextLinkData(const QString &url = QString(), int32 fullDisplayed = 1) : url(url), fullDisplayed(fullDisplayed) { 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) { void Text::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) {
NewlineBlock *lastNewline = 0; NewlineBlock *lastNewline = 0;
_maxWidth = _minHeight = 0;
int32 lineHeight = 0; int32 lineHeight = 0;
int32 result = 0, lastNewlineStart = 0; int32 result = 0, lastNewlineStart = 0;
QFixed _width = 0, last_rBearing = 0, last_rPadding = 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) { 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(); 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() { void Text::removeSkipBlock() {
if (!_blocks.isEmpty() && _blocks.back()->type() == TextBlockSkip) {
_text.resize(_blocks.back()->from());
_blocks.pop_back();
recountNaturalSize(false);
}
} }
LinksInText Text::calcLinksInText() const { 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 { int32 Text::countHeight(int32 w) const {
@ -4058,16 +4148,16 @@ QString textSearchKey(const QString &text) {
bool textSplit(QString &sendingText, QString &leftText, int32 limit) { bool textSplit(QString &sendingText, QString &leftText, int32 limit) {
if (leftText.isEmpty() || !limit) return false; if (leftText.isEmpty() || !limit) return false;
LinkRanges lnkRanges = textParseLinks(leftText, TextParseLinks | TextParseMentions | TextParseHashtags); LinksInText links = textParseLinks(leftText, TextParseLinks | TextParseMentions | TextParseHashtags);
int32 currentLink = 0, lnkCount = lnkRanges.size(); int32 currentLink = 0, lnkCount = links.size();
int32 s = 0, half = limit / 2, goodLevel = 0; 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) { 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; ++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 (s > half) {
if (inLink) { if (inLink) {
if (!goodLevel) good = ch; if (!goodLevel) good = ch;
@ -4125,8 +4215,8 @@ bool textSplit(QString &sendingText, QString &leftText, int32 limit) {
return true; return true;
} }
LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some code is duplicated in flattextarea.cpp! LinksInText textParseLinks(const QString &text, int32 flags, bool rich) { // some code is duplicated in flattextarea.cpp!
LinkRanges lnkRanges; LinksInText result;
bool withHashtags = (flags & TextParseHashtags); bool withHashtags = (flags & TextParseHashtags);
bool withMentions = (flags & TextParseMentions); 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 mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch();
QRegularExpressionMatch mBotCommand = withBotCommands ? _reBotCommand.match(text, 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, int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
domainEnd = mDomain.hasMatch() ? mDomain.capturedEnd() : INT_MAX, domainEnd = mDomain.hasMatch() ? mDomain.capturedEnd() : INT_MAX,
explicitDomainOffset = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedStart() : INT_MAX, explicitDomainOffset = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedStart() : INT_MAX,
@ -4214,9 +4305,9 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
continue; continue;
} }
} }
lnkType = LinkInTextMention;
link.from = start + mentionOffset; lnkOffset = mentionOffset;
link.len = start + mentionEnd - link.from; lnkLength = mentionEnd - mentionOffset;
} else if (hashtagOffset < domainOffset && hashtagOffset < botCommandOffset) { } else if (hashtagOffset < domainOffset && hashtagOffset < botCommandOffset) {
if (hashtagOffset > nextCmd) { if (hashtagOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len); 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; lnkType = LinkInTextHashtag;
link.len = start + hashtagEnd - link.from; lnkOffset = hashtagOffset;
lnkLength = hashtagEnd - hashtagOffset;
} else if (botCommandOffset < domainOffset) { } else if (botCommandOffset < domainOffset) {
if (botCommandOffset > nextCmd) { if (botCommandOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len); 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; lnkType = LinkInTextBotCommand;
link.len = start + botCommandEnd - link.from; lnkOffset = botCommandOffset;
lnkLength = botCommandEnd - botCommandOffset;
} else { } else {
if (domainOffset > nextCmd) { if (domainOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len); 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) { if (mailOffset < offset) {
mailOffset = offset; mailOffset = offset;
} }
link.from = start + mailOffset; lnkType = LinkInTextEmail;
link.len = domainEnd - mailOffset; lnkOffset = mailOffset;
lnkLength = domainEnd - mailOffset;
} }
} }
if (!link.from || !link.len) { if (lnkType == LinkInTextUrl && !lnkLength) {
if (!isProtocolValid || !isTopDomainValid) { if (!isProtocolValid || !isTopDomainValid) {
matchOffset = domainEnd; matchOffset = domainEnd;
continue; continue;
} }
link.from = start + domainOffset; lnkOffset = domainOffset;
QStack<const QChar*> parenth; QStack<const QChar*> parenth;
const QChar *domainEnd = start + mDomain.capturedEnd(), *p = domainEnd; const QChar *domainEnd = start + mDomain.capturedEnd(), *p = domainEnd;
@ -4306,15 +4400,15 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
continue; 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) { void emojiDraw(QPainter &p, EmojiPtr e, int x, int y) {

View File

@ -39,14 +39,57 @@ enum {
TextInstagramHashtags = 0x200, TextInstagramHashtags = 0x200,
}; };
struct LinkRange { enum LinkInTextType {
LinkRange() : from(0), len(0) { LinkInTextUrl,
} LinkInTextCustomUrl,
const QChar *from; LinkInTextEmail,
int32 len; LinkInTextHashtag,
LinkInTextMention,
LinkInTextBotCommand,
}; };
typedef QVector<LinkRange> LinkRanges; struct LinkInText {
LinkRanges textParseLinks(const QString &text, int32 flags, bool rich = false); 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" #include "gui/emoji_config.h"
@ -464,23 +507,6 @@ enum TextSelectType {
typedef QPair<QString, QString> TextCustomTag; // open str and close str typedef QPair<QString, QString> TextCustomTag; // open str and close str
typedef QMap<QChar, TextCustomTag> TextCustomTagsMap; 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 { class Text {
public: public:
@ -492,7 +518,7 @@ public:
int32 countHeight(int32 width) const; int32 countHeight(int32 width) const;
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions); 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 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); void setLink(uint16 lnkIndex, const TextLinkPtr &lnk);
bool hasLinks() const; bool hasLinks() const;
@ -500,7 +526,7 @@ public:
bool hasSkipBlock() const { bool hasSkipBlock() const {
return _blocks.isEmpty() ? false : _blocks.back()->type() == TextBlockSkip; return _blocks.isEmpty() ? false : _blocks.back()->type() == TextBlockSkip;
} }
void setSkipBlock(int32 width); void setSkipBlock(int32 width, int32 height);
void removeSkipBlock(); void removeSkipBlock();
LinksInText calcLinksInText() const; LinksInText calcLinksInText() const;

View File

@ -110,14 +110,16 @@ namespace {
return item ? item->toHistoryForwarded() : 0; return item ? item->toHistoryForwarded() : 0;
} }
inline const TextParseOptions &itemTextParseOptions(HistoryItem *item) { inline const TextParseOptions &itemTextParseOptions(HistoryItem *item) {
History *h = item->history(); return itemTextParseOptions(item->history(), item->from());
UserData *f = 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)) { if ((!h->peer->chat && h->peer->asUser()->botInfo) || (!f->chat && f->asUser()->botInfo) || (h->peer->chat && h->peer->asChat()->botStatus >= 0)) {
return _historyBotOptions; return _historyBotOptions;
} }
return _historyTextOptions; return _historyTextOptions;
} }
}
void historyInit() { void historyInit() {
_initTextOptions(); _initTextOptions();
@ -1035,7 +1037,7 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
int32 addToH = 0, skip = 0; int32 addToH = 0, skip = 0;
if (!isEmpty()) { if (!isEmpty()) {
addToH = -front()->height; if (width) addToH = -front()->height;
pop_front(); // remove date block pop_front(); // remove date block
} }
HistoryItem *till = isEmpty() ? 0 : front()->front(), *prev = 0; 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()) { if (prev && prev->date.date() != adding->date.date()) {
HistoryItem *dayItem = createDayServiceMsg(this, block, adding->date); HistoryItem *dayItem = createDayServiceMsg(this, block, adding->date);
block->push_back(dayItem); block->push_back(dayItem);
if (width) {
dayItem->y = block->height; dayItem->y = block->height;
block->height += dayItem->resize(width); block->height += dayItem->resize(width);
} }
}
block->push_back(adding); block->push_back(adding);
if (width) {
adding->y = block->height; adding->y = block->height;
block->height += adding->resize(width); block->height += adding->resize(width);
}
setMsgCount(msgCount + 1); setMsgCount(msgCount + 1);
prev = adding; prev = adding;
} }
@ -1063,9 +1069,11 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
if (till && prev && prev->date.date() != till->date.date()) { if (till && prev && prev->date.date() != till->date.date()) {
HistoryItem *dayItem = createDayServiceMsg(this, block, till->date); HistoryItem *dayItem = createDayServiceMsg(this, block, till->date);
block->push_back(dayItem); block->push_back(dayItem);
if (width) {
dayItem->y = block->height; dayItem->y = block->height;
block->height += dayItem->resize(width); block->height += dayItem->resize(width);
} }
}
if (block->size()) { if (block->size()) {
if (loadedAtBottom() && wasMsgCount < unreadCount && msgCount >= unreadCount) { if (loadedAtBottom() && wasMsgCount < unreadCount && msgCount >= unreadCount) {
for (int32 i = block->size(); i > 0; --i) { for (int32 i = block->size(); i > 0; --i) {
@ -1079,8 +1087,10 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
} }
} }
push_front(block); push_front(block);
if (width) {
addToH += block->height; addToH += block->height;
++skip; ++skip;
}
if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors
int32 mask = 0; int32 mask = 0;
@ -1167,16 +1177,18 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
HistoryBlock *dateBlock = new HistoryBlock(this); HistoryBlock *dateBlock = new HistoryBlock(this);
HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, front()->front()->date); HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, front()->front()->date);
dateBlock->push_back(dayItem); dateBlock->push_back(dayItem);
if (width) {
int32 dh = dayItem->resize(width); int32 dh = dayItem->resize(width);
dateBlock->height = dh; dateBlock->height = dh;
if (skip) { if (skip) {
front()->y += dh; front()->y += dh;
} }
push_front(dateBlock); // date block
addToH += dh; addToH += dh;
++skip; ++skip;
} }
if (addToH) { push_front(dateBlock); // date block
}
if (width && addToH) {
for (iterator i = begin(), e = end(); i != e; ++i) { for (iterator i = begin(), e = end(); i != e; ++i) {
if (skip) { if (skip) {
--skip; --skip;
@ -5026,7 +5038,7 @@ HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPD
QString text(textClean(qs(msg.vmessage))); QString text(textClean(qs(msg.vmessage)));
initTime(); initTime();
initMedia(msg.vmedia, text); 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) : 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); QString text(msg);
initTime(); initTime();
initMedia(media, text); 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) : 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 = fromMedia->clone();
_media->regItem(this); _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) : 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(); initTime();
initMediaFromDocument(doc); initMediaFromDocument(doc);
initDimensions(QString(), LinksInText()); setText(QString(), LinksInText());
} }
void HistoryMessage::initTime() { void HistoryMessage::initTime() {
@ -5154,22 +5166,6 @@ void HistoryMessage::initMediaFromDocument(DocumentData *doc) {
_media->regItem(this); _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) { void HistoryMessage::initDimensions(const HistoryItem *parent) {
if (justMedia()) { if (justMedia()) {
_media->initDimensions(this); _media->initDimensions(this);
@ -5182,20 +5178,14 @@ void HistoryMessage::initDimensions(const HistoryItem *parent) {
if (_media) { if (_media) {
_media->initDimensions(this); _media->initDimensions(this);
if (_media->isDisplayed() && _text.hasSkipBlock()) { if (_media->isDisplayed() && _text.hasSkipBlock()) {
QString was = HistoryMessage::selectedText(FullItemSel); _text.removeSkipBlock();
if (!was.isEmpty()) {
_text.setText(st::msgFont, was, itemTextParseOptions(this)); // without date skip
_textWidth = 0; _textWidth = 0;
_textHeight = 0; _textHeight = 0;
}
} else if (!_media->isDisplayed() && !_text.hasSkipBlock()) { } else if (!_media->isDisplayed() && !_text.hasSkipBlock()) {
QString was = HistoryMessage::selectedText(FullItemSel); _text.setSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y());
if (!was.isEmpty()) {
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), itemTextParseOptions(this)); // without date skip
_textWidth = 0; _textWidth = 0;
_textHeight = 0; _textHeight = 0;
} }
}
if (_media->isDisplayed()) { if (_media->isDisplayed()) {
int32 maxw = _media->maxWidth() + st::msgPadding.left() + st::msgPadding.right(); int32 maxw = _media->maxWidth() + st::msgPadding.left() + st::msgPadding.right();
if (maxw > _maxw) _maxw = maxw; if (maxw > _maxw) _maxw = maxw;
@ -5226,6 +5216,10 @@ QString HistoryMessage::selectedText(uint32 selection) const {
return _text.original(selectedFrom, selectedTo); return _text.original(selectedFrom, selectedTo);
} }
LinksInText HistoryMessage::textLinks() const {
return _text.calcLinksInText();
}
QString HistoryMessage::inDialogsText() const { QString HistoryMessage::inDialogsText() const {
QString result = _media ? _media->inDialogsText() : QString(); QString result = _media ? _media->inDialogsText() : QString();
return result.isEmpty() ? _text.original(0, 0xFFFF, false) : result; 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) { void HistoryMessage::setMedia(const MTPmessageMedia &media) {
if ((!_media || _media->isImageLink()) && media.type() == mtpc_messageMediaEmpty) return; if ((!_media || _media->isImageLink()) && media.type() == mtpc_messageMediaEmpty) return;
bool mediaWasDisplayed = false; bool mediaWasDisplayed = false;
if (_media) { if (_media) {
mediaWasDisplayed = _media->isDisplayed(); mediaWasDisplayed = _media->isDisplayed();
@ -5246,24 +5241,38 @@ void HistoryMessage::setMedia(const MTPmessageMedia &media) {
QString t; QString t;
initMedia(media, t); initMedia(media, t);
if (_media && _media->isDisplayed() && !mediaWasDisplayed) { if (_media && _media->isDisplayed() && !mediaWasDisplayed) {
QString was = HistoryMessage::selectedText(FullItemSel); _text.removeSkipBlock();
if (!was.isEmpty()) {
_text.setText(st::msgFont, was, itemTextParseOptions(this)); // without date skip
_textWidth = 0; _textWidth = 0;
_textHeight = 0; _textHeight = 0;
}
} else if (mediaWasDisplayed && (!_media || !_media->isDisplayed())) { } else if (mediaWasDisplayed && (!_media || !_media->isDisplayed())) {
QString was = HistoryMessage::selectedText(FullItemSel); _text.setSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y());
if (!was.isEmpty()) {
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), itemTextParseOptions(this)); // without date skip
_textWidth = 0; _textWidth = 0;
_textHeight = 0; _textHeight = 0;
} }
}
initDimensions(0); initDimensions(0);
if (App::main()) App::main()->itemResized(this); 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 { void HistoryMessage::draw(QPainter &p, uint32 selection) const {
textstyleSet(&(out() ? st::outTextStyle : st::inTextStyle)); 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) { int32 HistoryMessage::resize(int32 width, bool dontRecountText, const HistoryItem *parent) {
if (width < st::msgMinWidth) return _height;
width -= st::msgMargin.left() + st::msgMargin.right(); width -= st::msgMargin.left() + st::msgMargin.right();
if (justMedia()) { if (justMedia()) {
_height = _media->resize(width, dontRecountText, this); _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)) , fwdDate(::date(msg.vfwd_date))
, fwdFrom(App::user(msg.vfwd_from_id.v)) , fwdFrom(App::user(msg.vfwd_from_id.v))
, fwdFromVersion(fwdFrom->nameVersion) , fwdFromVersion(fwdFrom->nameVersion)
@ -5582,7 +5593,7 @@ HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const
fwdNameUpdated(); 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()) , fwdDate(msg->dateForwarded())
, fwdFrom(msg->fromForwarded()) , fwdFrom(msg->fromForwarded())
, fwdFromVersion(fwdFrom->nameVersion) , 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); 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) , replyToMsgId(msg.vreply_to_msg_id.v)
, replyToMsg(0) , replyToMsg(0)
, replyToVersion(0) , replyToVersion(0)

View File

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

View File

@ -982,6 +982,12 @@ DialogsIndexed &MainWidget::dialogsList() {
return dialogs.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) { void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId replyTo, WebPageId webPageId) {
saveRecentHashtags(text); saveRecentHashtags(text);
QString sendingText, leftText = text; QString sendingText, leftText = text;
@ -990,7 +996,10 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
MsgId newId = clientMsgId(); MsgId newId = clientMsgId();
uint64 randomId = MTP::nonce<uint64>(); uint64 randomId = MTP::nonce<uint64>();
sendingText = cleanMessage(sendingText);
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
App::historyRegSentText(randomId, sendingText);
MTPstring msgText(MTP_string(sendingText)); MTPstring msgText(MTP_string(sendingText));
int32 flags = newMessageFlags(hist->peer); // unread, out 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); WebPageData *page = App::webPage(webPageId);
media = MTP_messageMediaWebPage(MTP_webPagePending(MTP_long(page->id), MTP_int(page->pendingTill))); 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->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); finishForwarding(hist);
@ -2222,7 +2232,20 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
case mtpc_messages_sentMessage: { case mtpc_messages_sentMessage: {
const MTPDmessages_sentMessage &d(result.c_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 (updInited) {
if (!updPtsUpdated(d.vpts.v, d.vpts_count.v)) { 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) { if (item) {
item->setMedia(d.vmedia); item->setMedia(d.vmedia);
} }
@ -2240,7 +2265,20 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
case mtpc_messages_sentMessageLink: { case mtpc_messages_sentMessageLink: {
const MTPDmessages_sentMessageLink &d(result.c_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 (updInited) {
if (!updPtsUpdated(d.vpts.v, d.vpts_count.v)) { if (!updPtsUpdated(d.vpts.v, d.vpts_count.v)) {
@ -2250,12 +2288,12 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage
} }
App::feedUserLinks(d.vlinks); App::feedUserLinks(d.vlinks);
if (d.vmedia.type() != mtpc_messageMediaEmpty) { if (!item) {
HistoryItem *item = App::histItemById(d.vid.v); item = App::histItemById(d.vid.v);
}
if (item) { if (item) {
item->setMedia(d.vmedia); item->setMedia(d.vmedia);
} }
}
} break; } break;
}; };
} }
@ -3434,6 +3472,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
} }
App::historyUnregRandom(d.vrandom_id.v); App::historyUnregRandom(d.vrandom_id.v);
} }
App::historyUnregSentText(d.vrandom_id.v);
} break; } break;
case mtpc_updateReadMessagesContents: { case mtpc_updateReadMessagesContents: {