From c606d6b4598bb2413995aa12cb7f6e6c03c2ff26 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Sun, 25 Oct 2015 18:08:45 +0100
Subject: [PATCH] updating openal build, new code of getting original text and
 entities from Text, not tested yet

---
 MSVC.md                                  |  16 +-
 Telegram/SourceFiles/gui/contextmenu.cpp |   2 +-
 Telegram/SourceFiles/gui/text.cpp        | 224 +++++++++++------------
 Telegram/SourceFiles/gui/text.h          |  16 +-
 Telegram/SourceFiles/history.cpp         |  44 ++---
 Telegram/SourceFiles/history.h           |  13 +-
 Telegram/SourceFiles/mediaview.cpp       |   2 +-
 Telegram/SourceFiles/overviewwidget.cpp  |  18 +-
 8 files changed, 169 insertions(+), 166 deletions(-)

diff --git a/MSVC.md b/MSVC.md
index 3a510745a..94b5fab46 100644
--- a/MSVC.md
+++ b/MSVC.md
@@ -92,21 +92,17 @@ or download in ZIP and extract to **D:\TBuild\Libraries\**, rename **libexif-0.6
 Open **VS2015 x86 Native Tools Command Prompt.bat** (should be in **\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\Shortcuts\\** folder), go to **D:\\TBuild\\Libraries** and run
 
     git clone git://repo.or.cz/openal-soft.git
-    git checkout 9b6b084d
+    git checkout 298fdb22
     git apply ./../../tdesktop/Telegram/_openal_patch.diff
 
 #####Building library
 
 * Install [CMake](http://www.cmake.org/)
-* Open **VS2015 x86 Native Tools Command Prompt.bat** (should be in **\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\Shortcuts\\** folder) and go to **D:\TBuild\Libraries\openal-soft\build\**
-* Run **cmake -G "Visual Studio 14 2015" -D LIBTYPE:STRING=STATIC ..**
-* Open in VS2015 **D:\TBuild\Libraries\openal-soft\build\OpenAL.sln**
-* For **Debug** configuration
-  * OpenAL32 Properties > C/C++ > Code Generation > Runtime Library = **Multi-threaded Debug (/MTd)** – **OK**
-  * common Properties > C/C++ > Code Generation > Runtime Library = **Multi-threaded Debug (/MTd)** – **OK**
-* For **Release** configuration
-  * OpenAL32 Properties > C/C++ > Code Generation > Runtime Library = **Multi-threaded (/MT)** – **OK**
-  * common Properties > C/C++ > Code Generation > Runtime Library = **Multi-threaded (/MT)** – **OK**
+* Open **VS2015 x86 Native Tools Command Prompt.bat** (should be in **\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\Shortcuts\\** folder), go to **D:\TBuild\Libraries\openal-soft\build\** and run
+
+    cmake -G "Visual Studio 14 2015" -D LIBTYPE:STRING=STATIC -D FORCE_STATIC_VCRT:STRING=ON ..
+
+* Open in VS2015 **D:\TBuild\Libraries\openal-soft\build\OpenAL.sln** and build Debug and Release configurations
 
 ####Opus codec
 
diff --git a/Telegram/SourceFiles/gui/contextmenu.cpp b/Telegram/SourceFiles/gui/contextmenu.cpp
index 4af8e99c2..9a525bb8a 100644
--- a/Telegram/SourceFiles/gui/contextmenu.cpp
+++ b/Telegram/SourceFiles/gui/contextmenu.cpp
@@ -301,8 +301,8 @@ PopupMenu::PopupMenu(const style::PopupMenu &st) : TWidget(0)
 QAction *PopupMenu::addAction(const QString &text, const QObject *receiver, const char* member) {
 	QAction *a = 0;
 	_actions.push_back(a = new QAction(text, this));
-	connect(a, SIGNAL(triggered(bool)), receiver, member);
 	connect(a, SIGNAL(triggered(bool)), this, SLOT(hideStart()));
+	connect(a, SIGNAL(triggered(bool)), receiver, member);
 	connect(a, SIGNAL(changed()), this, SLOT(actionChanged()));
 
 	int32 w = _st.widthMin, mw = _st.widthMax;
diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp
index 814d37cf5..bcd702205 100644
--- a/Telegram/SourceFiles/gui/text.cpp
+++ b/Telegram/SourceFiles/gui/text.cpp
@@ -378,6 +378,9 @@ public:
 			}
 			removeFlags.erase(removeFlags.begin());
 		}
+		while (waitingEntity != entitiesEnd && start + waitingEntity->offset + waitingEntity->length <= ptr) {
+			++waitingEntity;
+		}
 		if (waitingEntity == entitiesEnd || ptr < start + waitingEntity->offset) {
 			return;
 		}
@@ -2664,111 +2667,6 @@ void Text::removeSkipBlock() {
 	}
 }
 
-EntitiesInText Text::calcEntitiesInText() const {
-	EntitiesInText result;
-	int32 lnkFrom = 0, lnkIndex = 0, offset = 0;
-	int32 flags = 0, italicFrom = 0, italicOffset = 0, boldFrom = 0, boldOffset = 0;
-	int32 codeFrom = 0, codeOffset = 0, preFrom = 0, preOffset = 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();
-		int32 blockFlags = (i == e) ? 0 : (*i)->flags();
-		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();
-						italicOffset += r.size();
-						boldOffset += r.size();
-						codeOffset += r.size();
-						preOffset += 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(EntityInText(EntityInTextMention, offset, url.size()));
-							} else if (url.at(0) == '#') {
-								result.push_back(EntityInText(EntityInTextHashtag, offset, url.size()));
-							} else if (url.at(0) == '/') {
-								result.push_back(EntityInText(EntityInTextBotCommand, offset, url.size()));
-							} else if (url.indexOf('@') > 0 && url.indexOf('/') <= 0) {
-								result.push_back(EntityInText(EntityInTextEmail, offset, url.size()));
-							} else {
-								result.push_back(EntityInText(EntityInTextUrl, offset, url.size()));
-							}
-							offset += url.size();
-							italicOffset += url.size();
-							boldOffset += url.size();
-							codeOffset += url.size();
-							preOffset += url.size();
-						} else {
-							result.push_back(EntityInText(EntityInTextCustomUrl, offset, r.size(), url));
-							offset += r.size();
-							italicOffset += r.size();
-							boldOffset += r.size();
-							codeOffset += r.size();
-							preOffset += r.size();
-						}
-					}
-				}
-			}
-			lnkIndex = blockLnkIndex;
-			lnkFrom = blockFrom;
-		} else if (blockFlags != flags) {
-			if ((blockFlags & TextBlockFItalic) && !(flags & TextBlockFItalic)) {
-				italicFrom = blockFrom;
-			} else if ((flags & TextBlockFItalic) && !(blockFlags & TextBlockFItalic)) {
-				result.push_back(EntityInText(EntityInTextItalic, italicOffset, blockFrom - italicFrom));
-			}
-			if ((blockFlags & TextBlockFSemibold) && !(flags & TextBlockFSemibold)) {
-				boldFrom = blockFrom;
-			} else if ((flags & TextBlockFSemibold) && !(blockFlags & TextBlockFSemibold)) {
-				result.push_back(EntityInText(EntityInTextBold, boldOffset, blockFrom - boldFrom));
-			}
-			if ((blockFlags & TextBlockFCode) && !(flags & TextBlockFCode)) {
-				codeFrom = blockFrom;
-			} else if ((flags & TextBlockFCode) && !(blockFlags & TextBlockFCode)) {
-				result.push_back(EntityInText(EntityInTextCode, codeOffset, blockFrom - codeFrom));
-			}
-			if ((blockFlags & TextBlockFPre) && !(flags & TextBlockFPre)) {
-				preFrom = blockFrom;
-			} else if ((flags & TextBlockFPre) && !(blockFlags & TextBlockFPre)) {
-				result.push_back(EntityInText(EntityInTextPre, preOffset, blockFrom - preFrom));
-			}
-			flags = blockFlags;
-		}
-		if (i == e) break;
-
-		TextBlockType type = (*i)->type();
-		if (type == TextBlockTSkip) continue;
-
-		int32 rangeFrom = (*i)->from(), rangeTo = uint16((*i)->from() + TextPainter::_blockLength(this, i, e));
-		if (rangeTo > rangeFrom) {
-			if (!blockLnkIndex) {
-				offset += rangeTo - rangeFrom;
-			}
-			if (!(blockFlags & TextBlockFItalic)) {
-				italicOffset += rangeTo - rangeFrom;
-			}
-			if (!(blockFlags & TextBlockFSemibold)) {
-				boldOffset += rangeTo - rangeFrom;
-			}
-			if (!(blockFlags & TextBlockFCode)) {
-				codeOffset += rangeTo - rangeFrom;
-			}
-			if (!(blockFlags & TextBlockFPre)) {
-				preOffset += rangeTo - rangeFrom;
-			}
-		}
-	}
-	return result;
-}
-
 int32 Text::countHeight(int32 w) const {
 	QFixed width = w;
 	if (width < _minResizeWidth) width = _minResizeWidth;
@@ -2942,7 +2840,7 @@ uint32 Text::adjustSelection(uint16 from, uint16 to, TextSelectType selectType)
 	return (from << 16) | to;
 }
 
-QString Text::original(uint16 selectedFrom, uint16 selectedTo, bool expandLinks) const {
+QString Text::original(uint16 selectedFrom, uint16 selectedTo, ExpandLinksMode mode) const {
 	QString result;
 	result.reserve(_text.size());
 
@@ -2959,14 +2857,19 @@ QString Text::original(uint16 selectedFrom, uint16 selectedTo, bool expandLinks)
 
 				if (rangeTo > rangeFrom) {
 					QStringRef r = _text.midRef(rangeFrom, rangeTo - rangeFrom);
-					if (url.isEmpty() || !expandLinks || lnkFrom != rangeFrom || blockFrom != rangeTo) {
+					if (url.isEmpty() || mode == ExpandLinksNone || lnkFrom != rangeFrom || blockFrom != rangeTo) {
 						result += r;
 					} 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
+						QString displayed = (u.isValid() ? u.toDisplayString() : url);
+						bool shortened = (r.size() > 3) && (_text.midRef(lnkFrom, r.size() - 3) == displayed.midRef(0, r.size() - 3));
+						bool same = (r == displayed.midRef(0, r.size())) || (r == url.midRef(0, r.size()));
+						if (same || shortened) {
 							result += url;
-						} else {
+						} else if (mode == ExpandLinksAll) {
 							result.append(r).append(qsl(" ( ")).append(url).append(qsl(" )"));
+						} else {
+							result += r;
 						}
 					}
 				}
@@ -2989,6 +2892,94 @@ QString Text::original(uint16 selectedFrom, uint16 selectedTo, bool expandLinks)
 	return result;
 }
 
+EntitiesInText Text::originalEntities() const {
+	EntitiesInText result;
+
+	int32 originalLength = 0, lnkStart = 0, italicStart = 0, boldStart = 0, codeStart = 0, preStart = 0;
+	int32 lnkFrom = 0, lnkIndex = 0, flags = 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();
+		int32 blockFlags = (i == e) ? 0 : (*i)->flags();
+		if (blockFlags != flags) {
+			if ((flags & TextBlockFItalic) && !(blockFlags & TextBlockFItalic)) { // write italic
+				result.push_back(EntityInText(EntityInTextItalic, italicStart, originalLength - italicStart));
+			} else if ((blockFlags & TextBlockFItalic) && !(flags & TextBlockFItalic)) {
+				italicStart = originalLength;
+			}
+			if ((flags & TextBlockFSemibold) && !(blockFlags & TextBlockFSemibold)) {
+				result.push_back(EntityInText(EntityInTextBold, boldStart, originalLength - boldStart));
+			} else if ((blockFlags & TextBlockFSemibold) && !(flags & TextBlockFSemibold)) {
+				boldStart = originalLength;
+			}
+			if ((flags & TextBlockFCode) && !(blockFlags & TextBlockFCode)) {
+				result.push_back(EntityInText(EntityInTextCode, codeStart, originalLength - codeStart));
+			} else if ((blockFlags & TextBlockFCode) && !(flags & TextBlockFCode)) {
+				codeStart = originalLength;
+			}
+			if ((flags & TextBlockFPre) && !(blockFlags & TextBlockFPre)) {
+				result.push_back(EntityInText(EntityInTextPre, preStart, originalLength - preStart));
+			} else if ((blockFlags & TextBlockFPre) && !(flags & TextBlockFPre)) {
+				preStart = originalLength;
+			}
+			flags = blockFlags;
+		}
+		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()) {
+						originalLength += r.size();
+					} else {
+						QUrl u(url);
+						QString displayed = (u.isValid() ? u.toDisplayString() : url);
+						bool shortened = (r.size() > 3) && (_text.midRef(lnkFrom, r.size() - 3) == displayed.midRef(0, r.size() - 3));
+						bool same = (r == displayed.midRef(0, r.size())) || (r == url.midRef(0, r.size()));
+						if (same || shortened) {
+							originalLength += url.size();
+							if (url.at(0) == '@') {
+								result.push_back(EntityInText(EntityInTextMention, lnkStart, originalLength - lnkStart));
+							} else if (url.at(0) == '#') {
+								result.push_back(EntityInText(EntityInTextHashtag, lnkStart, originalLength - lnkStart));
+							} else if (url.at(0) == '/') {
+								result.push_back(EntityInText(EntityInTextBotCommand, lnkStart, originalLength - lnkStart));
+							} else if (url.indexOf('@') > 0 && url.indexOf('/') <= 0) {
+								result.push_back(EntityInText(EntityInTextEmail, lnkStart, originalLength - lnkStart));
+							} else {
+								result.push_back(EntityInText(EntityInTextUrl, lnkStart, originalLength - lnkStart));
+							}
+						} else {
+							originalLength += r.size();
+							result.push_back(EntityInText(EntityInTextCustomUrl, lnkStart, originalLength - lnkStart, url));
+						}
+					}
+				}
+			}
+			lnkIndex = blockLnkIndex;
+			if (lnkIndex) {
+				lnkFrom = blockFrom;
+				lnkStart = originalLength;
+			}
+		}
+		if (i == e) break;
+
+		TextBlockType type = (*i)->type();
+		if (type == TextBlockTSkip) continue;
+
+		if (!blockLnkIndex) {
+			int32 rangeFrom = (*i)->from(), rangeTo = uint16((*i)->from() + TextPainter::_blockLength(this, i, e));
+			if (rangeTo > rangeFrom) {
+				originalLength += rangeTo - rangeFrom;
+			}
+		}
+	}
+	return result;
+}
+
 void Text::clean() {
 	for (TextBlocks::iterator i = _blocks.begin(), e = _blocks.end(); i != e; ++i) {
 		delete *i;
@@ -4822,12 +4813,19 @@ void replaceStringWithEntities(const QLatin1String &from, QChar to, QString &res
 				continue;
 			}
 		}
-		if (i != e) {
-			if (i->offset < nextOffset + len && i->offset + i->length > nextOffset) {
-				moveStringPart(start, length, offset, nextOffset - offset + len, entities);
-				continue;
+
+		bool skip = false;
+		for (; i != e; ++i) { // find and check next finishing entity
+			if (i->offset + i->length > nextOffset) {
+				skip = (i->offset < nextOffset + len);
+				break;
 			}
 		}
+		if (skip) {
+			moveStringPart(start, length, offset, nextOffset - offset + len, entities);
+			continue;
+		}
+
 		moveStringPart(start, length, offset, nextOffset - offset, entities);
 
 		*(start + length) = to;
diff --git a/Telegram/SourceFiles/gui/text.h b/Telegram/SourceFiles/gui/text.h
index a79169b12..885f65ebd 100644
--- a/Telegram/SourceFiles/gui/text.h
+++ b/Telegram/SourceFiles/gui/text.h
@@ -556,7 +556,6 @@ public:
 	}
 	void setSkipBlock(int32 width, int32 height);
 	void removeSkipBlock();
-	EntitiesInText calcEntitiesInText() const;
 
 	int32 maxWidth() const {
 		return _maxWidth.ceil().toInt();
@@ -602,7 +601,13 @@ public:
 	bool isNull() const {
 		return !_font;
 	}
-	QString original(uint16 selectedFrom = 0, uint16 selectedTo = 0xFFFF, bool expandLinks = true) const;
+	enum ExpandLinksMode {
+		ExpandLinksNone,
+		ExpandLinksShortened,
+		ExpandLinksAll,
+	};
+	QString original(uint16 selectedFrom = 0, uint16 selectedTo = 0xFFFF, ExpandLinksMode mode = ExpandLinksShortened) const;
+	EntitiesInText originalEntities() const;
 
 	bool lastDots(int32 dots, int32 maxdots = 3) { // hack for typing animation
 		if (_text.size() < maxdots) return false;
@@ -856,6 +861,7 @@ inline void cleanTextWithEntities(QString &result, EntitiesInText &entities) {
 }
 
 inline void trimTextWithEntities(QString &result, EntitiesInText &entities) {
+	bool foundNotTrimmed = false;
 	for (QChar *s = result.data(), *e = s + result.size(), *ch = e; ch != s;) { // rtrim
 		--ch;
 		if (!chIsTrimmed(*ch)) {
@@ -871,9 +877,15 @@ inline void trimTextWithEntities(QString &result, EntitiesInText &entities) {
 				}
 				result.resize(l);
 			}
+			foundNotTrimmed = true;
 			break;
 		}
 	}
+	if (!foundNotTrimmed) {
+		result.clear();
+		entities.clear();
+		return;
+	}
 
 	for (QChar *s = result.data(), *ch = s, *e = s + result.size(); ch != e; ++ch) { // ltrim
 		if (!chIsTrimmed(*ch)) {
diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp
index ab0e34da5..f664c8ecc 100644
--- a/Telegram/SourceFiles/history.cpp
+++ b/Telegram/SourceFiles/history.cpp
@@ -2919,15 +2919,11 @@ int32 HistoryPhoto::resize(int32 width, const HistoryItem *parent) {
 }
 
 const QString HistoryPhoto::inDialogsText() const {
-	return _caption.isEmpty() ? lang(lng_in_dlg_photo) : _caption.original(0, 0xFFFF, false);
+	return _caption.isEmpty() ? lang(lng_in_dlg_photo) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone);
 }
 
 const QString HistoryPhoto::inHistoryText() const {
-	return qsl("[ ") + lang(lng_in_dlg_photo) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF))) + qsl(" ]");
-}
-
-const Text &HistoryPhoto::captionForClone() const {
-	return _caption;
+	return qsl("[ ") + lang(lng_in_dlg_photo) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]");
 }
 
 bool HistoryPhoto::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
@@ -3253,11 +3249,11 @@ void HistoryVideo::unregItem(HistoryItem *item) {
 }
 
 const QString HistoryVideo::inDialogsText() const {
-	return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, false);
+	return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(0, 0xFFFF, Text::ExpandLinksNone);
 }
 
 const QString HistoryVideo::inHistoryText() const {
-	return qsl("[ ") + lang(lng_in_dlg_video) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF))) + qsl(" ]");
+	return qsl("[ ") + lang(lng_in_dlg_video) + (_caption.isEmpty() ? QString() : (qsl(", ") + _caption.original(0, 0xFFFF, Text::ExpandLinksAll))) + qsl(" ]");
 }
 
 bool HistoryVideo::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
@@ -4595,7 +4591,7 @@ const QString HistoryContact::inDialogsText() const {
 }
 
 const QString HistoryContact::inHistoryText() const {
-	return qsl("[ ") + lang(lng_in_dlg_contact) + qsl(" : ") + name.original(0, 0xFFFF, false) + qsl(", ") + phone + qsl(" ]");
+	return qsl("[ ") + lang(lng_in_dlg_contact) + qsl(" : ") + name.original() + qsl(", ") + phone + qsl(" ]");
 }
 
 bool HistoryContact::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const {
@@ -4653,7 +4649,7 @@ void HistoryContact::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32
 }
 
 HistoryMedia *HistoryContact::clone() const {
-	return new HistoryContact(userId, name.original(0, 0xFFFF, false), phone);
+	return new HistoryContact(userId, name.original(), phone);
 }
 
 void HistoryContact::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const {
@@ -6261,21 +6257,17 @@ bool HistoryMessage::uploading() const {
 
 QString HistoryMessage::selectedText(uint32 selection) const {
 	if (_media && selection == FullItemSel) {
-		QString text = _text.original(0, 0xFFFF), mediaText = _media->inHistoryText();
+		QString text = _text.original(0, 0xFFFF, Text::ExpandLinksAll), mediaText = _media->inHistoryText();
 		return text.isEmpty() ? mediaText : (mediaText.isEmpty() ? text : (text + ' ' + mediaText));
 	}
-	uint16 selectedFrom = (selection == FullItemSel) ? 0 : (selection >> 16) & 0xFFFF;
+	uint16 selectedFrom = (selection == FullItemSel) ? 0 : ((selection >> 16) & 0xFFFF);
 	uint16 selectedTo = (selection == FullItemSel) ? 0xFFFF : (selection & 0xFFFF);
-	return _text.original(selectedFrom, selectedTo);
-}
-
-EntitiesInText HistoryMessage::textEntities() const {
-	return _text.calcEntitiesInText();
+	return _text.original(selectedFrom, selectedTo, Text::ExpandLinksAll);
 }
 
 QString HistoryMessage::inDialogsText() const {
 	QString result = _media ? _media->inDialogsText() : QString();
-	return result.isEmpty() ? _text.original(0, 0xFFFF, false) : result;
+	return result.isEmpty() ? _text.original(0, 0xFFFF, Text::ExpandLinksNone) : result;
 }
 
 HistoryMedia *HistoryMessage::getMedia(bool inOverview) const {
@@ -6326,10 +6318,12 @@ void HistoryMessage::setText(const QString &text, const EntitiesInText &entities
 	}
 }
 
-void HistoryMessage::getTextWithEntities(QString &text, EntitiesInText &links) {
-	if (_text.isEmpty()) return;
-	links = _text.calcEntitiesInText();
-	text = _text.original();
+QString HistoryMessage::originalText() const {
+	return _text.isEmpty() ? QString() : _text.original();
+}
+
+EntitiesInText HistoryMessage::originalEntities() const {
+	return _text.isEmpty() ? EntitiesInText() : _text.originalEntities();
 }
 
 bool HistoryMessage::textHasLinks() {
@@ -6773,7 +6767,7 @@ HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const
 	fwdNameUpdated();
 }
 
-HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg) : HistoryMessage(history, block, id, newMessageFlags(history->peer) | (!history->peer->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage_flag_media_unread : 0), date, from, msg->justMedia() ? QString() : msg->HistoryMessage::selectedText(FullItemSel), msg->HistoryMessage::textEntities(), msg->getMedia())
+HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg) : HistoryMessage(history, block, id, newMessageFlags(history->peer) | (!history->peer->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage_flag_media_unread : 0), date, from, msg->HistoryMessage::originalText(), msg->HistoryMessage::originalEntities(), msg->getMedia())
 , fwdDate(msg->dateForwarded())
 , fwdFrom(msg->fromForwarded())
 , fwdFromVersion(fwdFrom->nameVersion)
@@ -7434,7 +7428,7 @@ QString HistoryServiceMsg::selectedText(uint32 selection) const {
 }
 
 QString HistoryServiceMsg::inDialogsText() const {
-	return _text.original(0, 0xFFFF, false);
+	return _text.original(0, 0xFFFF, Text::ExpandLinksNone);
 }
 
 QString HistoryServiceMsg::inReplyText() const {
@@ -7569,7 +7563,7 @@ void HistoryServiceMsg::drawInDialog(Painter &p, const QRect &r, bool act, const
 }
 
 QString HistoryServiceMsg::notificationText() const {
-    QString msg = _text.original(0, 0xFFFF);
+    QString msg = _text.original();
     if (msg.size() > 0xFF) msg = msg.mid(0, 0xFF) + qsl("..");
     return msg;
 }
diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h
index 2a2355577..32d42af30 100644
--- a/Telegram/SourceFiles/history.h
+++ b/Telegram/SourceFiles/history.h
@@ -933,7 +933,11 @@ public:
 	}
 	virtual void setText(const QString &text, const EntitiesInText &links) {
 	}
-	virtual void getTextWithEntities(QString &text, EntitiesInText &links) {
+	virtual QString originalText() const {
+		return QString();
+	}
+	virtual EntitiesInText originalEntities() const {
+		return EntitiesInText();
 	}
 	virtual bool textHasLinks() {
 		return false;
@@ -1129,7 +1133,6 @@ public:
 	}
 	const QString inDialogsText() const;
 	const QString inHistoryText() const;
-	const Text &captionForClone() const;
 	bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 	void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const;
 	HistoryMedia *clone() const;
@@ -1155,7 +1158,7 @@ public:
 	ImagePtr replyPreview();
 
 	QString getCaption() const {
-		return _caption.original(0, 0xFFFFU, true);
+		return _caption.original();
 	}
 
 private:
@@ -1549,12 +1552,12 @@ public:
 	}
 
 	QString selectedText(uint32 selection) const;
-	EntitiesInText textEntities() const;
 	QString inDialogsText() const;
 	HistoryMedia *getMedia(bool inOverview = false) const;
 	void setMedia(const MTPMessageMedia *media, bool allowEmitResize);
 	void setText(const QString &text, const EntitiesInText &entities);
-	void getTextWithEntities(QString &text, EntitiesInText &entities);
+	QString originalText() const;
+	EntitiesInText originalEntities() const;
 	bool textHasLinks();
 
 	int32 infoWidth() const {
diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp
index 08c643560..7903e223c 100644
--- a/Telegram/SourceFiles/mediaview.cpp
+++ b/Telegram/SourceFiles/mediaview.cpp
@@ -777,7 +777,7 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) {
 	_caption = Text();
 	if (HistoryMessage *itemMsg = item ? item->toHistoryMessage() : 0) {
 		if (HistoryPhoto *photoMsg = dynamic_cast<HistoryPhoto*>(itemMsg->getMedia())) {
-			_caption.setText(st::mvCaptionFont, photoMsg->captionForClone().original(0, 0xFFFF), (item->from()->isUser() && item->from()->asUser()->botInfo) ? _captionBotOptions : _captionTextOptions);
+			_caption.setText(st::mvCaptionFont, photoMsg->getCaption(), (item->from()->isUser() && item->from()->asUser()->botInfo) ? _captionBotOptions : _captionTextOptions);
 		}
 	}
 
diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp
index 95c4f4594..6f2d96b9d 100644
--- a/Telegram/SourceFiles/overviewwidget.cpp
+++ b/Telegram/SourceFiles/overviewwidget.cpp
@@ -31,31 +31,31 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
 #include "gui/filedialog.h"
 
 OverviewInner::CachedLink::CachedLink(HistoryItem *item) : titleWidth(0), page(0), pixw(0), pixh(0), text(st::msgMinWidth) {
-	QString msgText;
-	EntitiesInText msgLinks;
-	item->getTextWithEntities(msgText, msgLinks);
-	int32 from = 0, till = msgText.size(), lnk = msgLinks.size();
+	QString msgText = item->originalText();
+	EntitiesInText msgEntities = item->originalEntities();
+
+	int32 from = 0, till = msgText.size(), lnk = msgEntities.size();
 	for (int32 i = 0; i < lnk; ++i) {
-		if (msgLinks[i].type != EntityInTextUrl && msgLinks[i].type != EntityInTextCustomUrl && msgLinks[i].type != EntityInTextEmail) {
+		if (msgEntities[i].type != EntityInTextUrl && msgEntities[i].type != EntityInTextCustomUrl && msgEntities[i].type != EntityInTextEmail) {
 			continue;
 		}
-		QString url = msgLinks[i].text, text = msgText.mid(msgLinks[i].offset, msgLinks[i].length);
+		QString url = msgEntities[i].text, text = msgText.mid(msgEntities[i].offset, msgEntities[i].length);
 		urls.push_back(Link(url.isEmpty() ? text : url, text));
 	}
 	while (lnk > 0 && till > from) {
 		--lnk;
-		if (msgLinks[lnk].type != EntityInTextUrl && msgLinks[lnk].type != EntityInTextCustomUrl && msgLinks[lnk].type != EntityInTextEmail) {
+		if (msgEntities[lnk].type != EntityInTextUrl && msgEntities[lnk].type != EntityInTextCustomUrl && msgEntities[lnk].type != EntityInTextEmail) {
 			++lnk;
 			break;
 		}
-		int32 afterLinkStart = msgLinks[lnk].offset + msgLinks[lnk].length;
+		int32 afterLinkStart = msgEntities[lnk].offset + msgEntities[lnk].length;
 		if (till > afterLinkStart) {
 			if (!QRegularExpression(qsl("^[,.\\s_=+\\-;:`'\"\\(\\)\\[\\]\\{\\}<>*&^%\\$#@!\\\\/]+$")).match(msgText.mid(afterLinkStart, till - afterLinkStart)).hasMatch()) {
 				++lnk;
 				break;
 			}
 		}
-		till = msgLinks[lnk].offset;
+		till = msgEntities[lnk].offset;
 	}
 	if (!lnk) {
 		if (QRegularExpression(qsl("^[,.\\s\\-;:`'\"\\(\\)\\[\\]\\{\\}<>*&^%\\$#@!\\\\/]+$")).match(msgText.mid(from, till - from)).hasMatch()) {