mirror of https://github.com/procxx/kepka.git
serverside links parsing used
This commit is contained in:
parent
8dff205949
commit
4afa1aace0
|
@ -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];
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 ¤tText);
|
void initMediaFromText(QString ¤tText);
|
||||||
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);
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
Loading…
Reference in New Issue