mirror of https://github.com/procxx/kepka.git
				
				
				
			stickers emoji tab done, local cache for stickers, recent stickers and voice messages
This commit is contained in:
		
							parent
							
								
									59381b8ad2
								
							
						
					
					
						commit
						091bba0fc5
					
				| 
						 | 
					@ -163,7 +163,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 | 
				
			||||||
"lng_notification_preview" = "You have a new message";
 | 
					"lng_notification_preview" = "You have a new message";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"lng_settings_section_general" = "General";
 | 
					"lng_settings_section_general" = "General";
 | 
				
			||||||
"lng_settings_change_lang" = "Change Language";
 | 
					"lng_settings_change_lang" = "Change language";
 | 
				
			||||||
"lng_languages" = "Languages";
 | 
					"lng_languages" = "Languages";
 | 
				
			||||||
"lng_sure_save_language" = "Telegram will restart\nin order to change language";
 | 
					"lng_sure_save_language" = "Telegram will restart\nin order to change language";
 | 
				
			||||||
"lng_settings_auto_update" = "Update automatically";
 | 
					"lng_settings_auto_update" = "Update automatically";
 | 
				
			||||||
| 
						 | 
					@ -196,7 +196,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 | 
				
			||||||
"lng_download_path_label" = "Download path:";
 | 
					"lng_download_path_label" = "Download path:";
 | 
				
			||||||
"lng_download_path_temp" = "temp folder";
 | 
					"lng_download_path_temp" = "temp folder";
 | 
				
			||||||
"lng_download_path_default" = "default folder";
 | 
					"lng_download_path_default" = "default folder";
 | 
				
			||||||
"lng_download_path_clear" = "Clear All";
 | 
					"lng_download_path_clear" = "Clear all";
 | 
				
			||||||
"lng_download_path_header" = "Choose download path";
 | 
					"lng_download_path_header" = "Choose download path";
 | 
				
			||||||
"lng_download_path_default_radio" = "Telegram folder in system «Downloads»";
 | 
					"lng_download_path_default_radio" = "Telegram folder in system «Downloads»";
 | 
				
			||||||
"lng_download_path_temp_radio" = "Temp folder, cleared on logout or uninstall";
 | 
					"lng_download_path_temp_radio" = "Temp folder, cleared on logout or uninstall";
 | 
				
			||||||
| 
						 | 
					@ -211,12 +211,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 | 
				
			||||||
"lng_download_path_clear_failed" = "Clear failed :(";
 | 
					"lng_download_path_clear_failed" = "Clear failed :(";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"lng_settings_section_cache" = "Local storage";
 | 
					"lng_settings_section_cache" = "Local storage";
 | 
				
			||||||
"lng_settings_no_images_cached" = "No cached images found!";
 | 
					"lng_settings_no_data_cached" = "No cached data found!";
 | 
				
			||||||
"lng_settings_images_cached" = "Cached: {count:_not_used_|# image|# images}, {size}";
 | 
					"lng_settings_images_cached" = "{count:_not_used_|# image|# images}, {size}";
 | 
				
			||||||
"lng_local_images_clear" = "Clear All";
 | 
					"lng_settings_audios_cached" = "{count:_not_used_|# voice message|# voice messages}, {size}";
 | 
				
			||||||
"lng_local_images_clearing" = "Clearing..";
 | 
					"lng_local_storage_clear" = "Clear all";
 | 
				
			||||||
"lng_local_images_cleared" = "Cleared!";
 | 
					"lng_local_storage_clearing" = "Clearing..";
 | 
				
			||||||
"lng_local_images_clear_failed" = "Clear failed :(";
 | 
					"lng_local_storage_cleared" = "Cleared!";
 | 
				
			||||||
 | 
					"lng_local_storage_clear_failed" = "Clear failed :(";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"lng_settings_section_advanced" = "Advanced";
 | 
					"lng_settings_section_advanced" = "Advanced";
 | 
				
			||||||
"lng_connection_type" = "Connection type:";
 | 
					"lng_connection_type" = "Connection type:";
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -973,7 +973,6 @@ taMsgField: flatTextarea(taDefFlat) {
 | 
				
			||||||
	font: msgFont;
 | 
						font: msgFont;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
maxFieldHeight: 250px;
 | 
					maxFieldHeight: 250px;
 | 
				
			||||||
minFieldHeight: 28px;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
newMsgSound: ':/gui/art/newmsg.wav';
 | 
					newMsgSound: ':/gui/art/newmsg.wav';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1386,6 +1385,9 @@ dpiFont3: linkFont;
 | 
				
			||||||
dpiFont4: linkFont;
 | 
					dpiFont4: linkFont;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
emojiScroll: flatScroll(scrollDef) {
 | 
					emojiScroll: flatScroll(scrollDef) {
 | 
				
			||||||
 | 
						width: 5px;
 | 
				
			||||||
 | 
						deltax: 2px;
 | 
				
			||||||
 | 
						deltay: 1px;
 | 
				
			||||||
	topsh: 0px;
 | 
						topsh: 0px;
 | 
				
			||||||
	bottomsh: 0px;
 | 
						bottomsh: 0px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1407,12 +1409,15 @@ emojiPlaces: sprite(90px, 197px, 20px, 20px);
 | 
				
			||||||
emojiSymbolsActive: sprite(290px, 266px, 20px, 20px);
 | 
					emojiSymbolsActive: sprite(290px, 266px, 20px, 20px);
 | 
				
			||||||
emojiSymbolsOver: sprite(311px, 266px, 20px, 20px);
 | 
					emojiSymbolsOver: sprite(311px, 266px, 20px, 20px);
 | 
				
			||||||
emojiSymbols: sprite(111px, 197px, 20px, 20px);
 | 
					emojiSymbols: sprite(111px, 197px, 20px, 20px);
 | 
				
			||||||
 | 
					emojiStickersActive: sprite(311px, 308px, 20px, 20px);
 | 
				
			||||||
 | 
					emojiStickersOver: sprite(354px, 200px, 20px, 20px);
 | 
				
			||||||
 | 
					emojiStickers: sprite(375px, 200px, 20px, 20px);
 | 
				
			||||||
rbEmoji: flatCheckbox {
 | 
					rbEmoji: flatCheckbox {
 | 
				
			||||||
	textColor: transparent;
 | 
						textColor: transparent;
 | 
				
			||||||
	bgColor: transparent;
 | 
						bgColor: transparent;
 | 
				
			||||||
	disColor: transparent;
 | 
						disColor: transparent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	width: 36px;
 | 
						width: 29px;
 | 
				
			||||||
	height: 36px;
 | 
						height: 36px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	textTop: 0px;
 | 
						textTop: 0px;
 | 
				
			||||||
| 
						 | 
					@ -1423,7 +1428,7 @@ rbEmoji: flatCheckbox {
 | 
				
			||||||
	cursor: cursor(pointer);
 | 
						cursor: cursor(pointer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	disabledCursor: cursor(default);
 | 
						disabledCursor: cursor(default);
 | 
				
			||||||
	imagePos: point(8px, 8px);
 | 
						imagePos: point(5px, 8px);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
rbEmojiRecent: flatCheckbox(rbEmoji) {
 | 
					rbEmojiRecent: flatCheckbox(rbEmoji) {
 | 
				
			||||||
	imageRect: emojiRecent;
 | 
						imageRect: emojiRecent;
 | 
				
			||||||
| 
						 | 
					@ -1473,15 +1478,26 @@ rbEmojiSymbols: flatCheckbox(rbEmoji) {
 | 
				
			||||||
	disImageRect: emojiSymbols;
 | 
						disImageRect: emojiSymbols;
 | 
				
			||||||
	chkDisImageRect: emojiSymbolsActive;
 | 
						chkDisImageRect: emojiSymbolsActive;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
emojiPanPadding: margins(10px, 5px, 0px, 5px);
 | 
					rbEmojiStickers: flatCheckbox(rbEmojiRecent) {
 | 
				
			||||||
 | 
						imageRect: emojiStickers;
 | 
				
			||||||
 | 
						chkImageRect: emojiStickersActive;
 | 
				
			||||||
 | 
						overImageRect: emojiStickersOver;
 | 
				
			||||||
 | 
						chkOverImageRect: emojiStickersActive;
 | 
				
			||||||
 | 
						disImageRect: emojiStickers;
 | 
				
			||||||
 | 
						chkDisImageRect: emojiStickersActive;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					emojiPanPadding: margins(5px, 0px, 0px, 5px);
 | 
				
			||||||
emojiPanSize: size(28px, 28px);
 | 
					emojiPanSize: size(28px, 28px);
 | 
				
			||||||
emojiPanFont: font(fsize);
 | 
					 | 
				
			||||||
emojiPanText: #999;
 | 
					 | 
				
			||||||
emojiPanSub: 0px;
 | 
					emojiPanSub: 0px;
 | 
				
			||||||
emojiPanDuration: 200;
 | 
					emojiPanDuration: 200;
 | 
				
			||||||
emojiPanHover: #f0f0f0;
 | 
					emojiPanHover: #f0f0f0;
 | 
				
			||||||
emojiPanRound: 2px;
 | 
					emojiPanRound: 2px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					stickerPanRound: 3px;
 | 
				
			||||||
 | 
					stickerPanPadding: 2px;
 | 
				
			||||||
 | 
					stickerPanDelete: sprite(158px, 197px, 12px, 12px);
 | 
				
			||||||
 | 
					stickerPanDeleteOpacity: 0.5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
medviewNavBarWidth: 132px;
 | 
					medviewNavBarWidth: 132px;
 | 
				
			||||||
medviewLightNav: 0.5;
 | 
					medviewLightNav: 0.5;
 | 
				
			||||||
medviewDarkNav: 1;
 | 
					medviewDarkNav: 1;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,9 +73,11 @@ Type: filesandordirs; Name: "{userappdata}\{#MyAppName}\tdumps"
 | 
				
			||||||
Type: dirifempty; Name: "{userappdata}\{#MyAppName}"
 | 
					Type: dirifempty; Name: "{userappdata}\{#MyAppName}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Languages]
 | 
					[Languages]
 | 
				
			||||||
Name: "en"; MessagesFile: "compiler:Default.isl"
 | 
					 | 
				
			||||||
Name: "it"; MessagesFile: "compiler:Languages\Italian.isl"
 | 
					Name: "it"; MessagesFile: "compiler:Languages\Italian.isl"
 | 
				
			||||||
Name: "es"; MessagesFile: "compiler:Languages\Spanish.isl"
 | 
					Name: "es"; MessagesFile: "compiler:Languages\Spanish.isl"
 | 
				
			||||||
 | 
					Name: "de"; MessagesFile: "compiler:Languages\German.isl"
 | 
				
			||||||
 | 
					Name: "nl"; MessagesFile: "compiler:Languages\Dutch.isl"
 | 
				
			||||||
 | 
					Name: "pt"; MessagesFile: "compiler:Languages\Portuguese.isl"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Code]
 | 
					[Code]
 | 
				
			||||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
 | 
					procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,38 +52,41 @@ struct EmojiReplace {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EmojiReplace replaces[] = {
 | 
					EmojiReplace replaces[] = {
 | 
				
			||||||
	{0xD83DDE0AU, ":-)"},
 | 
						{ 0xD83DDE0AU, ":-)" },
 | 
				
			||||||
	{0xD83DDE03U, ":-D"},
 | 
						{ 0xD83DDE0DU, "8-)" },
 | 
				
			||||||
	{0xD83DDE09U, ";-)"},
 | 
						{ 0x2764U, "<3" },
 | 
				
			||||||
	{0xD83DDE06U, "xD"},
 | 
						{ 0xD83DDC8BU, ":kiss:" },
 | 
				
			||||||
	{0xD83DDE1CU, ";-P"},
 | 
						{ 0xD83DDE01U, ":grin:" },
 | 
				
			||||||
	{0xD83DDE0BU, ":-p"},
 | 
						{ 0xD83DDE02U, ":joy:" },
 | 
				
			||||||
	{0xD83DDE0DU, "8-)"},
 | 
						{ 0xD83DDE1AU, ":-*" },
 | 
				
			||||||
	{0xD83DDE0EU, "B-)"},
 | 
						{ 0xD83DDE06U, "xD" },
 | 
				
			||||||
	{0xD83DDE12U, ":-("},
 | 
						{ 0xD83DDC4DU, ":like:" },
 | 
				
			||||||
	{0xD83DDE0FU, ":]"},
 | 
						{ 0xD83DDC4EU, ":dislike:" },
 | 
				
			||||||
	{0xD83DDE14U, "3("},
 | 
						{ 0x261DU, ":up:" },
 | 
				
			||||||
	{0xD83DDE22U, ":'("},
 | 
						{ 0x270CU, ":v:" },
 | 
				
			||||||
	{0xD83DDE2DU, ":_("},
 | 
						{ 0xD83DDC4CU, ":ok:" },
 | 
				
			||||||
	{0xD83DDE29U, ":(("},
 | 
						{ 0xD83DDE0EU, "B-)" },
 | 
				
			||||||
	{0xD83DDE28U, ":o"},
 | 
						{ 0xD83DDE03U, ":-D" },
 | 
				
			||||||
	{0xD83DDE10U, ":|"},
 | 
						{ 0xD83DDE09U, ";-)" },
 | 
				
			||||||
	{0xD83DDE0CU, "3-)"},
 | 
						{ 0xD83DDE1CU, ";-P" },
 | 
				
			||||||
	{0xD83DDE20U, ">("},
 | 
						{ 0xD83DDE0BU, ":-p" },
 | 
				
			||||||
	{0xD83DDE21U, ">(("},
 | 
						{ 0xD83DDE14U, "3(" },
 | 
				
			||||||
	{0xD83DDE07U, "O:)"},
 | 
						{ 0xD83DDE1EU, ":-(" },
 | 
				
			||||||
	{0xD83DDE30U, ";o"},
 | 
						{ 0xD83DDE0FU, ":]" },
 | 
				
			||||||
	{0xD83DDE33U, "8|"},
 | 
						{ 0xD83DDE22U, ":'(" },
 | 
				
			||||||
	{0xD83DDE32U, "8o"},
 | 
						{ 0xD83DDE2DU, ":_(" },
 | 
				
			||||||
	{0xD83DDE37U, ":X"},
 | 
						{ 0xD83DDE29U, ":((" },
 | 
				
			||||||
	{0xD83DDE1AU, ":-*"},
 | 
						{ 0xD83DDE28U, ":o" },
 | 
				
			||||||
	{0xD83DDE08U, "}:)"},
 | 
						{ 0xD83DDE10U, ":|" },
 | 
				
			||||||
	{0x2764U, "<3"},
 | 
						{ 0xD83DDE0CU, "3-)" },
 | 
				
			||||||
	{0xD83DDC4DU, ":like:"},
 | 
						{ 0xD83DDE20U, ">(" },
 | 
				
			||||||
	{0xD83DDC4EU, ":dislike:"},
 | 
						{ 0xD83DDE21U, ">((" },
 | 
				
			||||||
	{0x261DU, ":up:"},
 | 
						{ 0xD83DDE07U, "O:)" },
 | 
				
			||||||
	{0x270CU, ":v:"},
 | 
						{ 0xD83DDE30U, ";o" },
 | 
				
			||||||
	{0xD83DDC4CU, ":ok:"}
 | 
						{ 0xD83DDE33U, "8|" },
 | 
				
			||||||
 | 
						{ 0xD83DDE32U, "8o" },
 | 
				
			||||||
 | 
						{ 0xD83DDE37U, ":X" },
 | 
				
			||||||
 | 
						{ 0xD83DDE08U, "}:)" },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace);
 | 
					const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace);
 | 
				
			||||||
typedef QMap<QString, uint32> ReplaceMap;
 | 
					typedef QMap<QString, uint32> ReplaceMap;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -819,21 +819,21 @@ namespace App {
 | 
				
			||||||
		return App::audio(audio.vid.v, convert, audio.vaccess_hash.v, audio.vuser_id.v, audio.vdate.v, audio.vduration.v, audio.vdc_id.v, audio.vsize.v);
 | 
							return App::audio(audio.vid.v, convert, audio.vaccess_hash.v, audio.vuser_id.v, audio.vdate.v, audio.vduration.v, audio.vdc_id.v, audio.vsize.v);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DocumentData *feedDocument(int32 user, const MTPdocument &document, const QPixmap &thumb) {
 | 
						DocumentData *feedDocument(const MTPdocument &document, const QPixmap &thumb) {
 | 
				
			||||||
		switch (document.type()) {
 | 
							switch (document.type()) {
 | 
				
			||||||
		case mtpc_document: {
 | 
							case mtpc_document: {
 | 
				
			||||||
			const MTPDdocument &d(document.c_document());
 | 
								const MTPDdocument &d(document.c_document());
 | 
				
			||||||
			return App::document(d.vid.v, 0, user, d.vaccess_hash.v, d.vdate.v, d.vattributes.c_vector().v, qs(d.vmime_type), ImagePtr(thumb, "JPG"), d.vdc_id.v, d.vsize.v);
 | 
								return App::document(d.vid.v, 0, d.vaccess_hash.v, d.vdate.v, d.vattributes.c_vector().v, qs(d.vmime_type), ImagePtr(thumb, "JPG"), d.vdc_id.v, d.vsize.v);
 | 
				
			||||||
		} break;
 | 
							} break;
 | 
				
			||||||
		case mtpc_documentEmpty: return App::document(document.c_documentEmpty().vid.v);
 | 
							case mtpc_documentEmpty: return App::document(document.c_documentEmpty().vid.v);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return App::document(0);
 | 
							return App::document(0);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DocumentData *feedDocument(int32 user, const MTPdocument &document, DocumentData *convert) {
 | 
						DocumentData *feedDocument(const MTPdocument &document, DocumentData *convert) {
 | 
				
			||||||
		switch (document.type()) {
 | 
							switch (document.type()) {
 | 
				
			||||||
		case mtpc_document: {
 | 
							case mtpc_document: {
 | 
				
			||||||
			return feedDocument(user, document.c_document(), convert);
 | 
								return feedDocument(document.c_document(), convert);
 | 
				
			||||||
		} break;
 | 
							} break;
 | 
				
			||||||
		case mtpc_documentEmpty: {
 | 
							case mtpc_documentEmpty: {
 | 
				
			||||||
			return App::document(document.c_documentEmpty().vid.v, convert);
 | 
								return App::document(document.c_documentEmpty().vid.v, convert);
 | 
				
			||||||
| 
						 | 
					@ -842,8 +842,8 @@ namespace App {
 | 
				
			||||||
		return App::document(0);
 | 
							return App::document(0);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DocumentData *feedDocument(int32 user, const MTPDdocument &document, DocumentData *convert) {
 | 
						DocumentData *feedDocument(const MTPDdocument &document, DocumentData *convert) {
 | 
				
			||||||
		return App::document(document.vid.v, convert, user, document.vaccess_hash.v, document.vdate.v, document.vattributes.c_vector().v, qs(document.vmime_type), App::image(document.vthumb), document.vdc_id.v, document.vsize.v);
 | 
							return App::document(document.vid.v, convert, document.vaccess_hash.v, document.vdate.v, document.vattributes.c_vector().v, qs(document.vmime_type), App::image(document.vthumb), document.vdc_id.v, document.vsize.v);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	UserData *userLoaded(const PeerId &user) {
 | 
						UserData *userLoaded(const PeerId &user) {
 | 
				
			||||||
| 
						 | 
					@ -1058,7 +1058,7 @@ namespace App {
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DocumentData *document(const DocumentId &document, DocumentData *convert, int32 user, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) {
 | 
						DocumentData *document(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) {
 | 
				
			||||||
		if (convert) {
 | 
							if (convert) {
 | 
				
			||||||
			if (convert->id != document) {
 | 
								if (convert->id != document) {
 | 
				
			||||||
				DocumentsData::iterator i = documentsData.find(convert->id);
 | 
									DocumentsData::iterator i = documentsData.find(convert->id);
 | 
				
			||||||
| 
						 | 
					@ -1070,13 +1070,14 @@ namespace App {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			convert->access = access;
 | 
								convert->access = access;
 | 
				
			||||||
			if (!convert->date && date) {
 | 
								if (!convert->date && date) {
 | 
				
			||||||
				convert->user = user;
 | 
					 | 
				
			||||||
				convert->date = date;
 | 
									convert->date = date;
 | 
				
			||||||
				convert->setattributes(attributes);
 | 
									convert->setattributes(attributes);
 | 
				
			||||||
				convert->mime = mime;
 | 
									convert->mime = mime;
 | 
				
			||||||
				convert->thumb = thumb;
 | 
									convert->thumb = thumb;
 | 
				
			||||||
				convert->dc = dc;
 | 
									convert->dc = dc;
 | 
				
			||||||
				convert->size = size;
 | 
									convert->size = size;
 | 
				
			||||||
 | 
								} else if (convert->thumb->isNull() && !thumb->isNull()) {
 | 
				
			||||||
 | 
									convert->thumb = thumb;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (convert->location.check()) {
 | 
								if (convert->location.check()) {
 | 
				
			||||||
| 
						 | 
					@ -1089,13 +1090,13 @@ namespace App {
 | 
				
			||||||
			if (convert) {
 | 
								if (convert) {
 | 
				
			||||||
				result = convert;
 | 
									result = convert;
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				result = new DocumentData(user, document, access, date, attributes, mime, thumb, dc, size);
 | 
									result = new DocumentData(document, access, date, attributes, mime, thumb, dc, size);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			documentsData.insert(document, result);
 | 
								documentsData.insert(document, result);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			result = i.value();
 | 
								result = i.value();
 | 
				
			||||||
			if (result != convert && !result->date && date) {
 | 
								if (result != convert) {
 | 
				
			||||||
				result->user = user;
 | 
									if (!result->date && date) {
 | 
				
			||||||
					result->access = access;
 | 
										result->access = access;
 | 
				
			||||||
					result->date = date;
 | 
										result->date = date;
 | 
				
			||||||
					result->setattributes(attributes);
 | 
										result->setattributes(attributes);
 | 
				
			||||||
| 
						 | 
					@ -1103,6 +1104,9 @@ namespace App {
 | 
				
			||||||
					result->thumb = thumb;
 | 
										result->thumb = thumb;
 | 
				
			||||||
					result->dc = dc;
 | 
										result->dc = dc;
 | 
				
			||||||
					result->size = size;
 | 
										result->size = size;
 | 
				
			||||||
 | 
									} else if (result->thumb->isNull() && !thumb->isNull()) {
 | 
				
			||||||
 | 
										result->thumb = thumb;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
| 
						 | 
					@ -1850,9 +1854,10 @@ namespace App {
 | 
				
			||||||
				case dbietRecent  : cSetEmojiTab(dbietRecent);   break;
 | 
									case dbietRecent  : cSetEmojiTab(dbietRecent);   break;
 | 
				
			||||||
				case dbietPeople  : cSetEmojiTab(dbietPeople);   break;
 | 
									case dbietPeople  : cSetEmojiTab(dbietPeople);   break;
 | 
				
			||||||
				case dbietNature  : cSetEmojiTab(dbietNature);   break;
 | 
									case dbietNature  : cSetEmojiTab(dbietNature);   break;
 | 
				
			||||||
				case dbietObjects: cSetEmojiTab(dbietObjects); break;
 | 
									case dbietObjects : cSetEmojiTab(dbietObjects);  break;
 | 
				
			||||||
				case dbietPlaces  : cSetEmojiTab(dbietPlaces);   break;
 | 
									case dbietPlaces  : cSetEmojiTab(dbietPlaces);   break;
 | 
				
			||||||
				case dbietSymbols: cSetEmojiTab(dbietSymbols); break;
 | 
									case dbietSymbols : cSetEmojiTab(dbietSymbols);  break;
 | 
				
			||||||
 | 
									case dbietStickers: cSetEmojiTab(dbietStickers); break;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} break;
 | 
								} break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2017,13 +2022,16 @@ namespace App {
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QImage readImage(const QString &file, QByteArray *format, bool opaque, bool *animated) {
 | 
						QImage readImage(const QString &file, QByteArray *format, bool opaque, bool *animated, QByteArray *content) {
 | 
				
			||||||
		QFile f(file);
 | 
							QFile f(file);
 | 
				
			||||||
		if (!f.open(QIODevice::ReadOnly)) {
 | 
							if (!f.open(QIODevice::ReadOnly)) {
 | 
				
			||||||
			if (animated) *animated = false;
 | 
								if (animated) *animated = false;
 | 
				
			||||||
			return QImage();
 | 
								return QImage();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return readImage(f.readAll(), format, opaque, animated);
 | 
							QByteArray img = f.readAll();
 | 
				
			||||||
 | 
							QImage result = readImage(img, format, opaque, animated);
 | 
				
			||||||
 | 
							if (content && !result.isNull()) *content = img;
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void regVideoItem(VideoData *data, HistoryItem *item) {
 | 
						void regVideoItem(VideoData *data, HistoryItem *item) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,9 +85,9 @@ namespace App {
 | 
				
			||||||
	PhotoData *feedPhoto(const MTPDphoto &photo, PhotoData *convert = 0);
 | 
						PhotoData *feedPhoto(const MTPDphoto &photo, PhotoData *convert = 0);
 | 
				
			||||||
	VideoData *feedVideo(const MTPDvideo &video, VideoData *convert = 0);
 | 
						VideoData *feedVideo(const MTPDvideo &video, VideoData *convert = 0);
 | 
				
			||||||
	AudioData *feedAudio(const MTPDaudio &audio, AudioData *convert = 0);
 | 
						AudioData *feedAudio(const MTPDaudio &audio, AudioData *convert = 0);
 | 
				
			||||||
	DocumentData *feedDocument(int32 user, const MTPdocument &document, const QPixmap &thumb);
 | 
						DocumentData *feedDocument(const MTPdocument &document, const QPixmap &thumb);
 | 
				
			||||||
	DocumentData *feedDocument(int32 user, const MTPdocument &document, DocumentData *convert = 0);
 | 
						DocumentData *feedDocument(const MTPdocument &document, DocumentData *convert = 0);
 | 
				
			||||||
	DocumentData *feedDocument(int32 user, const MTPDdocument &document, DocumentData *convert = 0);
 | 
						DocumentData *feedDocument(const MTPDdocument &document, DocumentData *convert = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	UserData *userLoaded(const PeerId &user);
 | 
						UserData *userLoaded(const PeerId &user);
 | 
				
			||||||
	ChatData *chatLoaded(const PeerId &chat);
 | 
						ChatData *chatLoaded(const PeerId &chat);
 | 
				
			||||||
| 
						 | 
					@ -106,7 +106,7 @@ namespace App {
 | 
				
			||||||
	PhotoData *photo(const PhotoId &photo, PhotoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr());
 | 
						PhotoData *photo(const PhotoId &photo, PhotoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr());
 | 
				
			||||||
	VideoData *video(const VideoId &video, VideoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
 | 
						VideoData *video(const VideoId &video, VideoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
 | 
				
			||||||
	AudioData *audio(const AudioId &audio, AudioData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 dc = 0, int32 size = 0);
 | 
						AudioData *audio(const AudioId &audio, AudioData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 dc = 0, int32 size = 0);
 | 
				
			||||||
	DocumentData *document(const DocumentId &document, DocumentData *convert = 0, int32 user = 0, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
 | 
						DocumentData *document(const DocumentId &document, DocumentData *convert = 0, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
 | 
				
			||||||
	ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type = InvalidImageLink, const QString &url = QString());
 | 
						ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type = InvalidImageLink, const QString &url = QString());
 | 
				
			||||||
	void forgetMedia();
 | 
						void forgetMedia();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -169,7 +169,7 @@ namespace App {
 | 
				
			||||||
	void setQuiting();
 | 
						void setQuiting();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    QImage readImage(QByteArray data, QByteArray *format = 0, bool opaque = true, bool *animated = 0);
 | 
					    QImage readImage(QByteArray data, QByteArray *format = 0, bool opaque = true, bool *animated = 0);
 | 
				
			||||||
	QImage readImage(const QString &file, QByteArray *format = 0, bool opaque = true, bool *animated = 0);
 | 
						QImage readImage(const QString &file, QByteArray *format = 0, bool opaque = true, bool *animated = 0, QByteArray *content = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void regVideoItem(VideoData *data, HistoryItem *item);
 | 
						void regVideoItem(VideoData *data, HistoryItem *item);
 | 
				
			||||||
	void unregVideoItem(VideoData *data, HistoryItem *item);
 | 
						void unregVideoItem(VideoData *data, HistoryItem *item);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 59 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 103 KiB  | 
| 
						 | 
					@ -29,40 +29,43 @@ namespace {
 | 
				
			||||||
		const char *replace;
 | 
							const char *replace;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	EmojiReplace replaces[] = {
 | 
						EmojiReplace replaces[] = {
 | 
				
			||||||
		{0xD83DDE0A, ":-)"},
 | 
							{ 0xD83DDE0AU, ":-)" },
 | 
				
			||||||
		{0xD83DDE03, ":-D"},
 | 
							{ 0xD83DDE0DU, "8-)" },
 | 
				
			||||||
		{0xD83DDE09, ";-)"},
 | 
							{ 0x2764U, "<3" },
 | 
				
			||||||
		{0xD83DDE06, "xD"},
 | 
							{ 0xD83DDC8BU, ":kiss:" },
 | 
				
			||||||
		{0xD83DDE1C, ";-P"},
 | 
							{ 0xD83DDE01U, ":grin:" },
 | 
				
			||||||
		{0xD83DDE0B, ":-p"},
 | 
							{ 0xD83DDE02U, ":joy:" },
 | 
				
			||||||
		{0xD83DDE0D, "8-)"},
 | 
							{ 0xD83DDE1AU, ":-*" },
 | 
				
			||||||
		{0xD83DDE0E, "B-)"},
 | 
							{ 0xD83DDE06U, "xD" },
 | 
				
			||||||
		{0xD83DDE12, ":-("},
 | 
							{ 0xD83DDC4DU, ":like:" },
 | 
				
			||||||
		{0xD83DDE0F, ":]"},
 | 
							{ 0xD83DDC4EU, ":dislike:" },
 | 
				
			||||||
		{0xD83DDE14, "3("},
 | 
							{ 0x261DU, ":up:" },
 | 
				
			||||||
		{0xD83DDE22, ":'("},
 | 
							{ 0x270CU, ":v:" },
 | 
				
			||||||
		{0xD83DDE2D, ":_("},
 | 
							{ 0xD83DDC4CU, ":ok:" },
 | 
				
			||||||
		{0xD83DDE29, ":(("},
 | 
							{ 0xD83DDE0EU, "B-)" },
 | 
				
			||||||
		{0xD83DDE28, ":o"},
 | 
							{ 0xD83DDE03U, ":-D" },
 | 
				
			||||||
		{0xD83DDE10, ":|"},
 | 
							{ 0xD83DDE09U, ";-)" },
 | 
				
			||||||
		{0xD83DDE0C, "3-)"},
 | 
							{ 0xD83DDE1CU, ";-P" },
 | 
				
			||||||
		{0xD83DDE20, ">("},
 | 
							{ 0xD83DDE0BU, ":-p" },
 | 
				
			||||||
		{0xD83DDE21, ">(("},
 | 
							{ 0xD83DDE14U, "3(" },
 | 
				
			||||||
		{0xD83DDE07, "O:)"},
 | 
							{ 0xD83DDE1EU, ":-(" },
 | 
				
			||||||
		{0xD83DDE30, ";o"},
 | 
							{ 0xD83DDE0FU, ":]" },
 | 
				
			||||||
		{0xD83DDE33, "8|"},
 | 
							{ 0xD83DDE22U, ":'(" },
 | 
				
			||||||
		{0xD83DDE32, "8o"},
 | 
							{ 0xD83DDE2DU, ":_(" },
 | 
				
			||||||
		{0xD83DDE37, ":X"},
 | 
							{ 0xD83DDE29U, ":((" },
 | 
				
			||||||
		{0xD83DDE1A, ":-*"},
 | 
							{ 0xD83DDE28U, ":o" },
 | 
				
			||||||
		{0xD83DDE08, "}:)"},
 | 
							{ 0xD83DDE10U, ":|" },
 | 
				
			||||||
		{0x2764, "<3"},
 | 
							{ 0xD83DDE0CU, "3-)" },
 | 
				
			||||||
		{0xD83DDC4D, ":like:"},
 | 
							{ 0xD83DDE20U, ">(" },
 | 
				
			||||||
		{0xD83DDC4E, ":dislike:"},
 | 
							{ 0xD83DDE21U, ">((" },
 | 
				
			||||||
		{0x261D, ":up:"},
 | 
							{ 0xD83DDE07U, "O:)" },
 | 
				
			||||||
		{0x270C, ":v:"},
 | 
							{ 0xD83DDE30U, ";o" },
 | 
				
			||||||
		{0xD83DDC4C, ":ok:"}
 | 
							{ 0xD83DDE33U, "8|" },
 | 
				
			||||||
 | 
							{ 0xD83DDE32U, "8o" },
 | 
				
			||||||
 | 
							{ 0xD83DDE37U, ":X" },
 | 
				
			||||||
 | 
							{ 0xD83DDE08U, "}:)" },
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace), replacesInRow = 8;
 | 
						const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace), replacesInRow = 7;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EmojiBox::EmojiBox() : _done(this, lang(lng_about_done), st::aboutCloseButton),
 | 
					EmojiBox::EmojiBox() : _done(this, lang(lng_about_done), st::aboutCloseButton),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,6 +95,8 @@ enum {
 | 
				
			||||||
	PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request
 | 
						PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request
 | 
				
			||||||
	EmojiPadPerRow = 7,
 | 
						EmojiPadPerRow = 7,
 | 
				
			||||||
	EmojiPadRowsPerPage = 6,
 | 
						EmojiPadRowsPerPage = 6,
 | 
				
			||||||
 | 
						StickerPadPerRow = 3,
 | 
				
			||||||
 | 
						StickersUpdateTimeout = 3600000, // update not more than once in an hour
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SearchPeopleLimit = 5,
 | 
						SearchPeopleLimit = 5,
 | 
				
			||||||
	MinUsernameLength = 5,
 | 
						MinUsernameLength = 5,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "dropdown.h"
 | 
					#include "dropdown.h"
 | 
				
			||||||
#include "historywidget.h"
 | 
					#include "historywidget.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "localstorage.h"
 | 
				
			||||||
#include "lang.h"
 | 
					#include "lang.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Dropdown::Dropdown(QWidget *parent) : TWidget(parent),
 | 
					Dropdown::Dropdown(QWidget *parent) : TWidget(parent),
 | 
				
			||||||
| 
						 | 
					@ -315,20 +316,76 @@ bool DragArea::animStep(float64 ms) {
 | 
				
			||||||
	return res;
 | 
						return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EmojiPanInner::EmojiPanInner(QWidget *parent) : QWidget(parent), _tab(cEmojiTab()), _selected(-1), _pressedSel(-1) {
 | 
					EmojiPanInner::EmojiPanInner(QWidget *parent) : QWidget(parent), _tab(cEmojiTab()), _selected(-1), _xSelected(-1), _pressedSel(-1), _xPressedSel(-1) {
 | 
				
			||||||
	resize(EmojiPadPerRow * st::emojiPanSize.width(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
 | 
						resize(EmojiPadPerRow * st::emojiPanSize.width(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
 | 
				
			||||||
	setMouseTracking(true);
 | 
						setMouseTracking(true);
 | 
				
			||||||
	setFocusPolicy(Qt::NoFocus);
 | 
						setFocusPolicy(Qt::NoFocus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_saveConfigTimer.setSingleShot(true);
 | 
						_saveConfigTimer.setSingleShot(true);
 | 
				
			||||||
	connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig()));
 | 
						connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig()));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EmojiPanInner::paintEvent(QPaintEvent *e) {
 | 
					void EmojiPanInner::paintEvent(QPaintEvent *e) {
 | 
				
			||||||
	QPainter p(this);
 | 
						QPainter p(this);
 | 
				
			||||||
	int32 size = _emojis.size();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QRect r = e ? e->rect() : rect();
 | 
						QRect r = e ? e->rect() : rect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (_tab == dbietStickers) {
 | 
				
			||||||
 | 
							int32 size = _stickers.size();
 | 
				
			||||||
 | 
							float64 stickerWidth = width() / float64(StickerPadPerRow);
 | 
				
			||||||
 | 
							int32 rows = (size / StickerPadPerRow) + ((size % StickerPadPerRow) ? 1 : 0), stickerSize = int32(stickerWidth);
 | 
				
			||||||
 | 
							int32 fromrow = qMax(qFloor(r.top() / stickerSize), 0), torow = qMin(qCeil(r.bottom() / stickerSize) + 1, rows);
 | 
				
			||||||
 | 
							for (int32 i = fromrow; i < torow; ++i) {
 | 
				
			||||||
 | 
								for (int32 j = 0; j < StickerPadPerRow; ++j) {
 | 
				
			||||||
 | 
									int32 index = i * StickerPadPerRow + j;
 | 
				
			||||||
 | 
									if (index >= size) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									float64 hover = _hovers[index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									QPoint pos(qRound(j * stickerWidth), i * stickerSize);
 | 
				
			||||||
 | 
									if (hover > 0) {
 | 
				
			||||||
 | 
										p.setOpacity(hover);
 | 
				
			||||||
 | 
										p.setBrush(st::emojiPanHover->b);
 | 
				
			||||||
 | 
										p.setPen(Qt::NoPen);
 | 
				
			||||||
 | 
										p.drawRoundedRect(QRect(pos, QSize(stickerSize, stickerSize)), st::stickerPanRound, st::stickerPanRound);
 | 
				
			||||||
 | 
										p.setOpacity(1);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									DocumentData *sticker = _stickers[index];
 | 
				
			||||||
 | 
									bool already = !sticker->already().isEmpty(), hasdata = !sticker->data.isEmpty();
 | 
				
			||||||
 | 
									if (!sticker->loader && sticker->status != FileFailed && !already && !hasdata) {
 | 
				
			||||||
 | 
										sticker->save(QString());
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (sticker->sticker->isNull() && (already || hasdata)) {
 | 
				
			||||||
 | 
										if (already) {
 | 
				
			||||||
 | 
											sticker->sticker = ImagePtr(sticker->already());
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											sticker->sticker = ImagePtr(sticker->data);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									float64 coef = qMin((stickerWidth - st::stickerPanPadding * 2) / float64(sticker->dimensions.width()), (stickerSize - st::stickerPanPadding * 2) / float64(sticker->dimensions.height()));
 | 
				
			||||||
 | 
									int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height());
 | 
				
			||||||
 | 
									QPoint ppos = pos + QPoint((stickerSize - w) / 2, (stickerSize - h) / 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (sticker->sticker->isNull()) {
 | 
				
			||||||
 | 
										p.drawPixmap(ppos, sticker->thumb->pix(w));
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										p.drawPixmap(ppos, sticker->sticker->pix(w));
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (hover > 0 && _isUserGen[index]) {
 | 
				
			||||||
 | 
										float64 xHover = _hovers[_stickers.size() + index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										QPoint xPos = pos + QPoint(stickerWidth - st::stickerPanDelete.pxWidth(), 0);
 | 
				
			||||||
 | 
										p.setOpacity(hover * (xHover + (1 - xHover) * st::stickerPanDeleteOpacity));
 | 
				
			||||||
 | 
										p.drawPixmap(xPos, App::sprite(), st::stickerPanDelete);
 | 
				
			||||||
 | 
										p.setOpacity(1);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							int32 size = _emojis.size();
 | 
				
			||||||
		int32 rows = (size / EmojiPadPerRow) + ((size % EmojiPadPerRow) ? 1 : 0);
 | 
							int32 rows = (size / EmojiPadPerRow) + ((size % EmojiPadPerRow) ? 1 : 0);
 | 
				
			||||||
		int32 fromrow = qMax(qFloor(r.top() / st::emojiPanSize.height()), 0), torow = qMin(qCeil(r.bottom() / st::emojiPanSize.height()) + 1, rows);
 | 
							int32 fromrow = qMax(qFloor(r.top() / st::emojiPanSize.height()), 0), torow = qMin(qCeil(r.bottom() / st::emojiPanSize.height()) + 1, rows);
 | 
				
			||||||
		for (int32 i = fromrow; i < torow; ++i) {
 | 
							for (int32 i = fromrow; i < torow; ++i) {
 | 
				
			||||||
| 
						 | 
					@ -350,18 +407,38 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) {
 | 
				
			||||||
				p.drawPixmap(w + QPoint((st::emojiPanSize.width() - st::emojiSize) / 2, (st::emojiPanSize.height() - st::emojiSize) / 2), App::emojis(), r);
 | 
									p.drawPixmap(w + QPoint((st::emojiPanSize.width() - st::emojiSize) / 2, (st::emojiPanSize.height() - st::emojiSize) / 2), App::emojis(), r);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EmojiPanInner::mousePressEvent(QMouseEvent *e) {
 | 
					void EmojiPanInner::mousePressEvent(QMouseEvent *e) {
 | 
				
			||||||
	_lastMousePos = e->globalPos();
 | 
						_lastMousePos = e->globalPos();
 | 
				
			||||||
	updateSelected();
 | 
						updateSelected();
 | 
				
			||||||
	_pressedSel = _selected;
 | 
						_pressedSel = _selected;
 | 
				
			||||||
 | 
						_xPressedSel = _xSelected;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) {
 | 
					void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) {
 | 
				
			||||||
	_lastMousePos = e->globalPos();
 | 
						_lastMousePos = e->globalPos();
 | 
				
			||||||
	updateSelected();
 | 
						updateSelected();
 | 
				
			||||||
	if (_selected == _pressedSel && _selected >= 0 && _selected < _emojis.size()) {
 | 
						if (_xSelected == _xPressedSel && _xSelected >= 0 && _tab == dbietStickers) {
 | 
				
			||||||
 | 
							RecentStickerPack recent(cRecentStickers());
 | 
				
			||||||
 | 
							DocumentData *sticker = _stickers.at(_xSelected - _stickers.size());
 | 
				
			||||||
 | 
							for (int32 i = 0, l = recent.size(); i < l; ++i) {
 | 
				
			||||||
 | 
								if (recent.at(i).first == sticker) {
 | 
				
			||||||
 | 
									recent.removeAt(i);
 | 
				
			||||||
 | 
									cSetRecentStickers(recent);
 | 
				
			||||||
 | 
									Local::writeRecentStickers();
 | 
				
			||||||
 | 
									showEmojiPack(dbietStickers);
 | 
				
			||||||
 | 
									updateSelected();
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (_selected == _pressedSel && _selected >= 0) {
 | 
				
			||||||
 | 
							if (_tab == dbietStickers) {
 | 
				
			||||||
 | 
								if (_selected < _stickers.size()) {
 | 
				
			||||||
 | 
									emit stickerSelected(_stickers[_selected]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else if (_selected < _emojis.size()) {
 | 
				
			||||||
			EmojiPtr emoji(_emojis[_selected]);
 | 
								EmojiPtr emoji(_emojis[_selected]);
 | 
				
			||||||
			RecentEmojiPack recent(cGetRecentEmojis());
 | 
								RecentEmojiPack recent(cGetRecentEmojis());
 | 
				
			||||||
			RecentEmojiPack::iterator i = recent.begin(), e = recent.end();
 | 
								RecentEmojiPack::iterator i = recent.begin(), e = recent.end();
 | 
				
			||||||
| 
						 | 
					@ -401,6 +478,7 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			emit emojiSelected(emoji);
 | 
								emit emojiSelected(emoji);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EmojiPanInner::onSaveConfig() {
 | 
					void EmojiPanInner::onSaveConfig() {
 | 
				
			||||||
| 
						 | 
					@ -413,21 +491,50 @@ void EmojiPanInner::mouseMoveEvent(QMouseEvent *e) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EmojiPanInner::leaveEvent(QEvent *e) {
 | 
					void EmojiPanInner::leaveEvent(QEvent *e) {
 | 
				
			||||||
	_lastMousePos = QCursor::pos();
 | 
						clearSelection();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EmojiPanInner::clearSelection(bool fast) {
 | 
				
			||||||
 | 
						_lastMousePos = mapToGlobal(QPoint(-10, -10));
 | 
				
			||||||
 | 
						if (fast) {
 | 
				
			||||||
 | 
							if (_tab == dbietStickers) {
 | 
				
			||||||
 | 
								_hovers = QVector<float64>(_stickers.size() * 2, 0);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								_hovers = QVector<float64>(_emojis.size(), 0);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_emojiAnimations.clear();
 | 
				
			||||||
 | 
							_selected = _pressedSel = _xSelected = _xPressedSel = -1;
 | 
				
			||||||
 | 
							anim::stop(this);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		updateSelected();
 | 
							updateSelected();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EmojiPanInner::updateSelected() {
 | 
					void EmojiPanInner::updateSelected() {
 | 
				
			||||||
	int32 selIndex = -1;
 | 
						int32 selIndex = -1, xSelIndex = -1;
 | 
				
			||||||
	QPoint p(mapFromGlobal(_lastMousePos));
 | 
						QPoint p(mapFromGlobal(_lastMousePos));
 | 
				
			||||||
	if (p.x() >= 0 && p.y() >= 0 && p.x() < EmojiPadPerRow * st::emojiPanSize.width()) {
 | 
						if (_tab == dbietStickers) {
 | 
				
			||||||
 | 
							float64 stickerWidth = width() / float64(StickerPadPerRow);
 | 
				
			||||||
 | 
							int32 stickerSize = int32(stickerWidth);
 | 
				
			||||||
 | 
							if (p.x() >= 0 && p.y() >= 0 && p.x() < StickerPadPerRow * stickerWidth) {
 | 
				
			||||||
 | 
								selIndex = qFloor(p.y() / stickerSize) * StickerPadPerRow + qFloor(p.x() / stickerWidth);
 | 
				
			||||||
 | 
								if (selIndex >= _stickers.size()) {
 | 
				
			||||||
 | 
									selIndex = -1;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									int32 inx = p.x() - (selIndex % StickerPadPerRow) * stickerWidth, iny = p.y() - ((selIndex / StickerPadPerRow) * stickerSize);
 | 
				
			||||||
 | 
									if (inx >= stickerWidth - st::stickerPanDelete.pxWidth() && iny < st::stickerPanDelete.pxHeight()) {
 | 
				
			||||||
 | 
										xSelIndex = _stickers.size() + selIndex;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (p.x() >= 0 && p.y() >= 0 && p.x() < EmojiPadPerRow * st::emojiPanSize.width()) {
 | 
				
			||||||
		selIndex = qFloor(p.y() / st::emojiPanSize.height()) * EmojiPadPerRow + qFloor(p.x() / st::emojiPanSize.width());
 | 
							selIndex = qFloor(p.y() / st::emojiPanSize.height()) * EmojiPadPerRow + qFloor(p.x() / st::emojiPanSize.width());
 | 
				
			||||||
		if (selIndex >= _emojis.size()) {
 | 
							if (selIndex >= _emojis.size()) {
 | 
				
			||||||
			selIndex = -1;
 | 
								selIndex = -1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (selIndex != _selected) {
 | 
					 | 
				
			||||||
	bool startanim = false;
 | 
						bool startanim = false;
 | 
				
			||||||
 | 
						if (selIndex != _selected) {
 | 
				
			||||||
		if (_selected >= 0) {
 | 
							if (_selected >= 0) {
 | 
				
			||||||
			_emojiAnimations.remove(_selected + 1);
 | 
								_emojiAnimations.remove(_selected + 1);
 | 
				
			||||||
			if (_emojiAnimations.find(-_selected - 1) == _emojiAnimations.end()) {
 | 
								if (_emojiAnimations.find(-_selected - 1) == _emojiAnimations.end()) {
 | 
				
			||||||
| 
						 | 
					@ -443,9 +550,26 @@ void EmojiPanInner::updateSelected() {
 | 
				
			||||||
				_emojiAnimations.insert(_selected + 1, getms());
 | 
									_emojiAnimations.insert(_selected + 1, getms());
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (startanim) anim::start(this);
 | 
					 | 
				
			||||||
		setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default);
 | 
							setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if (xSelIndex != _xSelected) {
 | 
				
			||||||
 | 
							if (_xSelected >= 0) {
 | 
				
			||||||
 | 
								_emojiAnimations.remove(_xSelected + 1);
 | 
				
			||||||
 | 
								if (_emojiAnimations.find(-_xSelected - 1) == _emojiAnimations.end()) {
 | 
				
			||||||
 | 
									if (_emojiAnimations.isEmpty()) startanim = true;
 | 
				
			||||||
 | 
									_emojiAnimations.insert(-_xSelected - 1, getms());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_xSelected = xSelIndex;
 | 
				
			||||||
 | 
							if (_xSelected >= 0) {
 | 
				
			||||||
 | 
								_emojiAnimations.remove(-_xSelected - 1);
 | 
				
			||||||
 | 
								if (_emojiAnimations.find(_xSelected + 1) == _emojiAnimations.end()) {
 | 
				
			||||||
 | 
									if (_emojiAnimations.isEmpty()) startanim = true;
 | 
				
			||||||
 | 
									_emojiAnimations.insert(_xSelected + 1, getms());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (startanim) anim::start(this);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool EmojiPanInner::animStep(float64 ms) {
 | 
					bool EmojiPanInner::animStep(float64 ms) {
 | 
				
			||||||
| 
						 | 
					@ -466,12 +590,42 @@ bool EmojiPanInner::animStep(float64 ms) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) {
 | 
					void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) {
 | 
				
			||||||
	_tab = packIndex;
 | 
						_tab = packIndex;
 | 
				
			||||||
 | 
						int32 h, size;
 | 
				
			||||||
 | 
						if (packIndex == dbietStickers) {
 | 
				
			||||||
 | 
							_emojis.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							float64 stickerWidth = width() / float64(StickerPadPerRow);
 | 
				
			||||||
 | 
							int32 stickerSize = int32(stickerWidth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							int32 l = cRecentStickers().size();
 | 
				
			||||||
 | 
							_stickers.resize(l);
 | 
				
			||||||
 | 
							_isUserGen.resize(l);
 | 
				
			||||||
 | 
							for (int32 i = 0; i < l; ++i) {
 | 
				
			||||||
 | 
								DocumentData *sticker = _stickers[i] = cRecentStickers().at(i).first;
 | 
				
			||||||
 | 
								_isUserGen[i] = (cRecentStickers().at(i).second < 0);
 | 
				
			||||||
 | 
								if (i < StickerPadPerRow * ((EmojiPadRowsPerPage * st::emojiPanSize.height() - int(st::emojiPanSub)) / stickerSize + 1)) {
 | 
				
			||||||
 | 
									bool already = !sticker->already().isEmpty(), hasdata = !sticker->data.isEmpty();
 | 
				
			||||||
 | 
									if (!sticker->loader && sticker->status != FileFailed && !already && !hasdata) {
 | 
				
			||||||
 | 
										sticker->save(QString());
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							size = _stickers.size();
 | 
				
			||||||
 | 
							h = ((size / StickerPadPerRow) + ((size % StickerPadPerRow) ? 1 : 0)) * stickerSize;
 | 
				
			||||||
 | 
							_hovers = QVector<float64>(size * 2, 0);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		_emojis = emojiPack(packIndex);
 | 
							_emojis = emojiPack(packIndex);
 | 
				
			||||||
	_hovers = QVector<float64>(_emojis.size(), 0);
 | 
							_stickers.clear();
 | 
				
			||||||
 | 
							_isUserGen.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							size = _emojis.size();
 | 
				
			||||||
 | 
							h = ((size / EmojiPadPerRow) + ((size % EmojiPadPerRow) ? 1 : 0)) * st::emojiPanSize.height();
 | 
				
			||||||
 | 
							_hovers = QVector<float64>(size, 0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						h = qMax(h, EmojiPadRowsPerPage * st::emojiPanSize.height() - int(st::emojiPanSub));
 | 
				
			||||||
	_emojiAnimations.clear();
 | 
						_emojiAnimations.clear();
 | 
				
			||||||
	_selected = _pressedSel = -1;
 | 
						_selected = _pressedSel = -1;
 | 
				
			||||||
	int32 size = _emojis.size();
 | 
					 | 
				
			||||||
	int32 h = qMax(((size / EmojiPadPerRow) + ((size % EmojiPadPerRow) ? 1 : 0)) * st::emojiPanSize.height(), EmojiPadRowsPerPage * st::emojiPanSize.height() - int(st::emojiPanSub));
 | 
					 | 
				
			||||||
	resize(width(), h);
 | 
						resize(width(), h);
 | 
				
			||||||
	_lastMousePos = QCursor::pos();
 | 
						_lastMousePos = QCursor::pos();
 | 
				
			||||||
	updateSelected();
 | 
						updateSelected();
 | 
				
			||||||
| 
						 | 
					@ -483,15 +637,18 @@ _hiding(false), a_opacity(0), _shadow(st::dropdownShadow),
 | 
				
			||||||
_recent  (this, qsl("emoji_group"), dbietRecent  , QString(), cEmojiTab() == dbietRecent  , st::rbEmojiRecent),
 | 
					_recent  (this, qsl("emoji_group"), dbietRecent  , QString(), cEmojiTab() == dbietRecent  , st::rbEmojiRecent),
 | 
				
			||||||
_people  (this, qsl("emoji_group"), dbietPeople  , QString(), cEmojiTab() == dbietPeople  , st::rbEmojiPeople),
 | 
					_people  (this, qsl("emoji_group"), dbietPeople  , QString(), cEmojiTab() == dbietPeople  , st::rbEmojiPeople),
 | 
				
			||||||
_nature  (this, qsl("emoji_group"), dbietNature  , QString(), cEmojiTab() == dbietNature  , st::rbEmojiNature),
 | 
					_nature  (this, qsl("emoji_group"), dbietNature  , QString(), cEmojiTab() == dbietNature  , st::rbEmojiNature),
 | 
				
			||||||
_objects(this, qsl("emoji_group"), dbietObjects, QString(), cEmojiTab() == dbietObjects, st::rbEmojiObjects),
 | 
					_objects (this, qsl("emoji_group"), dbietObjects , QString(), cEmojiTab() == dbietObjects , st::rbEmojiObjects),
 | 
				
			||||||
_places  (this, qsl("emoji_group"), dbietPlaces  , QString(), cEmojiTab() == dbietPlaces  , st::rbEmojiPlaces),
 | 
					_places  (this, qsl("emoji_group"), dbietPlaces  , QString(), cEmojiTab() == dbietPlaces  , st::rbEmojiPlaces),
 | 
				
			||||||
_symbols(this, qsl("emoji_group"), dbietSymbols, QString(), cEmojiTab() == dbietSymbols, st::rbEmojiSymbols),
 | 
					_symbols (this, qsl("emoji_group"), dbietSymbols , QString(), cEmojiTab() == dbietSymbols , st::rbEmojiSymbols),
 | 
				
			||||||
 | 
					_stickers(this, qsl("emoji_group"), dbietStickers, QString(), cEmojiTab() == dbietStickers, st::rbEmojiStickers),
 | 
				
			||||||
_scroll(this, st::emojiScroll), _inner() {
 | 
					_scroll(this, st::emojiScroll), _inner() {
 | 
				
			||||||
	setFocusPolicy(Qt::NoFocus);
 | 
						setFocusPolicy(Qt::NoFocus);
 | 
				
			||||||
	_scroll.setFocusPolicy(Qt::NoFocus);
 | 
						_scroll.setFocusPolicy(Qt::NoFocus);
 | 
				
			||||||
	_scroll.viewport()->setFocusPolicy(Qt::NoFocus);
 | 
						_scroll.viewport()->setFocusPolicy(Qt::NoFocus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cEmojiTab() != dbietStickers) {
 | 
				
			||||||
		_inner.showEmojiPack(cEmojiTab());
 | 
							_inner.showEmojiPack(cEmojiTab());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_scroll.setGeometry(st::dropdownPadding.left() + st::emojiPanPadding.left(), st::dropdownPadding.top() + _recent.height() + st::emojiPanPadding.top(), st::emojiPanPadding.left() + _inner.width() + st::emojiPanPadding.right(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
 | 
						_scroll.setGeometry(st::dropdownPadding.left() + st::emojiPanPadding.left(), st::dropdownPadding.top() + _recent.height() + st::emojiPanPadding.top(), st::emojiPanPadding.left() + _inner.width() + st::emojiPanPadding.right(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
 | 
				
			||||||
	_scroll.setWidget(&_inner);
 | 
						_scroll.setWidget(&_inner);
 | 
				
			||||||
| 
						 | 
					@ -500,7 +657,7 @@ _scroll(this, st::emojiScroll), _inner() {
 | 
				
			||||||
	_height = st::dropdownPadding.top() + _recent.height() + st::emojiPanPadding.top() + _scroll.height() + st::emojiPanPadding.bottom() + st::dropdownPadding.bottom();
 | 
						_height = st::dropdownPadding.top() + _recent.height() + st::emojiPanPadding.top() + _scroll.height() + st::emojiPanPadding.bottom() + st::dropdownPadding.bottom();
 | 
				
			||||||
	resize(_width, _height);
 | 
						resize(_width, _height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 left = st::dropdownPadding.left() + (_width - st::dropdownPadding.left() - st::dropdownPadding.right() - 6 * _recent.width()) / 2;
 | 
						int32 left = st::dropdownPadding.left() + (_width - st::dropdownPadding.left() - st::dropdownPadding.right() - 7 * _recent.width()) / 2;
 | 
				
			||||||
	int32 top = st::dropdownPadding.top();
 | 
						int32 top = st::dropdownPadding.top();
 | 
				
			||||||
	_recent.move(left, top);  left += _recent.width();
 | 
						_recent.move(left, top);  left += _recent.width();
 | 
				
			||||||
	_people.move(left, top);  left += _people.width();
 | 
						_people.move(left, top);  left += _people.width();
 | 
				
			||||||
| 
						 | 
					@ -508,6 +665,7 @@ _scroll(this, st::emojiScroll), _inner() {
 | 
				
			||||||
	_objects.move(left, top); left += _objects.width();
 | 
						_objects.move(left, top); left += _objects.width();
 | 
				
			||||||
	_places.move(left, top);  left += _places.width();
 | 
						_places.move(left, top);  left += _places.width();
 | 
				
			||||||
	_symbols.move(left, top); left += _symbols.width();
 | 
						_symbols.move(left, top); left += _symbols.width();
 | 
				
			||||||
 | 
						_stickers.move(left, top); left += _stickers.width();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_hideTimer.setSingleShot(true);
 | 
						_hideTimer.setSingleShot(true);
 | 
				
			||||||
	connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
 | 
						connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
 | 
				
			||||||
| 
						 | 
					@ -515,13 +673,15 @@ _scroll(this, st::emojiScroll), _inner() {
 | 
				
			||||||
	connect(&_recent  , SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
						connect(&_recent  , SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
				
			||||||
	connect(&_people  , SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
						connect(&_people  , SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
				
			||||||
	connect(&_nature  , SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
						connect(&_nature  , SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
				
			||||||
	connect(&_objects, SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
						connect(&_objects , SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
				
			||||||
	connect(&_places  , SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
						connect(&_places  , SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
				
			||||||
	connect(&_symbols, SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
						connect(&_symbols , SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
				
			||||||
 | 
						connect(&_stickers, SIGNAL(changed()), this, SLOT(onTabChange()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(updateSelected()));
 | 
						connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(updateSelected()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	connect(&_inner, SIGNAL(emojiSelected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr)));
 | 
						connect(&_inner, SIGNAL(emojiSelected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr)));
 | 
				
			||||||
 | 
						connect(&_inner, SIGNAL(stickerSelected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*)));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EmojiPan::paintEvent(QPaintEvent *e) {
 | 
					void EmojiPan::paintEvent(QPaintEvent *e) {
 | 
				
			||||||
| 
						 | 
					@ -627,14 +787,23 @@ void EmojiPan::showStart() {
 | 
				
			||||||
	show();
 | 
						show();
 | 
				
			||||||
	a_opacity.start(1);
 | 
						a_opacity.start(1);
 | 
				
			||||||
	anim::start(this);
 | 
						anim::start(this);
 | 
				
			||||||
 | 
						if (_stickers.checked()) emit updateStickers();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool EmojiPan::eventFilter(QObject *obj, QEvent *e) {
 | 
					bool EmojiPan::eventFilter(QObject *obj, QEvent *e) {
 | 
				
			||||||
	if (e->type() == QEvent::Enter) {
 | 
						if (e->type() == QEvent::Enter) {
 | 
				
			||||||
 | 
							//if (dynamic_cast<StickerPan*>(obj)) {
 | 
				
			||||||
 | 
							//	enterEvent(e);
 | 
				
			||||||
 | 
							//} else {
 | 
				
			||||||
			otherEnter();
 | 
								otherEnter();
 | 
				
			||||||
 | 
							//}
 | 
				
			||||||
	} else if (e->type() == QEvent::Leave) {
 | 
						} else if (e->type() == QEvent::Leave) {
 | 
				
			||||||
 | 
							//if (dynamic_cast<StickerPan*>(obj)) {
 | 
				
			||||||
 | 
							//	leaveEvent(e);
 | 
				
			||||||
 | 
							//} else {
 | 
				
			||||||
			otherLeave();
 | 
								otherLeave();
 | 
				
			||||||
	} else if (e->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton) {
 | 
							//}
 | 
				
			||||||
 | 
						} else if (e->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton/* && !dynamic_cast<StickerPan*>(obj)*/) {
 | 
				
			||||||
		if (isHidden() || _hiding) {
 | 
							if (isHidden() || _hiding) {
 | 
				
			||||||
			otherEnter();
 | 
								otherEnter();
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
| 
						 | 
					@ -651,6 +820,7 @@ void EmojiPan::showAll() {
 | 
				
			||||||
	_objects.show();
 | 
						_objects.show();
 | 
				
			||||||
	_places.show();
 | 
						_places.show();
 | 
				
			||||||
	_symbols.show();
 | 
						_symbols.show();
 | 
				
			||||||
 | 
						_stickers.show();
 | 
				
			||||||
	_scroll.show();
 | 
						_scroll.show();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -661,7 +831,9 @@ void EmojiPan::hideAll() {
 | 
				
			||||||
	_objects.hide();
 | 
						_objects.hide();
 | 
				
			||||||
	_places.hide();
 | 
						_places.hide();
 | 
				
			||||||
	_symbols.hide();
 | 
						_symbols.hide();
 | 
				
			||||||
 | 
						_stickers.hide();
 | 
				
			||||||
	_scroll.hide();
 | 
						_scroll.hide();
 | 
				
			||||||
 | 
						_inner.clearSelection(true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EmojiPan::onTabChange() {
 | 
					void EmojiPan::onTabChange() {
 | 
				
			||||||
| 
						 | 
					@ -671,10 +843,341 @@ void EmojiPan::onTabChange() {
 | 
				
			||||||
	else if (_objects.checked()) newTab = dbietObjects;
 | 
						else if (_objects.checked()) newTab = dbietObjects;
 | 
				
			||||||
	else if (_places.checked()) newTab = dbietPlaces;
 | 
						else if (_places.checked()) newTab = dbietPlaces;
 | 
				
			||||||
	else if (_symbols.checked()) newTab = dbietSymbols;
 | 
						else if (_symbols.checked()) newTab = dbietSymbols;
 | 
				
			||||||
 | 
						else if (_stickers.checked()) newTab = dbietStickers;
 | 
				
			||||||
	if (newTab != cEmojiTab()) {
 | 
						if (newTab != cEmojiTab()) {
 | 
				
			||||||
		cSetEmojiTab(newTab);
 | 
							cSetEmojiTab(newTab);
 | 
				
			||||||
		App::writeUserConfig();
 | 
							App::writeUserConfig();
 | 
				
			||||||
		_scroll.scrollToY(0);
 | 
							_scroll.scrollToY(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	_inner.showEmojiPack(newTab);
 | 
						_inner.showEmojiPack(newTab);
 | 
				
			||||||
 | 
						if (newTab == dbietStickers) {
 | 
				
			||||||
 | 
							emit updateStickers();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//StickerPanInner::StickerPanInner(QWidget *parent) : QWidget(parent), _emoji(0), _selected(-1), _pressedSel(-1) {
 | 
				
			||||||
 | 
					//	resize(StickerPadPerRow * st::stickerPanSize.width(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
 | 
				
			||||||
 | 
					//	setMouseTracking(true);
 | 
				
			||||||
 | 
					//	setFocusPolicy(Qt::NoFocus);
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPanInner::paintEvent(QPaintEvent *e) {
 | 
				
			||||||
 | 
					//	QPainter p(this);
 | 
				
			||||||
 | 
					//	int32 size = _stickers.size();
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	QRect r = e ? e->rect() : rect();
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	int32 rows = (size / StickerPadPerRow) + ((size % StickerPadPerRow) ? 1 : 0);
 | 
				
			||||||
 | 
					//	int32 fromrow = qMax(qFloor(r.top() / st::stickerPanSize.height()), 0), torow = qMin(qCeil(r.bottom() / st::stickerPanSize.height()) + 1, rows);
 | 
				
			||||||
 | 
					//	for (int32 i = fromrow; i < torow; ++i) {
 | 
				
			||||||
 | 
					//		for (int32 j = 0; j < StickerPadPerRow; ++j) {
 | 
				
			||||||
 | 
					//			int32 index = i * StickerPadPerRow + j;
 | 
				
			||||||
 | 
					//			if (index >= size) break;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//			float64 hover = _hovers[index];
 | 
				
			||||||
 | 
					//			QPoint pos(j * st::stickerPanSize.width(), i * st::stickerPanSize.height());
 | 
				
			||||||
 | 
					//			if (hover > 0) {
 | 
				
			||||||
 | 
					//				p.setOpacity(hover);
 | 
				
			||||||
 | 
					//				p.setBrush(st::stickerPanHover->b);
 | 
				
			||||||
 | 
					//				p.setPen(Qt::NoPen);
 | 
				
			||||||
 | 
					//				p.drawRoundedRect(QRect(pos, st::stickerPanSize), st::stickerPanRound, st::stickerPanRound);
 | 
				
			||||||
 | 
					//				p.setOpacity(1);
 | 
				
			||||||
 | 
					//			}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//			DocumentData *data = _stickers[index];
 | 
				
			||||||
 | 
					//			bool already = !data->already().isEmpty(), hasdata = !data->data.isEmpty();
 | 
				
			||||||
 | 
					//			if (!data->loader && data->status != FileFailed && !already && !hasdata) {
 | 
				
			||||||
 | 
					//				data->save(QString());
 | 
				
			||||||
 | 
					//			}
 | 
				
			||||||
 | 
					//			if (data->sticker->isNull() && (already || hasdata)) {
 | 
				
			||||||
 | 
					//				if (already) {
 | 
				
			||||||
 | 
					//					data->sticker = ImagePtr(data->already());
 | 
				
			||||||
 | 
					//				} else {
 | 
				
			||||||
 | 
					//					data->sticker = ImagePtr(data->data);
 | 
				
			||||||
 | 
					//				}
 | 
				
			||||||
 | 
					//			}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//			float64 coef = qMin(st::stickerPanSize.width() / float64(data->dimensions.width()), st::stickerPanSize.height() / float64(data->dimensions.height()));
 | 
				
			||||||
 | 
					//			int32 w = qRound(coef * data->dimensions.width()), h = qRound(coef * data->dimensions.height());
 | 
				
			||||||
 | 
					//			pos += QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//			if (data->sticker->isNull()) {
 | 
				
			||||||
 | 
					//				p.drawPixmap(pos, data->thumb->pix(w));
 | 
				
			||||||
 | 
					//			} else {
 | 
				
			||||||
 | 
					//				p.drawPixmap(pos, data->sticker->pix(w));
 | 
				
			||||||
 | 
					//			}
 | 
				
			||||||
 | 
					//		}
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPanInner::mousePressEvent(QMouseEvent *e) {
 | 
				
			||||||
 | 
					//	_lastMousePos = e->globalPos();
 | 
				
			||||||
 | 
					//	updateSelected();
 | 
				
			||||||
 | 
					//	_pressedSel = _selected;
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
 | 
				
			||||||
 | 
					//	_lastMousePos = e->globalPos();
 | 
				
			||||||
 | 
					//	updateSelected();
 | 
				
			||||||
 | 
					//	if (_selected == _pressedSel && _selected >= 0 && _selected < _stickers.size()) {
 | 
				
			||||||
 | 
					//		emit stickerSelected(_stickers[_selected]);
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPanInner::mouseMoveEvent(QMouseEvent *e) {
 | 
				
			||||||
 | 
					//	_lastMousePos = e->globalPos();
 | 
				
			||||||
 | 
					//	updateSelected();
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPanInner::leaveEvent(QEvent *e) {
 | 
				
			||||||
 | 
					//	_lastMousePos = QCursor::pos();
 | 
				
			||||||
 | 
					//	updateSelected();
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPanInner::updateSelected() {
 | 
				
			||||||
 | 
					//	int32 selIndex = -1;
 | 
				
			||||||
 | 
					//	QPoint p(mapFromGlobal(_lastMousePos));
 | 
				
			||||||
 | 
					//	if (p.x() >= 0 && p.y() >= 0 && p.x() < StickerPadPerRow * st::stickerPanSize.width()) {
 | 
				
			||||||
 | 
					//		selIndex = qFloor(p.y() / st::stickerPanSize.height()) * StickerPadPerRow + qFloor(p.x() / st::stickerPanSize.width());
 | 
				
			||||||
 | 
					//		if (selIndex >= _stickers.size()) {
 | 
				
			||||||
 | 
					//			selIndex = -1;
 | 
				
			||||||
 | 
					//		}
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//	if (selIndex != _selected) {
 | 
				
			||||||
 | 
					//		bool startanim = false;
 | 
				
			||||||
 | 
					//		if (_selected >= 0) {
 | 
				
			||||||
 | 
					//			_stickerAnimations.remove(_selected + 1);
 | 
				
			||||||
 | 
					//			if (_stickerAnimations.find(-_selected - 1) == _stickerAnimations.end()) {
 | 
				
			||||||
 | 
					//				if (_stickerAnimations.isEmpty()) startanim = true;
 | 
				
			||||||
 | 
					//				_stickerAnimations.insert(-_selected - 1, getms());
 | 
				
			||||||
 | 
					//			}
 | 
				
			||||||
 | 
					//		}
 | 
				
			||||||
 | 
					//		_selected = selIndex;
 | 
				
			||||||
 | 
					//		if (_selected >= 0) {
 | 
				
			||||||
 | 
					//			_stickerAnimations.remove(-_selected - 1);
 | 
				
			||||||
 | 
					//			if (_stickerAnimations.find(_selected + 1) == _stickerAnimations.end()) {
 | 
				
			||||||
 | 
					//				if (_stickerAnimations.isEmpty()) startanim = true;
 | 
				
			||||||
 | 
					//				_stickerAnimations.insert(_selected + 1, getms());
 | 
				
			||||||
 | 
					//			}
 | 
				
			||||||
 | 
					//		}
 | 
				
			||||||
 | 
					//		if (startanim) anim::start(this);
 | 
				
			||||||
 | 
					//		setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default);
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//bool StickerPanInner::animStep(float64 ms) {
 | 
				
			||||||
 | 
					//	uint64 now = getms();
 | 
				
			||||||
 | 
					//	for (StickerAnimations::iterator i = _stickerAnimations.begin(); i != _stickerAnimations.end();) {
 | 
				
			||||||
 | 
					//		float64 dt = float64(now - i.value()) / st::emojiPanDuration;
 | 
				
			||||||
 | 
					//		if (dt >= 1) {
 | 
				
			||||||
 | 
					//			_hovers[qAbs(i.key()) - 1] = (i.key() > 0) ? 1 : 0;
 | 
				
			||||||
 | 
					//			i = _stickerAnimations.erase(i);
 | 
				
			||||||
 | 
					//		} else {
 | 
				
			||||||
 | 
					//			_hovers[qAbs(i.key()) - 1] = (i.key() > 0) ? dt : (1 - dt);
 | 
				
			||||||
 | 
					//			++i;
 | 
				
			||||||
 | 
					//		}
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//	update();
 | 
				
			||||||
 | 
					//	return !_stickerAnimations.isEmpty();
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPanInner::showStickerPack(EmojiPtr emoji) {
 | 
				
			||||||
 | 
					//	StickerPack stickers = cStickers().value(emoji);
 | 
				
			||||||
 | 
					//	if (stickers.isEmpty()) {
 | 
				
			||||||
 | 
					//		_emoji = 0;
 | 
				
			||||||
 | 
					//	} else {
 | 
				
			||||||
 | 
					//		_emoji = emoji;
 | 
				
			||||||
 | 
					//		_stickers = stickers;
 | 
				
			||||||
 | 
					//		_hovers = QVector<float64>(_stickers.size(), 0);
 | 
				
			||||||
 | 
					//		_stickerAnimations.clear();
 | 
				
			||||||
 | 
					//		_selected = _pressedSel = -1;
 | 
				
			||||||
 | 
					//		int32 size = _stickers.size();
 | 
				
			||||||
 | 
					//		int32 h = qMax(((size / StickerPadPerRow) + ((size % StickerPadPerRow) ? 1 : 0)) * st::stickerPanSize.height(), EmojiPadRowsPerPage * st::emojiPanSize.height() - int(st::emojiPanSub));
 | 
				
			||||||
 | 
					//		resize(width(), h);
 | 
				
			||||||
 | 
					//		_lastMousePos = QCursor::pos();
 | 
				
			||||||
 | 
					//		updateSelected();
 | 
				
			||||||
 | 
					//		update();
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//bool StickerPanInner::hasContent() const {
 | 
				
			||||||
 | 
					//	return !!_emoji;
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//StickerPan::StickerPan(QWidget *parent) : TWidget(parent),
 | 
				
			||||||
 | 
					//_hiding(false), a_opacity(0), _shadow(st::dropdownShadow),
 | 
				
			||||||
 | 
					//_scroll(this, st::emojiScroll), _emoji(0), _inner() {
 | 
				
			||||||
 | 
					//	setFocusPolicy(Qt::NoFocus);
 | 
				
			||||||
 | 
					//	_scroll.setFocusPolicy(Qt::NoFocus);
 | 
				
			||||||
 | 
					//	_scroll.viewport()->setFocusPolicy(Qt::NoFocus);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	_inner.showStickerPack(0);
 | 
				
			||||||
 | 
					//	_scroll.setGeometry(st::dropdownPadding.left() + st::stickerPanPadding.left(), st::dropdownPadding.top() + st::rbEmoji.height + st::stickerPanPadding.top(), st::stickerPanPadding.left() + _inner.width() + st::stickerPanPadding.right(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub);
 | 
				
			||||||
 | 
					//	_scroll.setWidget(&_inner);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	_width = st::dropdownPadding.left() + st::stickerPanPadding.left() + _scroll.width() + st::stickerPanPadding.right() + st::dropdownPadding.right();
 | 
				
			||||||
 | 
					//	_height = st::dropdownPadding.top() + st::rbEmoji.height + st::stickerPanPadding.top() + _scroll.height() + st::stickerPanPadding.bottom() + st::dropdownPadding.bottom();
 | 
				
			||||||
 | 
					//	resize(_width, _height);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	_hideTimer.setSingleShot(true);
 | 
				
			||||||
 | 
					//	connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(updateSelected()));
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	connect(&_inner, SIGNAL(stickerSelected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*)));
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::setStickerPack(EmojiPtr emoji, bool show) {
 | 
				
			||||||
 | 
					//	_emoji = emoji;
 | 
				
			||||||
 | 
					//	_inner.showStickerPack(_emoji);
 | 
				
			||||||
 | 
					//	if (!_hiding && !isHidden() && !_inner.hasContent()) {
 | 
				
			||||||
 | 
					//		_hideTimer.stop();
 | 
				
			||||||
 | 
					//		hideStart();
 | 
				
			||||||
 | 
					//	} else if ((_hiding || isHidden()) && _inner.hasContent() && show) {
 | 
				
			||||||
 | 
					//		_hideTimer.stop();
 | 
				
			||||||
 | 
					//		showStart();
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::paintEvent(QPaintEvent *e) {
 | 
				
			||||||
 | 
					//	QPainter p(this);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	if (!_cache.isNull()) {
 | 
				
			||||||
 | 
					//		p.setOpacity(a_opacity.current());
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	QRect r(st::dropdownPadding.left(), st::dropdownPadding.top(), _width - st::dropdownPadding.left() - st::dropdownPadding.right(), _height - st::dropdownPadding.top() - st::dropdownPadding.bottom());
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	// draw shadow
 | 
				
			||||||
 | 
					//	_shadow.paint(p, r);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	if (_cache.isNull()) {
 | 
				
			||||||
 | 
					//		p.fillRect(r, st::white->b);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//		p.setFont(st::stickerPanFont->f);
 | 
				
			||||||
 | 
					//		p.setPen(st::stickerPanColor->p);
 | 
				
			||||||
 | 
					//		p.drawText(QRect(st::dropdownPadding.left(), st::dropdownPadding.top(), width() - st::dropdownPadding.left() - st::dropdownPadding.right(), st::rbEmoji.height), lang(lng_attach_stickers_header), style::al_center);
 | 
				
			||||||
 | 
					//	} else {
 | 
				
			||||||
 | 
					//		p.drawPixmap(r.left(), r.top(), _cache);
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::enterEvent(QEvent *e) {
 | 
				
			||||||
 | 
					//	_hideTimer.stop();
 | 
				
			||||||
 | 
					//	if (_hiding) showStart();
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::leaveEvent(QEvent *e) {
 | 
				
			||||||
 | 
					//	if (animating()) {
 | 
				
			||||||
 | 
					//		hideStart();
 | 
				
			||||||
 | 
					//	} else {
 | 
				
			||||||
 | 
					//		_hideTimer.start(300);
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::otherEnter() {
 | 
				
			||||||
 | 
					//	_hideTimer.stop();
 | 
				
			||||||
 | 
					//	showStart();
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::otherLeave() {
 | 
				
			||||||
 | 
					//	if (animating()) {
 | 
				
			||||||
 | 
					//		hideStart();
 | 
				
			||||||
 | 
					//	} else {
 | 
				
			||||||
 | 
					//		_hideTimer.start(0);
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::fastHide() {
 | 
				
			||||||
 | 
					//	if (animating()) {
 | 
				
			||||||
 | 
					//		anim::stop(this);
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//	a_opacity = anim::fvalue(0, 0);
 | 
				
			||||||
 | 
					//	_hideTimer.stop();
 | 
				
			||||||
 | 
					//	hide();
 | 
				
			||||||
 | 
					//	_cache = QPixmap();
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//bool StickerPan::animStep(float64 ms) {
 | 
				
			||||||
 | 
					//	float64 dt = ms / 150;
 | 
				
			||||||
 | 
					//	bool res = true;
 | 
				
			||||||
 | 
					//	if (dt >= 1) {
 | 
				
			||||||
 | 
					//		a_opacity.finish();
 | 
				
			||||||
 | 
					//		if (_hiding) {
 | 
				
			||||||
 | 
					//			hideFinish();
 | 
				
			||||||
 | 
					//		} else {
 | 
				
			||||||
 | 
					//			showAll();
 | 
				
			||||||
 | 
					//			_cache = QPixmap();
 | 
				
			||||||
 | 
					//		}
 | 
				
			||||||
 | 
					//		res = false;
 | 
				
			||||||
 | 
					//	} else {
 | 
				
			||||||
 | 
					//		a_opacity.update(dt, anim::linear);
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//	update();
 | 
				
			||||||
 | 
					//	return res;
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::hideStart() {
 | 
				
			||||||
 | 
					//	if (_cache.isNull()) {
 | 
				
			||||||
 | 
					//		showAll();
 | 
				
			||||||
 | 
					//		_cache = myGrab(this, rect().marginsRemoved(st::dropdownPadding));
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//	hideAll();
 | 
				
			||||||
 | 
					//	_hiding = true;
 | 
				
			||||||
 | 
					//	a_opacity.start(0);
 | 
				
			||||||
 | 
					//	anim::start(this);
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::hideFinish() {
 | 
				
			||||||
 | 
					//	hide();
 | 
				
			||||||
 | 
					//	_cache = QPixmap();
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::showStart() {
 | 
				
			||||||
 | 
					//	if (!isHidden() && a_opacity.current() == 1) {
 | 
				
			||||||
 | 
					//		return;
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//	if (!_inner.hasContent()) {
 | 
				
			||||||
 | 
					//		return;
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//	if (_cache.isNull()) {
 | 
				
			||||||
 | 
					//		showAll();
 | 
				
			||||||
 | 
					//		_cache = myGrab(this, rect().marginsRemoved(st::dropdownPadding));
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//	hideAll();
 | 
				
			||||||
 | 
					//	_hiding = false;
 | 
				
			||||||
 | 
					//	show();
 | 
				
			||||||
 | 
					//	a_opacity.start(1);
 | 
				
			||||||
 | 
					//	anim::start(this);
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//bool StickerPan::eventFilter(QObject *obj, QEvent *e) {
 | 
				
			||||||
 | 
					//	if (e->type() == QEvent::Enter) {
 | 
				
			||||||
 | 
					//		if (dynamic_cast<EmojiPan*>(obj)) {
 | 
				
			||||||
 | 
					//			enterEvent(e);
 | 
				
			||||||
 | 
					//		} else {
 | 
				
			||||||
 | 
					//			otherEnter();
 | 
				
			||||||
 | 
					//		}
 | 
				
			||||||
 | 
					//	} else if (e->type() == QEvent::Leave) {
 | 
				
			||||||
 | 
					//		if (dynamic_cast<EmojiPan*>(obj)) {
 | 
				
			||||||
 | 
					//			leaveEvent(e);
 | 
				
			||||||
 | 
					//		} else {
 | 
				
			||||||
 | 
					//			otherLeave();
 | 
				
			||||||
 | 
					//		}
 | 
				
			||||||
 | 
					//	} else if (e->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton && !dynamic_cast<EmojiPan*>(obj)) {
 | 
				
			||||||
 | 
					//		if (isHidden() || _hiding) {
 | 
				
			||||||
 | 
					//			otherEnter();
 | 
				
			||||||
 | 
					//		} else {
 | 
				
			||||||
 | 
					//			otherLeave();
 | 
				
			||||||
 | 
					//		}
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//	return false;
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::showAll() {
 | 
				
			||||||
 | 
					//	_scroll.show();
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//void StickerPan::hideAll() {
 | 
				
			||||||
 | 
					//	_scroll.hide();
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -134,6 +134,8 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void showEmojiPack(DBIEmojiTab packIndex);
 | 
						void showEmojiPack(DBIEmojiTab packIndex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void clearSelection(bool fast = false);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
public slots:
 | 
					public slots:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void updateSelected();
 | 
						void updateSelected();
 | 
				
			||||||
| 
						 | 
					@ -142,17 +144,20 @@ public slots:
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void emojiSelected(EmojiPtr emoji);
 | 
						void emojiSelected(EmojiPtr emoji);
 | 
				
			||||||
 | 
						void stickerSelected(DocumentData *sticker);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	typedef QMap<int32, uint64> EmojiAnimations; // index - showing, -index - hiding
 | 
						typedef QMap<int32, uint64> EmojiAnimations; // index - showing, -index - hiding
 | 
				
			||||||
	EmojiAnimations _emojiAnimations;
 | 
						EmojiAnimations _emojiAnimations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						StickerPack _stickers;
 | 
				
			||||||
 | 
						QVector<bool> _isUserGen;
 | 
				
			||||||
	QVector<EmojiPtr> _emojis;
 | 
						QVector<EmojiPtr> _emojis;
 | 
				
			||||||
	QVector<float64> _hovers;
 | 
						QVector<float64> _hovers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DBIEmojiTab _tab;
 | 
						DBIEmojiTab _tab;
 | 
				
			||||||
	int32 _selected, _pressedSel;
 | 
						int32 _selected, _xSelected, _pressedSel, _xPressedSel;
 | 
				
			||||||
	QPoint _lastMousePos;
 | 
						QPoint _lastMousePos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QTimer _saveConfigTimer;
 | 
						QTimer _saveConfigTimer;
 | 
				
			||||||
| 
						 | 
					@ -174,6 +179,9 @@ public:
 | 
				
			||||||
	void otherLeave();
 | 
						void otherLeave();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void fastHide();
 | 
						void fastHide();
 | 
				
			||||||
 | 
						bool hiding() const {
 | 
				
			||||||
 | 
							return _hiding || _hideTimer.isActive();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool animStep(float64 ms);
 | 
						bool animStep(float64 ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -191,6 +199,8 @@ public slots:
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void emojiSelected(EmojiPtr emoji);
 | 
						void emojiSelected(EmojiPtr emoji);
 | 
				
			||||||
 | 
						void stickerSelected(DocumentData *sticker);
 | 
				
			||||||
 | 
						void updateStickers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,10 +217,104 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BoxShadow _shadow;
 | 
						BoxShadow _shadow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	FlatRadiobutton _recent, _people, _nature, _objects, _places, _symbols;
 | 
						FlatRadiobutton _recent, _people, _nature, _objects, _places, _symbols, _stickers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 _emojiPack;
 | 
						int32 _emojiPack;
 | 
				
			||||||
	ScrollArea _scroll;
 | 
						ScrollArea _scroll;
 | 
				
			||||||
	EmojiPanInner _inner;
 | 
						EmojiPanInner _inner;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//class StickerPanInner : public QWidget, public Animated {
 | 
				
			||||||
 | 
					//	Q_OBJECT
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//public:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	StickerPanInner(QWidget *parent = 0);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void paintEvent(QPaintEvent *e);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void mousePressEvent(QMouseEvent *e);
 | 
				
			||||||
 | 
					//	void mouseReleaseEvent(QMouseEvent *e);
 | 
				
			||||||
 | 
					//	void mouseMoveEvent(QMouseEvent *e);
 | 
				
			||||||
 | 
					//	void leaveEvent(QEvent *e);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	bool animStep(float64 ms);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void showStickerPack(EmojiPtr emoji);
 | 
				
			||||||
 | 
					//	bool hasContent() const;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//public slots:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void updateSelected();
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//signals:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void stickerSelected(DocumentData *sticker);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//private:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	typedef QMap<int32, uint64> StickerAnimations; // index - showing, -index - hiding
 | 
				
			||||||
 | 
					//	StickerAnimations _stickerAnimations;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	StickerPack _stickers;
 | 
				
			||||||
 | 
					//	QVector<float64> _hovers;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	EmojiPtr _emoji;
 | 
				
			||||||
 | 
					//	int32 _selected, _pressedSel;
 | 
				
			||||||
 | 
					//	QPoint _lastMousePos;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//};
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//class StickerPan : public TWidget, public Animated {
 | 
				
			||||||
 | 
					//	Q_OBJECT
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//public:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	StickerPan(QWidget *parent);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void setStickerPack(EmojiPtr emoji, bool show);
 | 
				
			||||||
 | 
					//	void paintEvent(QPaintEvent *e);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void enterEvent(QEvent *e);
 | 
				
			||||||
 | 
					//	void leaveEvent(QEvent *e);
 | 
				
			||||||
 | 
					//	void otherEnter();
 | 
				
			||||||
 | 
					//	void otherLeave();
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void fastHide();
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	bool animStep(float64 ms);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	bool eventFilter(QObject *obj, QEvent *e);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//public slots:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void hideStart();
 | 
				
			||||||
 | 
					//	void hideFinish();
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void showStart();
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//signals:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void stickerSelected(DocumentData *sticker);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//private:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	void showAll();
 | 
				
			||||||
 | 
					//	void hideAll();
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	int32 _width, _height;
 | 
				
			||||||
 | 
					//	bool _hiding;
 | 
				
			||||||
 | 
					//	QPixmap _cache;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	anim::fvalue a_opacity;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	QTimer _hideTimer;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	BoxShadow _shadow;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	EmojiPtr _emoji;
 | 
				
			||||||
 | 
					//	ScrollArea _scroll;
 | 
				
			||||||
 | 
					//	StickerPanInner _inner;
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,9 +32,9 @@ void FileUploader::uploadMedia(MsgId msgId, const ReadyLocalMedia &media) {
 | 
				
			||||||
	} else if (media.type == ToPrepareDocument) {
 | 
						} else if (media.type == ToPrepareDocument) {
 | 
				
			||||||
		DocumentData *document;
 | 
							DocumentData *document;
 | 
				
			||||||
		if (media.photoThumbs.isEmpty()) {
 | 
							if (media.photoThumbs.isEmpty()) {
 | 
				
			||||||
			document = App::feedDocument(MTP::authedId(), media.document);
 | 
								document = App::feedDocument(media.document);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			document = App::feedDocument(MTP::authedId(), media.document, media.photoThumbs.begin().value());
 | 
								document = App::feedDocument(media.document, media.photoThumbs.begin().value());
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		document->status = FileUploading;
 | 
							document->status = FileUploading;
 | 
				
			||||||
		if (!media.file.isEmpty()) {
 | 
							if (!media.file.isEmpty()) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4512,6 +4512,71 @@ void findEmoji(const QChar *ch, const QChar *e, const QChar *&newEmojiEnd, uint3
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
							case 'k':
 | 
				
			||||||
 | 
								if (ch + 2 != e) switch ((ch + 2)->unicode()) {
 | 
				
			||||||
 | 
								case 'i':
 | 
				
			||||||
 | 
									if (ch + 3 != e) switch ((ch + 3)->unicode()) {
 | 
				
			||||||
 | 
									case 's':
 | 
				
			||||||
 | 
										if (ch + 4 != e) switch ((ch + 4)->unicode()) {
 | 
				
			||||||
 | 
										case 's':
 | 
				
			||||||
 | 
											if (ch + 5 != e) switch ((ch + 5)->unicode()) {
 | 
				
			||||||
 | 
											case ':':
 | 
				
			||||||
 | 
												newEmojiEnd = ch + 6;
 | 
				
			||||||
 | 
												if (newEmojiEnd == e || emojiEdge(newEmojiEnd) || newEmojiEnd->unicode() == ' ') {
 | 
				
			||||||
 | 
													emojiCode = 0xD83DDC8BU;
 | 
				
			||||||
 | 
													return;
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
							case 'j':
 | 
				
			||||||
 | 
								if (ch + 2 != e) switch ((ch + 2)->unicode()) {
 | 
				
			||||||
 | 
								case 'o':
 | 
				
			||||||
 | 
									if (ch + 3 != e) switch ((ch + 3)->unicode()) {
 | 
				
			||||||
 | 
									case 'y':
 | 
				
			||||||
 | 
										if (ch + 4 != e) switch ((ch + 4)->unicode()) {
 | 
				
			||||||
 | 
										case ':':
 | 
				
			||||||
 | 
											newEmojiEnd = ch + 5;
 | 
				
			||||||
 | 
											if (newEmojiEnd == e || emojiEdge(newEmojiEnd) || newEmojiEnd->unicode() == ' ') {
 | 
				
			||||||
 | 
												emojiCode = 0xD83DDE02U;
 | 
				
			||||||
 | 
												return;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
							case 'g':
 | 
				
			||||||
 | 
								if (ch + 2 != e) switch ((ch + 2)->unicode()) {
 | 
				
			||||||
 | 
								case 'r':
 | 
				
			||||||
 | 
									if (ch + 3 != e) switch ((ch + 3)->unicode()) {
 | 
				
			||||||
 | 
									case 'i':
 | 
				
			||||||
 | 
										if (ch + 4 != e) switch ((ch + 4)->unicode()) {
 | 
				
			||||||
 | 
										case 'n':
 | 
				
			||||||
 | 
											if (ch + 5 != e) switch ((ch + 5)->unicode()) {
 | 
				
			||||||
 | 
											case ':':
 | 
				
			||||||
 | 
												newEmojiEnd = ch + 6;
 | 
				
			||||||
 | 
												if (newEmojiEnd == e || emojiEdge(newEmojiEnd) || newEmojiEnd->unicode() == ' ') {
 | 
				
			||||||
 | 
													emojiCode = 0xD83DDE01U;
 | 
				
			||||||
 | 
													return;
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
		case 'd':
 | 
							case 'd':
 | 
				
			||||||
			if (ch + 2 != e) switch ((ch + 2)->unicode()) {
 | 
								if (ch + 2 != e) switch ((ch + 2)->unicode()) {
 | 
				
			||||||
			case 'i':
 | 
								case 'i':
 | 
				
			||||||
| 
						 | 
					@ -4605,7 +4670,7 @@ void findEmoji(const QChar *ch, const QChar *e, const QChar *&newEmojiEnd, uint3
 | 
				
			||||||
			case '(':
 | 
								case '(':
 | 
				
			||||||
				newEmojiEnd = ch + 3;
 | 
									newEmojiEnd = ch + 3;
 | 
				
			||||||
				if (newEmojiEnd == e || emojiEdge(newEmojiEnd) || newEmojiEnd->unicode() == ' ') {
 | 
									if (newEmojiEnd == e || emojiEdge(newEmojiEnd) || newEmojiEnd->unicode() == ' ') {
 | 
				
			||||||
					emojiCode = 0xD83DDE12U;
 | 
										emojiCode = 0xD83DDE1EU;
 | 
				
			||||||
					return;
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -166,6 +166,75 @@ QSize FlatTextarea::minimumSizeHint() const {
 | 
				
			||||||
	return geometry().size();
 | 
						return geometry().size();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EmojiPtr FlatTextarea::getSingleEmoji() const {
 | 
				
			||||||
 | 
						QString text;
 | 
				
			||||||
 | 
						QTextFragment fragment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getSingleEmojiFragment(text, fragment);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if (!text.isEmpty()) {
 | 
				
			||||||
 | 
							QString imageName = static_cast<const QTextImageFormat*>(&fragment.charFormat())->name();
 | 
				
			||||||
 | 
							return getEmoji(imageName.mid(8).toUInt(0, 16));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const {
 | 
				
			||||||
 | 
						int32 end = textCursor().position(), start = end - 1;
 | 
				
			||||||
 | 
						if (textCursor().anchor() != end) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (start < 0) start = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QTextDocument *doc(document());
 | 
				
			||||||
 | 
						QTextBlock from = doc->findBlock(start), till = doc->findBlock(end);
 | 
				
			||||||
 | 
						if (till.isValid()) till = till.next();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (QTextBlock b = from; b != till; b = b.next()) {
 | 
				
			||||||
 | 
							for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) {
 | 
				
			||||||
 | 
								QTextFragment fr(iter.fragment());
 | 
				
			||||||
 | 
								if (!fr.isValid()) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								int32 p = fr.position(), e = (p + fr.length());
 | 
				
			||||||
 | 
								if (p >= end || e <= start) {
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								QTextCharFormat f = fr.charFormat();
 | 
				
			||||||
 | 
								QString t(fr.text());
 | 
				
			||||||
 | 
								if (p < start) {
 | 
				
			||||||
 | 
									t = t.mid(start - p, end - start);
 | 
				
			||||||
 | 
								} else if (e > end) {
 | 
				
			||||||
 | 
									t = t.mid(0, end - p);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (f.isImageFormat() && !t.isEmpty() && t.at(0).unicode() == QChar::ObjectReplacementCharacter) {
 | 
				
			||||||
 | 
									QString imageName = static_cast<QTextImageFormat*>(&f)->name();
 | 
				
			||||||
 | 
									if (imageName.midRef(0, 8) == qsl("emoji://")) {
 | 
				
			||||||
 | 
										fragment = fr;
 | 
				
			||||||
 | 
										text = t;
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FlatTextarea::removeSingleEmoji() {
 | 
				
			||||||
 | 
						QString text;
 | 
				
			||||||
 | 
						QTextFragment fragment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getSingleEmojiFragment(text, fragment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!text.isEmpty()) {
 | 
				
			||||||
 | 
							QTextCursor t(textCursor());
 | 
				
			||||||
 | 
							t.setPosition(fragment.position());
 | 
				
			||||||
 | 
							t.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
 | 
				
			||||||
 | 
							t.removeSelectedText();
 | 
				
			||||||
 | 
							setTextCursor(t);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QString FlatTextarea::getText(int32 start, int32 end) const {
 | 
					QString FlatTextarea::getText(int32 start, int32 end) const {
 | 
				
			||||||
	if (end >= 0 && end <= start) return QString();
 | 
						if (end >= 0 && end <= start) return QString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,6 +48,8 @@ public:
 | 
				
			||||||
	QSize sizeHint() const;
 | 
						QSize sizeHint() const;
 | 
				
			||||||
	QSize minimumSizeHint() const;
 | 
						QSize minimumSizeHint() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EmojiPtr getSingleEmoji() const;
 | 
				
			||||||
 | 
						void removeSingleEmoji();
 | 
				
			||||||
	QString getText(int32 start = 0, int32 end = -1) const;
 | 
						QString getText(int32 start = 0, int32 end = -1) const;
 | 
				
			||||||
	bool hasText() const;
 | 
						bool hasText() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,6 +79,7 @@ protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void getSingleEmojiFragment(QString &text, QTextFragment &fragment) const;
 | 
				
			||||||
	void processDocumentContentsChange(int position, int charsAdded);
 | 
						void processDocumentContentsChange(int position, int charsAdded);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QMimeData *createMimeDataFromSelection() const;
 | 
						QMimeData *createMimeDataFromSelection() const;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -330,7 +330,7 @@ void Image::invalidateSizeCache() const {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LocalImage::LocalImage(const QString &file, QByteArray fmt) {
 | 
					LocalImage::LocalImage(const QString &file, QByteArray fmt) {
 | 
				
			||||||
	data = QPixmap::fromImage(App::readImage(file, &fmt, false), Qt::ColorOnly);
 | 
						data = QPixmap::fromImage(App::readImage(file, &fmt, false, 0, &saved), Qt::ColorOnly);
 | 
				
			||||||
	format = fmt;
 | 
						format = fmt;
 | 
				
			||||||
	if (!data.isNull()) {
 | 
						if (!data.isNull()) {
 | 
				
			||||||
		globalAquiredSize += int64(data.width()) * data.height() * 4;
 | 
							globalAquiredSize += int64(data.width()) * data.height() * 4;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4212,7 +4212,7 @@ LinkRanges textParseLinks(const QString &text, bool rich) {
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if (p > domainEnd) { // check, that domain ended
 | 
									if (p > domainEnd) { // check, that domain ended
 | 
				
			||||||
					if (domainEnd->unicode() != '/') {
 | 
										if (domainEnd->unicode() != '/' && domainEnd->unicode() != '?') {
 | 
				
			||||||
						matchOffset = domainEnd - start;
 | 
											matchOffset = domainEnd - start;
 | 
				
			||||||
						continue;
 | 
											continue;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -720,8 +720,8 @@ void DocumentCancelLink::onClick(Qt::MouseButton button) const {
 | 
				
			||||||
	data->cancel();
 | 
						data->cancel();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DocumentData::DocumentData(int32 user, const DocumentId &id, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) :
 | 
					DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) :
 | 
				
			||||||
user(user), id(id), type(FileDocument), duration(0), access(access), date(date), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0) {
 | 
					id(id), type(FileDocument), duration(0), access(access), date(date), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0) {
 | 
				
			||||||
	setattributes(attributes);
 | 
						setattributes(attributes);
 | 
				
			||||||
	location = Local::readFileLocation(mediaKey(mtpc_inputDocumentFileLocation, dc, id));
 | 
						location = Local::readFileLocation(mediaKey(mtpc_inputDocumentFileLocation, dc, id));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1289,27 +1289,14 @@ HistoryItem *History::createItemForwarded(HistoryBlock *block, MsgId id, History
 | 
				
			||||||
	return regItem(result);
 | 
						return regItem(result);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					HistoryItem *History::createItemDocument(HistoryBlock *block, MsgId id, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc) {
 | 
				
			||||||
HistoryItem *History::createItem(HistoryBlock *block, const MTPgeoChatMessage &msg, bool newMsg) {
 | 
					 | 
				
			||||||
	HistoryItem *result = 0;
 | 
						HistoryItem *result = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (msg.type()) {
 | 
						result = new HistoryMessage(this, block, id, out, unread, date, from, doc);
 | 
				
			||||||
	case mtpc_geoChatMessageEmpty:
 | 
					 | 
				
			||||||
		result = new HistoryServiceMsg(this, block, msg.c_geoChatMessageEmpty().vid.v, date(), lang(lng_message_empty));
 | 
					 | 
				
			||||||
	break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case mtpc_geoChatMessage:
 | 
					 | 
				
			||||||
		result = new HistoryMessage(this, block, msg.c_geoChatMessage());
 | 
					 | 
				
			||||||
	break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case mtpc_geoChatMessageService:
 | 
					 | 
				
			||||||
		result = new HistoryServiceMsg(this, block, msg.c_geoChatMessageService());
 | 
					 | 
				
			||||||
	break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return regItem(result);
 | 
						return regItem(result);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/**/
 | 
					
 | 
				
			||||||
HistoryItem *History::addToBackService(MsgId msgId, QDateTime date, const QString &text, bool out, bool unread, HistoryMedia *media, bool newMsg) {
 | 
					HistoryItem *History::addToBackService(MsgId msgId, QDateTime date, const QString &text, bool out, bool unread, HistoryMedia *media, bool newMsg) {
 | 
				
			||||||
	HistoryBlock *to = 0;
 | 
						HistoryBlock *to = 0;
 | 
				
			||||||
	bool newBlock = isEmpty();
 | 
						bool newBlock = isEmpty();
 | 
				
			||||||
| 
						 | 
					@ -1348,8 +1335,7 @@ HistoryItem *History::addToBackForwarded(MsgId id, HistoryMessage *item) {
 | 
				
			||||||
	return doAddToBack(to, newBlock, createItemForwarded(to, id, item), true);
 | 
						return doAddToBack(to, newBlock, createItemForwarded(to, id, item), true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					HistoryItem *History::addToBackDocument(MsgId id, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc) {
 | 
				
			||||||
HistoryItem *History::addToBack(const MTPgeoChatMessage &msg, bool newMsg) {
 | 
					 | 
				
			||||||
	HistoryBlock *to = 0;
 | 
						HistoryBlock *to = 0;
 | 
				
			||||||
	bool newBlock = isEmpty();
 | 
						bool newBlock = isEmpty();
 | 
				
			||||||
	if (newBlock) {
 | 
						if (newBlock) {
 | 
				
			||||||
| 
						 | 
					@ -1357,10 +1343,8 @@ HistoryItem *History::addToBack(const MTPgeoChatMessage &msg, bool newMsg) {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		to = back();
 | 
							to = back();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return doAddToBack(to, newBlock, createItemDocument(to, id, out, unread, date, from, doc), true);
 | 
				
			||||||
	return doAddToBack(to, newBlock, createItem(to, msg, newMsg), newMsg);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/**/
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
void History::createInitialDateBlock(const QDateTime &date) {
 | 
					void History::createInitialDateBlock(const QDateTime &date) {
 | 
				
			||||||
	HistoryBlock *dateBlock = new HistoryBlock(this); // date block
 | 
						HistoryBlock *dateBlock = new HistoryBlock(this); // date block
 | 
				
			||||||
| 
						 | 
					@ -2988,7 +2972,7 @@ void HistoryDocument::unregItem(HistoryItem *item) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HistoryDocument::updateFrom(const MTPMessageMedia &media) {
 | 
					void HistoryDocument::updateFrom(const MTPMessageMedia &media) {
 | 
				
			||||||
	if (media.type() == mtpc_messageMediaDocument) {
 | 
						if (media.type() == mtpc_messageMediaDocument) {
 | 
				
			||||||
		App::feedDocument(data->user, media.c_messageMediaDocument().vdocument, data);
 | 
							App::feedDocument(media.c_messageMediaDocument().vdocument, data);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3096,6 +3080,7 @@ void HistorySticker::initDimensions(const HistoryItem *parent) {
 | 
				
			||||||
void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const {
 | 
					void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const {
 | 
				
			||||||
	if (width < 0) width = w;
 | 
						if (width < 0) width = w;
 | 
				
			||||||
	if (width < 1) return;
 | 
						if (width < 1) return;
 | 
				
			||||||
 | 
						if (width > _maxw) width = _maxw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool out = parent->out(), hovered, pressed, already = !data->already().isEmpty(), hasdata = !data->data.isEmpty();
 | 
						bool out = parent->out(), hovered, pressed, already = !data->already().isEmpty(), hasdata = !data->data.isEmpty();
 | 
				
			||||||
	if (!data->loader && data->status != FileFailed && !already && !hasdata) {
 | 
						if (!data->loader && data->status != FileFailed && !already && !hasdata) {
 | 
				
			||||||
| 
						 | 
					@ -3158,7 +3143,8 @@ void HistorySticker::unregItem(HistoryItem *item) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HistorySticker::updateFrom(const MTPMessageMedia &media) {
 | 
					void HistorySticker::updateFrom(const MTPMessageMedia &media) {
 | 
				
			||||||
	if (media.type() == mtpc_messageMediaDocument) {
 | 
						if (media.type() == mtpc_messageMediaDocument) {
 | 
				
			||||||
		App::feedDocument(data->user, media.c_messageMediaDocument().vdocument, data);
 | 
							App::feedDocument(media.c_messageMediaDocument().vdocument, data);
 | 
				
			||||||
 | 
							if (App::main()) App::main()->incrementSticker(data);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3944,12 +3930,22 @@ HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgI
 | 
				
			||||||
, _textHeight(0)
 | 
					, _textHeight(0)
 | 
				
			||||||
, _media(0)
 | 
					, _media(0)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	QString text(msg);
 | 
					 | 
				
			||||||
	if (fromMedia) {
 | 
						if (fromMedia) {
 | 
				
			||||||
		_media = fromMedia->clone();
 | 
							_media = fromMedia->clone();
 | 
				
			||||||
		_media->regItem(this);
 | 
							_media->regItem(this);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	initDimensions(text);
 | 
						initDimensions(msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc) :
 | 
				
			||||||
 | 
					HistoryItem(history, block, msgId, out, unread, date, from)
 | 
				
			||||||
 | 
					, _text(st::msgMinWidth)
 | 
				
			||||||
 | 
					, _textWidth(0)
 | 
				
			||||||
 | 
					, _textHeight(0)
 | 
				
			||||||
 | 
					, _media(0)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						initMediaFromDocument(doc);
 | 
				
			||||||
 | 
						initDimensions(QString());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HistoryMessage::initMedia(const MTPMessageMedia &media, QString ¤tText) {
 | 
					void HistoryMessage::initMedia(const MTPMessageMedia &media, QString ¤tText) {
 | 
				
			||||||
| 
						 | 
					@ -3993,12 +3989,8 @@ void HistoryMessage::initMedia(const MTPMessageMedia &media, QString ¤tTex
 | 
				
			||||||
	case mtpc_messageMediaDocument: {
 | 
						case mtpc_messageMediaDocument: {
 | 
				
			||||||
		const MTPDocument &document(media.c_messageMediaDocument().vdocument);
 | 
							const MTPDocument &document(media.c_messageMediaDocument().vdocument);
 | 
				
			||||||
		if (document.type() == mtpc_document) {
 | 
							if (document.type() == mtpc_document) {
 | 
				
			||||||
			DocumentData *doc = App::feedDocument(_from->id, document);
 | 
								DocumentData *doc = App::feedDocument(document);
 | 
				
			||||||
			if (doc->type == StickerDocument && doc->dimensions.width() > 0 && doc->dimensions.height() > 0 && doc->size < StickerInMemory) {
 | 
								return initMediaFromDocument(doc);
 | 
				
			||||||
				_media = new HistorySticker(doc);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				_media = new HistoryDocument(doc);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} break;
 | 
						} break;
 | 
				
			||||||
	case mtpc_messageMediaUnsupported:
 | 
						case mtpc_messageMediaUnsupported:
 | 
				
			||||||
| 
						 | 
					@ -4007,6 +3999,15 @@ void HistoryMessage::initMedia(const MTPMessageMedia &media, QString ¤tTex
 | 
				
			||||||
	if (_media) _media->regItem(this);
 | 
						if (_media) _media->regItem(this);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void HistoryMessage::initMediaFromDocument(DocumentData *doc) {
 | 
				
			||||||
 | 
						if (doc->type == StickerDocument && doc->dimensions.width() > 0 && doc->dimensions.height() > 0 && doc->size < StickerInMemory) {
 | 
				
			||||||
 | 
							_media = new HistorySticker(doc);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							_media = new HistoryDocument(doc);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_media->regItem(this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HistoryMessage::initDimensions(const QString &text) {
 | 
					void HistoryMessage::initDimensions(const QString &text) {
 | 
				
			||||||
	_time = date.toString(qsl("hh:mm"));
 | 
						_time = date.toString(qsl("hh:mm"));
 | 
				
			||||||
	_timeWidth = st::msgDateFont->m.width(_time);
 | 
						_timeWidth = st::msgDateFont->m.width(_time);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -399,7 +399,7 @@ enum DocumentType {
 | 
				
			||||||
	AnimatedDocument
 | 
						AnimatedDocument
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
struct DocumentData {
 | 
					struct DocumentData {
 | 
				
			||||||
	DocumentData(int32 user, const DocumentId &id, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
 | 
						DocumentData(const DocumentId &id, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
 | 
				
			||||||
	void setattributes(const QVector<MTPDocumentAttribute> &attributes);
 | 
						void setattributes(const QVector<MTPDocumentAttribute> &attributes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void forget() {
 | 
						void forget() {
 | 
				
			||||||
| 
						 | 
					@ -435,8 +435,6 @@ struct DocumentData {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QString already(bool check = false);
 | 
						QString already(bool check = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 user;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DocumentId id;
 | 
						DocumentId id;
 | 
				
			||||||
	DocumentType type;
 | 
						DocumentType type;
 | 
				
			||||||
	QSize dimensions;
 | 
						QSize dimensions;
 | 
				
			||||||
| 
						 | 
					@ -640,12 +638,14 @@ struct History : public QList<HistoryBlock*> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	HistoryItem *createItem(HistoryBlock *block, const MTPmessage &msg, bool newMsg, bool returnExisting = false);
 | 
						HistoryItem *createItem(HistoryBlock *block, const MTPmessage &msg, bool newMsg, bool returnExisting = false);
 | 
				
			||||||
	HistoryItem *createItemForwarded(HistoryBlock *block, MsgId id, HistoryMessage *msg);
 | 
						HistoryItem *createItemForwarded(HistoryBlock *block, MsgId id, HistoryMessage *msg);
 | 
				
			||||||
//	HistoryItem *createItem(HistoryBlock *block, const MTPgeoChatMessage &msg, bool newMsg);
 | 
						HistoryItem *createItemDocument(HistoryBlock *block, MsgId id, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	HistoryItem *addToBackService(MsgId msgId, QDateTime date, const QString &text, bool out = false, bool unread = false, HistoryMedia *media = 0, bool newMsg = true);
 | 
						HistoryItem *addToBackService(MsgId msgId, QDateTime date, const QString &text, bool out = false, bool unread = false, HistoryMedia *media = 0, bool newMsg = true);
 | 
				
			||||||
	HistoryItem *addToBack(const MTPmessage &msg, bool newMsg = true);
 | 
						HistoryItem *addToBack(const MTPmessage &msg, bool newMsg = true);
 | 
				
			||||||
	HistoryItem *addToHistory(const MTPmessage &msg);
 | 
						HistoryItem *addToHistory(const MTPmessage &msg);
 | 
				
			||||||
	HistoryItem *addToBackForwarded(MsgId id, HistoryMessage *item);
 | 
						HistoryItem *addToBackForwarded(MsgId id, HistoryMessage *item);
 | 
				
			||||||
//	HistoryItem *addToBack(const MTPgeoChatMessage &msg, bool newMsg = true);
 | 
						HistoryItem *addToBackDocument(MsgId id, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void addToFront(const QVector<MTPMessage> &slice);
 | 
						void addToFront(const QVector<MTPMessage> &slice);
 | 
				
			||||||
	void addToBack(const QVector<MTPMessage> &slice);
 | 
						void addToBack(const QVector<MTPMessage> &slice);
 | 
				
			||||||
	void createInitialDateBlock(const QDateTime &date);
 | 
						void createInitialDateBlock(const QDateTime &date);
 | 
				
			||||||
| 
						 | 
					@ -1549,8 +1549,10 @@ public:
 | 
				
			||||||
	HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg);
 | 
						HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg);
 | 
				
			||||||
	HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, const MTPMessageMedia &media);
 | 
						HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, const MTPMessageMedia &media);
 | 
				
			||||||
	HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, HistoryMedia *media);
 | 
						HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, HistoryMedia *media);
 | 
				
			||||||
 | 
						HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, DocumentData *doc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void initMedia(const MTPMessageMedia &media, QString ¤tText);
 | 
						void initMedia(const MTPMessageMedia &media, QString ¤tText);
 | 
				
			||||||
 | 
						void initMediaFromDocument(DocumentData *doc);
 | 
				
			||||||
	void initDimensions(const HistoryItem *parent = 0);
 | 
						void initDimensions(const HistoryItem *parent = 0);
 | 
				
			||||||
	void initDimensions(const QString &text);
 | 
						void initDimensions(const QString &text);
 | 
				
			||||||
	void fromNameUpdated() const;
 | 
						void fromNameUpdated() const;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1233,11 +1233,11 @@ MessageField::MessageField(HistoryWidget *history, const style::flatTextarea &st
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MessageField::onChange() {
 | 
					void MessageField::onChange() {
 | 
				
			||||||
	int newh = ceil(document()->size().height()) + 2 * fakeMargin();
 | 
						int newh = ceil(document()->size().height()) + 2 * fakeMargin(), minh = st::btnSend.height - 2 * st::sendPadding;
 | 
				
			||||||
	if (newh > st::maxFieldHeight) {
 | 
						if (newh > st::maxFieldHeight) {
 | 
				
			||||||
		newh = st::maxFieldHeight;
 | 
							newh = st::maxFieldHeight;
 | 
				
			||||||
	} else if (newh < st::minFieldHeight) {
 | 
						} else if (newh < minh) {
 | 
				
			||||||
		newh = st::minFieldHeight;
 | 
							newh = minh;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	if (height() != newh) {
 | 
						if (height() != newh) {
 | 
				
			||||||
| 
						 | 
					@ -1533,41 +1533,44 @@ HistoryHider::~HistoryHider() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
 | 
					HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
 | 
				
			||||||
    , histRequestsCount(0)
 | 
					, _lastStickersUpdate(0)
 | 
				
			||||||
    , histPeer(0)
 | 
					, _stickersUpdateRequest(0)
 | 
				
			||||||
    , _activeHist(0)
 | 
					, histRequestsCount(0)
 | 
				
			||||||
    , histPreloading(0)
 | 
					, histPeer(0)
 | 
				
			||||||
	, _loadingAroundId(-1)
 | 
					, _activeHist(0)
 | 
				
			||||||
	, _loadingAroundRequest(0)
 | 
					, histPreloading(0)
 | 
				
			||||||
	, _scroll(this, st::historyScroll, false)
 | 
					, _loadingAroundId(-1)
 | 
				
			||||||
    , _list(0)
 | 
					, _loadingAroundRequest(0)
 | 
				
			||||||
    , hist(0)
 | 
					, _scroll(this, st::historyScroll, false)
 | 
				
			||||||
    , _histInited(false)
 | 
					, _list(0)
 | 
				
			||||||
	, _toHistoryEnd(this, st::historyToEnd)
 | 
					, hist(0)
 | 
				
			||||||
    , _send(this, lang(lng_send_button), st::btnSend)
 | 
					, _histInited(false)
 | 
				
			||||||
    , _attachDocument(this, st::btnAttachDocument)
 | 
					, _toHistoryEnd(this, st::historyToEnd)
 | 
				
			||||||
    , _attachPhoto(this, st::btnAttachPhoto)
 | 
					, _send(this, lang(lng_send_button), st::btnSend)
 | 
				
			||||||
    , _attachEmoji(this, st::btnAttachEmoji)
 | 
					, _attachDocument(this, st::btnAttachDocument)
 | 
				
			||||||
    , _field(this, st::taMsgField, lang(lng_message_ph))
 | 
					, _attachPhoto(this, st::btnAttachPhoto)
 | 
				
			||||||
    , _attachType(this)
 | 
					, _attachEmoji(this, st::btnAttachEmoji)
 | 
				
			||||||
    , _emojiPan(this)
 | 
					, _field(this, st::taMsgField, lang(lng_message_ph))
 | 
				
			||||||
    , _attachDrag(DragStateNone)
 | 
					, _attachType(this)
 | 
				
			||||||
    , _attachDragDocument(this)
 | 
					, _emojiPan(this)
 | 
				
			||||||
    , _attachDragPhoto(this)
 | 
					//, _stickerPan(this)
 | 
				
			||||||
    , imageLoader(this)
 | 
					, _attachDrag(DragStateNone)
 | 
				
			||||||
	, _synthedTextUpdate(false)
 | 
					, _attachDragDocument(this)
 | 
				
			||||||
    , loadingChatId(0)
 | 
					, _attachDragPhoto(this)
 | 
				
			||||||
    , loadingRequestId(0)
 | 
					, imageLoader(this)
 | 
				
			||||||
    , serviceImageCacheSize(0)
 | 
					, _synthedTextUpdate(false)
 | 
				
			||||||
    , confirmImageId(0)
 | 
					, loadingChatId(0)
 | 
				
			||||||
	, confirmWithText(false)
 | 
					, loadingRequestId(0)
 | 
				
			||||||
    , titlePeerTextWidth(0)
 | 
					, serviceImageCacheSize(0)
 | 
				
			||||||
    , bg(st::msgBG)
 | 
					, confirmImageId(0)
 | 
				
			||||||
    , hiderOffered(false)
 | 
					, confirmWithText(false)
 | 
				
			||||||
    , _scrollDelta(0)
 | 
					, titlePeerTextWidth(0)
 | 
				
			||||||
	, _typingRequest(0)
 | 
					, bg(st::msgBG)
 | 
				
			||||||
	, _saveDraftStart(0)
 | 
					, hiderOffered(false)
 | 
				
			||||||
	, _saveDraftText(false) {
 | 
					, _scrollDelta(0)
 | 
				
			||||||
 | 
					, _typingRequest(0)
 | 
				
			||||||
 | 
					, _saveDraftStart(0)
 | 
				
			||||||
 | 
					, _saveDraftText(false) {
 | 
				
			||||||
	_scroll.setFocusPolicy(Qt::NoFocus);
 | 
						_scroll.setFocusPolicy(Qt::NoFocus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setAcceptDrops(true);
 | 
						setAcceptDrops(true);
 | 
				
			||||||
| 
						 | 
					@ -1588,6 +1591,9 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
 | 
				
			||||||
	connect(App::wnd()->windowHandle(), SIGNAL(visibleChanged(bool)), this, SLOT(onVisibleChanged()));
 | 
						connect(App::wnd()->windowHandle(), SIGNAL(visibleChanged(bool)), this, SLOT(onVisibleChanged()));
 | 
				
			||||||
	connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer()));
 | 
						connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer()));
 | 
				
			||||||
	connect(&_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr)));
 | 
						connect(&_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr)));
 | 
				
			||||||
 | 
						connect(&_emojiPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*)));
 | 
				
			||||||
 | 
						connect(&_emojiPan, SIGNAL(updateStickers()), this, SLOT(updateStickers()));
 | 
				
			||||||
 | 
					//	connect(&_stickerPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*)));
 | 
				
			||||||
	connect(&_typingStopTimer, SIGNAL(timeout()), this, SLOT(cancelTyping()));
 | 
						connect(&_typingStopTimer, SIGNAL(timeout()), this, SLOT(cancelTyping()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_scrollTimer.setSingleShot(false);
 | 
						_scrollTimer.setSingleShot(false);
 | 
				
			||||||
| 
						 | 
					@ -1618,11 +1624,15 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
 | 
				
			||||||
	_attachDocument.installEventFilter(&_attachType);
 | 
						_attachDocument.installEventFilter(&_attachType);
 | 
				
			||||||
	_attachPhoto.installEventFilter(&_attachType);
 | 
						_attachPhoto.installEventFilter(&_attachType);
 | 
				
			||||||
	_attachEmoji.installEventFilter(&_emojiPan);
 | 
						_attachEmoji.installEventFilter(&_emojiPan);
 | 
				
			||||||
 | 
					//	_attachEmoji.installEventFilter(&_stickerPan);
 | 
				
			||||||
 | 
					//	_emojiPan.installEventFilter(&_stickerPan);
 | 
				
			||||||
 | 
					//	_stickerPan.installEventFilter(&_emojiPan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
 | 
						connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
 | 
				
			||||||
	connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
 | 
						connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
 | 
				
			||||||
	_attachType.hide();
 | 
						_attachType.hide();
 | 
				
			||||||
	_emojiPan.hide();
 | 
						_emojiPan.hide();
 | 
				
			||||||
 | 
					//	_stickerPan.hide();
 | 
				
			||||||
	_attachDragDocument.hide();
 | 
						_attachDragDocument.hide();
 | 
				
			||||||
	_attachDragPhoto.hide();
 | 
						_attachDragPhoto.hide();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1632,6 +1642,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HistoryWidget::onTextChange() {
 | 
					void HistoryWidget::onTextChange() {
 | 
				
			||||||
	updateTyping();
 | 
						updateTyping();
 | 
				
			||||||
 | 
					//	updateStickerPan();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!hist || _synthedTextUpdate) return;
 | 
						if (!hist || _synthedTextUpdate) return;
 | 
				
			||||||
	_saveDraftText = true;
 | 
						_saveDraftText = true;
 | 
				
			||||||
| 
						 | 
					@ -1639,6 +1650,8 @@ void HistoryWidget::onTextChange() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HistoryWidget::onDraftSaveDelayed() {
 | 
					void HistoryWidget::onDraftSaveDelayed() {
 | 
				
			||||||
 | 
					//	updateStickerPan();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!hist || _synthedTextUpdate) return;
 | 
						if (!hist || _synthedTextUpdate) return;
 | 
				
			||||||
	if (!_field.textCursor().anchor() && !_field.textCursor().position() && !_field.verticalScrollBar()->value()) {
 | 
						if (!_field.textCursor().anchor() && !_field.textCursor().position() && !_field.verticalScrollBar()->value()) {
 | 
				
			||||||
		if (!Local::hasDraftPositions(hist->peer->id)) return;
 | 
							if (!Local::hasDraftPositions(hist->peer->id)) return;
 | 
				
			||||||
| 
						 | 
					@ -1690,6 +1703,18 @@ void HistoryWidget::updateTyping(bool typing) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//void HistoryWidget::updateStickerPan() {
 | 
				
			||||||
 | 
					//	EmojiPtr e = _field.getSingleEmoji();
 | 
				
			||||||
 | 
					//	if (e) updateStickers();
 | 
				
			||||||
 | 
					//	_stickerPan.setStickerPack(e, !_emojiPan.isHidden() && !_emojiPan.hiding());
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void HistoryWidget::updateRecentStickers() {
 | 
				
			||||||
 | 
						if (cEmojiTab() == dbietStickers) {
 | 
				
			||||||
 | 
							_emojiPan.onTabChange();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HistoryWidget::typingDone(const MTPBool &result, mtpRequestId req) {
 | 
					void HistoryWidget::typingDone(const MTPBool &result, mtpRequestId req) {
 | 
				
			||||||
	if (_typingRequest == req) {
 | 
						if (_typingRequest == req) {
 | 
				
			||||||
		_typingRequest = 0;
 | 
							_typingRequest = 0;
 | 
				
			||||||
| 
						 | 
					@ -1734,6 +1759,98 @@ void HistoryWidget::chatLoaded(const MTPmessages_ChatFull &res) {
 | 
				
			||||||
	peerUpdated(App::chat(peerId));
 | 
						peerUpdated(App::chat(peerId));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void HistoryWidget::updateStickers() {
 | 
				
			||||||
 | 
						if (_lastStickersUpdate && getms(true) < _lastStickersUpdate + StickersUpdateTimeout) return;
 | 
				
			||||||
 | 
						if (_stickersUpdateRequest) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_stickersUpdateRequest = MTP::send(MTPmessages_GetAllStickers(MTP_string(cStickersHash())), rpcDone(&HistoryWidget::stickersGot), rpcFail(&HistoryWidget::stickersFailed));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
 | 
				
			||||||
 | 
						_lastStickersUpdate = getms(true);
 | 
				
			||||||
 | 
						_stickersUpdateRequest = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (stickers.type() == mtpc_messages_allStickers) {
 | 
				
			||||||
 | 
							const MTPDmessages_allStickers &d(stickers.c_messages_allStickers());
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							AllStickers all;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const QVector<MTPDocument> &docs(d.vdocuments.c_vector().v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const RecentStickerPack &recent(cRecentStickers());
 | 
				
			||||||
 | 
							RecentStickerPack add;
 | 
				
			||||||
 | 
							add.reserve(docs.size());
 | 
				
			||||||
 | 
							ushort addValue = recent.isEmpty() ? 1 : qAbs(recent.front().second);
 | 
				
			||||||
 | 
							for (int32 i = 0, l = docs.size(); i < l; ++i) {
 | 
				
			||||||
 | 
								DocumentData *doc = App::feedDocument(docs.at(i));
 | 
				
			||||||
 | 
								if (!doc) continue;
 | 
				
			||||||
 | 
								int32 j = 0, s = recent.size();
 | 
				
			||||||
 | 
								for (; j < s; ++j) {
 | 
				
			||||||
 | 
									if (doc == recent.at(j).first) break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (j < s) continue;
 | 
				
			||||||
 | 
								add.push_back(qMakePair(doc, addValue));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!add.isEmpty()) {
 | 
				
			||||||
 | 
								cSetRecentStickers(add + recent);
 | 
				
			||||||
 | 
								Local::writeRecentStickers();
 | 
				
			||||||
 | 
								_emojiPan.onTabChange();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							const QVector<MTPStickerPack> &packs(d.vpacks.c_vector().v);
 | 
				
			||||||
 | 
							for (int32 i = 0, l = packs.size(); i < l; ++i) {
 | 
				
			||||||
 | 
								if (packs.at(i).type() == mtpc_stickerPack) {
 | 
				
			||||||
 | 
									const MTPDstickerPack &p(packs.at(i).c_stickerPack());
 | 
				
			||||||
 | 
									QString emoticon(qs(p.vemoticon));
 | 
				
			||||||
 | 
									EmojiPtr e = 0;
 | 
				
			||||||
 | 
									for (const QChar *ch = emoticon.constData(), *end = emoticon.constEnd(); ch != end; ++ch) {
 | 
				
			||||||
 | 
										if (ch->isHighSurrogate()) {
 | 
				
			||||||
 | 
											if (ch + 1 < end && (ch + 1)->isLowSurrogate()) {
 | 
				
			||||||
 | 
												e = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
 | 
				
			||||||
 | 
												if (!e) {
 | 
				
			||||||
 | 
													++ch;
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											if (ch + 1 < end) {
 | 
				
			||||||
 | 
												if (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3) {
 | 
				
			||||||
 | 
													e = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode());
 | 
				
			||||||
 | 
												} else if ((ch + 1)->unicode() == 0xFE0F) {
 | 
				
			||||||
 | 
													e = getEmoji(ch->unicode());
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if (e) break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (e) {
 | 
				
			||||||
 | 
										const QVector<MTPlong> docs(p.vdocuments.c_vector().v);
 | 
				
			||||||
 | 
										if (!docs.isEmpty()) {
 | 
				
			||||||
 | 
											StickerPack &pack(all[e]);
 | 
				
			||||||
 | 
											pack.reserve(pack.size() + docs.size());
 | 
				
			||||||
 | 
											for (int32 j = 0, s = docs.size(); j < s; ++j) {
 | 
				
			||||||
 | 
												pack.push_back(App::document(docs.at(j).v));
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										LOG(("Sticker Error: Could not find emoji for string: %1").arg(emoticon));
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cSetStickers(all);
 | 
				
			||||||
 | 
							cSetStickersHash(qba(d.vhash));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//		updateStickerPan();
 | 
				
			||||||
 | 
							_emojiPan.onTabChange();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool HistoryWidget::stickersFailed(const RPCError &error) {
 | 
				
			||||||
 | 
						_lastStickersUpdate = getms(true);
 | 
				
			||||||
 | 
						_stickersUpdateRequest = 0;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HistoryWidget::clearLoadingAround() {
 | 
					void HistoryWidget::clearLoadingAround() {
 | 
				
			||||||
	_loadingAroundId = -1;
 | 
						_loadingAroundId = -1;
 | 
				
			||||||
	if (_loadingAroundRequest) {
 | 
						if (_loadingAroundRequest) {
 | 
				
			||||||
| 
						 | 
					@ -1928,6 +2045,7 @@ void HistoryWidget::updateControlsVisibility() {
 | 
				
			||||||
		_attachEmoji.hide();
 | 
							_attachEmoji.hide();
 | 
				
			||||||
		_attachType.hide();
 | 
							_attachType.hide();
 | 
				
			||||||
		_emojiPan.hide();
 | 
							_emojiPan.hide();
 | 
				
			||||||
 | 
					//		_stickerPan.hide();
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1957,6 +2075,7 @@ void HistoryWidget::updateControlsVisibility() {
 | 
				
			||||||
			_attachEmoji.hide();
 | 
								_attachEmoji.hide();
 | 
				
			||||||
			_attachType.hide();
 | 
								_attachType.hide();
 | 
				
			||||||
			_emojiPan.hide();
 | 
								_emojiPan.hide();
 | 
				
			||||||
 | 
					//			_stickerPan.hide();
 | 
				
			||||||
			if (!_field.isHidden()) {
 | 
								if (!_field.isHidden()) {
 | 
				
			||||||
				_field.hide();
 | 
									_field.hide();
 | 
				
			||||||
				resizeEvent(0);
 | 
									resizeEvent(0);
 | 
				
			||||||
| 
						 | 
					@ -1977,6 +2096,7 @@ void HistoryWidget::updateControlsVisibility() {
 | 
				
			||||||
			_attachEmoji.hide();
 | 
								_attachEmoji.hide();
 | 
				
			||||||
			_attachType.hide();
 | 
								_attachType.hide();
 | 
				
			||||||
			_emojiPan.hide();
 | 
								_emojiPan.hide();
 | 
				
			||||||
 | 
					//			_stickerPan.hide();
 | 
				
			||||||
			_toHistoryEnd.hide();
 | 
								_toHistoryEnd.hide();
 | 
				
			||||||
			if (!_field.isHidden()) {
 | 
								if (!_field.isHidden()) {
 | 
				
			||||||
				_field.hide();
 | 
									_field.hide();
 | 
				
			||||||
| 
						 | 
					@ -2301,6 +2421,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!_attachType.isHidden()) _attachType.hideStart();
 | 
							if (!_attachType.isHidden()) _attachType.hideStart();
 | 
				
			||||||
		if (!_emojiPan.isHidden()) _emojiPan.hideStart();
 | 
							if (!_emojiPan.isHidden()) _emojiPan.hideStart();
 | 
				
			||||||
 | 
					//		if (!_stickerPan.isHidden()) _stickerPan.hideStart();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_field.setFocus();
 | 
						_field.setFocus();
 | 
				
			||||||
| 
						 | 
					@ -2991,13 +3112,17 @@ void HistoryWidget::onDocumentUploaded(MsgId newId, const MTPInputFile &file) {
 | 
				
			||||||
	if (!MTP::authedId()) return;
 | 
						if (!MTP::authedId()) return;
 | 
				
			||||||
	HistoryMessage *item = dynamic_cast<HistoryMessage*>(App::histItemById(newId));
 | 
						HistoryMessage *item = dynamic_cast<HistoryMessage*>(App::histItemById(newId));
 | 
				
			||||||
	if (item) {
 | 
						if (item) {
 | 
				
			||||||
		HistoryDocument *media = dynamic_cast<HistoryDocument*>(item->getMedia());
 | 
							DocumentData *document = 0;
 | 
				
			||||||
		if (media) {
 | 
							if (HistoryDocument *media = dynamic_cast<HistoryDocument*>(item->getMedia())) {
 | 
				
			||||||
 | 
								document = media->document();
 | 
				
			||||||
 | 
							} else if (HistorySticker *media = dynamic_cast<HistorySticker*>(item->getMedia())) {
 | 
				
			||||||
 | 
								document = media->document();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (document) {
 | 
				
			||||||
			//App::main()->readServerHistory(item->history(), false);
 | 
								//App::main()->readServerHistory(item->history(), false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			uint64 randomId = MTP::nonce<uint64>();
 | 
								uint64 randomId = MTP::nonce<uint64>();
 | 
				
			||||||
			App::historyRegRandom(randomId, newId);
 | 
								App::historyRegRandom(randomId, newId);
 | 
				
			||||||
			DocumentData *document = media->document();
 | 
					 | 
				
			||||||
			History *hist = item->history();
 | 
								History *hist = item->history();
 | 
				
			||||||
			hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedDocument(file, MTP_string(document->mime), _composeDocumentAttributes(document)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
 | 
								hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedDocument(file, MTP_string(document->mime), _composeDocumentAttributes(document)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -3008,13 +3133,17 @@ void HistoryWidget::onThumbDocumentUploaded(MsgId newId, const MTPInputFile &fil
 | 
				
			||||||
	if (!MTP::authedId()) return;
 | 
						if (!MTP::authedId()) return;
 | 
				
			||||||
	HistoryMessage *item = dynamic_cast<HistoryMessage*>(App::histItemById(newId));
 | 
						HistoryMessage *item = dynamic_cast<HistoryMessage*>(App::histItemById(newId));
 | 
				
			||||||
	if (item) {
 | 
						if (item) {
 | 
				
			||||||
		HistoryDocument *media = dynamic_cast<HistoryDocument*>(item->getMedia());
 | 
							DocumentData *document = 0;
 | 
				
			||||||
		if (media) {
 | 
							if (HistoryDocument *media = dynamic_cast<HistoryDocument*>(item->getMedia())) {
 | 
				
			||||||
 | 
								document = media->document();
 | 
				
			||||||
 | 
							} else if (HistorySticker *media = dynamic_cast<HistorySticker*>(item->getMedia())) {
 | 
				
			||||||
 | 
								document = media->document();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (document) {
 | 
				
			||||||
			//App::main()->readServerHistory(item->history(), false);
 | 
								//App::main()->readServerHistory(item->history(), false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			uint64 randomId = MTP::nonce<uint64>();
 | 
								uint64 randomId = MTP::nonce<uint64>();
 | 
				
			||||||
			App::historyRegRandom(randomId, newId);
 | 
								App::historyRegRandom(randomId, newId);
 | 
				
			||||||
			DocumentData *document = media->document();
 | 
					 | 
				
			||||||
			History *hist = item->history();
 | 
								History *hist = item->history();
 | 
				
			||||||
			hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->mime), _composeDocumentAttributes(document)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
 | 
								hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->mime), _composeDocumentAttributes(document)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -3070,6 +3199,7 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_attachType.move(0, _attachDocument.y() - _attachType.height());
 | 
						_attachType.move(0, _attachDocument.y() - _attachType.height());
 | 
				
			||||||
	_emojiPan.move(width() - _emojiPan.width(), _attachEmoji.y() - _emojiPan.height());
 | 
						_emojiPan.move(width() - _emojiPan.width(), _attachEmoji.y() - _emojiPan.height());
 | 
				
			||||||
 | 
					//	_stickerPan.move(width() - _emojiPan.width() - _stickerPan.width() + st::dropdownPadding.left(), _attachEmoji.y() - _stickerPan.height());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (_attachDrag) {
 | 
						switch (_attachDrag) {
 | 
				
			||||||
	case DragStateFiles:
 | 
						case DragStateFiles:
 | 
				
			||||||
| 
						 | 
					@ -3278,6 +3408,35 @@ void HistoryWidget::onFieldTabbed() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void HistoryWidget::onStickerSend(DocumentData *sticker) {
 | 
				
			||||||
 | 
						if (!hist || !sticker) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						App::main()->readServerHistory(hist, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint64 randomId = MTP::nonce<uint64>();
 | 
				
			||||||
 | 
						MsgId newId = clientMsgId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hist->loadAround(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool out = (histPeer->input.type() != mtpc_inputPeerSelf), unread = (histPeer->input.type() != mtpc_inputPeerSelf);
 | 
				
			||||||
 | 
						hist->addToBackDocument(newId, out, unread, date(MTP_int(unixtime())), MTP::authedId(), sticker);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hist->sendRequestId = MTP::send(MTPmessages_SendMedia(histPeer->input, MTP_inputMediaDocument(MTP_inputDocument(MTP_long(sticker->id), MTP_long(sticker->access))), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						App::historyRegRandom(randomId, newId);
 | 
				
			||||||
 | 
						App::main()->historyToDown(hist);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						App::main()->dialogsToUp();
 | 
				
			||||||
 | 
						peerMessagesUpdated(histPeer->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!_attachType.isHidden()) _attachType.hideStart();
 | 
				
			||||||
 | 
						if (!_emojiPan.isHidden()) _emojiPan.hideStart();
 | 
				
			||||||
 | 
					//	if (!_stickerPan.isHidden()) _stickerPan.hideStart();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//	_field.removeSingleEmoji();
 | 
				
			||||||
 | 
						_field.setFocus();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void HistoryWidget::setFieldText(const QString &text) {
 | 
					void HistoryWidget::setFieldText(const QString &text) {
 | 
				
			||||||
	_synthedTextUpdate = true;
 | 
						_synthedTextUpdate = true;
 | 
				
			||||||
	_field.setPlainText(text);
 | 
						_field.setPlainText(text);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -288,6 +288,8 @@ public:
 | 
				
			||||||
	QRect historyRect() const;
 | 
						QRect historyRect() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void updateTyping(bool typing = true);
 | 
						void updateTyping(bool typing = true);
 | 
				
			||||||
 | 
					//	void updateStickerPan();
 | 
				
			||||||
 | 
						void updateRecentStickers();
 | 
				
			||||||
	void typingDone(const MTPBool &result, mtpRequestId req);
 | 
						void typingDone(const MTPBool &result, mtpRequestId req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void destroyData();
 | 
						void destroyData();
 | 
				
			||||||
| 
						 | 
					@ -379,6 +381,7 @@ public slots:
 | 
				
			||||||
	void onTextChange();
 | 
						void onTextChange();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void onFieldTabbed();
 | 
						void onFieldTabbed();
 | 
				
			||||||
 | 
						void onStickerSend(DocumentData *sticker);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void onVisibleChanged();
 | 
						void onVisibleChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -401,6 +404,8 @@ public slots:
 | 
				
			||||||
	void onDraftSaveDelayed();
 | 
						void onDraftSaveDelayed();
 | 
				
			||||||
	void onDraftSave(bool delayed = false);
 | 
						void onDraftSave(bool delayed = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void updateStickers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool messagesFailed(const RPCError &error, mtpRequestId requestId);
 | 
						bool messagesFailed(const RPCError &error, mtpRequestId requestId);
 | 
				
			||||||
| 
						 | 
					@ -409,6 +414,12 @@ private:
 | 
				
			||||||
	void addMessagesToBack(const QVector<MTPMessage> &messages);
 | 
						void addMessagesToBack(const QVector<MTPMessage> &messages);
 | 
				
			||||||
	void chatLoaded(const MTPmessages_ChatFull &res);
 | 
						void chatLoaded(const MTPmessages_ChatFull &res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void stickersGot(const MTPmessages_AllStickers &stickers);
 | 
				
			||||||
 | 
						bool stickersFailed(const RPCError &error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint64 _lastStickersUpdate;
 | 
				
			||||||
 | 
						mtpRequestId _stickersUpdateRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void writeDraft(const QString *text = 0, const MessageCursor *cursor = 0);
 | 
						void writeDraft(const QString *text = 0, const MessageCursor *cursor = 0);
 | 
				
			||||||
	void setFieldText(const QString &text);
 | 
						void setFieldText(const QString &text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -440,6 +451,7 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Dropdown _attachType;
 | 
						Dropdown _attachType;
 | 
				
			||||||
	EmojiPan _emojiPan;
 | 
						EmojiPan _emojiPan;
 | 
				
			||||||
 | 
					//	StickerPan _stickerPan;
 | 
				
			||||||
	DragState _attachDrag;
 | 
						DragState _attachDrag;
 | 
				
			||||||
	DragArea _attachDragDocument, _attachDragPhoto;
 | 
						DragArea _attachDragDocument, _attachDragPhoto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -167,14 +167,20 @@ void LocalImageLoaderPrivate::prepareImages() {
 | 
				
			||||||
			jpeg_id = id;
 | 
								jpeg_id = id;
 | 
				
			||||||
		} else if ((type == ToPrepareVideo || type == ToPrepareDocument) && !img.isNull()) {
 | 
							} else if ((type == ToPrepareVideo || type == ToPrepareDocument) && !img.isNull()) {
 | 
				
			||||||
			int32 w = img.width(), h = img.height();
 | 
								int32 w = img.width(), h = img.height();
 | 
				
			||||||
			if (animated) attributes.push_back(MTP_documentAttributeAnimated());
 | 
								QByteArray thumbFormat = "JPG";
 | 
				
			||||||
 | 
								if (animated) {
 | 
				
			||||||
 | 
									attributes.push_back(MTP_documentAttributeAnimated());
 | 
				
			||||||
 | 
								} else if (mime == qsl("image/webp") && w > 0 && h > 0 && filesize < StickerInMemory) {
 | 
				
			||||||
 | 
									attributes.push_back(MTP_documentAttributeSticker());
 | 
				
			||||||
 | 
									thumbFormat = "webp";
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			attributes.push_back(MTP_documentAttributeImageSize(MTP_int(w), MTP_int(h)));
 | 
								attributes.push_back(MTP_documentAttributeImageSize(MTP_int(w), MTP_int(h)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			QPixmap full = (w > 90 || h > 90) ? QPixmap::fromImage(img.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(img, Qt::ColorOnly);
 | 
								QPixmap full = (w > 90 || h > 90) ? QPixmap::fromImage(img.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(img, Qt::ColorOnly);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				QBuffer jpegBuffer(&jpeg);
 | 
									QBuffer jpegBuffer(&jpeg);
 | 
				
			||||||
				full.save(&jpegBuffer, "JPG", 87);
 | 
									full.save(&jpegBuffer, thumbFormat, 87);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			photoThumbs.insert('0', full);
 | 
								photoThumbs.insert('0', full);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,7 +84,7 @@ namespace {
 | 
				
			||||||
			result = MTP::nonce<FileKey>();
 | 
								result = MTP::nonce<FileKey>();
 | 
				
			||||||
			path.resize(_basePath.size());
 | 
								path.resize(_basePath.size());
 | 
				
			||||||
			path += toFilePart(result);
 | 
								path += toFilePart(result);
 | 
				
			||||||
		} while (keyAlreadyUsed(path));
 | 
							} while (!result || keyAlreadyUsed(path));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -437,8 +437,11 @@ namespace {
 | 
				
			||||||
		lskUserMap = 0,
 | 
							lskUserMap = 0,
 | 
				
			||||||
		lskDraft, // data: PeerId peer
 | 
							lskDraft, // data: PeerId peer
 | 
				
			||||||
		lskDraftPosition, // data: PeerId peer
 | 
							lskDraftPosition, // data: PeerId peer
 | 
				
			||||||
		lskStorage, // data: StorageKey location
 | 
							lskImages, // data: StorageKey location
 | 
				
			||||||
		lskLocations, // no data
 | 
							lskLocations, // no data
 | 
				
			||||||
 | 
							lskStickers, // data: StorageKey location
 | 
				
			||||||
 | 
							lskAudios, // data: StorageKey location
 | 
				
			||||||
 | 
							lskRecentStickers, // no data
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	typedef QMap<PeerId, FileKey> DraftsMap;
 | 
						typedef QMap<PeerId, FileKey> DraftsMap;
 | 
				
			||||||
| 
						 | 
					@ -446,11 +449,6 @@ namespace {
 | 
				
			||||||
	typedef QMap<PeerId, bool> DraftsNotReadMap;
 | 
						typedef QMap<PeerId, bool> DraftsNotReadMap;
 | 
				
			||||||
	DraftsNotReadMap _draftsNotReadMap;
 | 
						DraftsNotReadMap _draftsNotReadMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	typedef QPair<FileKey, qint32> FileDesc; // file, size
 | 
					 | 
				
			||||||
	typedef QMap<StorageKey, FileDesc> StorageMap;
 | 
					 | 
				
			||||||
	StorageMap _storageMap;
 | 
					 | 
				
			||||||
	int32 _storageFilesSize = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	typedef QMultiMap<MediaKey, FileLocation> FileLocations;
 | 
						typedef QMultiMap<MediaKey, FileLocation> FileLocations;
 | 
				
			||||||
	FileLocations _fileLocations;
 | 
						FileLocations _fileLocations;
 | 
				
			||||||
	typedef QPair<MediaKey, FileLocation> FileLocationPair;
 | 
						typedef QPair<MediaKey, FileLocation> FileLocationPair;
 | 
				
			||||||
| 
						 | 
					@ -458,6 +456,13 @@ namespace {
 | 
				
			||||||
	FileLocationPairs _fileLocationPairs;
 | 
						FileLocationPairs _fileLocationPairs;
 | 
				
			||||||
	FileKey _locationsKey = 0;
 | 
						FileKey _locationsKey = 0;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
						FileKey _recentStickersKey = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						typedef QPair<FileKey, qint32> FileDesc; // file, size
 | 
				
			||||||
 | 
						typedef QMap<StorageKey, FileDesc> StorageMap;
 | 
				
			||||||
 | 
						StorageMap _imagesMap, _stickersMap, _audiosMap;
 | 
				
			||||||
 | 
						int32 _storageImagesSize = 0, _storageStickersSize = 0, _storageAudiosSize = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool _mapChanged = false;
 | 
						bool _mapChanged = false;
 | 
				
			||||||
	int32 _oldMapVersion = 0;
 | 
						int32 _oldMapVersion = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -474,6 +479,8 @@ namespace {
 | 
				
			||||||
			_manager->writeLocations(when == WriteMapFast);
 | 
								_manager->writeLocations(when == WriteMapFast);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if (!_working()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_manager->writingLocations();
 | 
							_manager->writingLocations();
 | 
				
			||||||
		if (_fileLocations.isEmpty()) {
 | 
							if (_fileLocations.isEmpty()) {
 | 
				
			||||||
			if (_locationsKey) {
 | 
								if (_locationsKey) {
 | 
				
			||||||
| 
						 | 
					@ -484,15 +491,14 @@ namespace {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			if (!_locationsKey) {
 | 
								if (!_locationsKey) {
 | 
				
			||||||
				while (!_locationsKey) {
 | 
					 | 
				
			||||||
				_locationsKey = genKey();
 | 
									_locationsKey = genKey();
 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				_mapChanged = true;
 | 
									_mapChanged = true;
 | 
				
			||||||
				_writeMap(WriteMapFast);
 | 
									_writeMap(WriteMapFast);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			quint32 size = 0;
 | 
								quint32 size = 0;
 | 
				
			||||||
			for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) {
 | 
								for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) {
 | 
				
			||||||
				size += sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + i.value().name.size() * sizeof(ushort) + sizeof(qint64) + sizeof(quint32) + sizeof(qint8) + sizeof(quint32);
 | 
									// location + type + namelen + name + date + size
 | 
				
			||||||
 | 
									size += sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + i.value().name.size() * sizeof(ushort) + (sizeof(qint64) + sizeof(quint32) + sizeof(qint8)) + sizeof(quint32);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			EncryptedDescriptor data(size);
 | 
								EncryptedDescriptor data(size);
 | 
				
			||||||
			for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) {
 | 
								for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) {
 | 
				
			||||||
| 
						 | 
					@ -576,9 +582,9 @@ namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		DraftsMap draftsMap, draftsPositionsMap;
 | 
							DraftsMap draftsMap, draftsPositionsMap;
 | 
				
			||||||
		DraftsNotReadMap draftsNotReadMap;
 | 
							DraftsNotReadMap draftsNotReadMap;
 | 
				
			||||||
		StorageMap storageMap;
 | 
							StorageMap imagesMap, stickersMap, audiosMap;
 | 
				
			||||||
		qint64 storageFilesSize = 0;
 | 
							qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0;
 | 
				
			||||||
		quint64 locationsKey = 0;
 | 
							quint64 locationsKey = 0, recentStickersKey = 0;
 | 
				
			||||||
		while (!map.stream.atEnd()) {
 | 
							while (!map.stream.atEnd()) {
 | 
				
			||||||
			quint32 keyType;
 | 
								quint32 keyType;
 | 
				
			||||||
			map.stream >> keyType;
 | 
								map.stream >> keyType;
 | 
				
			||||||
| 
						 | 
					@ -604,7 +610,7 @@ namespace {
 | 
				
			||||||
					draftsPositionsMap.insert(p, key);
 | 
										draftsPositionsMap.insert(p, key);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} break;
 | 
								} break;
 | 
				
			||||||
			case lskStorage: {
 | 
								case lskImages: {
 | 
				
			||||||
				quint32 count = 0;
 | 
									quint32 count = 0;
 | 
				
			||||||
				map.stream >> count;
 | 
									map.stream >> count;
 | 
				
			||||||
				for (quint32 i = 0; i < count; ++i) {
 | 
									for (quint32 i = 0; i < count; ++i) {
 | 
				
			||||||
| 
						 | 
					@ -612,13 +618,40 @@ namespace {
 | 
				
			||||||
					quint64 first, second;
 | 
										quint64 first, second;
 | 
				
			||||||
					qint32 size;
 | 
										qint32 size;
 | 
				
			||||||
					map.stream >> key >> first >> second >> size;
 | 
										map.stream >> key >> first >> second >> size;
 | 
				
			||||||
					storageMap.insert(StorageKey(first, second), FileDesc(key, size));
 | 
										imagesMap.insert(StorageKey(first, second), FileDesc(key, size));
 | 
				
			||||||
					storageFilesSize += size;
 | 
										storageImagesSize += size;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} break;
 | 
				
			||||||
 | 
								case lskStickers: {
 | 
				
			||||||
 | 
									quint32 count = 0;
 | 
				
			||||||
 | 
									map.stream >> count;
 | 
				
			||||||
 | 
									for (quint32 i = 0; i < count; ++i) {
 | 
				
			||||||
 | 
										FileKey key;
 | 
				
			||||||
 | 
										quint64 first, second;
 | 
				
			||||||
 | 
										qint32 size;
 | 
				
			||||||
 | 
										map.stream >> key >> first >> second >> size;
 | 
				
			||||||
 | 
										stickersMap.insert(StorageKey(first, second), FileDesc(key, size));
 | 
				
			||||||
 | 
										storageStickersSize += size;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} break;
 | 
				
			||||||
 | 
								case lskAudios: {
 | 
				
			||||||
 | 
									quint32 count = 0;
 | 
				
			||||||
 | 
									map.stream >> count;
 | 
				
			||||||
 | 
									for (quint32 i = 0; i < count; ++i) {
 | 
				
			||||||
 | 
										FileKey key;
 | 
				
			||||||
 | 
										quint64 first, second;
 | 
				
			||||||
 | 
										qint32 size;
 | 
				
			||||||
 | 
										map.stream >> key >> first >> second >> size;
 | 
				
			||||||
 | 
										audiosMap.insert(StorageKey(first, second), FileDesc(key, size));
 | 
				
			||||||
 | 
										storageAudiosSize += size;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} break;
 | 
								} break;
 | 
				
			||||||
			case lskLocations: {
 | 
								case lskLocations: {
 | 
				
			||||||
				map.stream >> locationsKey;
 | 
									map.stream >> locationsKey;
 | 
				
			||||||
			} break;
 | 
								} break;
 | 
				
			||||||
 | 
								case lskRecentStickers: {
 | 
				
			||||||
 | 
									map.stream >> recentStickersKey;
 | 
				
			||||||
 | 
								} break;
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
				LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType));
 | 
									LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType));
 | 
				
			||||||
				return Local::ReadMapFailed;
 | 
									return Local::ReadMapFailed;
 | 
				
			||||||
| 
						 | 
					@ -632,9 +665,16 @@ namespace {
 | 
				
			||||||
		_draftsMap = draftsMap;
 | 
							_draftsMap = draftsMap;
 | 
				
			||||||
		_draftsPositionsMap = draftsPositionsMap;
 | 
							_draftsPositionsMap = draftsPositionsMap;
 | 
				
			||||||
		_draftsNotReadMap = draftsNotReadMap;
 | 
							_draftsNotReadMap = draftsNotReadMap;
 | 
				
			||||||
		_storageMap = storageMap;
 | 
					
 | 
				
			||||||
		_storageFilesSize = storageFilesSize;
 | 
							_imagesMap = imagesMap;
 | 
				
			||||||
 | 
							_storageImagesSize = storageImagesSize;
 | 
				
			||||||
 | 
							_stickersMap = stickersMap;
 | 
				
			||||||
 | 
							_storageStickersSize = storageStickersSize;
 | 
				
			||||||
 | 
							_audiosMap = audiosMap;
 | 
				
			||||||
 | 
							_storageAudiosSize = storageAudiosSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_locationsKey = locationsKey;
 | 
							_locationsKey = locationsKey;
 | 
				
			||||||
 | 
							_recentStickersKey = recentStickersKey;
 | 
				
			||||||
		_oldMapVersion = mapData.version;
 | 
							_oldMapVersion = mapData.version;
 | 
				
			||||||
		if (_oldMapVersion < AppVersion) {
 | 
							if (_oldMapVersion < AppVersion) {
 | 
				
			||||||
			_mapChanged = true;
 | 
								_mapChanged = true;
 | 
				
			||||||
| 
						 | 
					@ -687,8 +727,11 @@ namespace {
 | 
				
			||||||
		uint32 mapSize = 0;
 | 
							uint32 mapSize = 0;
 | 
				
			||||||
		if (!_draftsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2;
 | 
							if (!_draftsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2;
 | 
				
			||||||
		if (!_draftsPositionsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsPositionsMap.size() * sizeof(quint64) * 2;
 | 
							if (!_draftsPositionsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsPositionsMap.size() * sizeof(quint64) * 2;
 | 
				
			||||||
		if (!_storageMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _storageMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
 | 
							if (!_imagesMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _imagesMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
 | 
				
			||||||
 | 
							if (!_stickersMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickersMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
 | 
				
			||||||
 | 
							if (!_audiosMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _audiosMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
 | 
				
			||||||
		if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64);
 | 
							if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64);
 | 
				
			||||||
 | 
							if (_recentStickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
 | 
				
			||||||
		EncryptedDescriptor mapData(mapSize);
 | 
							EncryptedDescriptor mapData(mapSize);
 | 
				
			||||||
		if (!_draftsMap.isEmpty()) {
 | 
							if (!_draftsMap.isEmpty()) {
 | 
				
			||||||
			mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size());
 | 
								mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size());
 | 
				
			||||||
| 
						 | 
					@ -702,15 +745,30 @@ namespace {
 | 
				
			||||||
				mapData.stream << quint64(i.value()) << quint64(i.key());
 | 
									mapData.stream << quint64(i.value()) << quint64(i.key());
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!_storageMap.isEmpty()) {
 | 
							if (!_imagesMap.isEmpty()) {
 | 
				
			||||||
			mapData.stream << quint32(lskStorage) << quint32(_storageMap.size());
 | 
								mapData.stream << quint32(lskImages) << quint32(_imagesMap.size());
 | 
				
			||||||
			for (StorageMap::const_iterator i = _storageMap.cbegin(), e = _storageMap.cend(); i != e; ++i) {
 | 
								for (StorageMap::const_iterator i = _imagesMap.cbegin(), e = _imagesMap.cend(); i != e; ++i) {
 | 
				
			||||||
 | 
									mapData.stream << quint64(i.value().first) << quint64(i.key().first) << quint64(i.key().second) << qint32(i.value().second);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!_stickersMap.isEmpty()) {
 | 
				
			||||||
 | 
								mapData.stream << quint32(lskStickers) << quint32(_stickersMap.size());
 | 
				
			||||||
 | 
								for (StorageMap::const_iterator i = _stickersMap.cbegin(), e = _stickersMap.cend(); i != e; ++i) {
 | 
				
			||||||
 | 
									mapData.stream << quint64(i.value().first) << quint64(i.key().first) << quint64(i.key().second) << qint32(i.value().second);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!_audiosMap.isEmpty()) {
 | 
				
			||||||
 | 
								mapData.stream << quint32(lskAudios) << quint32(_audiosMap.size());
 | 
				
			||||||
 | 
								for (StorageMap::const_iterator i = _audiosMap.cbegin(), e = _audiosMap.cend(); i != e; ++i) {
 | 
				
			||||||
				mapData.stream << quint64(i.value().first) << quint64(i.key().first) << quint64(i.key().second) << qint32(i.value().second);
 | 
									mapData.stream << quint64(i.value().first) << quint64(i.key().first) << quint64(i.key().second) << qint32(i.value().second);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (_locationsKey) {
 | 
							if (_locationsKey) {
 | 
				
			||||||
			mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
 | 
								mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if (_recentStickersKey) {
 | 
				
			||||||
 | 
								mapData.stream << quint32(lskRecentStickers) << quint64(_recentStickersKey);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		map.writeEncrypted(mapData);
 | 
							map.writeEncrypted(mapData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		map.finish();
 | 
							map.finish();
 | 
				
			||||||
| 
						 | 
					@ -958,9 +1016,25 @@ namespace Local {
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qint32 _storageStickerSize(qint32 rawlen) {
 | 
				
			||||||
 | 
							// fulllen + storagekey + len + data
 | 
				
			||||||
 | 
							qint32 result = sizeof(uint32) + sizeof(quint64) * 2 + sizeof(quint32) + rawlen;
 | 
				
			||||||
 | 
							if (result & 0x0F) result += 0x10 - (result & 0x0F);
 | 
				
			||||||
 | 
							result += tdfMagicLen + sizeof(qint32) + sizeof(quint32) + 0x10 + 0x10; // magic + version + len of encrypted + part of sha1 + md5
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qint32 _storageAudioSize(qint32 rawlen) {
 | 
				
			||||||
 | 
							// fulllen + storagekey + len + data
 | 
				
			||||||
 | 
							qint32 result = sizeof(uint32) + sizeof(quint64) * 2 + sizeof(quint32) + rawlen;
 | 
				
			||||||
 | 
							if (result & 0x0F) result += 0x10 - (result & 0x0F);
 | 
				
			||||||
 | 
							result += tdfMagicLen + sizeof(qint32) + sizeof(quint32) + 0x10 + 0x10; // magic + version + len of encrypted + part of sha1 + md5
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void writeImage(const StorageKey &location, const ImagePtr &image) {
 | 
						void writeImage(const StorageKey &location, const ImagePtr &image) {
 | 
				
			||||||
		if (image->isNull() || !image->loaded()) return;
 | 
							if (image->isNull() || !image->loaded()) return;
 | 
				
			||||||
		if (_storageMap.constFind(location) != _storageMap.cend()) return;
 | 
							if (_imagesMap.constFind(location) != _imagesMap.cend()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		QByteArray fmt = image->savedFormat();
 | 
							QByteArray fmt = image->savedFormat();
 | 
				
			||||||
		mtpTypeId format = 0;
 | 
							mtpTypeId format = 0;
 | 
				
			||||||
| 
						 | 
					@ -981,10 +1055,10 @@ namespace Local {
 | 
				
			||||||
		if (!_working()) return;
 | 
							if (!_working()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		qint32 size = _storageImageSize(image.data.size());
 | 
							qint32 size = _storageImageSize(image.data.size());
 | 
				
			||||||
		StorageMap::const_iterator i = _storageMap.constFind(location);
 | 
							StorageMap::const_iterator i = _imagesMap.constFind(location);
 | 
				
			||||||
		if (i == _storageMap.cend()) {
 | 
							if (i == _imagesMap.cend()) {
 | 
				
			||||||
			i = _storageMap.insert(location, FileDesc(genKey(), size));
 | 
								i = _imagesMap.insert(location, FileDesc(genKey(), size));
 | 
				
			||||||
			_storageFilesSize += size;
 | 
								_storageImagesSize += size;
 | 
				
			||||||
			_mapChanged = true;
 | 
								_mapChanged = true;
 | 
				
			||||||
			_writeMap();
 | 
								_writeMap();
 | 
				
			||||||
		} else if (!overwrite) {
 | 
							} else if (!overwrite) {
 | 
				
			||||||
| 
						 | 
					@ -995,22 +1069,22 @@ namespace Local {
 | 
				
			||||||
		FileWriteDescriptor file(i.value().first, false);
 | 
							FileWriteDescriptor file(i.value().first, false);
 | 
				
			||||||
		file.writeEncrypted(data);
 | 
							file.writeEncrypted(data);
 | 
				
			||||||
		if (i.value().second != size) {
 | 
							if (i.value().second != size) {
 | 
				
			||||||
			_storageFilesSize += size;
 | 
								_storageImagesSize += size;
 | 
				
			||||||
			_storageFilesSize -= i.value().second;
 | 
								_storageImagesSize -= i.value().second;
 | 
				
			||||||
			_storageMap[location].second = size;
 | 
								_imagesMap[location].second = size;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	StorageImageSaved readImage(const StorageKey &location) {
 | 
						StorageImageSaved readImage(const StorageKey &location) {
 | 
				
			||||||
		StorageMap::iterator j = _storageMap.find(location);
 | 
							StorageMap::iterator j = _imagesMap.find(location);
 | 
				
			||||||
		if (j == _storageMap.cend()) {
 | 
							if (j == _imagesMap.cend()) {
 | 
				
			||||||
			return StorageImageSaved();
 | 
								return StorageImageSaved();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		FileReadDescriptor draft;
 | 
							FileReadDescriptor draft;
 | 
				
			||||||
		if (!readEncryptedFile(draft, toFilePart(j.value().first), false)) {
 | 
							if (!readEncryptedFile(draft, toFilePart(j.value().first), false)) {
 | 
				
			||||||
			clearKey(j.value().first, false);
 | 
								clearKey(j.value().first, false);
 | 
				
			||||||
			_storageFilesSize -= j.value().second;
 | 
								_storageImagesSize -= j.value().second;
 | 
				
			||||||
			_storageMap.erase(j);
 | 
								_imagesMap.erase(j);
 | 
				
			||||||
			return StorageImageSaved();
 | 
								return StorageImageSaved();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1023,16 +1097,194 @@ namespace Local {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 hasImages() {
 | 
						int32 hasImages() {
 | 
				
			||||||
		return _storageMap.size();
 | 
							return _imagesMap.size();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	qint64 storageFilesSize() {
 | 
						qint64 storageImagesSize() {
 | 
				
			||||||
		return _storageFilesSize;
 | 
							return _storageImagesSize;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeSticker(const StorageKey &location, const QByteArray &sticker, bool overwrite) {
 | 
				
			||||||
 | 
							if (!_working()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							qint32 size = _storageStickerSize(sticker.size());
 | 
				
			||||||
 | 
							StorageMap::const_iterator i = _stickersMap.constFind(location);
 | 
				
			||||||
 | 
							if (i == _stickersMap.cend()) {
 | 
				
			||||||
 | 
								i = _stickersMap.insert(location, FileDesc(genKey(), size));
 | 
				
			||||||
 | 
								_storageStickersSize += size;
 | 
				
			||||||
 | 
								_mapChanged = true;
 | 
				
			||||||
 | 
								_writeMap();
 | 
				
			||||||
 | 
							} else if (!overwrite) {
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + sticker.size());
 | 
				
			||||||
 | 
							data.stream << quint64(location.first) << quint64(location.second) << sticker;
 | 
				
			||||||
 | 
							FileWriteDescriptor file(i.value().first, false);
 | 
				
			||||||
 | 
							file.writeEncrypted(data);
 | 
				
			||||||
 | 
							if (i.value().second != size) {
 | 
				
			||||||
 | 
								_storageStickersSize += size;
 | 
				
			||||||
 | 
								_storageStickersSize -= i.value().second;
 | 
				
			||||||
 | 
								_stickersMap[location].second = size;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QByteArray readSticker(const StorageKey &location) {
 | 
				
			||||||
 | 
							StorageMap::iterator j = _stickersMap.find(location);
 | 
				
			||||||
 | 
							if (j == _stickersMap.cend()) {
 | 
				
			||||||
 | 
								return QByteArray();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							FileReadDescriptor draft;
 | 
				
			||||||
 | 
							if (!readEncryptedFile(draft, toFilePart(j.value().first), false)) {
 | 
				
			||||||
 | 
								clearKey(j.value().first, false);
 | 
				
			||||||
 | 
								_storageStickersSize -= j.value().second;
 | 
				
			||||||
 | 
								_stickersMap.erase(j);
 | 
				
			||||||
 | 
								return QByteArray();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							QByteArray stickerData;
 | 
				
			||||||
 | 
							quint64 locFirst, locSecond;
 | 
				
			||||||
 | 
							draft.stream >> locFirst >> locSecond >> stickerData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return (locFirst == location.first && locSecond == location.second) ? stickerData : QByteArray();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int32 hasStickers() {
 | 
				
			||||||
 | 
							return _stickersMap.size();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qint64 storageStickersSize() {
 | 
				
			||||||
 | 
							return _storageStickersSize;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeAudio(const StorageKey &location, const QByteArray &audio, bool overwrite) {
 | 
				
			||||||
 | 
							if (!_working()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							qint32 size = _storageAudioSize(audio.size());
 | 
				
			||||||
 | 
							StorageMap::const_iterator i = _audiosMap.constFind(location);
 | 
				
			||||||
 | 
							if (i == _audiosMap.cend()) {
 | 
				
			||||||
 | 
								i = _audiosMap.insert(location, FileDesc(genKey(), size));
 | 
				
			||||||
 | 
								_storageAudiosSize += size;
 | 
				
			||||||
 | 
								_mapChanged = true;
 | 
				
			||||||
 | 
								_writeMap();
 | 
				
			||||||
 | 
							} else if (!overwrite) {
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + audio.size());
 | 
				
			||||||
 | 
							data.stream << quint64(location.first) << quint64(location.second) << audio;
 | 
				
			||||||
 | 
							FileWriteDescriptor file(i.value().first, false);
 | 
				
			||||||
 | 
							file.writeEncrypted(data);
 | 
				
			||||||
 | 
							if (i.value().second != size) {
 | 
				
			||||||
 | 
								_storageAudiosSize += size;
 | 
				
			||||||
 | 
								_storageAudiosSize -= i.value().second;
 | 
				
			||||||
 | 
								_audiosMap[location].second = size;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QByteArray readAudio(const StorageKey &location) {
 | 
				
			||||||
 | 
							StorageMap::iterator j = _audiosMap.find(location);
 | 
				
			||||||
 | 
							if (j == _audiosMap.cend()) {
 | 
				
			||||||
 | 
								return QByteArray();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							FileReadDescriptor draft;
 | 
				
			||||||
 | 
							if (!readEncryptedFile(draft, toFilePart(j.value().first), false)) {
 | 
				
			||||||
 | 
								clearKey(j.value().first, false);
 | 
				
			||||||
 | 
								_storageAudiosSize -= j.value().second;
 | 
				
			||||||
 | 
								_audiosMap.erase(j);
 | 
				
			||||||
 | 
								return QByteArray();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							QByteArray audioData;
 | 
				
			||||||
 | 
							quint64 locFirst, locSecond;
 | 
				
			||||||
 | 
							draft.stream >> locFirst >> locSecond >> audioData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return (locFirst == location.first && locSecond == location.second) ? audioData : QByteArray();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int32 hasAudios() {
 | 
				
			||||||
 | 
							return _audiosMap.size();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qint64 storageAudiosSize() {
 | 
				
			||||||
 | 
							return _storageAudiosSize;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeRecentStickers() {
 | 
				
			||||||
 | 
							if (!_working()) return;
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
							const RecentStickerPack &recent(cRecentStickers());
 | 
				
			||||||
 | 
							if (recent.isEmpty()) {
 | 
				
			||||||
 | 
								if (_recentStickersKey) {
 | 
				
			||||||
 | 
									clearKey(_recentStickersKey);
 | 
				
			||||||
 | 
									_recentStickersKey = 0;
 | 
				
			||||||
 | 
									_mapChanged = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								_writeMap();
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (!_recentStickersKey) {
 | 
				
			||||||
 | 
									_recentStickersKey = genKey();
 | 
				
			||||||
 | 
									_mapChanged = true;
 | 
				
			||||||
 | 
									_writeMap(WriteMapFast);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								quint32 size = 0;
 | 
				
			||||||
 | 
								for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
 | 
				
			||||||
 | 
									DocumentData *doc = i->first;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// id + value + access + date + namelen + name + mimelen + mime + dc + size + width + height + type
 | 
				
			||||||
 | 
									size += sizeof(quint64) + sizeof(qint16) + sizeof(quint64) + sizeof(qint32) + (sizeof(quint32) + doc->name.size() * sizeof(ushort)) + (sizeof(quint32) + doc->mime.size() * sizeof(ushort)) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								EncryptedDescriptor data(size);
 | 
				
			||||||
 | 
								for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
 | 
				
			||||||
 | 
									DocumentData *doc = i->first;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									data.stream << quint64(doc->id) << qint16(i->second) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								FileWriteDescriptor file(_recentStickersKey);
 | 
				
			||||||
 | 
								file.writeEncrypted(data);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void readRecentStickers() {
 | 
				
			||||||
 | 
							if (!_recentStickersKey) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							FileReadDescriptor stickers;
 | 
				
			||||||
 | 
							if (!readEncryptedFile(stickers, toFilePart(_recentStickersKey))) {
 | 
				
			||||||
 | 
								clearKey(_recentStickersKey);
 | 
				
			||||||
 | 
								_recentStickersKey = 0;
 | 
				
			||||||
 | 
								_writeMap();
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							QMap<uint64, bool> read;
 | 
				
			||||||
 | 
							RecentStickerPack recent;
 | 
				
			||||||
 | 
							while (!stickers.stream.atEnd()) {
 | 
				
			||||||
 | 
								quint64 id, access;
 | 
				
			||||||
 | 
								QString name, mime;
 | 
				
			||||||
 | 
								qint32 date, dc, size, width, height, type;
 | 
				
			||||||
 | 
								qint16 value;
 | 
				
			||||||
 | 
								stickers.stream >> id >> value >> access >> date >> name >> mime >> dc >> size >> width >> height >> type;
 | 
				
			||||||
 | 
								if (read.contains(id)) continue;
 | 
				
			||||||
 | 
								read.insert(id, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								QVector<MTPDocumentAttribute> attributes;
 | 
				
			||||||
 | 
								if (!name.isEmpty()) attributes.push_back(MTP_documentAttributeFilename(MTP_string(name)));
 | 
				
			||||||
 | 
								if (type == AnimatedDocument) {
 | 
				
			||||||
 | 
									attributes.push_back(MTP_documentAttributeAnimated());
 | 
				
			||||||
 | 
								} else if (type == StickerDocument) {
 | 
				
			||||||
 | 
									attributes.push_back(MTP_documentAttributeSticker());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (width > 0 && height > 0) {
 | 
				
			||||||
 | 
									attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								recent.push_back(qMakePair(App::document(id, 0, access, date, attributes, mime, ImagePtr(), dc, size), value));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cSetRecentStickers(recent);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct ClearManagerData {
 | 
						struct ClearManagerData {
 | 
				
			||||||
		QThread *thread;
 | 
							QThread *thread;
 | 
				
			||||||
		StorageMap images;
 | 
							StorageMap images, stickers, audios;
 | 
				
			||||||
		QMutex mutex;
 | 
							QMutex mutex;
 | 
				
			||||||
		QList<int> tasks;
 | 
							QList<int> tasks;
 | 
				
			||||||
		bool working;
 | 
							bool working;
 | 
				
			||||||
| 
						 | 
					@ -1050,9 +1302,19 @@ namespace Local {
 | 
				
			||||||
		if (!data->tasks.isEmpty() && (data->tasks.at(0) == ClearManagerAll)) return true;
 | 
							if (!data->tasks.isEmpty() && (data->tasks.at(0) == ClearManagerAll)) return true;
 | 
				
			||||||
		if (task == ClearManagerAll) {
 | 
							if (task == ClearManagerAll) {
 | 
				
			||||||
			data->tasks.clear();
 | 
								data->tasks.clear();
 | 
				
			||||||
			if (!_storageMap.isEmpty()) {
 | 
								if (!_imagesMap.isEmpty()) {
 | 
				
			||||||
				_storageMap.clear();
 | 
									_imagesMap.clear();
 | 
				
			||||||
				_storageFilesSize = 0;
 | 
									_storageImagesSize = 0;
 | 
				
			||||||
 | 
									_mapChanged = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (!_stickersMap.isEmpty()) {
 | 
				
			||||||
 | 
									_stickersMap.clear();
 | 
				
			||||||
 | 
									_storageStickersSize = 0;
 | 
				
			||||||
 | 
									_mapChanged = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (!_audiosMap.isEmpty()) {
 | 
				
			||||||
 | 
									_audiosMap.clear();
 | 
				
			||||||
 | 
									_storageAudiosSize = 0;
 | 
				
			||||||
				_mapChanged = true;
 | 
									_mapChanged = true;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (!_draftsMap.isEmpty()) {
 | 
								if (!_draftsMap.isEmpty()) {
 | 
				
			||||||
| 
						 | 
					@ -1067,13 +1329,17 @@ namespace Local {
 | 
				
			||||||
				_locationsKey = 0;
 | 
									_locationsKey = 0;
 | 
				
			||||||
				_mapChanged = true;
 | 
									_mapChanged = true;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if (_recentStickersKey) {
 | 
				
			||||||
 | 
									_recentStickersKey = 0;
 | 
				
			||||||
 | 
									_mapChanged = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			_writeMap();
 | 
								_writeMap();
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			if (task & ClearManagerImages) {
 | 
								if (task & ClearManagerStorage) {
 | 
				
			||||||
				if (data->images.isEmpty()) {
 | 
									if (data->images.isEmpty()) {
 | 
				
			||||||
					data->images = _storageMap;
 | 
										data->images = _imagesMap;
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					for (StorageMap::const_iterator i = _storageMap.cbegin(), e = _storageMap.cend(); i != e; ++i) {
 | 
										for (StorageMap::const_iterator i = _imagesMap.cbegin(), e = _imagesMap.cend(); i != e; ++i) {
 | 
				
			||||||
						StorageKey k = i.key();
 | 
											StorageKey k = i.key();
 | 
				
			||||||
						while (data->images.constFind(k) != data->images.cend()) {
 | 
											while (data->images.constFind(k) != data->images.cend()) {
 | 
				
			||||||
							++k.second;
 | 
												++k.second;
 | 
				
			||||||
| 
						 | 
					@ -1081,12 +1347,44 @@ namespace Local {
 | 
				
			||||||
						data->images.insert(k, i.value());
 | 
											data->images.insert(k, i.value());
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if (!_storageMap.isEmpty()) {
 | 
									if (!_imagesMap.isEmpty()) {
 | 
				
			||||||
					_storageMap.clear();
 | 
										_imagesMap.clear();
 | 
				
			||||||
					_storageFilesSize = 0;
 | 
										_storageImagesSize = 0;
 | 
				
			||||||
					_mapChanged = true;
 | 
										_mapChanged = true;
 | 
				
			||||||
					_writeMap();
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
									if (data->stickers.isEmpty()) {
 | 
				
			||||||
 | 
										data->stickers = _stickersMap;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										for (StorageMap::const_iterator i = _stickersMap.cbegin(), e = _stickersMap.cend(); i != e; ++i) {
 | 
				
			||||||
 | 
											StorageKey k = i.key();
 | 
				
			||||||
 | 
											while (data->stickers.constFind(k) != data->stickers.cend()) {
 | 
				
			||||||
 | 
												++k.second;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											data->stickers.insert(k, i.value());
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (!_stickersMap.isEmpty()) {
 | 
				
			||||||
 | 
										_stickersMap.clear();
 | 
				
			||||||
 | 
										_storageStickersSize = 0;
 | 
				
			||||||
 | 
										_mapChanged = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (data->audios.isEmpty()) {
 | 
				
			||||||
 | 
										data->audios = _audiosMap;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										for (StorageMap::const_iterator i = _audiosMap.cbegin(), e = _audiosMap.cend(); i != e; ++i) {
 | 
				
			||||||
 | 
											StorageKey k = i.key();
 | 
				
			||||||
 | 
											while (data->audios.constFind(k) != data->audios.cend()) {
 | 
				
			||||||
 | 
												++k.second;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											data->audios.insert(k, i.value());
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (!_audiosMap.isEmpty()) {
 | 
				
			||||||
 | 
										_audiosMap.clear();
 | 
				
			||||||
 | 
										_storageAudiosSize = 0;
 | 
				
			||||||
 | 
										_mapChanged = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									_writeMap();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			for (int32 i = 0, l = data->tasks.size(); i < l; ++i) {
 | 
								for (int32 i = 0, l = data->tasks.size(); i < l; ++i) {
 | 
				
			||||||
				if (data->tasks.at(i) == task) return true;
 | 
									if (data->tasks.at(i) == task) return true;
 | 
				
			||||||
| 
						 | 
					@ -1121,7 +1419,7 @@ namespace Local {
 | 
				
			||||||
		while (true) {
 | 
							while (true) {
 | 
				
			||||||
			int task = 0;
 | 
								int task = 0;
 | 
				
			||||||
			bool result = false;
 | 
								bool result = false;
 | 
				
			||||||
			StorageMap images;
 | 
								StorageMap images, stickers, audios;
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				QMutexLocker lock(&data->mutex);
 | 
									QMutexLocker lock(&data->mutex);
 | 
				
			||||||
				if (data->tasks.isEmpty()) {
 | 
									if (data->tasks.isEmpty()) {
 | 
				
			||||||
| 
						 | 
					@ -1130,6 +1428,8 @@ namespace Local {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				task = data->tasks.at(0);
 | 
									task = data->tasks.at(0);
 | 
				
			||||||
				images = data->images;
 | 
									images = data->images;
 | 
				
			||||||
 | 
									stickers = data->stickers;
 | 
				
			||||||
 | 
									audios = data->audios;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			switch (task) {
 | 
								switch (task) {
 | 
				
			||||||
			case ClearManagerAll:
 | 
								case ClearManagerAll:
 | 
				
			||||||
| 
						 | 
					@ -1138,10 +1438,16 @@ namespace Local {
 | 
				
			||||||
			case ClearManagerDownloads:
 | 
								case ClearManagerDownloads:
 | 
				
			||||||
				result = QDir(cTempDir()).removeRecursively();
 | 
									result = QDir(cTempDir()).removeRecursively();
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
			case ClearManagerImages:
 | 
								case ClearManagerStorage:
 | 
				
			||||||
				for (StorageMap::const_iterator i = images.cbegin(), e = images.cend(); i != e; ++i) {
 | 
									for (StorageMap::const_iterator i = images.cbegin(), e = images.cend(); i != e; ++i) {
 | 
				
			||||||
					clearKey(i.value().first, false);
 | 
										clearKey(i.value().first, false);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
									for (StorageMap::const_iterator i = stickers.cbegin(), e = stickers.cend(); i != e; ++i) {
 | 
				
			||||||
 | 
										clearKey(i.value().first, false);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									for (StorageMap::const_iterator i = audios.cbegin(), e = audios.cend(); i != e; ++i) {
 | 
				
			||||||
 | 
										clearKey(i.value().first, false);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				result = true;
 | 
									result = true;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,7 +59,7 @@ namespace Local {
 | 
				
			||||||
	enum ClearManagerTask {
 | 
						enum ClearManagerTask {
 | 
				
			||||||
		ClearManagerAll = 0xFFFF,
 | 
							ClearManagerAll = 0xFFFF,
 | 
				
			||||||
		ClearManagerDownloads = 0x01,
 | 
							ClearManagerDownloads = 0x01,
 | 
				
			||||||
		ClearManagerImages = 0x02,
 | 
							ClearManagerStorage = 0x02,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct ClearManagerData;
 | 
						struct ClearManagerData;
 | 
				
			||||||
| 
						 | 
					@ -106,6 +106,19 @@ namespace Local {
 | 
				
			||||||
	void writeImage(const StorageKey &location, const StorageImageSaved &jpeg, bool overwrite = true);
 | 
						void writeImage(const StorageKey &location, const StorageImageSaved &jpeg, bool overwrite = true);
 | 
				
			||||||
	StorageImageSaved readImage(const StorageKey &location);
 | 
						StorageImageSaved readImage(const StorageKey &location);
 | 
				
			||||||
	int32 hasImages();
 | 
						int32 hasImages();
 | 
				
			||||||
	qint64 storageFilesSize();
 | 
						qint64 storageImagesSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeSticker(const StorageKey &location, const QByteArray &data, bool overwrite = true);
 | 
				
			||||||
 | 
						QByteArray readSticker(const StorageKey &location);
 | 
				
			||||||
 | 
						int32 hasStickers();
 | 
				
			||||||
 | 
						qint64 storageStickersSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeAudio(const StorageKey &location, const QByteArray &data, bool overwrite = true);
 | 
				
			||||||
 | 
						QByteArray readAudio(const StorageKey &location);
 | 
				
			||||||
 | 
						int32 hasAudios();
 | 
				
			||||||
 | 
						qint64 storageAudiosSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeRecentStickers();
 | 
				
			||||||
 | 
						void readRecentStickers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 | 
				
			||||||
#include "mainwidget.h"
 | 
					#include "mainwidget.h"
 | 
				
			||||||
#include "boxes/confirmbox.h"
 | 
					#include "boxes/confirmbox.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "localstorage.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "audio.h"
 | 
					#include "audio.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w),
 | 
					TopBarWidget::TopBarWidget(MainWidget *w) : TWidget(w),
 | 
				
			||||||
| 
						 | 
					@ -2121,6 +2123,8 @@ void MainWidget::start(const MTPUser &user) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_started = true;
 | 
						_started = true;
 | 
				
			||||||
	App::wnd()->sendServiceHistoryRequest();
 | 
						App::wnd()->sendServiceHistoryRequest();
 | 
				
			||||||
 | 
						Local::readRecentStickers();
 | 
				
			||||||
 | 
						history.updateRecentStickers();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool MainWidget::started() {
 | 
					bool MainWidget::started() {
 | 
				
			||||||
| 
						 | 
					@ -2267,6 +2271,45 @@ void MainWidget::updateNotifySetting(PeerData *peer, bool enabled) {
 | 
				
			||||||
	updateNotifySettingTimer.start(NotifySettingSaveTimeout);
 | 
						updateNotifySettingTimer.start(NotifySettingSaveTimeout);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MainWidget::incrementSticker(DocumentData *sticker) {
 | 
				
			||||||
 | 
						RecentStickerPack recent(cRecentStickers());
 | 
				
			||||||
 | 
						RecentStickerPack::iterator i = recent.begin(), e = recent.end();
 | 
				
			||||||
 | 
						for (; i != e; ++i) {
 | 
				
			||||||
 | 
							if (i->first == sticker) {
 | 
				
			||||||
 | 
								if (i->second > 0) {
 | 
				
			||||||
 | 
									++i->second;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									--i->second;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (qAbs(i->second) > 0x4000) {
 | 
				
			||||||
 | 
									for (RecentStickerPack::iterator j = recent.begin(); j != e; ++j) {
 | 
				
			||||||
 | 
										if (qAbs(j->second) > 1) {
 | 
				
			||||||
 | 
											j->second /= 2;
 | 
				
			||||||
 | 
										} else if (j->second > 0) {
 | 
				
			||||||
 | 
											j->second = 1;
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											j->second = -1;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								for (; i != recent.begin(); --i) {
 | 
				
			||||||
 | 
									if (qAbs((i - 1)->second) > qAbs(i->second)) {
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									qSwap(*i, *(i - 1));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (i == e) {
 | 
				
			||||||
 | 
							recent.push_front(qMakePair(sticker, -(recent.isEmpty() ? 1 : qAbs(recent.front().second))));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cSetRecentStickers(recent);
 | 
				
			||||||
 | 
						Local::writeRecentStickers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						history.updateRecentStickers();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MainWidget::activate() {
 | 
					void MainWidget::activate() {
 | 
				
			||||||
	if (!profile && !overview) {
 | 
						if (!profile && !overview) {
 | 
				
			||||||
		if (hider) {
 | 
							if (hider) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -196,6 +196,8 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void updateNotifySetting(PeerData *peer, bool enabled);
 | 
						void updateNotifySetting(PeerData *peer, bool enabled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void incrementSticker(DocumentData *sticker);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void activate();
 | 
						void activate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void createDialogAtTop(History *history, int32 unreadCount);
 | 
						void createDialogAtTop(History *history, int32 unreadCount);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -179,7 +179,7 @@ void MediaView::updateControls() {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		_dateText = lng_mediaview_date_time(lt_date, d.date().toString(qsl("dd.MM.yy")), lt_time, d.time().toString(qsl("hh:mm")));
 | 
							_dateText = lng_mediaview_date_time(lt_date, d.date().toString(qsl("dd.MM.yy")), lt_time, d.time().toString(qsl("hh:mm")));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_fromName.setText(st::medviewNameFont, _from->name);
 | 
						if (_from) _fromName.setText(st::medviewNameFont, _from->name);
 | 
				
			||||||
	updateHeader();
 | 
						updateHeader();
 | 
				
			||||||
	_leftNavVisible = _photo && (_index > 0 || (_index == 0 && _history && _history->_overview[OverviewPhotos].size() < _history->_overviewCount[OverviewPhotos]));
 | 
						_leftNavVisible = _photo && (_index > 0 || (_index == 0 && _history && _history->_overview[OverviewPhotos].size() < _history->_overviewCount[OverviewPhotos]));
 | 
				
			||||||
	_rightNavVisible = _photo && (_index >= 0 && (
 | 
						_rightNavVisible = _photo && (_index >= 0 && (
 | 
				
			||||||
| 
						 | 
					@ -480,7 +480,7 @@ void MediaView::showDocument(DocumentData *doc, QPixmap pix, HistoryItem *contex
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_x = (_avail.width() - _w) / 2;
 | 
						_x = (_avail.width() - _w) / 2;
 | 
				
			||||||
	_y = (_avail.height() - st::medviewBottomBar - _h) / 2;
 | 
						_y = (_avail.height() - st::medviewBottomBar - _h) / 2;
 | 
				
			||||||
	_from = App::user(_doc->user);
 | 
						_from = context ? context->from()->asUser() : 0;
 | 
				
			||||||
	_full = 1;
 | 
						_full = 1;
 | 
				
			||||||
	updateControls();
 | 
						updateControls();
 | 
				
			||||||
	if (isHidden()) {
 | 
						if (isHidden()) {
 | 
				
			||||||
| 
						 | 
					@ -721,11 +721,16 @@ void MediaView::paintEvent(QPaintEvent *e) {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		p.setPen(st::medviewNameColor->p);
 | 
							p.setPen(st::medviewNameColor->p);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if (_from) {
 | 
				
			||||||
 | 
							if (_nameNav.intersects(r)) {
 | 
				
			||||||
			if (_over == OverName) _fromName.replaceFont(st::medviewNameFont->underline());
 | 
								if (_over == OverName) _fromName.replaceFont(st::medviewNameFont->underline());
 | 
				
			||||||
	if (_nameNav.intersects(r)) _fromName.drawElided(p, _nameNav.left(), _nameNav.top(), _nameNav.width());
 | 
								_fromName.drawElided(p, _nameNav.left(), _nameNav.top(), _nameNav.width());
 | 
				
			||||||
			if (_over == OverName) _fromName.replaceFont(st::medviewNameFont);
 | 
								if (_over == OverName) _fromName.replaceFont(st::medviewNameFont);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// date
 | 
						// date
 | 
				
			||||||
 | 
						if (_dateNav.intersects(r)) {
 | 
				
			||||||
		if (_doc) {
 | 
							if (_doc) {
 | 
				
			||||||
			float64 o = overLevel(OverDate);
 | 
								float64 o = overLevel(OverDate);
 | 
				
			||||||
			p.setOpacity(st::medviewOverview.overOpacity * o + st::medviewOverview.opacity * (1 - o));
 | 
								p.setOpacity(st::medviewOverview.overOpacity * o + st::medviewOverview.opacity * (1 - o));
 | 
				
			||||||
| 
						 | 
					@ -734,7 +739,8 @@ void MediaView::paintEvent(QPaintEvent *e) {
 | 
				
			||||||
			p.setPen(st::medviewDateColor->p);
 | 
								p.setPen(st::medviewDateColor->p);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		p.setFont((_over == OverDate ? st::medviewDateFont->underline() : st::medviewDateFont)->f);
 | 
							p.setFont((_over == OverDate ? st::medviewDateFont->underline() : st::medviewDateFont)->f);
 | 
				
			||||||
	if (_dateNav.intersects(r)) p.drawText(_dateNav.left(), _dateNav.top() + st::medviewDateFont->ascent, _dateText);
 | 
							p.drawText(_dateNav.left(), _dateNav.top() + st::medviewDateFont->ascent, _dateText);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void MediaView::keyPressEvent(QKeyEvent *e) {
 | 
					void MediaView::keyPressEvent(QKeyEvent *e) {
 | 
				
			||||||
| 
						 | 
					@ -1060,7 +1066,7 @@ void MediaView::mouseReleaseEvent(QMouseEvent *e) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	textlnkDown(TextLinkPtr());
 | 
						textlnkDown(TextLinkPtr());
 | 
				
			||||||
	if (_over == OverName && _down == OverName) {
 | 
						if (_over == OverName && _down == OverName) {
 | 
				
			||||||
		if (App::wnd()) {
 | 
							if (App::wnd() && _from) {
 | 
				
			||||||
			onClose();
 | 
								onClose();
 | 
				
			||||||
			if (App::main()) App::main()->showPeerProfile(_from);
 | 
								if (App::main()) App::main()->showPeerProfile(_from);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -1339,7 +1345,9 @@ void MediaView::updatePolaroid() {
 | 
				
			||||||
		int32 minus1 = width() - _delete.x(), minus2 = _overview.x() + st::medviewHeaderFont->m.width(_header) - st::medviewOverview.width;
 | 
							int32 minus1 = width() - _delete.x(), minus2 = _overview.x() + st::medviewHeaderFont->m.width(_header) - st::medviewOverview.width;
 | 
				
			||||||
		if (minus2 > minus1) minus1 = minus2;
 | 
							if (minus2 > minus1) minus1 = minus2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		int32 nameWidth = _fromName.maxWidth(), maxWidth = width() - 2 * minus1, dateWidth = st::medviewDateFont->m.width(_dateText);
 | 
							int32 dateWidth = st::medviewDateFont->m.width(_dateText), maxWidth = width() - 2 * minus1;
 | 
				
			||||||
 | 
							if (_from) {
 | 
				
			||||||
 | 
								int32 nameWidth = _fromName.maxWidth();
 | 
				
			||||||
			if (maxWidth < dateWidth) {
 | 
								if (maxWidth < dateWidth) {
 | 
				
			||||||
				maxWidth = dateWidth;
 | 
									maxWidth = dateWidth;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -1348,6 +1356,10 @@ void MediaView::updatePolaroid() {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			_nameNav = QRect((_avail.width() - nameWidth) / 2, _avail.y() + _avail.height() - ((st::medviewPolaroid.bottom() + st::medviewBottomBar) / 2) + st::medviewNameTop, nameWidth, st::medviewNameFont->height);
 | 
								_nameNav = QRect((_avail.width() - nameWidth) / 2, _avail.y() + _avail.height() - ((st::medviewPolaroid.bottom() + st::medviewBottomBar) / 2) + st::medviewNameTop, nameWidth, st::medviewNameFont->height);
 | 
				
			||||||
			_dateNav = QRect((_avail.width() - dateWidth) / 2, _avail.y() + _avail.height() - ((st::medviewPolaroid.bottom() + st::medviewBottomBar) / 2) + st::medviewDateTop, dateWidth, st::medviewDateFont->height);
 | 
								_dateNav = QRect((_avail.width() - dateWidth) / 2, _avail.y() + _avail.height() - ((st::medviewPolaroid.bottom() + st::medviewBottomBar) / 2) + st::medviewDateTop, dateWidth, st::medviewDateFont->height);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								_nameNav = QRect(_avail.x() - 1, _avail.y() - 1, 0, 0);
 | 
				
			||||||
 | 
								_dateNav = QRect((_avail.width() - dateWidth) / 2, _avail.y() + _avail.height() - ((st::medviewPolaroid.bottom() + st::medviewBottomBar) / 2) + ((st::medviewNameTop + st::medviewDateTop) / 2), dateWidth, st::medviewDateFont->height);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		int32 pminw = qMin(st::medviewPolaroidMin.width(), int(_avail.width() - 2 * st::medviewNavBarWidth));
 | 
							int32 pminw = qMin(st::medviewPolaroidMin.width(), int(_avail.width() - 2 * st::medviewNavBarWidth));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -762,7 +762,12 @@ inline bool operator!=(const MTPstring &a, const MTPstring &b) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline QString qs(const MTPstring &v) {
 | 
					inline QString qs(const MTPstring &v) {
 | 
				
			||||||
	const string &d(v.c_string().v);
 | 
						const string &d(v.c_string().v);
 | 
				
			||||||
	return QString::fromUtf8(d.c_str(), d.length());
 | 
						return QString::fromUtf8(d.data(), d.length());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline QByteArray qba(const MTPstring &v) {
 | 
				
			||||||
 | 
						const string &d(v.c_string().v);
 | 
				
			||||||
 | 
						return QByteArray(d.data(), d.length());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MTPbool {
 | 
					class MTPbool {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -254,9 +254,18 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!locationType && triedLocal && (fname.isEmpty() || duplicateInData)) {
 | 
							if (!locationType && triedLocal && (fname.isEmpty() || duplicateInData)) {
 | 
				
			||||||
			Local::writeImage(storageKey(dc, volume, local), StorageImageSaved(type, data));
 | 
								Local::writeImage(storageKey(dc, volume, local), StorageImageSaved(type, data));
 | 
				
			||||||
		} else if (locationType && triedLocal && !fname.isEmpty()) {
 | 
							} else if (locationType && triedLocal) {
 | 
				
			||||||
 | 
								if (!fname.isEmpty()) {
 | 
				
			||||||
				Local::writeFileLocation(mediaKey(locationType, dc, id), FileLocation(type, fname));
 | 
									Local::writeFileLocation(mediaKey(locationType, dc, id), FileLocation(type, fname));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if (duplicateInData) {
 | 
				
			||||||
 | 
									if (locationType == mtpc_inputDocumentFileLocation) {
 | 
				
			||||||
 | 
										Local::writeSticker(mediaKey(locationType, dc, id), data);
 | 
				
			||||||
 | 
									} else if (locationType == mtpc_inputAudioFileLocation) {
 | 
				
			||||||
 | 
										Local::writeAudio(mediaKey(locationType, dc, id), data);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	emit progress(this);
 | 
						emit progress(this);
 | 
				
			||||||
	loadNext();
 | 
						loadNext();
 | 
				
			||||||
| 
						 | 
					@ -297,6 +306,25 @@ void mtpFileLoader::start(bool loadFirst, bool prior) {
 | 
				
			||||||
			StorageImageSaved cached = Local::readImage(storageKey(dc, volume, local));
 | 
								StorageImageSaved cached = Local::readImage(storageKey(dc, volume, local));
 | 
				
			||||||
			if (cached.type != mtpc_storage_fileUnknown) {
 | 
								if (cached.type != mtpc_storage_fileUnknown) {
 | 
				
			||||||
				data = cached.data;
 | 
									data = cached.data;
 | 
				
			||||||
 | 
									type = cached.type;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else if (locationType) {
 | 
				
			||||||
 | 
								if (!fname.isEmpty()) {
 | 
				
			||||||
 | 
									triedLocal = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (duplicateInData) {
 | 
				
			||||||
 | 
									if (locationType == mtpc_inputDocumentFileLocation) {
 | 
				
			||||||
 | 
										triedLocal = true;
 | 
				
			||||||
 | 
										data = Local::readSticker(mediaKey(locationType, dc, id));
 | 
				
			||||||
 | 
										if (!data.isEmpty()) type = mtpc_storage_filePartial;
 | 
				
			||||||
 | 
									} else if (locationType == mtpc_inputAudioFileLocation) {
 | 
				
			||||||
 | 
										triedLocal = true;
 | 
				
			||||||
 | 
										data = Local::readAudio(mediaKey(locationType, dc, id));
 | 
				
			||||||
 | 
										if (!data.isEmpty()) type = mtpc_storage_filePartial;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (triedLocal && !data.isEmpty()) {
 | 
				
			||||||
			if (!fname.isEmpty() && duplicateInData) {
 | 
								if (!fname.isEmpty() && duplicateInData) {
 | 
				
			||||||
				if (!fileIsOpen) fileIsOpen = file.open(QIODevice::WriteOnly);
 | 
									if (!fileIsOpen) fileIsOpen = file.open(QIODevice::WriteOnly);
 | 
				
			||||||
				if (!fileIsOpen) {
 | 
									if (!fileIsOpen) {
 | 
				
			||||||
| 
						 | 
					@ -306,7 +334,6 @@ void mtpFileLoader::start(bool loadFirst, bool prior) {
 | 
				
			||||||
					return finishFail();
 | 
										return finishFail();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
				type = cached.type;
 | 
					 | 
				
			||||||
			complete = true;
 | 
								complete = true;
 | 
				
			||||||
			if (fileIsOpen) {
 | 
								if (fileIsOpen) {
 | 
				
			||||||
				file.close();
 | 
									file.close();
 | 
				
			||||||
| 
						 | 
					@ -318,9 +345,6 @@ void mtpFileLoader::start(bool loadFirst, bool prior) {
 | 
				
			||||||
			emit progress(this);
 | 
								emit progress(this);
 | 
				
			||||||
			return loadNext();
 | 
								return loadNext();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		} else if (locationType && !fname.isEmpty()) {
 | 
					 | 
				
			||||||
			triedLocal = true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!fname.isEmpty() && !duplicateInData && !fileIsOpen) {
 | 
						if (!fname.isEmpty() && !duplicateInData && !fileIsOpen) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -357,6 +357,9 @@ enum {
 | 
				
			||||||
	mtpc_documentAttributeVideo = 0x5910cccb,
 | 
						mtpc_documentAttributeVideo = 0x5910cccb,
 | 
				
			||||||
	mtpc_documentAttributeAudio = 0x51448e5,
 | 
						mtpc_documentAttributeAudio = 0x51448e5,
 | 
				
			||||||
	mtpc_documentAttributeFilename = 0x15590068,
 | 
						mtpc_documentAttributeFilename = 0x15590068,
 | 
				
			||||||
 | 
						mtpc_stickerPack = 0x12b299d4,
 | 
				
			||||||
 | 
						mtpc_messages_allStickersNotModified = 0xe86602c3,
 | 
				
			||||||
 | 
						mtpc_messages_allStickers = 0xdcef3102,
 | 
				
			||||||
	mtpc_invokeAfterMsg = 0xcb9f372d,
 | 
						mtpc_invokeAfterMsg = 0xcb9f372d,
 | 
				
			||||||
	mtpc_invokeAfterMsgs = 0x3dc4b4f0,
 | 
						mtpc_invokeAfterMsgs = 0x3dc4b4f0,
 | 
				
			||||||
	mtpc_auth_checkPhone = 0x6fe51dfb,
 | 
						mtpc_auth_checkPhone = 0x6fe51dfb,
 | 
				
			||||||
| 
						 | 
					@ -463,7 +466,8 @@ enum {
 | 
				
			||||||
	mtpc_account_setAccountTTL = 0x2442485e,
 | 
						mtpc_account_setAccountTTL = 0x2442485e,
 | 
				
			||||||
	mtpc_contacts_resolveUsername = 0xbf0131c,
 | 
						mtpc_contacts_resolveUsername = 0xbf0131c,
 | 
				
			||||||
	mtpc_account_sendChangePhoneCode = 0xa407a8f4,
 | 
						mtpc_account_sendChangePhoneCode = 0xa407a8f4,
 | 
				
			||||||
	mtpc_account_changePhone = 0x70c32edb
 | 
						mtpc_account_changePhone = 0x70c32edb,
 | 
				
			||||||
 | 
						mtpc_messages_getAllStickers = 0xaa3bc868
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Type forward declarations
 | 
					// Type forward declarations
 | 
				
			||||||
| 
						 | 
					@ -962,6 +966,12 @@ class MTPDdocumentAttributeVideo;
 | 
				
			||||||
class MTPDdocumentAttributeAudio;
 | 
					class MTPDdocumentAttributeAudio;
 | 
				
			||||||
class MTPDdocumentAttributeFilename;
 | 
					class MTPDdocumentAttributeFilename;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MTPstickerPack;
 | 
				
			||||||
 | 
					class MTPDstickerPack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MTPmessages_allStickers;
 | 
				
			||||||
 | 
					class MTPDmessages_allStickers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Boxed types definitions
 | 
					// Boxed types definitions
 | 
				
			||||||
typedef MTPBoxed<MTPresPQ> MTPResPQ;
 | 
					typedef MTPBoxed<MTPresPQ> MTPResPQ;
 | 
				
			||||||
| 
						 | 
					@ -1092,6 +1102,8 @@ typedef MTPBoxed<MTPaccount_privacyRules> MTPaccount_PrivacyRules;
 | 
				
			||||||
typedef MTPBoxed<MTPaccountDaysTTL> MTPAccountDaysTTL;
 | 
					typedef MTPBoxed<MTPaccountDaysTTL> MTPAccountDaysTTL;
 | 
				
			||||||
typedef MTPBoxed<MTPaccount_sentChangePhoneCode> MTPaccount_SentChangePhoneCode;
 | 
					typedef MTPBoxed<MTPaccount_sentChangePhoneCode> MTPaccount_SentChangePhoneCode;
 | 
				
			||||||
typedef MTPBoxed<MTPdocumentAttribute> MTPDocumentAttribute;
 | 
					typedef MTPBoxed<MTPdocumentAttribute> MTPDocumentAttribute;
 | 
				
			||||||
 | 
					typedef MTPBoxed<MTPstickerPack> MTPStickerPack;
 | 
				
			||||||
 | 
					typedef MTPBoxed<MTPmessages_allStickers> MTPmessages_AllStickers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Type classes definitions
 | 
					// Type classes definitions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7133,6 +7145,75 @@ private:
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
typedef MTPBoxed<MTPdocumentAttribute> MTPDocumentAttribute;
 | 
					typedef MTPBoxed<MTPdocumentAttribute> MTPDocumentAttribute;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MTPstickerPack : private mtpDataOwner {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						MTPstickerPack();
 | 
				
			||||||
 | 
						MTPstickerPack(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_stickerPack) : mtpDataOwner(0) {
 | 
				
			||||||
 | 
							read(from, end, cons);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MTPDstickerPack &_stickerPack() {
 | 
				
			||||||
 | 
							if (!data) throw mtpErrorUninitialized();
 | 
				
			||||||
 | 
							split();
 | 
				
			||||||
 | 
							return *(MTPDstickerPack*)data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						const MTPDstickerPack &c_stickerPack() const {
 | 
				
			||||||
 | 
							if (!data) throw mtpErrorUninitialized();
 | 
				
			||||||
 | 
							return *(const MTPDstickerPack*)data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32 innerLength() const;
 | 
				
			||||||
 | 
						mtpTypeId type() const;
 | 
				
			||||||
 | 
						void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_stickerPack);
 | 
				
			||||||
 | 
						void write(mtpBuffer &to) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						typedef void ResponseType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						explicit MTPstickerPack(MTPDstickerPack *_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						friend MTPstickerPack MTP_stickerPack(const MTPstring &_emoticon, const MTPVector<MTPlong> &_documents);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					typedef MTPBoxed<MTPstickerPack> MTPStickerPack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MTPmessages_allStickers : private mtpDataOwner {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						MTPmessages_allStickers() : mtpDataOwner(0), _type(0) {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						MTPmessages_allStickers(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) : mtpDataOwner(0), _type(0) {
 | 
				
			||||||
 | 
							read(from, end, cons);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MTPDmessages_allStickers &_messages_allStickers() {
 | 
				
			||||||
 | 
							if (!data) throw mtpErrorUninitialized();
 | 
				
			||||||
 | 
							if (_type != mtpc_messages_allStickers) throw mtpErrorWrongTypeId(_type, mtpc_messages_allStickers);
 | 
				
			||||||
 | 
							split();
 | 
				
			||||||
 | 
							return *(MTPDmessages_allStickers*)data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						const MTPDmessages_allStickers &c_messages_allStickers() const {
 | 
				
			||||||
 | 
							if (!data) throw mtpErrorUninitialized();
 | 
				
			||||||
 | 
							if (_type != mtpc_messages_allStickers) throw mtpErrorWrongTypeId(_type, mtpc_messages_allStickers);
 | 
				
			||||||
 | 
							return *(const MTPDmessages_allStickers*)data;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32 innerLength() const;
 | 
				
			||||||
 | 
						mtpTypeId type() const;
 | 
				
			||||||
 | 
						void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons);
 | 
				
			||||||
 | 
						void write(mtpBuffer &to) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						typedef void ResponseType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						explicit MTPmessages_allStickers(mtpTypeId type);
 | 
				
			||||||
 | 
						explicit MTPmessages_allStickers(MTPDmessages_allStickers *_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						friend MTPmessages_allStickers MTP_messages_allStickersNotModified();
 | 
				
			||||||
 | 
						friend MTPmessages_allStickers MTP_messages_allStickers(const MTPstring &_hash, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPDocument> &_documents);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mtpTypeId _type;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					typedef MTPBoxed<MTPmessages_allStickers> MTPmessages_AllStickers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Type constructors with data
 | 
					// Type constructors with data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MTPDresPQ : public mtpDataImpl<MTPDresPQ> {
 | 
					class MTPDresPQ : public mtpDataImpl<MTPDresPQ> {
 | 
				
			||||||
| 
						 | 
					@ -9997,6 +10078,29 @@ public:
 | 
				
			||||||
	MTPstring vfile_name;
 | 
						MTPstring vfile_name;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MTPDstickerPack : public mtpDataImpl<MTPDstickerPack> {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						MTPDstickerPack() {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						MTPDstickerPack(const MTPstring &_emoticon, const MTPVector<MTPlong> &_documents) : vemoticon(_emoticon), vdocuments(_documents) {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MTPstring vemoticon;
 | 
				
			||||||
 | 
						MTPVector<MTPlong> vdocuments;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MTPDmessages_allStickers : public mtpDataImpl<MTPDmessages_allStickers> {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						MTPDmessages_allStickers() {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						MTPDmessages_allStickers(const MTPstring &_hash, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPDocument> &_documents) : vhash(_hash), vpacks(_packs), vdocuments(_documents) {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MTPstring vhash;
 | 
				
			||||||
 | 
						MTPVector<MTPStickerPack> vpacks;
 | 
				
			||||||
 | 
						MTPVector<MTPDocument> vdocuments;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RPC methods
 | 
					// RPC methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MTPreq_pq { // RPC method 'req_pq'
 | 
					class MTPreq_pq { // RPC method 'req_pq'
 | 
				
			||||||
| 
						 | 
					@ -14855,6 +14959,45 @@ public:
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MTPmessages_getAllStickers { // RPC method 'messages.getAllStickers'
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						MTPstring vhash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MTPmessages_getAllStickers() {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						MTPmessages_getAllStickers(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getAllStickers) {
 | 
				
			||||||
 | 
							read(from, end, cons);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						MTPmessages_getAllStickers(const MTPstring &_hash) : vhash(_hash) {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32 innerLength() const {
 | 
				
			||||||
 | 
							return vhash.innerLength();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mtpTypeId type() const {
 | 
				
			||||||
 | 
							return mtpc_messages_getAllStickers;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getAllStickers) {
 | 
				
			||||||
 | 
							vhash.read(from, end);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void write(mtpBuffer &to) const {
 | 
				
			||||||
 | 
							vhash.write(to);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						typedef MTPmessages_AllStickers ResponseType;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					class MTPmessages_GetAllStickers : public MTPBoxed<MTPmessages_getAllStickers> {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						MTPmessages_GetAllStickers() {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						MTPmessages_GetAllStickers(const MTPmessages_getAllStickers &v) : MTPBoxed<MTPmessages_getAllStickers>(v) {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						MTPmessages_GetAllStickers(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPmessages_getAllStickers>(from, end, cons) {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						MTPmessages_GetAllStickers(const MTPstring &_hash) : MTPBoxed<MTPmessages_getAllStickers>(MTPmessages_getAllStickers(_hash)) {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Inline methods definition
 | 
					// Inline methods definition
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline MTPresPQ::MTPresPQ() : mtpDataOwner(new MTPDresPQ()) {
 | 
					inline MTPresPQ::MTPresPQ() : mtpDataOwner(new MTPDresPQ()) {
 | 
				
			||||||
| 
						 | 
					@ -22980,6 +23123,88 @@ inline MTPdocumentAttribute MTP_documentAttributeFilename(const MTPstring &_file
 | 
				
			||||||
	return MTPdocumentAttribute(new MTPDdocumentAttributeFilename(_file_name));
 | 
						return MTPdocumentAttribute(new MTPDdocumentAttributeFilename(_file_name));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline MTPstickerPack::MTPstickerPack() : mtpDataOwner(new MTPDstickerPack()) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline uint32 MTPstickerPack::innerLength() const {
 | 
				
			||||||
 | 
						const MTPDstickerPack &v(c_stickerPack());
 | 
				
			||||||
 | 
						return v.vemoticon.innerLength() + v.vdocuments.innerLength();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline mtpTypeId MTPstickerPack::type() const {
 | 
				
			||||||
 | 
						return mtpc_stickerPack;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline void MTPstickerPack::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
 | 
				
			||||||
 | 
						if (cons != mtpc_stickerPack) throw mtpErrorUnexpected(cons, "MTPstickerPack");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!data) setData(new MTPDstickerPack());
 | 
				
			||||||
 | 
						MTPDstickerPack &v(_stickerPack());
 | 
				
			||||||
 | 
						v.vemoticon.read(from, end);
 | 
				
			||||||
 | 
						v.vdocuments.read(from, end);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline void MTPstickerPack::write(mtpBuffer &to) const {
 | 
				
			||||||
 | 
						const MTPDstickerPack &v(c_stickerPack());
 | 
				
			||||||
 | 
						v.vemoticon.write(to);
 | 
				
			||||||
 | 
						v.vdocuments.write(to);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline MTPstickerPack::MTPstickerPack(MTPDstickerPack *_data) : mtpDataOwner(_data) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline MTPstickerPack MTP_stickerPack(const MTPstring &_emoticon, const MTPVector<MTPlong> &_documents) {
 | 
				
			||||||
 | 
						return MTPstickerPack(new MTPDstickerPack(_emoticon, _documents));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline uint32 MTPmessages_allStickers::innerLength() const {
 | 
				
			||||||
 | 
						switch (_type) {
 | 
				
			||||||
 | 
							case mtpc_messages_allStickers: {
 | 
				
			||||||
 | 
								const MTPDmessages_allStickers &v(c_messages_allStickers());
 | 
				
			||||||
 | 
								return v.vhash.innerLength() + v.vpacks.innerLength() + v.vdocuments.innerLength();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline mtpTypeId MTPmessages_allStickers::type() const {
 | 
				
			||||||
 | 
						if (!_type) throw mtpErrorUninitialized();
 | 
				
			||||||
 | 
						return _type;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline void MTPmessages_allStickers::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
 | 
				
			||||||
 | 
						if (cons != _type) setData(0);
 | 
				
			||||||
 | 
						switch (cons) {
 | 
				
			||||||
 | 
							case mtpc_messages_allStickersNotModified: _type = cons; break;
 | 
				
			||||||
 | 
							case mtpc_messages_allStickers: _type = cons; {
 | 
				
			||||||
 | 
								if (!data) setData(new MTPDmessages_allStickers());
 | 
				
			||||||
 | 
								MTPDmessages_allStickers &v(_messages_allStickers());
 | 
				
			||||||
 | 
								v.vhash.read(from, end);
 | 
				
			||||||
 | 
								v.vpacks.read(from, end);
 | 
				
			||||||
 | 
								v.vdocuments.read(from, end);
 | 
				
			||||||
 | 
							} break;
 | 
				
			||||||
 | 
							default: throw mtpErrorUnexpected(cons, "MTPmessages_allStickers");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline void MTPmessages_allStickers::write(mtpBuffer &to) const {
 | 
				
			||||||
 | 
						switch (_type) {
 | 
				
			||||||
 | 
							case mtpc_messages_allStickers: {
 | 
				
			||||||
 | 
								const MTPDmessages_allStickers &v(c_messages_allStickers());
 | 
				
			||||||
 | 
								v.vhash.write(to);
 | 
				
			||||||
 | 
								v.vpacks.write(to);
 | 
				
			||||||
 | 
								v.vdocuments.write(to);
 | 
				
			||||||
 | 
							} break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline MTPmessages_allStickers::MTPmessages_allStickers(mtpTypeId type) : mtpDataOwner(0), _type(type) {
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
							case mtpc_messages_allStickersNotModified: break;
 | 
				
			||||||
 | 
							case mtpc_messages_allStickers: setData(new MTPDmessages_allStickers()); break;
 | 
				
			||||||
 | 
							default: throw mtpErrorBadTypeId(type, "MTPmessages_allStickers");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline MTPmessages_allStickers::MTPmessages_allStickers(MTPDmessages_allStickers *_data) : mtpDataOwner(_data), _type(mtpc_messages_allStickers) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline MTPmessages_allStickers MTP_messages_allStickersNotModified() {
 | 
				
			||||||
 | 
						return MTPmessages_allStickers(mtpc_messages_allStickersNotModified);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					inline MTPmessages_allStickers MTP_messages_allStickers(const MTPstring &_hash, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPDocument> &_documents) {
 | 
				
			||||||
 | 
						return MTPmessages_allStickers(new MTPDmessages_allStickers(_hash, _packs, _documents));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Human-readable text serialization
 | 
					// Human-readable text serialization
 | 
				
			||||||
#if (defined _DEBUG || defined _WITH_DEBUG)
 | 
					#if (defined _DEBUG || defined _WITH_DEBUG)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -549,6 +549,11 @@ documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute;
 | 
				
			||||||
documentAttributeAudio#51448e5 duration:int = DocumentAttribute;
 | 
					documentAttributeAudio#51448e5 duration:int = DocumentAttribute;
 | 
				
			||||||
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
 | 
					documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					stickerPack#12b299d4 emoticon:string documents:Vector<long> = StickerPack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					messages.allStickersNotModified#e86602c3 = messages.AllStickers;
 | 
				
			||||||
 | 
					messages.allStickers#dcef3102 hash:string packs:Vector<StickerPack> documents:Vector<Document> = messages.AllStickers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---functions---
 | 
					---functions---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
 | 
					invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
 | 
				
			||||||
| 
						 | 
					@ -682,3 +687,5 @@ contacts.resolveUsername#bf0131c username:string = User;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
account.sendChangePhoneCode#a407a8f4 phone_number:string = account.SentChangePhoneCode;
 | 
					account.sendChangePhoneCode#a407a8f4 phone_number:string = account.SentChangePhoneCode;
 | 
				
			||||||
account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User;
 | 
					account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					messages.getAllStickers#aa3bc868 hash:string = messages.AllStickers;
 | 
				
			||||||
| 
						 | 
					@ -850,6 +850,8 @@ void OverviewInner::onUpdateSelected() {
 | 
				
			||||||
		textlnkOver(lnk);
 | 
							textlnkOver(lnk);
 | 
				
			||||||
		App::hoveredLinkItem(lnk ? item : 0);
 | 
							App::hoveredLinkItem(lnk ? item : 0);
 | 
				
			||||||
		updateMsg(App::hoveredLinkItem());
 | 
							updateMsg(App::hoveredLinkItem());
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							App::mousedItem(item);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fixItemIndex(_dragItemIndex, _dragItem);
 | 
						fixItemIndex(_dragItemIndex, _dragItem);
 | 
				
			||||||
| 
						 | 
					@ -1089,6 +1091,23 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
 | 
				
			||||||
			_menu->addAction(lang(lng_context_select_msg), this, SLOT(selectMessage()))->setEnabled(true);
 | 
								_menu->addAction(lang(lng_context_select_msg), this, SLOT(selectMessage()))->setEnabled(true);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		App::contextItem(App::hoveredLinkItem());
 | 
							App::contextItem(App::hoveredLinkItem());
 | 
				
			||||||
 | 
						} else if (App::mousedItem() && App::mousedItem()->id == _mousedItem) {
 | 
				
			||||||
 | 
							_menu = new ContextMenu(_overview);
 | 
				
			||||||
 | 
							_menu->addAction(lang(lng_context_to_msg), this, SLOT(goToMessage()))->setEnabled(true);
 | 
				
			||||||
 | 
							if (isUponSelected > 1) {
 | 
				
			||||||
 | 
								_menu->addAction(lang(lng_context_forward_selected), _overview, SLOT(onForwardSelected()));
 | 
				
			||||||
 | 
								_menu->addAction(lang(lng_context_delete_selected), _overview, SLOT(onDeleteSelected()));
 | 
				
			||||||
 | 
								_menu->addAction(lang(lng_context_clear_selection), _overview, SLOT(onClearSelected()));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (isUponSelected != -2) {
 | 
				
			||||||
 | 
									if (dynamic_cast<HistoryMessage*>(App::mousedItem())) {
 | 
				
			||||||
 | 
										_menu->addAction(lang(lng_context_forward_msg), this, SLOT(forwardMessage()))->setEnabled(true);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									_menu->addAction(lang(lng_context_delete_msg), this, SLOT(deleteMessage()))->setEnabled(true);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								_menu->addAction(lang(lng_context_select_msg), this, SLOT(selectMessage()))->setEnabled(true);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							App::contextItem(App::mousedItem());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (_menu) {
 | 
						if (_menu) {
 | 
				
			||||||
		_menu->deleteOnHide();
 | 
							_menu->deleteOnHide();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,6 +72,10 @@ DBIEmojiTab gEmojiTab = dbietRecent;
 | 
				
			||||||
RecentEmojiPack gRecentEmojis;
 | 
					RecentEmojiPack gRecentEmojis;
 | 
				
			||||||
RecentEmojiPreload gRecentEmojisPreload;
 | 
					RecentEmojiPreload gRecentEmojisPreload;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AllStickers gStickers;
 | 
				
			||||||
 | 
					QByteArray gStickersHash;
 | 
				
			||||||
 | 
					RecentStickerPack gRecentStickers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int32 gLang = -2; // auto
 | 
					int32 gLang = -2; // auto
 | 
				
			||||||
QString gLangFile;
 | 
					QString gLangFile;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -147,7 +151,7 @@ void settingsParseArgs(int argc, char *argv[]) {
 | 
				
			||||||
const RecentEmojiPack &cGetRecentEmojis() {
 | 
					const RecentEmojiPack &cGetRecentEmojis() {
 | 
				
			||||||
	if (cRecentEmojis().isEmpty()) {
 | 
						if (cRecentEmojis().isEmpty()) {
 | 
				
			||||||
		RecentEmojiPack r;
 | 
							RecentEmojiPack r;
 | 
				
			||||||
		if (!cRecentEmojisPreload().isEmpty()) {
 | 
							if (false && !cRecentEmojisPreload().isEmpty()) {
 | 
				
			||||||
			RecentEmojiPreload p(cRecentEmojisPreload());
 | 
								RecentEmojiPreload p(cRecentEmojisPreload());
 | 
				
			||||||
			cSetRecentEmojisPreload(RecentEmojiPreload());
 | 
								cSetRecentEmojisPreload(RecentEmojiPreload());
 | 
				
			||||||
			r.reserve(p.size());
 | 
								r.reserve(p.size());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -145,6 +145,14 @@ DeclareSetting(RecentEmojiPreload, RecentEmojisPreload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const RecentEmojiPack &cGetRecentEmojis();
 | 
					const RecentEmojiPack &cGetRecentEmojis();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct DocumentData;
 | 
				
			||||||
 | 
					typedef QVector<DocumentData*> StickerPack;
 | 
				
			||||||
 | 
					typedef QMap<EmojiPtr, StickerPack> AllStickers;
 | 
				
			||||||
 | 
					DeclareSetting(AllStickers, Stickers);
 | 
				
			||||||
 | 
					DeclareSetting(QByteArray, StickersHash);
 | 
				
			||||||
 | 
					typedef QList<QPair<DocumentData*, int16> > RecentStickerPack;
 | 
				
			||||||
 | 
					DeclareSetting(RecentStickerPack, RecentStickers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DeclareSetting(int32, Lang);
 | 
					DeclareSetting(int32, Lang);
 | 
				
			||||||
DeclareSetting(QString, LangFile);
 | 
					DeclareSetting(QString, LangFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -160,10 +160,11 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : QWidget(parent),
 | 
				
			||||||
	_catsAndDogs(this, lang(lng_settings_cats_and_dogs), cCatsAndDogs()),
 | 
						_catsAndDogs(this, lang(lng_settings_cats_and_dogs), cCatsAndDogs()),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// local storage
 | 
						// local storage
 | 
				
			||||||
	_localImagesClear(this, lang(lng_local_images_clear)),
 | 
						_localStorageClear(this, lang(lng_local_storage_clear)),
 | 
				
			||||||
	_imagesClearingWidth(st::linkFont->m.width(lang(lng_local_images_clearing))),
 | 
						_localStorageHeight(1),
 | 
				
			||||||
	_imagesClearedWidth(st::linkFont->m.width(lang(lng_local_images_cleared))),
 | 
						_storageClearingWidth(st::linkFont->m.width(lang(lng_local_storage_clearing))),
 | 
				
			||||||
	_imagesClearFailedWidth(st::linkFont->m.width(lang(lng_local_images_clear_failed))),
 | 
						_storageClearedWidth(st::linkFont->m.width(lang(lng_local_storage_cleared))),
 | 
				
			||||||
 | 
						_storageClearFailedWidth(st::linkFont->m.width(lang(lng_local_storage_clear_failed))),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// advanced
 | 
						// advanced
 | 
				
			||||||
	_connectionType(this, lng_connection_auto(lt_type, QString())),
 | 
						_connectionType(this, lng_connection_auto(lt_type, QString())),
 | 
				
			||||||
| 
						 | 
					@ -249,11 +250,11 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : QWidget(parent),
 | 
				
			||||||
	connect(&_catsAndDogs, SIGNAL(changed()), this, SLOT(onCatsAndDogs()));
 | 
						connect(&_catsAndDogs, SIGNAL(changed()), this, SLOT(onCatsAndDogs()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// local storage
 | 
						// local storage
 | 
				
			||||||
	connect(&_localImagesClear, SIGNAL(clicked()), this, SLOT(onLocalImagesClear()));
 | 
						connect(&_localStorageClear, SIGNAL(clicked()), this, SLOT(onLocalStorageClear()));
 | 
				
			||||||
	switch (App::wnd()->localImagesState()) {
 | 
						switch (App::wnd()->localStorageState()) {
 | 
				
			||||||
	case Window::TempDirEmpty: _imagesClearState = TempDirEmpty; break;
 | 
						case Window::TempDirEmpty: _storageClearState = TempDirEmpty; break;
 | 
				
			||||||
	case Window::TempDirExists: _imagesClearState = TempDirExists; break;
 | 
						case Window::TempDirExists: _storageClearState = TempDirExists; break;
 | 
				
			||||||
	case Window::TempDirRemoving: _imagesClearState = TempDirClearing; break;
 | 
						case Window::TempDirRemoving: _storageClearState = TempDirClearing; break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// advanced
 | 
						// advanced
 | 
				
			||||||
| 
						 | 
					@ -481,27 +482,45 @@ void SettingsInner::paintEvent(QPaintEvent *e) {
 | 
				
			||||||
		p.setFont(st::setHeaderFont->f);
 | 
							p.setFont(st::setHeaderFont->f);
 | 
				
			||||||
		p.setPen(st::setHeaderColor->p);
 | 
							p.setPen(st::setHeaderColor->p);
 | 
				
			||||||
		p.drawText(_left + st::setHeaderLeft, top + st::setHeaderTop + st::setHeaderFont->ascent, lang(lng_settings_section_cache));
 | 
							p.drawText(_left + st::setHeaderLeft, top + st::setHeaderTop + st::setHeaderFont->ascent, lang(lng_settings_section_cache));
 | 
				
			||||||
		top += st::setHeaderSkip;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		QString localImagesText = lang(lng_settings_no_images_cached);
 | 
					 | 
				
			||||||
		int32 cnt = Local::hasImages();
 | 
					 | 
				
			||||||
		if (cnt) {
 | 
					 | 
				
			||||||
			localImagesText = lng_settings_images_cached(lt_count, cnt, lt_size, formatSizeText(Local::storageFilesSize()));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		p.setFont(st::linkFont->f);
 | 
							p.setFont(st::linkFont->f);
 | 
				
			||||||
		p.setPen(st::black->p);
 | 
							p.setPen(st::black->p);
 | 
				
			||||||
		p.drawText(_left + st::setHeaderLeft, top + st::linkFont->ascent, localImagesText);
 | 
					 | 
				
			||||||
		QString clearText;
 | 
							QString clearText;
 | 
				
			||||||
		int32 clearWidth = 0;
 | 
							int32 clearWidth = 0;
 | 
				
			||||||
		switch (_imagesClearState) {
 | 
							switch (_storageClearState) {
 | 
				
			||||||
		case TempDirClearing: clearText = lang(lng_local_images_clearing); clearWidth = _imagesClearingWidth; break;
 | 
							case TempDirClearing: clearText = lang(lng_local_storage_clearing); clearWidth = _storageClearingWidth; break;
 | 
				
			||||||
		case TempDirCleared: clearText = lang(lng_local_images_cleared); clearWidth = _imagesClearedWidth; break;
 | 
							case TempDirCleared: clearText = lang(lng_local_storage_cleared); clearWidth = _storageClearedWidth; break;
 | 
				
			||||||
		case TempDirClearFailed: clearText = lang(lng_local_images_clear_failed); clearWidth = _imagesClearFailedWidth; break;
 | 
							case TempDirClearFailed: clearText = lang(lng_local_storage_clear_failed); clearWidth = _storageClearFailedWidth; break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (clearWidth) {
 | 
							if (clearWidth) {
 | 
				
			||||||
			p.drawText(_left + st::setWidth - clearWidth, top + st::linkFont->ascent, clearText);
 | 
								p.drawText(_left + st::setWidth - clearWidth, top + st::setHeaderTop + st::setHeaderFont->ascent, clearText);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		top += _localImagesClear.height();
 | 
					
 | 
				
			||||||
 | 
							top += st::setHeaderSkip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							int32 cntImages = Local::hasImages() + Local::hasStickers(), cntAudios = Local::hasAudios();
 | 
				
			||||||
 | 
							if (cntImages > 0 && cntAudios > 0) {
 | 
				
			||||||
 | 
								if (_localStorageHeight != 2) {
 | 
				
			||||||
 | 
									cntAudios = 0;
 | 
				
			||||||
 | 
									QTimer::singleShot(0, this, SLOT(onUpdateLocalStorage()));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (_localStorageHeight != 1) {
 | 
				
			||||||
 | 
									QTimer::singleShot(0, this, SLOT(onUpdateLocalStorage()));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (cntImages > 0) {
 | 
				
			||||||
 | 
								QString cnt = lng_settings_images_cached(lt_count, cntImages, lt_size, formatSizeText(Local::storageImagesSize() + Local::storageStickersSize()));
 | 
				
			||||||
 | 
								p.drawText(_left + st::setHeaderLeft, top + st::linkFont->ascent, cnt);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (_localStorageHeight == 2) top += _localStorageClear.height() + st::setLittleSkip;
 | 
				
			||||||
 | 
							if (cntAudios > 0) {
 | 
				
			||||||
 | 
								QString cnt = lng_settings_audios_cached(lt_count, cntAudios, lt_size, formatSizeText(Local::storageAudiosSize()));
 | 
				
			||||||
 | 
								p.drawText(_left + st::setHeaderLeft, top + st::linkFont->ascent, cnt);
 | 
				
			||||||
 | 
							} else if (cntImages <= 0) {
 | 
				
			||||||
 | 
								p.drawText(_left + st::setHeaderLeft, top + st::linkFont->ascent, lang(lng_settings_no_data_cached));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							top += _localStorageClear.height();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// advanced
 | 
						// advanced
 | 
				
			||||||
| 
						 | 
					@ -590,8 +609,15 @@ void SettingsInner::resizeEvent(QResizeEvent *e) {
 | 
				
			||||||
		_catsAndDogs.move(_left, top); top += _catsAndDogs.height();
 | 
							_catsAndDogs.move(_left, top); top += _catsAndDogs.height();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// local storage
 | 
							// local storage
 | 
				
			||||||
 | 
							_localStorageClear.move(_left + st::setWidth - _localStorageClear.width(), top + st::setHeaderTop + st::setHeaderFont->ascent - st::linkFont->ascent);
 | 
				
			||||||
		top += st::setHeaderSkip;
 | 
							top += st::setHeaderSkip;
 | 
				
			||||||
		_localImagesClear.move(_left + st::setWidth - _localImagesClear.width(), top); top += _localImagesClear.height();
 | 
							if ((Local::hasImages() || Local::hasStickers()) && Local::hasAudios()) {
 | 
				
			||||||
 | 
								_localStorageHeight = 2;
 | 
				
			||||||
 | 
								top += _localStorageClear.height() + st::setLittleSkip;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								_localStorageHeight = 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							top += _localStorageClear.height();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// advanced
 | 
						// advanced
 | 
				
			||||||
| 
						 | 
					@ -816,14 +842,13 @@ void SettingsInner::showAll() {
 | 
				
			||||||
		_dontAskDownloadPath.hide();
 | 
							_dontAskDownloadPath.hide();
 | 
				
			||||||
		_downloadPathEdit.hide();
 | 
							_downloadPathEdit.hide();
 | 
				
			||||||
		_downloadPathClear.hide();
 | 
							_downloadPathClear.hide();
 | 
				
			||||||
		_localImagesClear.hide();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// local storage
 | 
						// local storage
 | 
				
			||||||
	if (self() && _imagesClearState == TempDirExists) {
 | 
						if (self() && _storageClearState == TempDirExists) {
 | 
				
			||||||
		_localImagesClear.show();
 | 
							_localStorageClear.show();
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		_localImagesClear.hide();
 | 
							_localStorageClear.hide();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// advanced
 | 
						// advanced
 | 
				
			||||||
| 
						 | 
					@ -935,6 +960,12 @@ void SettingsInner::onSaveTestLang() {
 | 
				
			||||||
	App::quit();
 | 
						App::quit();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SettingsInner::onUpdateLocalStorage() {
 | 
				
			||||||
 | 
						resizeEvent(0);
 | 
				
			||||||
 | 
						updateSize(width());
 | 
				
			||||||
 | 
						update();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SettingsInner::onAutoUpdate() {
 | 
					void SettingsInner::onAutoUpdate() {
 | 
				
			||||||
	cSetAutoUpdate(!cAutoUpdate());
 | 
						cSetAutoUpdate(!cAutoUpdate());
 | 
				
			||||||
	App::writeConfig();
 | 
						App::writeConfig();
 | 
				
			||||||
| 
						 | 
					@ -1210,9 +1241,9 @@ void SettingsInner::onDownloadPathClearSure() {
 | 
				
			||||||
	update();
 | 
						update();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SettingsInner::onLocalImagesClear() {
 | 
					void SettingsInner::onLocalStorageClear() {
 | 
				
			||||||
	App::wnd()->tempDirDelete(Local::ClearManagerImages);
 | 
						App::wnd()->tempDirDelete(Local::ClearManagerStorage);
 | 
				
			||||||
	_imagesClearState = TempDirClearing;
 | 
						_storageClearState = TempDirClearing;
 | 
				
			||||||
	showAll();
 | 
						showAll();
 | 
				
			||||||
	update();
 | 
						update();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1220,8 +1251,8 @@ void SettingsInner::onLocalImagesClear() {
 | 
				
			||||||
void SettingsInner::onTempDirCleared(int task) {
 | 
					void SettingsInner::onTempDirCleared(int task) {
 | 
				
			||||||
	if (task & Local::ClearManagerDownloads) {
 | 
						if (task & Local::ClearManagerDownloads) {
 | 
				
			||||||
		_tempDirClearState = TempDirCleared;
 | 
							_tempDirClearState = TempDirCleared;
 | 
				
			||||||
	} else if (task & Local::ClearManagerImages) {
 | 
						} else if (task & Local::ClearManagerStorage) {
 | 
				
			||||||
		_imagesClearState = TempDirCleared;
 | 
							_storageClearState = TempDirCleared;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	showAll();
 | 
						showAll();
 | 
				
			||||||
	update();
 | 
						update();
 | 
				
			||||||
| 
						 | 
					@ -1230,8 +1261,8 @@ void SettingsInner::onTempDirCleared(int task) {
 | 
				
			||||||
void SettingsInner::onTempDirClearFailed(int task) {
 | 
					void SettingsInner::onTempDirClearFailed(int task) {
 | 
				
			||||||
	if (task & Local::ClearManagerDownloads) {
 | 
						if (task & Local::ClearManagerDownloads) {
 | 
				
			||||||
		_tempDirClearState = TempDirClearFailed;
 | 
							_tempDirClearState = TempDirClearFailed;
 | 
				
			||||||
	} else if (task & Local::ClearManagerImages) {
 | 
						} else if (task & Local::ClearManagerStorage) {
 | 
				
			||||||
		_imagesClearState = TempDirClearFailed;
 | 
							_storageClearState = TempDirClearFailed;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	showAll();
 | 
						showAll();
 | 
				
			||||||
	update();
 | 
						update();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -127,7 +127,7 @@ public slots:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void onCatsAndDogs();
 | 
						void onCatsAndDogs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void onLocalImagesClear();
 | 
						void onLocalStorageClear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void onUpdateChecking();
 | 
						void onUpdateChecking();
 | 
				
			||||||
	void onUpdateLatest();
 | 
						void onUpdateLatest();
 | 
				
			||||||
| 
						 | 
					@ -145,6 +145,8 @@ public slots:
 | 
				
			||||||
	void onChangeLanguage();
 | 
						void onChangeLanguage();
 | 
				
			||||||
	void onSaveTestLang();
 | 
						void onSaveTestLang();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void onUpdateLocalStorage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void doneResetSessions(const MTPBool &res);
 | 
						void doneResetSessions(const MTPBool &res);
 | 
				
			||||||
| 
						 | 
					@ -222,9 +224,10 @@ private:
 | 
				
			||||||
	FlatCheckbox _catsAndDogs;
 | 
						FlatCheckbox _catsAndDogs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// local storage
 | 
						// local storage
 | 
				
			||||||
	LinkButton _localImagesClear;
 | 
						LinkButton _localStorageClear;
 | 
				
			||||||
	int32 _imagesClearingWidth, _imagesClearedWidth, _imagesClearFailedWidth;
 | 
						int32 _localStorageHeight;
 | 
				
			||||||
	TempDirClearState _imagesClearState;
 | 
						int32 _storageClearingWidth, _storageClearedWidth, _storageClearFailedWidth;
 | 
				
			||||||
 | 
						TempDirClearState _storageClearState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// advanced
 | 
						// advanced
 | 
				
			||||||
	LinkButton _connectionType, _resetSessions;
 | 
						LinkButton _connectionType, _resetSessions;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -319,6 +319,7 @@ enum DBIEmojiTab {
 | 
				
			||||||
	dbietObjects  =  2,
 | 
						dbietObjects  =  2,
 | 
				
			||||||
	dbietPlaces   =  3,
 | 
						dbietPlaces   =  3,
 | 
				
			||||||
	dbietSymbols  =  4,
 | 
						dbietSymbols  =  4,
 | 
				
			||||||
 | 
						dbietStickers =  666,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum DBIPlatform {
 | 
					enum DBIPlatform {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1044,11 +1044,11 @@ Window::TempDirState Window::tempDirState() {
 | 
				
			||||||
	return QDir(cTempDir()).exists() ? TempDirExists : TempDirEmpty;
 | 
						return QDir(cTempDir()).exists() ? TempDirExists : TempDirEmpty;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Window::TempDirState Window::localImagesState() {
 | 
					Window::TempDirState Window::localStorageState() {
 | 
				
			||||||
	if (_clearManager && _clearManager->hasTask(Local::ClearManagerImages)) {
 | 
						if (_clearManager && _clearManager->hasTask(Local::ClearManagerStorage)) {
 | 
				
			||||||
		return TempDirRemoving;
 | 
							return TempDirRemoving;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return Local::hasImages() ? TempDirExists : TempDirEmpty;
 | 
						return (Local::hasImages() || Local::hasStickers() || Local::hasAudios()) ? TempDirExists : TempDirEmpty;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Window::tempDirDelete(int task) {
 | 
					void Window::tempDirDelete(int task) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,7 +201,7 @@ public:
 | 
				
			||||||
		TempDirEmpty,
 | 
							TempDirEmpty,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	TempDirState tempDirState();
 | 
						TempDirState tempDirState();
 | 
				
			||||||
	TempDirState localImagesState();
 | 
						TempDirState localStorageState();
 | 
				
			||||||
	void tempDirDelete(int task);
 | 
						void tempDirDelete(int task);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void quit();
 | 
						void quit();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue