mirror of https://github.com/procxx/kepka.git
				
				
				
			caching web files to local, sending of inline bots results done properly, panel ux better for inline bots
This commit is contained in:
		
							parent
							
								
									945d9b1883
								
							
						
					
					
						commit
						35d4754380
					
				|  | @ -554,8 +554,8 @@ taDefFlat: flatTextarea { | ||||||
| 	font: inpDefFont; | 	font: inpDefFont; | ||||||
| 	cursor: cursor(text); | 	cursor: cursor(text); | ||||||
| 
 | 
 | ||||||
| 	phColor: #AAA; | 	phColor: #999; | ||||||
| 	phFocusColor: #CCC; | 	phFocusColor: #AAA; | ||||||
| 	phAlign: align(topleft); | 	phAlign: align(topleft); | ||||||
| 	phPos: point(2px, 0px); | 	phPos: point(2px, 0px); | ||||||
| 	phShift: 50px; | 	phShift: 50px; | ||||||
|  | @ -1595,7 +1595,7 @@ profileMinBtnPadding: 10px; | ||||||
| membersPadding: margins(0px, 10px, 0px, 10px); | membersPadding: margins(0px, 10px, 0px, 10px); | ||||||
| 
 | 
 | ||||||
| forwardMargins: margins(30px, 10px, 30px, 10px); | forwardMargins: margins(30px, 10px, 30px, 10px); | ||||||
| forwardFont: font(16px);  | forwardFont: font(16px); | ||||||
| forwardBg: rgba(0, 0, 0, 76); | forwardBg: rgba(0, 0, 0, 76); | ||||||
| btnProfileCancel: flatButton(btnDefFlat, btnDefBig) { | btnProfileCancel: flatButton(btnDefFlat, btnDefBig) { | ||||||
| 	color: #666d78; | 	color: #666d78; | ||||||
|  |  | ||||||
|  | @ -1443,6 +1443,18 @@ namespace App { | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	void updateImage(ImagePtr &old, ImagePtr now) { | ||||||
|  | 		if (now->isNull()) return; | ||||||
|  | 		if (old->isNull()) { | ||||||
|  | 			old = now; | ||||||
|  | 		} else if (DelayedStorageImage *img = old->toDelayedStorageImage()) { | ||||||
|  | 			StorageImageLocation loc = now->location(); | ||||||
|  | 			if (!loc.isNull()) { | ||||||
|  | 				img->setStorageLocation(loc); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	PhotoData *photo(const PhotoId &photo) { | 	PhotoData *photo(const PhotoId &photo) { | ||||||
| 		PhotosData::const_iterator i = ::photosData.constFind(photo); | 		PhotosData::const_iterator i = ::photosData.constFind(photo); | ||||||
| 		if (i == ::photosData.cend()) { | 		if (i == ::photosData.cend()) { | ||||||
|  | @ -1465,9 +1477,9 @@ namespace App { | ||||||
| 			if (date) { | 			if (date) { | ||||||
| 				convert->access = access; | 				convert->access = access; | ||||||
| 				convert->date = date; | 				convert->date = date; | ||||||
| 				if (convert->thumb->isNull() && !thumb->isNull()) convert->thumb = thumb; | 				updateImage(convert->thumb, thumb); | ||||||
| 				if (convert->medium->isNull() && !medium->isNull()) convert->medium = medium; | 				updateImage(convert->medium, medium); | ||||||
| 				if (convert->full->isNull() && !full->isNull()) convert->full = full; | 				updateImage(convert->full, full); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		PhotosData::const_iterator i = ::photosData.constFind(photo); | 		PhotosData::const_iterator i = ::photosData.constFind(photo); | ||||||
|  | @ -1485,9 +1497,9 @@ namespace App { | ||||||
| 			if (result != convert && date) { | 			if (result != convert && date) { | ||||||
| 				result->access = access; | 				result->access = access; | ||||||
| 				result->date = date; | 				result->date = date; | ||||||
| 				if (result->thumb->isNull() && !thumb->isNull()) result->thumb = thumb; | 				updateImage(result->thumb, thumb); | ||||||
| 				if (result->medium->isNull() && !medium->isNull()) result->medium = medium; | 				updateImage(result->medium, medium); | ||||||
| 				if (result->full->isNull() && !full->isNull()) result->full = full; | 				updateImage(result->full, full); | ||||||
| 			} | 			} | ||||||
| 			inLastIter = lastPhotosMap.find(result); | 			inLastIter = lastPhotosMap.find(result); | ||||||
| 		} | 		} | ||||||
|  | @ -1526,9 +1538,7 @@ namespace App { | ||||||
| 			if (date) { | 			if (date) { | ||||||
| 				convert->access = access; | 				convert->access = access; | ||||||
| 				convert->date = date; | 				convert->date = date; | ||||||
| 				if (convert->thumb->isNull() && !thumb->isNull()) { | 				updateImage(convert->thumb, thumb); | ||||||
| 					convert->thumb = thumb; |  | ||||||
| 				} |  | ||||||
| 				convert->duration = duration; | 				convert->duration = duration; | ||||||
| 				convert->w = w; | 				convert->w = w; | ||||||
| 				convert->h = h; | 				convert->h = h; | ||||||
|  | @ -1553,9 +1563,7 @@ namespace App { | ||||||
| 				result->duration = duration; | 				result->duration = duration; | ||||||
| 				result->w = w; | 				result->w = w; | ||||||
| 				result->h = h; | 				result->h = h; | ||||||
| 				if (result->thumb->isNull() && !thumb->isNull()) { | 				updateImage(result->thumb, thumb); | ||||||
| 					result->thumb = thumb; |  | ||||||
| 				} |  | ||||||
| 				result->dc = dc; | 				result->dc = dc; | ||||||
| 				result->size = size; | 				result->size = size; | ||||||
| 			} | 			} | ||||||
|  | @ -1632,9 +1640,6 @@ namespace App { | ||||||
| 				Local::copyStickerImage(mediaKey(DocumentFileLocation, convert->dc, convert->id), mediaKey(DocumentFileLocation, dc, document)); | 				Local::copyStickerImage(mediaKey(DocumentFileLocation, convert->dc, convert->id), mediaKey(DocumentFileLocation, dc, document)); | ||||||
| 				convert->id = document; | 				convert->id = document; | ||||||
| 				convert->status = FileReady; | 				convert->status = FileReady; | ||||||
| 				if (cSavedGifs().indexOf(convert) >= 0) { // id changed
 |  | ||||||
| 					Local::writeSavedGifs(); |  | ||||||
| 				} |  | ||||||
| 				sentSticker = !!convert->sticker(); | 				sentSticker = !!convert->sticker(); | ||||||
| 			} | 			} | ||||||
| 			if (date) { | 			if (date) { | ||||||
|  | @ -1652,6 +1657,11 @@ namespace App { | ||||||
| 					convert->sticker()->loc = thumbLocation; | 					convert->sticker()->loc = thumbLocation; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			if (cSavedGifs().indexOf(convert) >= 0) { // id changed
 | ||||||
|  | 				Local::writeSavedGifs(); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			const FileLocation &loc(convert->location(true)); | 			const FileLocation &loc(convert->location(true)); | ||||||
| 			if (!loc.isEmpty()) { | 			if (!loc.isEmpty()) { | ||||||
| 				Local::writeFileLocation(mediaKey(DocumentFileLocation, convert->dc, convert->id), loc); | 				Local::writeFileLocation(mediaKey(DocumentFileLocation, convert->dc, convert->id), loc); | ||||||
|  |  | ||||||
|  | @ -330,6 +330,7 @@ void AutoDownloadBox::onSave() { | ||||||
| 			for (DocumentsData::const_iterator i = data.cbegin(), e = data.cend(); i != e; ++i) { | 			for (DocumentsData::const_iterator i = data.cbegin(), e = data.cend(); i != e; ++i) { | ||||||
| 				i.value()->automaticLoadSettingsChanged(); | 				i.value()->automaticLoadSettingsChanged(); | ||||||
| 			} | 			} | ||||||
|  | 			Notify::automaticLoadSettingsChangedGif(); | ||||||
| 		} | 		} | ||||||
| 		changed = true; | 		changed = true; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -1143,7 +1143,7 @@ void EmojiPanInner::updateSelected() { | ||||||
| 
 | 
 | ||||||
| 	bool startanim = false; | 	bool startanim = false; | ||||||
| 	int oldSel = _selected, newSel = selIndex; | 	int oldSel = _selected, newSel = selIndex; | ||||||
| 	 | 
 | ||||||
| 	if (newSel != oldSel) { | 	if (newSel != oldSel) { | ||||||
| 		if (oldSel >= 0) { | 		if (oldSel >= 0) { | ||||||
| 			_animations.remove(oldSel + 1); | 			_animations.remove(oldSel + 1); | ||||||
|  | @ -1229,7 +1229,7 @@ StickerPanInner::StickerPanInner() : TWidget() | ||||||
| 
 | 
 | ||||||
| 	connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); | 	connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); | ||||||
| 	connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings())); | 	connect(&_settings, SIGNAL(clicked()), this, SLOT(onSettings())); | ||||||
| 	 | 
 | ||||||
| 	_previewTimer.setSingleShot(true); | 	_previewTimer.setSingleShot(true); | ||||||
| 	connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); | 	connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); | ||||||
| 
 | 
 | ||||||
|  | @ -1252,7 +1252,7 @@ void StickerPanInner::setScrollTop(int top) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int StickerPanInner::countHeight() { | int StickerPanInner::countHeight() { | ||||||
| 	int result = 0, minLastH = _maxHeight - st::rbEmoji.height - st::stickerPanPadding; | 	int result = 0, minLastH = _maxHeight - st::stickerPanPadding; | ||||||
| 	if (_showingInlineItems) { | 	if (_showingInlineItems) { | ||||||
| 		result = st::emojiPanHeader; | 		result = st::emojiPanHeader; | ||||||
| 		for (int i = 0, l = _inlineRows.count(); i < l; ++i) { | 		for (int i = 0, l = _inlineRows.count(); i < l; ++i) { | ||||||
|  | @ -1560,6 +1560,10 @@ void StickerPanInner::enterFromChildEvent(QEvent *e) { | ||||||
| 	updateSelected(); | 	updateSelected(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool StickerPanInner::showSectionIcons() const { | ||||||
|  | 	return !inlineResultsShown(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void StickerPanInner::clearSelection(bool fast) { | void StickerPanInner::clearSelection(bool fast) { | ||||||
| 	_lastMousePos = mapToGlobal(QPoint(-10, -10)); | 	_lastMousePos = mapToGlobal(QPoint(-10, -10)); | ||||||
| 	if (fast) { | 	if (fast) { | ||||||
|  | @ -2347,6 +2351,7 @@ void StickerPanInner::showStickerSet(uint64 setId) { | ||||||
| 		refreshSavedGifs(); | 		refreshSavedGifs(); | ||||||
| 		emit scrollToY(0); | 		emit scrollToY(0); | ||||||
| 		emit scrollUpdated(); | 		emit scrollUpdated(); | ||||||
|  | 		_setGifCommand = App::insertBotCommand(qsl("@gif")); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -2447,9 +2452,28 @@ EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button( | ||||||
| 	updateText(); | 	updateText(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiSwitchButton::updateText() { | void EmojiSwitchButton::updateText(const QString &inlineBotUsername) { | ||||||
| 	_text = lang(_toStickers ? (cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs) : lng_switch_emoji); | 	if (_toStickers) { | ||||||
|  | 		if (inlineBotUsername.isEmpty()) { | ||||||
|  | 			_text = lang(cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs); | ||||||
|  | 		} else { | ||||||
|  | 			_text = '@' + inlineBotUsername; | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		_text = lang(lng_switch_emoji); | ||||||
|  | 	} | ||||||
| 	_textWidth = st::emojiPanHeaderFont->width(_text); | 	_textWidth = st::emojiPanHeaderFont->width(_text); | ||||||
|  | 	if (_toStickers && !inlineBotUsername.isEmpty()) { | ||||||
|  | 		int32 maxw = 0; | ||||||
|  | 		for (int c = 0; c < emojiTabCount; ++c) { | ||||||
|  | 			maxw = qMax(maxw, st::emojiPanHeaderFont->width(lang(LangKey(lng_emoji_category0 + c)))); | ||||||
|  | 		} | ||||||
|  | 		maxw += st::emojiPanHeaderLeft + st::emojiSwitchSkip + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); | ||||||
|  | 		if (_textWidth > st::emojiPanWidth - maxw) { | ||||||
|  | 			_text = st::emojiPanHeaderFont->elided(_text, st::emojiPanWidth - maxw); | ||||||
|  | 			_textWidth = st::emojiPanHeaderFont->width(_text); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); | 	int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); | ||||||
| 	resize(w, st::emojiPanHeader); | 	resize(w, st::emojiPanHeader); | ||||||
|  | @ -2471,6 +2495,8 @@ void EmojiSwitchButton::paintEvent(QPaintEvent *e) { | ||||||
| 
 | 
 | ||||||
| EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) | EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) | ||||||
| , _maxHeight(st::emojiPanMaxHeight) | , _maxHeight(st::emojiPanMaxHeight) | ||||||
|  | , _maxHeightEmoji(_maxHeight - st::rbEmoji.height) | ||||||
|  | , _maxHeightStickers(_maxHeight - st::rbEmoji.height) | ||||||
| , _horizontal(false) | , _horizontal(false) | ||||||
| , _noTabUpdate(false) | , _noTabUpdate(false) | ||||||
| , _hiding(false) | , _hiding(false) | ||||||
|  | @ -2518,8 +2544,8 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) | ||||||
| 	_height = st::dropdownDef.padding.top() + _maxHeight + st::dropdownDef.padding.bottom(); | 	_height = st::dropdownDef.padding.top() + _maxHeight + st::dropdownDef.padding.bottom(); | ||||||
| 	resize(_width, _height); | 	resize(_width, _height); | ||||||
| 
 | 
 | ||||||
| 	e_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); | 	e_scroll.resize(st::emojiPanWidth, _maxHeightEmoji); | ||||||
| 	s_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); | 	s_scroll.resize(st::emojiPanWidth, _maxHeightStickers); | ||||||
| 
 | 
 | ||||||
| 	e_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); | 	e_scroll.move(st::dropdownDef.padding.left(), st::dropdownDef.padding.top()); | ||||||
| 	e_scroll.setWidget(&e_inner); | 	e_scroll.setWidget(&e_inner); | ||||||
|  | @ -2588,24 +2614,28 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) | ||||||
| 
 | 
 | ||||||
| void EmojiPan::setMaxHeight(int32 h) { | void EmojiPan::setMaxHeight(int32 h) { | ||||||
| 	h = qMin(int(st::emojiPanMaxHeight), h); | 	h = qMin(int(st::emojiPanMaxHeight), h); | ||||||
| 	if (h == _maxHeight) return; | 	int32 he = h - st::rbEmoji.height; | ||||||
|  | 	int32 hs = h - (s_inner.showSectionIcons() ? st::rbEmoji.height : 0); | ||||||
|  | 	if (h == _maxHeight && he == _maxHeightEmoji && hs == _maxHeightStickers) return; | ||||||
| 
 | 
 | ||||||
| 	int32 was = _maxHeight; | 	int32 was = _maxHeight, wase = _maxHeightEmoji, wass = _maxHeightStickers; | ||||||
| 	_maxHeight = h; | 	_maxHeight = h; | ||||||
|  | 	_maxHeightEmoji = he; | ||||||
|  | 	_maxHeightStickers = hs; | ||||||
| 
 | 
 | ||||||
| 	_height = st::dropdownDef.padding.top() + _maxHeight + st::dropdownDef.padding.bottom(); | 	_height = st::dropdownDef.padding.top() + _maxHeight + st::dropdownDef.padding.bottom(); | ||||||
| 	resize(_width, _height); | 	resize(_width, _height); | ||||||
| 
 | 
 | ||||||
| 	if (was > _maxHeight) { | 	if (was > _maxHeight || (was == _maxHeight && wass > _maxHeightStickers)) { | ||||||
| 		e_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); | 		e_scroll.resize(st::emojiPanWidth, _maxHeightEmoji); | ||||||
| 		s_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); | 		s_scroll.resize(st::emojiPanWidth, _maxHeightStickers); | ||||||
| 		s_inner.setMaxHeight(_maxHeight); | 		s_inner.setMaxHeight(_maxHeightStickers); | ||||||
| 		e_inner.setMaxHeight(_maxHeight); | 		e_inner.setMaxHeight(_maxHeightEmoji); | ||||||
| 	} else { | 	} else { | ||||||
| 		s_inner.setMaxHeight(_maxHeight); | 		s_inner.setMaxHeight(_maxHeightStickers); | ||||||
| 		e_inner.setMaxHeight(_maxHeight); | 		e_inner.setMaxHeight(_maxHeightEmoji); | ||||||
| 		e_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); | 		e_scroll.resize(st::emojiPanWidth, _maxHeightEmoji); | ||||||
| 		s_scroll.resize(st::emojiPanWidth, _maxHeight - st::rbEmoji.height); | 		s_scroll.resize(st::emojiPanWidth, _maxHeightStickers); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_iconsTop = st::dropdownDef.padding.top() + _maxHeight - st::rbEmoji.height; | 	_iconsTop = st::dropdownDef.padding.top() + _maxHeight - st::rbEmoji.height; | ||||||
|  | @ -2657,7 +2687,7 @@ void EmojiPan::paintEvent(QPaintEvent *e) { | ||||||
| 	if (_toCache.isNull()) { | 	if (_toCache.isNull()) { | ||||||
| 		if (_cache.isNull()) { | 		if (_cache.isNull()) { | ||||||
| 			p.fillRect(myrtlrect(r.x() + r.width() - st::emojiScroll.width, r.y(), st::emojiScroll.width, e_scroll.height()), st::white->b); | 			p.fillRect(myrtlrect(r.x() + r.width() - st::emojiScroll.width, r.y(), st::emojiScroll.width, e_scroll.height()), st::white->b); | ||||||
| 			if (_stickersShown) { | 			if (_stickersShown && s_inner.showSectionIcons()) { | ||||||
| 				p.fillRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height, st::emojiPanCategories->b); | 				p.fillRect(r.left(), _iconsTop, r.width(), st::rbEmoji.height, st::emojiPanCategories->b); | ||||||
| 				p.drawSpriteLeft(_iconsLeft + 7 * st::rbEmoji.width + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::stickersSettings); | 				p.drawSpriteLeft(_iconsLeft + 7 * st::rbEmoji.width + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::stickersSettings); | ||||||
| 
 | 
 | ||||||
|  | @ -2748,6 +2778,18 @@ void EmojiPan::paintEvent(QPaintEvent *e) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void EmojiPan::moveBottom(int32 bottom, bool force) { | ||||||
|  | 	if (isHidden() && !force) { | ||||||
|  | 		move(x(), bottom - height()); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	if (_stickersShown && s_inner.inlineResultsShown()) { | ||||||
|  | 		moveToLeft(0, bottom - height()); | ||||||
|  | 	} else { | ||||||
|  | 		moveToRight(0, bottom - height()); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void EmojiPan::enterEvent(QEvent *e) { | void EmojiPan::enterEvent(QEvent *e) { | ||||||
| 	_hideTimer.stop(); | 	_hideTimer.stop(); | ||||||
| 	if (_hiding) showStart(); | 	if (_hiding) showStart(); | ||||||
|  | @ -2919,7 +2961,10 @@ void EmojiPan::onRefreshIcons() { | ||||||
| 	} | 	} | ||||||
| 	updatePanelsPositions(s_panels, s_scroll.scrollTop()); | 	updatePanelsPositions(s_panels, s_scroll.scrollTop()); | ||||||
| 	updateSelected(); | 	updateSelected(); | ||||||
| 	if (_stickersShown) validateSelectedIcon(); | 	if (_stickersShown) { | ||||||
|  | 		validateSelectedIcon(); | ||||||
|  | 		setMaxHeight(_maxHeight); | ||||||
|  | 	} | ||||||
| 	updateIcons(); | 	updateIcons(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2995,6 +3040,8 @@ void EmojiPan::updateSelected() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiPan::updateIcons() { | void EmojiPan::updateIcons() { | ||||||
|  | 	if (!_stickersShown || !s_inner.showSectionIcons()) return; | ||||||
|  | 
 | ||||||
| 	QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); | 	QRect r(st::dropdownDef.padding.left(), st::dropdownDef.padding.top(), _width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(), _height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()); | ||||||
| 	update(r.left(), _iconsTop, r.width(), st::rbEmoji.height); | 	update(r.left(), _iconsTop, r.width(), st::rbEmoji.height); | ||||||
| } | } | ||||||
|  | @ -3108,6 +3155,7 @@ void EmojiPan::hideFinish() { | ||||||
| 	_cache = _toCache = _fromCache = QPixmap(); | 	_cache = _toCache = _fromCache = QPixmap(); | ||||||
| 	_a_slide.stop(); | 	_a_slide.stop(); | ||||||
| 	_horizontal = false; | 	_horizontal = false; | ||||||
|  | 	_hiding = false; | ||||||
| 
 | 
 | ||||||
| 	e_scroll.scrollToY(0); | 	e_scroll.scrollToY(0); | ||||||
| 	if (!_recent.checked()) { | 	if (!_recent.checked()) { | ||||||
|  | @ -3129,16 +3177,26 @@ void EmojiPan::hideFinish() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiPan::showStart() { | void EmojiPan::showStart() { | ||||||
| 	if (!isHidden() && a_opacity.current() == 1) { | 	if (!isHidden() && !_hiding) { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	if (isHidden()) { | 	if (isHidden()) { | ||||||
| 		e_inner.refreshRecent(); | 		e_inner.refreshRecent(); | ||||||
| 		s_inner.refreshRecent(); | 		if (s_inner.inlineResultsShown() && refreshInlineRows()) { | ||||||
|  | 			_stickersShown = true; | ||||||
|  | 		} else { | ||||||
|  | 			s_inner.refreshRecent(); | ||||||
|  | 			_stickersShown = false; | ||||||
|  | 		} | ||||||
| 		s_inner.preloadImages(); | 		s_inner.preloadImages(); | ||||||
| 		_stickersShown = false; | 		setMaxHeight(_maxHeight); | ||||||
| 		_fromCache = _toCache = QPixmap(); | 		_fromCache = _toCache = QPixmap(); | ||||||
| 		_a_slide.stop(); | 		_a_slide.stop(); | ||||||
|  | 		moveBottom(y() + height(), true); | ||||||
|  | 	} else if (_hiding) { | ||||||
|  | 		if (s_inner.inlineResultsShown() && refreshInlineRows()) { | ||||||
|  | 			onSwitch(); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	if (_cache.isNull()) { | 	if (_cache.isNull()) { | ||||||
| 		QPixmap from = _fromCache, to = _toCache; | 		QPixmap from = _fromCache, to = _toCache; | ||||||
|  | @ -3170,9 +3228,10 @@ bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { | ||||||
| 		//}
 | 		//}
 | ||||||
| 	} else if (e->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton/* && !dynamic_cast<StickerPan*>(obj)*/) { | 	} else if (e->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(e)->button() == Qt::LeftButton/* && !dynamic_cast<StickerPan*>(obj)*/) { | ||||||
| 		if (isHidden() || _hiding) { | 		if (isHidden() || _hiding) { | ||||||
| 			otherEnter(); | 			_hideTimer.stop(); | ||||||
|  | 			showStart(); | ||||||
| 		} else { | 		} else { | ||||||
| 			otherLeave(); | 			hideAnimated(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return false; | 	return false; | ||||||
|  | @ -3181,6 +3240,7 @@ bool EmojiPan::eventFilter(QObject *obj, QEvent *e) { | ||||||
| void EmojiPan::stickersInstalled(uint64 setId) { | void EmojiPan::stickersInstalled(uint64 setId) { | ||||||
| 	_stickersShown = true; | 	_stickersShown = true; | ||||||
| 	if (isHidden()) { | 	if (isHidden()) { | ||||||
|  | 		moveBottom(y() + height(), true); | ||||||
| 		show(); | 		show(); | ||||||
| 		a_opacity = anim::fvalue(0, 1); | 		a_opacity = anim::fvalue(0, 1); | ||||||
| 		a_opacity.update(0, anim::linear); | 		a_opacity.update(0, anim::linear); | ||||||
|  | @ -3188,6 +3248,7 @@ void EmojiPan::stickersInstalled(uint64 setId) { | ||||||
| 	} | 	} | ||||||
| 	showAll(); | 	showAll(); | ||||||
| 	s_inner.showStickerSet(setId); | 	s_inner.showStickerSet(setId); | ||||||
|  | 	setMaxHeight(_maxHeight); | ||||||
| 	showStart(); | 	showStart(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -3211,6 +3272,14 @@ bool EmojiPan::ui_isInlineItemBeingChosen() { | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void EmojiPan::notify_automaticLoadSettingsChangedGif() { | ||||||
|  | 	for (InlineCache::const_iterator i = _inlineCache.cbegin(), ei = _inlineCache.cend(); i != ei; ++i) { | ||||||
|  | 		for (InlineResults::const_iterator j = i.value()->results.cbegin(), ej = i.value()->results.cend(); j != ej; ++j) { | ||||||
|  | 			(*j)->automaticLoadSettingsChangedGif(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void EmojiPan::showAll() { | void EmojiPan::showAll() { | ||||||
| 	if (_stickersShown) { | 	if (_stickersShown) { | ||||||
| 		s_scroll.show(); | 		s_scroll.show(); | ||||||
|  | @ -3344,20 +3413,20 @@ void EmojiPan::onSwitch() { | ||||||
| 	_stickersShown = !_stickersShown; | 	_stickersShown = !_stickersShown; | ||||||
| 	if (!_stickersShown) { | 	if (!_stickersShown) { | ||||||
| 		Notify::clipStopperHidden(ClipStopperSavedGifsPanel); | 		Notify::clipStopperHidden(ClipStopperSavedGifsPanel); | ||||||
| 	} | 	} else { | ||||||
| 
 | 		if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { | ||||||
| 	if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { | 			s_inner.showStickerSet(DefaultStickerSetId); | ||||||
| 		s_inner.showStickerSet(DefaultStickerSetId); | 		} else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && cStickerSets().isEmpty()) { | ||||||
| 	} else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && cStickerSets().isEmpty()) { | 			s_inner.showStickerSet(NoneStickerSetId); | ||||||
| 		s_inner.showStickerSet(NoneStickerSetId); | 		} | ||||||
|  | 		validateSelectedIcon(); | ||||||
|  | 		setMaxHeight(_maxHeight); | ||||||
| 	} | 	} | ||||||
| 	_iconOver = -1; | 	_iconOver = -1; | ||||||
| 	_iconHovers = _icons.isEmpty() ? QVector<float64>() : QVector<float64>(_icons.size(), 0); | 	_iconHovers = _icons.isEmpty() ? QVector<float64>() : QVector<float64>(_icons.size(), 0); | ||||||
| 	_iconAnimations.clear(); | 	_iconAnimations.clear(); | ||||||
| 	_a_icons.stop(); | 	_a_icons.stop(); | ||||||
| 
 | 
 | ||||||
| 	validateSelectedIcon(); |  | ||||||
| 
 |  | ||||||
| 	_cache = QPixmap(); | 	_cache = QPixmap(); | ||||||
| 	showAll(); | 	showAll(); | ||||||
| 	_toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); | 	_toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); | ||||||
|  | @ -3425,10 +3494,20 @@ void EmojiPan::onDelayedHide() { | ||||||
| 	_removingSetId = 0; | 	_removingSetId = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void EmojiPan::clearInlineBot() { | ||||||
|  | 	inlineBotChanged(); | ||||||
|  | 	e_switch.updateText(); | ||||||
|  | 	e_switch.moveToRight(0, 0, st::emojiPanWidth); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void EmojiPan::inlineBotChanged() { | void EmojiPan::inlineBotChanged() { | ||||||
| 	if (!_inlineBot) return; | 	if (!_inlineBot) return; | ||||||
| 
 | 
 | ||||||
| 	if (!isHidden()) hideAnimated(); | 	if (!isHidden() && !_hiding) { | ||||||
|  | 		if (_stickersShown || !rect().contains(mapFromGlobal(QCursor::pos()))) { | ||||||
|  | 			hideAnimated(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if (_inlineRequestId) MTP::cancel(_inlineRequestId); | 	if (_inlineRequestId) MTP::cancel(_inlineRequestId); | ||||||
| 	_inlineRequestId = 0; | 	_inlineRequestId = 0; | ||||||
|  | @ -3555,6 +3634,7 @@ void EmojiPan::queryInlineBot(UserData *bot, QString query) { | ||||||
| 			_inlineRequestId = 0; | 			_inlineRequestId = 0; | ||||||
| 		} | 		} | ||||||
| 		if (_inlineCache.contains(query)) { | 		if (_inlineCache.contains(query)) { | ||||||
|  | 			_inlineRequestTimer.stop(); | ||||||
| 			_inlineQuery = query; | 			_inlineQuery = query; | ||||||
| 			showInlineRows(true); | 			showInlineRows(true); | ||||||
| 		} else { | 		} else { | ||||||
|  | @ -3565,7 +3645,7 @@ void EmojiPan::queryInlineBot(UserData *bot, QString query) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiPan::onInlineRequest() { | void EmojiPan::onInlineRequest() { | ||||||
| 	if (_inlineRequestId) return; | 	if (_inlineRequestId || !_inlineBot) return; | ||||||
| 	_inlineQuery = _inlineNextQuery; | 	_inlineQuery = _inlineNextQuery; | ||||||
| 
 | 
 | ||||||
| 	QString nextOffset; | 	QString nextOffset; | ||||||
|  | @ -3577,7 +3657,7 @@ void EmojiPan::onInlineRequest() { | ||||||
| 	_inlineRequestId = MTP::send(MTPmessages_GetInlineBotResults(_inlineBot->inputUser, MTP_string(_inlineQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::inlineResultsDone), rpcFail(&EmojiPan::inlineResultsFail)); | 	_inlineRequestId = MTP::send(MTPmessages_GetInlineBotResults(_inlineBot->inputUser, MTP_string(_inlineQuery), MTP_string(nextOffset)), rpcDone(&EmojiPan::inlineResultsDone), rpcFail(&EmojiPan::inlineResultsFail)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiPan::showInlineRows(bool newResults) { | bool EmojiPan::refreshInlineRows() { | ||||||
| 	bool clear = true; | 	bool clear = true; | ||||||
| 	InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery); | 	InlineCache::const_iterator i = _inlineCache.constFind(_inlineQuery); | ||||||
| 	if (i != _inlineCache.cend()) { | 	if (i != _inlineCache.cend()) { | ||||||
|  | @ -3586,26 +3666,25 @@ void EmojiPan::showInlineRows(bool newResults) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	s_inner.refreshInlineRows(_inlineBot, clear ? InlineResults() : i.value()->results, false); | 	s_inner.refreshInlineRows(_inlineBot, clear ? InlineResults() : i.value()->results, false); | ||||||
|  | 	return !clear; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EmojiPan::showInlineRows(bool newResults) { | ||||||
|  | 	bool clear = !refreshInlineRows(); | ||||||
| 	if (newResults) s_scroll.scrollToY(0); | 	if (newResults) s_scroll.scrollToY(0); | ||||||
| 	if (clear && !isHidden() && _stickersShown && s_inner.inlineResultsShown()) { | 
 | ||||||
|  | 	e_switch.updateText(clear ? QString() : _inlineBot->username); | ||||||
|  | 	e_switch.moveToRight(0, 0, st::emojiPanWidth); | ||||||
|  | 
 | ||||||
|  | 	bool hidden = isHidden(); | ||||||
|  | 	if (clear && !hidden && _stickersShown && s_inner.inlineResultsShown()) { | ||||||
| 		hideAnimated(); | 		hideAnimated(); | ||||||
| 	} else if (!clear) { | 	} else if (!clear) { | ||||||
| 		_hideTimer.stop(); | 		_hideTimer.stop(); | ||||||
| 		if (!isHidden() || _hiding) { | 		if (hidden || _hiding) { | ||||||
| 			if (!_stickersShown) { |  | ||||||
| 				onSwitch(); |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			_stickersShown = true; |  | ||||||
| 			if (isHidden()) { |  | ||||||
| 				show(); |  | ||||||
| 				a_opacity = anim::fvalue(0, 1); |  | ||||||
| 				a_opacity.update(0, anim::linear); |  | ||||||
| 				_cache = _fromCache = _toCache = QPixmap(); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if (isHidden() || _hiding) { |  | ||||||
| 			showStart(); | 			showStart(); | ||||||
|  | 		} else if (!_stickersShown) { | ||||||
|  | 			onSwitch(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -3662,7 +3741,7 @@ void MentionsInner::paintEvent(QPaintEvent *e) { | ||||||
| 			user->photo->load(); | 			user->photo->load(); | ||||||
| 			p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize)); | 			p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize)); | ||||||
| 			user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth); | 			user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth); | ||||||
| 			 | 
 | ||||||
| 			p.setFont(st::mentionFont->f); | 			p.setFont(st::mentionFont->f); | ||||||
| 			p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p); | 			p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p); | ||||||
| 			p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first); | 			p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first); | ||||||
|  | @ -4094,7 +4173,7 @@ void MentionsDropdown::setBoundings(QRect boundings) { | ||||||
| 
 | 
 | ||||||
| void MentionsDropdown::recount(bool toDown) { | void MentionsDropdown::recount(bool toDown) { | ||||||
| 	int32 h = (_mrows.isEmpty() ? (_hrows.isEmpty() ? _brows.size() : _hrows.size()) : _mrows.size()) * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst; | 	int32 h = (_mrows.isEmpty() ? (_hrows.isEmpty() ? _brows.size() : _hrows.size()) : _mrows.size()) * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst; | ||||||
| 	 | 
 | ||||||
| 	if (_inner.height() != h) { | 	if (_inner.height() != h) { | ||||||
| //		st += h - _inner.height();
 | //		st += h - _inner.height();
 | ||||||
| 		_inner.resize(width(), h); | 		_inner.resize(width(), h); | ||||||
|  |  | ||||||
|  | @ -254,7 +254,7 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void fillPanels(QVector<EmojiPanel*> &panels); | 	void fillPanels(QVector<EmojiPanel*> &panels); | ||||||
| 	void refreshPanels(QVector<EmojiPanel*> &panels); | 	void refreshPanels(QVector<EmojiPanel*> &panels); | ||||||
| 	 | 
 | ||||||
| public slots: | public slots: | ||||||
| 
 | 
 | ||||||
| 	void updateSelected(); | 	void updateSelected(); | ||||||
|  | @ -336,6 +336,7 @@ public: | ||||||
| 	void hideFinish(); | 	void hideFinish(); | ||||||
| 	void showStickerSet(uint64 setId); | 	void showStickerSet(uint64 setId); | ||||||
| 
 | 
 | ||||||
|  | 	bool showSectionIcons() const; | ||||||
| 	void clearSelection(bool fast = false); | 	void clearSelection(bool fast = false); | ||||||
| 
 | 
 | ||||||
| 	void refreshStickers(); | 	void refreshStickers(); | ||||||
|  | @ -398,7 +399,7 @@ private: | ||||||
| 
 | 
 | ||||||
| 	void paintInlineItems(Painter &p, const QRect &r); | 	void paintInlineItems(Painter &p, const QRect &r); | ||||||
| 	void paintStickers(Painter &p, const QRect &r); | 	void paintStickers(Painter &p, const QRect &r); | ||||||
| 		 | 
 | ||||||
| 	int32 _maxHeight; | 	int32 _maxHeight; | ||||||
| 
 | 
 | ||||||
| 	void appendSet(uint64 setId); | 	void appendSet(uint64 setId); | ||||||
|  | @ -514,7 +515,7 @@ public: | ||||||
| 
 | 
 | ||||||
| 	EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji
 | 	EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji
 | ||||||
| 	void paintEvent(QPaintEvent *e); | 	void paintEvent(QPaintEvent *e); | ||||||
| 	void updateText(); | 	void updateText(const QString &inlineBotUsername = QString()); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
| 
 | 
 | ||||||
|  | @ -534,6 +535,8 @@ public: | ||||||
| 	void setMaxHeight(int32 h); | 	void setMaxHeight(int32 h); | ||||||
| 	void paintEvent(QPaintEvent *e); | 	void paintEvent(QPaintEvent *e); | ||||||
| 
 | 
 | ||||||
|  | 	void moveBottom(int32 bottom, bool force = false); | ||||||
|  | 
 | ||||||
| 	void enterEvent(QEvent *e); | 	void enterEvent(QEvent *e); | ||||||
| 	void leaveEvent(QEvent *e); | 	void leaveEvent(QEvent *e); | ||||||
| 	void otherEnter(); | 	void otherEnter(); | ||||||
|  | @ -558,7 +561,7 @@ public: | ||||||
| 	void stickersInstalled(uint64 setId); | 	void stickersInstalled(uint64 setId); | ||||||
| 
 | 
 | ||||||
| 	void queryInlineBot(UserData *bot, QString query); | 	void queryInlineBot(UserData *bot, QString query); | ||||||
| 	void inlineBotChanged(); | 	void clearInlineBot(); | ||||||
| 
 | 
 | ||||||
| 	bool overlaps(const QRect &globalRect) { | 	bool overlaps(const QRect &globalRect) { | ||||||
| 		if (isHidden() || !_cache.isNull()) return false; | 		if (isHidden() || !_cache.isNull()) return false; | ||||||
|  | @ -574,6 +577,8 @@ public: | ||||||
| 	bool ui_isInlineItemVisible(const LayoutInlineItem *layout); | 	bool ui_isInlineItemVisible(const LayoutInlineItem *layout); | ||||||
| 	bool ui_isInlineItemBeingChosen(); | 	bool ui_isInlineItemBeingChosen(); | ||||||
| 
 | 
 | ||||||
|  | 	void notify_automaticLoadSettingsChangedGif(); | ||||||
|  | 
 | ||||||
| public slots: | public slots: | ||||||
| 
 | 
 | ||||||
| 	void refreshStickers(); | 	void refreshStickers(); | ||||||
|  | @ -614,7 +619,7 @@ private: | ||||||
| 
 | 
 | ||||||
| 	void validateSelectedIcon(bool animated = false); | 	void validateSelectedIcon(bool animated = false); | ||||||
| 
 | 
 | ||||||
| 	int32 _maxHeight; | 	int32 _maxHeight, _maxHeightEmoji, _maxHeightStickers; | ||||||
| 	bool _horizontal; | 	bool _horizontal; | ||||||
| 
 | 
 | ||||||
| 	void leaveToChildEvent(QEvent *e); | 	void leaveToChildEvent(QEvent *e); | ||||||
|  | @ -693,7 +698,9 @@ private: | ||||||
| 	InlineCache _inlineCache; | 	InlineCache _inlineCache; | ||||||
| 	QTimer _inlineRequestTimer; | 	QTimer _inlineRequestTimer; | ||||||
| 
 | 
 | ||||||
|  | 	void inlineBotChanged(); | ||||||
| 	void showInlineRows(bool newResults); | 	void showInlineRows(bool newResults); | ||||||
|  | 	bool refreshInlineRows(); | ||||||
| 	UserData *_inlineBot; | 	UserData *_inlineBot; | ||||||
| 	QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; | 	QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; | ||||||
| 	mtpRequestId _inlineRequestId; | 	mtpRequestId _inlineRequestId; | ||||||
|  |  | ||||||
|  | @ -164,4 +164,8 @@ namespace Notify { | ||||||
| 		if (MainWidget *m = App::main()) m->notify_historyItemLayoutChanged(item); | 		if (MainWidget *m = App::main()) m->notify_historyItemLayoutChanged(item); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	void automaticLoadSettingsChangedGif() { | ||||||
|  | 		if (MainWidget *m = App::main()) m->notify_automaticLoadSettingsChangedGif(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -90,4 +90,6 @@ namespace Notify { | ||||||
| 	} | 	} | ||||||
| 	void historyItemLayoutChanged(const HistoryItem *item); | 	void historyItemLayoutChanged(const HistoryItem *item); | ||||||
| 
 | 
 | ||||||
|  | 	void automaticLoadSettingsChangedGif(); | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -30,6 +30,7 @@ FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const | ||||||
| , _maxLength(-1) | , _maxLength(-1) | ||||||
| , _ctrlEnterSubmit(true) | , _ctrlEnterSubmit(true) | ||||||
| , _oldtext(v) | , _oldtext(v) | ||||||
|  | , _phAfter(0) | ||||||
| , _phVisible(!v.length()) | , _phVisible(!v.length()) | ||||||
| , a_phLeft(_phVisible ? 0 : st.phShift) | , a_phLeft(_phVisible ? 0 : st.phShift) | ||||||
| , a_phAlpha(_phVisible ? 1 : 0) | , a_phAlpha(_phVisible ? 1 : 0) | ||||||
|  | @ -47,10 +48,10 @@ FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const | ||||||
| , _correcting(false) { | , _correcting(false) { | ||||||
| 	setAcceptRichText(false); | 	setAcceptRichText(false); | ||||||
| 	resize(_st.width, _st.font->height); | 	resize(_st.width, _st.font->height); | ||||||
| 	 | 
 | ||||||
| 	setFont(_st.font->f); | 	setFont(_st.font->f); | ||||||
| 	setAlignment(_st.align); | 	setAlignment(_st.align); | ||||||
| 	 | 
 | ||||||
| 	setPlaceholder(pholder); | 	setPlaceholder(pholder); | ||||||
| 
 | 
 | ||||||
| 	QPalette p(palette()); | 	QPalette p(palette()); | ||||||
|  | @ -87,8 +88,21 @@ FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void FlatTextarea::setTextFast(const QString &text) { | void FlatTextarea::setTextFast(const QString &text, bool withUndo) { | ||||||
| 	setPlainText(text); | 	if (withUndo) { | ||||||
|  | 		QTextCursor c(document()->docHandle(), 0); | ||||||
|  | 		c.joinPreviousEditBlock(); | ||||||
|  | 		c.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); | ||||||
|  | 		c.insertText(text); | ||||||
|  | 		c.movePosition(QTextCursor::End); | ||||||
|  | 		c.endEditBlock(); | ||||||
|  | 	} else { | ||||||
|  | 		setPlainText(text); | ||||||
|  | 	} | ||||||
|  | 	finishPlaceholder(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void FlatTextarea::finishPlaceholder() { | ||||||
| 	if (_a_appearance.animating()) { | 	if (_a_appearance.animating()) { | ||||||
| 		a_phLeft.finish(); | 		a_phLeft.finish(); | ||||||
| 		a_phAlpha.finish(); | 		a_phAlpha.finish(); | ||||||
|  | @ -206,10 +220,14 @@ void FlatTextarea::paintEvent(QPaintEvent *e) { | ||||||
| 	if (phDraw) { | 	if (phDraw) { | ||||||
| 		p.save(); | 		p.save(); | ||||||
| 		p.setClipRect(r); | 		p.setClipRect(r); | ||||||
| 		QRect phRect(_st.textMrg.left() - _fakeMargin + _st.phPos.x() + a_phLeft.current(), _st.textMrg.top() - _fakeMargin + _st.phPos.y(), width() - _st.textMrg.left() - _st.textMrg.right(), height() - _st.textMrg.top() - _st.textMrg.bottom()); | 		p.setFont(_st.font); | ||||||
| 		p.setFont(_st.font->f); |  | ||||||
| 		p.setPen(a_phColor.current()); | 		p.setPen(a_phColor.current()); | ||||||
| 		p.drawText(phRect, _ph, QTextOption(_st.phAlign)); | 		if (_st.phAlign == style::al_topleft && _phAfter > 0) { | ||||||
|  | 			p.drawText(_st.textMrg.left() - _fakeMargin + a_phLeft.current() + _st.font->width(getLastText().mid(0, _phAfter)), _st.textMrg.top() - _fakeMargin - st::lineWidth + _st.font->ascent, _ph); | ||||||
|  | 		} else { | ||||||
|  | 			QRect phRect(_st.textMrg.left() - _fakeMargin + _st.phPos.x() + a_phLeft.current(), _st.textMrg.top() - _fakeMargin + _st.phPos.y(), width() - _st.textMrg.left() - _st.textMrg.right(), height() - _st.textMrg.top() - _st.textMrg.bottom()); | ||||||
|  | 			p.drawText(phRect, _ph, QTextOption(_st.phAlign)); | ||||||
|  | 		} | ||||||
| 		p.restore(); | 		p.restore(); | ||||||
| 		p.setOpacity(1); | 		p.setOpacity(1); | ||||||
| 	} | 	} | ||||||
|  | @ -241,7 +259,7 @@ EmojiPtr FlatTextarea::getSingleEmoji() const { | ||||||
| 	QTextFragment fragment; | 	QTextFragment fragment; | ||||||
| 
 | 
 | ||||||
| 	getSingleEmojiFragment(text, fragment); | 	getSingleEmojiFragment(text, fragment); | ||||||
| 	 | 
 | ||||||
| 	if (!text.isEmpty()) { | 	if (!text.isEmpty()) { | ||||||
| 		QTextCharFormat format = fragment.charFormat(); | 		QTextCharFormat format = fragment.charFormat(); | ||||||
| 		QString imageName = static_cast<QTextImageFormat*>(&format)->name(); | 		QString imageName = static_cast<QTextImageFormat*>(&format)->name(); | ||||||
|  | @ -253,9 +271,6 @@ EmojiPtr FlatTextarea::getSingleEmoji() const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&inlineBot, QString &inlineBotUsername) const { | void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&inlineBot, QString &inlineBotUsername) const { | ||||||
| 	int32 pos = textCursor().position(); |  | ||||||
| 	if (textCursor().anchor() != pos) return; |  | ||||||
| 
 |  | ||||||
| 	// check inline bot query
 | 	// check inline bot query
 | ||||||
| 	const QString &text(getLastText()); | 	const QString &text(getLastText()); | ||||||
| 	int32 inlineUsernameStart = 1, inlineUsernameLength = 0, size = text.size(); | 	int32 inlineUsernameStart = 1, inlineUsernameLength = 0, size = text.size(); | ||||||
|  | @ -303,6 +318,9 @@ void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&i | ||||||
| 		inlineBotUsername = QString(); | 		inlineBotUsername = QString(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	int32 pos = textCursor().position(); | ||||||
|  | 	if (textCursor().anchor() != pos) return; | ||||||
|  | 
 | ||||||
| 	// check mention / hashtag / bot command
 | 	// check mention / hashtag / bot command
 | ||||||
| 	QTextDocument *doc(document()); | 	QTextDocument *doc(document()); | ||||||
| 	QTextBlock block = doc->findBlock(pos); | 	QTextBlock block = doc->findBlock(pos); | ||||||
|  | @ -764,7 +782,7 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) { | ||||||
| 
 | 
 | ||||||
| 			emoji = 0; | 			emoji = 0; | ||||||
| 			replacePosition = -1; | 			replacePosition = -1; | ||||||
| 		} else {	 | 		} else { | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -888,14 +906,18 @@ void FlatTextarea::step_appearance(float64 ms, bool timer) { | ||||||
| 	if (timer) update(); | 	if (timer) update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void FlatTextarea::setPlaceholder(const QString &ph) { | void FlatTextarea::setPlaceholder(const QString &ph, int32 afterSymbols) { | ||||||
| 	_ph = ph; | 	_ph = ph; | ||||||
| 	_phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1); | 	if (_phAfter != afterSymbols) { | ||||||
|  | 		_phAfter = afterSymbols; | ||||||
|  | 		updatePlaceholder(); | ||||||
|  | 	} | ||||||
|  | 	_phelided = _st.font->elided(_ph, width() - _st.textMrg.left() - _st.textMrg.right() - _st.phPos.x() - 1 - (_phAfter ? _st.font->width(getLastText().mid(0, _phAfter)) : 0)); | ||||||
| 	if (_phVisible) update(); | 	if (_phVisible) update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void FlatTextarea::updatePlaceholder() { | void FlatTextarea::updatePlaceholder() { | ||||||
| 	bool vis = getLastText().isEmpty(); | 	bool vis = (getLastText().size() <= _phAfter); | ||||||
| 	if (vis == _phVisible) return; | 	if (vis == _phVisible) return; | ||||||
| 
 | 
 | ||||||
| 	a_phLeft.start(vis ? 0 : _st.phShift); | 	a_phLeft.start(vis ? 0 : _st.phShift); | ||||||
|  |  | ||||||
|  | @ -51,8 +51,9 @@ public: | ||||||
| 	const QString &getLastText() const { | 	const QString &getLastText() const { | ||||||
| 		return _oldtext; | 		return _oldtext; | ||||||
| 	} | 	} | ||||||
| 	void setPlaceholder(const QString &ph); | 	void setPlaceholder(const QString &ph, int32 afterSymbols = 0); | ||||||
| 	void updatePlaceholder(); | 	void updatePlaceholder(); | ||||||
|  | 	void finishPlaceholder(); | ||||||
| 
 | 
 | ||||||
| 	QRect getTextRect() const; | 	QRect getTextRect() const; | ||||||
| 	int32 fakeMargin() const; | 	int32 fakeMargin() const; | ||||||
|  | @ -78,7 +79,7 @@ public: | ||||||
| 	QMimeData *createMimeDataFromSelection() const; | 	QMimeData *createMimeDataFromSelection() const; | ||||||
| 	void setCtrlEnterSubmit(bool ctrlEnterSubmit); | 	void setCtrlEnterSubmit(bool ctrlEnterSubmit); | ||||||
| 
 | 
 | ||||||
| 	void setTextFast(const QString &text); | 	void setTextFast(const QString &text, bool withUndo = false); | ||||||
| 
 | 
 | ||||||
| public slots: | public slots: | ||||||
| 
 | 
 | ||||||
|  | @ -124,6 +125,7 @@ private: | ||||||
| 	bool _ctrlEnterSubmit; | 	bool _ctrlEnterSubmit; | ||||||
| 
 | 
 | ||||||
| 	QString _ph, _phelided, _oldtext; | 	QString _ph, _phelided, _oldtext; | ||||||
|  | 	int32 _phAfter; | ||||||
| 	bool _phVisible; | 	bool _phVisible; | ||||||
| 	anim::ivalue a_phLeft; | 	anim::ivalue a_phLeft; | ||||||
| 	anim::fvalue a_phAlpha; | 	anim::fvalue a_phAlpha; | ||||||
|  |  | ||||||
|  | @ -591,6 +591,10 @@ Image *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap | ||||||
| 	return new Image(filecontent, format, pixmap); | 	return new Image(filecontent, format, pixmap); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | Image *getImage(int32 width, int32 height) { | ||||||
|  | 	return new DelayedStorageImage(width, height); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void clearStorageImages() { | void clearStorageImages() { | ||||||
| 	for (StorageImages::const_iterator i = storageImages.cbegin(), e = storageImages.cend(); i != e; ++i) { | 	for (StorageImages::const_iterator i = storageImages.cbegin(), e = storageImages.cend(); i != e; ++i) { | ||||||
| 		delete i.value(); | 		delete i.value(); | ||||||
|  | @ -644,6 +648,13 @@ void RemoteImage::doCheckload() const { | ||||||
| 	_forgot = false; | 	_forgot = false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void RemoteImage::loadLocal() { | ||||||
|  | 	if (loaded() || amLoading()) return; | ||||||
|  | 
 | ||||||
|  | 	_loader = createLoader(LoadFromLocalOnly, true); | ||||||
|  | 	if (_loader) _loader->start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void RemoteImage::setData(QByteArray &bytes, const QByteArray &bytesFormat) { | void RemoteImage::setData(QByteArray &bytes, const QByteArray &bytesFormat) { | ||||||
| 	QBuffer buffer(&bytes); | 	QBuffer buffer(&bytes); | ||||||
| 
 | 
 | ||||||
|  | @ -781,6 +792,89 @@ FileLoader *StorageImage::createLoader(LoadFromCloudSetting fromCloud, bool auto | ||||||
| 	return new mtpFileLoader(&_location, _size, fromCloud, autoLoading); | 	return new mtpFileLoader(&_location, _size, fromCloud, autoLoading); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | DelayedStorageImage::DelayedStorageImage() : StorageImage(StorageImageLocation()) | ||||||
|  | , _loadRequested(false) | ||||||
|  | , _loadCancelled(false) | ||||||
|  | , _loadFromCloud(false) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DelayedStorageImage::DelayedStorageImage(int32 w, int32 h) : StorageImage(StorageImageLocation(w, h, 0, 0, 0, 0)) | ||||||
|  | , _loadRequested(false) | ||||||
|  | , _loadCancelled(false) | ||||||
|  | , _loadFromCloud(false) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DelayedStorageImage::DelayedStorageImage(QByteArray &bytes) : StorageImage(StorageImageLocation(), bytes) | ||||||
|  | , _loadRequested(false) | ||||||
|  | , _loadCancelled(false) | ||||||
|  | , _loadFromCloud(false) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DelayedStorageImage::setStorageLocation(const StorageImageLocation location) { | ||||||
|  | 	_location = location; | ||||||
|  | 	if (_loadRequested) { | ||||||
|  | 		if (!_loadCancelled) { | ||||||
|  | 			if (_loadFromCloud) { | ||||||
|  | 				load(); | ||||||
|  | 			} else { | ||||||
|  | 				loadLocal(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		_loadRequested = false; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DelayedStorageImage::automaticLoad(const HistoryItem *item) { | ||||||
|  | 	if (_location.isNull()) { | ||||||
|  | 		if (!_loadCancelled && item) { | ||||||
|  | 			bool loadFromCloud = false; | ||||||
|  | 			if (item->history()->peer->isUser()) { | ||||||
|  | 				loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate); | ||||||
|  | 			} else { | ||||||
|  | 				loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (_loadRequested) { | ||||||
|  | 				if (loadFromCloud) _loadFromCloud = loadFromCloud; | ||||||
|  | 			} else { | ||||||
|  | 				_loadFromCloud = loadFromCloud; | ||||||
|  | 				_loadRequested = true; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		StorageImage::automaticLoad(item); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DelayedStorageImage::automaticLoadSettingsChanged() { | ||||||
|  | 	if (_loadCancelled) _loadCancelled = false; | ||||||
|  | 	StorageImage::automaticLoadSettingsChanged(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DelayedStorageImage::load(bool loadFirst, bool prior) { | ||||||
|  | 	if (_location.isNull()) { | ||||||
|  | 		_loadRequested = _loadFromCloud = true; | ||||||
|  | 	} else { | ||||||
|  | 		StorageImage::load(loadFirst, prior); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DelayedStorageImage::loadEvenCancelled(bool loadFirst, bool prior) { | ||||||
|  | 	_loadCancelled = false; | ||||||
|  | 	StorageImage::loadEvenCancelled(loadFirst, prior); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DelayedStorageImage::displayLoading() const { | ||||||
|  | 	return _location.isNull() ? true : StorageImage::displayLoading(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DelayedStorageImage::cancel() { | ||||||
|  | 	if (_loadRequested) { | ||||||
|  | 		_loadRequested = false; | ||||||
|  | 	} | ||||||
|  | 	StorageImage::cancel(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| StorageImage *getImage(const StorageImageLocation &location, int32 size) { | StorageImage *getImage(const StorageImageLocation &location, int32 size) { | ||||||
| 	StorageKey key(storageKey(location)); | 	StorageKey key(storageKey(location)); | ||||||
| 	StorageImages::const_iterator i = storageImages.constFind(key); | 	StorageImages::const_iterator i = storageImages.constFind(key); | ||||||
|  |  | ||||||
|  | @ -109,6 +109,8 @@ inline bool operator!=(const StorageImageLocation &a, const StorageImageLocation | ||||||
| 
 | 
 | ||||||
| QPixmap imagePix(QImage img, int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh); | QPixmap imagePix(QImage img, int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh); | ||||||
| 
 | 
 | ||||||
|  | class DelayedStorageImage; | ||||||
|  | 
 | ||||||
| class HistoryItem; | class HistoryItem; | ||||||
| class Image { | class Image { | ||||||
| public: | public: | ||||||
|  | @ -173,7 +175,7 @@ public: | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	bool isNull() const; | 	bool isNull() const; | ||||||
| 	 | 
 | ||||||
| 	void forget() const; | 	void forget() const; | ||||||
| 
 | 
 | ||||||
| 	QByteArray savedFormat() const { | 	QByteArray savedFormat() const { | ||||||
|  | @ -183,6 +185,13 @@ public: | ||||||
| 		return _saved; | 		return _saved; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	virtual DelayedStorageImage *toDelayedStorageImage() { | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	virtual const DelayedStorageImage *toDelayedStorageImage() const { | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	virtual ~Image(); | 	virtual ~Image(); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|  | @ -209,6 +218,7 @@ Image *getImage(const QString &file, QByteArray format); | ||||||
| Image *getImage(const QByteArray &filecontent, QByteArray format); | Image *getImage(const QByteArray &filecontent, QByteArray format); | ||||||
| Image *getImage(const QPixmap &pixmap, QByteArray format); | Image *getImage(const QPixmap &pixmap, QByteArray format); | ||||||
| Image *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap); | Image *getImage(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap); | ||||||
|  | Image *getImage(int32 width, int32 height); | ||||||
| 
 | 
 | ||||||
| typedef QPair<uint64, uint64> StorageKey; | typedef QPair<uint64, uint64> StorageKey; | ||||||
| inline uint64 storageMix32To64(int32 a, int32 b) { | inline uint64 storageMix32To64(int32 a, int32 b) { | ||||||
|  | @ -256,6 +266,7 @@ protected: | ||||||
| 	void checkload() const { | 	void checkload() const { | ||||||
| 		doCheckload(); | 		doCheckload(); | ||||||
| 	} | 	} | ||||||
|  | 	void loadLocal(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	mutable FileLoader *_loader; | 	mutable FileLoader *_loader; | ||||||
|  | @ -271,10 +282,10 @@ public: | ||||||
| 
 | 
 | ||||||
| 	StorageImage(const StorageImageLocation &location, int32 size = 0); | 	StorageImage(const StorageImageLocation &location, int32 size = 0); | ||||||
| 	StorageImage(const StorageImageLocation &location, QByteArray &bytes); | 	StorageImage(const StorageImageLocation &location, QByteArray &bytes); | ||||||
| 	 | 
 | ||||||
| 	int32 width() const; | 	int32 width() const; | ||||||
| 	int32 height() const; | 	int32 height() const; | ||||||
| 	 | 
 | ||||||
| 	virtual void setInformation(int32 size, int32 width, int32 height); | 	virtual void setInformation(int32 size, int32 width, int32 height); | ||||||
| 	virtual FileLoader *createLoader(LoadFromCloudSetting fromCloud, bool autoLoading); | 	virtual FileLoader *createLoader(LoadFromCloudSetting fromCloud, bool autoLoading); | ||||||
| 
 | 
 | ||||||
|  | @ -282,12 +293,45 @@ public: | ||||||
| 		return _location; | 		return _location; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| private: | protected: | ||||||
| 	StorageImageLocation _location; | 	StorageImageLocation _location; | ||||||
| 	int32 _size; | 	int32 _size; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | class DelayedStorageImage : public StorageImage { | ||||||
|  | public: | ||||||
|  | 
 | ||||||
|  | 	DelayedStorageImage(); | ||||||
|  | 	DelayedStorageImage(int32 w, int32 h); | ||||||
|  | 	DelayedStorageImage(QByteArray &bytes); | ||||||
|  | 
 | ||||||
|  | 	void setStorageLocation(const StorageImageLocation location); | ||||||
|  | 
 | ||||||
|  | 	virtual DelayedStorageImage *toDelayedStorageImage() { | ||||||
|  | 		return this; | ||||||
|  | 	} | ||||||
|  | 	virtual const DelayedStorageImage *toDelayedStorageImage() const { | ||||||
|  | 		return this; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void automaticLoad(const HistoryItem *item); // auto load photo
 | ||||||
|  | 	void automaticLoadSettingsChanged(); | ||||||
|  | 
 | ||||||
|  | 	bool loading() const { | ||||||
|  | 		return _location.isNull() ? _loadRequested : StorageImage::loading(); | ||||||
|  | 	} | ||||||
|  | 	bool displayLoading() const; | ||||||
|  | 	void cancel(); | ||||||
|  | 
 | ||||||
|  | 	void load(bool loadFirst = false, bool prior = true); | ||||||
|  | 	void loadEvenCancelled(bool loadFirst = false, bool prior = true); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	bool _loadRequested, _loadCancelled, _loadFromCloud; | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| StorageImage *getImage(const StorageImageLocation &location, int32 size = 0); | StorageImage *getImage(const StorageImageLocation &location, int32 size = 0); | ||||||
| StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes); | StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes); | ||||||
| Image *getImage(int32 width, int32 height, const MTPFileLocation &location); | Image *getImage(int32 width, int32 height, const MTPFileLocation &location); | ||||||
|  | @ -327,8 +371,22 @@ public: | ||||||
| 	ImagePtr(const StorageImageLocation &location, const QByteArray &bytes) : Parent(getImage(location, bytes)) { | 	ImagePtr(const StorageImageLocation &location, const QByteArray &bytes) : Parent(getImage(location, bytes)) { | ||||||
| 	} | 	} | ||||||
| 	ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr()); | 	ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr()); | ||||||
|  | 	ImagePtr(int32 width, int32 height) : Parent(getImage(width, height)) { | ||||||
|  | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | inline QSize resizeKeepAspect(int32 width, int32 height, int32 towidth, int32 toheight) { | ||||||
|  | 	int32 w = qMax(width, 1), h = qMax(height, 1); | ||||||
|  | 	if (w * toheight > h * towidth) { | ||||||
|  | 		h = qRound(h * towidth / float64(w)); | ||||||
|  | 		w = towidth; | ||||||
|  | 	} else { | ||||||
|  | 		w = qRound(w * toheight / float64(h)); | ||||||
|  | 		h = toheight; | ||||||
|  | 	} | ||||||
|  | 	return QSize(qMax(w, 1), qMax(h, 1)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void clearStorageImages(); | void clearStorageImages(); | ||||||
| void clearAllImages(); | void clearAllImages(); | ||||||
| int64 imageCacheSize(); | int64 imageCacheSize(); | ||||||
|  |  | ||||||
|  | @ -209,7 +209,7 @@ void FakeDialogRow::paint(Painter &p, int32 w, bool act, bool sel, bool onlyBack | ||||||
| 	QRect fullRect(0, 0, w, st::dlgHeight); | 	QRect fullRect(0, 0, w, st::dlgHeight); | ||||||
| 	p.fillRect(fullRect, (act ? st::dlgActiveBG : (sel ? st::dlgHoverBG : st::dlgBG))->b); | 	p.fillRect(fullRect, (act ? st::dlgActiveBG : (sel ? st::dlgHoverBG : st::dlgBG))->b); | ||||||
| 	if (onlyBackground) return; | 	if (onlyBackground) return; | ||||||
| 	 | 
 | ||||||
| 	History *history = _item->history(); | 	History *history = _item->history(); | ||||||
| 	if (history->peer->migrateTo()) { | 	if (history->peer->migrateTo()) { | ||||||
| 		p.drawPixmap(st::dlgPaddingHor, st::dlgPaddingVer, history->peer->migrateTo()->photo->pix(st::dlgPhotoSize)); | 		p.drawPixmap(st::dlgPaddingHor, st::dlgPaddingVer, history->peer->migrateTo()->photo->pix(st::dlgPhotoSize)); | ||||||
|  | @ -2358,7 +2358,7 @@ void History::getNextShowFrom(HistoryBlock *block, int32 i) { | ||||||
| 
 | 
 | ||||||
| void History::addUnreadBar() { | void History::addUnreadBar() { | ||||||
| 	if (unreadBar || !showFrom || showFrom->detached() || !unreadCount) return; | 	if (unreadBar || !showFrom || showFrom->detached() || !unreadCount) return; | ||||||
| 	 | 
 | ||||||
| 	int32 count = unreadCount; | 	int32 count = unreadCount; | ||||||
| 	if (peer->migrateTo()) { | 	if (peer->migrateTo()) { | ||||||
| 		if (History *h = App::historyLoaded(peer->migrateTo()->id)) { | 		if (History *h = App::historyLoaded(peer->migrateTo()->id)) { | ||||||
|  | @ -2842,7 +2842,7 @@ void HistoryBlock::removeItem(HistoryItem *item) { | ||||||
| 				nextCollapse->destroy(); | 				nextCollapse->destroy(); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		 | 
 | ||||||
| 		// fix date items
 | 		// fix date items
 | ||||||
| 		HistoryItem *nextItem = (i < items.size() - 1) ? items[i + 1] : ((myIndex < history->blocks.size() - 1) ? history->blocks[myIndex + 1]->items[0] : 0); | 		HistoryItem *nextItem = (i < items.size() - 1) ? items[i + 1] : ((myIndex < history->blocks.size() - 1) ? history->blocks[myIndex + 1]->items[0] : 0); | ||||||
| 		if (nextItem && nextItem == history->unreadBar) { // skip unread bar
 | 		if (nextItem && nextItem == history->unreadBar) { // skip unread bar
 | ||||||
|  | @ -2991,7 +2991,7 @@ void HistoryItem::setId(MsgId newId) { | ||||||
| void HistoryItem::clipCallback(ClipReaderNotification notification) { | void HistoryItem::clipCallback(ClipReaderNotification notification) { | ||||||
| 	HistoryMedia *media = getMedia(); | 	HistoryMedia *media = getMedia(); | ||||||
| 	if (!media) return; | 	if (!media) return; | ||||||
| 	 | 
 | ||||||
| 	ClipReader *reader = media ? media->getClipReader() : 0; | 	ClipReader *reader = media ? media->getClipReader() : 0; | ||||||
| 	if (!reader) return; | 	if (!reader) return; | ||||||
| 
 | 
 | ||||||
|  | @ -4040,7 +4040,7 @@ HistoryDocument::HistoryDocument(DocumentData *document, const QString &caption, | ||||||
| , _namew(st::semiboldFont->width(_name)) | , _namew(st::semiboldFont->width(_name)) | ||||||
| , _caption(st::msgFileMinWidth - st::msgPadding.left() - st::msgPadding.right()) { | , _caption(st::msgFileMinWidth - st::msgPadding.left() - st::msgPadding.right()) { | ||||||
| 	setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); | 	setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); | ||||||
| 	 | 
 | ||||||
| 	setStatusSize(FileStatusSizeReady); | 	setStatusSize(FileStatusSizeReady); | ||||||
| 
 | 
 | ||||||
| 	if (!caption.isEmpty()) { | 	if (!caption.isEmpty()) { | ||||||
|  | @ -4152,7 +4152,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r | ||||||
| 		statustop = st::msgFileThumbStatusTop; | 		statustop = st::msgFileThumbStatusTop; | ||||||
| 		linktop = st::msgFileThumbLinkTop; | 		linktop = st::msgFileThumbLinkTop; | ||||||
| 		bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); | 		bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); | ||||||
| 	 | 
 | ||||||
| 		QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); | 		QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); | ||||||
| 		if (_data->thumb->loaded()) { | 		if (_data->thumb->loaded()) { | ||||||
| 			QPixmap thumb = loaded ? _data->thumb->pixSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize); | 			QPixmap thumb = loaded ? _data->thumb->pixSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize); | ||||||
|  | @ -4432,6 +4432,7 @@ HistoryGif::HistoryGif(DocumentData *document, const QString &caption, const His | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| HistoryGif::HistoryGif(const HistoryGif &other) : HistoryFileMedia() | HistoryGif::HistoryGif(const HistoryGif &other) : HistoryFileMedia() | ||||||
|  | , _parent(0) | ||||||
| , _data(other._data) | , _data(other._data) | ||||||
| , _thumbw(other._thumbw) | , _thumbw(other._thumbw) | ||||||
| , _thumbh(other._thumbh) | , _thumbh(other._thumbh) | ||||||
|  | @ -4442,6 +4443,7 @@ HistoryGif::HistoryGif(const HistoryGif &other) : HistoryFileMedia() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryGif::initDimensions(const HistoryItem *parent) { | void HistoryGif::initDimensions(const HistoryItem *parent) { | ||||||
|  | 	_parent = parent; | ||||||
| 	if (_caption.hasSkipBlock()) { | 	if (_caption.hasSkipBlock()) { | ||||||
| 		_caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight()); | 		_caption.setSkipBlock(parent->skipBlockWidth(), parent->skipBlockHeight()); | ||||||
| 	} | 	} | ||||||
|  | @ -4556,7 +4558,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo | ||||||
| 	if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; | 	if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; | ||||||
| 
 | 
 | ||||||
| 	_data->automaticLoad(parent); | 	_data->automaticLoad(parent); | ||||||
| 	bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); | 	bool loaded = _data->loaded(), displayLoading = (parent->id < 0) || _data->displayLoading(); | ||||||
| 	if (loaded && !gif() && _gif != BadClipReader && cAutoPlayGif()) { | 	if (loaded && !gif() && _gif != BadClipReader && cAutoPlayGif()) { | ||||||
| 		const_cast<HistoryGif*>(this)->playInline(const_cast<HistoryItem*>(parent)); | 		const_cast<HistoryGif*>(this)->playInline(const_cast<HistoryItem*>(parent)); | ||||||
| 		if (gif()) _gif->setAutoplay(); | 		if (gif()) _gif->setAutoplay(); | ||||||
|  | @ -4570,11 +4572,11 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo | ||||||
| 
 | 
 | ||||||
| 	bool animating = (gif() && _gif->started()); | 	bool animating = (gif() && _gif->started()); | ||||||
| 
 | 
 | ||||||
| 	if (!animating || _data->uploading()) { | 	if (!animating || parent->id < 0) { | ||||||
| 		if (displayLoading) { | 		if (displayLoading) { | ||||||
| 			ensureAnimation(parent); | 			ensureAnimation(parent); | ||||||
| 			if (!_animation->radial.animating()) { | 			if (!_animation->radial.animating()) { | ||||||
| 				_animation->radial.start(_data->progress()); | 				_animation->radial.start(dataProgress()); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		updateStatusText(parent); | 		updateStatusText(parent); | ||||||
|  | @ -4606,7 +4608,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (radial || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif())) || (_gif == BadClipReader)) { | 	if (radial || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif())) || (_gif == BadClipReader)) { | ||||||
|         float64 radialOpacity = (radial && loaded && !_data->uploading()) ? _animation->radial.opacity() : 1; |         float64 radialOpacity = (radial && loaded && parent->id > 0) ? _animation->radial.opacity() : 1; | ||||||
| 		QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); | 		QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); | ||||||
| 		p.setPen(Qt::NoPen); | 		p.setPen(Qt::NoPen); | ||||||
| 		if (selected) { | 		if (selected) { | ||||||
|  | @ -4641,7 +4643,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo | ||||||
| 			_animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg); | 			_animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (!animating || _data->uploading()) { | 		if (!animating || parent->id < 0) { | ||||||
| 			int32 statusX = skipx + st::msgDateImgDelta + st::msgDateImgPadding.x(), statusY = skipy + st::msgDateImgDelta + st::msgDateImgPadding.y(); | 			int32 statusX = skipx + st::msgDateImgDelta + st::msgDateImgPadding.x(), statusY = skipy + st::msgDateImgDelta + st::msgDateImgPadding.y(); | ||||||
| 			int32 statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); | 			int32 statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); | ||||||
| 			int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); | 			int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); | ||||||
|  | @ -4784,6 +4786,18 @@ HistoryGif::~HistoryGif() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | float64 HistoryGif::dataProgress() const { | ||||||
|  | 	return (_data->uploading() || !_parent || _parent->id > 0) ? _data->progress() : 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool HistoryGif::dataFinished() const { | ||||||
|  | 	return (!_parent || _parent->id > 0) ? (!_data->loading() && !_data->uploading()) : false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool HistoryGif::dataLoaded() const { | ||||||
|  | 	return (!_parent || _parent->id > 0) ? _data->loaded() : false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia() | HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia() | ||||||
| , _pixw(1) | , _pixw(1) | ||||||
| , _pixh(1) | , _pixh(1) | ||||||
|  | @ -5392,7 +5406,7 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, const QRect &r, | ||||||
| 	if (_asArticle || (_attach && _attach->customInfoLayout() && _attach->currentWidth() + parent->skipBlockWidth() > width + bubble.left() + bubble.right())) { | 	if (_asArticle || (_attach && _attach->customInfoLayout() && _attach->currentWidth() + parent->skipBlockWidth() > width + bubble.left() + bubble.right())) { | ||||||
| 		bshift += st::msgDateFont->height; | 		bshift += st::msgDateFont->height; | ||||||
| 	} | 	} | ||||||
| 		 | 
 | ||||||
| 	QRect bar(rtlrect(st::msgPadding.left(), 0, st::webPageBar, _height - bshift, _width)); | 	QRect bar(rtlrect(st::msgPadding.left(), 0, st::webPageBar, _height - bshift, _width)); | ||||||
| 	p.fillRect(bar, barfg); | 	p.fillRect(bar, barfg); | ||||||
| 
 | 
 | ||||||
|  | @ -5455,7 +5469,7 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, const QRect &r, | ||||||
| 
 | 
 | ||||||
| 		_attach->draw(p, parent, r.translated(-attachLeft, -attachTop), selected, ms); | 		_attach->draw(p, parent, r.translated(-attachLeft, -attachTop), selected, ms); | ||||||
| 		int32 pixwidth = _attach->currentWidth(), pixheight = _attach->height(); | 		int32 pixwidth = _attach->currentWidth(), pixheight = _attach->height(); | ||||||
| 		 | 
 | ||||||
| 		if (_data->type == WebPageVideo) { | 		if (_data->type == WebPageVideo) { | ||||||
| 			if (_data->siteName == qstr("YouTube")) { | 			if (_data->siteName == qstr("YouTube")) { | ||||||
| 				p.drawPixmap(QPoint((pixwidth - st::youtubeIcon.pxWidth()) / 2, (pixheight - st::youtubeIcon.pxHeight()) / 2), App::sprite(), st::youtubeIcon); | 				p.drawPixmap(QPoint((pixwidth - st::youtubeIcon.pxWidth()) / 2, (pixheight - st::youtubeIcon.pxHeight()) / 2), App::sprite(), st::youtubeIcon); | ||||||
|  | @ -5807,12 +5821,39 @@ int32 HistoryImageLink::fullHeight() const { | ||||||
| 	return st::minPhotoSize; | 	return st::minPhotoSize; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ViaInlineBotLink::onClick(Qt::MouseButton button) const { | ||||||
|  | 	App::insertBotCommand('@' + _bot->username); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | HistoryMessageVia::HistoryMessageVia(int32 userId) | ||||||
|  | 	: bot(App::userLoaded(peerFromUser(userId))) | ||||||
|  | 	, width(0) | ||||||
|  | 	, fullWidth(st::msgServiceNameFont->width(qsl("via @") + bot->username)) | ||||||
|  | 	, lnk(new ViaInlineBotLink(bot)) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool HistoryMessageVia::isNull() const { | ||||||
|  | 	return !bot || bot->username.isEmpty(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HistoryMessageVia::resize(int32 availw) { | ||||||
|  | 	if (width < fullWidth && availw > width) { | ||||||
|  | 		if (availw < fullWidth) { | ||||||
|  | 			text = st::msgServiceNameFont->elided(qsl("via @") + bot->username, availw); | ||||||
|  | 			width = st::msgServiceNameFont->width(text); | ||||||
|  | 		} else { | ||||||
|  | 			text = qsl("via @") + bot->username; | ||||||
|  | 			width = fullWidth; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg) : | HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg) : | ||||||
| 	HistoryItem(history, block, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) | 	HistoryItem(history, block, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) | ||||||
| , _text(st::msgMinWidth) | , _text(st::msgMinWidth) | ||||||
| , _textWidth(0) | , _textWidth(0) | ||||||
| , _textHeight(0) | , _textHeight(0) | ||||||
| , _viaBot(msg.has_via_bot_id() ? App::userLoaded(peerFromUser(msg.vvia_bot_id)) : 0) | , _via(msg.has_via_bot_id() ? new HistoryMessageVia(msg.vvia_bot_id.v) : 0) | ||||||
| , _media(0) | , _media(0) | ||||||
| , _views(msg.has_views() ? msg.vviews.v : -1) { | , _views(msg.has_views() ? msg.vviews.v : -1) { | ||||||
| 	QString text(textClean(qs(msg.vmessage))); | 	QString text(textClean(qs(msg.vmessage))); | ||||||
|  | @ -5826,7 +5867,7 @@ HistoryItem(history, block, msgId, flags, date, from) | ||||||
| , _text(st::msgMinWidth) | , _text(st::msgMinWidth) | ||||||
| , _textWidth(0) | , _textWidth(0) | ||||||
| , _textHeight(0) | , _textHeight(0) | ||||||
| , _viaBot(viaBotId ? App::userLoaded(peerFromUser(viaBotId)) : 0) | , _via(viaBotId ? new HistoryMessageVia(viaBotId) : 0) | ||||||
| , _media(0) | , _media(0) | ||||||
| , _views(fromChannel() ? 1 : -1) { | , _views(fromChannel() ? 1 : -1) { | ||||||
| 	initTime(); | 	initTime(); | ||||||
|  | @ -5842,7 +5883,7 @@ HistoryItem(history, block, msgId, flags, date, from) | ||||||
| , _text(st::msgMinWidth) | , _text(st::msgMinWidth) | ||||||
| , _textWidth(0) | , _textWidth(0) | ||||||
| , _textHeight(0) | , _textHeight(0) | ||||||
| , _viaBot(viaBotId ? App::userLoaded(peerFromUser(viaBotId)) : 0) | , _via(viaBotId ? new HistoryMessageVia(viaBotId) : 0) | ||||||
| , _media(0) | , _media(0) | ||||||
| , _views(fromChannel() ? 1 : -1) { | , _views(fromChannel() ? 1 : -1) { | ||||||
| 	initTime(); | 	initTime(); | ||||||
|  | @ -5855,7 +5896,7 @@ HistoryItem(history, block, msgId, flags, date, from) | ||||||
| , _text(st::msgMinWidth) | , _text(st::msgMinWidth) | ||||||
| , _textWidth(0) | , _textWidth(0) | ||||||
| , _textHeight(0) | , _textHeight(0) | ||||||
| , _viaBot(viaBotId ? App::userLoaded(peerFromUser(viaBotId)) : 0) | , _via(viaBotId ? new HistoryMessageVia(viaBotId) : 0) | ||||||
| , _media(0) | , _media(0) | ||||||
| , _views(fromChannel() ? 1 : -1) { | , _views(fromChannel() ? 1 : -1) { | ||||||
| 	initTime(); | 	initTime(); | ||||||
|  | @ -5994,6 +6035,11 @@ void HistoryMessage::initDimensions() { | ||||||
| 			if (maxw > _maxw) _maxw = maxw; | 			if (maxw > _maxw) _maxw = maxw; | ||||||
| 			_minh += _media->minHeight(); | 			_minh += _media->minHeight(); | ||||||
| 		} | 		} | ||||||
|  | 		if (via()) { | ||||||
|  | 			if (st::msgPadding.left() + via()->fullWidth + st::msgPadding.right() > _maxw) { | ||||||
|  | 				_maxw = st::msgPadding.left() + via()->fullWidth + st::msgPadding.right() > _maxw; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		_media->initDimensions(this); | 		_media->initDimensions(this); | ||||||
| 		_maxw = _media->maxWidth(); | 		_maxw = _media->maxWidth(); | ||||||
|  | @ -6251,7 +6297,7 @@ void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 m | ||||||
| 		App::roundRect(p, r, bg, cors, &sh); | 		App::roundRect(p, r, bg, cors, &sh); | ||||||
| 
 | 
 | ||||||
| 		if (displayFromName()) { | 		if (displayFromName()) { | ||||||
| 			p.setFont(st::msgNameFont->f); | 			p.setFont(st::msgNameFont); | ||||||
| 			if (fromChannel()) { | 			if (fromChannel()) { | ||||||
| 				p.setPen(selected ? st::msgInServiceFgSelected : st::msgInServiceFg); | 				p.setPen(selected ? st::msgInServiceFgSelected : st::msgInServiceFg); | ||||||
| 			} else { | 			} else { | ||||||
|  | @ -6260,6 +6306,7 @@ void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 m | ||||||
| 			_from->nameText.drawElided(p, r.left() + st::msgPadding.left(), r.top() + st::msgPadding.top(), width - st::msgPadding.left() - st::msgPadding.right()); | 			_from->nameText.drawElided(p, r.left() + st::msgPadding.left(), r.top() + st::msgPadding.top(), width - st::msgPadding.left() - st::msgPadding.right()); | ||||||
| 			r.setTop(r.top() + st::msgNameFont->height); | 			r.setTop(r.top() + st::msgNameFont->height); | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
| 		QRect trect(r.marginsAdded(-st::msgPadding)); | 		QRect trect(r.marginsAdded(-st::msgPadding)); | ||||||
| 		drawMessageText(p, trect, selection); | 		drawMessageText(p, trect, selection); | ||||||
| 
 | 
 | ||||||
|  | @ -6286,7 +6333,15 @@ void HistoryMessage::draw(Painter &p, const QRect &r, uint32 selection, uint64 m | ||||||
| 	textstyleRestore(); | 	textstyleRestore(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryMessage::drawMessageText(Painter &p, const QRect &trect, uint32 selection) const { | void HistoryMessage::drawMessageText(Painter &p, QRect trect, uint32 selection) const { | ||||||
|  | 	bool outbg = out() && !fromChannel(), selected = (selection == FullSelection); | ||||||
|  | 	if (via()) { | ||||||
|  | 		p.setFont(st::msgServiceNameFont); | ||||||
|  | 		p.setPen((selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg))->p); | ||||||
|  | 		p.drawTextLeft(trect.left(), trect.top(), _history->width, via()->text); | ||||||
|  | 		trect.setY(trect.y() + st::msgServiceNameFont->height); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	p.setPen(st::msgColor->p); | 	p.setPen(st::msgColor->p); | ||||||
| 	p.setFont(st::msgFont->f); | 	p.setFont(st::msgFont->f); | ||||||
| 	uint16 selectedFrom = (selection == FullSelection) ? 0 : (selection >> 16) & 0xFFFF; | 	uint16 selectedFrom = (selection == FullSelection) ? 0 : (selection >> 16) & 0xFFFF; | ||||||
|  | @ -6330,6 +6385,14 @@ int32 HistoryMessage::resize(int32 width) { | ||||||
| 				_height += st::msgNameFont->height; | 				_height += st::msgNameFont->height; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		if (via()) { | ||||||
|  | 			via()->resize(width - st::msgPadding.left() - st::msgPadding.right()); | ||||||
|  | 			if (emptyText() && !displayFromName()) { | ||||||
|  | 				_height += st::msgPadding.top() + st::msgNameFont->height + st::mediaHeaderSkip; | ||||||
|  | 			} else { | ||||||
|  | 				_height += st::msgNameFont->height; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		_height = _media->resize(width, this); | 		_height = _media->resize(width, this); | ||||||
| 	} | 	} | ||||||
|  | @ -6390,7 +6453,6 @@ void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 | ||||||
| 			} | 			} | ||||||
| 			r.setTop(r.top() + st::msgNameFont->height); | 			r.setTop(r.top() + st::msgNameFont->height); | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 		getStateFromMessageText(lnk, state, x, y, r); | 		getStateFromMessageText(lnk, state, x, y, r); | ||||||
| 	} else { | 	} else { | ||||||
| 		_media->getState(lnk, state, x - left, y - st::msgMargin.top(), this); | 		_media->getState(lnk, state, x - left, y - st::msgMargin.top(), this); | ||||||
|  | @ -6401,6 +6463,15 @@ void HistoryMessage::getStateFromMessageText(TextLinkPtr &lnk, HistoryCursorStat | ||||||
| 	bool inDate = false; | 	bool inDate = false; | ||||||
| 
 | 
 | ||||||
| 	QRect trect(r.marginsAdded(-st::msgPadding)); | 	QRect trect(r.marginsAdded(-st::msgPadding)); | ||||||
|  | 
 | ||||||
|  | 	if (via()) { | ||||||
|  | 		if (x >= trect.left() && y >= trect.top() && y < trect.top() + st::msgNameFont->height && x < trect.left() + via()->width) { | ||||||
|  | 			lnk = via()->lnk; | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		trect.setTop(trect.top() + st::msgNameFont->height); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	TextLinkPtr medialnk; | 	TextLinkPtr medialnk; | ||||||
| 	if (_media && _media->isDisplayed()) { | 	if (_media && _media->isDisplayed()) { | ||||||
| 		if (!_media->customInfoLayout()) { | 		if (!_media->customInfoLayout()) { | ||||||
|  | @ -6443,6 +6514,9 @@ void HistoryMessage::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, | ||||||
| 		if (displayFromName()) { // from user left name
 | 		if (displayFromName()) { // from user left name
 | ||||||
| 			r.setTop(r.top() + st::msgNameFont->height); | 			r.setTop(r.top() + st::msgNameFont->height); | ||||||
| 		} | 		} | ||||||
|  | 		if (via()) { | ||||||
|  | 			r.setTop(r.top() + st::msgNameFont->height); | ||||||
|  | 		} | ||||||
| 		QRect trect(r.marginsAdded(-st::msgPadding)); | 		QRect trect(r.marginsAdded(-st::msgPadding)); | ||||||
| 		if (_media && _media->isDisplayed()) { | 		if (_media && _media->isDisplayed()) { | ||||||
| 			trect.setBottom(trect.bottom() - _media->height()); | 			trect.setBottom(trect.bottom() - _media->height()); | ||||||
|  | @ -6489,8 +6563,9 @@ QString HistoryMessage::notificationText() const { | ||||||
| HistoryMessage::~HistoryMessage() { | HistoryMessage::~HistoryMessage() { | ||||||
| 	if (_media) { | 	if (_media) { | ||||||
| 		_media->unregItem(this); | 		_media->unregItem(this); | ||||||
| 		delete _media; | 		deleteAndMark(_media); | ||||||
| 	} | 	} | ||||||
|  | 	deleteAndMark(_via); | ||||||
| 	if (_flags & MTPDmessage::flag_reply_markup) { | 	if (_flags & MTPDmessage::flag_reply_markup) { | ||||||
| 		App::clearReplyMarkup(channelId(), id); | 		App::clearReplyMarkup(channelId(), id); | ||||||
| 	} | 	} | ||||||
|  | @ -6506,7 +6581,7 @@ HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg) | HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, QDateTime date, int32 from, HistoryMessage *msg) | ||||||
| : HistoryMessage(history, block, id, newMessageFlags(history->peer) | (!history->peer->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage::flag_media_unread : 0), msg->viaBot() ? peerToUser(msg->viaBot()->id) : 0, date, from, msg->HistoryMessage::originalText(), msg->HistoryMessage::originalEntities(), msg->getMedia()) | : HistoryMessage(history, block, id, newMessageFlags(history->peer) | (!history->peer->isChannel() && msg->getMedia() && (msg->getMedia()->type() == MediaTypeAudio/* || msg->getMedia()->type() == MediaTypeVideo*/) ? MTPDmessage::flag_media_unread : 0), msg->via() ? peerToUser(msg->viaBot()->id) : 0, date, from, msg->HistoryMessage::originalText(), msg->HistoryMessage::originalEntities(), msg->getMedia()) | ||||||
| , fwdDate(msg->dateForwarded()) | , fwdDate(msg->dateForwarded()) | ||||||
| , fwdFrom(msg->fromForwarded()) | , fwdFrom(msg->fromForwarded()) | ||||||
| , fwdFromVersion(fwdFrom->nameVersion) | , fwdFromVersion(fwdFrom->nameVersion) | ||||||
|  | @ -6548,7 +6623,7 @@ void HistoryForwarded::drawForwardedFrom(Painter &p, int32 x, int32 y, int32 w, | ||||||
| 
 | 
 | ||||||
| 	bool outbg = out() && !fromChannel(); | 	bool outbg = out() && !fromChannel(); | ||||||
| 	p.setPen((selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg))->p); | 	p.setPen((selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg))->p); | ||||||
| 	p.setFont(serviceFont->f); | 	p.setFont(serviceFont); | ||||||
| 
 | 
 | ||||||
| 	if (w >= fromWidth) { | 	if (w >= fromWidth) { | ||||||
| 		p.drawText(x, y + serviceFont->ascent, lang(lng_forwarded_from)); | 		p.drawText(x, y + serviceFont->ascent, lang(lng_forwarded_from)); | ||||||
|  | @ -6560,22 +6635,23 @@ void HistoryForwarded::drawForwardedFrom(Painter &p, int32 x, int32 y, int32 w, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryForwarded::drawMessageText(Painter &p, const QRect &trect, uint32 selection) const { | void HistoryForwarded::drawMessageText(Painter &p, QRect trect, uint32 selection) const { | ||||||
| 	QRect realtrect(trect); |  | ||||||
| 	if (displayForwardedFrom()) { | 	if (displayForwardedFrom()) { | ||||||
| 		drawForwardedFrom(p, realtrect.x(), realtrect.y(), realtrect.width(), (selection == FullSelection)); | 		drawForwardedFrom(p, trect.x(), trect.y(), trect.width(), (selection == FullSelection)); | ||||||
| 		realtrect.setY(trect.y() + st::msgServiceNameFont->height); | 		trect.setY(trect.y() + st::msgServiceNameFont->height); | ||||||
| 	} | 	} | ||||||
| 	HistoryMessage::drawMessageText(p, realtrect, selection); | 	HistoryMessage::drawMessageText(p, trect, selection); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int32 HistoryForwarded::resize(int32 width) { | int32 HistoryForwarded::resize(int32 width) { | ||||||
| 	HistoryMessage::resize(width); | 	HistoryMessage::resize(width); | ||||||
| 	if (drawBubble() && displayForwardedFrom()) { | 	if (drawBubble()) { | ||||||
| 		if (emptyText() && !displayFromName()) { | 		if (displayForwardedFrom()) { | ||||||
| 			_height += st::msgPadding.top() + st::msgServiceNameFont->height + st::mediaHeaderSkip; | 			if (emptyText() && !displayFromName() && !via()) { | ||||||
| 		} else { | 				_height += st::msgPadding.top() + st::msgServiceNameFont->height + st::mediaHeaderSkip; | ||||||
| 			_height += st::msgServiceNameFont->height; | 			} else { | ||||||
|  | 				_height += st::msgServiceNameFont->height; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return _height; | 	return _height; | ||||||
|  | @ -6719,7 +6795,7 @@ bool HistoryReply::updateReplyTo(bool force) { | ||||||
| 		replyToText.setText(st::msgFont, replyToMsg->inReplyText(), _textDlgOptions); | 		replyToText.setText(st::msgFont, replyToMsg->inReplyText(), _textDlgOptions); | ||||||
| 
 | 
 | ||||||
| 		replyToNameUpdated(); | 		replyToNameUpdated(); | ||||||
| 		 | 
 | ||||||
| 		replyToLnk = TextLinkPtr(new MessageLink(replyToMsg->history()->peer->id, replyToMsg->id)); | 		replyToLnk = TextLinkPtr(new MessageLink(replyToMsg->history()->peer->id, replyToMsg->id)); | ||||||
| 	} else if (force) { | 	} else if (force) { | ||||||
| 		replyToMsgId = 0; | 		replyToMsgId = 0; | ||||||
|  | @ -6836,21 +6912,20 @@ void HistoryReply::drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selec | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryReply::drawMessageText(Painter &p, const QRect &trect, uint32 selection) const { | void HistoryReply::drawMessageText(Painter &p, QRect trect, uint32 selection) const { | ||||||
| 	int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); | 	int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); | ||||||
| 
 | 
 | ||||||
| 	drawReplyTo(p, trect.x(), trect.y(), trect.width(), (selection == FullSelection)); | 	drawReplyTo(p, trect.x(), trect.y(), trect.width(), (selection == FullSelection)); | ||||||
| 
 | 
 | ||||||
| 	QRect realtrect(trect); | 	trect.setY(trect.y() + h); | ||||||
| 	realtrect.setY(trect.y() + h); | 	HistoryMessage::drawMessageText(p, trect, selection); | ||||||
| 	HistoryMessage::drawMessageText(p, realtrect, selection); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int32 HistoryReply::resize(int32 width) { | int32 HistoryReply::resize(int32 width) { | ||||||
| 	HistoryMessage::resize(width); | 	HistoryMessage::resize(width); | ||||||
| 
 | 
 | ||||||
| 	if (drawBubble()) { | 	if (drawBubble()) { | ||||||
| 		if (emptyText() && !displayFromName()) { | 		if (emptyText() && !displayFromName() && !via()) { | ||||||
| 			_height += st::msgPadding.top() + st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom() + st::mediaHeaderSkip; | 			_height += st::msgPadding.top() + st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom() + st::mediaHeaderSkip; | ||||||
| 		} else { | 		} else { | ||||||
| 			_height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); | 			_height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); | ||||||
|  |  | ||||||
|  | @ -807,6 +807,10 @@ public: | ||||||
| 	virtual int32 resize(int32 width) = 0; // return new height
 | 	virtual int32 resize(int32 width) = 0; // return new height
 | ||||||
| 	virtual void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const = 0; | 	virtual void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const = 0; | ||||||
| 
 | 
 | ||||||
|  | 	virtual UserData *viaBot() const { | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	History *history() const { | 	History *history() const { | ||||||
| 		return _history; | 		return _history; | ||||||
| 	} | 	} | ||||||
|  | @ -1066,7 +1070,7 @@ public: | ||||||
| 	CommentsLink(HistoryItem *item) : _item(item) { | 	CommentsLink(HistoryItem *item) : _item(item) { | ||||||
| 	} | 	} | ||||||
| 	void onClick(Qt::MouseButton button) const; | 	void onClick(Qt::MouseButton button) const; | ||||||
| 	 | 
 | ||||||
| private: | private: | ||||||
| 	HistoryItem *_item; | 	HistoryItem *_item; | ||||||
| }; | }; | ||||||
|  | @ -1243,7 +1247,7 @@ protected: | ||||||
| 	} | 	} | ||||||
| 	bool isThumbAnimation(uint64 ms) const { | 	bool isThumbAnimation(uint64 ms) const { | ||||||
| 		if (!_animation || !_animation->_a_thumbOver.animating()) return false; | 		if (!_animation || !_animation->_a_thumbOver.animating()) return false; | ||||||
| 		 | 
 | ||||||
| 		_animation->_a_thumbOver.step(ms); | 		_animation->_a_thumbOver.step(ms); | ||||||
| 		return _animation && _animation->_a_thumbOver.animating(); | 		return _animation && _animation->_a_thumbOver.animating(); | ||||||
| 	} | 	} | ||||||
|  | @ -1311,7 +1315,7 @@ public: | ||||||
| 		return _caption.original(); | 		return _caption.original(); | ||||||
| 	} | 	} | ||||||
| 	bool needsBubble(const HistoryItem *parent) const { | 	bool needsBubble(const HistoryItem *parent) const { | ||||||
| 		return !_caption.isEmpty() || parent->toHistoryReply(); | 		return !_caption.isEmpty() || parent->toHistoryReply() || parent->viaBot(); | ||||||
| 	} | 	} | ||||||
| 	bool customInfoLayout() const { | 	bool customInfoLayout() const { | ||||||
| 		return _caption.isEmpty(); | 		return _caption.isEmpty(); | ||||||
|  | @ -1380,7 +1384,7 @@ public: | ||||||
| 	ImagePtr replyPreview(); | 	ImagePtr replyPreview(); | ||||||
| 
 | 
 | ||||||
| 	bool needsBubble(const HistoryItem *parent) const { | 	bool needsBubble(const HistoryItem *parent) const { | ||||||
| 		return !_caption.isEmpty() || parent->toHistoryReply(); | 		return !_caption.isEmpty() || parent->toHistoryReply() || parent->viaBot(); | ||||||
| 	} | 	} | ||||||
| 	bool customInfoLayout() const { | 	bool customInfoLayout() const { | ||||||
| 		return _caption.isEmpty(); | 		return _caption.isEmpty(); | ||||||
|  | @ -1616,7 +1620,7 @@ public: | ||||||
| 		return _caption.original(); | 		return _caption.original(); | ||||||
| 	} | 	} | ||||||
| 	bool needsBubble(const HistoryItem *parent) const { | 	bool needsBubble(const HistoryItem *parent) const { | ||||||
| 		return !_caption.isEmpty() || parent->toHistoryReply(); | 		return !_caption.isEmpty() || parent->toHistoryReply() || parent->viaBot(); | ||||||
| 	} | 	} | ||||||
| 	bool customInfoLayout() const { | 	bool customInfoLayout() const { | ||||||
| 		return _caption.isEmpty(); | 		return _caption.isEmpty(); | ||||||
|  | @ -1632,18 +1636,13 @@ public: | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
| 
 | 
 | ||||||
| 	float64 dataProgress() const { | 	float64 dataProgress() const; | ||||||
| 		return _data->progress(); | 	bool dataFinished() const; | ||||||
| 	} | 	bool dataLoaded() const; | ||||||
| 	bool dataFinished() const { |  | ||||||
| 		return !_data->loading() && !_data->uploading(); |  | ||||||
| 	} |  | ||||||
| 	bool dataLoaded() const { |  | ||||||
| 		return _data->loaded(); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 
 | 
 | ||||||
|  | 	const HistoryItem *_parent; | ||||||
| 	DocumentData *_data; | 	DocumentData *_data; | ||||||
| 	int32 _thumbw, _thumbh; | 	int32 _thumbw, _thumbh; | ||||||
| 	Text _caption; | 	Text _caption; | ||||||
|  | @ -1840,7 +1839,7 @@ public: | ||||||
| 	HistoryMedia *attach() const { | 	HistoryMedia *attach() const { | ||||||
| 		return _attach; | 		return _attach; | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	~HistoryWebPage(); | 	~HistoryWebPage(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  | @ -1885,7 +1884,7 @@ public: | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	bool needsBubble(const HistoryItem *parent) const { | 	bool needsBubble(const HistoryItem *parent) const { | ||||||
| 		return !_title.isEmpty() || !_description.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply(); | 		return !_title.isEmpty() || !_description.isEmpty() || parent->toHistoryForwarded() || parent->toHistoryReply() || parent->viaBot(); | ||||||
| 	} | 	} | ||||||
| 	bool customInfoLayout() const { | 	bool customInfoLayout() const { | ||||||
| 		return true; | 		return true; | ||||||
|  | @ -1901,6 +1900,33 @@ private: | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | class ViaInlineBotLink : public ITextLink { | ||||||
|  | 	TEXT_LINK_CLASS(ViaInlineBotLink) | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	ViaInlineBotLink(UserData *bot) : _bot(bot) { | ||||||
|  | 	} | ||||||
|  | 	void onClick(Qt::MouseButton button) const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	UserData *_bot; | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class HistoryMessageVia { | ||||||
|  | public: | ||||||
|  | 	HistoryMessageVia(int32 userId); | ||||||
|  | 
 | ||||||
|  | 	bool isNull() const; | ||||||
|  | 	void resize(int32 availw); | ||||||
|  | 
 | ||||||
|  | 	UserData *bot; | ||||||
|  | 	QString text; | ||||||
|  | 	int32 width, fullWidth; | ||||||
|  | 	TextLinkPtr lnk; | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class HistoryMessage : public HistoryItem { | class HistoryMessage : public HistoryItem { | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|  | @ -1915,8 +1941,11 @@ public: | ||||||
| 	void initDimensions(); | 	void initDimensions(); | ||||||
| 	void fromNameUpdated() const; | 	void fromNameUpdated() const; | ||||||
| 
 | 
 | ||||||
| 	UserData *viaBot() const { | 	virtual HistoryMessageVia *via() const { | ||||||
| 		return _viaBot; | 		return (_via && !_via->isNull()) ? _via : 0; | ||||||
|  | 	} | ||||||
|  | 	virtual UserData *viaBot() const { | ||||||
|  | 		return via() ? via()->bot : 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	int32 plainMaxWidth() const; | 	int32 plainMaxWidth() const; | ||||||
|  | @ -1932,7 +1961,7 @@ public: | ||||||
| 		return drawBubble(); | 		return drawBubble(); | ||||||
| 	} | 	} | ||||||
| 	bool displayFromName() const { | 	bool displayFromName() const { | ||||||
| 		return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || !_media->hideFromName()); | 		return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || viaBot() || !_media->hideFromName()); | ||||||
| 	} | 	} | ||||||
| 	bool uploading() const { | 	bool uploading() const { | ||||||
| 		return _media && _media->uploading(); | 		return _media && _media->uploading(); | ||||||
|  | @ -1943,7 +1972,7 @@ public: | ||||||
| 	void setId(MsgId newId); | 	void setId(MsgId newId); | ||||||
| 	void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; | 	void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; | ||||||
| 
 | 
 | ||||||
| 	virtual void drawMessageText(Painter &p, const QRect &trect, uint32 selection) const; | 	virtual void drawMessageText(Painter &p, QRect trect, uint32 selection) const; | ||||||
| 
 | 
 | ||||||
| 	int32 resize(int32 width); | 	int32 resize(int32 width); | ||||||
| 	bool hasPoint(int32 x, int32 y) const; | 	bool hasPoint(int32 x, int32 y) const; | ||||||
|  | @ -1966,7 +1995,7 @@ public: | ||||||
| 	void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const; | 	void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const; | ||||||
|     QString notificationHeader() const; |     QString notificationHeader() const; | ||||||
|     QString notificationText() const; |     QString notificationText() const; | ||||||
|      | 
 | ||||||
| 	void updateMedia(const MTPMessageMedia *media, bool allowEmitResize) { | 	void updateMedia(const MTPMessageMedia *media, bool allowEmitResize) { | ||||||
| 		if (media && _media && _media->type() != MediaTypeWebPage) { | 		if (media && _media && _media->type() != MediaTypeWebPage) { | ||||||
| 			_media->updateFrom(*media, this, allowEmitResize); | 			_media->updateFrom(*media, this, allowEmitResize); | ||||||
|  | @ -2036,12 +2065,12 @@ protected: | ||||||
| 	Text _text; | 	Text _text; | ||||||
| 
 | 
 | ||||||
| 	int32 _textWidth, _textHeight; | 	int32 _textWidth, _textHeight; | ||||||
| 	UserData *_viaBot; | 	HistoryMessageVia *_via; | ||||||
| 
 | 
 | ||||||
| 	HistoryMedia *_media; | 	HistoryMedia *_media; | ||||||
| 	QString _timeText; | 	QString _timeText; | ||||||
| 	int32 _timeWidth; | 	int32 _timeWidth; | ||||||
| 	 | 
 | ||||||
| 	QString _viewsText; | 	QString _viewsText; | ||||||
| 	int32 _views, _viewsWidth; | 	int32 _views, _viewsWidth; | ||||||
| 
 | 
 | ||||||
|  | @ -2058,7 +2087,7 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; | 	void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; | ||||||
| 	void drawForwardedFrom(Painter &p, int32 x, int32 y, int32 w, bool selected) const; | 	void drawForwardedFrom(Painter &p, int32 x, int32 y, int32 w, bool selected) const; | ||||||
| 	void drawMessageText(Painter &p, const QRect &trect, uint32 selection) const; | 	void drawMessageText(Painter &p, QRect trect, uint32 selection) const; | ||||||
| 	int32 resize(int32 width); | 	int32 resize(int32 width); | ||||||
| 	bool hasPoint(int32 x, int32 y) const; | 	bool hasPoint(int32 x, int32 y) const; | ||||||
| 	void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; | 	void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; | ||||||
|  | @ -2115,7 +2144,7 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; | 	void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; | ||||||
| 	void drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selected, bool likeService = false) const; | 	void drawReplyTo(Painter &p, int32 x, int32 y, int32 w, bool selected, bool likeService = false) const; | ||||||
| 	void drawMessageText(Painter &p, const QRect &trect, uint32 selection) const; | 	void drawMessageText(Painter &p, QRect trect, uint32 selection) const; | ||||||
| 	int32 resize(int32 width); | 	int32 resize(int32 width); | ||||||
| 	bool hasPoint(int32 x, int32 y) const; | 	bool hasPoint(int32 x, int32 y) const; | ||||||
| 	void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; | 	void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; | ||||||
|  |  | ||||||
|  | @ -357,7 +357,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) { | ||||||
| 		_touchPrevPos = _touchPos; | 		_touchPrevPos = _touchPos; | ||||||
| 		_touchPos = e->touchPoints().cbegin()->screenPos().toPoint(); | 		_touchPos = e->touchPoints().cbegin()->screenPos().toPoint(); | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	switch (e->type()) { | 	switch (e->type()) { | ||||||
| 	case QEvent::TouchBegin: | 	case QEvent::TouchBegin: | ||||||
| 		if (_menu) { | 		if (_menu) { | ||||||
|  | @ -682,7 +682,7 @@ void HistoryInner::itemRemoved(HistoryItem *item) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (_dragAction == NoDrag) return; | 	if (_dragAction == NoDrag) return; | ||||||
| 	 | 
 | ||||||
| 	if (_dragItem == item) { | 	if (_dragItem == item) { | ||||||
| 		dragActionCancel(); | 		dragActionCancel(); | ||||||
| 	} | 	} | ||||||
|  | @ -956,7 +956,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 				QString contextMenuText = item->selectedText(FullSelection); | 				QString contextMenuText = item->selectedText(FullSelection); | ||||||
| 				if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || _dragCursorState == HistoryInTextCursorState)) { | 				if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || (msg->getMedia()->type() != MediaTypeSticker && msg->getMedia()->type() != MediaTypeGif))) { | ||||||
| 					_menu->addAction(lang(lng_context_copy_text), this, SLOT(copyContextText()))->setEnabled(true); | 					_menu->addAction(lang(lng_context_copy_text), this, SLOT(copyContextText()))->setEnabled(true); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | @ -1044,7 +1044,7 @@ void HistoryInner::copyContextUrl() { | ||||||
| void HistoryInner::saveContextImage() { | void HistoryInner::saveContextImage() { | ||||||
|     PhotoLink *lnk = dynamic_cast<PhotoLink*>(_contextMenuLnk.data()); |     PhotoLink *lnk = dynamic_cast<PhotoLink*>(_contextMenuLnk.data()); | ||||||
| 	if (!lnk) return; | 	if (!lnk) return; | ||||||
| 	 | 
 | ||||||
| 	PhotoData *photo = lnk->photo(); | 	PhotoData *photo = lnk->photo(); | ||||||
| 	if (!photo || !photo->date || !photo->loaded()) return; | 	if (!photo || !photo->date || !photo->loaded()) return; | ||||||
| 
 | 
 | ||||||
|  | @ -1059,7 +1059,7 @@ void HistoryInner::saveContextImage() { | ||||||
| void HistoryInner::copyContextImage() { | void HistoryInner::copyContextImage() { | ||||||
|     PhotoLink *lnk = dynamic_cast<PhotoLink*>(_contextMenuLnk.data()); |     PhotoLink *lnk = dynamic_cast<PhotoLink*>(_contextMenuLnk.data()); | ||||||
| 	if (!lnk) return; | 	if (!lnk) return; | ||||||
| 	 | 
 | ||||||
| 	PhotoData *photo = lnk->photo(); | 	PhotoData *photo = lnk->photo(); | ||||||
| 	if (!photo || !photo->date || !photo->loaded()) return; | 	if (!photo || !photo->date || !photo->loaded()) return; | ||||||
| 
 | 
 | ||||||
|  | @ -1640,7 +1640,7 @@ void HistoryInner::onUpdateSelected() { | ||||||
| 		} else if (_dragCursorState == HistoryInDateCursorState) { | 		} else if (_dragCursorState == HistoryInDateCursorState) { | ||||||
| //			cur = style::cur_cross;
 | //			cur = style::cur_cross;
 | ||||||
| 		} | 		} | ||||||
| 	} else if (item) {		 | 	} else if (item) { | ||||||
| 		if (item != _dragItem || (m - _dragStartPos).manhattanLength() >= QApplication::startDragDistance()) { | 		if (item != _dragItem || (m - _dragStartPos).manhattanLength() >= QApplication::startDragDistance()) { | ||||||
| 			if (_dragAction == PrepareDrag) { | 			if (_dragAction == PrepareDrag) { | ||||||
| 				_dragAction = Dragging; | 				_dragAction = Dragging; | ||||||
|  | @ -1742,7 +1742,7 @@ void HistoryInner::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dr | ||||||
| 		force = true; | 		force = true; | ||||||
| 	} | 	} | ||||||
| 	if (!force) return; | 	if (!force) return; | ||||||
| 	 | 
 | ||||||
| 	update(); | 	update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2568,7 +2568,7 @@ bool HistoryHider::offerPeer(PeerId peer) { | ||||||
| 	if (toTextWidth > box.width() - st::boxPadding.left() - st::boxButtonPadding.right()) { | 	if (toTextWidth > box.width() - st::boxPadding.left() - st::boxButtonPadding.right()) { | ||||||
| 		toTextWidth = box.width() - st::boxPadding.left() - st::boxButtonPadding.right(); | 		toTextWidth = box.width() - st::boxPadding.left() - st::boxButtonPadding.right(); | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	resizeEvent(0); | 	resizeEvent(0); | ||||||
| 	update(); | 	update(); | ||||||
| 	setFocus(); | 	setFocus(); | ||||||
|  | @ -3458,7 +3458,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		App::main()->peerUpdated(_peer); | 		App::main()->peerUpdated(_peer); | ||||||
| 		 | 
 | ||||||
| 		if (_history->draftToId > 0 || !_history->draft.isEmpty()) { | 		if (_history->draftToId > 0 || !_history->draft.isEmpty()) { | ||||||
| 			applyDraft(false); | 			applyDraft(false); | ||||||
| 			_replyToId = readyToForward() ? 0 : _history->draftToId; | 			_replyToId = readyToForward() ? 0 : _history->draftToId; | ||||||
|  | @ -3823,11 +3823,10 @@ void HistoryWidget::updateControlsVisibility() { | ||||||
| 				} | 				} | ||||||
| 				if (hasBroadcastToggle()) { | 				if (hasBroadcastToggle()) { | ||||||
| 					_broadcast.show(); | 					_broadcast.show(); | ||||||
| 					_field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph)); |  | ||||||
| 				} else { | 				} else { | ||||||
| 					_broadcast.hide(); | 					_broadcast.hide(); | ||||||
| 					_field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_peer->asChannel()->canPublish() ? lng_broadcast_ph : lng_comment_ph) : lng_message_ph)); |  | ||||||
| 				} | 				} | ||||||
|  | 				updateFieldPlaceholder(); | ||||||
| 			} | 			} | ||||||
| 			if (_replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { | 			if (_replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { | ||||||
| 				if (_replyForwardPreviewCancel.isHidden()) { | 				if (_replyForwardPreviewCancel.isHidden()) { | ||||||
|  | @ -4187,7 +4186,7 @@ void HistoryWidget::loadMessagesDown() { | ||||||
| 	if (_history->isEmpty() && _migrated && _migrated->isEmpty()) { | 	if (_history->isEmpty() && _migrated && _migrated->isEmpty()) { | ||||||
| 		return firstLoadMessages(); | 		return firstLoadMessages(); | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
| 	bool loadMigrated = _migrated && !(_migrated->isEmpty() || _migrated->loadedAtBottom() || (!_history->isEmpty() && !_history->loadedAtTop())); | 	bool loadMigrated = _migrated && !(_migrated->isEmpty() || _migrated->loadedAtBottom() || (!_history->isEmpty() && !_history->loadedAtTop())); | ||||||
| 	History *from = loadMigrated ? _migrated : _history; | 	History *from = loadMigrated ? _migrated : _history; | ||||||
| 	if (from->loadedAtBottom()) { | 	if (from->loadedAtBottom()) { | ||||||
|  | @ -4277,7 +4276,7 @@ void HistoryWidget::onListScroll() { | ||||||
| 
 | 
 | ||||||
| 	updateToEndVisibility(); | 	updateToEndVisibility(); | ||||||
| 	updateCollapseCommentsVisibility(); | 	updateCollapseCommentsVisibility(); | ||||||
| 	 | 
 | ||||||
| 	int st = _scroll.scrollTop(), stm = _scroll.scrollTopMax(), sh = _scroll.height(); | 	int st = _scroll.scrollTop(), stm = _scroll.scrollTopMax(), sh = _scroll.height(); | ||||||
| 	if (st + PreloadHeightsCount * sh > stm) { | 	if (st + PreloadHeightsCount * sh > stm) { | ||||||
| 		loadMessagesDown(); | 		loadMessagesDown(); | ||||||
|  | @ -4442,7 +4441,7 @@ void HistoryWidget::onMuteUnmute() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryWidget::onBroadcastChange() { | void HistoryWidget::onBroadcastChange() { | ||||||
| 	_field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph)); | 	updateFieldPlaceholder(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) { | void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) { | ||||||
|  | @ -4465,7 +4464,7 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const | ||||||
| 
 | 
 | ||||||
| 	PeerData *p = App::peer(peer); | 	PeerData *p = App::peer(peer); | ||||||
| 	int32 flags = newMessageFlags(p) | MTPDmessage::flag_media; // unread, out
 | 	int32 flags = newMessageFlags(p) | MTPDmessage::flag_media; // unread, out
 | ||||||
| 	 | 
 | ||||||
| 	bool lastKeyboardUsed = lastForceReplyReplied(FullMsgId(peerToChannel(peer), replyTo)); | 	bool lastKeyboardUsed = lastForceReplyReplied(FullMsgId(peerToChannel(peer), replyTo)); | ||||||
| 
 | 
 | ||||||
| 	int32 sendFlags = 0; | 	int32 sendFlags = 0; | ||||||
|  | @ -4666,7 +4665,7 @@ void HistoryWidget::onPhotoSelect() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	QStringList photoExtensions(cPhotoExtensions()); | 	QStringList photoExtensions(cPhotoExtensions()); | ||||||
| 	QStringList imgExtensions(cImgExtensions());	 | 	QStringList imgExtensions(cImgExtensions()); | ||||||
| 	QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;Photo files (*") + photoExtensions.join(qsl(" *")) + qsl(");;All files (*.*)")); | 	QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;Photo files (*") + photoExtensions.join(qsl(" *")) + qsl(");;All files (*.*)")); | ||||||
| 
 | 
 | ||||||
| 	QStringList files; | 	QStringList files; | ||||||
|  | @ -4694,7 +4693,7 @@ void HistoryWidget::onDocumentSelect() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	QStringList photoExtensions(cPhotoExtensions()); | 	QStringList photoExtensions(cPhotoExtensions()); | ||||||
| 	QStringList imgExtensions(cImgExtensions());	 | 	QStringList imgExtensions(cImgExtensions()); | ||||||
| 	QString filter(qsl("All files (*.*);;Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;Photo files (*") + photoExtensions.join(qsl(" *")) + qsl(")")); | 	QString filter(qsl("All files (*.*);;Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;Photo files (*") + photoExtensions.join(qsl(" *")) + qsl(")")); | ||||||
| 
 | 
 | ||||||
| 	QStringList files; | 	QStringList files; | ||||||
|  | @ -4838,23 +4837,27 @@ void HistoryWidget::insertBotCommand(const QString &cmd) { | ||||||
| 	if (!bot->isUser() || !bot->asUser()->botInfo) bot = 0; | 	if (!bot->isUser() || !bot->asUser()->botInfo) bot = 0; | ||||||
| 	QString username = bot ? bot->asUser()->username : QString(); | 	QString username = bot ? bot->asUser()->username : QString(); | ||||||
| 	int32 botStatus = _peer->isChat() ? _peer->asChat()->botStatus : (_peer->isMegagroup() ? _peer->asChannel()->mgInfo->botStatus : -1); | 	int32 botStatus = _peer->isChat() ? _peer->asChat()->botStatus : (_peer->isMegagroup() ? _peer->asChannel()->mgInfo->botStatus : -1); | ||||||
| 	if (toInsert.indexOf('@') < 2 && !username.isEmpty() && (botStatus == 0 || botStatus == 2)) { | 	if (toInsert.indexOf('@') < 0 && !username.isEmpty() && (botStatus == 0 || botStatus == 2)) { | ||||||
| 		toInsert += '@' + username; | 		toInsert += '@' + username; | ||||||
| 	} | 	} | ||||||
| 	toInsert += ' '; | 	toInsert += ' '; | ||||||
| 
 | 
 | ||||||
| 	QString text = _field.getLastText(); | 	if (toInsert.at(0) != '@') { | ||||||
| 	QRegularExpressionMatch m = QRegularExpression(qsl("^/[A-Za-z_0-9]{0,64}(@[A-Za-z_0-9]{0,32})?(\\s|$)")).match(text); | 		QString text = _field.getLastText(); | ||||||
| 	if (m.hasMatch()) { | 		QRegularExpressionMatch m = QRegularExpression(qsl("^/[A-Za-z_0-9]{0,64}(@[A-Za-z_0-9]{0,32})?(\\s|$)")).match(text); | ||||||
| 		text = toInsert + text.mid(m.capturedLength()); | 		if (m.hasMatch()) { | ||||||
| 	} else { | 			text = toInsert + text.mid(m.capturedLength()); | ||||||
| 		text = toInsert + text; | 		} else { | ||||||
| 	} | 			text = toInsert + text; | ||||||
| 	_field.setText(text); | 		} | ||||||
|  | 		_field.setTextFast(text); | ||||||
| 
 | 
 | ||||||
| 	QTextCursor cur(_field.textCursor()); | 		QTextCursor cur(_field.textCursor()); | ||||||
| 	cur.movePosition(QTextCursor::End); | 		cur.movePosition(QTextCursor::End); | ||||||
| 	_field.setTextCursor(cur); | 		_field.setTextCursor(cur); | ||||||
|  | 	} else { | ||||||
|  | 		_field.setTextFast(toInsert, true); | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) { | bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) { | ||||||
|  | @ -4951,23 +4954,21 @@ bool HistoryWidget::hasBroadcastToggle() const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryWidget::inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result) { | void HistoryWidget::inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result) { | ||||||
| 	_inlineBot = 0; | 	_inlineBotUsername = QString(); | ||||||
| 	if (result.type() == mtpc_contacts_resolvedPeer) { | 	if (result.type() == mtpc_contacts_resolvedPeer) { | ||||||
| 		const MTPDcontacts_resolvedPeer &d(result.c_contacts_resolvedPeer()); | 		const MTPDcontacts_resolvedPeer &d(result.c_contacts_resolvedPeer()); | ||||||
| 		App::feedUsers(d.vusers); | 		App::feedUsers(d.vusers); | ||||||
| 		App::feedChats(d.vchats); | 		App::feedChats(d.vchats); | ||||||
| 		PeerId peerId = peerFromMTP(d.vpeer); |  | ||||||
| 		if (peerId && peerIsUser(peerId)) { |  | ||||||
| 			_inlineBot = App::user(peerId); |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	onCheckMentionDropdown(); | 	onCheckMentionDropdown(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool HistoryWidget::inlineBotResolveFail(const RPCError &error) { | bool HistoryWidget::inlineBotResolveFail(QString name, const RPCError &error) { | ||||||
| 	if (mtpIsFlood(error)) return false; | 	if (mtpIsFlood(error)) return false; | ||||||
| 	_inlineBot = 0; | 	if (name == _inlineBotUsername) { | ||||||
| 	onCheckMentionDropdown(); | 		_inlineBot = 0; | ||||||
|  | 		onCheckMentionDropdown(); | ||||||
|  | 	} | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -5299,7 +5300,7 @@ void HistoryWidget::onFieldResize() { | ||||||
| 	_cmdStart.move(_attachEmoji.x() - _cmdStart.width(), height() - kbh - _cmdStart.height()); | 	_cmdStart.move(_attachEmoji.x() - _cmdStart.width(), height() - kbh - _cmdStart.height()); | ||||||
| 
 | 
 | ||||||
| 	_attachType.move(0, _attachDocument.y() - _attachType.height()); | 	_attachType.move(0, _attachDocument.y() - _attachType.height()); | ||||||
| 	_emojiPan.move(width() - _emojiPan.width(), _attachEmoji.y() - _emojiPan.height()); | 	_emojiPan.moveBottom(_attachEmoji.y()); | ||||||
| 
 | 
 | ||||||
| 	updateListSize(); | 	updateListSize(); | ||||||
| 	updateField(); | 	updateField(); | ||||||
|  | @ -5312,6 +5313,7 @@ void HistoryWidget::onFieldFocused() { | ||||||
| void HistoryWidget::onCheckMentionDropdown() { | void HistoryWidget::onCheckMentionDropdown() { | ||||||
| 	if (!_history || _a_show.animating()) return; | 	if (!_history || _a_show.animating()) return; | ||||||
| 
 | 
 | ||||||
|  | 	UserData *bot = _inlineBot; | ||||||
| 	QString start, inlineBotUsername(_inlineBotUsername); | 	QString start, inlineBotUsername(_inlineBotUsername); | ||||||
| 	_field.getMentionHashtagBotCommandStart(start, _inlineBot, _inlineBotUsername); | 	_field.getMentionHashtagBotCommandStart(start, _inlineBot, _inlineBotUsername); | ||||||
| 	if (inlineBotUsername != _inlineBotUsername) { | 	if (inlineBotUsername != _inlineBotUsername) { | ||||||
|  | @ -5320,8 +5322,7 @@ void HistoryWidget::onCheckMentionDropdown() { | ||||||
| 			_inlineBotResolveRequestId = 0; | 			_inlineBotResolveRequestId = 0; | ||||||
| 		} | 		} | ||||||
| 		if (_inlineBot == InlineBotLookingUpData) { | 		if (_inlineBot == InlineBotLookingUpData) { | ||||||
| 			LOG(("Inline bot unknown! resolving @%1").arg(_inlineBotUsername)); | 			_inlineBotResolveRequestId = MTP::send(MTPcontacts_ResolveUsername(MTP_string(_inlineBotUsername)), rpcDone(&HistoryWidget::inlineBotResolveDone), rpcFail(&HistoryWidget::inlineBotResolveFail, _inlineBotUsername)); | ||||||
| 			_inlineBotResolveRequestId = MTP::send(MTPcontacts_ResolveUsername(MTP_string(_inlineBotUsername)), rpcDone(&HistoryWidget::inlineBotResolveDone), rpcFail(&HistoryWidget::inlineBotResolveFail)); |  | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 	} else if (_inlineBot == InlineBotLookingUpData) { | 	} else if (_inlineBot == InlineBotLookingUpData) { | ||||||
|  | @ -5329,12 +5330,19 @@ void HistoryWidget::onCheckMentionDropdown() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (_inlineBot) { | 	if (_inlineBot) { | ||||||
|  | 		if (_inlineBot != bot) { | ||||||
|  | 			updateFieldPlaceholder(); | ||||||
|  | 		} | ||||||
| 		_emojiPan.queryInlineBot(_inlineBot, start); | 		_emojiPan.queryInlineBot(_inlineBot, start); | ||||||
| 		if (!_attachMention.isHidden()) { | 		if (!_attachMention.isHidden()) { | ||||||
| 			_attachMention.hideStart(); | 			_attachMention.hideStart(); | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		_emojiPan.inlineBotChanged(); | 		if (_inlineBot != bot) { | ||||||
|  | 			updateFieldPlaceholder(); | ||||||
|  | 			_field.finishPlaceholder(); | ||||||
|  | 		} | ||||||
|  | 		_emojiPan.clearInlineBot(); | ||||||
| 		if (!start.isEmpty()) { | 		if (!start.isEmpty()) { | ||||||
| 			if (start.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtags(); | 			if (start.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtags(); | ||||||
| 			if (start.at(0) == '@' && _peer->isUser()) return; | 			if (start.at(0) == '@' && _peer->isUser()) return; | ||||||
|  | @ -5348,6 +5356,16 @@ void HistoryWidget::onCheckMentionDropdown() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void HistoryWidget::updateFieldPlaceholder() { | ||||||
|  | 	if (_inlineBot && _inlineBot != InlineBotLookingUpData) { | ||||||
|  | 		_field.setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2); | ||||||
|  | 	} else if (hasBroadcastToggle()) { | ||||||
|  | 		_field.setPlaceholder(lang(_broadcast.checked() ? lng_broadcast_ph : lng_comment_ph)); | ||||||
|  | 	} else { | ||||||
|  | 		_field.setPlaceholder(lang((_history && _history->isChannel() && !_history->isMegagroup()) ? (_peer->asChannel()->canPublish() ? lng_broadcast_ph : lng_comment_ph) : lng_message_ph)); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void HistoryWidget::uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm, const QString &source, bool withText) { | void HistoryWidget::uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm, const QString &source, bool withText) { | ||||||
| 	if (!_history) return; | 	if (!_history) return; | ||||||
| 
 | 
 | ||||||
|  | @ -5790,6 +5808,10 @@ void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void HistoryWidget::notify_automaticLoadSettingsChangedGif() { | ||||||
|  | 	_emojiPan.notify_automaticLoadSettingsChangedGif(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void HistoryWidget::resizeEvent(QResizeEvent *e) { | void HistoryWidget::resizeEvent(QResizeEvent *e) { | ||||||
| 	_reportSpamPanel.resize(width(), _reportSpamPanel.height()); | 	_reportSpamPanel.resize(width(), _reportSpamPanel.height()); | ||||||
| 
 | 
 | ||||||
|  | @ -5828,7 +5850,7 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { | ||||||
| 
 | 
 | ||||||
| 	_attachType.move(0, _attachDocument.y() - _attachType.height()); | 	_attachType.move(0, _attachDocument.y() - _attachType.height()); | ||||||
| 	_emojiPan.setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height()); | 	_emojiPan.setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height()); | ||||||
| 	_emojiPan.move(width() - _emojiPan.width(), _attachEmoji.y() - _emojiPan.height()); | 	_emojiPan.moveBottom(_attachEmoji.y()); | ||||||
| 
 | 
 | ||||||
| 	switch (_attachDrag) { | 	switch (_attachDrag) { | ||||||
| 	case DragStateFiles: | 	case DragStateFiles: | ||||||
|  | @ -6341,18 +6363,21 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) { | ||||||
| 		} else if (result->type == qstr("photo")) { | 		} else if (result->type == qstr("photo")) { | ||||||
| 			QImage fileThumb(result->thumb->pix().toImage()); | 			QImage fileThumb(result->thumb->pix().toImage()); | ||||||
| 
 | 
 | ||||||
| 			PreparedPhotoThumbs photoThumbs; |  | ||||||
| 			QVector<MTPPhotoSize> photoSizes; | 			QVector<MTPPhotoSize> photoSizes; | ||||||
| 
 | 
 | ||||||
| 			QPixmap thumb = (fileThumb.width() > 100 || fileThumb.height() > 100) ? QPixmap::fromImage(fileThumb.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(fileThumb); | 			QPixmap thumb = (fileThumb.width() > 100 || fileThumb.height() > 100) ? QPixmap::fromImage(fileThumb.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(fileThumb); | ||||||
| 			photoThumbs.insert('s', thumb); | 			ImagePtr thumbPtr = ImagePtr(thumb, "JPG"); | ||||||
| 			photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0))); | 			photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0))); | ||||||
| 
 | 
 | ||||||
| 			QPixmap medium = (fileThumb.width() > 320 || fileThumb.height() > 320) ? QPixmap::fromImage(fileThumb.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(fileThumb); | 			QSize medium = resizeKeepAspect(result->width, result->height, 320, 320); | ||||||
| 			photoThumbs.insert('m', medium); |  | ||||||
| 			photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0))); | 			photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0))); | ||||||
| 
 | 
 | ||||||
| 			MTPPhoto photo = MTP_photo(MTP_long(MTP::nonce<uint64>()), MTP_long(0), MTP_int(unixtime()), MTP_vector<MTPPhotoSize>(photoSizes)); | 			photoSizes.push_back(MTP_photoSize(MTP_string("x"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(result->width), MTP_int(result->height), MTP_int(0))); | ||||||
|  | 
 | ||||||
|  | 			uint64 photoId = MTP::nonce<uint64>(); | ||||||
|  | 			PhotoData *ph = App::photoSet(photoId, 0, 0, unixtime(), thumbPtr, ImagePtr(medium.width(), medium.height()), ImagePtr(result->width, result->height)); | ||||||
|  | 			MTPPhoto photo = MTP_photo(MTP_long(photoId), MTP_long(0), MTP_int(ph->date), MTP_vector<MTPPhotoSize>(photoSizes)); | ||||||
|  | 
 | ||||||
| 			_history->addNewMessage(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(_history->peer->id), MTPPeer(), MTPint(), MTP_int(bot ? peerToUser(bot->id) : 0), MTP_int(replyToId()), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(photo, MTP_string(result->caption)), MTPnullMarkup, MTPnullEntities, MTP_int(1)), NewMessageUnread); | 			_history->addNewMessage(MTP_message(MTP_int(flags), MTP_int(newId.msg), MTP_int(fromChannelName ? 0 : MTP::authedId()), peerToMTP(_history->peer->id), MTPPeer(), MTPint(), MTP_int(bot ? peerToUser(bot->id) : 0), MTP_int(replyToId()), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(photo, MTP_string(result->caption)), MTPnullMarkup, MTPnullEntities, MTP_int(1)), NewMessageUnread); | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
|  |  | ||||||
|  | @ -98,7 +98,7 @@ public: | ||||||
| 	void notifyMigrateUpdated(); | 	void notifyMigrateUpdated(); | ||||||
| 
 | 
 | ||||||
| 	~HistoryInner(); | 	~HistoryInner(); | ||||||
| 	 | 
 | ||||||
| public slots: | public slots: | ||||||
| 
 | 
 | ||||||
| 	void onUpdateSelected(); | 	void onUpdateSelected(); | ||||||
|  | @ -186,7 +186,7 @@ private: | ||||||
| 	bool _touchScroll, _touchSelect, _touchInProgress; | 	bool _touchScroll, _touchSelect, _touchInProgress; | ||||||
| 	QPoint _touchStart, _touchPrevPos, _touchPos; | 	QPoint _touchStart, _touchPrevPos, _touchPos; | ||||||
| 	QTimer _touchSelectTimer; | 	QTimer _touchSelectTimer; | ||||||
| 	 | 
 | ||||||
| 	TouchScrollState _touchScrollState; | 	TouchScrollState _touchScrollState; | ||||||
| 	bool _touchPrevPosValid, _touchWaitingAcceleration; | 	bool _touchPrevPosValid, _touchWaitingAcceleration; | ||||||
| 	QPoint _touchSpeed; | 	QPoint _touchSpeed; | ||||||
|  | @ -445,6 +445,8 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void destroyData(); | 	void destroyData(); | ||||||
| 
 | 
 | ||||||
|  | 	void updateFieldPlaceholder(); | ||||||
|  | 
 | ||||||
| 	void uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, const QString &source = QString(), bool withText = false); | 	void uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, const QString &source = QString(), bool withText = false); | ||||||
| 	void uploadFile(const QString &file, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, bool withText = false); // with confirmation
 | 	void uploadFile(const QString &file, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, bool withText = false); // with confirmation
 | ||||||
| 	void uploadFiles(const QStringList &files, PrepareMediaType type); | 	void uploadFiles(const QStringList &files, PrepareMediaType type); | ||||||
|  | @ -484,7 +486,7 @@ public: | ||||||
| 	void noSelectingScroll(); | 	void noSelectingScroll(); | ||||||
| 
 | 
 | ||||||
| 	bool touchScroll(const QPoint &delta); | 	bool touchScroll(const QPoint &delta); | ||||||
|      | 
 | ||||||
| 	uint64 animActiveTimeStart(const HistoryItem *msg) const; | 	uint64 animActiveTimeStart(const HistoryItem *msg) const; | ||||||
| 	void stopAnimActive(); | 	void stopAnimActive(); | ||||||
| 
 | 
 | ||||||
|  | @ -564,6 +566,7 @@ public: | ||||||
| 	bool ui_isInlineItemBeingChosen(); | 	bool ui_isInlineItemBeingChosen(); | ||||||
| 
 | 
 | ||||||
| 	void notify_historyItemLayoutChanged(const HistoryItem *item); | 	void notify_historyItemLayoutChanged(const HistoryItem *item); | ||||||
|  | 	void notify_automaticLoadSettingsChangedGif(); | ||||||
| 	void notify_botCommandsChanged(UserData *user); | 	void notify_botCommandsChanged(UserData *user); | ||||||
| 	void notify_userIsBotChanged(UserData *user); | 	void notify_userIsBotChanged(UserData *user); | ||||||
| 	void notify_migrateUpdated(PeerData *peer); | 	void notify_migrateUpdated(PeerData *peer); | ||||||
|  | @ -769,7 +772,7 @@ private: | ||||||
| 	QString _inlineBotUsername; | 	QString _inlineBotUsername; | ||||||
| 	mtpRequestId _inlineBotResolveRequestId; | 	mtpRequestId _inlineBotResolveRequestId; | ||||||
| 	void inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result); | 	void inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result); | ||||||
| 	bool inlineBotResolveFail(const RPCError &error); | 	bool inlineBotResolveFail(QString name, const RPCError &error); | ||||||
| 
 | 
 | ||||||
| 	bool isBotStart() const; | 	bool isBotStart() const; | ||||||
| 	bool isBlocked() const; | 	bool isBlocked() const; | ||||||
|  |  | ||||||
|  | @ -504,7 +504,7 @@ void LayoutOverviewVideo::paint(Painter &p, const QRect &clip, uint32 selection, | ||||||
| 
 | 
 | ||||||
| void LayoutOverviewVideo::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { | void LayoutOverviewVideo::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { | ||||||
| 	bool loaded = _data->loaded(); | 	bool loaded = _data->loaded(); | ||||||
| 		 | 
 | ||||||
| 	if (hasPoint(x, y)) { | 	if (hasPoint(x, y)) { | ||||||
| 		link = loaded ? _openl : (_data->loading() ? _cancell : _savel); | 		link = loaded ? _openl : (_data->loading() ? _cancell : _savel); | ||||||
| 	} | 	} | ||||||
|  | @ -1275,7 +1275,7 @@ void LayoutOverviewLink::getState(TextLinkPtr &link, HistoryCursorState &cursor, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| LayoutOverviewLink::Link::Link(const QString &url, const QString &text)  | LayoutOverviewLink::Link::Link(const QString &url, const QString &text) | ||||||
| : text(text) | : text(text) | ||||||
| , width(st::normalFont->width(text)) | , width(st::normalFont->width(text)) | ||||||
| , lnk(linkFromUrl(url)) { | , lnk(linkFromUrl(url)) { | ||||||
|  | @ -1371,7 +1371,7 @@ void DeleteSavedGifLink::onClick(Qt::MouseButton button) const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LayoutInlineGif::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { | void LayoutInlineGif::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { | ||||||
| //	content_automaticLoad();
 | 	content_automaticLoad(); | ||||||
| 
 | 
 | ||||||
| 	bool loaded = content_loaded(), loading = content_loading(), displayLoading = content_displayLoading(); | 	bool loaded = content_loaded(), loading = content_loading(), displayLoading = content_displayLoading(); | ||||||
| 	if (loaded && !gif() && _gif != BadClipReader) { | 	if (loaded && !gif() && _gif != BadClipReader) { | ||||||
|  | @ -1832,23 +1832,20 @@ void LayoutInlineWebVideo::initDimensions() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LayoutInlineWebVideo::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { | void LayoutInlineWebVideo::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { | ||||||
| 	int32 left = 0; | 	int32 left = st::inlineThumbSize + st::inlineThumbSkip; | ||||||
| 	if (!_result->thumb->isNull()) { | 	prepareThumb(st::inlineThumbSize, st::inlineThumbSize); | ||||||
| 		left = st::inlineThumbSize + st::inlineThumbSkip; | 	if (_thumb.isNull()) { | ||||||
| 		prepareThumb(st::inlineThumbSize, st::inlineThumbSize); | 		p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), _result->thumb->isNull() ? st::black : st::overviewPhotoBg); | ||||||
| 		if (_thumb.isNull()) { | 	} else { | ||||||
| 			p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), _result->thumb->isNull() ? st::black : st::overviewPhotoBg); | 		p.drawPixmapLeft(0, st::inlineRowMargin, _width, _thumb); | ||||||
| 		} else { | 	} | ||||||
| 			p.drawPixmapLeft(0, st::inlineRowMargin, _width, _thumb); |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		if (!_duration.isEmpty()) { | 	if (!_duration.isEmpty()) { | ||||||
| 			int32 durationTop = st::inlineRowMargin + st::inlineThumbSize - st::normalFont->height - st::inlineDurationMargin; | 		int32 durationTop = st::inlineRowMargin + st::inlineThumbSize - st::normalFont->height - st::inlineDurationMargin; | ||||||
| 			p.fillRect(rtlrect(0, durationTop - st::inlineDurationMargin, st::inlineThumbSize, st::normalFont->height + 2 * st::inlineDurationMargin, _width), st::msgDateImgBg); | 		p.fillRect(rtlrect(0, durationTop - st::inlineDurationMargin, st::inlineThumbSize, st::normalFont->height + 2 * st::inlineDurationMargin, _width), st::msgDateImgBg); | ||||||
| 			p.setPen(st::white); | 		p.setPen(st::white); | ||||||
| 			p.setFont(st::normalFont); | 		p.setFont(st::normalFont); | ||||||
| 			p.drawTextRight(_width - st::inlineThumbSize + st::inlineDurationMargin, durationTop, _width, _duration); | 		p.drawTextRight(_width - st::inlineThumbSize + st::inlineDurationMargin, durationTop, _width, _duration); | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	p.setPen(st::black); | 	p.setPen(st::black); | ||||||
|  | @ -1951,7 +1948,7 @@ int32 LayoutInlineArticle::resizeGetHeight(int32 width) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LayoutInlineArticle::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { | void LayoutInlineArticle::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { | ||||||
| 	int32 left = 0; | 	int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft; | ||||||
| 	if (_withThumb) { | 	if (_withThumb) { | ||||||
| 		left = st::inlineThumbSize + st::inlineThumbSkip; | 		left = st::inlineThumbSize + st::inlineThumbSkip; | ||||||
| 		prepareThumb(st::inlineThumbSize, st::inlineThumbSize); | 		prepareThumb(st::inlineThumbSize, st::inlineThumbSize); | ||||||
|  |  | ||||||
|  | @ -367,7 +367,7 @@ namespace { | ||||||
| 	bool fileExists(const FileKey &fkey, int options = UserPath | SafePath) { | 	bool fileExists(const FileKey &fkey, int options = UserPath | SafePath) { | ||||||
| 		return fileExists(toFilePart(fkey), options); | 		return fileExists(toFilePart(fkey), options); | ||||||
| 	} | 	} | ||||||
| 		 | 
 | ||||||
| 	bool readFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath) { | 	bool readFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath) { | ||||||
| 		if (options & UserPath) { | 		if (options & UserPath) { | ||||||
| 			if (!_userWorking()) return false; | 			if (!_userWorking()) return false; | ||||||
|  | @ -561,6 +561,8 @@ namespace { | ||||||
| 	typedef QMap<PeerId, bool> DraftsNotReadMap; | 	typedef QMap<PeerId, bool> DraftsNotReadMap; | ||||||
| 	DraftsNotReadMap _draftsNotReadMap; | 	DraftsNotReadMap _draftsNotReadMap; | ||||||
| 
 | 
 | ||||||
|  | 	typedef QPair<FileKey, qint32> FileDesc; // file, size
 | ||||||
|  | 
 | ||||||
| 	typedef QMultiMap<MediaKey, FileLocation> FileLocations; | 	typedef QMultiMap<MediaKey, FileLocation> FileLocations; | ||||||
| 	FileLocations _fileLocations; | 	FileLocations _fileLocations; | ||||||
| 	typedef QPair<MediaKey, FileLocation> FileLocationPair; | 	typedef QPair<MediaKey, FileLocation> FileLocationPair; | ||||||
|  | @ -568,10 +570,13 @@ namespace { | ||||||
| 	FileLocationPairs _fileLocationPairs; | 	FileLocationPairs _fileLocationPairs; | ||||||
| 	typedef QMap<MediaKey, MediaKey> FileLocationAliases; | 	typedef QMap<MediaKey, MediaKey> FileLocationAliases; | ||||||
| 	FileLocationAliases _fileLocationAliases; | 	FileLocationAliases _fileLocationAliases; | ||||||
|  | 	typedef QMap<QString, FileDesc> WebFilesMap; | ||||||
|  | 	WebFilesMap _webFilesMap; | ||||||
|  | 	uint64 _storageWebFilesSize = 0; | ||||||
| 	FileKey _locationsKey = 0, _reportSpamStatusesKey = 0; | 	FileKey _locationsKey = 0, _reportSpamStatusesKey = 0; | ||||||
| 	 | 
 | ||||||
| 	FileKey _recentStickersKeyOld = 0, _stickersKey = 0, _savedGifsKey = 0; | 	FileKey _recentStickersKeyOld = 0, _stickersKey = 0, _savedGifsKey = 0; | ||||||
| 	 | 
 | ||||||
| 	FileKey _backgroundKey = 0; | 	FileKey _backgroundKey = 0; | ||||||
| 	bool _backgroundWasRead = false; | 	bool _backgroundWasRead = false; | ||||||
| 
 | 
 | ||||||
|  | @ -581,7 +586,6 @@ namespace { | ||||||
| 
 | 
 | ||||||
| 	FileKey _savedPeersKey = 0; | 	FileKey _savedPeersKey = 0; | ||||||
| 
 | 
 | ||||||
| 	typedef QPair<FileKey, qint32> FileDesc; // file, size
 |  | ||||||
| 	typedef QMap<StorageKey, FileDesc> StorageMap; | 	typedef QMap<StorageKey, FileDesc> StorageMap; | ||||||
| 	StorageMap _imagesMap, _stickerImagesMap, _audiosMap; | 	StorageMap _imagesMap, _stickerImagesMap, _audiosMap; | ||||||
| 	int32 _storageImagesSize = 0, _storageStickersSize = 0, _storageAudiosSize = 0; | 	int32 _storageImagesSize = 0, _storageStickersSize = 0, _storageAudiosSize = 0; | ||||||
|  | @ -643,6 +647,12 @@ namespace { | ||||||
| 				size += sizeof(quint64) * 2 + sizeof(quint64) * 2; | 				size += sizeof(quint64) * 2 + sizeof(quint64) * 2; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			size += sizeof(quint32); // web files count
 | ||||||
|  | 			for (WebFilesMap::const_iterator i = _webFilesMap.cbegin(), e = _webFilesMap.cend(); i != e; ++i) { | ||||||
|  | 				// url + filekey + size
 | ||||||
|  | 				size += _stringSize(i.key()) + sizeof(quint64) + sizeof(qint32); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			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) { | ||||||
| 				data.stream << quint64(i.key().first) << quint64(i.key().second) << quint32(i.value().type) << i.value().name(); | 				data.stream << quint64(i.key().first) << quint64(i.key().second) << quint32(i.value().type) << i.value().name(); | ||||||
|  | @ -663,6 +673,11 @@ namespace { | ||||||
| 				data.stream << quint64(i.key().first) << quint64(i.key().second) << quint64(i.value().first) << quint64(i.value().second); | 				data.stream << quint64(i.key().first) << quint64(i.key().second) << quint64(i.value().first) << quint64(i.value().second); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			data.stream << quint32(_webFilesMap.size()); | ||||||
|  | 			for (WebFilesMap::const_iterator i = _webFilesMap.cbegin(), e = _webFilesMap.cend(); i != e; ++i) { | ||||||
|  | 				data.stream << i.key() << quint64(i.value().first) << qint32(i.value().second); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			FileWriteDescriptor file(_locationsKey); | 			FileWriteDescriptor file(_locationsKey); | ||||||
| 			file.writeEncrypted(data); | 			file.writeEncrypted(data); | ||||||
| 		} | 		} | ||||||
|  | @ -710,6 +725,20 @@ namespace { | ||||||
| 				locations.stream >> kfirst >> ksecond >> vfirst >> vsecond; | 				locations.stream >> kfirst >> ksecond >> vfirst >> vsecond; | ||||||
| 				_fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond)); | 				_fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond)); | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			_storageWebFilesSize = 0; | ||||||
|  | 			_webFilesMap.clear(); | ||||||
|  | 
 | ||||||
|  | 			quint32 webLocationsCount; | ||||||
|  | 			locations.stream >> webLocationsCount; | ||||||
|  | 			for (quint32 i = 0; i < webLocationsCount; ++i) { | ||||||
|  | 				QString url; | ||||||
|  | 				quint64 key; | ||||||
|  | 				qint32 size; | ||||||
|  | 				locations.stream >> url >> key >> size; | ||||||
|  | 				_webFilesMap.insert(url, FileDesc(key, size)); | ||||||
|  | 				_storageWebFilesSize += size; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -2114,7 +2143,7 @@ namespace Local { | ||||||
| 			data.stream << quint32(dbiDcOption) << quint32(i.key()); | 			data.stream << quint32(dbiDcOption) << quint32(i.key()); | ||||||
| 			data.stream << quint32(i->flags) << QString::fromUtf8(i->ip.data(), i->ip.size()); | 			data.stream << quint32(i->flags) << QString::fromUtf8(i->ip.data(), i->ip.size()); | ||||||
| 			data.stream << quint32(i->port); | 			data.stream << quint32(i->port); | ||||||
| 		}			 | 		} | ||||||
| 		data.stream << quint32(dbiLangFile) << cLangFile(); | 		data.stream << quint32(dbiLangFile) << cLangFile(); | ||||||
| 
 | 
 | ||||||
| 		data.stream << quint32(dbiConnectionType) << qint32(cConnectionType()); | 		data.stream << quint32(dbiConnectionType) << qint32(cConnectionType()); | ||||||
|  | @ -2154,6 +2183,8 @@ namespace Local { | ||||||
| 		_stickerImagesMap.clear(); | 		_stickerImagesMap.clear(); | ||||||
| 		_audiosMap.clear(); | 		_audiosMap.clear(); | ||||||
| 		_storageImagesSize = _storageStickersSize = _storageAudiosSize = 0; | 		_storageImagesSize = _storageStickersSize = _storageAudiosSize = 0; | ||||||
|  | 		_webFilesMap.clear(); | ||||||
|  | 		_storageWebFilesSize = 0; | ||||||
| 		_locationsKey = _reportSpamStatusesKey = 0; | 		_locationsKey = _reportSpamStatusesKey = 0; | ||||||
| 		_recentStickersKeyOld = _stickersKey = _savedGifsKey = 0; | 		_recentStickersKeyOld = _stickersKey = _savedGifsKey = 0; | ||||||
| 		_backgroundKey = _userSettingsKey = _recentHashtagsKey = _savedPeersKey = 0; | 		_backgroundKey = _userSettingsKey = _recentHashtagsKey = _savedPeersKey = 0; | ||||||
|  | @ -2437,9 +2468,10 @@ namespace Local { | ||||||
| 			quint32 imageType; | 			quint32 imageType; | ||||||
| 			readFromStream(image.stream, locFirst, locSecond, imageType, imageData); | 			readFromStream(image.stream, locFirst, locSecond, imageType, imageData); | ||||||
| 
 | 
 | ||||||
| 			if (locFirst != _location.first || locSecond != _location.second) { | 			// we're saving files now before we have actual location
 | ||||||
| 				return; | 			//if (locFirst != _location.first || locSecond != _location.second) {
 | ||||||
| 			} | 			//	return;
 | ||||||
|  | 			//}
 | ||||||
| 
 | 
 | ||||||
| 			_result = new Result(StorageFileType(imageType), imageData, _readImageFlag); | 			_result = new Result(StorageFileType(imageType), imageData, _readImageFlag); | ||||||
| 		} | 		} | ||||||
|  | @ -2652,6 +2684,110 @@ namespace Local { | ||||||
| 		return _storageAudiosSize; | 		return _storageAudiosSize; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	qint32 _storageWebFileSize(const QString &url, qint32 rawlen) { | ||||||
|  | 		// fulllen + url + len + data
 | ||||||
|  | 		qint32 result = sizeof(uint32) + _stringSize(url) + 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 writeWebFile(const QString &url, const QByteArray &content, bool overwrite) { | ||||||
|  | 		if (!_working()) return; | ||||||
|  | 
 | ||||||
|  | 		qint32 size = _storageWebFileSize(url, content.size()); | ||||||
|  | 		WebFilesMap::const_iterator i = _webFilesMap.constFind(url); | ||||||
|  | 		if (i == _webFilesMap.cend()) { | ||||||
|  | 			i = _webFilesMap.insert(url, FileDesc(genKey(UserPath), size)); | ||||||
|  | 			_storageWebFilesSize += size; | ||||||
|  | 			_writeLocations(); | ||||||
|  | 		} else if (!overwrite) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		EncryptedDescriptor data(_stringSize(url) + sizeof(quint32) + sizeof(quint32) + content.size()); | ||||||
|  | 		data.stream << url << content; | ||||||
|  | 		FileWriteDescriptor file(i.value().first, UserPath); | ||||||
|  | 		file.writeEncrypted(data); | ||||||
|  | 		if (i.value().second != size) { | ||||||
|  | 			_storageWebFilesSize += size; | ||||||
|  | 			_storageWebFilesSize -= i.value().second; | ||||||
|  | 			_webFilesMap[url].second = size; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	class WebFileLoadTask : public Task { | ||||||
|  | 	public: | ||||||
|  | 		WebFileLoadTask(const FileKey &key, const QString &url, webFileLoader *loader) | ||||||
|  | 			: _key(key) | ||||||
|  | 			, _url(url) | ||||||
|  | 			, _loader(loader) | ||||||
|  | 			, _result(0) { | ||||||
|  | 		} | ||||||
|  | 		void process() { | ||||||
|  | 			FileReadDescriptor image; | ||||||
|  | 			if (!readEncryptedFile(image, _key, UserPath)) { | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			QByteArray imageData; | ||||||
|  | 			QString url; | ||||||
|  | 			image.stream >> url >> imageData; | ||||||
|  | 
 | ||||||
|  | 			_result = new Result(StorageFilePartial, imageData); | ||||||
|  | 		} | ||||||
|  | 		void finish() { | ||||||
|  | 			if (_result) { | ||||||
|  | 				_loader->localLoaded(_result->image, _result->format, _result->pixmap); | ||||||
|  | 			} else { | ||||||
|  | 				WebFilesMap::iterator j = _webFilesMap.find(_url); | ||||||
|  | 				if (j != _webFilesMap.cend() && j->first == _key) { | ||||||
|  | 					clearKey(j.value().first, UserPath); | ||||||
|  | 					_storageWebFilesSize -= j.value().second; | ||||||
|  | 					_webFilesMap.erase(j); | ||||||
|  | 				} | ||||||
|  | 				_loader->localLoaded(StorageImageSaved()); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		virtual ~WebFileLoadTask() { | ||||||
|  | 			deleteAndMark(_result); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	protected: | ||||||
|  | 		FileKey _key; | ||||||
|  | 		QString _url; | ||||||
|  | 		struct Result { | ||||||
|  | 			Result(StorageFileType type, const QByteArray &data) : image(type, data) { | ||||||
|  | 				QByteArray guessFormat; | ||||||
|  | 				pixmap = QPixmap::fromImage(App::readImage(data, &guessFormat, false), Qt::ColorOnly); | ||||||
|  | 				if (!pixmap.isNull()) { | ||||||
|  | 					format = guessFormat; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			StorageImageSaved image; | ||||||
|  | 			QByteArray format; | ||||||
|  | 			QPixmap pixmap; | ||||||
|  | 		}; | ||||||
|  | 		webFileLoader *_loader; | ||||||
|  | 		Result *_result; | ||||||
|  | 
 | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	TaskId startWebFileLoad(const QString &url, webFileLoader *loader) { | ||||||
|  | 		WebFilesMap::const_iterator j = _webFilesMap.constFind(url); | ||||||
|  | 		if (j == _webFilesMap.cend() || !_localLoader) { | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 		return _localLoader->addTask(new WebFileLoadTask(j->first, url, loader)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	int32 hasWebFiles() { | ||||||
|  | 		return _webFilesMap.size(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	qint64 storageWebFilesSize() { | ||||||
|  | 		return _storageWebFilesSize; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	void cancelTask(TaskId id) { | 	void cancelTask(TaskId id) { | ||||||
| 		if (_localLoader) { | 		if (_localLoader) { | ||||||
| 			_localLoader->cancelTask(id); | 			_localLoader->cancelTask(id); | ||||||
|  | @ -3003,7 +3139,7 @@ namespace Local { | ||||||
| 			data.stream << quint32(saved.size()); | 			data.stream << quint32(saved.size()); | ||||||
| 			for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { | 			for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { | ||||||
| 				DocumentData *doc = *i; | 				DocumentData *doc = *i; | ||||||
| 				 | 
 | ||||||
| 				data.stream << quint64(doc->id) << 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) << qint32(doc->duration()); | 				data.stream << quint64(doc->id) << 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) << qint32(doc->duration()); | ||||||
| 				_writeStorageImageLocation(data.stream, doc->thumb->location()); | 				_writeStorageImageLocation(data.stream, doc->thumb->location()); | ||||||
| 			} | 			} | ||||||
|  | @ -3056,7 +3192,7 @@ namespace Local { | ||||||
| 
 | 
 | ||||||
| 			DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.isNull() ? ImagePtr() : ImagePtr(thumb), dc, size, thumb); | 			DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.isNull() ? ImagePtr() : ImagePtr(thumb), dc, size, thumb); | ||||||
| 			if (!doc->isAnimation()) continue; | 			if (!doc->isAnimation()) continue; | ||||||
| 			 | 
 | ||||||
| 			saved.push_back(doc); | 			saved.push_back(doc); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -3185,7 +3321,7 @@ namespace Local { | ||||||
| 
 | 
 | ||||||
| 		QString tag; | 		QString tag; | ||||||
| 		quint16 count; | 		quint16 count; | ||||||
| 			 | 
 | ||||||
| 		RecentHashtagPack write, search; | 		RecentHashtagPack write, search; | ||||||
| 		if (writeCount) { | 		if (writeCount) { | ||||||
| 			write.reserve(writeCount); | 			write.reserve(writeCount); | ||||||
|  | @ -3416,7 +3552,7 @@ namespace Local { | ||||||
| 
 | 
 | ||||||
| 			QDateTime t; | 			QDateTime t; | ||||||
| 			saved.stream >> t; | 			saved.stream >> t; | ||||||
| 			 | 
 | ||||||
| 			cRefSavedPeers().insert(peer, t); | 			cRefSavedPeers().insert(peer, t); | ||||||
| 			cRefSavedPeersByTime().insert(t, peer); | 			cRefSavedPeersByTime().insert(t, peer); | ||||||
| 			peers.push_back(peer); | 			peers.push_back(peer); | ||||||
|  |  | ||||||
|  | @ -65,7 +65,7 @@ namespace Local { | ||||||
| 
 | 
 | ||||||
| 	bool checkPasscode(const QByteArray &passcode); | 	bool checkPasscode(const QByteArray &passcode); | ||||||
| 	void setPasscode(const QByteArray &passcode); | 	void setPasscode(const QByteArray &passcode); | ||||||
| 	 | 
 | ||||||
| 	enum ClearManagerTask { | 	enum ClearManagerTask { | ||||||
| 		ClearManagerAll = 0xFFFF, | 		ClearManagerAll = 0xFFFF, | ||||||
| 		ClearManagerDownloads = 0x01, | 		ClearManagerDownloads = 0x01, | ||||||
|  | @ -139,6 +139,11 @@ namespace Local { | ||||||
| 	int32 hasAudios(); | 	int32 hasAudios(); | ||||||
| 	qint64 storageAudiosSize(); | 	qint64 storageAudiosSize(); | ||||||
| 
 | 
 | ||||||
|  | 	void writeWebFile(const QString &url, const QByteArray &data, bool overwrite = true); | ||||||
|  | 	TaskId startWebFileLoad(const QString &url, webFileLoader *loader); | ||||||
|  | 	int32 hasWebFiles(); | ||||||
|  | 	qint64 storageWebFilesSize(); | ||||||
|  | 
 | ||||||
| 	void cancelTask(TaskId id); | 	void cancelTask(TaskId id); | ||||||
| 
 | 
 | ||||||
| 	void writeStickers(); | 	void writeStickers(); | ||||||
|  |  | ||||||
|  | @ -601,7 +601,7 @@ void MainWidget::cancelForwarding() { | ||||||
| 
 | 
 | ||||||
| void MainWidget::finishForwarding(History *hist, bool broadcast) { | void MainWidget::finishForwarding(History *hist, bool broadcast) { | ||||||
| 	if (!hist) return; | 	if (!hist) return; | ||||||
| 	 | 
 | ||||||
| 	bool fromChannelName = hist->peer->isChannel() && !hist->peer->isMegagroup() && hist->peer->asChannel()->canPublish() && (hist->peer->asChannel()->isBroadcast() || broadcast); | 	bool fromChannelName = hist->peer->isChannel() && !hist->peer->isMegagroup() && hist->peer->asChannel()->canPublish() && (hist->peer->asChannel()->isBroadcast() || broadcast); | ||||||
| 	if (!_toForward.isEmpty()) { | 	if (!_toForward.isEmpty()) { | ||||||
| 		bool genClientSideMessage = (_toForward.size() < 2); | 		bool genClientSideMessage = (_toForward.size() < 2); | ||||||
|  | @ -815,6 +815,10 @@ void MainWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { | ||||||
| 	if (overview) overview->notify_historyItemLayoutChanged(item); | 	if (overview) overview->notify_historyItemLayoutChanged(item); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void MainWidget::notify_automaticLoadSettingsChangedGif() { | ||||||
|  | 	history.notify_automaticLoadSettingsChangedGif(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void MainWidget::notify_historyItemResized(const HistoryItem *item, bool scrollToIt) { | void MainWidget::notify_historyItemResized(const HistoryItem *item, bool scrollToIt) { | ||||||
| 	if (!item || ((history.peer() == item->history()->peer || (history.peer() && history.peer() == item->history()->peer->migrateTo())) && !item->detached())) { | 	if (!item || ((history.peer() == item->history()->peer || (history.peer() && history.peer() == item->history()->peer->migrateTo())) && !item->detached())) { | ||||||
| 		history.notify_historyItemResized(item, scrollToIt); | 		history.notify_historyItemResized(item, scrollToIt); | ||||||
|  | @ -1355,7 +1359,7 @@ void MainWidget::saveRecentHashtags(const QString &text) { | ||||||
| 
 | 
 | ||||||
| void MainWidget::readServerHistory(History *hist, bool force) { | void MainWidget::readServerHistory(History *hist, bool force) { | ||||||
| 	if (!hist || (!force && !hist->unreadCount)) return; | 	if (!hist || (!force && !hist->unreadCount)) return; | ||||||
|      | 
 | ||||||
| 	MsgId upTo = hist->inboxRead(0); | 	MsgId upTo = hist->inboxRead(0); | ||||||
| 	if (hist->isChannel() && !hist->peer->asChannel()->amIn()) { | 	if (hist->isChannel() && !hist->peer->asChannel()->amIn()) { | ||||||
| 		return; // no read request for channels that I didn't koin
 | 		return; // no read request for channels that I didn't koin
 | ||||||
|  | @ -2988,7 +2992,7 @@ void MainWidget::updSetState(int32 pts, int32 date, int32 qts, int32 seq) { | ||||||
| 
 | 
 | ||||||
| void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff) { | void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff) { | ||||||
| 	_channelFailDifferenceTimeout.remove(channel); | 	_channelFailDifferenceTimeout.remove(channel); | ||||||
| 	 | 
 | ||||||
| 	int32 timeout = 0; | 	int32 timeout = 0; | ||||||
| 	bool isFinal = true; | 	bool isFinal = true; | ||||||
| 	switch (diff.type()) { | 	switch (diff.type()) { | ||||||
|  | @ -3041,7 +3045,7 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha | ||||||
| 
 | 
 | ||||||
| 		App::feedUsers(d.vusers); | 		App::feedUsers(d.vusers); | ||||||
| 		App::feedChats(d.vchats, false); | 		App::feedChats(d.vchats, false); | ||||||
| 		 | 
 | ||||||
| 		_handlingChannelDifference = true; | 		_handlingChannelDifference = true; | ||||||
| 		feedMessageIds(d.vother_updates); | 		feedMessageIds(d.vother_updates); | ||||||
| 
 | 
 | ||||||
|  | @ -3194,7 +3198,7 @@ void MainWidget::gotDifference(const MTPupdates_Difference &diff) { | ||||||
| 		noUpdatesTimer.start(NoUpdatesTimeout); | 		noUpdatesTimer.start(NoUpdatesTimeout); | ||||||
| 
 | 
 | ||||||
| 		_ptsWaiter.setRequesting(false); | 		_ptsWaiter.setRequesting(false); | ||||||
| 		 | 
 | ||||||
| 		App::emitPeerUpdated(); | 		App::emitPeerUpdated(); | ||||||
| 	} break; | 	} break; | ||||||
| 	case mtpc_updates_differenceSlice: { | 	case mtpc_updates_differenceSlice: { | ||||||
|  | @ -3995,7 +3999,7 @@ void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) { | ||||||
| 	if (end <= from || !MTP::authedId()) return; | 	if (end <= from || !MTP::authedId()) return; | ||||||
| 
 | 
 | ||||||
| 	App::wnd()->checkAutoLock(); | 	App::wnd()->checkAutoLock(); | ||||||
| 	 | 
 | ||||||
| 	if (mtpTypeId(*from) == mtpc_new_session_created) { | 	if (mtpTypeId(*from) == mtpc_new_session_created) { | ||||||
| 		MTPNewSession newSession(from, end); | 		MTPNewSession newSession(from, end); | ||||||
| 		updSeq = 0; | 		updSeq = 0; | ||||||
|  | @ -4135,7 +4139,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) { | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		 | 
 | ||||||
| 		if (!ptsUpdated(d.vpts.v, d.vpts_count.v, updates)) { | 		if (!ptsUpdated(d.vpts.v, d.vpts_count.v, updates)) { | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -94,7 +94,7 @@ private: | ||||||
| 	bool _canDelete; | 	bool _canDelete; | ||||||
| 	QString _selStr; | 	QString _selStr; | ||||||
| 	int32 _selStrLeft, _selStrWidth; | 	int32 _selStrLeft, _selStrWidth; | ||||||
|      | 
 | ||||||
|     bool _animating; |     bool _animating; | ||||||
| 
 | 
 | ||||||
| 	FlatButton _clearSelection; | 	FlatButton _clearSelection; | ||||||
|  | @ -317,10 +317,10 @@ public: | ||||||
| 
 | 
 | ||||||
| 	DialogsIndexed &contactsList(); | 	DialogsIndexed &contactsList(); | ||||||
| 	DialogsIndexed &dialogsList(); | 	DialogsIndexed &dialogsList(); | ||||||
|      | 
 | ||||||
| 	void sendMessage(History *hist, const QString &text, MsgId replyTo, bool broadcast, WebPageId webPageId = 0); | 	void sendMessage(History *hist, const QString &text, MsgId replyTo, bool broadcast, WebPageId webPageId = 0); | ||||||
| 	void saveRecentHashtags(const QString &text); | 	void saveRecentHashtags(const QString &text); | ||||||
|      | 
 | ||||||
|     void readServerHistory(History *history, bool force = true); |     void readServerHistory(History *history, bool force = true); | ||||||
| 
 | 
 | ||||||
| 	uint64 animActiveTimeStart(const HistoryItem *msg) const; | 	uint64 animActiveTimeStart(const HistoryItem *msg) const; | ||||||
|  | @ -364,7 +364,7 @@ public: | ||||||
| 	void updateBotKeyboard(History *h); | 	void updateBotKeyboard(History *h); | ||||||
| 
 | 
 | ||||||
| 	void pushReplyReturn(HistoryItem *item); | 	void pushReplyReturn(HistoryItem *item); | ||||||
| 	 | 
 | ||||||
| 	bool hasForwardingItems(); | 	bool hasForwardingItems(); | ||||||
| 	void fillForwardingInfo(Text *&from, Text *&text, bool &serviceColor, ImagePtr &preview); | 	void fillForwardingInfo(Text *&from, Text *&text, bool &serviceColor, ImagePtr &preview); | ||||||
| 	void updateForwardingTexts(); | 	void updateForwardingTexts(); | ||||||
|  | @ -422,6 +422,7 @@ public: | ||||||
| 	void notify_clipStopperHidden(ClipStopperType type); | 	void notify_clipStopperHidden(ClipStopperType type); | ||||||
| 	void notify_historyItemResized(const HistoryItem *row, bool scrollToIt); | 	void notify_historyItemResized(const HistoryItem *row, bool scrollToIt); | ||||||
| 	void notify_historyItemLayoutChanged(const HistoryItem *item); | 	void notify_historyItemLayoutChanged(const HistoryItem *item); | ||||||
|  | 	void notify_automaticLoadSettingsChangedGif(); | ||||||
| 
 | 
 | ||||||
| 	~MainWidget(); | 	~MainWidget(); | ||||||
| 
 | 
 | ||||||
|  | @ -615,7 +616,7 @@ private: | ||||||
| 
 | 
 | ||||||
| 	QSet<PeerData*> updateNotifySettingPeers; | 	QSet<PeerData*> updateNotifySettingPeers; | ||||||
| 	SingleTimer updateNotifySettingTimer; | 	SingleTimer updateNotifySettingTimer; | ||||||
|      | 
 | ||||||
|     typedef QMap<PeerData*, QPair<mtpRequestId, MsgId> > ReadRequests; |     typedef QMap<PeerData*, QPair<mtpRequestId, MsgId> > ReadRequests; | ||||||
|     ReadRequests _readRequests; |     ReadRequests _readRequests; | ||||||
| 	typedef QMap<PeerData*, MsgId> ReadRequestsPending; | 	typedef QMap<PeerData*, MsgId> ReadRequestsPending; | ||||||
|  |  | ||||||
|  | @ -628,7 +628,7 @@ void webFileLoader::onFinished(const QByteArray &data) { | ||||||
| 	emit App::wnd()->imageLoaded(); | 	emit App::wnd()->imageLoaded(); | ||||||
| 
 | 
 | ||||||
| 	if (_localStatus == LocalNotFound || _localStatus == LocalFailed) { | 	if (_localStatus == LocalNotFound || _localStatus == LocalFailed) { | ||||||
| 		//Local::writeWebFile(_url, StorageImageSaved(mtpToStorageType(_type), _data));
 | 		Local::writeWebFile(_url, _data); | ||||||
| 	} | 	} | ||||||
| 	emit progress(this); | 	emit progress(this); | ||||||
| 	loadNext(); | 	loadNext(); | ||||||
|  | @ -646,7 +646,7 @@ bool webFileLoader::tryLoadLocal() { | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_localTaskId = 0;// Local::startWebFileLoad(_url, this);
 | 	_localTaskId = Local::startWebFileLoad(_url, this); | ||||||
| 	if (_localStatus != LocalNotTried) { | 	if (_localStatus != LocalNotTried) { | ||||||
| 		return _complete; | 		return _complete; | ||||||
| 	} else if (_localTaskId) { | 	} else if (_localTaskId) { | ||||||
|  |  | ||||||
|  | @ -279,7 +279,7 @@ protected: | ||||||
| 
 | 
 | ||||||
| 	uint64 _id; // for other locations
 | 	uint64 _id; // for other locations
 | ||||||
| 	uint64 _access; | 	uint64 _access; | ||||||
| 	 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class webFileLoaderPrivate; | class webFileLoaderPrivate; | ||||||
|  |  | ||||||
|  | @ -283,7 +283,7 @@ void UserData::setBotInfo(const MTPBotInfo &info) { | ||||||
| 	case mtpc_botInfo: { | 	case mtpc_botInfo: { | ||||||
| 		const MTPDbotInfo &d(info.c_botInfo()); | 		const MTPDbotInfo &d(info.c_botInfo()); | ||||||
| 		if (peerFromUser(d.vuser_id.v) != id) return; | 		if (peerFromUser(d.vuser_id.v) != id) return; | ||||||
| 		 | 
 | ||||||
| 		if (botInfo) { | 		if (botInfo) { | ||||||
| 			botInfo->version = d.vversion.v; | 			botInfo->version = d.vversion.v; | ||||||
| 		} else { | 		} else { | ||||||
|  | @ -296,7 +296,7 @@ void UserData::setBotInfo(const MTPBotInfo &info) { | ||||||
| 			botInfo->text = Text(st::msgMinWidth); | 			botInfo->text = Text(st::msgMinWidth); | ||||||
| 		} | 		} | ||||||
| 		botInfo->shareText = qs(d.vshare_text); | 		botInfo->shareText = qs(d.vshare_text); | ||||||
| 		 | 
 | ||||||
| 		const QVector<MTPBotCommand> &v(d.vcommands.c_vector().v); | 		const QVector<MTPBotCommand> &v(d.vcommands.c_vector().v); | ||||||
| 		botInfo->commands.reserve(v.size()); | 		botInfo->commands.reserve(v.size()); | ||||||
| 		bool changedCommands = false; | 		bool changedCommands = false; | ||||||
|  | @ -681,7 +681,7 @@ void PhotoSaveLink::onClick(Qt::MouseButton button) const { | ||||||
| 
 | 
 | ||||||
| void PhotoCancelLink::onClick(Qt::MouseButton button) const { | void PhotoCancelLink::onClick(Qt::MouseButton button) const { | ||||||
| 	if (button != Qt::LeftButton) return; | 	if (button != Qt::LeftButton) return; | ||||||
| 	 | 
 | ||||||
| 	PhotoData *data = photo(); | 	PhotoData *data = photo(); | ||||||
| 	if (!data->date) return; | 	if (!data->date) return; | ||||||
| 
 | 
 | ||||||
|  | @ -2050,6 +2050,11 @@ void InlineResult::automaticLoadGif() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void InlineResult::automaticLoadSettingsChangedGif() { | ||||||
|  | 	if (loaded() || _loader != CancelledWebFileLoader) return; | ||||||
|  | 	_loader = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void InlineResult::saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading) { | void InlineResult::saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading) { | ||||||
| 	if (loaded()) { | 	if (loaded()) { | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
|  | @ -1273,7 +1273,7 @@ inline WebPageType toWebPageType(const QString &type) { | ||||||
| 
 | 
 | ||||||
| struct WebPageData { | struct WebPageData { | ||||||
| 	WebPageData(const WebPageId &id, WebPageType type = WebPageArticle, const QString &url = QString(), const QString &displayUrl = QString(), const QString &siteName = QString(), const QString &title = QString(), const QString &description = QString(), PhotoData *photo = 0, DocumentData *doc = 0, int32 duration = 0, const QString &author = QString(), int32 pendingTill = -1); | 	WebPageData(const WebPageId &id, WebPageType type = WebPageArticle, const QString &url = QString(), const QString &displayUrl = QString(), const QString &siteName = QString(), const QString &title = QString(), const QString &description = QString(), PhotoData *photo = 0, DocumentData *doc = 0, int32 duration = 0, const QString &author = QString(), int32 pendingTill = -1); | ||||||
| 	 | 
 | ||||||
| 	void forget() { | 	void forget() { | ||||||
| 		if (photo) photo->forget(); | 		if (photo) photo->forget(); | ||||||
| 	} | 	} | ||||||
|  | @ -1365,6 +1365,7 @@ public: | ||||||
| 	ImagePtr thumb; | 	ImagePtr thumb; | ||||||
| 
 | 
 | ||||||
| 	void automaticLoadGif(); | 	void automaticLoadGif(); | ||||||
|  | 	void automaticLoadSettingsChangedGif(); | ||||||
| 	void saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading); | 	void saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading); | ||||||
| 	void cancelFile(); | 	void cancelFile(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue