diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt
index ad646bfed..89fadeb4b 100644
--- a/Telegram/Resources/style.txt
+++ b/Telegram/Resources/style.txt
@@ -1991,10 +1991,10 @@ stickerPreviewDuration: 150;
 stickerPreviewBg: #FFFFFFB0;
 stickerPreviewMin: 0.1;
 
-savedGifsLeft: 15px;
-savedGifsSkip: 3px;
-savedGifHeight: 96px;
-savedGifMinWidth: 64px;
+inlineResultsLeft: 15px;
+inlineResultsSkip: 3px;
+inlineMediaHeight: 96px;
+inlineResultsMinWidth: 64px;
 
 verifiedCheckProfile: sprite(285px, 235px, 18px, 18px);
 verifiedCheckProfilePos: point(7px, 6px);
diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp
index 87d4418fc..a2126d779 100644
--- a/Telegram/SourceFiles/dropdown.cpp
+++ b/Telegram/SourceFiles/dropdown.cpp
@@ -1309,7 +1309,7 @@ void StickerPanInner::paintContextItems(Painter &p, const QRect &r) {
 		const ContextRow &contextRow(_contextRows.at(row));
 		if (top >= r.top() + r.height()) break;
 		if (top + contextRow.height > r.top()) {
-			int32 left = st::savedGifsLeft;
+			int32 left = st::inlineResultsLeft;
 			for (int32 col = 0, cols = contextRow.items.size(); col < cols; ++col) {
 				if (left >= tox) break;
 
@@ -1319,7 +1319,7 @@ void StickerPanInner::paintContextItems(Painter &p, const QRect &r) {
 					contextRow.items.at(col)->paint(p, r.translated(-left, -top), 0, &context);
 					p.translate(-left, -top);
 				}
-				left += w + st::savedGifsSkip;
+				left += w + st::inlineResultsSkip;
 			}
 		}
 		top += contextRow.height;
@@ -1598,6 +1598,33 @@ void StickerPanInner::refreshStickers() {
 	updateSelected();
 }
 
+void StickerPanInner::contextRowsAddItem(DocumentData *savedGif, ContextResult *result, ContextRow &row, int32 &sumWidth) {
+	LayoutContextItem *layout = 0;
+	if (savedGif) {
+		layout = layoutPrepareSavedGif(savedGif, (_contextRows.size() * MatrixRowShift) + row.items.size());
+	} else if (result) {
+		layout = layoutPrepareContextResult(result, (_contextRows.size() * MatrixRowShift) + row.items.size());
+	}
+	if (!layout) return;
+
+	contextRowFinalize(row, sumWidth, layout->fullLine());
+	row.items.push_back(layout);
+	sumWidth += layout->maxWidth();
+}
+
+void StickerPanInner::contextRowFinalize(ContextRow &row, int32 &sumWidth, bool force) {
+	if (row.items.isEmpty()) return;
+
+	bool full = (row.items.size() >= SavedGifsMaxPerRow);
+	bool big = (sumWidth >= st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - (row.items.size() - 1) * st::inlineResultsSkip);
+	if (full || big || force) {
+		_contextRows.push_back(layoutContextRow(row, (full || big) ? sumWidth : 0));
+		row = ContextRow();
+		row.items.reserve(SavedGifsMaxPerRow);
+		sumWidth = 0;
+	}
+}
+
 void StickerPanInner::refreshSavedGifs() {
 	if (_showingSavedGifs) {
 		clearContextRows();
@@ -1610,33 +1637,11 @@ void StickerPanInner::refreshSavedGifs() {
 				_contextRows.reserve(saved.size());
 				ContextRow row;
 				row.items.reserve(SavedGifsMaxPerRow);
-				int32 maxWidth = width() - st::savedGifsLeft, sumWidth = 0, widths[SavedGifsMaxPerRow] = { 0 };
+				int32 sumWidth = 0;
 				for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) {
-					DocumentData *doc = *i;
-					int32 w = doc->dimensions.width(), h = doc->dimensions.height();
-					if ((w <= 0 || h <= 0) && !doc->thumb->isNull()) {
-						w = doc->thumb->width();
-						h = doc->thumb->height();
-					}
-					if (w <= 0 || h <= 0) continue;
-
-					w = w * st::savedGifHeight / h;
-					widths[row.items.size()] = w;
-
-					w = qMax(w, int32(st::savedGifMinWidth));
-					sumWidth += w;
-					row.items.push_back(layoutPrepare(doc, (_contextRows.size() * MatrixRowShift) + row.items.size(), w));
-
-					if (row.items.size() >= SavedGifsMaxPerRow || sumWidth >= maxWidth - (row.items.size() - 1) * st::savedGifsSkip) {
-						_contextRows.push_back(layoutContextRow(row, widths, sumWidth));
-						row = ContextRow();
-						sumWidth = 0;
-						memset(widths, 0, sizeof(widths));
-					}
-				}
-				if (!row.items.isEmpty()) {
-					_contextRows.push_back(layoutContextRow(row, 0, 0));
+					contextRowsAddItem(*i, 0, row, sumWidth);
 				}
+				contextRowFinalize(row, sumWidth, true);
 			}
 			deleteUnusedGifLayouts();
 
@@ -1651,12 +1656,8 @@ void StickerPanInner::refreshSavedGifs() {
 	updateSelected();
 }
 
-void StickerPanInner::refreshContextResults(const ContextResults &results) {
-
-}
-
 void StickerPanInner::contextBotChanged() {
-	refreshContextResults(ContextResults());
+	refreshContextRows(0, ContextResults());
 	deleteUnusedContextLayouts();
 }
 
@@ -1664,19 +1665,43 @@ void StickerPanInner::clearContextRows() {
 	clearSelection(true);
 	for (ContextRows::const_iterator i = _contextRows.cbegin(), e = _contextRows.cend(); i != e; ++i) {
 		for (ContextItems::const_iterator j = i->items.cbegin(), end = i->items.cend(); j != end; ++j) {
-			(*j)->setPosition(-1, 0);
+			(*j)->setPosition(-1);
 		}
 	}
 	_contextRows.clear();
 }
 
-LayoutContextGif *StickerPanInner::layoutPrepare(DocumentData *doc, int32 position, int32 width) {
+LayoutContextGif *StickerPanInner::layoutPrepareSavedGif(DocumentData *doc, int32 position) {
 	GifLayouts::const_iterator i = _gifLayouts.constFind(doc);
 	if (i == _gifLayouts.cend()) {
-		i = _gifLayouts.insert(doc, new LayoutContextGif(doc, true));
+		i = _gifLayouts.insert(doc, new LayoutContextGif(0, doc, true));
 		i.value()->initDimensions();
 	}
-	i.value()->setPosition(position, width);
+	if (!i.value()->maxWidth()) return 0;
+
+	i.value()->setPosition(position);
+	return i.value();
+}
+
+LayoutContextItem *StickerPanInner::layoutPrepareContextResult(ContextResult *result, int32 position) {
+	ContextLayouts::const_iterator i = _contextLayouts.constFind(result);
+	if (i == _contextLayouts.cend()) {
+		LayoutContextItem *layout = 0;
+		if (result->type == qstr("gif")) {
+			layout = new LayoutContextGif(result, 0, false);
+		} else if (result->type == qstr("photo")) {
+		} else if (result->type == qstr("web_player_video")) {
+		} else if (result->type == qstr("article")) {
+			return 0;
+		}
+		if (!layout) return 0;
+
+		i = _contextLayouts.insert(result, layout);
+		layout->initDimensions();
+	}
+	if (!i.value()->maxWidth()) return 0;
+
+	i.value()->setPosition(position);
 	return i.value();
 }
 
@@ -1716,20 +1741,19 @@ void StickerPanInner::deleteUnusedContextLayouts() {
 	}
 }
 
-StickerPanInner::ContextRow &StickerPanInner::layoutContextRow(ContextRow &row, int32 *widths, int32 sumWidth) {
+StickerPanInner::ContextRow &StickerPanInner::layoutContextRow(ContextRow &row, int32 sumWidth) {
 	int32 count = row.items.size();
 	t_assert(count <= SavedGifsMaxPerRow);
 
 	row.height = 0;
-	int32 availw = width() - st::savedGifsLeft - st::savedGifsSkip * (count - 1);
+	int32 availw = width() - st::inlineResultsLeft - st::inlineResultsSkip * (count - 1);
 	for (int32 i = 0; i < count; ++i) {
-		int32 w = widths ? (widths[i] * availw / sumWidth) : row.items.at(i)->width();
-		int32 actualw = qMax(w, int32(st::savedGifMinWidth));
+		int32 w = sumWidth ? (row.items.at(i)->maxWidth() * availw / sumWidth) : row.items.at(i)->maxWidth();
+		int32 actualw = qMax(w, int32(st::inlineResultsMinWidth));
 		row.height = qMax(row.height, row.items.at(i)->resizeGetHeight(actualw));
-
-		if (widths) {
+		if (sumWidth) {
 			availw -= actualw;
-			sumWidth -= qMax(widths[i], int32(st::savedGifMinWidth));
+			sumWidth -= row.items.at(i)->maxWidth();
 		}
 	}
 	return row;
@@ -1778,8 +1802,85 @@ uint64 StickerPanInner::currentSet(int yOffset) const {
 	return _sets.isEmpty() ? RecentStickerSetId : _sets.back().id;
 }
 
-void StickerPanInner::refreshContextRows(const ContextResults &results) {
+void StickerPanInner::refreshContextRows(UserData *bot, const ContextResults &results) {
+	int32 count = results.size(), until = 0, untilrow = 0, untilcol = 0;
+	if (!count) {
+		_contextRows.clear();
+		_showingSavedGifs = true;
+		if (_showingContextItems) {
+			refreshSavedGifs();
+			emit scrollToY(0);
+			emit scrollUpdated();
+		}
+		return;
+	}
 
+	t_assert(bot != 0);
+	_inlineBotTitle = lng_inline_bot_results(lt_inline_bot, bot->username.isEmpty() ? bot->name : ('@' + bot->username));
+
+	_showingContextItems = true;
+	_showingSavedGifs = false;
+	for (; until < count;) {
+		if (untilrow >= _contextRows.size() || _contextRows.at(untilrow).items.at(untilcol)->result() != results.at(until)) {
+			break;
+		}
+		++until;
+		if (++untilcol == _contextRows.at(untilrow).items.size()) {
+			++untilrow;
+			untilcol = 0;
+		}
+	}
+	if (until == count) { // all items are layed out
+		if (untilrow == _contextRows.size()) { // nothing changed
+			return;
+		}
+
+		for (int32 i = untilrow, l = _contextRows.size(), skip = untilcol; i < l; ++i) {
+			for (int32 j = 0, s = _contextRows.at(i).items.size(); j < s; ++j) {
+				if (skip) {
+					--skip;
+				} else {
+					_contextRows.at(i).items.at(j)->setPosition(-1);
+				}
+			}
+		}
+		if (!untilcol) { // all good rows are filled
+			_contextRows.resize(untilrow);
+			return;
+		}
+		_contextRows.resize(untilrow + 1);
+		_contextRows[untilrow].items.resize(untilcol);
+		_contextRows[untilrow] = layoutContextRow(_contextRows[untilrow]);
+		return;
+	}
+	if (untilrow && !untilcol) { // remove last row, maybe it is not full
+		--untilrow;
+		untilcol = _contextRows.at(untilrow).items.size();
+	}
+	until -= untilcol;
+
+	for (int32 i = untilrow, l = _contextRows.size(); i < l; ++i) {
+		for (int32 j = 0, s = _contextRows.at(i).items.size(); j < s; ++j) {
+			_contextRows.at(i).items.at(j)->setPosition(-1);
+		}
+	}
+	_contextRows.resize(untilrow);
+
+	_contextRows.reserve(count);
+	ContextRow row;
+	row.items.reserve(SavedGifsMaxPerRow);
+	int32 sumWidth = 0;
+	for (int32 i = until; i < count; ++i) {
+		contextRowsAddItem(0, results.at(i), row, sumWidth);
+	}
+	contextRowFinalize(row, sumWidth, true);
+
+	int32 h = countHeight();
+	if (h != height()) resize(width(), h);
+	update();
+
+	emit refreshIcons();
+	updateSelected();
 }
 
 void StickerPanInner::ui_repaintContextItem(const LayoutContextItem *layout) {
@@ -1916,7 +2017,7 @@ void StickerPanInner::fillPanels(QVector<EmojiPanel*> &panels) {
 	panels.clear();
 
 	if (_showingContextItems) {
-		panels.push_back(new EmojiPanel(parentWidget(), lang(lng_saved_gifs), NoneStickerSetId, true, 0));
+		panels.push_back(new EmojiPanel(parentWidget(), _showingSavedGifs ? lang(lng_saved_gifs) : _inlineBotTitle, NoneStickerSetId, true, 0));
 		panels.back()->show();
 		return;
 	}
@@ -1957,7 +2058,7 @@ void StickerPanInner::updateSelected() {
 	QPoint p(mapFromGlobal(_lastMousePos));
 
 	if (_showingContextItems) {
-		int sx = (rtl() ? width() - p.x() : p.x()) - st::savedGifsLeft, sy = p.y() - st::emojiPanHeader;
+		int sx = (rtl() ? width() - p.x() : p.x()) - st::inlineResultsLeft, sy = p.y() - st::emojiPanHeader;
 		int32 row = -1, col = -1, sel = -1;
 		TextLinkPtr lnk;
 		HistoryCursorState cursor = HistoryDefaultCursorState;
@@ -1978,7 +2079,7 @@ void StickerPanInner::updateSelected() {
 				if (sx < width) {
 					break;
 				}
-				sx -= width + st::savedGifsSkip;
+				sx -= width + st::inlineResultsSkip;
 			}
 			if (col < contextItems.size()) {
 				sel = row * MatrixRowShift + col;
@@ -2118,7 +2219,6 @@ void StickerPanInner::onPreview() {
 	if (_pressedSel < 0) return;
 	if (_showingContextItems) {
 		int32 row = _pressedSel / MatrixRowShift, col = _pressedSel % MatrixRowShift;
-		if (col >= SavedGifsMaxPerRow) col -= SavedGifsMaxPerRow;
 		if (row < _contextRows.size() && col < _contextRows.at(row).items.size() && _contextRows.at(row).items.at(col)->document() && _contextRows.at(row).items.at(col)->document()->loaded()) {
 			Ui::showStickerPreview(_contextRows.at(row).items.at(col)->document());
 			_previewShown = true;
@@ -2165,9 +2265,6 @@ void StickerPanInner::showStickerSet(uint64 setId) {
 	clearSelection(true);
 
 	if (setId == NoneStickerSetId) {
-		if (_contextRows.isEmpty() && !cSavedGifs().isEmpty()) {
-			refreshSavedGifs();
-		}
 		bool wasNotShowingGifs = !_showingContextItems;
 		if (wasNotShowingGifs) {
 			_showingContextItems = true;
@@ -2582,7 +2679,7 @@ void EmojiPan::enterEvent(QEvent *e) {
 }
 
 void EmojiPan::leaveEvent(QEvent *e) {
-	if (_removingSetId) return;
+	if (_removingSetId || s_inner.contextResultsShown()) return;
 	if (_a_appearance.animating()) {
 		hideStart();
 	} else {
@@ -2596,6 +2693,7 @@ void EmojiPan::otherEnter() {
 }
 
 void EmojiPan::otherLeave() {
+	if (_removingSetId || s_inner.contextResultsShown()) return;
 	if (_a_appearance.animating()) {
 		hideStart();
 	} else {
@@ -2909,6 +3007,8 @@ void EmojiPan::step_appearance(float64 ms, bool timer) {
 }
 
 void EmojiPan::hideStart() {
+	if (_removingSetId || s_inner.contextResultsShown()) return;
+
 	if (_cache.isNull()) {
 		QPixmap from = _fromCache, to = _toCache;
 		_fromCache = _toCache = QPixmap();
@@ -3249,6 +3349,8 @@ void EmojiPan::onDelayedHide() {
 void EmojiPan::contextBotChanged() {
 	if (!_contextBot) return;
 
+	if (!isHidden()) hideStart();
+
 	if (_contextRequestId) MTP::cancel(_contextRequestId);
 	_contextRequestId = 0;
 	_contextQuery = _contextNextQuery = _contextNextOffset = QString();
@@ -3311,8 +3413,31 @@ void EmojiPan::contextResultsDone(const MTPmessages_BotResults &result) {
 				message = &r.vsend_message;
 			} break;
 			}
-			bool badAttachment = (!result->photo || result->photo->access) && (!result->doc || result->doc->access);
-			bool canSend = (result->photo || result->doc || !result->message.isEmpty());
+			bool badAttachment = (result->photo && !result->photo->access) || (result->doc && !result->doc->access);
+
+			if (!message) {
+				delete result;
+				continue;
+			}
+			switch (message->type()) {
+			case mtpc_botContextMessageMediaAuto: {
+				const MTPDbotContextMessageMediaAuto &r(message->c_botContextMessageMediaAuto());
+				result->caption = qs(r.vcaption);
+			} break;
+
+			case mtpc_botContextMessageText: {
+				const MTPDbotContextMessageText &r(message->c_botContextMessageText());
+				result->message = qs(r.vmessage);
+				if (r.has_entities()) result->entities = entitiesFromMTP(r.ventities.c_vector().v);
+				result->noWebPage = r.is_no_webpage();
+			} break;
+
+			default: {
+				badAttachment = true;
+			} break;
+			}
+
+			bool canSend = (result->photo || result->doc || !result->message.isEmpty() || (!result->content_url.isEmpty() && (result->type == qstr("gif") || result->type == qstr("photo"))));
 			if (result->type.isEmpty() || badAttachment || !canSend) {
 				delete result;
 			} else {
@@ -3323,7 +3448,7 @@ void EmojiPan::contextResultsDone(const MTPmessages_BotResults &result) {
 		it.value()->clearResults();
 		it.value()->nextOffset = QString();
 	}
-	refreshContextRows(!adding);
+	showContextRows(!adding);
 }
 
 bool EmojiPan::contextResultsFail(const RPCError &error) {
@@ -3332,7 +3457,7 @@ bool EmojiPan::contextResultsFail(const RPCError &error) {
 	return true;
 }
 
-void EmojiPan::showContextResults(UserData *bot, QString query) {
+void EmojiPan::queryContextBot(UserData *bot, QString query) {
 	bool force = false;
 	if (bot != _contextBot) {
 		contextBotChanged();
@@ -3344,10 +3469,11 @@ void EmojiPan::showContextResults(UserData *bot, QString query) {
 		_contextRequestId = 0;
 	}
 	if (_contextQuery != query || force) {
-		if (_contextCache.contains(_contextQuery)) {
-			refreshContextRows(true);
+		if (_contextCache.contains(query)) {
+			_contextQuery = query;
+			showContextRows(true);
 		} else {
-			_contextNextQuery = _contextQuery;
+			_contextNextQuery = query;
 			_contextRequestTimer.start(ContextBotRequestDelay);
 		}
 	}
@@ -3366,16 +3492,37 @@ void EmojiPan::onContextRequest() {
 	_contextRequestId = MTP::send(MTPmessages_GetContextBotResults(_contextBot->inputUser, MTP_string(_contextQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::contextResultsDone), rpcFail(&EmojiPan::contextResultsFail));
 }
 
-void EmojiPan::refreshContextRows(bool newResults) {
+void EmojiPan::showContextRows(bool newResults) {
 	bool clear = true;
 	ContextCache::const_iterator i = _contextCache.constFind(_contextQuery);
 	if (i != _contextCache.cend()) {
-		clear = !i.value()->results.isEmpty();
+		clear = i.value()->results.isEmpty();
 		_contextNextOffset = i.value()->nextOffset;
 	}
 
-	s_inner.refreshContextRows(clear ? ContextResults() : i.value()->results);
+	s_inner.refreshContextRows(_contextBot, clear ? ContextResults() : i.value()->results);
 	if (newResults) s_scroll.scrollToY(0);
+	if (clear && !isHidden() && _stickersShown && s_inner.contextResultsShown()) {
+		hideStart();
+	} else if (!clear) {
+		_hideTimer.stop();
+		if (!_stickersShown) {
+			if (!isHidden() || _hiding) {
+				onSwitch();
+			} else {
+				_stickersShown = true;
+				if (isHidden()) {
+					show();
+					a_opacity = anim::fvalue(0, 1);
+					a_opacity.update(0, anim::linear);
+					_cache = _fromCache = _toCache = QPixmap();
+				}
+			}
+		}
+		if (isHidden() || _hiding) {
+			showStart();
+		}
+	}
 }
 
 MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows)
diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h
index 70d4e0387..7ca67f376 100644
--- a/Telegram/SourceFiles/dropdown.h
+++ b/Telegram/SourceFiles/dropdown.h
@@ -314,31 +314,6 @@ struct StickerIcon {
 	int32 pixw, pixh;
 };
 
-struct ContextResult {
-	ContextResult(uint64 queryId)
-		: queryId(queryId)
-		, doc(0)
-		, photo(0)
-		, width(0)
-		, height(0)
-		, duration(0)
-		, noWebPage(false) {
-	}
-	uint64 queryId;
-	QString id, type;
-	DocumentData *doc;
-	PhotoData *photo;
-	QString title, description, url, thumb_url;
-	QString content_type, content_url;
-	int32 width, height, duration;
-
-	QString message; // botContextMessageText
-	bool noWebPage;
-	EntitiesInText entities;
-	QString caption; // if message.isEmpty() use botContextMessageMediaAuto
-};
-typedef QList<ContextResult*> ContextResults;
-
 class StickerPanInner : public TWidget {
 	Q_OBJECT
 
@@ -366,8 +341,9 @@ public:
 	void refreshStickers();
 	void refreshRecentStickers(bool resize = true);
 	void refreshSavedGifs();
-	void refreshContextRows(const ContextResults &results);
+	void refreshContextRows(UserData *bot, const ContextResults &results);
 	void refreshRecent();
+	void contextBotChanged();
 
 	void fillIcons(QList<StickerIcon> &icons);
 	void fillPanels(QVector<EmojiPanel*> &panels);
@@ -377,13 +353,15 @@ public:
 	void preloadImages();
 
 	uint64 currentSet(int yOffset) const;
-	void refreshContextResults(const ContextResults &results);
-	void contextBotChanged();
 
 	void ui_repaintContextItem(const LayoutContextItem *layout);
 	bool ui_isContextItemVisible(const LayoutContextItem *layout);
 	bool ui_isContextItemBeingChosen();
 
+	bool contextResultsShown() const {
+		return _showingContextItems && !_showingSavedGifs;
+	}
+
 	~StickerPanInner() {
 		clearContextRows();
 		deleteUnusedGifLayouts();
@@ -445,28 +423,33 @@ private:
 	QList<bool> _custom;
 
 	bool _showingSavedGifs, _showingContextItems;
+	QString _inlineBotTitle;
 	uint64 _lastScrolled;
 	QTimer _updateContextItems;
 
-	typedef QList<LayoutContextItem*> ContextItems;
+	typedef QVector<LayoutContextItem*> ContextItems;
 	struct ContextRow {
 		ContextRow() : height(0) {
 		}
 		int32 height;
 		ContextItems items;
 	};
-	typedef QList<ContextRow> ContextRows;
+	typedef QVector<ContextRow> ContextRows;
 	ContextRows _contextRows;
 	void clearContextRows();
 
 	typedef QMap<DocumentData*, LayoutContextGif*> GifLayouts;
 	GifLayouts _gifLayouts;
-	LayoutContextGif *layoutPrepare(DocumentData *doc, int32 position, int32 width);
+	LayoutContextGif *layoutPrepareSavedGif(DocumentData *doc, int32 position);
 
 	typedef QMap<ContextResult*, LayoutContextItem*> ContextLayouts;
 	ContextLayouts _contextLayouts;
+	LayoutContextItem *layoutPrepareContextResult(ContextResult *result, int32 position);
 
-	ContextRow &layoutContextRow(ContextRow &row, int32 *widths, int32 sumWidth);
+	void contextRowsAddItem(DocumentData *savedGif, ContextResult *result, ContextRow &row, int32 &sumWidth);
+	void contextRowFinalize(ContextRow &row, int32 &sumWidth, bool force = false);
+
+	ContextRow &layoutContextRow(ContextRow &row, int32 sumWidth = 0);
 	void deleteUnusedGifLayouts();
 
 	void deleteUnusedContextLayouts();
@@ -569,7 +552,7 @@ public:
 	bool eventFilter(QObject *obj, QEvent *e);
 	void stickersInstalled(uint64 setId);
 
-	void showContextResults(UserData *bot, QString query);
+	void queryContextBot(UserData *bot, QString query);
 	void contextBotChanged();
 
 	bool overlaps(const QRect &globalRect) {
@@ -701,7 +684,7 @@ private:
 	ContextCache _contextCache;
 	QTimer _contextRequestTimer;
 
-	void refreshContextRows(bool newResults);
+	void showContextRows(bool newResults);
 	UserData *_contextBot;
 	QString _contextQuery, _contextNextQuery, _contextNextOffset;
 	mtpRequestId _contextRequestId;
diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp
index 80853107d..28d2eed80 100644
--- a/Telegram/SourceFiles/history.cpp
+++ b/Telegram/SourceFiles/history.cpp
@@ -5559,207 +5559,6 @@ HistoryWebPage::~HistoryWebPage() {
 	deleteAndMark(_attach);
 }
 
-namespace {
-	ImageLinkManager manager;
-}
-
-void ImageLinkManager::init() {
-	if (manager) delete manager;
-	manager = new QNetworkAccessManager();
-	App::setProxySettings(*manager);
-
-	connect(manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), this, SLOT(onFailed(QNetworkReply*)));
-	connect(manager, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), this, SLOT(onFailed(QNetworkReply*)));
-	connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onFinished(QNetworkReply*)));
-
-	if (black) delete black;
-	QImage b(cIntRetinaFactor(), cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
-	{
-		QPainter p(&b);
-		p.fillRect(QRect(0, 0, cIntRetinaFactor(), cIntRetinaFactor()), st::white->b);
-	}
-	QPixmap p = QPixmap::fromImage(b, Qt::ColorOnly);
-	p.setDevicePixelRatio(cRetinaFactor());
-	black = new ImagePtr(p, "PNG");
-}
-
-void ImageLinkManager::reinit() {
-	if (manager) App::setProxySettings(*manager);
-}
-
-void ImageLinkManager::deinit() {
-	if (manager) {
-		delete manager;
-		manager = 0;
-	}
-	if (black) {
-		delete black;
-		black = 0;
-	}
-	dataLoadings.clear();
-	imageLoadings.clear();
-}
-
-void initImageLinkManager() {
-	manager.init();
-}
-
-void reinitImageLinkManager() {
-	manager.reinit();
-}
-
-void deinitImageLinkManager() {
-	manager.deinit();
-}
-
-void ImageLinkManager::getData(ImageLinkData *data) {
-	if (!manager) {
-		DEBUG_LOG(("App Error: getting image link data without manager init!"));
-		return failed(data);
-	}
-	QString url;
-	switch (data->type) {
-	case GoogleMapsLink: {
-		int32 w = st::locationSize.width(), h = st::locationSize.height();
-		int32 zoom = 13, scale = 1;
-		if (cScale() == dbisTwo || cRetina()) {
-			scale = 2;
-		} else {
-			w = convertScale(w);
-			h = convertScale(h);
-		}
-		url = qsl("https://maps.googleapis.com/maps/api/staticmap?center=") + data->id.mid(9) + qsl("&zoom=%1&size=%2x%3&maptype=roadmap&scale=%4&markers=color:red|size:big|").arg(zoom).arg(w).arg(h).arg(scale) + data->id.mid(9) + qsl("&sensor=false");
-		QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url)));
-		imageLoadings[reply] = data;
-	} break;
-	default: {
-		failed(data);
-	} break;
-	}
-}
-
-void ImageLinkManager::onFinished(QNetworkReply *reply) {
-	if (!manager) return;
-	if (reply->error() != QNetworkReply::NoError) return onFailed(reply);
-
-	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
-	if (statusCode.isValid()) {
-		int status = statusCode.toInt();
-		if (status == 301 || status == 302) {
-			QString loc = reply->header(QNetworkRequest::LocationHeader).toString();
-			if (!loc.isEmpty()) {
-				QMap<QNetworkReply*, ImageLinkData*>::iterator i = dataLoadings.find(reply);
-				if (i != dataLoadings.cend()) {
-					ImageLinkData *d = i.value();
-					if (serverRedirects.constFind(d) == serverRedirects.cend()) {
-						serverRedirects.insert(d, 1);
-					} else if (++serverRedirects[d] > MaxHttpRedirects) {
-						DEBUG_LOG(("Network Error: Too many HTTP redirects in onFinished() for image link: %1").arg(loc));
-						return onFailed(reply);
-					}
-					dataLoadings.erase(i);
-					dataLoadings.insert(manager->get(QNetworkRequest(loc)), d);
-					return;
-				} else if ((i = imageLoadings.find(reply)) != imageLoadings.cend()) {
-					ImageLinkData *d = i.value();
-					if (serverRedirects.constFind(d) == serverRedirects.cend()) {
-						serverRedirects.insert(d, 1);
-					} else if (++serverRedirects[d] > MaxHttpRedirects) {
-						DEBUG_LOG(("Network Error: Too many HTTP redirects in onFinished() for image link: %1").arg(loc));
-						return onFailed(reply);
-					}
-					imageLoadings.erase(i);
-					imageLoadings.insert(manager->get(QNetworkRequest(loc)), d);
-					return;
-				}
-			}
-		}
-		if (status != 200) {
-			DEBUG_LOG(("Network Error: Bad HTTP status received in onFinished() for image link: %1").arg(status));
-			return onFailed(reply);
-		}
-	}
-
-	ImageLinkData *d = 0;
-	QMap<QNetworkReply*, ImageLinkData*>::iterator i = dataLoadings.find(reply);
-	if (i != dataLoadings.cend()) {
-		d = i.value();
-		dataLoadings.erase(i);
-
-		QJsonParseError e;
-		QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &e);
-		if (e.error != QJsonParseError::NoError) {
-			DEBUG_LOG(("JSON Error: Bad json received in onFinished() for image link"));
-			return onFailed(reply);
-		}
-		switch (d->type) {
-		case GoogleMapsLink: failed(d); break;
-		}
-
-		if (App::main()) App::main()->update();
-	} else {
-		i = imageLoadings.find(reply);
-		if (i != imageLoadings.cend()) {
-			d = i.value();
-			imageLoadings.erase(i);
-
-			QPixmap thumb;
-			QByteArray format;
-			QByteArray data(reply->readAll());
-			{
-				QBuffer buffer(&data);
-				QImageReader reader(&buffer);
-#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
-				reader.setAutoTransform(true);
-#endif
-				thumb = QPixmap::fromImageReader(&reader, Qt::ColorOnly);
-				format = reader.format();
-				thumb.setDevicePixelRatio(cRetinaFactor());
-				if (format.isEmpty()) format = QByteArray("JPG");
-			}
-			d->loading = false;
-			d->thumb = thumb.isNull() ? (*black) : ImagePtr(thumb, format);
-			serverRedirects.remove(d);
-			if (App::main()) App::main()->update();
-		}
-	}
-}
-
-void ImageLinkManager::onFailed(QNetworkReply *reply) {
-	if (!manager) return;
-
-	ImageLinkData *d = 0;
-	QMap<QNetworkReply*, ImageLinkData*>::iterator i = dataLoadings.find(reply);
-	if (i != dataLoadings.cend()) {
-		d = i.value();
-		dataLoadings.erase(i);
-	} else {
-		i = imageLoadings.find(reply);
-		if (i != imageLoadings.cend()) {
-			d = i.value();
-			imageLoadings.erase(i);
-		}
-	}
-	DEBUG_LOG(("Network Error: failed to get data for image link %1, error %2").arg(d ? d->id : 0).arg(reply->errorString()));
-	if (d) {
-		failed(d);
-	}
-}
-
-void ImageLinkManager::failed(ImageLinkData *data) {
-	data->loading = false;
-	data->thumb = *black;
-	serverRedirects.remove(data);
-}
-
-void ImageLinkData::load() {
-	if (!thumb->isNull()) return thumb->load(false, false);
-	if (loading) return;
-
-	loading = true;
-	manager.getData(this);
-}
-
 HistoryImageLink::HistoryImageLink(const QString &url, const QString &title, const QString &description) : HistoryMedia(),
 _title(st::msgMinWidth),
 _description(st::msgMinWidth) {
diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h
index 4efa11993..a322508e6 100644
--- a/Telegram/SourceFiles/history.h
+++ b/Telegram/SourceFiles/history.h
@@ -1859,54 +1859,6 @@ private:
 	int16 _pixw, _pixh;
 };
 
-void initImageLinkManager();
-void reinitImageLinkManager();
-void deinitImageLinkManager();
-
-enum ImageLinkType {
-	InvalidImageLink = 0,
-	GoogleMapsLink
-};
-struct ImageLinkData {
-	ImageLinkData(const QString &id) : id(id), type(InvalidImageLink), loading(false) {
-	}
-
-	QString id;
-	ImagePtr thumb;
-	ImageLinkType type;
-	bool loading;
-
-	void load();
-};
-
-class ImageLinkManager : public QObject {
-	Q_OBJECT
-public:
-	ImageLinkManager() : manager(0), black(0) {
-	}
-	void init();
-	void reinit();
-	void deinit();
-
-	void getData(ImageLinkData *data);
-
-	~ImageLinkManager() {
-		deinit();
-	}
-
-public slots:
-	void onFinished(QNetworkReply *reply);
-	void onFailed(QNetworkReply *reply);
-
-private:
-	void failed(ImageLinkData *data);
-
-	QNetworkAccessManager *manager;
-	QMap<QNetworkReply*, ImageLinkData*> dataLoadings, imageLoadings;
-	QMap<ImageLinkData*, int32> serverRedirects;
-	ImagePtr *black;
-};
-
 class HistoryImageLink : public HistoryMedia {
 public:
 
diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp
index a748f9011..b4cabb514 100644
--- a/Telegram/SourceFiles/historywidget.cpp
+++ b/Telegram/SourceFiles/historywidget.cpp
@@ -5327,7 +5327,7 @@ void HistoryWidget::onCheckMentionDropdown() {
 	}
 
 	if (_contextBot) {
-		_emojiPan.showContextResults(_contextBot, start);
+		_emojiPan.queryContextBot(_contextBot, start);
 		if (!_attachMention.isHidden()) {
 			_attachMention.hideStart();
 		}
diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp
index 14a53db93..48a56910d 100644
--- a/Telegram/SourceFiles/layout.cpp
+++ b/Telegram/SourceFiles/layout.cpp
@@ -1281,33 +1281,31 @@ LayoutOverviewLink::Link::Link(const QString &url, const QString &text)
 , lnk(linkFromUrl(url)) {
 }
 
-LayoutContextItem::LayoutContextItem(ContextResult *result)
+LayoutContextItem::LayoutContextItem(ContextResult *result, DocumentData *doc, PhotoData *photo)
 : _result(result)
-, _doc(0)
-, _position(0) {
-}
-
-LayoutContextItem::LayoutContextItem(DocumentData *doc)
-: _result(0)
 , _doc(doc)
+, _photo(photo)
 , _position(0) {
 }
 
-void LayoutContextItem::setPosition(int32 position, int32 width) {
+void LayoutContextItem::setPosition(int32 position) {
 	_position = position;
-	resizeGetHeight(width);
 }
 
 int32 LayoutContextItem::position() const {
 	return _position;
 }
 
+ContextResult *LayoutContextItem::result() const {
+	return _result;
+}
+
 DocumentData *LayoutContextItem::document() const {
 	return _doc;
 }
 
-ContextResult *LayoutContextItem::result() const {
-	return _result;
+PhotoData *LayoutContextItem::photo() const {
+	return _photo;
 }
 
 void LayoutContextItem::preload() {
@@ -1316,27 +1314,37 @@ void LayoutContextItem::preload() {
 			_result->photo->thumb->load();
 		} else if (_result->doc) {
 			_result->doc->thumb->load();
+		} else if (!_result->thumb_url.isEmpty()) {
+			_result->thumb->load();
 		}
 	} else if (_doc) {
 		_doc->thumb->load();
+	} else if (_photo) {
+		_photo->medium->load();
 	}
 }
 
-LayoutContextGif::LayoutContextGif(DocumentData *data, bool saved) : LayoutContextItem(data)
+LayoutContextGif::LayoutContextGif(ContextResult *result, DocumentData *doc, bool saved) : LayoutContextItem(result, doc, 0)
 , _state(0)
 , _gif(0)
 , _send(new SendContextItemLink())
-, _delete(saved ? new DeleteSavedGifLink(data) : 0)
+, _delete((doc && saved) ? new DeleteSavedGifLink(doc) : 0)
 , _animation(0) {
 }
 
 void LayoutContextGif::initDimensions() {
-	_maxw = st::emojiPanWidth - st::emojiScroll.width - st::savedGifsLeft;
-	_minh = st::savedGifHeight + st::savedGifsSkip;
+	int32 w = content_width(), h = content_height();
+	if (w <= 0 || h <= 0) {
+		_maxw = 0;
+	} else {
+		w = w * st::inlineMediaHeight / h;
+		_maxw = qMax(w, int32(st::inlineResultsMinWidth));
+	}
+	_minh = st::inlineMediaHeight + st::inlineResultsSkip;
 }
 
-void LayoutContextGif::setPosition(int32 position, int32 width) {
-	LayoutContextItem::setPosition(position, width);
+void LayoutContextGif::setPosition(int32 position) {
+	LayoutContextItem::setPosition(position);
 	if (_position < 0) {
 		if (gif()) delete _gif;
 		_gif = 0;
@@ -1357,12 +1365,12 @@ void DeleteSavedGifLink::onClick(Qt::MouseButton button) const {
 }
 
 void LayoutContextGif::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const {
-	_doc->automaticLoad(0);
+	content_automaticLoad();
 
-	bool loaded = _doc->loaded(), displayLoading = _doc->displayLoading();
+	bool loaded = content_loaded(), loading = content_loading(), displayLoading = content_displayLoading();
 	if (loaded && !gif() && _gif != BadClipReader) {
 		LayoutContextGif *that = const_cast<LayoutContextGif*>(this);
-		that->_gif = new ClipReader(_doc->location(), _doc->data(), func(that, &LayoutContextGif::clipCallback));
+		that->_gif = new ClipReader(content_location(), content_data(), func(that, &LayoutContextGif::clipCallback));
 		if (gif()) _gif->setAutoplay();
 	}
 
@@ -1370,12 +1378,12 @@ void LayoutContextGif::paint(Painter &p, const QRect &clip, uint32 selection, co
 	if (displayLoading) {
 		ensureAnimation();
 		if (!_animation->radial.animating()) {
-			_animation->radial.start(_doc->progress());
+			_animation->radial.start(content_progress());
 		}
 	}
 	bool radial = isRadialAnimation(context->ms);
 
-	int32 height = st::savedGifHeight;
+	int32 height = st::inlineMediaHeight;
 	QSize frame = countFrameSize();
 
 	QRect r(0, 0, _width, height);
@@ -1385,20 +1393,12 @@ void LayoutContextGif::paint(Painter &p, const QRect &clip, uint32 selection, co
 		t_assert(ctx);
 		p.drawPixmap(r.topLeft(), _gif->current(frame.width(), frame.height(), _width, height, ctx->paused ? 0 : context->ms));
 	} else {
-		if (!_doc->thumb->isNull()) {
-			if (_doc->thumb->loaded()) {
-				if (_thumb.width() != _width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
-					const_cast<LayoutContextGif*>(this)->_thumb = _doc->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), true, false, false, _width, height);
-				}
-			} else {
-				_doc->thumb->load();
-			}
-		}
+		prepareThumb(_width, height, frame);
 		p.drawPixmap(r.topLeft(), _thumb);
 	}
 
-	if (radial || (!_gif && !loaded && !_doc->loading()) || (_gif == BadClipReader)) {
-		float64 radialOpacity = (radial && loaded && !_doc->uploading()) ? _animation->radial.opacity() : 1;
+	if (radial || (!_gif && !loaded && !loading) || (_gif == BadClipReader)) {
+		float64 radialOpacity = (radial && loaded) ? _animation->radial.opacity() : 1;
 		if (_animation && _animation->_a_over.animating(context->ms)) {
 			float64 over = _animation->_a_over.current();
 			p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over));
@@ -1410,9 +1410,9 @@ void LayoutContextGif::paint(Painter &p, const QRect &clip, uint32 selection, co
 
 		p.setOpacity(radialOpacity);
 		style::sprite icon;
-		if (_doc->loaded() && !radial) {
+		if (loaded && !radial) {
 			icon = st::msgFileInPlay;
-		} else if (radial || _doc->loading()) {
+		} else if (radial || loading) {
 			icon = st::msgFileInCancel;
 		} else {
 			icon = st::msgFileInDownload;
@@ -1436,8 +1436,8 @@ void LayoutContextGif::paint(Painter &p, const QRect &clip, uint32 selection, co
 }
 
 void LayoutContextGif::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const {
-	if (x >= 0 && x < _width && y >= 0 && y < st::savedGifHeight) {
-		if ((rtl() ? _width - x : x) >= _width - st::stickerPanDelete.pxWidth() && y < st::stickerPanDelete.pxHeight()) {
+	if (x >= 0 && x < _width && y >= 0 && y < st::inlineMediaHeight) {
+		if (_delete && (rtl() ? _width - x : x) >= _width - st::stickerPanDelete.pxWidth() && y < st::stickerPanDelete.pxHeight()) {
 			link = _delete;
 		} else {
 			link = _send;
@@ -1446,15 +1446,15 @@ void LayoutContextGif::getState(TextLinkPtr &link, HistoryCursorState &cursor, i
 }
 
 void LayoutContextGif::linkOver(const TextLinkPtr &link) {
-	if (link == _delete) {
+	if (_delete && link == _delete) {
 		if (!(_state & StateDeleteOver)) {
 			EnsureAnimation(_a_deleteOver, 0, func(this, &LayoutContextGif::update));
 			_state |= StateDeleteOver;
 			_a_deleteOver.start(1, st::stickersRowDuration);
 		}
 	}
-	if (link == _delete || link == _send) {
-		if (!_doc->loaded()) {
+	if ((_delete && link == _delete) || link == _send) {
+		if (!content_loaded()) {
 			ensureAnimation();
 			if (!(_state & StateOver)) {
 				EnsureAnimation(_animation->_a_over, 0, func(this, &LayoutContextGif::update));
@@ -1466,15 +1466,15 @@ void LayoutContextGif::linkOver(const TextLinkPtr &link) {
 }
 
 void LayoutContextGif::linkOut(const TextLinkPtr &link) {
-	if (link == _delete) {
+	if (_delete && link == _delete) {
 		if (_state & StateDeleteOver) {
 			EnsureAnimation(_a_deleteOver, 1, func(this, &LayoutContextGif::update));
 			_state &= ~StateDeleteOver;
 			_a_deleteOver.start(0, st::stickersRowDuration);
 		}
 	}
-	if (link == _delete || link == _send) {
-		if (!_doc->loaded()) {
+	if ((_delete && link == _delete) || link == _send) {
+		if (!content_loaded()) {
 			ensureAnimation();
 			if (_state & StateOver) {
 				EnsureAnimation(_animation->_a_over, 1, func(this, &LayoutContextGif::update));
@@ -1487,7 +1487,7 @@ void LayoutContextGif::linkOut(const TextLinkPtr &link) {
 
 QSize LayoutContextGif::countFrameSize() const {
 	bool animating = (gif() && _gif->ready());
-	int32 framew = animating ? _gif->width() : _doc->thumb->width(), frameh = animating ? _gif->height() : _doc->thumb->height(), height = st::savedGifHeight;
+	int32 framew = animating ? _gif->width() : content_width(), frameh = animating ? _gif->height() : content_height(), height = st::inlineMediaHeight;
 	if (framew * height > frameh * _width) {
 		if (framew < st::maxStickerSize || frameh > height) {
 			if (frameh > height || (framew * height / frameh) <= st::maxStickerSize) {
@@ -1516,6 +1516,26 @@ LayoutContextGif::~LayoutContextGif() {
 	deleteAndMark(_animation);
 }
 
+void LayoutContextGif::prepareThumb(int32 width, int32 height, const QSize &frame) const {
+	if (_doc && !_doc->thumb->isNull()) {
+		if (_doc->thumb->loaded()) {
+			if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
+				const_cast<LayoutContextGif*>(this)->_thumb = _doc->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), true, false, false, width, height);
+			}
+		} else {
+			_doc->thumb->load();
+		}
+	} else if (_result && !_result->thumb_url.isEmpty()) {
+		if (_result->thumb->loaded()) {
+			if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
+				const_cast<LayoutContextGif*>(this)->_thumb = _result->thumb->pixNoCache(frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), true, false, false, width, height);
+			}
+		} else {
+			_result->thumb->load();
+		}
+	}
+}
+
 void LayoutContextGif::ensureAnimation() const {
 	if (!_animation) {
 		_animation = new AnimationData(animation(const_cast<LayoutContextGif*>(this), &LayoutContextGif::step_radial));
@@ -1533,8 +1553,8 @@ void LayoutContextGif::step_radial(uint64 ms, bool timer) {
 	if (timer) {
 		update();
 	} else {
-		_animation->radial.update(_doc->progress(), !_doc->loading() || _doc->loaded(), ms);
-		if (!_animation->radial.animating() && _doc->loaded()) {
+		_animation->radial.update(content_progress(), !content_loading() || content_loaded(), ms);
+		if (!_animation->radial.animating() && content_loaded()) {
 			delete _animation;
 			_animation = 0;
 		}
@@ -1548,15 +1568,15 @@ void LayoutContextGif::clipCallback(ClipReaderNotification notification) {
 			if (_gif->state() == ClipError) {
 				delete _gif;
 				_gif = BadClipReader;
-				_doc->forget();
+				content_forget();
 			} else if (_gif->ready() && !_gif->started()) {
-				int32 height = st::savedGifHeight;
+				int32 height = st::inlineMediaHeight;
 				QSize frame = countFrameSize();
 				_gif->start(frame.width(), frame.height(), _width, height, false);
 			} else if (_gif->paused() && !Ui::isContextItemVisible(this)) {
 				delete _gif;
 				_gif = 0;
-				_doc->forget();
+				content_forget();
 			}
 		}
 
@@ -1576,3 +1596,71 @@ void LayoutContextGif::update() {
 		Ui::repaintContextItem(this);
 	}
 }
+
+int32 LayoutContextGif::content_width() const {
+	if (_doc) {
+		if (_doc->dimensions.width() > 0) {
+			return _doc->dimensions.width();
+		}
+		if (!_doc->thumb->isNull()) {
+			return _doc->thumb->width();
+		}
+	} else if (_result) {
+		return _result->width;
+	}
+	return 0;
+}
+
+int32 LayoutContextGif::content_height() const {
+	if (_doc) {
+		if (_doc->dimensions.height() > 0) {
+			return _doc->dimensions.height();
+		}
+		if (!_doc->thumb->isNull()) {
+			return _doc->thumb->height();
+		}
+	} else if (_result) {
+		return _result->height;
+	}
+	return 0;
+}
+
+bool LayoutContextGif::content_loading() const {
+	return _doc ? _doc->loading() : _result->loading();
+}
+
+bool LayoutContextGif::content_displayLoading() const {
+	return _doc ? _doc->displayLoading() : _result->displayLoading();
+}
+
+bool LayoutContextGif::content_loaded() const {
+	return _doc ? _doc->loaded() : _result->loaded();
+}
+
+float64 LayoutContextGif::content_progress() const {
+	return _doc ? _doc->progress() : _result->progress();
+}
+
+void LayoutContextGif::content_automaticLoad() const {
+	if (_doc) {
+		_doc->automaticLoad(0);
+	} else {
+		_result->automaticLoadGif();
+	}
+}
+
+void LayoutContextGif::content_forget() {
+	if (_doc) {
+		_doc->forget();
+	} else {
+		_result->forget();
+	}
+}
+
+FileLocation LayoutContextGif::content_location() const {
+	return _doc ? _doc->location() : FileLocation();
+}
+
+QByteArray LayoutContextGif::content_data() const {
+	return _doc ? _doc->data() : _result->data();
+}
diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h
index 4b5b74a6f..59dc51556 100644
--- a/Telegram/SourceFiles/layout.h
+++ b/Telegram/SourceFiles/layout.h
@@ -490,19 +490,24 @@ struct ContextResult;
 class LayoutContextItem : public LayoutItem {
 public:
 
-	LayoutContextItem(ContextResult *result);
-	LayoutContextItem(DocumentData *doc);
+	LayoutContextItem(ContextResult *result, DocumentData *doc, PhotoData *photo);
 	
-	virtual void setPosition(int32 position, int32 width);
+	virtual void setPosition(int32 position);
 	int32 position() const;
 
-	DocumentData *document() const;
+	virtual bool fullLine() const {
+		return true;
+	}
+
 	ContextResult *result() const;
+	DocumentData *document() const;
+	PhotoData *photo() const;
 	void preload();
 
 protected:
 	ContextResult *_result;
 	DocumentData *_doc;
+	PhotoData *_photo;
 
 	int32 _position; // < 0 means removed from layout
 
@@ -532,11 +537,15 @@ private:
 
 class LayoutContextGif : public LayoutContextItem {
 public:
-	LayoutContextGif(DocumentData *data, bool saved);
+	LayoutContextGif(ContextResult *result, DocumentData *doc, bool saved);
 
-	virtual void setPosition(int32 position, int32 width);
+	virtual void setPosition(int32 position);
 	virtual void initDimensions();
 
+	virtual bool fullLine() const {
+		return false;
+	}
+
 	virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const;
 	virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const;
 	virtual void linkOver(const TextLinkPtr &lnk);
@@ -547,6 +556,17 @@ public:
 private:
 	QSize countFrameSize() const;
 
+	int32 content_width() const;
+	int32 content_height() const;
+	bool content_loading() const;
+	bool content_displayLoading() const;
+	bool content_loaded() const;
+	float64 content_progress() const;
+	void content_automaticLoad() const;
+	void content_forget();
+	FileLocation content_location() const;
+	QByteArray content_data() const;
+
 	enum StateFlags {
 		StateOver       = 0x01,
 		StateDeleteOver = 0x02,
@@ -559,6 +579,7 @@ private:
 		return (!_gif || _gif == BadClipReader) ? false : true;
 	}
 	QPixmap _thumb;
+	void prepareThumb(int32 width, int32 height, const QSize &frame) const;
 
 	void ensureAnimation() const;
 	bool isRadialAnimation(uint64 ms) const;
diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp
index 464d832e4..e7ed45606 100644
--- a/Telegram/SourceFiles/structs.cpp
+++ b/Telegram/SourceFiles/structs.cpp
@@ -1833,6 +1833,234 @@ WebPageData::WebPageData(const WebPageId &id, WebPageType type, const QString &u
 , pendingTill(pendingTill) {
 }
 
+namespace {
+	ImageLinkManager manager;
+}
+
+void ImageLinkManager::init() {
+	if (manager) delete manager;
+	manager = new QNetworkAccessManager();
+	App::setProxySettings(*manager);
+
+	connect(manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), this, SLOT(onFailed(QNetworkReply*)));
+	connect(manager, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), this, SLOT(onFailed(QNetworkReply*)));
+	connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onFinished(QNetworkReply*)));
+
+	if (black) delete black;
+	QImage b(cIntRetinaFactor(), cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
+	{
+		QPainter p(&b);
+		p.fillRect(QRect(0, 0, cIntRetinaFactor(), cIntRetinaFactor()), st::white->b);
+	}
+	QPixmap p = QPixmap::fromImage(b, Qt::ColorOnly);
+	p.setDevicePixelRatio(cRetinaFactor());
+	black = new ImagePtr(p, "PNG");
+}
+
+void ImageLinkManager::reinit() {
+	if (manager) App::setProxySettings(*manager);
+}
+
+void ImageLinkManager::deinit() {
+	if (manager) {
+		delete manager;
+		manager = 0;
+	}
+	if (black) {
+		delete black;
+		black = 0;
+	}
+	dataLoadings.clear();
+	imageLoadings.clear();
+}
+
+void initImageLinkManager() {
+	manager.init();
+}
+
+void reinitImageLinkManager() {
+	manager.reinit();
+}
+
+void deinitImageLinkManager() {
+	manager.deinit();
+}
+
+void ImageLinkManager::getData(ImageLinkData *data) {
+	if (!manager) {
+		DEBUG_LOG(("App Error: getting image link data without manager init!"));
+		return failed(data);
+	}
+	QString url;
+	switch (data->type) {
+	case GoogleMapsLink: {
+		int32 w = st::locationSize.width(), h = st::locationSize.height();
+		int32 zoom = 13, scale = 1;
+		if (cScale() == dbisTwo || cRetina()) {
+			scale = 2;
+		} else {
+			w = convertScale(w);
+			h = convertScale(h);
+		}
+		url = qsl("https://maps.googleapis.com/maps/api/staticmap?center=") + data->id.mid(9) + qsl("&zoom=%1&size=%2x%3&maptype=roadmap&scale=%4&markers=color:red|size:big|").arg(zoom).arg(w).arg(h).arg(scale) + data->id.mid(9) + qsl("&sensor=false");
+		QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url)));
+		imageLoadings[reply] = data;
+	} break;
+	default: {
+		failed(data);
+	} break;
+	}
+}
+
+void ImageLinkManager::onFinished(QNetworkReply *reply) {
+	if (!manager) return;
+	if (reply->error() != QNetworkReply::NoError) return onFailed(reply);
+
+	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
+	if (statusCode.isValid()) {
+		int status = statusCode.toInt();
+		if (status == 301 || status == 302) {
+			QString loc = reply->header(QNetworkRequest::LocationHeader).toString();
+			if (!loc.isEmpty()) {
+				QMap<QNetworkReply*, ImageLinkData*>::iterator i = dataLoadings.find(reply);
+				if (i != dataLoadings.cend()) {
+					ImageLinkData *d = i.value();
+					if (serverRedirects.constFind(d) == serverRedirects.cend()) {
+						serverRedirects.insert(d, 1);
+					} else if (++serverRedirects[d] > MaxHttpRedirects) {
+						DEBUG_LOG(("Network Error: Too many HTTP redirects in onFinished() for image link: %1").arg(loc));
+						return onFailed(reply);
+					}
+					dataLoadings.erase(i);
+					dataLoadings.insert(manager->get(QNetworkRequest(loc)), d);
+					return;
+				} else if ((i = imageLoadings.find(reply)) != imageLoadings.cend()) {
+					ImageLinkData *d = i.value();
+					if (serverRedirects.constFind(d) == serverRedirects.cend()) {
+						serverRedirects.insert(d, 1);
+					} else if (++serverRedirects[d] > MaxHttpRedirects) {
+						DEBUG_LOG(("Network Error: Too many HTTP redirects in onFinished() for image link: %1").arg(loc));
+						return onFailed(reply);
+					}
+					imageLoadings.erase(i);
+					imageLoadings.insert(manager->get(QNetworkRequest(loc)), d);
+					return;
+				}
+			}
+		}
+		if (status != 200) {
+			DEBUG_LOG(("Network Error: Bad HTTP status received in onFinished() for image link: %1").arg(status));
+			return onFailed(reply);
+		}
+	}
+
+	ImageLinkData *d = 0;
+	QMap<QNetworkReply*, ImageLinkData*>::iterator i = dataLoadings.find(reply);
+	if (i != dataLoadings.cend()) {
+		d = i.value();
+		dataLoadings.erase(i);
+
+		QJsonParseError e;
+		QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &e);
+		if (e.error != QJsonParseError::NoError) {
+			DEBUG_LOG(("JSON Error: Bad json received in onFinished() for image link"));
+			return onFailed(reply);
+		}
+		switch (d->type) {
+		case GoogleMapsLink: failed(d); break;
+		}
+
+		if (App::main()) App::main()->update();
+	} else {
+		i = imageLoadings.find(reply);
+		if (i != imageLoadings.cend()) {
+			d = i.value();
+			imageLoadings.erase(i);
+
+			QPixmap thumb;
+			QByteArray format;
+			QByteArray data(reply->readAll());
+			{
+				QBuffer buffer(&data);
+				QImageReader reader(&buffer);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
+				reader.setAutoTransform(true);
+#endif
+				thumb = QPixmap::fromImageReader(&reader, Qt::ColorOnly);
+				format = reader.format();
+				thumb.setDevicePixelRatio(cRetinaFactor());
+				if (format.isEmpty()) format = QByteArray("JPG");
+			}
+			d->loading = false;
+			d->thumb = thumb.isNull() ? (*black) : ImagePtr(thumb, format);
+			serverRedirects.remove(d);
+			if (App::main()) App::main()->update();
+		}
+	}
+}
+
+void ImageLinkManager::onFailed(QNetworkReply *reply) {
+	if (!manager) return;
+
+	ImageLinkData *d = 0;
+	QMap<QNetworkReply*, ImageLinkData*>::iterator i = dataLoadings.find(reply);
+	if (i != dataLoadings.cend()) {
+		d = i.value();
+		dataLoadings.erase(i);
+	} else {
+		i = imageLoadings.find(reply);
+		if (i != imageLoadings.cend()) {
+			d = i.value();
+			imageLoadings.erase(i);
+		}
+	}
+	DEBUG_LOG(("Network Error: failed to get data for image link %1, error %2").arg(d ? d->id : 0).arg(reply->errorString()));
+	if (d) {
+		failed(d);
+	}
+}
+
+void ImageLinkManager::failed(ImageLinkData *data) {
+	data->loading = false;
+	data->thumb = *black;
+	serverRedirects.remove(data);
+}
+
+void ImageLinkData::load() {
+	if (!thumb->isNull()) return thumb->load(false, false);
+	if (loading) return;
+
+	loading = true;
+	manager.getData(this);
+}
+
+void ContextResult::automaticLoadGif() const {
+
+}
+
+QByteArray ContextResult::data() const {
+	return _data;
+}
+
+bool ContextResult::loading() const {
+	return false;
+}
+
+bool ContextResult::loaded() const {
+	return false;
+}
+
+bool ContextResult::displayLoading() const {
+	return false;
+}
+
+void ContextResult::forget() {
+}
+
+float64 ContextResult::progress() const {
+	return 0.;
+}
+
 void PeerLink::onClick(Qt::MouseButton button) const {
 	if (button == Qt::LeftButton && App::main()) {
 		if (peer() && peer()->isChannel() && App::main()->historyPeer() != peer()) {
diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h
index 2a9d1b463..16bb39f0c 100644
--- a/Telegram/SourceFiles/structs.h
+++ b/Telegram/SourceFiles/structs.h
@@ -1289,6 +1289,93 @@ struct WebPageData {
 
 };
 
+void initImageLinkManager();
+void reinitImageLinkManager();
+void deinitImageLinkManager();
+
+enum ImageLinkType {
+	InvalidImageLink = 0,
+	GoogleMapsLink
+};
+struct ImageLinkData {
+	ImageLinkData(const QString &id) : id(id), type(InvalidImageLink), loading(false) {
+	}
+
+	QString id;
+	ImagePtr thumb;
+	ImageLinkType type;
+	bool loading;
+
+	void load();
+};
+
+class ImageLinkManager : public QObject {
+	Q_OBJECT
+public:
+	ImageLinkManager() : manager(0), black(0) {
+	}
+	void init();
+	void reinit();
+	void deinit();
+
+	void getData(ImageLinkData *data);
+
+	~ImageLinkManager() {
+		deinit();
+	}
+
+	public slots:
+	void onFinished(QNetworkReply *reply);
+	void onFailed(QNetworkReply *reply);
+
+private:
+	void failed(ImageLinkData *data);
+
+	QNetworkAccessManager *manager;
+	QMap<QNetworkReply*, ImageLinkData*> dataLoadings, imageLoadings;
+	QMap<ImageLinkData*, int32> serverRedirects;
+	ImagePtr *black;
+};
+
+class ContextResult {
+public:
+	ContextResult(uint64 queryId)
+		: queryId(queryId)
+		, doc(0)
+		, photo(0)
+		, width(0)
+		, height(0)
+		, duration(0)
+		, noWebPage(false) {
+	}
+	uint64 queryId;
+	QString id, type;
+	DocumentData *doc;
+	PhotoData *photo;
+	QString title, description, url, thumb_url;
+	QString content_type, content_url;
+	int32 width, height, duration;
+
+	QString message; // botContextMessageText
+	bool noWebPage;
+	EntitiesInText entities;
+	QString caption; // if message.isEmpty() use botContextMessageMediaAuto
+
+	ImagePtr thumb;
+	void automaticLoadGif() const;
+	QByteArray data() const;
+	bool loading() const;
+	bool loaded() const;
+	bool displayLoading() const;
+	void forget();
+	float64 progress() const;
+
+private:
+	QByteArray _data;
+
+};
+typedef QList<ContextResult*> ContextResults;
+
 QString saveFileName(const QString &title, const QString &filter, const QString &prefix, QString name, bool savingAs, const QDir &dir = QDir());
 MsgId clientMsgId();
 
diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj
index f0f085171..f1bb666d9 100644
--- a/Telegram/Telegram.vcxproj
+++ b/Telegram/Telegram.vcxproj
@@ -255,10 +255,6 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
-    <ClCompile Include="GeneratedFiles\Debug\moc_history.cpp">
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
-    </ClCompile>
     <ClCompile Include="GeneratedFiles\Debug\moc_historywidget.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -389,6 +385,10 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_structs.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Debug\moc_sysbuttons.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -513,10 +513,6 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
-    <ClCompile Include="GeneratedFiles\Deploy\moc_history.cpp">
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
-    </ClCompile>
     <ClCompile Include="GeneratedFiles\Deploy\moc_historywidget.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -647,6 +643,10 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Deploy\moc_structs.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Deploy\moc_sysbuttons.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -797,10 +797,6 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
     </ClCompile>
-    <ClCompile Include="GeneratedFiles\Release\moc_history.cpp">
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
-    </ClCompile>
     <ClCompile Include="GeneratedFiles\Release\moc_historywidget.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@@ -931,6 +927,10 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_structs.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Release\moc_sysbuttons.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@@ -1625,18 +1625,24 @@
     <ClInclude Include="SourceFiles\gui\style_core.h" />
     <ClInclude Include="SourceFiles\gui\text.h" />
     <CustomBuild Include="SourceFiles\history.h">
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing history.h...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS  "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl\Release\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui" "-fstdafx.h" "-f../../SourceFiles/history.h"</Command>
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing history.h...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS  "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl_debug\Debug\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui" "-fstdafx.h" "-f../../SourceFiles/history.h"</Command>
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing history.h...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS  "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl\Release\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui" "-fstdafx.h" "-f../../SourceFiles/history.h"</Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">
+      </Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">
+      </Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">
+      </Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+      </Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+      </Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+      </Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+      </Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+      </Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+      </Command>
     </CustomBuild>
     <CustomBuild Include="SourceFiles\historywidget.h">
       <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing historywidget.h...</Message>
@@ -2018,7 +2024,20 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClInclude>
     <ClInclude Include="SourceFiles\settings.h" />
-    <ClInclude Include="SourceFiles\structs.h" />
+    <CustomBuild Include="SourceFiles\structs.h">
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing structs.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/structs.h"  -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl\Release\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing structs.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/structs.h"  -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl_debug\Debug\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing structs.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/structs.h"  -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl\Release\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui"</Command>
+    </CustomBuild>
     <ClInclude Include="SourceFiles\style.h" />
     <CustomBuild Include="SourceFiles\sysbuttons.h">
       <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing sysbuttons.h...</Message>
diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters
index ac2f9642c..f3c736802 100644
--- a/Telegram/Telegram.vcxproj.filters
+++ b/Telegram/Telegram.vcxproj.filters
@@ -663,15 +663,6 @@
     <ClCompile Include="GeneratedFiles\Release\moc_usernamebox.cpp">
       <Filter>Generated Files\Release</Filter>
     </ClCompile>
-    <ClCompile Include="GeneratedFiles\Deploy\moc_history.cpp">
-      <Filter>Generated Files\Deploy</Filter>
-    </ClCompile>
-    <ClCompile Include="GeneratedFiles\Debug\moc_history.cpp">
-      <Filter>Generated Files\Debug</Filter>
-    </ClCompile>
-    <ClCompile Include="GeneratedFiles\Release\moc_history.cpp">
-      <Filter>Generated Files\Release</Filter>
-    </ClCompile>
     <ClCompile Include="SourceFiles\types.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
@@ -897,6 +888,15 @@
     <ClCompile Include="SourceFiles\layout.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Deploy\moc_structs.cpp">
+      <Filter>Generated Files\Deploy</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_structs.cpp">
+      <Filter>Generated Files\Debug</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_structs.cpp">
+      <Filter>Generated Files\Release</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="SourceFiles\stdafx.h">
@@ -977,9 +977,6 @@
     <ClInclude Include="SourceFiles\pspecific_mac_p.h">
       <Filter>Source Files</Filter>
     </ClInclude>
-    <ClInclude Include="SourceFiles\structs.h">
-      <Filter>Source Files</Filter>
-    </ClInclude>
     <ClInclude Include="SourceFiles\numbers.h">
       <Filter>Source Files</Filter>
     </ClInclude>
@@ -1210,6 +1207,9 @@
     <CustomBuild Include="SourceFiles\layout.h">
       <Filter>Source Files</Filter>
     </CustomBuild>
+    <CustomBuild Include="SourceFiles\structs.h">
+      <Filter>Source Files</Filter>
+    </CustomBuild>
   </ItemGroup>
   <ItemGroup>
     <None Include="SourceFiles\langs\lang_it.strings">