mirror of https://github.com/procxx/kepka.git
				
				
				
			Moved History[Media] classes to history_media_types module.
This commit is contained in:
		
							parent
							
								
									d277b0d4bb
								
							
						
					
					
						commit
						538ffb9727
					
				
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 177 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 241 KiB  | 
| 
						 | 
					@ -1232,7 +1232,7 @@ introErrLabelTextStyle: textStyle(defaultTextStyle) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mediaPadding: margins(0px, 0px, 0px, 0px);//1px, 1px, 1px, 1px);//2px, 2px, 2px, 2px);
 | 
					mediaPadding: margins(0px, 0px, 0px, 0px);//1px, 1px, 1px, 1px);//2px, 2px, 2px, 2px);
 | 
				
			||||||
mediaCaptionSkip: 5px;
 | 
					mediaCaptionSkip: 5px;
 | 
				
			||||||
mediaHeaderSkip: 5px;
 | 
					mediaInBubbleSkip: 5px;
 | 
				
			||||||
mediaThumbSize: 48px;
 | 
					mediaThumbSize: 48px;
 | 
				
			||||||
mediaNameTop: 3px;
 | 
					mediaNameTop: 3px;
 | 
				
			||||||
mediaDetailsShift: 3px;
 | 
					mediaDetailsShift: 3px;
 | 
				
			||||||
| 
						 | 
					@ -2285,7 +2285,6 @@ webPageLeft: 10px;
 | 
				
			||||||
webPageBar: 2px;
 | 
					webPageBar: 2px;
 | 
				
			||||||
webPageTitleFont: semiboldFont;
 | 
					webPageTitleFont: semiboldFont;
 | 
				
			||||||
webPageDescriptionFont: normalFont;
 | 
					webPageDescriptionFont: normalFont;
 | 
				
			||||||
webPagePhotoSkip: 5px;
 | 
					 | 
				
			||||||
webPagePhotoSize: 100px;
 | 
					webPagePhotoSize: 100px;
 | 
				
			||||||
webPagePhotoDelta: 8px;
 | 
					webPagePhotoDelta: 8px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "data/data_abstract_structure.h"
 | 
					#include "data/data_abstract_structure.h"
 | 
				
			||||||
#include "history/history_service_layout.h"
 | 
					#include "history/history_service_layout.h"
 | 
				
			||||||
#include "history/history_location_manager.h"
 | 
					#include "history/history_location_manager.h"
 | 
				
			||||||
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
#include "media/media_audio.h"
 | 
					#include "media/media_audio.h"
 | 
				
			||||||
#include "inline_bots/inline_bot_layout_item.h"
 | 
					#include "inline_bots/inline_bot_layout_item.h"
 | 
				
			||||||
#include "application.h"
 | 
					#include "application.h"
 | 
				
			||||||
| 
						 | 
					@ -1523,7 +1524,7 @@ namespace {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GameData *feedGame(const MTPDgame &game, GameData *convert) {
 | 
						GameData *feedGame(const MTPDgame &game, GameData *convert) {
 | 
				
			||||||
		return App::gameSet(game.vid.v, convert, game.vaccess_hash.v, qs(game.vshort_name), qs(game.vtitle), qs(game.vdescription), qs(game.vurl), App::feedPhoto(game.vphoto), game.has_document() ? App::feedDocument(game.vdocument) : nullptr);
 | 
							return App::gameSet(game.vid.v, convert, game.vaccess_hash.v, qs(game.vshort_name), qs(game.vtitle), qs(game.vdescription), App::feedPhoto(game.vphoto), game.has_document() ? App::feedDocument(game.vdocument) : nullptr);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	UserData *curUser() {
 | 
						UserData *curUser() {
 | 
				
			||||||
| 
						 | 
					@ -1847,7 +1848,7 @@ namespace {
 | 
				
			||||||
		return i.value();
 | 
							return i.value();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, const QString &url, PhotoData *photo, DocumentData *document) {
 | 
						GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, PhotoData *photo, DocumentData *document) {
 | 
				
			||||||
		if (convert) {
 | 
							if (convert) {
 | 
				
			||||||
			if (convert->id != game) {
 | 
								if (convert->id != game) {
 | 
				
			||||||
				auto i = gamesData.find(convert->id);
 | 
									auto i = gamesData.find(convert->id);
 | 
				
			||||||
| 
						 | 
					@ -1856,12 +1857,11 @@ namespace {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				convert->id = game;
 | 
									convert->id = game;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (convert->url.isEmpty() && !url.isEmpty()) {
 | 
								if (convert->shortName.isEmpty() && !shortName.isEmpty()) {
 | 
				
			||||||
				convert->accessHash = accessHash;
 | 
									convert->accessHash = accessHash;
 | 
				
			||||||
				convert->shortName = shortName;
 | 
									convert->shortName = shortName;
 | 
				
			||||||
				convert->title = title;
 | 
									convert->title = title;
 | 
				
			||||||
				convert->description = description;
 | 
									convert->description = description;
 | 
				
			||||||
				convert->url = url;
 | 
					 | 
				
			||||||
				convert->photo = photo;
 | 
									convert->photo = photo;
 | 
				
			||||||
				convert->document = document;
 | 
									convert->document = document;
 | 
				
			||||||
				if (App::main()) App::main()->gameUpdated(convert);
 | 
									if (App::main()) App::main()->gameUpdated(convert);
 | 
				
			||||||
| 
						 | 
					@ -1873,18 +1873,17 @@ namespace {
 | 
				
			||||||
			if (convert) {
 | 
								if (convert) {
 | 
				
			||||||
				result = convert;
 | 
									result = convert;
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				result = new GameData(game, accessHash, shortName, title, description, url, photo, document);
 | 
									result = new GameData(game, accessHash, shortName, title, description, photo, document);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			gamesData.insert(game, result);
 | 
								gamesData.insert(game, result);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			result = i.value();
 | 
								result = i.value();
 | 
				
			||||||
			if (result != convert) {
 | 
								if (result != convert) {
 | 
				
			||||||
				if (result->url.isEmpty() && !url.isEmpty()) {
 | 
									if (result->shortName.isEmpty() && !shortName.isEmpty()) {
 | 
				
			||||||
					result->accessHash = accessHash;
 | 
										result->accessHash = accessHash;
 | 
				
			||||||
					result->shortName = shortName;
 | 
										result->shortName = shortName;
 | 
				
			||||||
					result->title = title;
 | 
										result->title = title;
 | 
				
			||||||
					result->description = description;
 | 
										result->description = description;
 | 
				
			||||||
					result->url = url;
 | 
					 | 
				
			||||||
					result->photo = photo;
 | 
										result->photo = photo;
 | 
				
			||||||
					result->document = document;
 | 
										result->document = document;
 | 
				
			||||||
					if (App::main()) App::main()->gameUpdated(result);
 | 
										if (App::main()) App::main()->gameUpdated(result);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,6 +44,9 @@ using GifItems = QHash<Media::Clip::Reader*, HistoryItem*>;
 | 
				
			||||||
using PhotosData = QHash<PhotoId, PhotoData*>;
 | 
					using PhotosData = QHash<PhotoId, PhotoData*>;
 | 
				
			||||||
using DocumentsData = QHash<DocumentId, DocumentData*>;
 | 
					using DocumentsData = QHash<DocumentId, DocumentData*>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct LocationCoords;
 | 
				
			||||||
 | 
					struct LocationData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace App {
 | 
					namespace App {
 | 
				
			||||||
	AppClass *app();
 | 
						AppClass *app();
 | 
				
			||||||
	MainWindow *wnd();
 | 
						MainWindow *wnd();
 | 
				
			||||||
| 
						 | 
					@ -154,7 +157,7 @@ namespace App {
 | 
				
			||||||
	WebPageData *webPage(const WebPageId &webPage);
 | 
						WebPageData *webPage(const WebPageId &webPage);
 | 
				
			||||||
	WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill);
 | 
						WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill);
 | 
				
			||||||
	GameData *game(const GameId &game);
 | 
						GameData *game(const GameId &game);
 | 
				
			||||||
	GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, const QString &url, PhotoData *photo, DocumentData *doc);
 | 
						GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc);
 | 
				
			||||||
	LocationData *location(const LocationCoords &coords);
 | 
						LocationData *location(const LocationCoords &coords);
 | 
				
			||||||
	void forgetMedia();
 | 
						void forgetMedia();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "localstorage.h"
 | 
					#include "localstorage.h"
 | 
				
			||||||
#include "mainwidget.h"
 | 
					#include "mainwidget.h"
 | 
				
			||||||
#include "photosendbox.h"
 | 
					#include "photosendbox.h"
 | 
				
			||||||
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxWideWidth)
 | 
					PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxWideWidth)
 | 
				
			||||||
, _file(file)
 | 
					, _file(file)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "stdafx.h"
 | 
					#include "stdafx.h"
 | 
				
			||||||
#include "history.h"
 | 
					#include "history.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
#include "dialogs/dialogs_indexed_list.h"
 | 
					#include "dialogs/dialogs_indexed_list.h"
 | 
				
			||||||
#include "styles/style_dialogs.h"
 | 
					#include "styles/style_dialogs.h"
 | 
				
			||||||
#include "data/data_drafts.h"
 | 
					#include "data/data_drafts.h"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,84 +28,64 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "styles/style_dialogs.h"
 | 
					#include "styles/style_dialogs.h"
 | 
				
			||||||
#include "fileuploader.h"
 | 
					#include "fileuploader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReplyMarkupClickHandler : public LeftButtonClickHandler {
 | 
					ReplyMarkupClickHandler::ReplyMarkupClickHandler(const HistoryItem *item, int row, int col)
 | 
				
			||||||
public:
 | 
					: _itemId(item->fullId())
 | 
				
			||||||
	ReplyMarkupClickHandler(const HistoryItem *item, int row, int col) : _itemId(item->fullId()), _row(row), _col(col) {
 | 
					, _row(row)
 | 
				
			||||||
	}
 | 
					, _col(col) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QString tooltip() const override {
 | 
					// Copy to clipboard support.
 | 
				
			||||||
		return _fullDisplayed ? QString() : buttonText();
 | 
					void ReplyMarkupClickHandler::copyToClipboard() const {
 | 
				
			||||||
 | 
						if (auto button = getButton()) {
 | 
				
			||||||
 | 
							if (button->type == HistoryMessageReplyMarkup::Button::Type::Url) {
 | 
				
			||||||
 | 
								auto url = QString::fromUtf8(button->data);
 | 
				
			||||||
 | 
								if (!url.isEmpty()) {
 | 
				
			||||||
 | 
									QApplication::clipboard()->setText(url);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void setFullDisplayed(bool full) {
 | 
					QString ReplyMarkupClickHandler::copyToClipboardContextItemText() const {
 | 
				
			||||||
		_fullDisplayed = full;
 | 
						if (auto button = getButton()) {
 | 
				
			||||||
 | 
							if (button->type == HistoryMessageReplyMarkup::Button::Type::Url) {
 | 
				
			||||||
 | 
								return lang(lng_context_copy_link);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return QString();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Copy to clipboard support.
 | 
					// Finds the corresponding button in the items markup struct.
 | 
				
			||||||
	void copyToClipboard() const override {
 | 
					// If the button is not found it returns nullptr.
 | 
				
			||||||
		if (auto button = getButton()) {
 | 
					// Note: it is possible that we will point to the different button
 | 
				
			||||||
			if (button->type == HistoryMessageReplyMarkup::Button::Type::Url) {
 | 
					// than the one was used when constructing the handler, but not a big deal.
 | 
				
			||||||
				auto url = QString::fromUtf8(button->data);
 | 
					const HistoryMessageReplyMarkup::Button *ReplyMarkupClickHandler::getButton() const {
 | 
				
			||||||
				if (!url.isEmpty()) {
 | 
						if (auto item = App::histItemById(_itemId)) {
 | 
				
			||||||
					QApplication::clipboard()->setText(url);
 | 
							if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
 | 
				
			||||||
 | 
								if (_row < markup->rows.size()) {
 | 
				
			||||||
 | 
									auto &row = markup->rows.at(_row);
 | 
				
			||||||
 | 
									if (_col < row.size()) {
 | 
				
			||||||
 | 
										return &row.at(_col);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	QString copyToClipboardContextItemText() const override {
 | 
						return nullptr;
 | 
				
			||||||
		if (auto button = getButton()) {
 | 
					}
 | 
				
			||||||
			if (button->type == HistoryMessageReplyMarkup::Button::Type::Url) {
 | 
					
 | 
				
			||||||
				return lang(lng_context_copy_link);
 | 
					void ReplyMarkupClickHandler::onClickImpl() const {
 | 
				
			||||||
			}
 | 
						if (auto item = App::histItemById(_itemId)) {
 | 
				
			||||||
		}
 | 
							App::activateBotCommand(item, _row, _col);
 | 
				
			||||||
		return QString();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Finds the corresponding button in the items markup struct.
 | 
					// Returns the full text of the corresponding button.
 | 
				
			||||||
	// If the button is not found it returns nullptr.
 | 
					QString ReplyMarkupClickHandler::buttonText() const {
 | 
				
			||||||
	// Note: it is possible that we will point to the different button
 | 
						if (auto button = getButton()) {
 | 
				
			||||||
	// than the one was used when constructing the handler, but not a big deal.
 | 
							return button->text;
 | 
				
			||||||
	const HistoryMessageReplyMarkup::Button *getButton() const {
 | 
					 | 
				
			||||||
		if (auto item = App::histItemById(_itemId)) {
 | 
					 | 
				
			||||||
			if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
 | 
					 | 
				
			||||||
				if (_row < markup->rows.size()) {
 | 
					 | 
				
			||||||
					auto &row = markup->rows.at(_row);
 | 
					 | 
				
			||||||
					if (_col < row.size()) {
 | 
					 | 
				
			||||||
						return &row.at(_col);
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return nullptr;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return QString();
 | 
				
			||||||
	// We hold only FullMsgId, not HistoryItem*, because all click handlers
 | 
					}
 | 
				
			||||||
	// are activated async and the item may be already destroyed.
 | 
					 | 
				
			||||||
	void setMessageId(const FullMsgId &msgId) {
 | 
					 | 
				
			||||||
		_itemId = msgId;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	void onClickImpl() const override {
 | 
					 | 
				
			||||||
		if (auto item = App::histItemById(_itemId)) {
 | 
					 | 
				
			||||||
			App::activateBotCommand(item, _row, _col);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	FullMsgId _itemId;
 | 
					 | 
				
			||||||
	int _row, _col;
 | 
					 | 
				
			||||||
	bool _fullDisplayed = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Returns the full text of the corresponding button.
 | 
					 | 
				
			||||||
	QString buttonText() const {
 | 
					 | 
				
			||||||
		if (auto button = getButton()) {
 | 
					 | 
				
			||||||
			return button->text;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return QString();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s)
 | 
					ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s)
 | 
				
			||||||
	: _item(item)
 | 
						: _item(item)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -230,7 +230,47 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReplyMarkupClickHandler;
 | 
					class ReplyMarkupClickHandler : public LeftButtonClickHandler {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						ReplyMarkupClickHandler(const HistoryItem *item, int row, int col);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QString tooltip() const override {
 | 
				
			||||||
 | 
							return _fullDisplayed ? QString() : buttonText();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void setFullDisplayed(bool full) {
 | 
				
			||||||
 | 
							_fullDisplayed = full;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Copy to clipboard support.
 | 
				
			||||||
 | 
						void copyToClipboard() const override;
 | 
				
			||||||
 | 
						QString copyToClipboardContextItemText() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Finds the corresponding button in the items markup struct.
 | 
				
			||||||
 | 
						// If the button is not found it returns nullptr.
 | 
				
			||||||
 | 
						// Note: it is possible that we will point to the different button
 | 
				
			||||||
 | 
						// than the one was used when constructing the handler, but not a big deal.
 | 
				
			||||||
 | 
						const HistoryMessageReplyMarkup::Button *getButton() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We hold only FullMsgId, not HistoryItem*, because all click handlers
 | 
				
			||||||
 | 
						// are activated async and the item may be already destroyed.
 | 
				
			||||||
 | 
						void setMessageId(const FullMsgId &msgId) {
 | 
				
			||||||
 | 
							_itemId = msgId;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						void onClickImpl() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						FullMsgId _itemId;
 | 
				
			||||||
 | 
						int _row, _col;
 | 
				
			||||||
 | 
						bool _fullDisplayed = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Returns the full text of the corresponding button.
 | 
				
			||||||
 | 
						QString buttonText() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReplyKeyboard {
 | 
					class ReplyKeyboard {
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	struct Button;
 | 
						struct Button;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,38 +20,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void historyInitMedia();
 | 
					enum class MediaInBubbleState {
 | 
				
			||||||
 | 
						None,
 | 
				
			||||||
class RadialAnimation {
 | 
						Top,
 | 
				
			||||||
public:
 | 
						Middle,
 | 
				
			||||||
	RadialAnimation(AnimationCallbacks &&callbacks);
 | 
						Bottom,
 | 
				
			||||||
 | 
					 | 
				
			||||||
	float64 opacity() const {
 | 
					 | 
				
			||||||
		return _opacity;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool animating() const {
 | 
					 | 
				
			||||||
		return _animation.animating();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void start(float64 prg);
 | 
					 | 
				
			||||||
	void update(float64 prg, bool finished, uint64 ms);
 | 
					 | 
				
			||||||
	void stop();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void step(uint64 ms);
 | 
					 | 
				
			||||||
	void step() {
 | 
					 | 
				
			||||||
		step(getms());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	uint64 _firstStart = 0;
 | 
					 | 
				
			||||||
	uint64 _lastStart = 0;
 | 
					 | 
				
			||||||
	uint64 _lastTime = 0;
 | 
					 | 
				
			||||||
	float64 _opacity = 0.;
 | 
					 | 
				
			||||||
	anim::ivalue a_arcEnd, a_arcStart;
 | 
					 | 
				
			||||||
	Animation _animation;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HistoryMedia : public HistoryElement {
 | 
					class HistoryMedia : public HistoryElement {
 | 
				
			||||||
| 
						 | 
					@ -67,7 +40,10 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Returns text with link-start and link-end commands for service-color highlighting.
 | 
						// Returns text with link-start and link-end commands for service-color highlighting.
 | 
				
			||||||
	// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
 | 
						// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
 | 
				
			||||||
	virtual QString inDialogsText() const;
 | 
						virtual QString inDialogsText() const {
 | 
				
			||||||
 | 
							auto result = notificationText();
 | 
				
			||||||
 | 
							return result.isEmpty() ? QString() : textcmdLink(1, textClean(result));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	virtual TextWithEntities selectedText(TextSelection selection) const = 0;
 | 
						virtual TextWithEntities selectedText(TextSelection selection) const = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool hasPoint(int x, int y) const {
 | 
						bool hasPoint(int x, int y) const {
 | 
				
			||||||
| 
						 | 
					@ -177,881 +153,22 @@ public:
 | 
				
			||||||
		return _width;
 | 
							return _width;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void setInBubbleState(MediaInBubbleState state) {
 | 
				
			||||||
 | 
							_inBubbleState = state;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						MediaInBubbleState inBubbleState() const {
 | 
				
			||||||
 | 
							return _inBubbleState;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool isBubbleTop() const {
 | 
				
			||||||
 | 
							return (_inBubbleState == MediaInBubbleState::Top) || (_inBubbleState == MediaInBubbleState::None);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool isBubbleBottom() const {
 | 
				
			||||||
 | 
							return (_inBubbleState == MediaInBubbleState::Bottom) || (_inBubbleState == MediaInBubbleState::None);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
	HistoryItem *_parent;
 | 
						HistoryItem *_parent;
 | 
				
			||||||
	int _width = 0;
 | 
						int _width = 0;
 | 
				
			||||||
 | 
						MediaInBubbleState _inBubbleState = MediaInBubbleState::None;
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HistoryFileMedia : public HistoryMedia {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	using HistoryMedia::HistoryMedia;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return p == _openl || p == _savel || p == _cancell;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return p == _openl || p == _savel || p == _cancell;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
 | 
					 | 
				
			||||||
	void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	~HistoryFileMedia();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	ClickHandlerPtr _openl, _savel, _cancell;
 | 
					 | 
				
			||||||
	void setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell);
 | 
					 | 
				
			||||||
	void setDocumentLinks(DocumentData *document, bool inlinegif = false) {
 | 
					 | 
				
			||||||
		ClickHandlerPtr open, save;
 | 
					 | 
				
			||||||
		if (inlinegif) {
 | 
					 | 
				
			||||||
			open.reset(new GifOpenClickHandler(document));
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			open.reset(new DocumentOpenClickHandler(document));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (inlinegif) {
 | 
					 | 
				
			||||||
			save.reset(new GifOpenClickHandler(document));
 | 
					 | 
				
			||||||
		} else if (document->voice()) {
 | 
					 | 
				
			||||||
			save.reset(new DocumentOpenClickHandler(document));
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			save.reset(new DocumentSaveClickHandler(document));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		setLinks(std_::move(open), std_::move(save), MakeShared<DocumentCancelClickHandler>(document));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// >= 0 will contain download / upload string, _statusSize = loaded bytes
 | 
					 | 
				
			||||||
	// < 0 will contain played string, _statusSize = -(seconds + 1) played
 | 
					 | 
				
			||||||
	// 0x7FFFFFF0 will contain status for not yet downloaded file
 | 
					 | 
				
			||||||
	// 0x7FFFFFF1 will contain status for already downloaded file
 | 
					 | 
				
			||||||
	// 0x7FFFFFF2 will contain status for failed to download / upload file
 | 
					 | 
				
			||||||
	mutable int32 _statusSize;
 | 
					 | 
				
			||||||
	mutable QString _statusText;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// duration = -1 - no duration, duration = -2 - "GIF" duration
 | 
					 | 
				
			||||||
	void setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void step_thumbOver(float64 ms, bool timer);
 | 
					 | 
				
			||||||
	void step_radial(uint64 ms, bool timer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void ensureAnimation() const;
 | 
					 | 
				
			||||||
	void checkAnimationFinished();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool isRadialAnimation(uint64 ms) const {
 | 
					 | 
				
			||||||
		if (!_animation || !_animation->radial.animating()) return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		_animation->radial.step(ms);
 | 
					 | 
				
			||||||
		return _animation && _animation->radial.animating();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool isThumbAnimation(uint64 ms) const {
 | 
					 | 
				
			||||||
		if (!_animation || !_animation->_a_thumbOver.animating()) return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		_animation->_a_thumbOver.step(ms);
 | 
					 | 
				
			||||||
		return _animation && _animation->_a_thumbOver.animating();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	virtual float64 dataProgress() const = 0;
 | 
					 | 
				
			||||||
	virtual bool dataFinished() const = 0;
 | 
					 | 
				
			||||||
	virtual bool dataLoaded() const = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct AnimationData {
 | 
					 | 
				
			||||||
		AnimationData(AnimationCallbacks &&thumbOverCallbacks, AnimationCallbacks &&radialCallbacks) : a_thumbOver(0, 0)
 | 
					 | 
				
			||||||
			, _a_thumbOver(std_::move(thumbOverCallbacks))
 | 
					 | 
				
			||||||
			, radial(std_::move(radialCallbacks)) {
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		anim::fvalue a_thumbOver;
 | 
					 | 
				
			||||||
		Animation _a_thumbOver;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		RadialAnimation radial;
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	mutable AnimationData *_animation = nullptr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HistoryPhoto : public HistoryFileMedia {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	HistoryPhoto(HistoryItem *parent, PhotoData *photo, const QString &caption);
 | 
					 | 
				
			||||||
	HistoryPhoto(HistoryItem *parent, PeerData *chat, const MTPDphoto &photo, int width);
 | 
					 | 
				
			||||||
	HistoryPhoto(HistoryItem *parent, const HistoryPhoto &other);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void init();
 | 
					 | 
				
			||||||
	HistoryMediaType type() const override {
 | 
					 | 
				
			||||||
		return MediaTypePhoto;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	HistoryPhoto *clone(HistoryItem *newParent) const override {
 | 
					 | 
				
			||||||
		return new HistoryPhoto(newParent, *this);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initDimensions() override;
 | 
					 | 
				
			||||||
	int resizeGetHeight(int width) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
					 | 
				
			||||||
	HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
 | 
					 | 
				
			||||||
		return _caption.adjustSelection(selection, type);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool hasTextForCopy() const override {
 | 
					 | 
				
			||||||
		return !_caption.isEmpty();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	QString notificationText() const override;
 | 
					 | 
				
			||||||
	QString inDialogsText() const override;
 | 
					 | 
				
			||||||
	TextWithEntities selectedText(TextSelection selection) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	PhotoData *photo() const {
 | 
					 | 
				
			||||||
		return _data;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void updateSentMedia(const MTPMessageMedia &media) override;
 | 
					 | 
				
			||||||
	bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void attachToParent() override;
 | 
					 | 
				
			||||||
	void detachFromParent() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool hasReplyPreview() const override {
 | 
					 | 
				
			||||||
		return !_data->thumb->isNull();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ImagePtr replyPreview() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextWithEntities getCaption() const override {
 | 
					 | 
				
			||||||
		return _caption.originalTextWithEntities();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool needsBubble() const override {
 | 
					 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (_parent->viaBot()) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool customInfoLayout() const override {
 | 
					 | 
				
			||||||
		return _caption.isEmpty();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool hideFromName() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	float64 dataProgress() const override {
 | 
					 | 
				
			||||||
		return _data->progress();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dataFinished() const override {
 | 
					 | 
				
			||||||
		return !_data->loading() && !_data->uploading();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dataLoaded() const override {
 | 
					 | 
				
			||||||
		return _data->loaded();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	PhotoData *_data;
 | 
					 | 
				
			||||||
	int16 _pixw = 1;
 | 
					 | 
				
			||||||
	int16 _pixh = 1;
 | 
					 | 
				
			||||||
	Text _caption;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HistoryVideo : public HistoryFileMedia {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	HistoryVideo(HistoryItem *parent, DocumentData *document, const QString &caption);
 | 
					 | 
				
			||||||
	HistoryVideo(HistoryItem *parent, const HistoryVideo &other);
 | 
					 | 
				
			||||||
	HistoryMediaType type() const override {
 | 
					 | 
				
			||||||
		return MediaTypeVideo;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	HistoryVideo *clone(HistoryItem *newParent) const override {
 | 
					 | 
				
			||||||
		return new HistoryVideo(newParent, *this);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initDimensions() override;
 | 
					 | 
				
			||||||
	int resizeGetHeight(int width) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
					 | 
				
			||||||
	HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
 | 
					 | 
				
			||||||
		return _caption.adjustSelection(selection, type);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool hasTextForCopy() const override {
 | 
					 | 
				
			||||||
		return !_caption.isEmpty();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	QString notificationText() const override;
 | 
					 | 
				
			||||||
	QString inDialogsText() const override;
 | 
					 | 
				
			||||||
	TextWithEntities selectedText(TextSelection selection) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DocumentData *getDocument() override {
 | 
					 | 
				
			||||||
		return _data;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool uploading() const override {
 | 
					 | 
				
			||||||
		return _data->uploading();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void attachToParent() override;
 | 
					 | 
				
			||||||
	void detachFromParent() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool hasReplyPreview() const override {
 | 
					 | 
				
			||||||
		return !_data->thumb->isNull();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ImagePtr replyPreview() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextWithEntities getCaption() const override {
 | 
					 | 
				
			||||||
		return _caption.originalTextWithEntities();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool needsBubble() const override {
 | 
					 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (_parent->viaBot()) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool customInfoLayout() const override {
 | 
					 | 
				
			||||||
		return _caption.isEmpty();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool hideFromName() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	float64 dataProgress() const override {
 | 
					 | 
				
			||||||
		return _data->progress();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dataFinished() const override {
 | 
					 | 
				
			||||||
		return !_data->loading() && !_data->uploading();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dataLoaded() const override {
 | 
					 | 
				
			||||||
		return _data->loaded();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	DocumentData *_data;
 | 
					 | 
				
			||||||
	int32 _thumbw;
 | 
					 | 
				
			||||||
	Text _caption;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void setStatusSize(int32 newSize) const;
 | 
					 | 
				
			||||||
	void updateStatusText() const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct HistoryDocumentThumbed : public BaseComponent<HistoryDocumentThumbed> {
 | 
					 | 
				
			||||||
	ClickHandlerPtr _linksavel, _linkcancell;
 | 
					 | 
				
			||||||
	int _thumbw = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mutable int _linkw = 0;
 | 
					 | 
				
			||||||
	mutable QString _link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
struct HistoryDocumentCaptioned : public BaseComponent<HistoryDocumentCaptioned> {
 | 
					 | 
				
			||||||
	Text _caption = { int(st::msgFileMinWidth) - st::msgPadding.left() - st::msgPadding.right() };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
struct HistoryDocumentNamed : public BaseComponent<HistoryDocumentNamed> {
 | 
					 | 
				
			||||||
	QString _name;
 | 
					 | 
				
			||||||
	int _namew = 0;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
class HistoryDocument;
 | 
					 | 
				
			||||||
struct HistoryDocumentVoicePlayback {
 | 
					 | 
				
			||||||
	HistoryDocumentVoicePlayback(const HistoryDocument *that);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int32 _position;
 | 
					 | 
				
			||||||
	anim::fvalue a_progress;
 | 
					 | 
				
			||||||
	Animation _a_progress;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
struct HistoryDocumentVoice : public BaseComponent<HistoryDocumentVoice> {
 | 
					 | 
				
			||||||
	HistoryDocumentVoice &operator=(HistoryDocumentVoice &&other) {
 | 
					 | 
				
			||||||
		std::swap(_playback, other._playback);
 | 
					 | 
				
			||||||
		return *this;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	~HistoryDocumentVoice() {
 | 
					 | 
				
			||||||
		deleteAndMark(_playback);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	void ensurePlayback(const HistoryDocument *interfaces) const;
 | 
					 | 
				
			||||||
	void checkPlaybackFinished() const;
 | 
					 | 
				
			||||||
	mutable HistoryDocumentVoicePlayback *_playback = nullptr;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HistoryDocument : public HistoryFileMedia, public Composer {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	HistoryDocument(HistoryItem *parent, DocumentData *document, const QString &caption);
 | 
					 | 
				
			||||||
	HistoryDocument(HistoryItem *parent, const HistoryDocument &other);
 | 
					 | 
				
			||||||
	HistoryMediaType type() const override {
 | 
					 | 
				
			||||||
		return _data->voice() ? MediaTypeVoiceFile : (_data->song() ? MediaTypeMusicFile : MediaTypeFile);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	HistoryDocument *clone(HistoryItem *newParent) const override {
 | 
					 | 
				
			||||||
		return new HistoryDocument(newParent, *this);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initDimensions() override;
 | 
					 | 
				
			||||||
	int resizeGetHeight(int width) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
					 | 
				
			||||||
	HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
 | 
					 | 
				
			||||||
		if (auto captioned = Get<HistoryDocumentCaptioned>()) {
 | 
					 | 
				
			||||||
			return captioned->_caption.adjustSelection(selection, type);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return selection;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool hasTextForCopy() const override {
 | 
					 | 
				
			||||||
		return Has<HistoryDocumentCaptioned>();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	QString notificationText() const override;
 | 
					 | 
				
			||||||
	QString inDialogsText() const override;
 | 
					 | 
				
			||||||
	TextWithEntities selectedText(TextSelection selection) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool uploading() const override {
 | 
					 | 
				
			||||||
		return _data->uploading();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DocumentData *getDocument() override {
 | 
					 | 
				
			||||||
		return _data;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void attachToParent() override;
 | 
					 | 
				
			||||||
	void detachFromParent() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void updateSentMedia(const MTPMessageMedia &media) override;
 | 
					 | 
				
			||||||
	bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool hasReplyPreview() const override {
 | 
					 | 
				
			||||||
		return !_data->thumb->isNull();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ImagePtr replyPreview() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextWithEntities getCaption() const override {
 | 
					 | 
				
			||||||
		if (const HistoryDocumentCaptioned *captioned = Get<HistoryDocumentCaptioned>()) {
 | 
					 | 
				
			||||||
			return captioned->_caption.originalTextWithEntities();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return TextWithEntities();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool needsBubble() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool customInfoLayout() const override {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	QMargins bubbleMargins() const override {
 | 
					 | 
				
			||||||
		return Get<HistoryDocumentThumbed>() ? QMargins(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbPadding.left(), st::msgFileThumbPadding.bottom()) : st::msgPadding;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool hideForwardedFrom() const override {
 | 
					 | 
				
			||||||
		return _data->song();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void step_voiceProgress(float64 ms, bool timer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	float64 dataProgress() const override {
 | 
					 | 
				
			||||||
		return _data->progress();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dataFinished() const override {
 | 
					 | 
				
			||||||
		return !_data->loading() && !_data->uploading();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dataLoaded() const override {
 | 
					 | 
				
			||||||
		return _data->loaded();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	void createComponents(bool caption);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void setStatusSize(int32 newSize, qint64 realDuration = 0) const;
 | 
					 | 
				
			||||||
	bool updateStatusText() const; // returns showPause
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
								   // Callback is a void(const QString &, const QString &, const Text &) functor.
 | 
					 | 
				
			||||||
								   // It will be called as callback(attachType, attachFileName, attachCaption).
 | 
					 | 
				
			||||||
	template <typename Callback>
 | 
					 | 
				
			||||||
	void buildStringRepresentation(Callback callback) const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DocumentData *_data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HistoryGif : public HistoryFileMedia {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	HistoryGif(HistoryItem *parent, DocumentData *document, const QString &caption);
 | 
					 | 
				
			||||||
	HistoryGif(HistoryItem *parent, const HistoryGif &other);
 | 
					 | 
				
			||||||
	HistoryMediaType type() const override {
 | 
					 | 
				
			||||||
		return MediaTypeGif;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	HistoryGif *clone(HistoryItem *newParent) const override {
 | 
					 | 
				
			||||||
		return new HistoryGif(newParent, *this);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initDimensions() override;
 | 
					 | 
				
			||||||
	int resizeGetHeight(int width) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
					 | 
				
			||||||
	HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
 | 
					 | 
				
			||||||
		return _caption.adjustSelection(selection, type);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool hasTextForCopy() const override {
 | 
					 | 
				
			||||||
		return !_caption.isEmpty();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	QString notificationText() const override;
 | 
					 | 
				
			||||||
	QString inDialogsText() const override;
 | 
					 | 
				
			||||||
	TextWithEntities selectedText(TextSelection selection) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool uploading() const override {
 | 
					 | 
				
			||||||
		return _data->uploading();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DocumentData *getDocument() override {
 | 
					 | 
				
			||||||
		return _data;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Media::Clip::Reader *getClipReader() override {
 | 
					 | 
				
			||||||
		return gif();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool playInline(bool autoplay) override;
 | 
					 | 
				
			||||||
	void stopInline() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void attachToParent() override;
 | 
					 | 
				
			||||||
	void detachFromParent() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void updateSentMedia(const MTPMessageMedia &media) override;
 | 
					 | 
				
			||||||
	bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool hasReplyPreview() const override {
 | 
					 | 
				
			||||||
		return !_data->thumb->isNull();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ImagePtr replyPreview() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextWithEntities getCaption() const override {
 | 
					 | 
				
			||||||
		return _caption.originalTextWithEntities();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool needsBubble() const override {
 | 
					 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (_parent->viaBot()) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool customInfoLayout() const override {
 | 
					 | 
				
			||||||
		return _caption.isEmpty();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool hideFromName() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	~HistoryGif();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	float64 dataProgress() const override;
 | 
					 | 
				
			||||||
	bool dataFinished() const override;
 | 
					 | 
				
			||||||
	bool dataLoaded() const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	DocumentData *_data;
 | 
					 | 
				
			||||||
	int32 _thumbw, _thumbh;
 | 
					 | 
				
			||||||
	Text _caption;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Media::Clip::Reader *_gif;
 | 
					 | 
				
			||||||
	Media::Clip::Reader *gif() {
 | 
					 | 
				
			||||||
		return (_gif == Media::Clip::BadReader) ? nullptr : _gif;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	const Media::Clip::Reader *gif() const {
 | 
					 | 
				
			||||||
		return (_gif == Media::Clip::BadReader) ? nullptr : _gif;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void setStatusSize(int32 newSize) const;
 | 
					 | 
				
			||||||
	void updateStatusText() const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HistorySticker : public HistoryMedia {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	HistorySticker(HistoryItem *parent, DocumentData *document);
 | 
					 | 
				
			||||||
	HistoryMediaType type() const override {
 | 
					 | 
				
			||||||
		return MediaTypeSticker;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	HistorySticker *clone(HistoryItem *newParent) const override {
 | 
					 | 
				
			||||||
		return new HistorySticker(newParent, _data);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initDimensions() override;
 | 
					 | 
				
			||||||
	int resizeGetHeight(int width) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
					 | 
				
			||||||
	HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dragItem() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	QString notificationText() const override;
 | 
					 | 
				
			||||||
	TextWithEntities selectedText(TextSelection selection) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DocumentData *getDocument() override {
 | 
					 | 
				
			||||||
		return _data;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void attachToParent() override;
 | 
					 | 
				
			||||||
	void detachFromParent() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void updateSentMedia(const MTPMessageMedia &media) override;
 | 
					 | 
				
			||||||
	bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool needsBubble() const override {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool customInfoLayout() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	int additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const;
 | 
					 | 
				
			||||||
	int additionalWidth() const {
 | 
					 | 
				
			||||||
		return additionalWidth(_parent->Get<HistoryMessageVia>(), _parent->Get<HistoryMessageReply>());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	QString toString() const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int16 _pixw, _pixh;
 | 
					 | 
				
			||||||
	ClickHandlerPtr _packLink;
 | 
					 | 
				
			||||||
	DocumentData *_data;
 | 
					 | 
				
			||||||
	QString _emoji;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SendMessageClickHandler : public PeerClickHandler {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	using PeerClickHandler::PeerClickHandler;
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	void onClickImpl() const override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class AddContactClickHandler : public MessageClickHandler {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	using MessageClickHandler::MessageClickHandler;
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	void onClickImpl() const override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HistoryContact : public HistoryMedia {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	HistoryContact(HistoryItem *parent, int32 userId, const QString &first, const QString &last, const QString &phone);
 | 
					 | 
				
			||||||
	HistoryMediaType type() const override {
 | 
					 | 
				
			||||||
		return MediaTypeContact;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	HistoryContact *clone(HistoryItem *newParent) const override {
 | 
					 | 
				
			||||||
		return new HistoryContact(newParent, _userId, _fname, _lname, _phone);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initDimensions() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
					 | 
				
			||||||
	HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	QString notificationText() const override;
 | 
					 | 
				
			||||||
	TextWithEntities selectedText(TextSelection selection) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void attachToParent() override;
 | 
					 | 
				
			||||||
	void detachFromParent() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void updateSentMedia(const MTPMessageMedia &media) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool needsBubble() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool customInfoLayout() const override {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const QString &fname() const {
 | 
					 | 
				
			||||||
		return _fname;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	const QString &lname() const {
 | 
					 | 
				
			||||||
		return _lname;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	const QString &phone() const {
 | 
					 | 
				
			||||||
		return _phone;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int32 _userId;
 | 
					 | 
				
			||||||
	UserData *_contact;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int32 _phonew;
 | 
					 | 
				
			||||||
	QString _fname, _lname, _phone;
 | 
					 | 
				
			||||||
	Text _name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ClickHandlerPtr _linkl;
 | 
					 | 
				
			||||||
	int32 _linkw;
 | 
					 | 
				
			||||||
	QString _link;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HistoryWebPage : public HistoryMedia {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	HistoryWebPage(HistoryItem *parent, WebPageData *data);
 | 
					 | 
				
			||||||
	HistoryWebPage(HistoryItem *parent, const HistoryWebPage &other);
 | 
					 | 
				
			||||||
	HistoryMediaType type() const override {
 | 
					 | 
				
			||||||
		return MediaTypeWebPage;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	HistoryWebPage *clone(HistoryItem *newParent) const override {
 | 
					 | 
				
			||||||
		return new HistoryWebPage(newParent, *this);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initDimensions() override;
 | 
					 | 
				
			||||||
	int resizeGetHeight(int width) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
					 | 
				
			||||||
	HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
 | 
					 | 
				
			||||||
	bool hasTextForCopy() const override {
 | 
					 | 
				
			||||||
		return false; // we do not add _title and _description in FullSelection text copy.
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return _attach && _attach->toggleSelectionByHandlerClick(p);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return _attach && _attach->dragItemByHandler(p);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextWithEntities selectedText(TextSelection selection) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
 | 
					 | 
				
			||||||
	void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool isDisplayed() const override {
 | 
					 | 
				
			||||||
		return !_data->pendingTill;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	DocumentData *getDocument() override {
 | 
					 | 
				
			||||||
		return _attach ? _attach->getDocument() : 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Media::Clip::Reader *getClipReader() override {
 | 
					 | 
				
			||||||
		return _attach ? _attach->getClipReader() : 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool playInline(bool autoplay) override {
 | 
					 | 
				
			||||||
		return _attach ? _attach->playInline(autoplay) : false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	void stopInline() override {
 | 
					 | 
				
			||||||
		if (_attach) _attach->stopInline();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void attachToParent() override;
 | 
					 | 
				
			||||||
	void detachFromParent() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool hasReplyPreview() const override {
 | 
					 | 
				
			||||||
		return (_data->photo && !_data->photo->thumb->isNull()) || (_data->document && !_data->document->thumb->isNull());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ImagePtr replyPreview() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	WebPageData *webpage() {
 | 
					 | 
				
			||||||
		return _data;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool needsBubble() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool customInfoLayout() const override {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	HistoryMedia *attach() const {
 | 
					 | 
				
			||||||
		return _attach.get();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	TextSelection toDescriptionSelection(TextSelection selection) const {
 | 
					 | 
				
			||||||
		return internal::unshiftSelection(selection, _title);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	TextSelection fromDescriptionSelection(TextSelection selection) const {
 | 
					 | 
				
			||||||
		return internal::shiftSelection(selection, _title);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	WebPageData *_data;
 | 
					 | 
				
			||||||
	ClickHandlerPtr _openl;
 | 
					 | 
				
			||||||
	std_::unique_ptr<HistoryMedia> _attach;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool _asArticle = false;
 | 
					 | 
				
			||||||
	int32 _titleLines, _descriptionLines;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Text _title, _description;
 | 
					 | 
				
			||||||
	int32 _siteNameWidth = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	QString _duration;
 | 
					 | 
				
			||||||
	int32 _durationWidth = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int16 _pixw = 0;
 | 
					 | 
				
			||||||
	int16 _pixh = 0;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HistoryGame : public HistoryMedia {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	HistoryGame(HistoryItem *parent, GameData *data);
 | 
					 | 
				
			||||||
	HistoryGame(HistoryItem *parent, const HistoryGame &other);
 | 
					 | 
				
			||||||
	HistoryMediaType type() const override {
 | 
					 | 
				
			||||||
		return MediaTypeGame;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	HistoryGame *clone(HistoryItem *newParent) const override {
 | 
					 | 
				
			||||||
		return new HistoryGame(newParent, *this);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initDimensions() override;
 | 
					 | 
				
			||||||
	int resizeGetHeight(int width) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
					 | 
				
			||||||
	HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
 | 
					 | 
				
			||||||
	bool isAboveMessage() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool hasTextForCopy() const override {
 | 
					 | 
				
			||||||
		return false; // we do not add _title and _description in FullSelection text copy.
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return _attach && _attach->toggleSelectionByHandlerClick(p);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return _attach && _attach->dragItemByHandler(p);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextWithEntities selectedText(TextSelection selection) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
 | 
					 | 
				
			||||||
	void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DocumentData *getDocument() override {
 | 
					 | 
				
			||||||
		return _attach ? _attach->getDocument() : nullptr;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Media::Clip::Reader *getClipReader() override {
 | 
					 | 
				
			||||||
		return _attach ? _attach->getClipReader() : nullptr;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool playInline(bool autoplay) override {
 | 
					 | 
				
			||||||
		return _attach ? _attach->playInline(autoplay) : false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	void stopInline() override {
 | 
					 | 
				
			||||||
		if (_attach) _attach->stopInline();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void attachToParent() override;
 | 
					 | 
				
			||||||
	void detachFromParent() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool hasReplyPreview() const override {
 | 
					 | 
				
			||||||
		return (_data->photo && !_data->photo->thumb->isNull()) || (_data->document && !_data->document->thumb->isNull());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ImagePtr replyPreview() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	GameData *game() {
 | 
					 | 
				
			||||||
		return _data;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool needsBubble() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool customInfoLayout() const override {
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	HistoryMedia *attach() const {
 | 
					 | 
				
			||||||
		return _attach.get();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	TextSelection toDescriptionSelection(TextSelection selection) const {
 | 
					 | 
				
			||||||
		return internal::unshiftSelection(selection, _title);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	TextSelection fromDescriptionSelection(TextSelection selection) const {
 | 
					 | 
				
			||||||
		return internal::shiftSelection(selection, _title);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	GameData *_data;
 | 
					 | 
				
			||||||
	ClickHandlerPtr _openl;
 | 
					 | 
				
			||||||
	std_::unique_ptr<HistoryMedia> _attach;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int32 _titleLines, _descriptionLines;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Text _title, _description;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct LocationCoords;
 | 
					 | 
				
			||||||
struct LocationData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HistoryLocation : public HistoryMedia {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	HistoryLocation(HistoryItem *parent, const LocationCoords &coords, const QString &title = QString(), const QString &description = QString());
 | 
					 | 
				
			||||||
	HistoryLocation(HistoryItem *parent, const HistoryLocation &other);
 | 
					 | 
				
			||||||
	HistoryMediaType type() const override {
 | 
					 | 
				
			||||||
		return MediaTypeLocation;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	HistoryLocation *clone(HistoryItem *newParent) const override {
 | 
					 | 
				
			||||||
		return new HistoryLocation(newParent, *this);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void initDimensions() override;
 | 
					 | 
				
			||||||
	int resizeGetHeight(int32 width) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
					 | 
				
			||||||
	HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
 | 
					 | 
				
			||||||
	bool hasTextForCopy() const override {
 | 
					 | 
				
			||||||
		return !_title.isEmpty() || !_description.isEmpty();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return p == _link;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
					 | 
				
			||||||
		return p == _link;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	QString notificationText() const override;
 | 
					 | 
				
			||||||
	QString inDialogsText() const override;
 | 
					 | 
				
			||||||
	TextWithEntities selectedText(TextSelection selection) const override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool needsBubble() const override {
 | 
					 | 
				
			||||||
		if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (_parent->viaBot()) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool customInfoLayout() const override {
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	TextSelection toDescriptionSelection(TextSelection selection) const {
 | 
					 | 
				
			||||||
		return internal::unshiftSelection(selection, _title);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	TextSelection fromDescriptionSelection(TextSelection selection) const {
 | 
					 | 
				
			||||||
		return internal::shiftSelection(selection, _title);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LocationData *_data;
 | 
					 | 
				
			||||||
	Text _title, _description;
 | 
					 | 
				
			||||||
	ClickHandlerPtr _link;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int32 fullWidth() const;
 | 
					 | 
				
			||||||
	int32 fullHeight() const;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 | 
				
			||||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
					Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
#include "stdafx.h"
 | 
					#include "stdafx.h"
 | 
				
			||||||
#include "history/history_media.h"
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "lang.h"
 | 
					#include "lang.h"
 | 
				
			||||||
#include "mainwidget.h"
 | 
					#include "mainwidget.h"
 | 
				
			||||||
| 
						 | 
					@ -86,81 +86,6 @@ void historyInitMedia() {
 | 
				
			||||||
	initTextOptions();
 | 
						initTextOptions();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RadialAnimation::RadialAnimation(AnimationCallbacks &&callbacks)
 | 
					 | 
				
			||||||
: a_arcEnd(0, 0)
 | 
					 | 
				
			||||||
, a_arcStart(0, FullArcLength)
 | 
					 | 
				
			||||||
, _animation(std_::move(callbacks)) {
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RadialAnimation::start(float64 prg) {
 | 
					 | 
				
			||||||
	_firstStart = _lastStart = _lastTime = getms();
 | 
					 | 
				
			||||||
	int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength), iprgstrict = qRound(prg * AlmostFullArcLength);
 | 
					 | 
				
			||||||
	a_arcEnd = anim::ivalue(iprgstrict, iprg);
 | 
					 | 
				
			||||||
	_animation.start();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RadialAnimation::update(float64 prg, bool finished, uint64 ms) {
 | 
					 | 
				
			||||||
	int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength);
 | 
					 | 
				
			||||||
	if (iprg != a_arcEnd.to()) {
 | 
					 | 
				
			||||||
		a_arcEnd.start(iprg);
 | 
					 | 
				
			||||||
		_lastStart = _lastTime;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_lastTime = ms;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	float64 dt = float64(ms - _lastStart), fulldt = float64(ms - _firstStart);
 | 
					 | 
				
			||||||
	_opacity = qMin(fulldt / st::radialDuration, 1.);
 | 
					 | 
				
			||||||
	if (!finished) {
 | 
					 | 
				
			||||||
		a_arcEnd.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear);
 | 
					 | 
				
			||||||
	} else if (dt >= st::radialDuration) {
 | 
					 | 
				
			||||||
		a_arcEnd.update(1, anim::linear);
 | 
					 | 
				
			||||||
		stop();
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		float64 r = dt / st::radialDuration;
 | 
					 | 
				
			||||||
		a_arcEnd.update(r, anim::linear);
 | 
					 | 
				
			||||||
		_opacity *= 1 - r;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	float64 fromstart = fulldt / st::radialPeriod;
 | 
					 | 
				
			||||||
	a_arcStart.update(fromstart - std::floor(fromstart), anim::linear);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RadialAnimation::stop() {
 | 
					 | 
				
			||||||
	_firstStart = _lastStart = _lastTime = 0;
 | 
					 | 
				
			||||||
	a_arcEnd = anim::ivalue(0, 0);
 | 
					 | 
				
			||||||
	_animation.stop();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RadialAnimation::step(uint64 ms) {
 | 
					 | 
				
			||||||
	_animation.step(ms);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color) {
 | 
					 | 
				
			||||||
	float64 o = p.opacity();
 | 
					 | 
				
			||||||
	p.setOpacity(o * _opacity);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	QPen pen(color->p), was(p.pen());
 | 
					 | 
				
			||||||
	pen.setWidth(thickness);
 | 
					 | 
				
			||||||
	p.setPen(pen);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int32 len = MinArcLength + a_arcEnd.current();
 | 
					 | 
				
			||||||
	int32 from = QuarterArcLength - a_arcStart.current() - len;
 | 
					 | 
				
			||||||
	if (rtl()) {
 | 
					 | 
				
			||||||
		from = QuarterArcLength - (from - QuarterArcLength) - len;
 | 
					 | 
				
			||||||
		if (from < 0) from += FullArcLength;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p.setRenderHint(QPainter::HighQualityAntialiasing);
 | 
					 | 
				
			||||||
	p.drawArc(inner, from, len);
 | 
					 | 
				
			||||||
	p.setRenderHint(QPainter::HighQualityAntialiasing, false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p.setPen(was);
 | 
					 | 
				
			||||||
	p.setOpacity(o);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
QString HistoryMedia::inDialogsText() const {
 | 
					 | 
				
			||||||
	auto result = notificationText();
 | 
					 | 
				
			||||||
	return result.isEmpty() ? QString() : textcmdLink(1, textClean(result));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int32 documentMaxStatusWidth(DocumentData *document) {
 | 
					int32 documentMaxStatusWidth(DocumentData *document) {
 | 
				
			||||||
| 
						 | 
					@ -371,7 +296,11 @@ void HistoryPhoto::initDimensions() {
 | 
				
			||||||
			_maxw += st::mediaPadding.left() + st::mediaPadding.right();
 | 
								_maxw += st::mediaPadding.left() + st::mediaPadding.right();
 | 
				
			||||||
			_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
								_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
				
			||||||
			if (!_caption.isEmpty()) {
 | 
								if (!_caption.isEmpty()) {
 | 
				
			||||||
				_minh += st::mediaCaptionSkip + _caption.countHeight(maxActualWidth - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
 | 
									auto captionw = maxActualWidth - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
 | 
									_minh += st::mediaCaptionSkip + _caption.countHeight(captionw);
 | 
				
			||||||
 | 
									if (isBubbleBottom()) {
 | 
				
			||||||
 | 
										_minh += st::msgPadding.bottom();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
| 
						 | 
					@ -417,7 +346,10 @@ int HistoryPhoto::resizeGetHeight(int width) {
 | 
				
			||||||
		_height += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
							_height += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			int captionw = _width - st::msgPadding.left() - st::msgPadding.right();
 | 
								int captionw = _width - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
			_height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
 | 
								_height += st::mediaCaptionSkip + _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									_height += st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return _height;
 | 
						return _height;
 | 
				
			||||||
| 
						 | 
					@ -425,7 +357,6 @@ int HistoryPhoto::resizeGetHeight(int width) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const {
 | 
					void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const {
 | 
				
			||||||
	if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
 | 
						if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
 | 
				
			||||||
	p.fillRect(QRect(0, 0, _width, _height), QColor(128, 255, 128));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_data->automaticLoad(_parent);
 | 
						_data->automaticLoad(_parent);
 | 
				
			||||||
	bool selected = (selection == FullSelection);
 | 
						bool selected = (selection == FullSelection);
 | 
				
			||||||
| 
						 | 
					@ -453,7 +384,10 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, uin
 | 
				
			||||||
		width -= st::mediaPadding.left() + st::mediaPadding.right();
 | 
							width -= st::mediaPadding.left() + st::mediaPadding.right();
 | 
				
			||||||
		height -= skipy + st::mediaPadding.bottom();
 | 
							height -= skipy + st::mediaPadding.bottom();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
 | 
								height -= st::mediaCaptionSkip + _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									height -= st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
 | 
							App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
 | 
				
			||||||
| 
						 | 
					@ -470,7 +404,8 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, uin
 | 
				
			||||||
	QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
 | 
						QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
 | 
				
			||||||
	p.drawPixmap(rthumb.topLeft(), pix);
 | 
						p.drawPixmap(rthumb.topLeft(), pix);
 | 
				
			||||||
	if (selected) {
 | 
						if (selected) {
 | 
				
			||||||
		App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayLargeCorners);
 | 
							auto overlayCorners = inWebPage ? SelectedOverlaySmallCorners : SelectedOverlayLargeCorners;
 | 
				
			||||||
 | 
							App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, overlayCorners);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (notChild && (radial || (!loaded && !_data->loading()))) {
 | 
						if (notChild && (radial || (!loaded && !_data->loading()))) {
 | 
				
			||||||
| 
						 | 
					@ -540,7 +475,10 @@ HistoryTextState HistoryPhoto::getState(int x, int y, HistoryStateRequest reques
 | 
				
			||||||
		skipy = st::mediaPadding.top();
 | 
							skipy = st::mediaPadding.top();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			int captionw = width - st::msgPadding.left() - st::msgPadding.right();
 | 
								int captionw = width - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
			height -= _caption.countHeight(captionw) + st::msgPadding.bottom();
 | 
								height -= _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									height -= st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
 | 
								if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
 | 
				
			||||||
				result = _caption.getState(x - st::msgPadding.left(), y - height, captionw, request.forText());
 | 
									result = _caption.getState(x - st::msgPadding.left(), y - height, captionw, request.forText());
 | 
				
			||||||
				return result;
 | 
									return result;
 | 
				
			||||||
| 
						 | 
					@ -712,7 +650,11 @@ void HistoryVideo::initDimensions() {
 | 
				
			||||||
		_maxw += st::mediaPadding.left() + st::mediaPadding.right();
 | 
							_maxw += st::mediaPadding.left() + st::mediaPadding.right();
 | 
				
			||||||
		_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
							_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			_minh += st::mediaCaptionSkip + _caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
 | 
								auto captionw = _maxw - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
 | 
								_minh += st::mediaCaptionSkip + _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									_minh += st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -750,7 +692,10 @@ int HistoryVideo::resizeGetHeight(int width) {
 | 
				
			||||||
		_height += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
							_height += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			int captionw = _width - st::msgPadding.left() - st::msgPadding.right();
 | 
								int captionw = _width - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
			_height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
 | 
								_height += st::mediaCaptionSkip + _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									_height += st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return _height;
 | 
						return _height;
 | 
				
			||||||
| 
						 | 
					@ -785,16 +730,22 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, uin
 | 
				
			||||||
		width -= st::mediaPadding.left() + st::mediaPadding.right();
 | 
							width -= st::mediaPadding.left() + st::mediaPadding.right();
 | 
				
			||||||
		height -= skipy + st::mediaPadding.bottom();
 | 
							height -= skipy + st::mediaPadding.bottom();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
 | 
								height -= st::mediaCaptionSkip + _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									height -= st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
 | 
							App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto inWebPage = (_parent->getMedia() != this);
 | 
				
			||||||
 | 
						auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
 | 
				
			||||||
	QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
 | 
						QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
 | 
				
			||||||
	p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(ImageRoundRadius::Large, _thumbw, 0, width, height));
 | 
						p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(roundRadius, _thumbw, 0, width, height));
 | 
				
			||||||
	if (selected) {
 | 
						if (selected) {
 | 
				
			||||||
		App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayLargeCorners);
 | 
							auto overlayCorners = inWebPage ? SelectedOverlaySmallCorners : SelectedOverlayLargeCorners;
 | 
				
			||||||
 | 
							App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, overlayCorners);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
 | 
						QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
 | 
				
			||||||
| 
						 | 
					@ -866,8 +817,11 @@ HistoryTextState HistoryVideo::getState(int x, int y, HistoryStateRequest reques
 | 
				
			||||||
		skipx = st::mediaPadding.left();
 | 
							skipx = st::mediaPadding.left();
 | 
				
			||||||
		skipy = st::mediaPadding.top();
 | 
							skipy = st::mediaPadding.top();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
 | 
								auto captionw = width - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
			height -= _caption.countHeight(captionw) + st::msgPadding.bottom();
 | 
								height -= _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									height -= st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
 | 
								if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
 | 
				
			||||||
				result = _caption.getState(x - st::msgPadding.left(), y - height, captionw, request.forText());
 | 
									result = _caption.getState(x - st::msgPadding.left(), y - height, captionw, request.forText());
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -1077,7 +1031,11 @@ void HistoryDocument::initDimensions() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (captioned) {
 | 
						if (captioned) {
 | 
				
			||||||
		_minh += captioned->_caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
 | 
							auto captionw = _maxw - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
 | 
							_minh += captioned->_caption.countHeight(captionw);
 | 
				
			||||||
 | 
							if (isBubbleBottom()) {
 | 
				
			||||||
 | 
								_minh += st::msgPadding.bottom();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		_height = _minh;
 | 
							_height = _minh;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1095,7 +1053,11 @@ int HistoryDocument::resizeGetHeight(int width) {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		_height = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom();
 | 
							_height = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_height += captioned->_caption.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
 | 
						auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
 | 
						_height += captioned->_caption.countHeight(captionw);
 | 
				
			||||||
 | 
						if (isBubbleBottom()) {
 | 
				
			||||||
 | 
							_height += st::msgPadding.bottom();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return _height;
 | 
						return _height;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1129,11 +1091,14 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection,
 | 
				
			||||||
		linktop = st::msgFileThumbLinkTop;
 | 
							linktop = st::msgFileThumbLinkTop;
 | 
				
			||||||
		bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom();
 | 
							bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto inWebPage = (_parent->getMedia() != this);
 | 
				
			||||||
 | 
							auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
 | 
				
			||||||
		QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width));
 | 
							QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width));
 | 
				
			||||||
		QPixmap thumb = loaded ? _data->thumb->pixSingle(ImageRoundRadius::Large, thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(ImageRoundRadius::Small, thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize);
 | 
							QPixmap thumb = loaded ? _data->thumb->pixSingle(roundRadius, thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(ImageRoundRadius::Small, thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize);
 | 
				
			||||||
		p.drawPixmap(rthumb.topLeft(), thumb);
 | 
							p.drawPixmap(rthumb.topLeft(), thumb);
 | 
				
			||||||
		if (selected) {
 | 
							if (selected) {
 | 
				
			||||||
			App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayLargeCorners);
 | 
								auto overlayCorners = inWebPage ? SelectedOverlaySmallCorners : SelectedOverlayLargeCorners;
 | 
				
			||||||
 | 
								App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, overlayCorners);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (radial || (!loaded && !_data->loading())) {
 | 
							if (radial || (!loaded && !_data->loading())) {
 | 
				
			||||||
| 
						 | 
					@ -1366,7 +1331,11 @@ HistoryTextState HistoryDocument::getState(int x, int y, HistoryStateRequest req
 | 
				
			||||||
			result = captioned->_caption.getState(x - st::msgPadding.left(), y - bottom, _width - st::msgPadding.left() - st::msgPadding.right(), request.forText());
 | 
								result = captioned->_caption.getState(x - st::msgPadding.left(), y - bottom, _width - st::msgPadding.left() - st::msgPadding.right(), request.forText());
 | 
				
			||||||
			return result;
 | 
								return result;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		height -= captioned->_caption.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
 | 
							auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
 | 
							height -= captioned->_caption.countHeight(captionw);
 | 
				
			||||||
 | 
							if (isBubbleBottom()) {
 | 
				
			||||||
 | 
								height -= st::msgPadding.bottom();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (x >= 0 && y >= 0 && x < _width && y < height && !_data->loading() && !_data->uploading() && _data->isValid()) {
 | 
						if (x >= 0 && y >= 0 && x < _width && y < height && !_data->loading() && !_data->uploading() && _data->isValid()) {
 | 
				
			||||||
		result.link = _openl;
 | 
							result.link = _openl;
 | 
				
			||||||
| 
						 | 
					@ -1628,7 +1597,11 @@ void HistoryGif::initDimensions() {
 | 
				
			||||||
		_maxw += st::mediaPadding.left() + st::mediaPadding.right();
 | 
							_maxw += st::mediaPadding.left() + st::mediaPadding.right();
 | 
				
			||||||
		_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
							_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			_minh += st::mediaCaptionSkip + _caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
 | 
								auto captionw = _maxw - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
 | 
								_minh += st::mediaCaptionSkip + _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									_minh += st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1685,7 +1658,11 @@ int HistoryGif::resizeGetHeight(int width) {
 | 
				
			||||||
		_width += st::mediaPadding.left() + st::mediaPadding.right();
 | 
							_width += st::mediaPadding.left() + st::mediaPadding.right();
 | 
				
			||||||
		_height += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
							_height += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			_height += st::mediaCaptionSkip + _caption.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
 | 
								auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
 | 
								_height += st::mediaCaptionSkip + _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									_height += st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1729,7 +1706,10 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, uint6
 | 
				
			||||||
		width -= st::mediaPadding.left() + st::mediaPadding.right();
 | 
							width -= st::mediaPadding.left() + st::mediaPadding.right();
 | 
				
			||||||
		height -= skipy + st::mediaPadding.bottom();
 | 
							height -= skipy + st::mediaPadding.bottom();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
 | 
								height -= st::mediaCaptionSkip + _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									height -= st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
 | 
							App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
 | 
				
			||||||
| 
						 | 
					@ -1740,10 +1720,14 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, uint6
 | 
				
			||||||
	if (animating) {
 | 
						if (animating) {
 | 
				
			||||||
		p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, width, height, (Ui::isLayerShown() || Ui::isMediaViewShown() || Ui::isInlineItemBeingChosen()) ? 0 : ms));
 | 
							p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, width, height, (Ui::isLayerShown() || Ui::isMediaViewShown() || Ui::isInlineItemBeingChosen()) ? 0 : ms));
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(ImageRoundRadius::Large, _thumbw, _thumbh, width, height));
 | 
							auto inWebPage = (_parent->getMedia() != this);
 | 
				
			||||||
 | 
							auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
 | 
				
			||||||
 | 
							p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(roundRadius, _thumbw, _thumbh, width, height));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (selected) {
 | 
						if (selected) {
 | 
				
			||||||
		App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayLargeCorners);
 | 
							auto inWebPage = (_parent->getMedia() != this);
 | 
				
			||||||
 | 
							auto overlayCorners = inWebPage ? SelectedOverlaySmallCorners : SelectedOverlayLargeCorners;
 | 
				
			||||||
 | 
							App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, overlayCorners);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (radial || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif())) || (_gif == Media::Clip::BadReader)) {
 | 
						if (radial || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif())) || (_gif == Media::Clip::BadReader)) {
 | 
				
			||||||
| 
						 | 
					@ -1817,8 +1801,11 @@ HistoryTextState HistoryGif::getState(int x, int y, HistoryStateRequest request)
 | 
				
			||||||
		skipx = st::mediaPadding.left();
 | 
							skipx = st::mediaPadding.left();
 | 
				
			||||||
		skipy = st::mediaPadding.top();
 | 
							skipy = st::mediaPadding.top();
 | 
				
			||||||
		if (!_caption.isEmpty()) {
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
			int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
 | 
								auto captionw = width - st::msgPadding.left() - st::msgPadding.right();
 | 
				
			||||||
			height -= _caption.countHeight(captionw) + st::msgPadding.bottom();
 | 
								height -= _caption.countHeight(captionw);
 | 
				
			||||||
 | 
								if (isBubbleBottom()) {
 | 
				
			||||||
 | 
									height -= st::msgPadding.bottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
 | 
								if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
 | 
				
			||||||
				result = _caption.getState(x - st::msgPadding.left(), y - height, captionw, request.forText());
 | 
									result = _caption.getState(x - st::msgPadding.left(), y - height, captionw, request.forText());
 | 
				
			||||||
				return result;
 | 
									return result;
 | 
				
			||||||
| 
						 | 
					@ -2547,25 +2534,31 @@ void HistoryWebPage::initDimensions() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (_siteNameWidth) {
 | 
						if (_siteNameWidth) {
 | 
				
			||||||
		if (_title.isEmpty() && _description.isEmpty()) {
 | 
							if (_title.isEmpty() && _description.isEmpty()) {
 | 
				
			||||||
			_maxw = qMax(_maxw, int32(_siteNameWidth + _parent->skipBlockWidth()));
 | 
								accumulate_max(_maxw, _siteNameWidth + _parent->skipBlockWidth());
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			_maxw = qMax(_maxw, int32(_siteNameWidth + articlePhotoMaxWidth));
 | 
								accumulate_max(_maxw, _siteNameWidth + articlePhotoMaxWidth);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		_minh += _lineHeight;
 | 
							_minh += _lineHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (!_title.isEmpty()) {
 | 
						if (!_title.isEmpty()) {
 | 
				
			||||||
		_maxw = qMax(_maxw, int32(_title.maxWidth() + articlePhotoMaxWidth));
 | 
							accumulate_max(_maxw, _title.maxWidth() + articlePhotoMaxWidth);
 | 
				
			||||||
		_minh += titleMinHeight;
 | 
							_minh += titleMinHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (!_description.isEmpty()) {
 | 
						if (!_description.isEmpty()) {
 | 
				
			||||||
		_maxw = qMax(_maxw, int32(_description.maxWidth() + articlePhotoMaxWidth));
 | 
							accumulate_max(_maxw, _description.maxWidth() + articlePhotoMaxWidth);
 | 
				
			||||||
		_minh += descriptionMinHeight;
 | 
							_minh += descriptionMinHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (_attach) {
 | 
						if (_attach) {
 | 
				
			||||||
		if (_minh) _minh += st::webPagePhotoSkip;
 | 
							auto attachAtTop = !_siteNameWidth && _title.isEmpty() && _description.isEmpty();
 | 
				
			||||||
 | 
							if (!attachAtTop) _minh += st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_attach->initDimensions();
 | 
							_attach->initDimensions();
 | 
				
			||||||
		QMargins bubble(_attach->bubbleMargins());
 | 
							QMargins bubble(_attach->bubbleMargins());
 | 
				
			||||||
		_maxw = qMax(_maxw, int32(_attach->maxWidth() - bubble.left() - bubble.top() + (_attach->customInfoLayout() ? skipBlockWidth : 0)));
 | 
							auto maxMediaWidth = _attach->maxWidth() - bubble.left() - bubble.right();
 | 
				
			||||||
 | 
							if (isBubbleBottom() && _attach->customInfoLayout()) {
 | 
				
			||||||
 | 
								maxMediaWidth += skipBlockWidth;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							accumulate_max(_maxw, maxMediaWidth);
 | 
				
			||||||
		_minh += _attach->minHeight() - bubble.top() - bubble.bottom();
 | 
							_minh += _attach->minHeight() - bubble.top() - bubble.bottom();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (_data->type == WebPageVideo && _data->duration) {
 | 
						if (_data->type == WebPageVideo && _data->duration) {
 | 
				
			||||||
| 
						 | 
					@ -2573,10 +2566,11 @@ void HistoryWebPage::initDimensions() {
 | 
				
			||||||
		_durationWidth = st::msgDateFont->width(_duration);
 | 
							_durationWidth = st::msgDateFont->width(_duration);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_maxw += st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
 | 
						_maxw += st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
 | 
				
			||||||
	_minh += st::msgPadding.bottom();
 | 
						auto padding = inBubblePadding();
 | 
				
			||||||
 | 
						_minh += padding.top() + padding.bottom();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (_asArticle) {
 | 
						if (_asArticle) {
 | 
				
			||||||
		_minh = resizeGetHeight(_maxw); // hack
 | 
							_minh = resizeGetHeight(_maxw);
 | 
				
			||||||
										//		_minh += st::msgDateFont->height;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2625,7 +2619,7 @@ int HistoryWebPage::resizeGetHeight(int width) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			_pixh -= _lineHeight;
 | 
								_pixh -= _lineHeight;
 | 
				
			||||||
		} while (_pixh > _lineHeight);
 | 
							} while (_pixh > _lineHeight);
 | 
				
			||||||
		_height += st::msgDateFont->height;
 | 
							_height += bottomInfoPadding();
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		_height = siteNameHeight;
 | 
							_height = siteNameHeight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2653,18 +2647,20 @@ int HistoryWebPage::resizeGetHeight(int width) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (_attach) {
 | 
							if (_attach) {
 | 
				
			||||||
			if (_height) _height += st::webPagePhotoSkip;
 | 
								auto attachAtTop = !_siteNameWidth && !_titleLines && !_descriptionLines;
 | 
				
			||||||
 | 
								if (!attachAtTop) _height += st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			QMargins bubble(_attach->bubbleMargins());
 | 
								QMargins bubble(_attach->bubbleMargins());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			_attach->resizeGetHeight(width + bubble.left() + bubble.right());
 | 
								_attach->resizeGetHeight(width + bubble.left() + bubble.right());
 | 
				
			||||||
			_height += _attach->height() - bubble.top() - bubble.bottom();
 | 
								_height += _attach->height() - bubble.top() - bubble.bottom();
 | 
				
			||||||
			if (_attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
 | 
								if (isBubbleBottom() && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
 | 
				
			||||||
				_height += st::msgDateFont->height;
 | 
									_height += bottomInfoPadding();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_height += st::msgPadding.bottom();
 | 
						auto padding = inBubblePadding();
 | 
				
			||||||
 | 
						_height += padding.top() + padding.bottom();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return _height;
 | 
						return _height;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2680,14 +2676,16 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u
 | 
				
			||||||
	style::color semibold = (selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
 | 
						style::color semibold = (selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
 | 
				
			||||||
	style::color regular = (selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
 | 
						style::color regular = (selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 lshift = st::msgPadding.left() + st::webPageLeft, rshift = st::msgPadding.right(), bshift = st::msgPadding.bottom();
 | 
					 | 
				
			||||||
	width -= lshift + rshift;
 | 
					 | 
				
			||||||
	QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
 | 
						QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
 | 
				
			||||||
	if (_asArticle || (_attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right())) {
 | 
						auto padding = inBubblePadding();
 | 
				
			||||||
		bshift += st::msgDateFont->height;
 | 
						auto tshift = padding.top();
 | 
				
			||||||
 | 
						auto bshift = padding.bottom();
 | 
				
			||||||
 | 
						if (_asArticle || (isBubbleBottom() && _attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right())) {
 | 
				
			||||||
 | 
							bshift += bottomInfoPadding();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						width -= padding.left() + padding.right();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QRect bar(rtlrect(st::msgPadding.left(), 0, st::webPageBar, _height - bshift, _width));
 | 
						QRect bar(rtlrect(st::msgPadding.left(), tshift, st::webPageBar, _height - tshift - bshift, _width));
 | 
				
			||||||
	p.fillRect(bar, barfg);
 | 
						p.fillRect(bar, barfg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (_asArticle) {
 | 
						if (_asArticle) {
 | 
				
			||||||
| 
						 | 
					@ -2707,17 +2705,16 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			pix = _data->photo->thumb->pixBlurredSingle(ImageRoundRadius::Small, pixw, pixh, pw, ph);
 | 
								pix = _data->photo->thumb->pixBlurredSingle(ImageRoundRadius::Small, pixw, pixh, pw, ph);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		p.drawPixmapLeft(lshift + width - pw, 0, _width, pix);
 | 
							p.drawPixmapLeft(padding.left() + width - pw, 0, _width, pix);
 | 
				
			||||||
		if (selected) {
 | 
							if (selected) {
 | 
				
			||||||
			App::roundRect(p, rtlrect(lshift + width - pw, 0, pw, _pixh, _width), textstyleCurrent()->selectOverlay, SelectedOverlaySmallCorners);
 | 
								App::roundRect(p, rtlrect(padding.left() + width - pw, 0, pw, _pixh, _width), textstyleCurrent()->selectOverlay, SelectedOverlaySmallCorners);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		width -= pw + st::webPagePhotoDelta;
 | 
							width -= pw + st::webPagePhotoDelta;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	int32 tshift = 0;
 | 
					 | 
				
			||||||
	if (_siteNameWidth) {
 | 
						if (_siteNameWidth) {
 | 
				
			||||||
		p.setFont(st::webPageTitleFont);
 | 
							p.setFont(st::webPageTitleFont);
 | 
				
			||||||
		p.setPen(semibold);
 | 
							p.setPen(semibold);
 | 
				
			||||||
		p.drawTextLeft(lshift, tshift, _width, (width >= _siteNameWidth) ? _data->siteName : st::webPageTitleFont->elided(_data->siteName, width));
 | 
							p.drawTextLeft(padding.left(), tshift, _width, (width >= _siteNameWidth) ? _data->siteName : st::webPageTitleFont->elided(_data->siteName, width));
 | 
				
			||||||
		tshift += _lineHeight;
 | 
							tshift += _lineHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (_titleLines) {
 | 
						if (_titleLines) {
 | 
				
			||||||
| 
						 | 
					@ -2726,7 +2723,7 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u
 | 
				
			||||||
		if (_title.hasSkipBlock()) {
 | 
							if (_title.hasSkipBlock()) {
 | 
				
			||||||
			endskip = _parent->skipBlockWidth();
 | 
								endskip = _parent->skipBlockWidth();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		_title.drawLeftElided(p, lshift, tshift, width, _width, _titleLines, style::al_left, 0, -1, endskip, false, selection);
 | 
							_title.drawLeftElided(p, padding.left(), tshift, width, _width, _titleLines, style::al_left, 0, -1, endskip, false, selection);
 | 
				
			||||||
		tshift += _titleLines * _lineHeight;
 | 
							tshift += _titleLines * _lineHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (_descriptionLines) {
 | 
						if (_descriptionLines) {
 | 
				
			||||||
| 
						 | 
					@ -2735,16 +2732,17 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u
 | 
				
			||||||
		if (_description.hasSkipBlock()) {
 | 
							if (_description.hasSkipBlock()) {
 | 
				
			||||||
			endskip = _parent->skipBlockWidth();
 | 
								endskip = _parent->skipBlockWidth();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		_description.drawLeftElided(p, lshift, tshift, width, _width, _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection));
 | 
							_description.drawLeftElided(p, padding.left(), tshift, width, _width, _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection));
 | 
				
			||||||
		tshift += _descriptionLines * _lineHeight;
 | 
							tshift += _descriptionLines * _lineHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (_attach) {
 | 
						if (_attach) {
 | 
				
			||||||
		if (tshift) tshift += st::webPagePhotoSkip;
 | 
							auto attachAtTop = !_siteNameWidth && !_titleLines && !_descriptionLines;
 | 
				
			||||||
 | 
							if (!attachAtTop) tshift += st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top();
 | 
							auto attachLeft = padding.left() - bubble.left();
 | 
				
			||||||
 | 
							auto attachTop = tshift - bubble.top();
 | 
				
			||||||
		if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
 | 
							if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		p.save();
 | 
					 | 
				
			||||||
		p.translate(attachLeft, attachTop);
 | 
							p.translate(attachLeft, attachTop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 };
 | 
							auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 };
 | 
				
			||||||
| 
						 | 
					@ -2771,7 +2769,7 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		p.restore();
 | 
							p.translate(-attachLeft, -attachTop);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2781,22 +2779,24 @@ HistoryTextState HistoryWebPage::getState(int x, int y, HistoryStateRequest requ
 | 
				
			||||||
	if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
 | 
						if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
 | 
				
			||||||
	int32 skipx = 0, skipy = 0, width = _width, height = _height;
 | 
						int32 skipx = 0, skipy = 0, width = _width, height = _height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 lshift = st::msgPadding.left() + st::webPageLeft, rshift = st::msgPadding.right(), bshift = st::msgPadding.bottom();
 | 
					 | 
				
			||||||
	width -= lshift + rshift;
 | 
					 | 
				
			||||||
	QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
 | 
						QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
 | 
				
			||||||
	if (_asArticle || (_attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right())) {
 | 
						auto padding = inBubblePadding();
 | 
				
			||||||
		bshift += st::msgDateFont->height;
 | 
						auto tshift = padding.top();
 | 
				
			||||||
 | 
						auto bshift = padding.bottom();
 | 
				
			||||||
 | 
						if (_asArticle || (isBubbleBottom() && _attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right())) {
 | 
				
			||||||
 | 
							bshift += bottomInfoPadding();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						width -= padding.left() + padding.right();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool inThumb = false;
 | 
						bool inThumb = false;
 | 
				
			||||||
	if (_asArticle) {
 | 
						if (_asArticle) {
 | 
				
			||||||
		int32 pw = qMax(_pixw, int16(_lineHeight));
 | 
							int32 pw = qMax(_pixw, int16(_lineHeight));
 | 
				
			||||||
		if (rtlrect(lshift + width - pw, 0, pw, _pixh, _width).contains(x, y)) {
 | 
							if (rtlrect(padding.left() + width - pw, 0, pw, _pixh, _width).contains(x, y)) {
 | 
				
			||||||
			inThumb = true;
 | 
								inThumb = true;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		width -= pw + st::webPagePhotoDelta;
 | 
							width -= pw + st::webPagePhotoDelta;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	int tshift = 0, symbolAdd = 0;
 | 
						int symbolAdd = 0;
 | 
				
			||||||
	if (_siteNameWidth) {
 | 
						if (_siteNameWidth) {
 | 
				
			||||||
		tshift += _lineHeight;
 | 
							tshift += _lineHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -2804,7 +2804,7 @@ HistoryTextState HistoryWebPage::getState(int x, int y, HistoryStateRequest requ
 | 
				
			||||||
		if (y >= tshift && y < tshift + _titleLines * _lineHeight) {
 | 
							if (y >= tshift && y < tshift + _titleLines * _lineHeight) {
 | 
				
			||||||
			Text::StateRequestElided titleRequest = request.forText();
 | 
								Text::StateRequestElided titleRequest = request.forText();
 | 
				
			||||||
			titleRequest.lines = _titleLines;
 | 
								titleRequest.lines = _titleLines;
 | 
				
			||||||
			result = _title.getStateElidedLeft(x - lshift, y - tshift, width, _width, titleRequest);
 | 
								result = _title.getStateElidedLeft(x - padding.left(), y - tshift, width, _width, titleRequest);
 | 
				
			||||||
		} else if (y >= tshift + _titleLines * _lineHeight) {
 | 
							} else if (y >= tshift + _titleLines * _lineHeight) {
 | 
				
			||||||
			symbolAdd += _title.length();
 | 
								symbolAdd += _title.length();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -2814,7 +2814,7 @@ HistoryTextState HistoryWebPage::getState(int x, int y, HistoryStateRequest requ
 | 
				
			||||||
		if (y >= tshift && y < tshift + _descriptionLines * _lineHeight) {
 | 
							if (y >= tshift && y < tshift + _descriptionLines * _lineHeight) {
 | 
				
			||||||
			Text::StateRequestElided descriptionRequest = request.forText();
 | 
								Text::StateRequestElided descriptionRequest = request.forText();
 | 
				
			||||||
			descriptionRequest.lines = _descriptionLines;
 | 
								descriptionRequest.lines = _descriptionLines;
 | 
				
			||||||
			result = _description.getStateElidedLeft(x - lshift, y - tshift, width, _width, descriptionRequest);
 | 
								result = _description.getStateElidedLeft(x - padding.left(), y - tshift, width, _width, descriptionRequest);
 | 
				
			||||||
		} else if (y >= tshift + _descriptionLines * _lineHeight) {
 | 
							} else if (y >= tshift + _descriptionLines * _lineHeight) {
 | 
				
			||||||
			symbolAdd += _description.length();
 | 
								symbolAdd += _description.length();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -2823,10 +2823,12 @@ HistoryTextState HistoryWebPage::getState(int x, int y, HistoryStateRequest requ
 | 
				
			||||||
	if (inThumb) {
 | 
						if (inThumb) {
 | 
				
			||||||
		result.link = _openl;
 | 
							result.link = _openl;
 | 
				
			||||||
	} else if (_attach) {
 | 
						} else if (_attach) {
 | 
				
			||||||
		if (tshift) tshift += st::webPagePhotoSkip;
 | 
							auto attachAtTop = !_siteNameWidth && !_titleLines && !_descriptionLines;
 | 
				
			||||||
 | 
							if (!attachAtTop) tshift += st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (x >= lshift && x < lshift + width && y >= tshift && y < _height - st::msgPadding.bottom()) {
 | 
							if (x >= padding.left() && x < padding.left() + width && y >= tshift && y < _height - bshift) {
 | 
				
			||||||
			int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top();
 | 
								auto attachLeft = padding.left() - bubble.left();
 | 
				
			||||||
 | 
								auto attachTop = tshift - bubble.top();
 | 
				
			||||||
			if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
 | 
								if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
 | 
				
			||||||
			result = _attach->getState(x - attachLeft, y - attachTop, request);
 | 
								result = _attach->getState(x - attachLeft, y - attachTop, request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2901,6 +2903,28 @@ ImagePtr HistoryWebPage::replyPreview() {
 | 
				
			||||||
	return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr());
 | 
						return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QMargins HistoryWebPage::inBubblePadding() const {
 | 
				
			||||||
 | 
						auto lshift = st::msgPadding.left() + st::webPageLeft;
 | 
				
			||||||
 | 
						auto rshift = st::msgPadding.right();
 | 
				
			||||||
 | 
						auto bshift = isBubbleBottom() ? st::msgPadding.left() : st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
						auto tshift = isBubbleTop() ? st::msgPadding.left() : st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
						return QMargins(lshift, tshift, rshift, bshift);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int HistoryWebPage::bottomInfoPadding() const {
 | 
				
			||||||
 | 
						if (!isBubbleBottom()) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto result = st::msgDateFont->height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// we use padding greater than st::msgPadding.bottom() in the
 | 
				
			||||||
 | 
						// bottom of the bubble so that the left line looks pretty.
 | 
				
			||||||
 | 
						// but if we have bottom skip because of the info display
 | 
				
			||||||
 | 
						// we don't need that additional padding so we replace it
 | 
				
			||||||
 | 
						// back with st::msgPadding.bottom() instead of left().
 | 
				
			||||||
 | 
						result += st::msgPadding.bottom() - st::msgPadding.left();
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HistoryGame::HistoryGame(HistoryItem *parent, GameData *data) : HistoryMedia(parent)
 | 
					HistoryGame::HistoryGame(HistoryItem *parent, GameData *data) : HistoryMedia(parent)
 | 
				
			||||||
, _data(data)
 | 
					, _data(data)
 | 
				
			||||||
, _title(st::msgMinWidth - st::webPageLeft)
 | 
					, _title(st::msgMinWidth - st::webPageLeft)
 | 
				
			||||||
| 
						 | 
					@ -2917,7 +2941,7 @@ HistoryGame::HistoryGame(HistoryItem *parent, const HistoryGame &other) : Histor
 | 
				
			||||||
void HistoryGame::initDimensions() {
 | 
					void HistoryGame::initDimensions() {
 | 
				
			||||||
	if (!_lineHeight) _lineHeight = qMax(st::webPageTitleFont->height, st::webPageDescriptionFont->height);
 | 
						if (!_lineHeight) _lineHeight = qMax(st::webPageTitleFont->height, st::webPageDescriptionFont->height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!_openl && !_data->url.isEmpty()) _openl.reset(new UrlClickHandler(_data->url, true));
 | 
						if (!_openl) _openl.reset(new ReplyMarkupClickHandler(_parent, 0, 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto title = _data->title;
 | 
						auto title = _data->title;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2960,36 +2984,44 @@ void HistoryGame::initDimensions() {
 | 
				
			||||||
	int32 l = st::msgPadding.left() + st::webPageLeft, r = st::msgPadding.right();
 | 
						int32 l = st::msgPadding.left() + st::webPageLeft, r = st::msgPadding.right();
 | 
				
			||||||
	int32 skipBlockWidth = _parent->skipBlockWidth();
 | 
						int32 skipBlockWidth = _parent->skipBlockWidth();
 | 
				
			||||||
	_maxw = skipBlockWidth;
 | 
						_maxw = skipBlockWidth;
 | 
				
			||||||
	_minh = st::msgPadding.top();
 | 
						_minh = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 titleMinHeight = _title.isEmpty() ? 0 : _lineHeight;
 | 
						int32 titleMinHeight = _title.isEmpty() ? 0 : _lineHeight;
 | 
				
			||||||
	int32 descMaxLines = (4 + (titleMinHeight ? 0 : 1));
 | 
						int32 descMaxLines = (4 + (titleMinHeight ? 0 : 1));
 | 
				
			||||||
	int32 descriptionMinHeight = _description.isEmpty() ? 0 : qMin(_description.minHeight(), descMaxLines * _lineHeight);
 | 
						int32 descriptionMinHeight = _description.isEmpty() ? 0 : qMin(_description.minHeight(), descMaxLines * _lineHeight);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!_title.isEmpty()) {
 | 
						if (!_title.isEmpty()) {
 | 
				
			||||||
		_maxw = qMax(_maxw, int32(_title.maxWidth()));
 | 
							accumulate_max(_maxw, _title.maxWidth());
 | 
				
			||||||
		_minh += titleMinHeight;
 | 
							_minh += titleMinHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (!_description.isEmpty()) {
 | 
						if (!_description.isEmpty()) {
 | 
				
			||||||
		_maxw = qMax(_maxw, int32(_description.maxWidth()));
 | 
							accumulate_max(_maxw, _description.maxWidth());
 | 
				
			||||||
		_minh += descriptionMinHeight;
 | 
							_minh += descriptionMinHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (_attach) {
 | 
						if (_attach) {
 | 
				
			||||||
		if (_minh) _minh += st::webPagePhotoSkip;
 | 
							auto attachAtTop = !_titleLines && !_descriptionLines;
 | 
				
			||||||
 | 
							if (!attachAtTop) _minh += st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_attach->initDimensions();
 | 
							_attach->initDimensions();
 | 
				
			||||||
		QMargins bubble(_attach->bubbleMargins());
 | 
							QMargins bubble(_attach->bubbleMargins());
 | 
				
			||||||
		_maxw = qMax(_maxw, int32(_attach->maxWidth() - bubble.left() - bubble.top()));
 | 
							auto maxMediaWidth = _attach->maxWidth() - bubble.left() - bubble.right();
 | 
				
			||||||
 | 
							if (isBubbleBottom() && _attach->customInfoLayout()) {
 | 
				
			||||||
 | 
								maxMediaWidth += skipBlockWidth;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							accumulate_max(_maxw, maxMediaWidth);
 | 
				
			||||||
		_minh += _attach->minHeight() - bubble.top() - bubble.bottom();
 | 
							_minh += _attach->minHeight() - bubble.top() - bubble.bottom();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_maxw += st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
 | 
						_maxw += st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
 | 
				
			||||||
 | 
						auto padding = inBubblePadding();
 | 
				
			||||||
 | 
						_minh += padding.top() + padding.bottom();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int HistoryGame::resizeGetHeight(int width) {
 | 
					int HistoryGame::resizeGetHeight(int width) {
 | 
				
			||||||
	_width = qMin(width, _maxw);
 | 
						_width = qMin(width, _maxw);
 | 
				
			||||||
	width -= st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
 | 
						width -= st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 linesMax = 5;
 | 
						int linesMax = 5;
 | 
				
			||||||
	_height = st::msgPadding.top();
 | 
						_height = 0;
 | 
				
			||||||
	if (_title.isEmpty()) {
 | 
						if (_title.isEmpty()) {
 | 
				
			||||||
		_titleLines = 0;
 | 
							_titleLines = 0;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
| 
						 | 
					@ -3014,13 +3046,19 @@ int HistoryGame::resizeGetHeight(int width) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (_attach) {
 | 
						if (_attach) {
 | 
				
			||||||
		if (_height) _height += st::webPagePhotoSkip;
 | 
							auto attachAtTop = !_titleLines && !_descriptionLines;
 | 
				
			||||||
 | 
							if (!attachAtTop) _height += st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		QMargins bubble(_attach->bubbleMargins());
 | 
							QMargins bubble(_attach->bubbleMargins());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_attach->resizeGetHeight(width + bubble.left() + bubble.right());
 | 
							_attach->resizeGetHeight(width + bubble.left() + bubble.right());
 | 
				
			||||||
		_height += _attach->height() - bubble.top() - bubble.bottom();
 | 
							_height += _attach->height() - bubble.top() - bubble.bottom();
 | 
				
			||||||
 | 
							if (isBubbleBottom() && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
 | 
				
			||||||
 | 
								_height += bottomInfoPadding();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						auto padding = inBubblePadding();
 | 
				
			||||||
 | 
						_height += padding.top() + padding.bottom();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return _height;
 | 
						return _height;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3036,21 +3074,25 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, uint
 | 
				
			||||||
	style::color semibold = (selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
 | 
						style::color semibold = (selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
 | 
				
			||||||
	style::color regular = (selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
 | 
						style::color regular = (selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 lshift = st::msgPadding.left() + st::webPageLeft, rshift = st::msgPadding.right(), bshift = 0;
 | 
					 | 
				
			||||||
	width -= lshift + rshift;
 | 
					 | 
				
			||||||
	QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
 | 
						QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
 | 
				
			||||||
 | 
						auto padding = inBubblePadding();
 | 
				
			||||||
 | 
						auto tshift = padding.top();
 | 
				
			||||||
 | 
						auto bshift = padding.bottom();
 | 
				
			||||||
 | 
						if (isBubbleBottom() && _attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
 | 
				
			||||||
 | 
							bshift += bottomInfoPadding();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						width -= padding.left() + padding.right();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QRect bar(rtlrect(st::msgPadding.left(), st::msgPadding.top(), st::webPageBar, _height - bshift, _width));
 | 
						QRect bar(rtlrect(st::msgPadding.left(), tshift, st::webPageBar, _height - tshift - bshift, _width));
 | 
				
			||||||
	p.fillRect(bar, barfg);
 | 
						p.fillRect(bar, barfg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 tshift = st::msgPadding.top();
 | 
					 | 
				
			||||||
	if (_titleLines) {
 | 
						if (_titleLines) {
 | 
				
			||||||
		p.setPen(st::black);
 | 
							p.setPen(st::black);
 | 
				
			||||||
		int32 endskip = 0;
 | 
							int32 endskip = 0;
 | 
				
			||||||
		if (_title.hasSkipBlock()) {
 | 
							if (_title.hasSkipBlock()) {
 | 
				
			||||||
			endskip = _parent->skipBlockWidth();
 | 
								endskip = _parent->skipBlockWidth();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		_title.drawLeftElided(p, lshift, tshift, width, _width, _titleLines, style::al_left, 0, -1, endskip, false, selection);
 | 
							_title.drawLeftElided(p, padding.left(), tshift, width, _width, _titleLines, style::al_left, 0, -1, endskip, false, selection);
 | 
				
			||||||
		tshift += _titleLines * _lineHeight;
 | 
							tshift += _titleLines * _lineHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (_descriptionLines) {
 | 
						if (_descriptionLines) {
 | 
				
			||||||
| 
						 | 
					@ -3059,13 +3101,15 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, uint
 | 
				
			||||||
		if (_description.hasSkipBlock()) {
 | 
							if (_description.hasSkipBlock()) {
 | 
				
			||||||
			endskip = _parent->skipBlockWidth();
 | 
								endskip = _parent->skipBlockWidth();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		_description.drawLeftElided(p, lshift, tshift, width, _width, _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection));
 | 
							_description.drawLeftElided(p, padding.left(), tshift, width, _width, _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection));
 | 
				
			||||||
		tshift += _descriptionLines * _lineHeight;
 | 
							tshift += _descriptionLines * _lineHeight;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (_attach) {
 | 
						if (_attach) {
 | 
				
			||||||
		if (tshift) tshift += st::webPagePhotoSkip;
 | 
							auto attachAtTop = !_titleLines && !_descriptionLines;
 | 
				
			||||||
 | 
							if (!attachAtTop) tshift += st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top();
 | 
							auto attachLeft = padding.left() - bubble.left();
 | 
				
			||||||
 | 
							auto attachTop = tshift - bubble.top();
 | 
				
			||||||
		if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
 | 
							if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 };
 | 
							auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 };
 | 
				
			||||||
| 
						 | 
					@ -3082,17 +3126,22 @@ HistoryTextState HistoryGame::getState(int x, int y, HistoryStateRequest request
 | 
				
			||||||
	if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
 | 
						if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
 | 
				
			||||||
	int32 width = _width, height = _height;
 | 
						int32 width = _width, height = _height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 lshift = st::msgPadding.left() + st::webPageLeft, rshift = st::msgPadding.right(), bshift = 0;
 | 
					 | 
				
			||||||
	width -= lshift + rshift;
 | 
					 | 
				
			||||||
	QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
 | 
						QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
 | 
				
			||||||
 | 
						auto padding = inBubblePadding();
 | 
				
			||||||
 | 
						auto tshift = padding.top();
 | 
				
			||||||
 | 
						auto bshift = padding.bottom();
 | 
				
			||||||
 | 
						if (isBubbleBottom() && _attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
 | 
				
			||||||
 | 
							bshift += bottomInfoPadding();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						width -= padding.left() + padding.right();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool inThumb = false;
 | 
						bool inThumb = false;
 | 
				
			||||||
	int tshift = st::msgPadding.top(), symbolAdd = 0;
 | 
						int symbolAdd = 0;
 | 
				
			||||||
	if (_titleLines) {
 | 
						if (_titleLines) {
 | 
				
			||||||
		if (y >= tshift && y < tshift + _titleLines * _lineHeight) {
 | 
							if (y >= tshift && y < tshift + _titleLines * _lineHeight) {
 | 
				
			||||||
			Text::StateRequestElided titleRequest = request.forText();
 | 
								Text::StateRequestElided titleRequest = request.forText();
 | 
				
			||||||
			titleRequest.lines = _titleLines;
 | 
								titleRequest.lines = _titleLines;
 | 
				
			||||||
			result = _title.getStateElidedLeft(x - lshift, y - tshift, width, _width, titleRequest);
 | 
								result = _title.getStateElidedLeft(x - padding.left(), y - tshift, width, _width, titleRequest);
 | 
				
			||||||
		} else if (y >= tshift + _titleLines * _lineHeight) {
 | 
							} else if (y >= tshift + _titleLines * _lineHeight) {
 | 
				
			||||||
			symbolAdd += _title.length();
 | 
								symbolAdd += _title.length();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -3102,7 +3151,7 @@ HistoryTextState HistoryGame::getState(int x, int y, HistoryStateRequest request
 | 
				
			||||||
		if (y >= tshift && y < tshift + _descriptionLines * _lineHeight) {
 | 
							if (y >= tshift && y < tshift + _descriptionLines * _lineHeight) {
 | 
				
			||||||
			Text::StateRequestElided descriptionRequest = request.forText();
 | 
								Text::StateRequestElided descriptionRequest = request.forText();
 | 
				
			||||||
			descriptionRequest.lines = _descriptionLines;
 | 
								descriptionRequest.lines = _descriptionLines;
 | 
				
			||||||
			result = _description.getStateElidedLeft(x - lshift, y - tshift, width, _width, descriptionRequest);
 | 
								result = _description.getStateElidedLeft(x - padding.left(), y - tshift, width, _width, descriptionRequest);
 | 
				
			||||||
		} else if (y >= tshift + _descriptionLines * _lineHeight) {
 | 
							} else if (y >= tshift + _descriptionLines * _lineHeight) {
 | 
				
			||||||
			symbolAdd += _description.length();
 | 
								symbolAdd += _description.length();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -3111,9 +3160,14 @@ HistoryTextState HistoryGame::getState(int x, int y, HistoryStateRequest request
 | 
				
			||||||
	if (inThumb) {
 | 
						if (inThumb) {
 | 
				
			||||||
		result.link = _openl;
 | 
							result.link = _openl;
 | 
				
			||||||
	} else if (_attach) {
 | 
						} else if (_attach) {
 | 
				
			||||||
		if (tshift) tshift += st::webPagePhotoSkip;
 | 
							auto attachAtTop = !_titleLines && !_descriptionLines;
 | 
				
			||||||
 | 
							if (!attachAtTop) tshift += st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (x >= lshift && x < lshift + width && y >= tshift && y < _height) {
 | 
							auto attachLeft = padding.left() - bubble.left();
 | 
				
			||||||
 | 
							auto attachTop = tshift - bubble.top();
 | 
				
			||||||
 | 
							if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (x >= attachLeft && x < attachLeft + _attach->currentWidth() && y >= tshift && y < _height - bshift) {
 | 
				
			||||||
			result.link = _openl;
 | 
								result.link = _openl;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -3177,6 +3231,28 @@ ImagePtr HistoryGame::replyPreview() {
 | 
				
			||||||
	return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr());
 | 
						return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QMargins HistoryGame::inBubblePadding() const {
 | 
				
			||||||
 | 
						auto lshift = st::msgPadding.left() + st::webPageLeft;
 | 
				
			||||||
 | 
						auto rshift = st::msgPadding.right();
 | 
				
			||||||
 | 
						auto bshift = isBubbleBottom() ? st::msgPadding.left() : st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
						auto tshift = isBubbleTop() ? st::msgPadding.left() : st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
						return QMargins(lshift, tshift, rshift, bshift);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int HistoryGame::bottomInfoPadding() const {
 | 
				
			||||||
 | 
						if (!isBubbleBottom()) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto result = st::msgDateFont->height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// we use padding greater than st::msgPadding.bottom() in the
 | 
				
			||||||
 | 
						// bottom of the bubble so that the left line looks pretty.
 | 
				
			||||||
 | 
						// but if we have bottom skip because of the info display
 | 
				
			||||||
 | 
						// we don't need that additional padding so we replace it
 | 
				
			||||||
 | 
						// back with st::msgPadding.bottom() instead of left().
 | 
				
			||||||
 | 
						result += st::msgPadding.bottom() - st::msgPadding.left();
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HistoryLocation::HistoryLocation(HistoryItem *parent, const LocationCoords &coords, const QString &title, const QString &description) : HistoryMedia(parent)
 | 
					HistoryLocation::HistoryLocation(HistoryItem *parent, const LocationCoords &coords, const QString &title, const QString &description) : HistoryMedia(parent)
 | 
				
			||||||
, _data(App::location(coords))
 | 
					, _data(App::location(coords))
 | 
				
			||||||
, _title(st::msgMinWidth)
 | 
					, _title(st::msgMinWidth)
 | 
				
			||||||
| 
						 | 
					@ -3220,8 +3296,8 @@ void HistoryLocation::initDimensions() {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
							_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
 | 
				
			||||||
		if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
							if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
				
			||||||
			_minh += st::webPagePhotoSkip;
 | 
								_minh += st::mediaInBubbleSkip;
 | 
				
			||||||
			if (!_parent->Has<HistoryMessageForwarded>() && !_parent->Has<HistoryMessageReply>()) {
 | 
								if (isBubbleTop()) {
 | 
				
			||||||
				_minh += st::msgPadding.top();
 | 
									_minh += st::msgPadding.top();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -3260,8 +3336,8 @@ int HistoryLocation::resizeGetHeight(int width) {
 | 
				
			||||||
			_height += qMin(_description.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()), st::webPageDescriptionFont->height * 3);
 | 
								_height += qMin(_description.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()), st::webPageDescriptionFont->height * 3);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
							if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
				
			||||||
			_height += st::webPagePhotoSkip;
 | 
								_height += st::mediaInBubbleSkip;
 | 
				
			||||||
			if (!_parent->Has<HistoryMessageForwarded>() && !_parent->Has<HistoryMessageReply>()) {
 | 
								if (isBubbleTop()) {
 | 
				
			||||||
				_height += st::msgPadding.top();
 | 
									_height += st::msgPadding.top();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -3281,7 +3357,7 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection,
 | 
				
			||||||
		skipy = st::mediaPadding.top();
 | 
							skipy = st::mediaPadding.top();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
							if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
				
			||||||
			if (!_parent->Has<HistoryMessageForwarded>() && !_parent->Has<HistoryMessageReply>()) {
 | 
								if (isBubbleTop()) {
 | 
				
			||||||
				skipy += st::msgPadding.top();
 | 
									skipy += st::msgPadding.top();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -3299,7 +3375,7 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection,
 | 
				
			||||||
			skipy += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
 | 
								skipy += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
							if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
				
			||||||
			skipy += st::webPagePhotoSkip;
 | 
								skipy += st::mediaInBubbleSkip;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		height -= skipy + st::mediaPadding.bottom();
 | 
							height -= skipy + st::mediaPadding.bottom();
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
| 
						 | 
					@ -3347,7 +3423,7 @@ HistoryTextState HistoryLocation::getState(int x, int y, HistoryStateRequest req
 | 
				
			||||||
		skipy = st::mediaPadding.top();
 | 
							skipy = st::mediaPadding.top();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
							if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
				
			||||||
			if (!_parent->Has<HistoryMessageForwarded>() && !_parent->Has<HistoryMessageReply>()) {
 | 
								if (isBubbleTop()) {
 | 
				
			||||||
				skipy += st::msgPadding.top();
 | 
									skipy += st::msgPadding.top();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -3375,7 +3451,7 @@ HistoryTextState HistoryLocation::getState(int x, int y, HistoryStateRequest req
 | 
				
			||||||
			skipy += descriptionh;
 | 
								skipy += descriptionh;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
							if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
				
			||||||
			skipy += st::webPagePhotoSkip;
 | 
								skipy += st::mediaInBubbleSkip;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		height -= skipy + st::mediaPadding.bottom();
 | 
							height -= skipy + st::mediaPadding.bottom();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,906 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					This file is part of Telegram Desktop,
 | 
				
			||||||
 | 
					the official desktop version of Telegram messaging app, see https://telegram.org
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Telegram Desktop is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					(at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
				
			||||||
 | 
					GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In addition, as a special exception, the copyright holders give permission
 | 
				
			||||||
 | 
					to link the code of portions of this program with the OpenSSL library.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 | 
				
			||||||
 | 
					Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ui/effects/radial_animation.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void historyInitMedia();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HistoryFileMedia : public HistoryMedia {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						using HistoryMedia::HistoryMedia;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return p == _openl || p == _savel || p == _cancell;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return p == _openl || p == _savel || p == _cancell;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
 | 
				
			||||||
 | 
						void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~HistoryFileMedia();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						ClickHandlerPtr _openl, _savel, _cancell;
 | 
				
			||||||
 | 
						void setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell);
 | 
				
			||||||
 | 
						void setDocumentLinks(DocumentData *document, bool inlinegif = false) {
 | 
				
			||||||
 | 
							ClickHandlerPtr open, save;
 | 
				
			||||||
 | 
							if (inlinegif) {
 | 
				
			||||||
 | 
								open.reset(new GifOpenClickHandler(document));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								open.reset(new DocumentOpenClickHandler(document));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (inlinegif) {
 | 
				
			||||||
 | 
								save.reset(new GifOpenClickHandler(document));
 | 
				
			||||||
 | 
							} else if (document->voice()) {
 | 
				
			||||||
 | 
								save.reset(new DocumentOpenClickHandler(document));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								save.reset(new DocumentSaveClickHandler(document));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							setLinks(std_::move(open), std_::move(save), MakeShared<DocumentCancelClickHandler>(document));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// >= 0 will contain download / upload string, _statusSize = loaded bytes
 | 
				
			||||||
 | 
						// < 0 will contain played string, _statusSize = -(seconds + 1) played
 | 
				
			||||||
 | 
						// 0x7FFFFFF0 will contain status for not yet downloaded file
 | 
				
			||||||
 | 
						// 0x7FFFFFF1 will contain status for already downloaded file
 | 
				
			||||||
 | 
						// 0x7FFFFFF2 will contain status for failed to download / upload file
 | 
				
			||||||
 | 
						mutable int32 _statusSize;
 | 
				
			||||||
 | 
						mutable QString _statusText;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// duration = -1 - no duration, duration = -2 - "GIF" duration
 | 
				
			||||||
 | 
						void setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void step_thumbOver(float64 ms, bool timer);
 | 
				
			||||||
 | 
						void step_radial(uint64 ms, bool timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void ensureAnimation() const;
 | 
				
			||||||
 | 
						void checkAnimationFinished();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool isRadialAnimation(uint64 ms) const {
 | 
				
			||||||
 | 
							if (!_animation || !_animation->radial.animating()) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_animation->radial.step(ms);
 | 
				
			||||||
 | 
							return _animation && _animation->radial.animating();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool isThumbAnimation(uint64 ms) const {
 | 
				
			||||||
 | 
							if (!_animation || !_animation->_a_thumbOver.animating()) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_animation->_a_thumbOver.step(ms);
 | 
				
			||||||
 | 
							return _animation && _animation->_a_thumbOver.animating();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual float64 dataProgress() const = 0;
 | 
				
			||||||
 | 
						virtual bool dataFinished() const = 0;
 | 
				
			||||||
 | 
						virtual bool dataLoaded() const = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct AnimationData {
 | 
				
			||||||
 | 
							AnimationData(AnimationCallbacks &&thumbOverCallbacks, AnimationCallbacks &&radialCallbacks) : a_thumbOver(0, 0)
 | 
				
			||||||
 | 
								, _a_thumbOver(std_::move(thumbOverCallbacks))
 | 
				
			||||||
 | 
								, radial(std_::move(radialCallbacks)) {
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							anim::fvalue a_thumbOver;
 | 
				
			||||||
 | 
							Animation _a_thumbOver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Ui::RadialAnimation radial;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						mutable AnimationData *_animation = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HistoryPhoto : public HistoryFileMedia {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						HistoryPhoto(HistoryItem *parent, PhotoData *photo, const QString &caption);
 | 
				
			||||||
 | 
						HistoryPhoto(HistoryItem *parent, PeerData *chat, const MTPDphoto &photo, int width);
 | 
				
			||||||
 | 
						HistoryPhoto(HistoryItem *parent, const HistoryPhoto &other);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void init();
 | 
				
			||||||
 | 
						HistoryMediaType type() const override {
 | 
				
			||||||
 | 
							return MediaTypePhoto;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						HistoryPhoto *clone(HistoryItem *newParent) const override {
 | 
				
			||||||
 | 
							return new HistoryPhoto(newParent, *this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initDimensions() override;
 | 
				
			||||||
 | 
						int resizeGetHeight(int width) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
				
			||||||
 | 
						HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
 | 
				
			||||||
 | 
							return _caption.adjustSelection(selection, type);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool hasTextForCopy() const override {
 | 
				
			||||||
 | 
							return !_caption.isEmpty();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QString notificationText() const override;
 | 
				
			||||||
 | 
						QString inDialogsText() const override;
 | 
				
			||||||
 | 
						TextWithEntities selectedText(TextSelection selection) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PhotoData *photo() const {
 | 
				
			||||||
 | 
							return _data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void updateSentMedia(const MTPMessageMedia &media) override;
 | 
				
			||||||
 | 
						bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void attachToParent() override;
 | 
				
			||||||
 | 
						void detachFromParent() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool hasReplyPreview() const override {
 | 
				
			||||||
 | 
							return !_data->thumb->isNull();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ImagePtr replyPreview() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextWithEntities getCaption() const override {
 | 
				
			||||||
 | 
							return _caption.originalTextWithEntities();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool needsBubble() const override {
 | 
				
			||||||
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (_parent->viaBot()) {
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool customInfoLayout() const override {
 | 
				
			||||||
 | 
							return _caption.isEmpty();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool hideFromName() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						float64 dataProgress() const override {
 | 
				
			||||||
 | 
							return _data->progress();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dataFinished() const override {
 | 
				
			||||||
 | 
							return !_data->loading() && !_data->uploading();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dataLoaded() const override {
 | 
				
			||||||
 | 
							return _data->loaded();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						PhotoData *_data;
 | 
				
			||||||
 | 
						int16 _pixw = 1;
 | 
				
			||||||
 | 
						int16 _pixh = 1;
 | 
				
			||||||
 | 
						Text _caption;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HistoryVideo : public HistoryFileMedia {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						HistoryVideo(HistoryItem *parent, DocumentData *document, const QString &caption);
 | 
				
			||||||
 | 
						HistoryVideo(HistoryItem *parent, const HistoryVideo &other);
 | 
				
			||||||
 | 
						HistoryMediaType type() const override {
 | 
				
			||||||
 | 
							return MediaTypeVideo;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						HistoryVideo *clone(HistoryItem *newParent) const override {
 | 
				
			||||||
 | 
							return new HistoryVideo(newParent, *this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initDimensions() override;
 | 
				
			||||||
 | 
						int resizeGetHeight(int width) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
				
			||||||
 | 
						HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
 | 
				
			||||||
 | 
							return _caption.adjustSelection(selection, type);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool hasTextForCopy() const override {
 | 
				
			||||||
 | 
							return !_caption.isEmpty();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QString notificationText() const override;
 | 
				
			||||||
 | 
						QString inDialogsText() const override;
 | 
				
			||||||
 | 
						TextWithEntities selectedText(TextSelection selection) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DocumentData *getDocument() override {
 | 
				
			||||||
 | 
							return _data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool uploading() const override {
 | 
				
			||||||
 | 
							return _data->uploading();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void attachToParent() override;
 | 
				
			||||||
 | 
						void detachFromParent() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool hasReplyPreview() const override {
 | 
				
			||||||
 | 
							return !_data->thumb->isNull();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ImagePtr replyPreview() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextWithEntities getCaption() const override {
 | 
				
			||||||
 | 
							return _caption.originalTextWithEntities();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool needsBubble() const override {
 | 
				
			||||||
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (_parent->viaBot()) {
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool customInfoLayout() const override {
 | 
				
			||||||
 | 
							return _caption.isEmpty();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool hideFromName() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						float64 dataProgress() const override {
 | 
				
			||||||
 | 
							return _data->progress();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dataFinished() const override {
 | 
				
			||||||
 | 
							return !_data->loading() && !_data->uploading();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dataLoaded() const override {
 | 
				
			||||||
 | 
							return _data->loaded();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						DocumentData *_data;
 | 
				
			||||||
 | 
						int32 _thumbw;
 | 
				
			||||||
 | 
						Text _caption;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void setStatusSize(int32 newSize) const;
 | 
				
			||||||
 | 
						void updateStatusText() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct HistoryDocumentThumbed : public BaseComponent<HistoryDocumentThumbed> {
 | 
				
			||||||
 | 
						ClickHandlerPtr _linksavel, _linkcancell;
 | 
				
			||||||
 | 
						int _thumbw = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutable int _linkw = 0;
 | 
				
			||||||
 | 
						mutable QString _link;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					struct HistoryDocumentCaptioned : public BaseComponent<HistoryDocumentCaptioned> {
 | 
				
			||||||
 | 
						Text _caption = { int(st::msgFileMinWidth) - st::msgPadding.left() - st::msgPadding.right() };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					struct HistoryDocumentNamed : public BaseComponent<HistoryDocumentNamed> {
 | 
				
			||||||
 | 
						QString _name;
 | 
				
			||||||
 | 
						int _namew = 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					class HistoryDocument;
 | 
				
			||||||
 | 
					struct HistoryDocumentVoicePlayback {
 | 
				
			||||||
 | 
						HistoryDocumentVoicePlayback(const HistoryDocument *that);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int32 _position;
 | 
				
			||||||
 | 
						anim::fvalue a_progress;
 | 
				
			||||||
 | 
						Animation _a_progress;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					struct HistoryDocumentVoice : public BaseComponent<HistoryDocumentVoice> {
 | 
				
			||||||
 | 
						HistoryDocumentVoice &operator=(HistoryDocumentVoice &&other) {
 | 
				
			||||||
 | 
							std::swap(_playback, other._playback);
 | 
				
			||||||
 | 
							return *this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						~HistoryDocumentVoice() {
 | 
				
			||||||
 | 
							deleteAndMark(_playback);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void ensurePlayback(const HistoryDocument *interfaces) const;
 | 
				
			||||||
 | 
						void checkPlaybackFinished() const;
 | 
				
			||||||
 | 
						mutable HistoryDocumentVoicePlayback *_playback = nullptr;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HistoryDocument : public HistoryFileMedia, public Composer {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						HistoryDocument(HistoryItem *parent, DocumentData *document, const QString &caption);
 | 
				
			||||||
 | 
						HistoryDocument(HistoryItem *parent, const HistoryDocument &other);
 | 
				
			||||||
 | 
						HistoryMediaType type() const override {
 | 
				
			||||||
 | 
							return _data->voice() ? MediaTypeVoiceFile : (_data->song() ? MediaTypeMusicFile : MediaTypeFile);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						HistoryDocument *clone(HistoryItem *newParent) const override {
 | 
				
			||||||
 | 
							return new HistoryDocument(newParent, *this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initDimensions() override;
 | 
				
			||||||
 | 
						int resizeGetHeight(int width) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
				
			||||||
 | 
						HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
 | 
				
			||||||
 | 
							if (auto captioned = Get<HistoryDocumentCaptioned>()) {
 | 
				
			||||||
 | 
								return captioned->_caption.adjustSelection(selection, type);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return selection;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool hasTextForCopy() const override {
 | 
				
			||||||
 | 
							return Has<HistoryDocumentCaptioned>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QString notificationText() const override;
 | 
				
			||||||
 | 
						QString inDialogsText() const override;
 | 
				
			||||||
 | 
						TextWithEntities selectedText(TextSelection selection) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool uploading() const override {
 | 
				
			||||||
 | 
							return _data->uploading();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DocumentData *getDocument() override {
 | 
				
			||||||
 | 
							return _data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void attachToParent() override;
 | 
				
			||||||
 | 
						void detachFromParent() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void updateSentMedia(const MTPMessageMedia &media) override;
 | 
				
			||||||
 | 
						bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool hasReplyPreview() const override {
 | 
				
			||||||
 | 
							return !_data->thumb->isNull();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ImagePtr replyPreview() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextWithEntities getCaption() const override {
 | 
				
			||||||
 | 
							if (const HistoryDocumentCaptioned *captioned = Get<HistoryDocumentCaptioned>()) {
 | 
				
			||||||
 | 
								return captioned->_caption.originalTextWithEntities();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return TextWithEntities();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool needsBubble() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool customInfoLayout() const override {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						QMargins bubbleMargins() const override {
 | 
				
			||||||
 | 
							return Get<HistoryDocumentThumbed>() ? QMargins(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbPadding.left(), st::msgFileThumbPadding.bottom()) : st::msgPadding;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool hideForwardedFrom() const override {
 | 
				
			||||||
 | 
							return _data->song();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void step_voiceProgress(float64 ms, bool timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						float64 dataProgress() const override {
 | 
				
			||||||
 | 
							return _data->progress();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dataFinished() const override {
 | 
				
			||||||
 | 
							return !_data->loading() && !_data->uploading();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dataLoaded() const override {
 | 
				
			||||||
 | 
							return _data->loaded();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						void createComponents(bool caption);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void setStatusSize(int32 newSize, qint64 realDuration = 0) const;
 | 
				
			||||||
 | 
						bool updateStatusText() const; // returns showPause
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													   // Callback is a void(const QString &, const QString &, const Text &) functor.
 | 
				
			||||||
 | 
													   // It will be called as callback(attachType, attachFileName, attachCaption).
 | 
				
			||||||
 | 
						template <typename Callback>
 | 
				
			||||||
 | 
						void buildStringRepresentation(Callback callback) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DocumentData *_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HistoryGif : public HistoryFileMedia {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						HistoryGif(HistoryItem *parent, DocumentData *document, const QString &caption);
 | 
				
			||||||
 | 
						HistoryGif(HistoryItem *parent, const HistoryGif &other);
 | 
				
			||||||
 | 
						HistoryMediaType type() const override {
 | 
				
			||||||
 | 
							return MediaTypeGif;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						HistoryGif *clone(HistoryItem *newParent) const override {
 | 
				
			||||||
 | 
							return new HistoryGif(newParent, *this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initDimensions() override;
 | 
				
			||||||
 | 
						int resizeGetHeight(int width) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
				
			||||||
 | 
						HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
 | 
				
			||||||
 | 
							return _caption.adjustSelection(selection, type);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool hasTextForCopy() const override {
 | 
				
			||||||
 | 
							return !_caption.isEmpty();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QString notificationText() const override;
 | 
				
			||||||
 | 
						QString inDialogsText() const override;
 | 
				
			||||||
 | 
						TextWithEntities selectedText(TextSelection selection) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool uploading() const override {
 | 
				
			||||||
 | 
							return _data->uploading();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DocumentData *getDocument() override {
 | 
				
			||||||
 | 
							return _data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Media::Clip::Reader *getClipReader() override {
 | 
				
			||||||
 | 
							return gif();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool playInline(bool autoplay) override;
 | 
				
			||||||
 | 
						void stopInline() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void attachToParent() override;
 | 
				
			||||||
 | 
						void detachFromParent() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void updateSentMedia(const MTPMessageMedia &media) override;
 | 
				
			||||||
 | 
						bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool hasReplyPreview() const override {
 | 
				
			||||||
 | 
							return !_data->thumb->isNull();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ImagePtr replyPreview() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextWithEntities getCaption() const override {
 | 
				
			||||||
 | 
							return _caption.originalTextWithEntities();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool needsBubble() const override {
 | 
				
			||||||
 | 
							if (!_caption.isEmpty()) {
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (_parent->viaBot()) {
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool customInfoLayout() const override {
 | 
				
			||||||
 | 
							return _caption.isEmpty();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool hideFromName() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~HistoryGif();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						float64 dataProgress() const override;
 | 
				
			||||||
 | 
						bool dataFinished() const override;
 | 
				
			||||||
 | 
						bool dataLoaded() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						DocumentData *_data;
 | 
				
			||||||
 | 
						int32 _thumbw, _thumbh;
 | 
				
			||||||
 | 
						Text _caption;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Media::Clip::Reader *_gif;
 | 
				
			||||||
 | 
						Media::Clip::Reader *gif() {
 | 
				
			||||||
 | 
							return (_gif == Media::Clip::BadReader) ? nullptr : _gif;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						const Media::Clip::Reader *gif() const {
 | 
				
			||||||
 | 
							return (_gif == Media::Clip::BadReader) ? nullptr : _gif;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void setStatusSize(int32 newSize) const;
 | 
				
			||||||
 | 
						void updateStatusText() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HistorySticker : public HistoryMedia {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						HistorySticker(HistoryItem *parent, DocumentData *document);
 | 
				
			||||||
 | 
						HistoryMediaType type() const override {
 | 
				
			||||||
 | 
							return MediaTypeSticker;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						HistorySticker *clone(HistoryItem *newParent) const override {
 | 
				
			||||||
 | 
							return new HistorySticker(newParent, _data);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initDimensions() override;
 | 
				
			||||||
 | 
						int resizeGetHeight(int width) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
				
			||||||
 | 
						HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dragItem() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QString notificationText() const override;
 | 
				
			||||||
 | 
						TextWithEntities selectedText(TextSelection selection) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DocumentData *getDocument() override {
 | 
				
			||||||
 | 
							return _data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void attachToParent() override;
 | 
				
			||||||
 | 
						void detachFromParent() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void updateSentMedia(const MTPMessageMedia &media) override;
 | 
				
			||||||
 | 
						bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool needsBubble() const override {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool customInfoLayout() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						int additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const;
 | 
				
			||||||
 | 
						int additionalWidth() const {
 | 
				
			||||||
 | 
							return additionalWidth(_parent->Get<HistoryMessageVia>(), _parent->Get<HistoryMessageReply>());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						QString toString() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int16 _pixw, _pixh;
 | 
				
			||||||
 | 
						ClickHandlerPtr _packLink;
 | 
				
			||||||
 | 
						DocumentData *_data;
 | 
				
			||||||
 | 
						QString _emoji;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HistoryContact : public HistoryMedia {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						HistoryContact(HistoryItem *parent, int32 userId, const QString &first, const QString &last, const QString &phone);
 | 
				
			||||||
 | 
						HistoryMediaType type() const override {
 | 
				
			||||||
 | 
							return MediaTypeContact;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						HistoryContact *clone(HistoryItem *newParent) const override {
 | 
				
			||||||
 | 
							return new HistoryContact(newParent, _userId, _fname, _lname, _phone);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initDimensions() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
				
			||||||
 | 
						HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QString notificationText() const override;
 | 
				
			||||||
 | 
						TextWithEntities selectedText(TextSelection selection) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void attachToParent() override;
 | 
				
			||||||
 | 
						void detachFromParent() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void updateSentMedia(const MTPMessageMedia &media) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool needsBubble() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool customInfoLayout() const override {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const QString &fname() const {
 | 
				
			||||||
 | 
							return _fname;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						const QString &lname() const {
 | 
				
			||||||
 | 
							return _lname;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						const QString &phone() const {
 | 
				
			||||||
 | 
							return _phone;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int32 _userId;
 | 
				
			||||||
 | 
						UserData *_contact;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int32 _phonew;
 | 
				
			||||||
 | 
						QString _fname, _lname, _phone;
 | 
				
			||||||
 | 
						Text _name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ClickHandlerPtr _linkl;
 | 
				
			||||||
 | 
						int32 _linkw;
 | 
				
			||||||
 | 
						QString _link;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HistoryWebPage : public HistoryMedia {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						HistoryWebPage(HistoryItem *parent, WebPageData *data);
 | 
				
			||||||
 | 
						HistoryWebPage(HistoryItem *parent, const HistoryWebPage &other);
 | 
				
			||||||
 | 
						HistoryMediaType type() const override {
 | 
				
			||||||
 | 
							return MediaTypeWebPage;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						HistoryWebPage *clone(HistoryItem *newParent) const override {
 | 
				
			||||||
 | 
							return new HistoryWebPage(newParent, *this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initDimensions() override;
 | 
				
			||||||
 | 
						int resizeGetHeight(int width) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
				
			||||||
 | 
						HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
 | 
				
			||||||
 | 
						bool hasTextForCopy() const override {
 | 
				
			||||||
 | 
							return false; // we do not add _title and _description in FullSelection text copy.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return _attach && _attach->toggleSelectionByHandlerClick(p);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return _attach && _attach->dragItemByHandler(p);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextWithEntities selectedText(TextSelection selection) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
 | 
				
			||||||
 | 
						void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool isDisplayed() const override {
 | 
				
			||||||
 | 
							return !_data->pendingTill;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						DocumentData *getDocument() override {
 | 
				
			||||||
 | 
							return _attach ? _attach->getDocument() : 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Media::Clip::Reader *getClipReader() override {
 | 
				
			||||||
 | 
							return _attach ? _attach->getClipReader() : 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool playInline(bool autoplay) override {
 | 
				
			||||||
 | 
							return _attach ? _attach->playInline(autoplay) : false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void stopInline() override {
 | 
				
			||||||
 | 
							if (_attach) _attach->stopInline();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void attachToParent() override;
 | 
				
			||||||
 | 
						void detachFromParent() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool hasReplyPreview() const override {
 | 
				
			||||||
 | 
							return (_data->photo && !_data->photo->thumb->isNull()) || (_data->document && !_data->document->thumb->isNull());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ImagePtr replyPreview() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						WebPageData *webpage() {
 | 
				
			||||||
 | 
							return _data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool needsBubble() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool customInfoLayout() const override {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						HistoryMedia *attach() const {
 | 
				
			||||||
 | 
							return _attach.get();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						TextSelection toDescriptionSelection(TextSelection selection) const {
 | 
				
			||||||
 | 
							return internal::unshiftSelection(selection, _title);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						TextSelection fromDescriptionSelection(TextSelection selection) const {
 | 
				
			||||||
 | 
							return internal::shiftSelection(selection, _title);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						QMargins inBubblePadding() const;
 | 
				
			||||||
 | 
						int bottomInfoPadding() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						WebPageData *_data;
 | 
				
			||||||
 | 
						ClickHandlerPtr _openl;
 | 
				
			||||||
 | 
						std_::unique_ptr<HistoryMedia> _attach;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool _asArticle = false;
 | 
				
			||||||
 | 
						int32 _titleLines, _descriptionLines;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Text _title, _description;
 | 
				
			||||||
 | 
						int32 _siteNameWidth = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QString _duration;
 | 
				
			||||||
 | 
						int32 _durationWidth = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int16 _pixw = 0;
 | 
				
			||||||
 | 
						int16 _pixh = 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HistoryGame : public HistoryMedia {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						HistoryGame(HistoryItem *parent, GameData *data);
 | 
				
			||||||
 | 
						HistoryGame(HistoryItem *parent, const HistoryGame &other);
 | 
				
			||||||
 | 
						HistoryMediaType type() const override {
 | 
				
			||||||
 | 
							return MediaTypeGame;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						HistoryGame *clone(HistoryItem *newParent) const override {
 | 
				
			||||||
 | 
							return new HistoryGame(newParent, *this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initDimensions() override;
 | 
				
			||||||
 | 
						int resizeGetHeight(int width) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
				
			||||||
 | 
						HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
 | 
				
			||||||
 | 
						bool isAboveMessage() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool hasTextForCopy() const override {
 | 
				
			||||||
 | 
							return false; // we do not add _title and _description in FullSelection text copy.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return _attach && _attach->toggleSelectionByHandlerClick(p);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return _attach && _attach->dragItemByHandler(p);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextWithEntities selectedText(TextSelection selection) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
 | 
				
			||||||
 | 
						void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DocumentData *getDocument() override {
 | 
				
			||||||
 | 
							return _attach ? _attach->getDocument() : nullptr;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Media::Clip::Reader *getClipReader() override {
 | 
				
			||||||
 | 
							return _attach ? _attach->getClipReader() : nullptr;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool playInline(bool autoplay) override {
 | 
				
			||||||
 | 
							return _attach ? _attach->playInline(autoplay) : false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void stopInline() override {
 | 
				
			||||||
 | 
							if (_attach) _attach->stopInline();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void attachToParent() override;
 | 
				
			||||||
 | 
						void detachFromParent() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool hasReplyPreview() const override {
 | 
				
			||||||
 | 
							return (_data->photo && !_data->photo->thumb->isNull()) || (_data->document && !_data->document->thumb->isNull());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ImagePtr replyPreview() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GameData *game() {
 | 
				
			||||||
 | 
							return _data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool needsBubble() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool customInfoLayout() const override {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						HistoryMedia *attach() const {
 | 
				
			||||||
 | 
							return _attach.get();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						TextSelection toDescriptionSelection(TextSelection selection) const {
 | 
				
			||||||
 | 
							return internal::unshiftSelection(selection, _title);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						TextSelection fromDescriptionSelection(TextSelection selection) const {
 | 
				
			||||||
 | 
							return internal::shiftSelection(selection, _title);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						QMargins inBubblePadding() const;
 | 
				
			||||||
 | 
						int bottomInfoPadding() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GameData *_data;
 | 
				
			||||||
 | 
						ClickHandlerPtr _openl;
 | 
				
			||||||
 | 
						std_::unique_ptr<HistoryMedia> _attach;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int32 _titleLines, _descriptionLines;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Text _title, _description;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct LocationCoords;
 | 
				
			||||||
 | 
					struct LocationData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HistoryLocation : public HistoryMedia {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						HistoryLocation(HistoryItem *parent, const LocationCoords &coords, const QString &title = QString(), const QString &description = QString());
 | 
				
			||||||
 | 
						HistoryLocation(HistoryItem *parent, const HistoryLocation &other);
 | 
				
			||||||
 | 
						HistoryMediaType type() const override {
 | 
				
			||||||
 | 
							return MediaTypeLocation;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						HistoryLocation *clone(HistoryItem *newParent) const override {
 | 
				
			||||||
 | 
							return new HistoryLocation(newParent, *this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initDimensions() override;
 | 
				
			||||||
 | 
						int resizeGetHeight(int32 width) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
 | 
				
			||||||
 | 
						HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
 | 
				
			||||||
 | 
						bool hasTextForCopy() const override {
 | 
				
			||||||
 | 
							return !_title.isEmpty() || !_description.isEmpty();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return p == _link;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool dragItemByHandler(const ClickHandlerPtr &p) const override {
 | 
				
			||||||
 | 
							return p == _link;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QString notificationText() const override;
 | 
				
			||||||
 | 
						QString inDialogsText() const override;
 | 
				
			||||||
 | 
						TextWithEntities selectedText(TextSelection selection) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool needsBubble() const override {
 | 
				
			||||||
 | 
							if (!_title.isEmpty() || !_description.isEmpty()) {
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (_parent->viaBot()) {
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool customInfoLayout() const override {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						TextSelection toDescriptionSelection(TextSelection selection) const {
 | 
				
			||||||
 | 
							return internal::unshiftSelection(selection, _title);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						TextSelection fromDescriptionSelection(TextSelection selection) const {
 | 
				
			||||||
 | 
							return internal::shiftSelection(selection, _title);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LocationData *_data;
 | 
				
			||||||
 | 
						Text _title, _description;
 | 
				
			||||||
 | 
						ClickHandlerPtr _link;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int32 fullWidth() const;
 | 
				
			||||||
 | 
						int32 fullHeight() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SendMessageClickHandler : public PeerClickHandler {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						using PeerClickHandler::PeerClickHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						void onClickImpl() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddContactClickHandler : public MessageClickHandler {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						using MessageClickHandler::MessageClickHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						void onClickImpl() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "apiwrap.h"
 | 
					#include "apiwrap.h"
 | 
				
			||||||
#include "history/history_location_manager.h"
 | 
					#include "history/history_location_manager.h"
 | 
				
			||||||
#include "history/history_service_layout.h"
 | 
					#include "history/history_service_layout.h"
 | 
				
			||||||
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
#include "styles/style_dialogs.h"
 | 
					#include "styles/style_dialogs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
| 
						 | 
					@ -476,6 +477,39 @@ void HistoryMessage::createComponentsHelper(MTPDmessage::Flags flags, MsgId repl
 | 
				
			||||||
	createComponents(config);
 | 
						createComponents(config);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void HistoryMessage::updateMediaInBubbleState() {
 | 
				
			||||||
 | 
						if (!_media) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!drawBubble()) {
 | 
				
			||||||
 | 
							_media->setInBubbleState(MediaInBubbleState::None);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool hasSomethingAbove = displayFromName() || displayForwardedFrom() || Has<HistoryMessageVia>();
 | 
				
			||||||
 | 
						bool hasSomethingBelow = false;
 | 
				
			||||||
 | 
						if (!emptyText()) {
 | 
				
			||||||
 | 
							if (_media->isAboveMessage()) {
 | 
				
			||||||
 | 
								hasSomethingBelow = true;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								hasSomethingAbove = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						auto computeState = [hasSomethingAbove, hasSomethingBelow] {
 | 
				
			||||||
 | 
							if (hasSomethingAbove) {
 | 
				
			||||||
 | 
								if (hasSomethingBelow) {
 | 
				
			||||||
 | 
									return MediaInBubbleState::Middle;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return MediaInBubbleState::Bottom;
 | 
				
			||||||
 | 
							} else if (hasSomethingBelow) {
 | 
				
			||||||
 | 
								return MediaInBubbleState::Top;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return MediaInBubbleState::None;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						_media->setInBubbleState(computeState());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool HistoryMessage::displayEditedBadge(bool hasViaBot) const {
 | 
					bool HistoryMessage::displayEditedBadge(bool hasViaBot) const {
 | 
				
			||||||
	if (!(_flags & MTPDmessage::Flag::f_edit_date)) {
 | 
						if (!(_flags & MTPDmessage::Flag::f_edit_date)) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
| 
						 | 
					@ -658,6 +692,8 @@ void HistoryMessage::initDimensions() {
 | 
				
			||||||
	if (reply) {
 | 
						if (reply) {
 | 
				
			||||||
		reply->updateName();
 | 
							reply->updateName();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						updateMediaInBubbleState();
 | 
				
			||||||
	if (drawBubble()) {
 | 
						if (drawBubble()) {
 | 
				
			||||||
		auto fwd = Get<HistoryMessageForwarded>();
 | 
							auto fwd = Get<HistoryMessageForwarded>();
 | 
				
			||||||
		auto via = Get<HistoryMessageVia>();
 | 
							auto via = Get<HistoryMessageVia>();
 | 
				
			||||||
| 
						 | 
					@ -665,9 +701,11 @@ void HistoryMessage::initDimensions() {
 | 
				
			||||||
			fwd->create(via);
 | 
								fwd->create(via);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto mediaDisplayed = false;
 | 
				
			||||||
		if (_media) {
 | 
							if (_media) {
 | 
				
			||||||
 | 
								mediaDisplayed = _media->isDisplayed();
 | 
				
			||||||
			_media->initDimensions();
 | 
								_media->initDimensions();
 | 
				
			||||||
			if (_media->isDisplayed() && !_media->isAboveMessage()) {
 | 
								if (mediaDisplayed && _media->isBubbleBottom()) {
 | 
				
			||||||
				if (_text.hasSkipBlock()) {
 | 
									if (_text.hasSkipBlock()) {
 | 
				
			||||||
					_text.removeSkipBlock();
 | 
										_text.removeSkipBlock();
 | 
				
			||||||
					_textWidth = -1;
 | 
										_textWidth = -1;
 | 
				
			||||||
| 
						 | 
					@ -681,19 +719,21 @@ void HistoryMessage::initDimensions() {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_maxw = plainMaxWidth();
 | 
							_maxw = plainMaxWidth();
 | 
				
			||||||
		if (emptyText()) {
 | 
							_minh = emptyText() ? 0 : _text.minHeight();
 | 
				
			||||||
			_minh = 0;
 | 
							if (mediaDisplayed) {
 | 
				
			||||||
		} else {
 | 
								if (!_media->isBubbleTop()) {
 | 
				
			||||||
			_minh = st::msgPadding.top() + _text.minHeight() + st::msgPadding.bottom();
 | 
									_minh += st::msgPadding.top() + st::mediaInBubbleSkip;
 | 
				
			||||||
		}
 | 
								}
 | 
				
			||||||
		if (_media && _media->isDisplayed()) {
 | 
								if (!_media->isBubbleBottom()) {
 | 
				
			||||||
			int32 maxw = _media->maxWidth();
 | 
									_minh += st::msgPadding.bottom() + st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								auto maxw = _media->maxWidth();
 | 
				
			||||||
			if (maxw > _maxw) _maxw = maxw;
 | 
								if (maxw > _maxw) _maxw = maxw;
 | 
				
			||||||
			_minh += _media->minHeight();
 | 
								_minh += _media->minHeight();
 | 
				
			||||||
		}
 | 
							} else {
 | 
				
			||||||
		if (!_media) {
 | 
								_minh += st::msgPadding.top() + st::msgPadding.bottom();
 | 
				
			||||||
			if (displayFromName()) {
 | 
								if (displayFromName()) {
 | 
				
			||||||
				int32 namew = st::msgPadding.left() + author()->nameText.maxWidth() + st::msgPadding.right();
 | 
									auto namew = st::msgPadding.left() + author()->nameText.maxWidth() + st::msgPadding.right();
 | 
				
			||||||
				if (via && !fwd) {
 | 
									if (via && !fwd) {
 | 
				
			||||||
					namew += st::msgServiceFont->spacew + via->_maxWidth;
 | 
										namew += st::msgServiceFont->spacew + via->_maxWidth;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
| 
						 | 
					@ -704,7 +744,7 @@ void HistoryMessage::initDimensions() {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (fwd) {
 | 
								if (fwd) {
 | 
				
			||||||
				int32 _namew = st::msgPadding.left() + fwd->_text.maxWidth() + st::msgPadding.right();
 | 
									auto _namew = st::msgPadding.left() + fwd->_text.maxWidth() + st::msgPadding.right();
 | 
				
			||||||
				if (via) {
 | 
									if (via) {
 | 
				
			||||||
					_namew += st::msgServiceFont->spacew + via->_maxWidth;
 | 
										_namew += st::msgServiceFont->spacew + via->_maxWidth;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
| 
						 | 
					@ -1029,19 +1069,19 @@ void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, int32 width
 | 
				
			||||||
	int32 infoRight = right, infoBottom = bottom;
 | 
						int32 infoRight = right, infoBottom = bottom;
 | 
				
			||||||
	switch (type) {
 | 
						switch (type) {
 | 
				
			||||||
	case InfoDisplayDefault:
 | 
						case InfoDisplayDefault:
 | 
				
			||||||
	infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
 | 
							infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
 | 
				
			||||||
	infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
 | 
							infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
 | 
				
			||||||
	p.setPen(selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
 | 
							p.setPen(selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
 | 
				
			||||||
	break;
 | 
						break;
 | 
				
			||||||
	case InfoDisplayOverImage:
 | 
						case InfoDisplayOverImage:
 | 
				
			||||||
	infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
 | 
							infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
 | 
				
			||||||
	infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
 | 
							infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
 | 
				
			||||||
	p.setPen(st::msgDateImgColor);
 | 
							p.setPen(st::msgDateImgColor);
 | 
				
			||||||
	break;
 | 
						break;
 | 
				
			||||||
	case InfoDisplayOverBackground:
 | 
						case InfoDisplayOverBackground:
 | 
				
			||||||
	infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
 | 
							infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
 | 
				
			||||||
	infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
 | 
							infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
 | 
				
			||||||
	p.setPen(st::msgServiceColor);
 | 
							p.setPen(st::msgServiceColor);
 | 
				
			||||||
	break;
 | 
						break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1152,9 +1192,6 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u
 | 
				
			||||||
	int dateh = 0, unreadbarh = 0;
 | 
						int dateh = 0, unreadbarh = 0;
 | 
				
			||||||
	if (auto date = Get<HistoryMessageDate>()) {
 | 
						if (auto date = Get<HistoryMessageDate>()) {
 | 
				
			||||||
		dateh = date->height();
 | 
							dateh = date->height();
 | 
				
			||||||
		//if (r.intersects(QRect(0, 0, _history->width, dateh))) {
 | 
					 | 
				
			||||||
		//	date->paint(p, 0, _history->width);
 | 
					 | 
				
			||||||
		//}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (auto unreadbar = Get<HistoryMessageUnreadBar>()) {
 | 
						if (auto unreadbar = Get<HistoryMessageUnreadBar>()) {
 | 
				
			||||||
		unreadbarh = unreadbar->height();
 | 
							unreadbarh = unreadbar->height();
 | 
				
			||||||
| 
						 | 
					@ -1197,7 +1234,8 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u
 | 
				
			||||||
			fromNameUpdated(width);
 | 
								fromNameUpdated(width);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		int32 top = marginTop();
 | 
							auto mediaDisplayed = _media && _media->isDisplayed();
 | 
				
			||||||
 | 
							auto top = marginTop();
 | 
				
			||||||
		QRect r(left, top, width, height - top - marginBottom());
 | 
							QRect r(left, top, width, height - top - marginBottom());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		style::color bg(selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg));
 | 
							style::color bg(selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg));
 | 
				
			||||||
| 
						 | 
					@ -1206,17 +1244,23 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u
 | 
				
			||||||
		App::roundRect(p, r, bg, cors, &sh);
 | 
							App::roundRect(p, r, bg, cors, &sh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		QRect trect(r.marginsAdded(-st::msgPadding));
 | 
							QRect trect(r.marginsAdded(-st::msgPadding));
 | 
				
			||||||
		paintFromName(p, trect, selected);
 | 
							if (mediaDisplayed && _media->isBubbleTop()) {
 | 
				
			||||||
		paintForwardedInfo(p, trect, selected);
 | 
								trect.setY(trect.y() - st::msgPadding.top());
 | 
				
			||||||
		paintReplyInfo(p, trect, selected);
 | 
							} else {
 | 
				
			||||||
		paintViaBotIdInfo(p, trect, selected);
 | 
								paintFromName(p, trect, selected);
 | 
				
			||||||
 | 
								paintForwardedInfo(p, trect, selected);
 | 
				
			||||||
 | 
								paintReplyInfo(p, trect, selected);
 | 
				
			||||||
 | 
								paintViaBotIdInfo(p, trect, selected);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (mediaDisplayed && _media->isBubbleBottom()) {
 | 
				
			||||||
 | 
								trect.setHeight(trect.height() + st::msgPadding.bottom());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		auto needDrawInfo = true;
 | 
							auto needDrawInfo = true;
 | 
				
			||||||
		if (_media && _media->isDisplayed()) {
 | 
							if (mediaDisplayed) {
 | 
				
			||||||
			auto mediaAboveText = _media->isAboveMessage();
 | 
								auto mediaAboveText = _media->isAboveMessage();
 | 
				
			||||||
			auto mediaHeight = _media->height();
 | 
								auto mediaHeight = _media->height();
 | 
				
			||||||
			auto mediaLeft = trect.x() - st::msgPadding.left();
 | 
								auto mediaLeft = trect.x() - st::msgPadding.left();
 | 
				
			||||||
			auto mediaTop = mediaAboveText ? (trect.y() - st::msgPadding.top()) : (r.y() + r.height() - mediaHeight);
 | 
								auto mediaTop = mediaAboveText ? trect.y() : (trect.y() + trect.height() - mediaHeight);
 | 
				
			||||||
			if (!mediaAboveText) {
 | 
								if (!mediaAboveText) {
 | 
				
			||||||
				paintText(p, trect, selection);
 | 
									paintText(p, trect, selection);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -1361,46 +1405,50 @@ int HistoryMessage::performResizeGetHeight(int width) {
 | 
				
			||||||
		auto reply = Get<HistoryMessageReply>();
 | 
							auto reply = Get<HistoryMessageReply>();
 | 
				
			||||||
		auto via = Get<HistoryMessageVia>();
 | 
							auto via = Get<HistoryMessageVia>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bool media = (_media && _media->isDisplayed());
 | 
							auto mediaDisplayed = false;
 | 
				
			||||||
 | 
							auto mediaInBubbleState = MediaInBubbleState::None;
 | 
				
			||||||
 | 
							if (_media) {
 | 
				
			||||||
 | 
								mediaDisplayed = _media->isDisplayed();
 | 
				
			||||||
 | 
								mediaInBubbleState = _media->inBubbleState();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if (width >= _maxw) {
 | 
							if (width >= _maxw) {
 | 
				
			||||||
			_height = _minh;
 | 
								_height = _minh;
 | 
				
			||||||
			if (media) _media->resizeGetHeight(_maxw);
 | 
								if (mediaDisplayed) _media->resizeGetHeight(_maxw);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			if (emptyText()) {
 | 
								if (emptyText()) {
 | 
				
			||||||
				_height = 0;
 | 
									_height = 0;
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				int32 textWidth = qMax(width - st::msgPadding.left() - st::msgPadding.right(), 1);
 | 
									auto textWidth = qMax(width - st::msgPadding.left() - st::msgPadding.right(), 1);
 | 
				
			||||||
				if (textWidth != _textWidth) {
 | 
									if (textWidth != _textWidth) {
 | 
				
			||||||
					textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle));
 | 
										textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle));
 | 
				
			||||||
					_textWidth = textWidth;
 | 
										_textWidth = textWidth;
 | 
				
			||||||
					_textHeight = _text.countHeight(textWidth);
 | 
										_textHeight = _text.countHeight(textWidth);
 | 
				
			||||||
					textstyleRestore();
 | 
										textstyleRestore();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				_height = st::msgPadding.top() + _textHeight + st::msgPadding.bottom();
 | 
									_height = _textHeight;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (mediaDisplayed) {
 | 
				
			||||||
 | 
									if (!_media->isBubbleTop()) {
 | 
				
			||||||
 | 
										_height += st::msgPadding.top() + st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (!_media->isBubbleBottom()) {
 | 
				
			||||||
 | 
										_height += st::msgPadding.bottom() + st::mediaInBubbleSkip;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									_height += _media->resizeGetHeight(width);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									_height += st::msgPadding.top() + st::msgPadding.bottom();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (media) _height += _media->resizeGetHeight(width);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		auto mediaTopPaddingAdded = !emptyText();
 | 
					 | 
				
			||||||
		if (displayFromName()) {
 | 
							if (displayFromName()) {
 | 
				
			||||||
			int32 l = 0, w = 0;
 | 
								int32 l = 0, w = 0;
 | 
				
			||||||
			countPositionAndSize(l, w);
 | 
								countPositionAndSize(l, w);
 | 
				
			||||||
			fromNameUpdated(w);
 | 
								fromNameUpdated(w);
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!mediaTopPaddingAdded) {
 | 
					 | 
				
			||||||
				_height += st::msgPadding.top() + st::mediaHeaderSkip;
 | 
					 | 
				
			||||||
				mediaTopPaddingAdded = true;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			_height += st::msgNameFont->height;
 | 
								_height += st::msgNameFont->height;
 | 
				
			||||||
		} else if (via && !fwd) {
 | 
							} else if (via && !fwd) {
 | 
				
			||||||
			int32 l = 0, w = 0;
 | 
								int32 l = 0, w = 0;
 | 
				
			||||||
			countPositionAndSize(l, w);
 | 
								countPositionAndSize(l, w);
 | 
				
			||||||
			via->resize(w - st::msgPadding.left() - st::msgPadding.right());
 | 
								via->resize(w - st::msgPadding.left() - st::msgPadding.right());
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!mediaTopPaddingAdded) {
 | 
					 | 
				
			||||||
				_height += st::msgPadding.top() + st::mediaHeaderSkip;
 | 
					 | 
				
			||||||
				mediaTopPaddingAdded = true;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			_height += st::msgNameFont->height;
 | 
								_height += st::msgNameFont->height;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1408,11 +1456,6 @@ int HistoryMessage::performResizeGetHeight(int width) {
 | 
				
			||||||
			int32 l = 0, w = 0;
 | 
								int32 l = 0, w = 0;
 | 
				
			||||||
			countPositionAndSize(l, w);
 | 
								countPositionAndSize(l, w);
 | 
				
			||||||
			int32 fwdheight = ((fwd->_text.maxWidth() > (w - st::msgPadding.left() - st::msgPadding.right())) ? 2 : 1) * st::semiboldFont->height;
 | 
								int32 fwdheight = ((fwd->_text.maxWidth() > (w - st::msgPadding.left() - st::msgPadding.right())) ? 2 : 1) * st::semiboldFont->height;
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!mediaTopPaddingAdded) {
 | 
					 | 
				
			||||||
				_height += st::msgPadding.top() + st::mediaHeaderSkip;
 | 
					 | 
				
			||||||
				mediaTopPaddingAdded = true;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			_height += fwdheight;
 | 
								_height += fwdheight;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1420,11 +1463,6 @@ int HistoryMessage::performResizeGetHeight(int width) {
 | 
				
			||||||
			int32 l = 0, w = 0;
 | 
								int32 l = 0, w = 0;
 | 
				
			||||||
			countPositionAndSize(l, w);
 | 
								countPositionAndSize(l, w);
 | 
				
			||||||
			reply->resize(w - st::msgPadding.left() - st::msgPadding.right());
 | 
								reply->resize(w - st::msgPadding.left() - st::msgPadding.right());
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!mediaTopPaddingAdded) {
 | 
					 | 
				
			||||||
				_height += st::msgPadding.top() + st::mediaHeaderSkip;
 | 
					 | 
				
			||||||
				mediaTopPaddingAdded = true;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			_height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
 | 
								_height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else if (_media) {
 | 
						} else if (_media) {
 | 
				
			||||||
| 
						 | 
					@ -1465,12 +1503,12 @@ bool HistoryMessage::pointInTime(int32 right, int32 bottom, int x, int y, InfoDi
 | 
				
			||||||
	int32 infoRight = right, infoBottom = bottom;
 | 
						int32 infoRight = right, infoBottom = bottom;
 | 
				
			||||||
	switch (type) {
 | 
						switch (type) {
 | 
				
			||||||
	case InfoDisplayDefault:
 | 
						case InfoDisplayDefault:
 | 
				
			||||||
	infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
 | 
							infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
 | 
				
			||||||
	infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
 | 
							infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
 | 
				
			||||||
	break;
 | 
						break;
 | 
				
			||||||
	case InfoDisplayOverImage:
 | 
						case InfoDisplayOverImage:
 | 
				
			||||||
	infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
 | 
							infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
 | 
				
			||||||
	infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
 | 
							infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
 | 
				
			||||||
	break;
 | 
						break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	int32 dateX = infoRight - HistoryMessage::infoWidth() + HistoryMessage::timeLeft();
 | 
						int32 dateX = infoRight - HistoryMessage::infoWidth() + HistoryMessage::timeLeft();
 | 
				
			||||||
| 
						 | 
					@ -1493,85 +1531,47 @@ HistoryTextState HistoryMessage::getState(int x, int y, HistoryStateRequest requ
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (drawBubble()) {
 | 
						if (drawBubble()) {
 | 
				
			||||||
		auto fwd = Get<HistoryMessageForwarded>();
 | 
							auto mediaDisplayed = _media && _media->isDisplayed();
 | 
				
			||||||
		auto via = Get<HistoryMessageVia>();
 | 
							auto top = marginTop();
 | 
				
			||||||
		auto reply = Get<HistoryMessageReply>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		int top = marginTop();
 | 
					 | 
				
			||||||
		QRect r(left, top, width, height - top - marginBottom());
 | 
							QRect r(left, top, width, height - top - marginBottom());
 | 
				
			||||||
		QRect trect(r.marginsAdded(-st::msgPadding));
 | 
							QRect trect(r.marginsAdded(-st::msgPadding));
 | 
				
			||||||
		if (displayFromName()) {
 | 
							if (mediaDisplayed && _media->isBubbleTop()) {
 | 
				
			||||||
			if (y >= trect.top() && y < trect.top() + st::msgNameFont->height) {
 | 
								trect.setY(trect.y() - st::msgPadding.top());
 | 
				
			||||||
				if (x >= trect.left() && x < trect.left() + trect.width() && x < trect.left() + author()->nameText.maxWidth()) {
 | 
							} else {
 | 
				
			||||||
					result.link = author()->openLink();
 | 
								if (getStateFromName(x, y, trect, &result)) return result;
 | 
				
			||||||
					return result;
 | 
								if (getStateForwardedInfo(x, y, trect, &result, request)) return result;
 | 
				
			||||||
				}
 | 
								if (getStateReplyInfo(x, y, trect, &result)) return result;
 | 
				
			||||||
				if (via && !fwd && x >= trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew && x < trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew + via->_width) {
 | 
								if (getStateViaBotIdInfo(x, y, trect, &result)) return result;
 | 
				
			||||||
					result.link = via->_lnk;
 | 
					 | 
				
			||||||
					return result;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			trect.setTop(trect.top() + st::msgNameFont->height);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (displayForwardedFrom()) {
 | 
							if (mediaDisplayed && _media->isBubbleBottom()) {
 | 
				
			||||||
			int32 fwdheight = ((fwd->_text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height;
 | 
								trect.setHeight(trect.height() + st::msgPadding.bottom());
 | 
				
			||||||
			if (y >= trect.top() && y < trect.top() + fwdheight) {
 | 
					 | 
				
			||||||
				bool breakEverywhere = (fwd->_text.countHeight(trect.width()) > 2 * st::semiboldFont->height);
 | 
					 | 
				
			||||||
				auto textRequest = request.forText();
 | 
					 | 
				
			||||||
				if (breakEverywhere) {
 | 
					 | 
				
			||||||
					textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				textstyleSet(&st::inFwdTextStyle);
 | 
					 | 
				
			||||||
				result = fwd->_text.getState(x - trect.left(), y - trect.top(), trect.width(), textRequest);
 | 
					 | 
				
			||||||
				textstyleRestore();
 | 
					 | 
				
			||||||
				result.symbol = 0;
 | 
					 | 
				
			||||||
				result.afterSymbol = false;
 | 
					 | 
				
			||||||
				if (breakEverywhere) {
 | 
					 | 
				
			||||||
					result.cursor = HistoryInForwardedCursorState;
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					result.cursor = HistoryDefaultCursorState;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				return result;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			trect.setTop(trect.top() + fwdheight);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (reply) {
 | 
					 | 
				
			||||||
			int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
 | 
					 | 
				
			||||||
			if (y >= trect.top() && y < trect.top() + h) {
 | 
					 | 
				
			||||||
				if (reply->replyToMsg && y >= trect.top() + st::msgReplyPadding.top() && y < trect.top() + st::msgReplyPadding.top() + st::msgReplyBarSize.height() && x >= trect.left() && x < trect.left() + trect.width()) {
 | 
					 | 
				
			||||||
					result.link = reply->replyToLink();
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				return result;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			trect.setTop(trect.top() + h);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (via && !displayFromName() && !displayForwardedFrom()) {
 | 
					 | 
				
			||||||
			if (x >= trect.left() && y >= trect.top() && y < trect.top() + st::msgNameFont->height && x < trect.left() + via->_width) {
 | 
					 | 
				
			||||||
				result.link = via->_lnk;
 | 
					 | 
				
			||||||
				return result;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			trect.setTop(trect.top() + st::msgNameFont->height);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		bool inDate = false, mediaDisplayed = _media && _media->isDisplayed();
 | 
					 | 
				
			||||||
		if (!mediaDisplayed || !_media->customInfoLayout()) {
 | 
					 | 
				
			||||||
			inDate = HistoryMessage::pointInTime(r.x() + r.width(), r.y() + r.height(), x, y, InfoDisplayDefault);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto needDateCheck = true;
 | 
				
			||||||
		if (mediaDisplayed) {
 | 
							if (mediaDisplayed) {
 | 
				
			||||||
			trect.setBottom(trect.bottom() - _media->height());
 | 
								auto mediaAboveText = _media->isAboveMessage();
 | 
				
			||||||
			if (y >= r.bottom() - _media->height()) {
 | 
								auto mediaHeight = _media->height();
 | 
				
			||||||
				result = _media->getState(x - r.left(), y - (r.bottom() - _media->height()), request);
 | 
								auto mediaLeft = trect.x() - st::msgPadding.left();
 | 
				
			||||||
 | 
								auto mediaTop = mediaAboveText ? trect.y() : (trect.y() + trect.height() - mediaHeight);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (y >= mediaTop && y < mediaTop + mediaHeight) {
 | 
				
			||||||
 | 
									result = _media->getState(x - mediaLeft, y - mediaTop, request);
 | 
				
			||||||
				result.symbol += _text.length();
 | 
									result.symbol += _text.length();
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if (mediaAboveText) {
 | 
				
			||||||
 | 
										trect.setY(trect.y() + mediaHeight);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									getStateText(x, y, trect, &result, request);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								needDateCheck = !_media->customInfoLayout();
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								getStateText(x, y, trect, &result, request);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!mediaDisplayed || (y < r.bottom() - _media->height())) {
 | 
							if (needDateCheck) {
 | 
				
			||||||
			textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle));
 | 
								if (HistoryMessage::pointInTime(r.x() + r.width(), r.y() + r.height(), x, y, InfoDisplayDefault)) {
 | 
				
			||||||
			result = _text.getState(x - trect.x(), y - trect.y(), trect.width(), request.forText());
 | 
									result.cursor = HistoryInDateCursorState;
 | 
				
			||||||
			textstyleRestore();
 | 
								}
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (inDate) {
 | 
					 | 
				
			||||||
			result.cursor = HistoryInDateCursorState;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else if (_media) {
 | 
						} else if (_media) {
 | 
				
			||||||
		result = _media->getState(x - left, y - marginTop(), request);
 | 
							result = _media->getState(x - left, y - marginTop(), request);
 | 
				
			||||||
| 
						 | 
					@ -1589,6 +1589,89 @@ HistoryTextState HistoryMessage::getState(int x, int y, HistoryStateRequest requ
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool HistoryMessage::getStateFromName(int x, int y, QRect &trect, HistoryTextState *outResult) const {
 | 
				
			||||||
 | 
						if (displayFromName()) {
 | 
				
			||||||
 | 
							if (y >= trect.top() && y < trect.top() + st::msgNameFont->height) {
 | 
				
			||||||
 | 
								if (x >= trect.left() && x < trect.left() + trect.width() && x < trect.left() + author()->nameText.maxWidth()) {
 | 
				
			||||||
 | 
									outResult->link = author()->openLink();
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								auto fwd = Get<HistoryMessageForwarded>();
 | 
				
			||||||
 | 
								auto via = Get<HistoryMessageVia>();
 | 
				
			||||||
 | 
								if (via && !fwd && x >= trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew && x < trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew + via->_width) {
 | 
				
			||||||
 | 
									outResult->link = via->_lnk;
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							trect.setTop(trect.top() + st::msgNameFont->height);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool HistoryMessage::getStateForwardedInfo(int x, int y, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const {
 | 
				
			||||||
 | 
						if (displayForwardedFrom()) {
 | 
				
			||||||
 | 
							auto fwd = Get<HistoryMessageForwarded>();
 | 
				
			||||||
 | 
							int32 fwdheight = ((fwd->_text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height;
 | 
				
			||||||
 | 
							if (y >= trect.top() && y < trect.top() + fwdheight) {
 | 
				
			||||||
 | 
								bool breakEverywhere = (fwd->_text.countHeight(trect.width()) > 2 * st::semiboldFont->height);
 | 
				
			||||||
 | 
								auto textRequest = request.forText();
 | 
				
			||||||
 | 
								if (breakEverywhere) {
 | 
				
			||||||
 | 
									textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								textstyleSet(&st::inFwdTextStyle);
 | 
				
			||||||
 | 
								*outResult = fwd->_text.getState(x - trect.left(), y - trect.top(), trect.width(), textRequest);
 | 
				
			||||||
 | 
								textstyleRestore();
 | 
				
			||||||
 | 
								outResult->symbol = 0;
 | 
				
			||||||
 | 
								outResult->afterSymbol = false;
 | 
				
			||||||
 | 
								if (breakEverywhere) {
 | 
				
			||||||
 | 
									outResult->cursor = HistoryInForwardedCursorState;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									outResult->cursor = HistoryDefaultCursorState;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							trect.setTop(trect.top() + fwdheight);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool HistoryMessage::getStateReplyInfo(int x, int y, QRect &trect, HistoryTextState *outResult) const {
 | 
				
			||||||
 | 
						if (auto reply = Get<HistoryMessageReply>()) {
 | 
				
			||||||
 | 
							int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
 | 
				
			||||||
 | 
							if (y >= trect.top() && y < trect.top() + h) {
 | 
				
			||||||
 | 
								if (reply->replyToMsg && y >= trect.top() + st::msgReplyPadding.top() && y < trect.top() + st::msgReplyPadding.top() + st::msgReplyBarSize.height() && x >= trect.left() && x < trect.left() + trect.width()) {
 | 
				
			||||||
 | 
									outResult->link = reply->replyToLink();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							trect.setTop(trect.top() + h);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool HistoryMessage::getStateViaBotIdInfo(int x, int y, QRect &trect, HistoryTextState *outResult) const {
 | 
				
			||||||
 | 
						if (!displayFromName() && !Has<HistoryMessageForwarded>()) {
 | 
				
			||||||
 | 
							if (auto via = Get<HistoryMessageVia>()) {
 | 
				
			||||||
 | 
								if (x >= trect.left() && y >= trect.top() && y < trect.top() + st::msgNameFont->height && x < trect.left() + via->_width) {
 | 
				
			||||||
 | 
									outResult->link = via->_lnk;
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								trect.setTop(trect.top() + st::msgNameFont->height);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool HistoryMessage::getStateText(int x, int y, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const {
 | 
				
			||||||
 | 
						if (trect.contains(x, y)) {
 | 
				
			||||||
 | 
							textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle));
 | 
				
			||||||
 | 
							*outResult = _text.getState(x - trect.x(), y - trect.y(), trect.width(), request.forText());
 | 
				
			||||||
 | 
							textstyleRestore();
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TextSelection HistoryMessage::adjustSelection(TextSelection selection, TextSelectType type) const {
 | 
					TextSelection HistoryMessage::adjustSelection(TextSelection selection, TextSelectType type) const {
 | 
				
			||||||
	if (!_media || selection.to <= _text.length()) {
 | 
						if (!_media || selection.to <= _text.length()) {
 | 
				
			||||||
		return _text.adjustSelection(selection, type);
 | 
							return _text.adjustSelection(selection, type);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -174,7 +174,7 @@ private:
 | 
				
			||||||
	void applyEditionToEmpty();
 | 
						void applyEditionToEmpty();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool displayForwardedFrom() const {
 | 
						bool displayForwardedFrom() const {
 | 
				
			||||||
		if (const HistoryMessageForwarded *fwd = Get<HistoryMessageForwarded>()) {
 | 
							if (auto fwd = Get<HistoryMessageForwarded>()) {
 | 
				
			||||||
			return Has<HistoryMessageVia>() || !_media || !_media->isDisplayed() || fwd->_authorOriginal->isChannel() || !_media->hideForwardedFrom();
 | 
								return Has<HistoryMessageVia>() || !_media || !_media->isDisplayed() || fwd->_authorOriginal->isChannel() || !_media->hideForwardedFrom();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
| 
						 | 
					@ -182,12 +182,16 @@ private:
 | 
				
			||||||
	void paintFromName(Painter &p, QRect &trect, bool selected) const;
 | 
						void paintFromName(Painter &p, QRect &trect, bool selected) const;
 | 
				
			||||||
	void paintForwardedInfo(Painter &p, QRect &trect, bool selected) const;
 | 
						void paintForwardedInfo(Painter &p, QRect &trect, bool selected) const;
 | 
				
			||||||
	void paintReplyInfo(Painter &p, QRect &trect, bool selected) const;
 | 
						void paintReplyInfo(Painter &p, QRect &trect, bool selected) const;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// this method draws "via @bot" if it is not painted in forwarded info or in from name
 | 
						// this method draws "via @bot" if it is not painted in forwarded info or in from name
 | 
				
			||||||
	void paintViaBotIdInfo(Painter &p, QRect &trect, bool selected) const;
 | 
						void paintViaBotIdInfo(Painter &p, QRect &trect, bool selected) const;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	void paintText(Painter &p, QRect &trect, TextSelection selection) const;
 | 
						void paintText(Painter &p, QRect &trect, TextSelection selection) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool getStateFromName(int x, int y, QRect &trect, HistoryTextState *outResult) const;
 | 
				
			||||||
 | 
						bool getStateForwardedInfo(int x, int y, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const;
 | 
				
			||||||
 | 
						bool getStateReplyInfo(int x, int y, QRect &trect, HistoryTextState *outResult) const;
 | 
				
			||||||
 | 
						bool getStateViaBotIdInfo(int x, int y, QRect &trect, HistoryTextState *outResult) const;
 | 
				
			||||||
 | 
						bool getStateText(int x, int y, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void setMedia(const MTPMessageMedia *media);
 | 
						void setMedia(const MTPMessageMedia *media);
 | 
				
			||||||
	void setReplyMarkup(const MTPReplyMarkup *markup);
 | 
						void setReplyMarkup(const MTPReplyMarkup *markup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -223,6 +227,8 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void updateMediaInBubbleState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline MTPDmessage::Flags newMessageFlags(PeerData *p) {
 | 
					inline MTPDmessage::Flags newMessageFlags(PeerData *p) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "inline_bots/inline_bot_result.h"
 | 
					#include "inline_bots/inline_bot_result.h"
 | 
				
			||||||
#include "data/data_drafts.h"
 | 
					#include "data/data_drafts.h"
 | 
				
			||||||
#include "history/history_service_layout.h"
 | 
					#include "history/history_service_layout.h"
 | 
				
			||||||
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
#include "profile/profile_members_widget.h"
 | 
					#include "profile/profile_members_widget.h"
 | 
				
			||||||
#include "core/click_handler_types.h"
 | 
					#include "core/click_handler_types.h"
 | 
				
			||||||
#include "stickers/emoji_pan.h"
 | 
					#include "stickers/emoji_pan.h"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "inline_bots/inline_bot_layout_item.h"
 | 
					#include "inline_bots/inline_bot_layout_item.h"
 | 
				
			||||||
 | 
					#include "ui/effects/radial_animation.h"
 | 
				
			||||||
#include "ui/text/text.h"
 | 
					#include "ui/text/text.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace InlineBots {
 | 
					namespace InlineBots {
 | 
				
			||||||
| 
						 | 
					@ -113,7 +114,7 @@ private:
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		bool over;
 | 
							bool over;
 | 
				
			||||||
		FloatAnimation _a_over;
 | 
							FloatAnimation _a_over;
 | 
				
			||||||
		RadialAnimation radial;
 | 
							Ui::RadialAnimation radial;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	mutable AnimationData *_animation = nullptr;
 | 
						mutable AnimationData *_animation = nullptr;
 | 
				
			||||||
	mutable FloatAnimation _a_deleteOver;
 | 
						mutable FloatAnimation _a_deleteOver;
 | 
				
			||||||
| 
						 | 
					@ -275,7 +276,7 @@ private:
 | 
				
			||||||
		anim::fvalue a_thumbOver;
 | 
							anim::fvalue a_thumbOver;
 | 
				
			||||||
		Animation _a_thumbOver;
 | 
							Animation _a_thumbOver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		RadialAnimation radial;
 | 
							Ui::RadialAnimation radial;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	mutable std_::unique_ptr<AnimationData> _animation;
 | 
						mutable std_::unique_ptr<AnimationData> _animation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "media/view/media_clip_controller.h"
 | 
					#include "media/view/media_clip_controller.h"
 | 
				
			||||||
#include "styles/style_mediaview.h"
 | 
					#include "styles/style_mediaview.h"
 | 
				
			||||||
#include "media/media_audio.h"
 | 
					#include "media/media_audio.h"
 | 
				
			||||||
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "dropdown.h"
 | 
					#include "dropdown.h"
 | 
				
			||||||
 | 
					#include "ui/effects/radial_animation.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Media {
 | 
					namespace Media {
 | 
				
			||||||
namespace Clip {
 | 
					namespace Clip {
 | 
				
			||||||
| 
						 | 
					@ -237,7 +238,7 @@ private:
 | 
				
			||||||
	LinkButton _docDownload, _docSaveAs, _docCancel;
 | 
						LinkButton _docDownload, _docSaveAs, _docCancel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QRect _photoRadialRect;
 | 
						QRect _photoRadialRect;
 | 
				
			||||||
	RadialAnimation _radial;
 | 
						Ui::RadialAnimation _radial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	History *_migrated = nullptr;
 | 
						History *_migrated = nullptr;
 | 
				
			||||||
	History *_history = nullptr; // if conversation photos or files overview
 | 
						History *_history = nullptr; // if conversation photos or files overview
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -654,7 +654,7 @@ inputBotInlineMessageGame#3c00f8aa reply_markup:ReplyMarkup = InputBotInlineMess
 | 
				
			||||||
inputBotInlineResult#2cbbe15a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:InputBotInlineMessage = InputBotInlineResult;
 | 
					inputBotInlineResult#2cbbe15a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:InputBotInlineMessage = InputBotInlineResult;
 | 
				
			||||||
inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult;
 | 
					inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult;
 | 
				
			||||||
inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult;
 | 
					inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult;
 | 
				
			||||||
inputBotInlineResultGame#efff34f9 flags:# id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult;
 | 
					inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
botInlineMessageMediaAuto#a74b15b flags:# caption:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
 | 
					botInlineMessageMediaAuto#a74b15b flags:# caption:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
 | 
				
			||||||
botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
 | 
					botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
 | 
				
			||||||
| 
						 | 
					@ -725,7 +725,7 @@ maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;
 | 
				
			||||||
inputStickeredMediaPhoto#4a992157 id:InputPhoto = InputStickeredMedia;
 | 
					inputStickeredMediaPhoto#4a992157 id:InputPhoto = InputStickeredMedia;
 | 
				
			||||||
inputStickeredMediaDocument#438865b id:InputDocument = InputStickeredMedia;
 | 
					inputStickeredMediaDocument#438865b id:InputDocument = InputStickeredMedia;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
game#b351c590 flags:# id:long access_hash:long short_name:string title:string description:string url:string photo:Photo document:flags.0?Document = Game;
 | 
					game#bdf9653b flags:# id:long access_hash:long short_name:string title:string description:string photo:Photo document:flags.0?Document = Game;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inputGameID#32c3e77 id:long access_hash:long = InputGame;
 | 
					inputGameID#32c3e77 id:long access_hash:long = InputGame;
 | 
				
			||||||
inputGameShortName#c331e80a bot_id:InputUser short_name:string = InputGame;
 | 
					inputGameShortName#c331e80a bot_id:InputUser short_name:string = InputGame;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5471,10 +5471,9 @@ void _serialize_inputBotInlineResultGame(MTPStringLogger &to, int32 stage, int32
 | 
				
			||||||
		to.add("\n").addSpaces(lev);
 | 
							to.add("\n").addSpaces(lev);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	switch (stage) {
 | 
						switch (stage) {
 | 
				
			||||||
	case 0: to.add("  flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
						case 0: to.add("  id: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
				
			||||||
	case 1: to.add("  id: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
						case 1: to.add("  short_name: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
				
			||||||
	case 2: to.add("  short_name: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
						case 2: to.add("  send_message: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
				
			||||||
	case 3: to.add("  send_message: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
					 | 
				
			||||||
	default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
 | 
						default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -6064,9 +6063,8 @@ void _serialize_game(MTPStringLogger &to, int32 stage, int32 lev, Types &types,
 | 
				
			||||||
	case 3: to.add("  short_name: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
						case 3: to.add("  short_name: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
				
			||||||
	case 4: to.add("  title: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
						case 4: to.add("  title: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
				
			||||||
	case 5: to.add("  description: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
						case 5: to.add("  description: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
				
			||||||
	case 6: to.add("  url: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
						case 6: to.add("  photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
				
			||||||
	case 7: to.add("  photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
 | 
						case 7: to.add("  document: "); ++stages.back(); if (flag & MTPDgame::Flag::f_document) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
 | 
				
			||||||
	case 8: to.add("  document: "); ++stages.back(); if (flag & MTPDgame::Flag::f_document) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
 | 
					 | 
				
			||||||
	default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
 | 
						default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -478,7 +478,7 @@ enum {
 | 
				
			||||||
	mtpc_inputBotInlineResult = 0x2cbbe15a,
 | 
						mtpc_inputBotInlineResult = 0x2cbbe15a,
 | 
				
			||||||
	mtpc_inputBotInlineResultPhoto = 0xa8d864a7,
 | 
						mtpc_inputBotInlineResultPhoto = 0xa8d864a7,
 | 
				
			||||||
	mtpc_inputBotInlineResultDocument = 0xfff8fdc4,
 | 
						mtpc_inputBotInlineResultDocument = 0xfff8fdc4,
 | 
				
			||||||
	mtpc_inputBotInlineResultGame = 0xefff34f9,
 | 
						mtpc_inputBotInlineResultGame = 0x4fa417f2,
 | 
				
			||||||
	mtpc_botInlineMessageMediaAuto = 0xa74b15b,
 | 
						mtpc_botInlineMessageMediaAuto = 0xa74b15b,
 | 
				
			||||||
	mtpc_botInlineMessageText = 0x8c7f65e2,
 | 
						mtpc_botInlineMessageText = 0x8c7f65e2,
 | 
				
			||||||
	mtpc_botInlineMessageMediaGeo = 0x3a8fd8b8,
 | 
						mtpc_botInlineMessageMediaGeo = 0x3a8fd8b8,
 | 
				
			||||||
| 
						 | 
					@ -524,7 +524,7 @@ enum {
 | 
				
			||||||
	mtpc_maskCoords = 0xaed6dbb2,
 | 
						mtpc_maskCoords = 0xaed6dbb2,
 | 
				
			||||||
	mtpc_inputStickeredMediaPhoto = 0x4a992157,
 | 
						mtpc_inputStickeredMediaPhoto = 0x4a992157,
 | 
				
			||||||
	mtpc_inputStickeredMediaDocument = 0x438865b,
 | 
						mtpc_inputStickeredMediaDocument = 0x438865b,
 | 
				
			||||||
	mtpc_game = 0xb351c590,
 | 
						mtpc_game = 0xbdf9653b,
 | 
				
			||||||
	mtpc_inputGameID = 0x32c3e77,
 | 
						mtpc_inputGameID = 0x32c3e77,
 | 
				
			||||||
	mtpc_inputGameShortName = 0xc331e80a,
 | 
						mtpc_inputGameShortName = 0xc331e80a,
 | 
				
			||||||
	mtpc_highScore = 0x58fffcd0,
 | 
						mtpc_highScore = 0x58fffcd0,
 | 
				
			||||||
| 
						 | 
					@ -14738,18 +14738,11 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MTPDinputBotInlineResultGame : public mtpDataImpl<MTPDinputBotInlineResultGame> {
 | 
					class MTPDinputBotInlineResultGame : public mtpDataImpl<MTPDinputBotInlineResultGame> {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	enum class Flag : int32 {
 | 
					 | 
				
			||||||
		MAX_FIELD = (1 << 0),
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	Q_DECLARE_FLAGS(Flags, Flag);
 | 
					 | 
				
			||||||
	friend inline Flags operator~(Flag v) { return QFlag(~static_cast<int32>(v)); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	MTPDinputBotInlineResultGame() {
 | 
						MTPDinputBotInlineResultGame() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	MTPDinputBotInlineResultGame(const MTPflags<MTPDinputBotInlineResultGame::Flags> &_flags, const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) : vflags(_flags), vid(_id), vshort_name(_short_name), vsend_message(_send_message) {
 | 
						MTPDinputBotInlineResultGame(const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) : vid(_id), vshort_name(_short_name), vsend_message(_send_message) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	MTPflags<MTPDinputBotInlineResultGame::Flags> vflags;
 | 
					 | 
				
			||||||
	MTPstring vid;
 | 
						MTPstring vid;
 | 
				
			||||||
	MTPstring vshort_name;
 | 
						MTPstring vshort_name;
 | 
				
			||||||
	MTPInputBotInlineMessage vsend_message;
 | 
						MTPInputBotInlineMessage vsend_message;
 | 
				
			||||||
| 
						 | 
					@ -15322,7 +15315,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	MTPDgame() {
 | 
						MTPDgame() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	MTPDgame(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPstring &_url, const MTPPhoto &_photo, const MTPDocument &_document) : vflags(_flags), vid(_id), vaccess_hash(_access_hash), vshort_name(_short_name), vtitle(_title), vdescription(_description), vurl(_url), vphoto(_photo), vdocument(_document) {
 | 
						MTPDgame(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPDocument &_document) : vflags(_flags), vid(_id), vaccess_hash(_access_hash), vshort_name(_short_name), vtitle(_title), vdescription(_description), vphoto(_photo), vdocument(_document) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	MTPflags<MTPDgame::Flags> vflags;
 | 
						MTPflags<MTPDgame::Flags> vflags;
 | 
				
			||||||
| 
						 | 
					@ -15331,7 +15324,6 @@ public:
 | 
				
			||||||
	MTPstring vshort_name;
 | 
						MTPstring vshort_name;
 | 
				
			||||||
	MTPstring vtitle;
 | 
						MTPstring vtitle;
 | 
				
			||||||
	MTPstring vdescription;
 | 
						MTPstring vdescription;
 | 
				
			||||||
	MTPstring vurl;
 | 
					 | 
				
			||||||
	MTPPhoto vphoto;
 | 
						MTPPhoto vphoto;
 | 
				
			||||||
	MTPDocument vdocument;
 | 
						MTPDocument vdocument;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -25161,8 +25153,8 @@ public:
 | 
				
			||||||
	inline static MTPinputBotInlineResult new_inputBotInlineResultDocument(const MTPflags<MTPDinputBotInlineResultDocument::Flags> &_flags, const MTPstring &_id, const MTPstring &_type, const MTPstring &_title, const MTPstring &_description, const MTPInputDocument &_document, const MTPInputBotInlineMessage &_send_message) {
 | 
						inline static MTPinputBotInlineResult new_inputBotInlineResultDocument(const MTPflags<MTPDinputBotInlineResultDocument::Flags> &_flags, const MTPstring &_id, const MTPstring &_type, const MTPstring &_title, const MTPstring &_description, const MTPInputDocument &_document, const MTPInputBotInlineMessage &_send_message) {
 | 
				
			||||||
		return MTPinputBotInlineResult(new MTPDinputBotInlineResultDocument(_flags, _id, _type, _title, _description, _document, _send_message));
 | 
							return MTPinputBotInlineResult(new MTPDinputBotInlineResultDocument(_flags, _id, _type, _title, _description, _document, _send_message));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	inline static MTPinputBotInlineResult new_inputBotInlineResultGame(const MTPflags<MTPDinputBotInlineResultGame::Flags> &_flags, const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) {
 | 
						inline static MTPinputBotInlineResult new_inputBotInlineResultGame(const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) {
 | 
				
			||||||
		return MTPinputBotInlineResult(new MTPDinputBotInlineResultGame(_flags, _id, _short_name, _send_message));
 | 
							return MTPinputBotInlineResult(new MTPDinputBotInlineResultGame(_id, _short_name, _send_message));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	inline static MTPbotInlineMessage new_botInlineMessageMediaAuto(const MTPflags<MTPDbotInlineMessageMediaAuto::Flags> &_flags, const MTPstring &_caption, const MTPReplyMarkup &_reply_markup) {
 | 
						inline static MTPbotInlineMessage new_botInlineMessageMediaAuto(const MTPflags<MTPDbotInlineMessageMediaAuto::Flags> &_flags, const MTPstring &_caption, const MTPReplyMarkup &_reply_markup) {
 | 
				
			||||||
		return MTPbotInlineMessage(new MTPDbotInlineMessageMediaAuto(_flags, _caption, _reply_markup));
 | 
							return MTPbotInlineMessage(new MTPDbotInlineMessageMediaAuto(_flags, _caption, _reply_markup));
 | 
				
			||||||
| 
						 | 
					@ -25299,8 +25291,8 @@ public:
 | 
				
			||||||
	inline static MTPinputStickeredMedia new_inputStickeredMediaDocument(const MTPInputDocument &_id) {
 | 
						inline static MTPinputStickeredMedia new_inputStickeredMediaDocument(const MTPInputDocument &_id) {
 | 
				
			||||||
		return MTPinputStickeredMedia(new MTPDinputStickeredMediaDocument(_id));
 | 
							return MTPinputStickeredMedia(new MTPDinputStickeredMediaDocument(_id));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	inline static MTPgame new_game(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPstring &_url, const MTPPhoto &_photo, const MTPDocument &_document) {
 | 
						inline static MTPgame new_game(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPDocument &_document) {
 | 
				
			||||||
		return MTPgame(new MTPDgame(_flags, _id, _access_hash, _short_name, _title, _description, _url, _photo, _document));
 | 
							return MTPgame(new MTPDgame(_flags, _id, _access_hash, _short_name, _title, _description, _photo, _document));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	inline static MTPinputGame new_inputGameID(const MTPlong &_id, const MTPlong &_access_hash) {
 | 
						inline static MTPinputGame new_inputGameID(const MTPlong &_id, const MTPlong &_access_hash) {
 | 
				
			||||||
		return MTPinputGame(new MTPDinputGameID(_id, _access_hash));
 | 
							return MTPinputGame(new MTPDinputGameID(_id, _access_hash));
 | 
				
			||||||
| 
						 | 
					@ -35831,7 +35823,7 @@ inline uint32 MTPinputBotInlineResult::innerLength() const {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		case mtpc_inputBotInlineResultGame: {
 | 
							case mtpc_inputBotInlineResultGame: {
 | 
				
			||||||
			const MTPDinputBotInlineResultGame &v(c_inputBotInlineResultGame());
 | 
								const MTPDinputBotInlineResultGame &v(c_inputBotInlineResultGame());
 | 
				
			||||||
			return v.vflags.innerLength() + v.vid.innerLength() + v.vshort_name.innerLength() + v.vsend_message.innerLength();
 | 
								return v.vid.innerLength() + v.vshort_name.innerLength() + v.vsend_message.innerLength();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					@ -35882,7 +35874,6 @@ inline void MTPinputBotInlineResult::read(const mtpPrime *&from, const mtpPrime
 | 
				
			||||||
		case mtpc_inputBotInlineResultGame: _type = cons; {
 | 
							case mtpc_inputBotInlineResultGame: _type = cons; {
 | 
				
			||||||
			if (!data) setData(new MTPDinputBotInlineResultGame());
 | 
								if (!data) setData(new MTPDinputBotInlineResultGame());
 | 
				
			||||||
			MTPDinputBotInlineResultGame &v(_inputBotInlineResultGame());
 | 
								MTPDinputBotInlineResultGame &v(_inputBotInlineResultGame());
 | 
				
			||||||
			v.vflags.read(from, end);
 | 
					 | 
				
			||||||
			v.vid.read(from, end);
 | 
								v.vid.read(from, end);
 | 
				
			||||||
			v.vshort_name.read(from, end);
 | 
								v.vshort_name.read(from, end);
 | 
				
			||||||
			v.vsend_message.read(from, end);
 | 
								v.vsend_message.read(from, end);
 | 
				
			||||||
| 
						 | 
					@ -35927,7 +35918,6 @@ inline void MTPinputBotInlineResult::write(mtpBuffer &to) const {
 | 
				
			||||||
		} break;
 | 
							} break;
 | 
				
			||||||
		case mtpc_inputBotInlineResultGame: {
 | 
							case mtpc_inputBotInlineResultGame: {
 | 
				
			||||||
			const MTPDinputBotInlineResultGame &v(c_inputBotInlineResultGame());
 | 
								const MTPDinputBotInlineResultGame &v(c_inputBotInlineResultGame());
 | 
				
			||||||
			v.vflags.write(to);
 | 
					 | 
				
			||||||
			v.vid.write(to);
 | 
								v.vid.write(to);
 | 
				
			||||||
			v.vshort_name.write(to);
 | 
								v.vshort_name.write(to);
 | 
				
			||||||
			v.vsend_message.write(to);
 | 
								v.vsend_message.write(to);
 | 
				
			||||||
| 
						 | 
					@ -35962,8 +35952,8 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(MTPDinputBotInlineResultDocument::Flags)
 | 
				
			||||||
inline MTPinputBotInlineResult MTP_inputBotInlineResultDocument(const MTPflags<MTPDinputBotInlineResultDocument::Flags> &_flags, const MTPstring &_id, const MTPstring &_type, const MTPstring &_title, const MTPstring &_description, const MTPInputDocument &_document, const MTPInputBotInlineMessage &_send_message) {
 | 
					inline MTPinputBotInlineResult MTP_inputBotInlineResultDocument(const MTPflags<MTPDinputBotInlineResultDocument::Flags> &_flags, const MTPstring &_id, const MTPstring &_type, const MTPstring &_title, const MTPstring &_description, const MTPInputDocument &_document, const MTPInputBotInlineMessage &_send_message) {
 | 
				
			||||||
	return MTP::internal::TypeCreator::new_inputBotInlineResultDocument(_flags, _id, _type, _title, _description, _document, _send_message);
 | 
						return MTP::internal::TypeCreator::new_inputBotInlineResultDocument(_flags, _id, _type, _title, _description, _document, _send_message);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
inline MTPinputBotInlineResult MTP_inputBotInlineResultGame(const MTPflags<MTPDinputBotInlineResultGame::Flags> &_flags, const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) {
 | 
					inline MTPinputBotInlineResult MTP_inputBotInlineResultGame(const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) {
 | 
				
			||||||
	return MTP::internal::TypeCreator::new_inputBotInlineResultGame(_flags, _id, _short_name, _send_message);
 | 
						return MTP::internal::TypeCreator::new_inputBotInlineResultGame(_id, _short_name, _send_message);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline uint32 MTPbotInlineMessage::innerLength() const {
 | 
					inline uint32 MTPbotInlineMessage::innerLength() const {
 | 
				
			||||||
| 
						 | 
					@ -37184,7 +37174,7 @@ inline MTPgame::MTPgame() : mtpDataOwner(new MTPDgame()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline uint32 MTPgame::innerLength() const {
 | 
					inline uint32 MTPgame::innerLength() const {
 | 
				
			||||||
	const MTPDgame &v(c_game());
 | 
						const MTPDgame &v(c_game());
 | 
				
			||||||
	return v.vflags.innerLength() + v.vid.innerLength() + v.vaccess_hash.innerLength() + v.vshort_name.innerLength() + v.vtitle.innerLength() + v.vdescription.innerLength() + v.vurl.innerLength() + v.vphoto.innerLength() + (v.has_document() ? v.vdocument.innerLength() : 0);
 | 
						return v.vflags.innerLength() + v.vid.innerLength() + v.vaccess_hash.innerLength() + v.vshort_name.innerLength() + v.vtitle.innerLength() + v.vdescription.innerLength() + v.vphoto.innerLength() + (v.has_document() ? v.vdocument.innerLength() : 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
inline mtpTypeId MTPgame::type() const {
 | 
					inline mtpTypeId MTPgame::type() const {
 | 
				
			||||||
	return mtpc_game;
 | 
						return mtpc_game;
 | 
				
			||||||
| 
						 | 
					@ -37200,7 +37190,6 @@ inline void MTPgame::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId
 | 
				
			||||||
	v.vshort_name.read(from, end);
 | 
						v.vshort_name.read(from, end);
 | 
				
			||||||
	v.vtitle.read(from, end);
 | 
						v.vtitle.read(from, end);
 | 
				
			||||||
	v.vdescription.read(from, end);
 | 
						v.vdescription.read(from, end);
 | 
				
			||||||
	v.vurl.read(from, end);
 | 
					 | 
				
			||||||
	v.vphoto.read(from, end);
 | 
						v.vphoto.read(from, end);
 | 
				
			||||||
	if (v.has_document()) { v.vdocument.read(from, end); } else { v.vdocument = MTPDocument(); }
 | 
						if (v.has_document()) { v.vdocument.read(from, end); } else { v.vdocument = MTPDocument(); }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -37212,15 +37201,14 @@ inline void MTPgame::write(mtpBuffer &to) const {
 | 
				
			||||||
	v.vshort_name.write(to);
 | 
						v.vshort_name.write(to);
 | 
				
			||||||
	v.vtitle.write(to);
 | 
						v.vtitle.write(to);
 | 
				
			||||||
	v.vdescription.write(to);
 | 
						v.vdescription.write(to);
 | 
				
			||||||
	v.vurl.write(to);
 | 
					 | 
				
			||||||
	v.vphoto.write(to);
 | 
						v.vphoto.write(to);
 | 
				
			||||||
	if (v.has_document()) v.vdocument.write(to);
 | 
						if (v.has_document()) v.vdocument.write(to);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
inline MTPgame::MTPgame(MTPDgame *_data) : mtpDataOwner(_data) {
 | 
					inline MTPgame::MTPgame(MTPDgame *_data) : mtpDataOwner(_data) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(MTPDgame::Flags)
 | 
					Q_DECLARE_OPERATORS_FOR_FLAGS(MTPDgame::Flags)
 | 
				
			||||||
inline MTPgame MTP_game(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPstring &_url, const MTPPhoto &_photo, const MTPDocument &_document) {
 | 
					inline MTPgame MTP_game(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPDocument &_document) {
 | 
				
			||||||
	return MTP::internal::TypeCreator::new_game(_flags, _id, _access_hash, _short_name, _title, _description, _url, _photo, _document);
 | 
						return MTP::internal::TypeCreator::new_game(_flags, _id, _access_hash, _short_name, _title, _description, _photo, _document);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline uint32 MTPinputGame::innerLength() const {
 | 
					inline uint32 MTPinputGame::innerLength() const {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "playerwidget.h"
 | 
					#include "playerwidget.h"
 | 
				
			||||||
#include "media/media_audio.h"
 | 
					#include "media/media_audio.h"
 | 
				
			||||||
#include "localstorage.h"
 | 
					#include "localstorage.h"
 | 
				
			||||||
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Overview {
 | 
					namespace Overview {
 | 
				
			||||||
namespace Layout {
 | 
					namespace Layout {
 | 
				
			||||||
| 
						 | 
					@ -91,7 +92,7 @@ void RadialProgressItem::step_radial(uint64 ms, bool timer) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RadialProgressItem::ensureRadial() const {
 | 
					void RadialProgressItem::ensureRadial() const {
 | 
				
			||||||
	if (!_radial) {
 | 
						if (!_radial) {
 | 
				
			||||||
		_radial = new RadialAnimation(animation(const_cast<RadialProgressItem*>(this), &RadialProgressItem::step_radial));
 | 
							_radial = new Ui::RadialAnimation(animation(const_cast<RadialProgressItem*>(this), &RadialProgressItem::step_radial));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "layout.h"
 | 
					#include "layout.h"
 | 
				
			||||||
#include "core/click_handler_types.h"
 | 
					#include "core/click_handler_types.h"
 | 
				
			||||||
 | 
					#include "ui/effects/radial_animation.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Overview {
 | 
					namespace Overview {
 | 
				
			||||||
namespace Layout {
 | 
					namespace Layout {
 | 
				
			||||||
| 
						 | 
					@ -130,7 +131,7 @@ protected:
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutable RadialAnimation *_radial;
 | 
						mutable Ui::RadialAnimation *_radial;
 | 
				
			||||||
	anim::fvalue a_iconOver;
 | 
						anim::fvalue a_iconOver;
 | 
				
			||||||
	mutable Animation _a_iconOver;
 | 
						mutable Animation _a_iconOver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,6 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "application.h"
 | 
					#include "application.h"
 | 
				
			||||||
#include "playerwidget.h"
 | 
					#include "playerwidget.h"
 | 
				
			||||||
#include "overview/overview_layout.h"
 | 
					#include "overview/overview_layout.h"
 | 
				
			||||||
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
 | 
					// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "mainwidget.h"
 | 
					#include "mainwidget.h"
 | 
				
			||||||
#include "localstorage.h"
 | 
					#include "localstorage.h"
 | 
				
			||||||
#include "media/media_audio.h"
 | 
					#include "media/media_audio.h"
 | 
				
			||||||
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent)
 | 
					PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent)
 | 
				
			||||||
, _a_state(animation(this, &PlayerWidget::step_state))
 | 
					, _a_state(animation(this, &PlayerWidget::step_state))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "settings/settings_block_widget.h"
 | 
					#include "settings/settings_block_widget.h"
 | 
				
			||||||
 | 
					#include "ui/effects/radial_animation.h"
 | 
				
			||||||
#include "ui/filedialog.h"
 | 
					#include "ui/filedialog.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LinkButton;
 | 
					class LinkButton;
 | 
				
			||||||
| 
						 | 
					@ -61,7 +62,7 @@ private:
 | 
				
			||||||
	ChildWidget<LinkButton> _chooseFromGallery;
 | 
						ChildWidget<LinkButton> _chooseFromGallery;
 | 
				
			||||||
	ChildWidget<LinkButton> _chooseFromFile;
 | 
						ChildWidget<LinkButton> _chooseFromFile;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RadialAnimation _radial;
 | 
						Ui::RadialAnimation _radial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "boxes/confirmbox.h"
 | 
					#include "boxes/confirmbox.h"
 | 
				
			||||||
#include "media/media_audio.h"
 | 
					#include "media/media_audio.h"
 | 
				
			||||||
#include "localstorage.h"
 | 
					#include "localstorage.h"
 | 
				
			||||||
 | 
					#include "history/history_media_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
	int peerColorIndex(const PeerId &peer) {
 | 
						int peerColorIndex(const PeerId &peer) {
 | 
				
			||||||
| 
						 | 
					@ -1628,12 +1629,11 @@ WebPageData::WebPageData(const WebPageId &id, WebPageType type, const QString &u
 | 
				
			||||||
, pendingTill(pendingTill) {
 | 
					, pendingTill(pendingTill) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GameData::GameData(const GameId &id, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, const QString &url, PhotoData *photo, DocumentData *document) : id(id)
 | 
					GameData::GameData(const GameId &id, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, PhotoData *photo, DocumentData *document) : id(id)
 | 
				
			||||||
, accessHash(accessHash)
 | 
					, accessHash(accessHash)
 | 
				
			||||||
, shortName(shortName)
 | 
					, shortName(shortName)
 | 
				
			||||||
, title(title)
 | 
					, title(title)
 | 
				
			||||||
, description(description)
 | 
					, description(description)
 | 
				
			||||||
, url(url)
 | 
					 | 
				
			||||||
, photo(photo)
 | 
					, photo(photo)
 | 
				
			||||||
, document(document) {
 | 
					, document(document) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1368,7 +1368,7 @@ struct WebPageData {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct GameData {
 | 
					struct GameData {
 | 
				
			||||||
	GameData(const GameId &id, const uint64 &accessHash = 0, const QString &shortName = QString(), const QString &title = QString(), const QString &description = QString(), const QString &url = QString(), PhotoData *photo = nullptr, DocumentData *doc = nullptr);
 | 
						GameData(const GameId &id, const uint64 &accessHash = 0, const QString &shortName = QString(), const QString &title = QString(), const QString &description = QString(), PhotoData *photo = nullptr, DocumentData *doc = nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void forget() {
 | 
						void forget() {
 | 
				
			||||||
		if (document) document->forget();
 | 
							if (document) document->forget();
 | 
				
			||||||
| 
						 | 
					@ -1377,7 +1377,7 @@ struct GameData {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GameId id;
 | 
						GameId id;
 | 
				
			||||||
	uint64 accessHash;
 | 
						uint64 accessHash;
 | 
				
			||||||
	QString shortName, title, description, url;
 | 
						QString shortName, title, description;
 | 
				
			||||||
	PhotoData *photo;
 | 
						PhotoData *photo;
 | 
				
			||||||
	DocumentData *document;
 | 
						DocumentData *document;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,96 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					This file is part of Telegram Desktop,
 | 
				
			||||||
 | 
					the official desktop version of Telegram messaging app, see https://telegram.org
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Telegram Desktop is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					(at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
				
			||||||
 | 
					GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In addition, as a special exception, the copyright holders give permission
 | 
				
			||||||
 | 
					to link the code of portions of this program with the OpenSSL library.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 | 
				
			||||||
 | 
					Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#include "stdafx.h"
 | 
				
			||||||
 | 
					#include "ui/effects/radial_animation.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ui {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RadialAnimation::RadialAnimation(AnimationCallbacks &&callbacks)
 | 
				
			||||||
 | 
						: a_arcEnd(0, 0)
 | 
				
			||||||
 | 
						, a_arcStart(0, FullArcLength)
 | 
				
			||||||
 | 
						, _animation(std_::move(callbacks)) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RadialAnimation::start(float64 prg) {
 | 
				
			||||||
 | 
						_firstStart = _lastStart = _lastTime = getms();
 | 
				
			||||||
 | 
						int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength), iprgstrict = qRound(prg * AlmostFullArcLength);
 | 
				
			||||||
 | 
						a_arcEnd = anim::ivalue(iprgstrict, iprg);
 | 
				
			||||||
 | 
						_animation.start();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RadialAnimation::update(float64 prg, bool finished, uint64 ms) {
 | 
				
			||||||
 | 
						int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength);
 | 
				
			||||||
 | 
						if (iprg != a_arcEnd.to()) {
 | 
				
			||||||
 | 
							a_arcEnd.start(iprg);
 | 
				
			||||||
 | 
							_lastStart = _lastTime;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_lastTime = ms;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float64 dt = float64(ms - _lastStart), fulldt = float64(ms - _firstStart);
 | 
				
			||||||
 | 
						_opacity = qMin(fulldt / st::radialDuration, 1.);
 | 
				
			||||||
 | 
						if (!finished) {
 | 
				
			||||||
 | 
							a_arcEnd.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear);
 | 
				
			||||||
 | 
						} else if (dt >= st::radialDuration) {
 | 
				
			||||||
 | 
							a_arcEnd.update(1, anim::linear);
 | 
				
			||||||
 | 
							stop();
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							float64 r = dt / st::radialDuration;
 | 
				
			||||||
 | 
							a_arcEnd.update(r, anim::linear);
 | 
				
			||||||
 | 
							_opacity *= 1 - r;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						float64 fromstart = fulldt / st::radialPeriod;
 | 
				
			||||||
 | 
						a_arcStart.update(fromstart - std::floor(fromstart), anim::linear);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RadialAnimation::stop() {
 | 
				
			||||||
 | 
						_firstStart = _lastStart = _lastTime = 0;
 | 
				
			||||||
 | 
						a_arcEnd = anim::ivalue(0, 0);
 | 
				
			||||||
 | 
						_animation.stop();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RadialAnimation::step(uint64 ms) {
 | 
				
			||||||
 | 
						_animation.step(ms);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color) {
 | 
				
			||||||
 | 
						float64 o = p.opacity();
 | 
				
			||||||
 | 
						p.setOpacity(o * _opacity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QPen pen(color->p), was(p.pen());
 | 
				
			||||||
 | 
						pen.setWidth(thickness);
 | 
				
			||||||
 | 
						p.setPen(pen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int32 len = MinArcLength + a_arcEnd.current();
 | 
				
			||||||
 | 
						int32 from = QuarterArcLength - a_arcStart.current() - len;
 | 
				
			||||||
 | 
						if (rtl()) {
 | 
				
			||||||
 | 
							from = QuarterArcLength - (from - QuarterArcLength) - len;
 | 
				
			||||||
 | 
							if (from < 0) from += FullArcLength;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.setRenderHint(QPainter::HighQualityAntialiasing);
 | 
				
			||||||
 | 
						p.drawArc(inner, from, len);
 | 
				
			||||||
 | 
						p.setRenderHint(QPainter::HighQualityAntialiasing, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.setPen(was);
 | 
				
			||||||
 | 
						p.setOpacity(o);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace Ui
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,57 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					This file is part of Telegram Desktop,
 | 
				
			||||||
 | 
					the official desktop version of Telegram messaging app, see https://telegram.org
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Telegram Desktop is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					(at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
				
			||||||
 | 
					GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In addition, as a special exception, the copyright holders give permission
 | 
				
			||||||
 | 
					to link the code of portions of this program with the OpenSSL library.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 | 
				
			||||||
 | 
					Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ui {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RadialAnimation {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						RadialAnimation(AnimationCallbacks &&callbacks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float64 opacity() const {
 | 
				
			||||||
 | 
							return _opacity;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool animating() const {
 | 
				
			||||||
 | 
							return _animation.animating();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void start(float64 prg);
 | 
				
			||||||
 | 
						void update(float64 prg, bool finished, uint64 ms);
 | 
				
			||||||
 | 
						void stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void step(uint64 ms);
 | 
				
			||||||
 | 
						void step() {
 | 
				
			||||||
 | 
							step(getms());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						uint64 _firstStart = 0;
 | 
				
			||||||
 | 
						uint64 _lastStart = 0;
 | 
				
			||||||
 | 
						uint64 _lastTime = 0;
 | 
				
			||||||
 | 
						float64 _opacity = 0.;
 | 
				
			||||||
 | 
						anim::ivalue a_arcEnd, a_arcStart;
 | 
				
			||||||
 | 
						Animation _animation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace Ui
 | 
				
			||||||
| 
						 | 
					@ -2891,6 +2891,10 @@ TextSelection Text::adjustSelection(TextSelection selection, TextSelectType sele
 | 
				
			||||||
	return { from, to };
 | 
						return { from, to };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Text::isEmpty() const {
 | 
				
			||||||
 | 
						return _blocks.empty() || _blocks[0]->type() == TextBlockTSkip;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template <typename AppendPartCallback, typename ClickHandlerStartCallback, typename ClickHandlerFinishCallback, typename FlagsChangeCallback>
 | 
					template <typename AppendPartCallback, typename ClickHandlerStartCallback, typename ClickHandlerFinishCallback, typename FlagsChangeCallback>
 | 
				
			||||||
void Text::enumerateText(TextSelection selection, AppendPartCallback appendPartCallback, ClickHandlerStartCallback clickHandlerStartCallback, ClickHandlerFinishCallback clickHandlerFinishCallback, FlagsChangeCallback flagsChangeCallback) const {
 | 
					void Text::enumerateText(TextSelection selection, AppendPartCallback appendPartCallback, ClickHandlerStartCallback clickHandlerStartCallback, ClickHandlerFinishCallback clickHandlerFinishCallback, FlagsChangeCallback flagsChangeCallback) const {
 | 
				
			||||||
	if (isEmpty() || selection.empty()) {
 | 
						if (isEmpty() || selection.empty()) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -173,9 +173,7 @@ public:
 | 
				
			||||||
		return (selection.from == 0) && (selection.to >= _text.size());
 | 
							return (selection.from == 0) && (selection.to >= _text.size());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool isEmpty() const {
 | 
						bool isEmpty() const;
 | 
				
			||||||
		return _text.isEmpty();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bool isNull() const {
 | 
						bool isNull() const {
 | 
				
			||||||
		return !_font;
 | 
							return !_font;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -222,8 +222,9 @@
 | 
				
			||||||
      '<(src_loc)/history/history_item.h',
 | 
					      '<(src_loc)/history/history_item.h',
 | 
				
			||||||
      '<(src_loc)/history/history_location_manager.cpp',
 | 
					      '<(src_loc)/history/history_location_manager.cpp',
 | 
				
			||||||
      '<(src_loc)/history/history_location_manager.h',
 | 
					      '<(src_loc)/history/history_location_manager.h',
 | 
				
			||||||
      '<(src_loc)/history/history_media.cpp',
 | 
					 | 
				
			||||||
      '<(src_loc)/history/history_media.h',
 | 
					      '<(src_loc)/history/history_media.h',
 | 
				
			||||||
 | 
					      '<(src_loc)/history/history_media_types.cpp',
 | 
				
			||||||
 | 
					      '<(src_loc)/history/history_media_types.h',
 | 
				
			||||||
      '<(src_loc)/history/history_message.cpp',
 | 
					      '<(src_loc)/history/history_message.cpp',
 | 
				
			||||||
      '<(src_loc)/history/history_message.h',
 | 
					      '<(src_loc)/history/history_message.h',
 | 
				
			||||||
      '<(src_loc)/history/history_service_layout.cpp',
 | 
					      '<(src_loc)/history/history_service_layout.cpp',
 | 
				
			||||||
| 
						 | 
					@ -404,6 +405,8 @@
 | 
				
			||||||
      '<(src_loc)/ui/buttons/round_button.h',
 | 
					      '<(src_loc)/ui/buttons/round_button.h',
 | 
				
			||||||
      '<(src_loc)/ui/effects/fade_animation.cpp',
 | 
					      '<(src_loc)/ui/effects/fade_animation.cpp',
 | 
				
			||||||
      '<(src_loc)/ui/effects/fade_animation.h',
 | 
					      '<(src_loc)/ui/effects/fade_animation.h',
 | 
				
			||||||
 | 
					      '<(src_loc)/ui/effects/radial_animation.cpp',
 | 
				
			||||||
 | 
					      '<(src_loc)/ui/effects/radial_animation.h',
 | 
				
			||||||
      '<(src_loc)/ui/style/style_core.cpp',
 | 
					      '<(src_loc)/ui/style/style_core.cpp',
 | 
				
			||||||
      '<(src_loc)/ui/style/style_core.h',
 | 
					      '<(src_loc)/ui/style/style_core.h',
 | 
				
			||||||
      '<(src_loc)/ui/style/style_core_color.cpp',
 | 
					      '<(src_loc)/ui/style/style_core_color.cpp',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue