mirror of https://github.com/procxx/kepka.git
				
				
				
			Use plain HistoryItem in channel admin events log.
Instead of using a complex AdminLog::Item use just HistoryItem*.
This commit is contained in:
		
							parent
							
								
									e39b95175b
								
							
						
					
					
						commit
						5c87b42135
					
				|  | @ -63,6 +63,16 @@ public: | ||||||
| 	base::Observable<gsl::not_null<const HistoryItem*>> &repaintLogEntry() { | 	base::Observable<gsl::not_null<const HistoryItem*>> &repaintLogEntry() { | ||||||
| 		return _repaintLogEntry; | 		return _repaintLogEntry; | ||||||
| 	} | 	} | ||||||
|  | 	base::Observable<void> &pendingHistoryResize() { | ||||||
|  | 		return _pendingHistoryResize; | ||||||
|  | 	} | ||||||
|  | 	struct ItemVisibilityQuery { | ||||||
|  | 		gsl::not_null<HistoryItem*> item; | ||||||
|  | 		gsl::not_null<bool*> isVisible; | ||||||
|  | 	}; | ||||||
|  | 	base::Observable<ItemVisibilityQuery> &queryItemVisibility() { | ||||||
|  | 		return _queryItemVisibility; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	void copyFrom(const AuthSessionData &other) { | 	void copyFrom(const AuthSessionData &other) { | ||||||
| 		_variables = other._variables; | 		_variables = other._variables; | ||||||
|  | @ -139,6 +149,8 @@ private: | ||||||
| 	base::Observable<void> _savedGifsUpdated; | 	base::Observable<void> _savedGifsUpdated; | ||||||
| 	base::Observable<gsl::not_null<History*>> _historyCleared; | 	base::Observable<gsl::not_null<History*>> _historyCleared; | ||||||
| 	base::Observable<gsl::not_null<const HistoryItem*>> _repaintLogEntry; | 	base::Observable<gsl::not_null<const HistoryItem*>> _repaintLogEntry; | ||||||
|  | 	base::Observable<void> _pendingHistoryResize; | ||||||
|  | 	base::Observable<ItemVisibilityQuery> _queryItemVisibility; | ||||||
| 	Variables _variables; | 	Variables _variables; | ||||||
| 	TimeMs _lastTimeVideoPlayedAt = 0; | 	TimeMs _lastTimeVideoPlayedAt = 0; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org | ||||||
| #include "mainwindow.h" | #include "mainwindow.h" | ||||||
| #include "mainwidget.h" | #include "mainwidget.h" | ||||||
| #include "messenger.h" | #include "messenger.h" | ||||||
|  | #include "auth_session.h" | ||||||
| #include "boxes/confirm_box.h" | #include "boxes/confirm_box.h" | ||||||
| #include "layerwidget.h" | #include "layerwidget.h" | ||||||
| #include "lang/lang_keys.h" | #include "lang/lang_keys.h" | ||||||
|  | @ -368,9 +369,8 @@ void historyMuteUpdated(History *history) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void handlePendingHistoryUpdate() { | void handlePendingHistoryUpdate() { | ||||||
| 	if (auto main = App::main()) { | 	AuthSession::Current().data().pendingHistoryResize().notify(true); | ||||||
| 		main->notify_handlePendingHistoryUpdate(); | 
 | ||||||
| 	} |  | ||||||
| 	for (auto item : base::take(Global::RefPendingRepaintItems())) { | 	for (auto item : base::take(Global::RefPendingRepaintItems())) { | ||||||
| 		Ui::repaintHistoryItem(item); | 		Ui::repaintHistoryItem(item); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,7 +22,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org | ||||||
| 
 | 
 | ||||||
| #include "styles/style_history.h" | #include "styles/style_history.h" | ||||||
| #include "history/history_media_types.h" | #include "history/history_media_types.h" | ||||||
| #include "history/history_admin_log_item.h" |  | ||||||
| #include "history/history_admin_log_section.h" | #include "history/history_admin_log_section.h" | ||||||
| #include "mainwindow.h" | #include "mainwindow.h" | ||||||
| #include "window/window_controller.h" | #include "window/window_controller.h" | ||||||
|  | @ -46,9 +45,18 @@ InnerWidget::InnerWidget(QWidget *parent, gsl::not_null<Window::Controller*> con | ||||||
| 	setMouseTracking(true); | 	setMouseTracking(true); | ||||||
| 	_scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); }); | 	_scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); }); | ||||||
| 	subscribe(AuthSession::Current().data().repaintLogEntry(), [this](gsl::not_null<const HistoryItem*> historyItem) { | 	subscribe(AuthSession::Current().data().repaintLogEntry(), [this](gsl::not_null<const HistoryItem*> historyItem) { | ||||||
| 		auto it = _itemsByHistoryItems.find(historyItem); | 		if (_history == historyItem->history()) { | ||||||
| 		if (it != _itemsByHistoryItems.cend()) { | 			repaintItem(historyItem); | ||||||
| 			repaintItem(it->second); | 		} | ||||||
|  | 	}); | ||||||
|  | 	subscribe(AuthSession::Current().data().pendingHistoryResize(), [this] { handlePendingHistoryResize(); }); | ||||||
|  | 	subscribe(AuthSession::Current().data().queryItemVisibility(), [this](const AuthSessionData::ItemVisibilityQuery &query) { | ||||||
|  | 		if (_history != query.item->history() || !query.item->isLogEntry() || !isVisible()) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		auto top = itemTop(query.item); | ||||||
|  | 		if (top >= 0 && top + query.item->height() > _visibleTop && top < _visibleBottom) { | ||||||
|  | 			*query.isVisible = true; | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|  | @ -64,11 +72,11 @@ void InnerWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) { | ||||||
| void InnerWidget::updateVisibleTopItem() { | void InnerWidget::updateVisibleTopItem() { | ||||||
| 	auto start = std::rbegin(_items), end = std::rend(_items); | 	auto start = std::rbegin(_items), end = std::rend(_items); | ||||||
| 	auto from = std::upper_bound(start, end, _visibleTop, [](int top, auto &elem) { | 	auto from = std::upper_bound(start, end, _visibleTop, [](int top, auto &elem) { | ||||||
| 		return top <= elem->top() + elem->height(); | 		return top <= elem->y() + elem->height(); | ||||||
| 	}); | 	}); | ||||||
| 	if (from != end) { | 	if (from != end) { | ||||||
| 		_visibleTopItem = from->get(); | 		_visibleTopItem = *from; | ||||||
| 		_visibleTopFromItem = _visibleTop - _visibleTopItem->top(); | 		_visibleTopFromItem = _visibleTop - _visibleTopItem->y(); | ||||||
| 	} else { | 	} else { | ||||||
| 		_visibleTopItem = nullptr; | 		_visibleTopItem = nullptr; | ||||||
| 		_visibleTopFromItem = _visibleTop; | 		_visibleTopFromItem = _visibleTop; | ||||||
|  | @ -144,13 +152,15 @@ void InnerWidget::applyFilter(MTPDchannelAdminLogEventsFilter::Flags flags, cons | ||||||
| 
 | 
 | ||||||
| QString InnerWidget::tooltipText() const { | QString InnerWidget::tooltipText() const { | ||||||
| 	if (_mouseCursorState == HistoryInDateCursorState && _mouseAction == MouseAction::None) { | 	if (_mouseCursorState == HistoryInDateCursorState && _mouseAction == MouseAction::None) { | ||||||
| 		if (_itemOver) { | 		if (auto item = App::hoveredItem()) { | ||||||
| 			auto dateText = _itemOver->date().toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)); | 			auto dateText = item->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)); | ||||||
| 			return dateText; | 			return dateText; | ||||||
| 		} | 		} | ||||||
| 	} else if (_mouseCursorState == HistoryInForwardedCursorState && _mouseAction == MouseAction::None) { | 	} else if (_mouseCursorState == HistoryInForwardedCursorState && _mouseAction == MouseAction::None) { | ||||||
| 		if (_itemOver) { | 		if (auto item = App::hoveredItem()) { | ||||||
| 			auto forwarded = _itemOver->getForwardedInfoText(); | 			if (auto forwarded = item->Get<HistoryMessageForwarded>()) { | ||||||
|  | 				return forwarded->_text.originalText(AllTextSelection, ExpandLinksNone); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} else if (auto lnk = ClickHandler::getActive()) { | 	} else if (auto lnk = ClickHandler::getActive()) { | ||||||
| 		return lnk->tooltip(); | 		return lnk->tooltip(); | ||||||
|  | @ -226,11 +236,26 @@ void InnerWidget::preloadMore(Direction direction) { | ||||||
| 			_items.reserve(_items.size() + events.size()); | 			_items.reserve(_items.size() + events.size()); | ||||||
| 			for_const (auto &event, events) { | 			for_const (auto &event, events) { | ||||||
| 				t_assert(event.type() == mtpc_channelAdminLogEvent); | 				t_assert(event.type() == mtpc_channelAdminLogEvent); | ||||||
| 				_items.push_back(std::make_unique<Item>(_history, _idManager, event.c_channelAdminLogEvent())); | 				auto &data = event.c_channelAdminLogEvent(); | ||||||
|  | 				auto count = 0; | ||||||
|  | 				GenerateItems(_history, _idManager, data, [this, id = data.vid.v, &count](HistoryItemOwned item) { | ||||||
|  | 					_items.push_back(std::move(item)); | ||||||
|  | 					_itemsByIds.emplace(id, item.get()); | ||||||
|  | 					++count; | ||||||
|  | 				}); | ||||||
|  | 				if (count > 1) { | ||||||
|  | 					// Reverse the inner order of the added messages, because we load events
 | ||||||
|  | 					// from bottom to top but inside one event they go from top to bottom.
 | ||||||
|  | 					auto full = _items.size(); | ||||||
|  | 					auto from = full - count; | ||||||
|  | 					for (auto i = 0, toReverse = count / 2; i != toReverse; ++i) { | ||||||
|  | 						std::swap(_items[from + i], _items[full - i - 1]); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			if (!_items.empty()) { | 			if (!_items.empty()) { | ||||||
| 				_maxId = _items.front()->id(); | 				_maxId = (--_itemsByIds.end())->first; | ||||||
| 				_minId = _items.back()->id(); | 				_minId = _itemsByIds.begin()->first; | ||||||
| 				if (_minId == 1) { | 				if (_minId == 1) { | ||||||
| 					_upLoaded = true; | 					_upLoaded = true; | ||||||
| 				} | 				} | ||||||
|  | @ -262,7 +287,7 @@ int InnerWidget::resizeGetHeight(int newWidth) { | ||||||
| 
 | 
 | ||||||
| 	auto newHeight = 0; | 	auto newHeight = 0; | ||||||
| 	for (auto &item : base::reversed(_items)) { | 	for (auto &item : base::reversed(_items)) { | ||||||
| 		item->setTop(newHeight); | 		item->setY(newHeight); | ||||||
| 		newHeight += item->resizeGetHeight(newWidth); | 		newHeight += item->resizeGetHeight(newWidth); | ||||||
| 	} | 	} | ||||||
| 	_itemsHeight = newHeight; | 	_itemsHeight = newHeight; | ||||||
|  | @ -357,7 +382,10 @@ void InnerWidget::enterEventHook(QEvent *e) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void InnerWidget::leaveEventHook(QEvent *e) { | void InnerWidget::leaveEventHook(QEvent *e) { | ||||||
| 	repaintItem(base::take(_itemOver)); | 	if (auto item = App::hoveredItem()) { | ||||||
|  | 		repaintItem(item); | ||||||
|  | 		App::hoveredItem(nullptr); | ||||||
|  | 	} | ||||||
| 	ClickHandler::clearActive(); | 	ClickHandler::clearActive(); | ||||||
| 	Ui::Tooltip::Hide(); | 	Ui::Tooltip::Hide(); | ||||||
| 	if (!ClickHandler::getPressed() && _cursor != style::cur_default) { | 	if (!ClickHandler::getPressed() && _cursor != style::cur_default) { | ||||||
|  | @ -372,14 +400,14 @@ void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butt | ||||||
| 	if (button != Qt::LeftButton) return; | 	if (button != Qt::LeftButton) return; | ||||||
| 
 | 
 | ||||||
| 	ClickHandler::pressed(); | 	ClickHandler::pressed(); | ||||||
| 	if (_itemPressed != _itemOver) { | 	if (App::pressedItem() != App::hoveredItem()) { | ||||||
| 		repaintItem(_itemPressed); | 		repaintItem(App::pressedItem()); | ||||||
| 		_itemPressed = _itemOver; | 		App::pressedItem(App::hoveredItem()); | ||||||
| 		repaintItem(_itemPressed); | 		repaintItem(App::pressedItem()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_mouseAction = MouseAction::None; | 	_mouseAction = MouseAction::None; | ||||||
| 	_mouseActionItem = _itemNearest; | 	_mouseActionItem = App::mousedItem(); | ||||||
| 	_dragStartPosition = mapPointToItem(mapFromGlobal(screenPos), _mouseActionItem); | 	_dragStartPosition = mapPointToItem(mapFromGlobal(screenPos), _mouseActionItem); | ||||||
| 	_pressWasInactive = _controller->window()->wasInactivePress(); | 	_pressWasInactive = _controller->window()->wasInactivePress(); | ||||||
| 	if (_pressWasInactive) _controller->window()->setInactivePress(false); | 	if (_pressWasInactive) _controller->window()->setInactivePress(false); | ||||||
|  | @ -392,7 +420,7 @@ void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butt | ||||||
| 		if (_trippleClickTimer.isActive() && (screenPos - _trippleClickPoint).manhattanLength() < QApplication::startDragDistance()) { | 		if (_trippleClickTimer.isActive() && (screenPos - _trippleClickPoint).manhattanLength() < QApplication::startDragDistance()) { | ||||||
| 			HistoryStateRequest request; | 			HistoryStateRequest request; | ||||||
| 			request.flags = Text::StateRequest::Flag::LookupSymbol; | 			request.flags = Text::StateRequest::Flag::LookupSymbol; | ||||||
| 			dragState = _mouseActionItem->getState(_dragStartPosition, request).data; | 			dragState = _mouseActionItem->getState(_dragStartPosition, request); | ||||||
| 			if (dragState.cursor == HistoryInTextCursorState) { | 			if (dragState.cursor == HistoryInTextCursorState) { | ||||||
| 				auto selection = TextSelection { dragState.symbol, dragState.symbol }; | 				auto selection = TextSelection { dragState.symbol, dragState.symbol }; | ||||||
| 				repaintItem(std::exchange(_selectedItem, _mouseActionItem)); | 				repaintItem(std::exchange(_selectedItem, _mouseActionItem)); | ||||||
|  | @ -403,13 +431,13 @@ void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butt | ||||||
| 				mouseActionUpdate(_mousePosition); | 				mouseActionUpdate(_mousePosition); | ||||||
| 				_trippleClickTimer.callOnce(QApplication::doubleClickInterval()); | 				_trippleClickTimer.callOnce(QApplication::doubleClickInterval()); | ||||||
| 			} | 			} | ||||||
| 		} else if (_itemPressed) { | 		} else if (App::pressedItem()) { | ||||||
| 			HistoryStateRequest request; | 			HistoryStateRequest request; | ||||||
| 			request.flags = Text::StateRequest::Flag::LookupSymbol; | 			request.flags = Text::StateRequest::Flag::LookupSymbol; | ||||||
| 			dragState = _mouseActionItem->getState(_dragStartPosition, request).data; | 			dragState = _mouseActionItem->getState(_dragStartPosition, request); | ||||||
| 		} | 		} | ||||||
| 		if (_mouseSelectType != TextSelectType::Paragraphs) { | 		if (_mouseSelectType != TextSelectType::Paragraphs) { | ||||||
| 			if (_itemPressed) { | 			if (App::pressedItem()) { | ||||||
| 				_mouseTextSymbol = dragState.symbol; | 				_mouseTextSymbol = dragState.symbol; | ||||||
| 				auto uponSelected = (dragState.cursor == HistoryInTextCursorState); | 				auto uponSelected = (dragState.cursor == HistoryInTextCursorState); | ||||||
| 				if (uponSelected) { | 				if (uponSelected) { | ||||||
|  | @ -422,16 +450,16 @@ void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butt | ||||||
| 				if (uponSelected) { | 				if (uponSelected) { | ||||||
| 					_mouseAction = MouseAction::PrepareDrag; // start text drag
 | 					_mouseAction = MouseAction::PrepareDrag; // start text drag
 | ||||||
| 				} else if (!_pressWasInactive) { | 				} else if (!_pressWasInactive) { | ||||||
| 					//if (dynamic_cast<HistorySticker*>(_itemPressed->getMedia())) {
 | 					if (dynamic_cast<HistorySticker*>(App::pressedItem()->getMedia())) { | ||||||
| 					//	_mouseAction = MouseAction::PrepareDrag; // start sticker drag or by-date drag
 | 						_mouseAction = MouseAction::PrepareDrag; // start sticker drag or by-date drag
 | ||||||
| 					//} else { // TODO
 | 					} else { | ||||||
| 						if (dragState.afterSymbol) ++_mouseTextSymbol; | 						if (dragState.afterSymbol) ++_mouseTextSymbol; | ||||||
| 						auto selection = TextSelection { _mouseTextSymbol, _mouseTextSymbol }; | 						auto selection = TextSelection { _mouseTextSymbol, _mouseTextSymbol }; | ||||||
| 						repaintItem(std::exchange(_selectedItem, _mouseActionItem)); | 						repaintItem(std::exchange(_selectedItem, _mouseActionItem)); | ||||||
| 						_selectedText = selection; | 						_selectedText = selection; | ||||||
| 						_mouseAction = MouseAction::Selecting; | 						_mouseAction = MouseAction::Selecting; | ||||||
| 						repaintItem(_mouseActionItem); | 						repaintItem(_mouseActionItem); | ||||||
| 					//} // TODO
 | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -464,7 +492,10 @@ void InnerWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton but | ||||||
| 	if (_mouseAction == MouseAction::Dragging) { | 	if (_mouseAction == MouseAction::Dragging) { | ||||||
| 		activated.clear(); | 		activated.clear(); | ||||||
| 	} | 	} | ||||||
| 	repaintItem(base::take(_itemPressed)); | 	if (App::pressedItem()) { | ||||||
|  | 		repaintItem(App::pressedItem()); | ||||||
|  | 		App::pressedItem(nullptr); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	_wasSelectedText = false; | 	_wasSelectedText = false; | ||||||
| 
 | 
 | ||||||
|  | @ -504,26 +535,27 @@ void InnerWidget::updateSelected() { | ||||||
| 	auto from = (point.y() >= _itemsTop && point.y() < _itemsTop + _itemsHeight) ? std::upper_bound(start, end, point.y(), [this](int top, auto &elem) { | 	auto from = (point.y() >= _itemsTop && point.y() < _itemsTop + _itemsHeight) ? std::upper_bound(start, end, point.y(), [this](int top, auto &elem) { | ||||||
| 		return top <= itemTop(elem.get()) + elem->height(); | 		return top <= itemTop(elem.get()) + elem->height(); | ||||||
| 	}) : end; | 	}) : end; | ||||||
| 	if (from != end) { | 	auto item = (from != end) ? from->get() : nullptr; | ||||||
| 		_itemNearest = from->get(); | 	if (item) { | ||||||
| 		itemPoint = mapPointToItem(point, _itemNearest); | 		App::mousedItem(item); | ||||||
| 		if (_itemNearest->hasPoint(itemPoint)) { | 		itemPoint = mapPointToItem(point, item); | ||||||
| 			if (_itemOver != _itemNearest) { | 		if (item->hasPoint(itemPoint)) { | ||||||
| 				repaintItem(std::exchange(_itemOver, _itemNearest)); | 			if (App::hoveredItem() != item) { | ||||||
| 				repaintItem(_itemNearest); | 				repaintItem(App::hoveredItem()); | ||||||
|  | 				App::hoveredItem(item); | ||||||
|  | 				repaintItem(App::hoveredItem()); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else if (App::hoveredItem()) { | ||||||
| 			repaintItem(base::take(_itemOver)); | 			repaintItem(App::hoveredItem()); | ||||||
|  | 			App::hoveredItem(nullptr); | ||||||
| 		} | 		} | ||||||
| 	} else { |  | ||||||
| 		_itemNearest = nullptr; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	Item::TextState dragState; | 	HistoryTextState dragState; | ||||||
| 	ClickHandlerHost *lnkhost = nullptr; | 	ClickHandlerHost *lnkhost = nullptr; | ||||||
| 	auto selectingText = (_itemNearest == _mouseActionItem && _itemNearest == _itemOver && _selectedItem); | 	auto selectingText = (item == _mouseActionItem && item == App::hoveredItem() && _selectedItem); | ||||||
| 	if (_itemNearest) { | 	if (item) { | ||||||
| 		if (_itemNearest != _mouseActionItem || (itemPoint - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { | 		if (item != _mouseActionItem || (itemPoint - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { | ||||||
| 			if (_mouseAction == MouseAction::PrepareDrag) { | 			if (_mouseAction == MouseAction::PrepareDrag) { | ||||||
| 				_mouseAction = MouseAction::Dragging; | 				_mouseAction = MouseAction::Dragging; | ||||||
| 				InvokeQueued(this, [this] { performDrag(); }); | 				InvokeQueued(this, [this] { performDrag(); }); | ||||||
|  | @ -579,60 +611,60 @@ void InnerWidget::updateSelected() { | ||||||
| 		//	}
 | 		//	}
 | ||||||
| 		//	return true;
 | 		//	return true;
 | ||||||
| 		//}); // TODO
 | 		//}); // TODO
 | ||||||
| 		if (!dragState.data.link) { | 		if (!dragState.link) { | ||||||
| 			HistoryStateRequest request; | 			HistoryStateRequest request; | ||||||
| 			if (_mouseAction == MouseAction::Selecting) { | 			if (_mouseAction == MouseAction::Selecting) { | ||||||
| 				request.flags |= Text::StateRequest::Flag::LookupSymbol; | 				request.flags |= Text::StateRequest::Flag::LookupSymbol; | ||||||
| 			} else { | 			} else { | ||||||
| 				selectingText = false; | 				selectingText = false; | ||||||
| 			} | 			} | ||||||
| 			dragState = _itemNearest->getState(itemPoint, request); | 			dragState = item->getState(itemPoint, request); | ||||||
| 			lnkhost = dragState.handler; | 			lnkhost = item; | ||||||
| 			if (!dragState.data.link && itemPoint.x() >= st::historyPhotoLeft && itemPoint.x() < st::historyPhotoLeft + st::msgPhotoSize) { | 			if (!dragState.link && itemPoint.x() >= st::historyPhotoLeft && itemPoint.x() < st::historyPhotoLeft + st::msgPhotoSize) { | ||||||
| 				//if (auto msg = item->toHistoryMessage()) {
 | 				if (auto msg = item->toHistoryMessage()) { | ||||||
| 				//	if (msg->hasFromPhoto()) {
 | 					if (msg->hasFromPhoto()) { | ||||||
| 				//		enumerateUserpics([&dragState, &lnkhost, &point](HistoryMessage *message, int userpicTop) -> bool {
 | 						//enumerateUserpics([&dragState, &lnkhost, &point](HistoryMessage *message, int userpicTop) -> bool {
 | ||||||
| 				//			// stop enumeration if the userpic is below our point
 | 						//	// stop enumeration if the userpic is below our point
 | ||||||
| 				//			if (userpicTop > point.y()) {
 | 						//	if (userpicTop > point.y()) {
 | ||||||
| 				//				return false;
 | 						//		return false;
 | ||||||
| 				//			}
 | 						//	}
 | ||||||
| 
 | 
 | ||||||
| 				//			// stop enumeration if we've found a userpic under the cursor
 | 						//	// stop enumeration if we've found a userpic under the cursor
 | ||||||
| 				//			if (point.y() >= userpicTop && point.y() < userpicTop + st::msgPhotoSize) {
 | 						//	if (point.y() >= userpicTop && point.y() < userpicTop + st::msgPhotoSize) {
 | ||||||
| 				//				dragState.link = message->from()->openLink();
 | 						//		dragState.link = message->from()->openLink();
 | ||||||
| 				//				lnkhost = message;
 | 						//		lnkhost = message;
 | ||||||
| 				//				return false;
 | 						//		return false;
 | ||||||
| 				//			}
 | 						//	}
 | ||||||
| 				//			return true;
 | 						//	return true;
 | ||||||
| 				//		});
 | 						//}); // TODO
 | ||||||
| 				//	}
 | 					} | ||||||
| 				//} // TODO
 | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	auto lnkChanged = ClickHandler::setActive(dragState.data.link, lnkhost); | 	auto lnkChanged = ClickHandler::setActive(dragState.link, lnkhost); | ||||||
| 	if (lnkChanged || dragState.data.cursor != _mouseCursorState) { | 	if (lnkChanged || dragState.cursor != _mouseCursorState) { | ||||||
| 		Ui::Tooltip::Hide(); | 		Ui::Tooltip::Hide(); | ||||||
| 	} | 	} | ||||||
| 	if (dragState.data.link || dragState.data.cursor == HistoryInDateCursorState || dragState.data.cursor == HistoryInForwardedCursorState) { | 	if (dragState.link || dragState.cursor == HistoryInDateCursorState || dragState.cursor == HistoryInForwardedCursorState) { | ||||||
| 		Ui::Tooltip::Show(1000, this); | 		Ui::Tooltip::Show(1000, this); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	auto cursor = style::cur_default; | 	auto cursor = style::cur_default; | ||||||
| 	if (_mouseAction == MouseAction::None) { | 	if (_mouseAction == MouseAction::None) { | ||||||
| 		_mouseCursorState = dragState.data.cursor; | 		_mouseCursorState = dragState.cursor; | ||||||
| 		if (dragState.data.link) { | 		if (dragState.link) { | ||||||
| 			cursor = style::cur_pointer; | 			cursor = style::cur_pointer; | ||||||
| 		} else if (_mouseCursorState == HistoryInTextCursorState) { | 		} else if (_mouseCursorState == HistoryInTextCursorState) { | ||||||
| 			cursor = style::cur_text; | 			cursor = style::cur_text; | ||||||
| 		} else if (_mouseCursorState == HistoryInDateCursorState) { | 		} else if (_mouseCursorState == HistoryInDateCursorState) { | ||||||
| //			cursor = style::cur_cross;
 | //			cursor = style::cur_cross;
 | ||||||
| 		} | 		} | ||||||
| 	} else if (_itemNearest) { | 	} else if (item) { | ||||||
| 		if (_mouseAction == MouseAction::Selecting) { | 		if (_mouseAction == MouseAction::Selecting) { | ||||||
| 			if (selectingText) { | 			if (selectingText) { | ||||||
| 				auto second = dragState.data.symbol; | 				auto second = dragState.symbol; | ||||||
| 				if (dragState.data.afterSymbol && _mouseSelectType == TextSelectType::Letters) { | 				if (dragState.afterSymbol && _mouseSelectType == TextSelectType::Letters) { | ||||||
| 					++second; | 					++second; | ||||||
| 				} | 				} | ||||||
| 				auto selection = _mouseActionItem->adjustSelection({ qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }, _mouseSelectType); | 				auto selection = _mouseActionItem->adjustSelection({ qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }, _mouseSelectType); | ||||||
|  | @ -656,9 +688,13 @@ void InnerWidget::updateSelected() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Voice message seek support.
 | 	// Voice message seek support.
 | ||||||
| 	if (_itemPressed) { | 	if (auto pressedItem = App::pressedLinkItem()) { | ||||||
| 		auto adjustedPoint = mapPointToItem(point, _itemPressed); | 		if (!pressedItem->detached()) { | ||||||
| 		_itemPressed->updatePressed(adjustedPoint); | 			if (pressedItem->history() == _history) { | ||||||
|  | 				auto adjustedPoint = mapPointToItem(point, pressedItem); | ||||||
|  | 				pressedItem->updatePressed(adjustedPoint); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//if (_mouseAction == MouseAction::Selecting) {
 | 	//if (_mouseAction == MouseAction::Selecting) {
 | ||||||
|  | @ -763,24 +799,31 @@ void InnerWidget::performDrag() { | ||||||
| 	//} // TODO
 | 	//} // TODO
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int InnerWidget::itemTop(gsl::not_null<Item*> item) const { | int InnerWidget::itemTop(gsl::not_null<const HistoryItem*> item) const { | ||||||
| 	return _itemsTop + item->top(); | 	return _itemsTop + item->y(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void InnerWidget::repaintItem(Item *item) { | void InnerWidget::repaintItem(const HistoryItem *item) { | ||||||
| 	if (!item) { | 	if (!item) { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	update(0, itemTop(item), width(), item->height()); | 	update(0, itemTop(item), width(), item->height()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QPoint InnerWidget::mapPointToItem(QPoint point, Item *item) const { | QPoint InnerWidget::mapPointToItem(QPoint point, const HistoryItem *item) const { | ||||||
| 	if (!item) { | 	if (!item) { | ||||||
| 		return QPoint(); | 		return QPoint(); | ||||||
| 	} | 	} | ||||||
| 	return point - QPoint(0, itemTop(item)); | 	return point - QPoint(0, itemTop(item)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void InnerWidget::handlePendingHistoryResize() { | ||||||
|  | 	if (_history->hasPendingResizedItems()) { | ||||||
|  | 		_history->resizeGetHeight(width()); | ||||||
|  | 		updateSize(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| InnerWidget::~InnerWidget() = default; | InnerWidget::~InnerWidget() = default; | ||||||
| 
 | 
 | ||||||
| } // namespace AdminLog
 | } // namespace AdminLog
 | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org | ||||||
| */ | */ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include "history/history_admin_log_item.h" | ||||||
| #include "ui/widgets/tooltip.h" | #include "ui/widgets/tooltip.h" | ||||||
| #include "mtproto/sender.h" | #include "mtproto/sender.h" | ||||||
| #include "base/timer.h" | #include "base/timer.h" | ||||||
|  | @ -35,7 +36,6 @@ class Controller; | ||||||
| namespace AdminLog { | namespace AdminLog { | ||||||
| 
 | 
 | ||||||
| class SectionMemento; | class SectionMemento; | ||||||
| class Item; |  | ||||||
| 
 | 
 | ||||||
| class LocalIdManager { | class LocalIdManager { | ||||||
| public: | public: | ||||||
|  | @ -118,9 +118,10 @@ private: | ||||||
| 	void mouseActionCancel(); | 	void mouseActionCancel(); | ||||||
| 	void updateSelected(); | 	void updateSelected(); | ||||||
| 	void performDrag(); | 	void performDrag(); | ||||||
| 	int itemTop(gsl::not_null<Item*> item) const; | 	int itemTop(gsl::not_null<const HistoryItem*> item) const; | ||||||
| 	void repaintItem(Item *item); | 	void repaintItem(const HistoryItem *item); | ||||||
| 	QPoint mapPointToItem(QPoint point, Item *item) const; | 	QPoint mapPointToItem(QPoint point, const HistoryItem *item) const; | ||||||
|  | 	void handlePendingHistoryResize(); | ||||||
| 
 | 
 | ||||||
| 	void checkPreloadMore(); | 	void checkPreloadMore(); | ||||||
| 	void updateVisibleTopItem(); | 	void updateVisibleTopItem(); | ||||||
|  | @ -142,8 +143,8 @@ private: | ||||||
| 	gsl::not_null<History*> _history; | 	gsl::not_null<History*> _history; | ||||||
| 	base::lambda<void()> _cancelledCallback; | 	base::lambda<void()> _cancelledCallback; | ||||||
| 	base::lambda<void(int top)> _scrollTo; | 	base::lambda<void(int top)> _scrollTo; | ||||||
| 	std::vector<std::unique_ptr<Item>> _items; | 	std::vector<HistoryItemOwned> _items; | ||||||
| 	std::map<gsl::not_null<HistoryItem*>, gsl::not_null<Item*>, std::less<>> _itemsByHistoryItems; | 	std::map<uint64, HistoryItem*> _itemsByIds; | ||||||
| 	int _itemsTop = 0; | 	int _itemsTop = 0; | ||||||
| 	int _itemsHeight = 0; | 	int _itemsHeight = 0; | ||||||
| 
 | 
 | ||||||
|  | @ -151,14 +152,14 @@ private: | ||||||
| 	int _minHeight = 0; | 	int _minHeight = 0; | ||||||
| 	int _visibleTop = 0; | 	int _visibleTop = 0; | ||||||
| 	int _visibleBottom = 0; | 	int _visibleBottom = 0; | ||||||
| 	Item *_visibleTopItem = nullptr; | 	HistoryItem *_visibleTopItem = nullptr; | ||||||
| 	int _visibleTopFromItem = 0; | 	int _visibleTopFromItem = 0; | ||||||
| 
 | 
 | ||||||
| 	bool _scrollDateShown = false; | 	bool _scrollDateShown = false; | ||||||
| 	Animation _scrollDateOpacity; | 	Animation _scrollDateOpacity; | ||||||
| 	SingleQueuedInvokation _scrollDateCheck; | 	SingleQueuedInvokation _scrollDateCheck; | ||||||
| 	base::Timer _scrollDateHideTimer; | 	base::Timer _scrollDateHideTimer; | ||||||
| 	Item *_scrollDateLastItem = nullptr; | 	HistoryItem *_scrollDateLastItem = nullptr; | ||||||
| 	int _scrollDateLastItemTop = 0; | 	int _scrollDateLastItemTop = 0; | ||||||
| 	ClickHandlerPtr _scrollDateLink; | 	ClickHandlerPtr _scrollDateLink; | ||||||
| 
 | 
 | ||||||
|  | @ -174,15 +175,12 @@ private: | ||||||
| 	TextSelectType _mouseSelectType = TextSelectType::Letters; | 	TextSelectType _mouseSelectType = TextSelectType::Letters; | ||||||
| 	QPoint _dragStartPosition; | 	QPoint _dragStartPosition; | ||||||
| 	QPoint _mousePosition; | 	QPoint _mousePosition; | ||||||
| 	Item *_mouseActionItem = nullptr; | 	HistoryItem *_mouseActionItem = nullptr; | ||||||
| 	HistoryCursorState _mouseCursorState = HistoryDefaultCursorState; | 	HistoryCursorState _mouseCursorState = HistoryDefaultCursorState; | ||||||
| 	uint16 _mouseTextSymbol = 0; | 	uint16 _mouseTextSymbol = 0; | ||||||
| 	bool _pressWasInactive = false; | 	bool _pressWasInactive = false; | ||||||
| 
 | 
 | ||||||
| 	Item *_itemNearest = nullptr; | 	HistoryItem *_selectedItem = nullptr; | ||||||
| 	Item *_itemOver = nullptr; |  | ||||||
| 	Item *_itemPressed = nullptr; |  | ||||||
| 	Item *_selectedItem = nullptr; |  | ||||||
| 	TextSelection _selectedText; | 	TextSelection _selectedText; | ||||||
| 	bool _wasSelectedText = false; // was some text selected in current drag action
 | 	bool _wasSelectedText = false; // was some text selected in current drag action
 | ||||||
| 	Qt::CursorShape _cursor = style::cur_default; | 	Qt::CursorShape _cursor = style::cur_default; | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org | ||||||
| */ | */ | ||||||
| #include "history/history_admin_log_item.h" | #include "history/history_admin_log_item.h" | ||||||
| 
 | 
 | ||||||
|  | #include "history/history_admin_log_inner.h" | ||||||
| #include "lang/lang_keys.h" | #include "lang/lang_keys.h" | ||||||
| #include "messenger.h" | #include "messenger.h" | ||||||
| 
 | 
 | ||||||
|  | @ -233,35 +234,39 @@ TextWithEntities GenerateParticipantChangeText(gsl::not_null<ChannelData*> chann | ||||||
| 
 | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
| 
 | 
 | ||||||
| Item::Item(gsl::not_null<History*> history, LocalIdManager &idManager, const MTPDchannelAdminLogEvent &event) | void GenerateItems(gsl::not_null<History*> history, LocalIdManager &idManager, const MTPDchannelAdminLogEvent &event, base::lambda<void(HistoryItemOwned item)> callback) { | ||||||
| : _id(event.vid.v) | 	Expects(history->peer->isChannel()); | ||||||
| , _date(::date(event.vdate)) | 
 | ||||||
| , _history(history) | 	auto id = event.vid.v; | ||||||
| , _from(App::user(event.vuser_id.v)) { | 	auto from = App::user(event.vuser_id.v); | ||||||
|  | 	auto channel = history->peer->asChannel(); | ||||||
| 	auto &action = event.vaction; | 	auto &action = event.vaction; | ||||||
| 	auto date = event.vdate; | 	auto date = event.vdate; | ||||||
|  | 	auto addPart = [&callback](gsl::not_null<HistoryItem*> item) { | ||||||
|  | 		return callback(HistoryItemOwned(item)); | ||||||
|  | 	}; | ||||||
| 
 | 
 | ||||||
| 	using ServiceFlag = MTPDmessageService::Flag; | 	using ServiceFlag = MTPDmessageService::Flag; | ||||||
| 	using Flag = MTPDmessage::Flag; | 	using Flag = MTPDmessage::Flag; | ||||||
| 	auto fromName = App::peerName(_from); | 	auto fromName = App::peerName(from); | ||||||
| 	auto fromLink = _from->openLink(); | 	auto fromLink = peerOpenClickHandler(from); | ||||||
| 	auto fromLinkText = textcmdLink(1, fromName); | 	auto fromLinkText = textcmdLink(1, fromName); | ||||||
| 
 | 
 | ||||||
| 	auto addSimpleServiceMessage = [this, &idManager, date, fromLink](const QString &text, PhotoData *photo = nullptr) { | 	auto addSimpleServiceMessage = [&](const QString &text, PhotoData *photo = nullptr) { | ||||||
| 		auto message = HistoryService::PreparedText { text }; | 		auto message = HistoryService::PreparedText { text }; | ||||||
| 		message.links.push_back(fromLink); | 		message.links.push_back(fromLink); | ||||||
| 		addPart(HistoryService::create(_history, idManager.next(), ::date(date), message, 0, peerToUser(_from->id), photo)); | 		addPart(HistoryService::create(history, idManager.next(), ::date(date), message, 0, peerToUser(from->id), photo)); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createChangeTitle = [this, addSimpleServiceMessage, fromLinkText](const MTPDchannelAdminLogEventActionChangeTitle &action) { | 	auto createChangeTitle = [&](const MTPDchannelAdminLogEventActionChangeTitle &action) { | ||||||
| 		auto text = (channel()->isMegagroup() ? lng_action_changed_title : lng_admin_log_changed_title_channel)(lt_from, fromLinkText, lt_title, qs(action.vnew_value)); | 		auto text = (channel->isMegagroup() ? lng_action_changed_title : lng_admin_log_changed_title_channel)(lt_from, fromLinkText, lt_title, qs(action.vnew_value)); | ||||||
| 		addSimpleServiceMessage(text); | 		addSimpleServiceMessage(text); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createChangeAbout = [this, &idManager, date, addSimpleServiceMessage, fromLinkText](const MTPDchannelAdminLogEventActionChangeAbout &action) { | 	auto createChangeAbout = [&](const MTPDchannelAdminLogEventActionChangeAbout &action) { | ||||||
| 		auto newValue = qs(action.vnew_value); | 		auto newValue = qs(action.vnew_value); | ||||||
| 		auto oldValue = qs(action.vprev_value); | 		auto oldValue = qs(action.vprev_value); | ||||||
| 		auto text = (channel()->isMegagroup() | 		auto text = (channel->isMegagroup() | ||||||
| 			? (newValue.isEmpty() ? lng_admin_log_removed_description_group : lng_admin_log_changed_description_group) | 			? (newValue.isEmpty() ? lng_admin_log_removed_description_group : lng_admin_log_changed_description_group) | ||||||
| 			: (newValue.isEmpty() ? lng_admin_log_removed_description_channel : lng_admin_log_changed_description_channel) | 			: (newValue.isEmpty() ? lng_admin_log_removed_description_channel : lng_admin_log_changed_description_channel) | ||||||
| 			)(lt_from, fromLinkText); | 			)(lt_from, fromLinkText); | ||||||
|  | @ -271,18 +276,18 @@ Item::Item(gsl::not_null<History*> history, LocalIdManager &idManager, const MTP | ||||||
| 		auto bodyReplyTo = 0; | 		auto bodyReplyTo = 0; | ||||||
| 		auto bodyViaBotId = 0; | 		auto bodyViaBotId = 0; | ||||||
| 		auto newDescription = PrepareText(newValue, QString()); | 		auto newDescription = PrepareText(newValue, QString()); | ||||||
| 		auto body = HistoryMessage::create(_history, idManager.next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(_from->id), newDescription); | 		auto body = HistoryMessage::create(history, idManager.next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(from->id), newDescription); | ||||||
| 		if (!oldValue.isEmpty()) { | 		if (!oldValue.isEmpty()) { | ||||||
| 			auto oldDescription = PrepareText(oldValue, QString()); | 			auto oldDescription = PrepareText(oldValue, QString()); | ||||||
| 			body->addLogEntryOriginal(_id, lang(lng_admin_log_previous_description), oldDescription); | 			body->addLogEntryOriginal(id, lang(lng_admin_log_previous_description), oldDescription); | ||||||
| 		} | 		} | ||||||
| 		addPart(body); | 		addPart(body); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createChangeUsername = [this, &idManager, date, addSimpleServiceMessage, fromLinkText](const MTPDchannelAdminLogEventActionChangeUsername &action) { | 	auto createChangeUsername = [&](const MTPDchannelAdminLogEventActionChangeUsername &action) { | ||||||
| 		auto newValue = qs(action.vnew_value); | 		auto newValue = qs(action.vnew_value); | ||||||
| 		auto oldValue = qs(action.vprev_value); | 		auto oldValue = qs(action.vprev_value); | ||||||
| 		auto text = (channel()->isMegagroup() | 		auto text = (channel->isMegagroup() | ||||||
| 			? (newValue.isEmpty() ? lng_admin_log_removed_link_group : lng_admin_log_changed_link_group) | 			? (newValue.isEmpty() ? lng_admin_log_removed_link_group : lng_admin_log_changed_link_group) | ||||||
| 			: (newValue.isEmpty() ? lng_admin_log_removed_link_channel : lng_admin_log_changed_link_channel) | 			: (newValue.isEmpty() ? lng_admin_log_removed_link_channel : lng_admin_log_changed_link_channel) | ||||||
| 			)(lt_from, fromLinkText); | 			)(lt_from, fromLinkText); | ||||||
|  | @ -292,44 +297,44 @@ Item::Item(gsl::not_null<History*> history, LocalIdManager &idManager, const MTP | ||||||
| 		auto bodyReplyTo = 0; | 		auto bodyReplyTo = 0; | ||||||
| 		auto bodyViaBotId = 0; | 		auto bodyViaBotId = 0; | ||||||
| 		auto newLink = newValue.isEmpty() ? TextWithEntities() : PrepareText(Messenger::Instance().createInternalLinkFull(newValue), QString()); | 		auto newLink = newValue.isEmpty() ? TextWithEntities() : PrepareText(Messenger::Instance().createInternalLinkFull(newValue), QString()); | ||||||
| 		auto body = HistoryMessage::create(_history, idManager.next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(_from->id), newLink); | 		auto body = HistoryMessage::create(history, idManager.next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(from->id), newLink); | ||||||
| 		if (!oldValue.isEmpty()) { | 		if (!oldValue.isEmpty()) { | ||||||
| 			auto oldLink = PrepareText(Messenger::Instance().createInternalLinkFull(oldValue), QString()); | 			auto oldLink = PrepareText(Messenger::Instance().createInternalLinkFull(oldValue), QString()); | ||||||
| 			body->addLogEntryOriginal(_id, lang(lng_admin_log_previous_link), oldLink); | 			body->addLogEntryOriginal(id, lang(lng_admin_log_previous_link), oldLink); | ||||||
| 		} | 		} | ||||||
| 		addPart(body); | 		addPart(body); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createChangePhoto = [this, date, addSimpleServiceMessage, fromLinkText](const MTPDchannelAdminLogEventActionChangePhoto &action) { | 	auto createChangePhoto = [&](const MTPDchannelAdminLogEventActionChangePhoto &action) { | ||||||
| 		t_assert(action.vnew_photo.type() == mtpc_chatPhoto); | 		t_assert(action.vnew_photo.type() == mtpc_chatPhoto); | ||||||
| 		auto photo = GenerateChatPhoto(channel()->bareId(), _id, date, action.vnew_photo.c_chatPhoto()); | 		auto photo = GenerateChatPhoto(channel->bareId(), id, date, action.vnew_photo.c_chatPhoto()); | ||||||
| 
 | 
 | ||||||
| 		auto text = (channel()->isMegagroup() ? lng_admin_log_changed_photo_group : lng_admin_log_changed_photo_channel)(lt_from, fromLinkText); | 		auto text = (channel->isMegagroup() ? lng_admin_log_changed_photo_group : lng_admin_log_changed_photo_channel)(lt_from, fromLinkText); | ||||||
| 		addSimpleServiceMessage(text, photo); | 		addSimpleServiceMessage(text, photo); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createToggleInvites = [this, addSimpleServiceMessage, fromLinkText](const MTPDchannelAdminLogEventActionToggleInvites &action) { | 	auto createToggleInvites = [&](const MTPDchannelAdminLogEventActionToggleInvites &action) { | ||||||
| 		auto enabled = (action.vnew_value.type() == mtpc_boolTrue); | 		auto enabled = (action.vnew_value.type() == mtpc_boolTrue); | ||||||
| 		auto text = (enabled ? lng_admin_log_invites_enabled : lng_admin_log_invites_disabled)(lt_from, fromLinkText); | 		auto text = (enabled ? lng_admin_log_invites_enabled : lng_admin_log_invites_disabled)(lt_from, fromLinkText); | ||||||
| 		addSimpleServiceMessage(text); | 		addSimpleServiceMessage(text); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createToggleSignatures = [this, addSimpleServiceMessage, fromLinkText](const MTPDchannelAdminLogEventActionToggleSignatures &action) { | 	auto createToggleSignatures = [&](const MTPDchannelAdminLogEventActionToggleSignatures &action) { | ||||||
| 		auto enabled = (action.vnew_value.type() == mtpc_boolTrue); | 		auto enabled = (action.vnew_value.type() == mtpc_boolTrue); | ||||||
| 		auto text = (enabled ? lng_admin_log_signatures_enabled : lng_admin_log_signatures_disabled)(lt_from, fromLinkText); | 		auto text = (enabled ? lng_admin_log_signatures_enabled : lng_admin_log_signatures_disabled)(lt_from, fromLinkText); | ||||||
| 		addSimpleServiceMessage(text); | 		addSimpleServiceMessage(text); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createUpdatePinned = [this, &idManager, date, addSimpleServiceMessage, fromLinkText](const MTPDchannelAdminLogEventActionUpdatePinned &action) { | 	auto createUpdatePinned = [&](const MTPDchannelAdminLogEventActionUpdatePinned &action) { | ||||||
| 		auto text = lng_admin_log_pinned_message(lt_from, fromLinkText); | 		auto text = lng_admin_log_pinned_message(lt_from, fromLinkText); | ||||||
| 		addSimpleServiceMessage(text); | 		addSimpleServiceMessage(text); | ||||||
| 
 | 
 | ||||||
| 		auto applyServiceAction = false; | 		auto applyServiceAction = false; | ||||||
| 		auto detachExistingItem = false; | 		auto detachExistingItem = false; | ||||||
| 		addPart(_history->createItem(PrepareLogMessage(action.vmessage, idManager.next(), date.v), applyServiceAction, detachExistingItem)); | 		addPart(history->createItem(PrepareLogMessage(action.vmessage, idManager.next(), date.v), applyServiceAction, detachExistingItem)); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createEditMessage = [this, &idManager, date, addSimpleServiceMessage, fromLinkText](const MTPDchannelAdminLogEventActionEditMessage &action) { | 	auto createEditMessage = [&](const MTPDchannelAdminLogEventActionEditMessage &action) { | ||||||
| 		auto newValue = ExtractEditedText(action.vnew_message); | 		auto newValue = ExtractEditedText(action.vnew_message); | ||||||
| 		auto canHaveCaption = MediaCanHaveCaption(action.vnew_message); | 		auto canHaveCaption = MediaCanHaveCaption(action.vnew_message); | ||||||
| 		auto text = (canHaveCaption | 		auto text = (canHaveCaption | ||||||
|  | @ -341,54 +346,54 @@ Item::Item(gsl::not_null<History*> history, LocalIdManager &idManager, const MTP | ||||||
| 		auto oldValue = ExtractEditedText(action.vprev_message); | 		auto oldValue = ExtractEditedText(action.vprev_message); | ||||||
| 		auto applyServiceAction = false; | 		auto applyServiceAction = false; | ||||||
| 		auto detachExistingItem = false; | 		auto detachExistingItem = false; | ||||||
| 		auto body = _history->createItem(PrepareLogMessage(action.vnew_message, idManager.next(), date.v), applyServiceAction, detachExistingItem); | 		auto body = history->createItem(PrepareLogMessage(action.vnew_message, idManager.next(), date.v), applyServiceAction, detachExistingItem); | ||||||
| 		if (!oldValue.text.isEmpty()) { | 		if (!oldValue.text.isEmpty()) { | ||||||
| 			body->addLogEntryOriginal(_id, lang(canHaveCaption ? lng_admin_log_previous_caption : lng_admin_log_previous_message), oldValue); | 			body->addLogEntryOriginal(id, lang(canHaveCaption ? lng_admin_log_previous_caption : lng_admin_log_previous_message), oldValue); | ||||||
| 		} | 		} | ||||||
| 		addPart(body); | 		addPart(body); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createDeleteMessage = [this, &idManager, date, addSimpleServiceMessage, fromLinkText](const MTPDchannelAdminLogEventActionDeleteMessage &action) { | 	auto createDeleteMessage = [&](const MTPDchannelAdminLogEventActionDeleteMessage &action) { | ||||||
| 		auto text = lng_admin_log_deleted_message(lt_from, fromLinkText); | 		auto text = lng_admin_log_deleted_message(lt_from, fromLinkText); | ||||||
| 		addSimpleServiceMessage(text); | 		addSimpleServiceMessage(text); | ||||||
| 
 | 
 | ||||||
| 		auto applyServiceAction = false; | 		auto applyServiceAction = false; | ||||||
| 		auto detachExistingItem = false; | 		auto detachExistingItem = false; | ||||||
| 		addPart(_history->createItem(PrepareLogMessage(action.vmessage, idManager.next(), date.v), applyServiceAction, detachExistingItem)); | 		addPart(history->createItem(PrepareLogMessage(action.vmessage, idManager.next(), date.v), applyServiceAction, detachExistingItem)); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createParticipantJoin = [this, &idManager, addSimpleServiceMessage, fromLinkText]() { | 	auto createParticipantJoin = [&]() { | ||||||
| 		auto text = lng_admin_log_participant_joined(lt_from, fromLinkText); | 		auto text = lng_admin_log_participant_joined(lt_from, fromLinkText); | ||||||
| 		addSimpleServiceMessage(text); | 		addSimpleServiceMessage(text); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createParticipantLeave = [this, &idManager, addSimpleServiceMessage, fromLinkText]() { | 	auto createParticipantLeave = [&]() { | ||||||
| 		auto text = lng_admin_log_participant_left(lt_from, fromLinkText); | 		auto text = lng_admin_log_participant_left(lt_from, fromLinkText); | ||||||
| 		addSimpleServiceMessage(text); | 		addSimpleServiceMessage(text); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createParticipantInvite = [this, &idManager, date](const MTPDchannelAdminLogEventActionParticipantInvite &action) { | 	auto createParticipantInvite = [&](const MTPDchannelAdminLogEventActionParticipantInvite &action) { | ||||||
| 		auto bodyFlags = Flag::f_entities | Flag::f_from_id; | 		auto bodyFlags = Flag::f_entities | Flag::f_from_id; | ||||||
| 		auto bodyReplyTo = 0; | 		auto bodyReplyTo = 0; | ||||||
| 		auto bodyViaBotId = 0; | 		auto bodyViaBotId = 0; | ||||||
| 		auto bodyText = GenerateParticipantChangeText(channel(), action.vparticipant); | 		auto bodyText = GenerateParticipantChangeText(channel, action.vparticipant); | ||||||
| 		addPart(HistoryMessage::create(_history, idManager.next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(_from->id), bodyText)); | 		addPart(HistoryMessage::create(history, idManager.next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(from->id), bodyText)); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createParticipantToggleBan = [this, &idManager, date](const MTPDchannelAdminLogEventActionParticipantToggleBan &action) { | 	auto createParticipantToggleBan = [&](const MTPDchannelAdminLogEventActionParticipantToggleBan &action) { | ||||||
| 		auto bodyFlags = Flag::f_entities | Flag::f_from_id; | 		auto bodyFlags = Flag::f_entities | Flag::f_from_id; | ||||||
| 		auto bodyReplyTo = 0; | 		auto bodyReplyTo = 0; | ||||||
| 		auto bodyViaBotId = 0; | 		auto bodyViaBotId = 0; | ||||||
| 		auto bodyText = GenerateParticipantChangeText(channel(), action.vnew_participant, &action.vprev_participant); | 		auto bodyText = GenerateParticipantChangeText(channel, action.vnew_participant, &action.vprev_participant); | ||||||
| 		addPart(HistoryMessage::create(_history, idManager.next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(_from->id), bodyText)); | 		addPart(HistoryMessage::create(history, idManager.next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(from->id), bodyText)); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto createParticipantToggleAdmin = [this, &idManager, date](const MTPDchannelAdminLogEventActionParticipantToggleAdmin &action) { | 	auto createParticipantToggleAdmin = [&](const MTPDchannelAdminLogEventActionParticipantToggleAdmin &action) { | ||||||
| 		auto bodyFlags = Flag::f_entities | Flag::f_from_id; | 		auto bodyFlags = Flag::f_entities | Flag::f_from_id; | ||||||
| 		auto bodyReplyTo = 0; | 		auto bodyReplyTo = 0; | ||||||
| 		auto bodyViaBotId = 0; | 		auto bodyViaBotId = 0; | ||||||
| 		auto bodyText = GenerateParticipantChangeText(channel(), action.vnew_participant, &action.vprev_participant); | 		auto bodyText = GenerateParticipantChangeText(channel, action.vnew_participant, &action.vprev_participant); | ||||||
| 		addPart(HistoryMessage::create(_history, idManager.next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(_from->id), bodyText)); | 		addPart(HistoryMessage::create(history, idManager.next(), bodyFlags, bodyReplyTo, bodyViaBotId, ::date(date), peerToUser(from->id), bodyText)); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	switch (action.type()) { | 	switch (action.type()) { | ||||||
|  | @ -410,78 +415,4 @@ Item::Item(gsl::not_null<History*> history, LocalIdManager &idManager, const MTP | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Item::addPart(HistoryItem *item) { |  | ||||||
| 	_parts.push_back(item); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int Item::resizeGetHeight(int newWidth) { |  | ||||||
| 	_height = 0; |  | ||||||
| 	for (auto part : _parts) { |  | ||||||
| 		_height += part->resizeGetHeight(newWidth); |  | ||||||
| 	} |  | ||||||
| 	return _height; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Item::draw(Painter &p, QRect clip, TextSelection selection, TimeMs ms) { |  | ||||||
| 	auto top = 0; |  | ||||||
| 	for (auto part : _parts) { |  | ||||||
| 		auto height = part->height(); |  | ||||||
| 		if (clip.top() < top + height && clip.top() + clip.height() > top) { |  | ||||||
| 			p.translate(0, top); |  | ||||||
| 			part->draw(p, clip.translated(0, -top), selection, ms); |  | ||||||
| 			p.translate(0, -top); |  | ||||||
| 		} |  | ||||||
| 		top += height; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool Item::hasPoint(QPoint point) const { |  | ||||||
| 	auto top = 0; |  | ||||||
| 	for (auto part : _parts) { |  | ||||||
| 		auto height = part->height(); |  | ||||||
| 		if (point.y() >= top && point.y() < top + height) { |  | ||||||
| 			return part->hasPoint(point - QPoint(0, top)); |  | ||||||
| 		} |  | ||||||
| 		top += height; |  | ||||||
| 	} |  | ||||||
| 	return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Item::TextState Item::getState(QPoint point, HistoryStateRequest request) const { |  | ||||||
| 	auto top = 0; |  | ||||||
| 	for (auto part : _parts) { |  | ||||||
| 		auto height = part->height(); |  | ||||||
| 		if (point.y() >= top && point.y() < top + height) { |  | ||||||
| 			auto result = TextState(); |  | ||||||
| 			result.data = part->getState(point - QPoint(0, top), request); |  | ||||||
| 			result.handler = part; |  | ||||||
| 			return result; |  | ||||||
| 		} |  | ||||||
| 		top += height; |  | ||||||
| 	} |  | ||||||
| 	return Item::TextState(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| TextSelection Item::adjustSelection(TextSelection selection, TextSelectType type) const { |  | ||||||
| 	return selection; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Item::updatePressed(QPoint point) { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| QString Item::getForwardedInfoText() const { |  | ||||||
| 	for (auto part : _parts) { |  | ||||||
| 		if (auto forwarded = part->Get<HistoryMessageForwarded>()) { |  | ||||||
| 			return forwarded->_text.originalText(AllTextSelection, ExpandLinksNone); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return QString(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Item::~Item() { |  | ||||||
| 	for (auto part : _parts) { |  | ||||||
| 		part->destroy(); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace AdminLog
 | } // namespace AdminLog
 | ||||||
|  |  | ||||||
|  | @ -20,59 +20,44 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org | ||||||
| */ | */ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "history/history_admin_log_inner.h" |  | ||||||
| 
 |  | ||||||
| namespace AdminLog { | namespace AdminLog { | ||||||
| 
 | 
 | ||||||
| class Item { | class HistoryItemOwned; | ||||||
|  | class LocalIdManager; | ||||||
|  | 
 | ||||||
|  | void GenerateItems(gsl::not_null<History*> history, LocalIdManager &idManager, const MTPDchannelAdminLogEvent &event, base::lambda<void(HistoryItemOwned item)> callback); | ||||||
|  | 
 | ||||||
|  | // Smart pointer wrapper for HistoryItem* that destroys the owned item.
 | ||||||
|  | class HistoryItemOwned { | ||||||
| public: | public: | ||||||
| 	struct TextState { | 	explicit HistoryItemOwned(gsl::not_null<HistoryItem*> data) : _data(data) { | ||||||
| 		HistoryTextState data; |  | ||||||
| 		ClickHandlerHost *handler = nullptr; |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	Item(gsl::not_null<History*> history, LocalIdManager &idManager, const MTPDchannelAdminLogEvent &event); |  | ||||||
| 
 |  | ||||||
| 	uint64 id() const { |  | ||||||
| 		return _id; |  | ||||||
| 	} | 	} | ||||||
| 	QDateTime date() const { | 	HistoryItemOwned(const HistoryItemOwned &other) = delete; | ||||||
| 		return _date; | 	HistoryItemOwned &operator=(const HistoryItemOwned &other) = delete; | ||||||
|  | 	HistoryItemOwned(HistoryItemOwned &&other) : _data(base::take(other._data)) { | ||||||
| 	} | 	} | ||||||
| 	int top() const { | 	HistoryItemOwned &operator=(HistoryItemOwned &&other) { | ||||||
| 		return _top; | 		_data = base::take(other._data); | ||||||
|  | 		return *this; | ||||||
| 	} | 	} | ||||||
| 	void setTop(int top) { | 	~HistoryItemOwned() { | ||||||
| 		_top = top; | 		if (_data) { | ||||||
| 	} | 			_data->destroy(); | ||||||
| 	int height() const { | 		} | ||||||
| 		return _height; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	int resizeGetHeight(int newWidth); | 	HistoryItem *get() const { | ||||||
| 	void draw(Painter &p, QRect clip, TextSelection selection, TimeMs ms); | 		return _data; | ||||||
| 	bool hasPoint(QPoint point) const; | 	} | ||||||
| 	TextState getState(QPoint point, HistoryStateRequest request) const; | 	HistoryItem *operator->() const { | ||||||
| 	TextSelection adjustSelection(TextSelection selection, TextSelectType type) const; | 		return get(); | ||||||
| 	void updatePressed(QPoint point); | 	} | ||||||
| 
 | 	operator HistoryItem*() const { | ||||||
| 	QString getForwardedInfoText() const; | 		return get(); | ||||||
| 
 | 	} | ||||||
| 	~Item(); |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	gsl::not_null<ChannelData*> channel() { | 	HistoryItem *_data = nullptr; | ||||||
| 		return _history->peer->asChannel(); |  | ||||||
| 	} |  | ||||||
| 	void addPart(HistoryItem *item); |  | ||||||
| 
 |  | ||||||
| 	uint64 _id = 0; |  | ||||||
| 	QDateTime _date; |  | ||||||
| 	gsl::not_null<History*> _history; |  | ||||||
| 	gsl::not_null<UserData*> _from; |  | ||||||
| 	std::vector<HistoryItem*> _parts; |  | ||||||
| 	int _top = 0; |  | ||||||
| 	int _height = 0; |  | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -780,7 +780,7 @@ void HistoryInner::touchScrollUpdated(const QPoint &screenPos) { | ||||||
| 	touchUpdateSpeed(); | 	touchUpdateSpeed(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QPoint HistoryInner::mapMouseToItem(QPoint p, HistoryItem *item) { | QPoint HistoryInner::mapPointToItem(QPoint p, HistoryItem *item) { | ||||||
| 	int32 msgy = itemTop(item); | 	int32 msgy = itemTop(item); | ||||||
| 	if (msgy < 0) return QPoint(0, 0); | 	if (msgy < 0) return QPoint(0, 0); | ||||||
| 
 | 
 | ||||||
|  | @ -809,7 +809,7 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but | ||||||
| 
 | 
 | ||||||
| 	_mouseAction = MouseAction::None; | 	_mouseAction = MouseAction::None; | ||||||
| 	_mouseActionItem = App::mousedItem(); | 	_mouseActionItem = App::mousedItem(); | ||||||
| 	_dragStartPosition = mapMouseToItem(mapFromGlobal(screenPos), _mouseActionItem); | 	_dragStartPosition = mapPointToItem(mapFromGlobal(screenPos), _mouseActionItem); | ||||||
| 	_pressWasInactive = _controller->window()->wasInactivePress(); | 	_pressWasInactive = _controller->window()->wasInactivePress(); | ||||||
| 	if (_pressWasInactive) _controller->window()->setInactivePress(false); | 	if (_pressWasInactive) _controller->window()->setInactivePress(false); | ||||||
| 
 | 
 | ||||||
|  | @ -1037,7 +1037,7 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu | ||||||
| 		// if we are in selecting items mode perhaps we want to
 | 		// if we are in selecting items mode perhaps we want to
 | ||||||
| 		// toggle selection instead of activating the pressed link
 | 		// toggle selection instead of activating the pressed link
 | ||||||
| 		if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && button != Qt::RightButton) { | 		if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && button != Qt::RightButton) { | ||||||
| 			if (HistoryMedia *media = pressed->getMedia()) { | 			if (auto media = pressed->getMedia()) { | ||||||
| 				if (media->toggleSelectionByHandlerClick(activated)) { | 				if (media->toggleSelectionByHandlerClick(activated)) { | ||||||
| 					activated.clear(); | 					activated.clear(); | ||||||
| 				} | 				} | ||||||
|  | @ -1174,7 +1174,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { | ||||||
| 			uint16 selFrom = _selected.cbegin().value().from, selTo = _selected.cbegin().value().to; | 			uint16 selFrom = _selected.cbegin().value().from, selTo = _selected.cbegin().value().to; | ||||||
| 			hasSelected = (selTo > selFrom) ? 1 : 0; | 			hasSelected = (selTo > selFrom) ? 1 : 0; | ||||||
| 			if (App::mousedItem() && App::mousedItem() == App::hoveredItem()) { | 			if (App::mousedItem() && App::mousedItem() == App::hoveredItem()) { | ||||||
| 				QPoint mousePos(mapMouseToItem(mapFromGlobal(_mousePosition), App::mousedItem())); | 				auto mousePos = mapPointToItem(mapFromGlobal(_mousePosition), App::mousedItem()); | ||||||
| 				HistoryStateRequest request; | 				HistoryStateRequest request; | ||||||
| 				request.flags |= Text::StateRequest::Flag::LookupSymbol; | 				request.flags |= Text::StateRequest::Flag::LookupSymbol; | ||||||
| 				auto dragState = App::mousedItem()->getState(mousePos, request); | 				auto dragState = App::mousedItem()->getState(mousePos, request); | ||||||
|  | @ -2006,7 +2006,7 @@ void HistoryInner::onUpdateSelected() { | ||||||
| 		item = block->items[_curItem]; | 		item = block->items[_curItem]; | ||||||
| 
 | 
 | ||||||
| 		App::mousedItem(item); | 		App::mousedItem(item); | ||||||
| 		m = mapMouseToItem(point, item); | 		m = mapPointToItem(point, item); | ||||||
| 		if (item->hasPoint(m)) { | 		if (item->hasPoint(m)) { | ||||||
| 			if (App::hoveredItem() != item) { | 			if (App::hoveredItem() != item) { | ||||||
| 				repaintItem(App::hoveredItem()); | 				repaintItem(App::hoveredItem()); | ||||||
|  | @ -2208,7 +2208,7 @@ void HistoryInner::onUpdateSelected() { | ||||||
| 	if (auto pressedItem = App::pressedLinkItem()) { | 	if (auto pressedItem = App::pressedLinkItem()) { | ||||||
| 		if (!pressedItem->detached()) { | 		if (!pressedItem->detached()) { | ||||||
| 			if (pressedItem->history() == _history || pressedItem->history() == _migrated) { | 			if (pressedItem->history() == _history || pressedItem->history() == _migrated) { | ||||||
| 				auto adjustedPoint = mapMouseToItem(point, pressedItem); | 				auto adjustedPoint = mapPointToItem(point, pressedItem); | ||||||
| 				pressedItem->updatePressed(adjustedPoint); | 				pressedItem->updatePressed(adjustedPoint); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -45,7 +45,7 @@ public: | ||||||
| 	TextWithEntities getSelectedText() const; | 	TextWithEntities getSelectedText() const; | ||||||
| 
 | 
 | ||||||
| 	void touchScrollUpdated(const QPoint &screenPos); | 	void touchScrollUpdated(const QPoint &screenPos); | ||||||
| 	QPoint mapMouseToItem(QPoint p, HistoryItem *item); | 	QPoint mapPointToItem(QPoint p, HistoryItem *item); | ||||||
| 
 | 
 | ||||||
| 	void recountHeight(); | 	void recountHeight(); | ||||||
| 	void updateSize(); | 	void updateSize(); | ||||||
|  |  | ||||||
|  | @ -724,34 +724,31 @@ void HistoryItem::detachFast() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryItem::previousItemChanged() { | void HistoryItem::previousItemChanged() { | ||||||
|  | 	Expects(!isLogEntry()); | ||||||
| 	recountDisplayDate(); | 	recountDisplayDate(); | ||||||
| 	recountAttachToPrevious(); | 	recountAttachToPrevious(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Called only if there is no more next item! Not always when it changes!
 | // Called only if there is no more next item! Not always when it changes!
 | ||||||
| void HistoryItem::nextItemChanged() { | void HistoryItem::nextItemChanged() { | ||||||
|  | 	Expects(!isLogEntry()); | ||||||
| 	setAttachToNext(false); | 	setAttachToNext(false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryItem::recountAttachToPrevious() { | void HistoryItem::recountAttachToPrevious() { | ||||||
| 	bool attach = false; | 	Expects(!isLogEntry()); | ||||||
|  | 	auto attachToPrevious = false; | ||||||
| 	if (auto previous = previousItem()) { | 	if (auto previous = previousItem()) { | ||||||
| 		if (!Has<HistoryMessageDate>() && !Has<HistoryMessageUnreadBar>()) { | 		if (!Has<HistoryMessageDate>() && !Has<HistoryMessageUnreadBar>()) { | ||||||
| 			attach = !isPost() && !previous->isPost() | 			attachToPrevious = !isPost() && !previous->isPost() | ||||||
| 				&& !serviceMsg() && !previous->serviceMsg() | 				&& !serviceMsg() && !previous->serviceMsg() | ||||||
| 				&& !isEmpty() && !previous->isEmpty() | 				&& !isEmpty() && !previous->isEmpty() | ||||||
| 				&& previous->from() == from() | 				&& previous->from() == from() | ||||||
| 				&& (qAbs(previous->date.secsTo(date)) < kAttachMessageToPreviousSecondsDelta); | 				&& (qAbs(previous->date.secsTo(date)) < kAttachMessageToPreviousSecondsDelta); | ||||||
| 		} | 		} | ||||||
| 		previous->setAttachToNext(attach); | 		previous->setAttachToNext(attachToPrevious); | ||||||
| 	} |  | ||||||
| 	if (attach && !(_flags & MTPDmessage_ClientFlag::f_attach_to_previous)) { |  | ||||||
| 		_flags |= MTPDmessage_ClientFlag::f_attach_to_previous; |  | ||||||
| 		setPendingInitDimensions(); |  | ||||||
| 	} else if (!attach && (_flags & MTPDmessage_ClientFlag::f_attach_to_previous)) { |  | ||||||
| 		_flags &= ~MTPDmessage_ClientFlag::f_attach_to_previous; |  | ||||||
| 		setPendingInitDimensions(); |  | ||||||
| 	} | 	} | ||||||
|  | 	setAttachToPrevious(attachToPrevious); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryItem::setAttachToNext(bool attachToNext) { | void HistoryItem::setAttachToNext(bool attachToNext) { | ||||||
|  | @ -764,6 +761,16 @@ void HistoryItem::setAttachToNext(bool attachToNext) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void HistoryItem::setAttachToPrevious(bool attachToPrevious) { | ||||||
|  | 	if (attachToPrevious && !(_flags & MTPDmessage_ClientFlag::f_attach_to_previous)) { | ||||||
|  | 		_flags |= MTPDmessage_ClientFlag::f_attach_to_previous; | ||||||
|  | 		setPendingInitDimensions(); | ||||||
|  | 	} else if (!attachToPrevious && (_flags & MTPDmessage_ClientFlag::f_attach_to_previous)) { | ||||||
|  | 		_flags &= ~MTPDmessage_ClientFlag::f_attach_to_previous; | ||||||
|  | 		setPendingInitDimensions(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void HistoryItem::setId(MsgId newId) { | void HistoryItem::setId(MsgId newId) { | ||||||
| 	history()->changeMsgId(id, newId); | 	history()->changeMsgId(id, newId); | ||||||
| 	id = newId; | 	id = newId; | ||||||
|  | @ -954,6 +961,8 @@ bool HistoryItem::unread() const { | ||||||
| 
 | 
 | ||||||
| void HistoryItem::destroyUnreadBar() { | void HistoryItem::destroyUnreadBar() { | ||||||
| 	if (Has<HistoryMessageUnreadBar>()) { | 	if (Has<HistoryMessageUnreadBar>()) { | ||||||
|  | 		t_assert(!isLogEntry()); | ||||||
|  | 
 | ||||||
| 		RemoveComponents(HistoryMessageUnreadBar::Bit()); | 		RemoveComponents(HistoryMessageUnreadBar::Bit()); | ||||||
| 		setPendingInitDimensions(); | 		setPendingInitDimensions(); | ||||||
| 		if (_history->unreadBar == this) { | 		if (_history->unreadBar == this) { | ||||||
|  | @ -965,6 +974,7 @@ void HistoryItem::destroyUnreadBar() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryItem::setUnreadBarCount(int count) { | void HistoryItem::setUnreadBarCount(int count) { | ||||||
|  | 	Expects(!isLogEntry()); | ||||||
| 	if (count > 0) { | 	if (count > 0) { | ||||||
| 		HistoryMessageUnreadBar *bar; | 		HistoryMessageUnreadBar *bar; | ||||||
| 		if (!Has<HistoryMessageUnreadBar>()) { | 		if (!Has<HistoryMessageUnreadBar>()) { | ||||||
|  | @ -988,6 +998,7 @@ void HistoryItem::setUnreadBarCount(int count) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryItem::setUnreadBarFreezed() { | void HistoryItem::setUnreadBarFreezed() { | ||||||
|  | 	Expects(!isLogEntry()); | ||||||
| 	if (auto bar = Get<HistoryMessageUnreadBar>()) { | 	if (auto bar = Get<HistoryMessageUnreadBar>()) { | ||||||
| 		bar->_freezed = true; | 		bar->_freezed = true; | ||||||
| 	} | 	} | ||||||
|  | @ -1010,14 +1021,14 @@ void HistoryItem::clipCallback(Media::Clip::Notification notification) { | ||||||
| 	case NotificationReinit: { | 	case NotificationReinit: { | ||||||
| 		auto stopped = false; | 		auto stopped = false; | ||||||
| 		if (reader->autoPausedGif()) { | 		if (reader->autoPausedGif()) { | ||||||
| 			if (auto m = App::main()) { | 			auto amVisible = false; | ||||||
| 				if (!m->isItemVisible(this)) { // stop animation if it is not visible
 | 			AuthSession::Current().data().queryItemVisibility().notify({ this, &amVisible }, true); | ||||||
| 					media->stopInline(); | 			if (!amVisible) { // stop animation if it is not visible
 | ||||||
| 					if (auto document = media->getDocument()) { // forget data from memory
 | 				media->stopInline(); | ||||||
| 						document->forget(); | 				if (auto document = media->getDocument()) { // forget data from memory
 | ||||||
| 					} | 					document->forget(); | ||||||
| 					stopped = true; |  | ||||||
| 				} | 				} | ||||||
|  | 				stopped = true; | ||||||
| 			} | 			} | ||||||
| 		} else if (reader->mode() == Media::Clip::Reader::Mode::Video && reader->state() == Media::Clip::State::Finished) { | 		} else if (reader->mode() == Media::Clip::Reader::Mode::Video && reader->state() == Media::Clip::State::Finished) { | ||||||
| 			// Stop finished video message.
 | 			// Stop finished video message.
 | ||||||
|  | @ -1065,7 +1076,8 @@ void HistoryItem::audioTrackUpdated() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryItem::recountDisplayDate() { | void HistoryItem::recountDisplayDate() { | ||||||
| 	bool displayingDate = ([this]() { | 	Expects(!isLogEntry()); | ||||||
|  | 	setDisplayDate(([this]() { | ||||||
| 		if (isEmpty()) { | 		if (isEmpty()) { | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
|  | @ -1074,13 +1086,15 @@ void HistoryItem::recountDisplayDate() { | ||||||
| 			return previous->isEmpty() || (previous->date.date() != date.date()); | 			return previous->isEmpty() || (previous->date.date() != date.date()); | ||||||
| 		} | 		} | ||||||
| 		return true; | 		return true; | ||||||
| 	})(); | 	})()); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 	if (displayingDate && !Has<HistoryMessageDate>()) { | void HistoryItem::setDisplayDate(bool displayDate) { | ||||||
|  | 	if (displayDate && !Has<HistoryMessageDate>()) { | ||||||
| 		AddComponents(HistoryMessageDate::Bit()); | 		AddComponents(HistoryMessageDate::Bit()); | ||||||
| 		Get<HistoryMessageDate>()->init(date); | 		Get<HistoryMessageDate>()->init(date); | ||||||
| 		setPendingInitDimensions(); | 		setPendingInitDimensions(); | ||||||
| 	} else if (!displayingDate && Has<HistoryMessageDate>()) { | 	} else if (!displayDate && Has<HistoryMessageDate>()) { | ||||||
| 		RemoveComponents(HistoryMessageDate::Bit()); | 		RemoveComponents(HistoryMessageDate::Bit()); | ||||||
| 		setPendingInitDimensions(); | 		setPendingInitDimensions(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -542,6 +542,7 @@ public: | ||||||
| 		return !_block; | 		return !_block; | ||||||
| 	} | 	} | ||||||
| 	void attachToBlock(HistoryBlock *block, int index) { | 	void attachToBlock(HistoryBlock *block, int index) { | ||||||
|  | 		Expects(!isLogEntry()); | ||||||
| 		Expects(_block == nullptr); | 		Expects(_block == nullptr); | ||||||
| 		Expects(_indexInBlock < 0); | 		Expects(_indexInBlock < 0); | ||||||
| 		Expects(block != nullptr); | 		Expects(block != nullptr); | ||||||
|  | @ -820,7 +821,7 @@ public: | ||||||
| 	} | 	} | ||||||
| 	void setPendingResize() { | 	void setPendingResize() { | ||||||
| 		_flags |= MTPDmessage_ClientFlag::f_pending_resize; | 		_flags |= MTPDmessage_ClientFlag::f_pending_resize; | ||||||
| 		if (!detached()) { | 		if (!detached() || isLogEntry()) { | ||||||
| 			_history->setHasPendingResizedItems(); | 			_history->setHasPendingResizedItems(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -879,6 +880,19 @@ public: | ||||||
| 	void clipCallback(Media::Clip::Notification notification); | 	void clipCallback(Media::Clip::Notification notification); | ||||||
| 	void audioTrackUpdated(); | 	void audioTrackUpdated(); | ||||||
| 
 | 
 | ||||||
|  | 	void setLogEntryDisplayDate(bool displayDate) { | ||||||
|  | 		Expects(isLogEntry()); | ||||||
|  | 		setDisplayDate(displayDate); | ||||||
|  | 	} | ||||||
|  | 	void setLogEntryAttachToPrevious(bool attachToPrevious) { | ||||||
|  | 		Expects(isLogEntry()); | ||||||
|  | 		setAttachToNext(attachToPrevious); | ||||||
|  | 	} | ||||||
|  | 	void setLogEntryAttachToNext(bool attachToNext) { | ||||||
|  | 		Expects(isLogEntry()); | ||||||
|  | 		setAttachToNext(attachToNext); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	~HistoryItem(); | 	~HistoryItem(); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|  | @ -929,18 +943,27 @@ protected: | ||||||
| 		return nullptr; | 		return nullptr; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// this should be called only from previousItemChanged()
 | 	// This should be called only from previousItemChanged()
 | ||||||
| 	// to add required bits to the Composer mask
 | 	// to add required bits to the Composer mask
 | ||||||
| 	// after that always use Has<HistoryMessageDate>()
 | 	// after that always use Has<HistoryMessageDate>().
 | ||||||
| 	void recountDisplayDate(); | 	void recountDisplayDate(); | ||||||
| 
 | 
 | ||||||
| 	// this should be called only from previousItemChanged() or when
 | 	// This should be called only from previousItemChanged() or when
 | ||||||
| 	// HistoryMessageDate or HistoryMessageUnreadBar bit is changed in the Composer mask
 | 	// HistoryMessageDate or HistoryMessageUnreadBar bit is changed in the Composer mask
 | ||||||
| 	// then the result should be cached in a client side flag MTPDmessage_ClientFlag::f_attach_to_previous
 | 	// then the result should be cached in a client side flag MTPDmessage_ClientFlag::f_attach_to_previous.
 | ||||||
| 	void recountAttachToPrevious(); | 	void recountAttachToPrevious(); | ||||||
| 
 | 
 | ||||||
| 	// this should be called only recountAttachToPrevious() of the next item
 | 	// This should be called only from recountDisplayDate().
 | ||||||
| 	// or when the next item is removed through nextItemChanged() call
 | 	// Also this is called from setLogEntryDisplayDate() for channel log entries.
 | ||||||
|  | 	void setDisplayDate(bool displayDate); | ||||||
|  | 
 | ||||||
|  | 	// This should be called only from recountAttachToPrevious().
 | ||||||
|  | 	// Also this is called from setLogEntryAttachToPrevious() for channel log entries.
 | ||||||
|  | 	void setAttachToPrevious(bool attachToNext); | ||||||
|  | 
 | ||||||
|  | 	// This should be called only from recountAttachToPrevious() of the next item
 | ||||||
|  | 	// or when the next item is removed through nextItemChanged() call.
 | ||||||
|  | 	// Also this is called from setLogEntryAttachToNext() for channel log entries.
 | ||||||
| 	void setAttachToNext(bool attachToNext); | 	void setAttachToNext(bool attachToNext); | ||||||
| 
 | 
 | ||||||
| 	const HistoryMessageReplyMarkup *inlineReplyMarkup() const { | 	const HistoryMessageReplyMarkup *inlineReplyMarkup() const { | ||||||
|  |  | ||||||
|  | @ -681,6 +681,19 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null<Window::Controller*> | ||||||
| 		// So we force HistoryWidget::resizeEvent() here, without WA_UpdatesDisabled.
 | 		// So we force HistoryWidget::resizeEvent() here, without WA_UpdatesDisabled.
 | ||||||
| 		myEnsureResized(this); | 		myEnsureResized(this); | ||||||
| 	}); | 	}); | ||||||
|  | 	subscribe(AuthSession::Current().data().pendingHistoryResize(), [this] { handlePendingHistoryUpdate(); }); | ||||||
|  | 	subscribe(AuthSession::Current().data().queryItemVisibility(), [this](const AuthSessionData::ItemVisibilityQuery &query) { | ||||||
|  | 		if (_a_show.animating() || _history != query.item->history() || query.item->detached() || !isVisible()) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		auto top = _list->itemTop(query.item); | ||||||
|  | 		if (top >= 0) { | ||||||
|  | 			auto scrollTop = _scroll->scrollTop(); | ||||||
|  | 			if (top + query.item->height() > scrollTop && top < scrollTop + _scroll->height()) { | ||||||
|  | 				*query.isVisible = true; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}); | ||||||
| 
 | 
 | ||||||
| 	orderWidgets(); | 	orderWidgets(); | ||||||
| } | } | ||||||
|  | @ -701,7 +714,7 @@ void HistoryWidget::scrollToCurrentVoiceMessage(FullMsgId fromId, FullMsgId toId | ||||||
| 
 | 
 | ||||||
| 	// If history has pending resize items, the scrollTopItem won't be updated.
 | 	// If history has pending resize items, the scrollTopItem won't be updated.
 | ||||||
| 	// And the scrollTop will be reset back to scrollTopItem + scrollTopOffset.
 | 	// And the scrollTop will be reset back to scrollTopItem + scrollTopOffset.
 | ||||||
| 	notify_handlePendingHistoryUpdate(); | 	handlePendingHistoryUpdate(); | ||||||
| 
 | 
 | ||||||
| 	auto toTop = _list->itemTop(to); | 	auto toTop = _list->itemTop(to); | ||||||
| 	if (toTop >= 0 && !isItemCompletelyHidden(from)) { | 	if (toTop >= 0 && !isItemCompletelyHidden(from)) { | ||||||
|  | @ -4782,17 +4795,6 @@ void HistoryWidget::grabFinish() { | ||||||
| 	_topShadow->show(); | 	_topShadow->show(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool HistoryWidget::isItemVisible(HistoryItem *item) { |  | ||||||
| 	if (isHidden() || _a_show.animating() || !_list) { |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| 	int32 top = _list->itemTop(item), st = _scroll->scrollTop(); |  | ||||||
| 	if (top < 0 || top + item->height() <= st || top >= st + _scroll->height()) { |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| 	return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void HistoryWidget::ui_repaintHistoryItem(gsl::not_null<const HistoryItem*> item) { | void HistoryWidget::ui_repaintHistoryItem(gsl::not_null<const HistoryItem*> item) { | ||||||
| 	if (_peer && _list && (item->history() == _history || (_migrated && item->history() == _migrated))) { | 	if (_peer && _list && (item->history() == _history || (_migrated && item->history() == _migrated))) { | ||||||
| 		auto ms = getms(); | 		auto ms = getms(); | ||||||
|  | @ -4825,7 +4827,7 @@ void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryWidget::notify_handlePendingHistoryUpdate() { | void HistoryWidget::handlePendingHistoryUpdate() { | ||||||
| 	if (hasPendingResizedItems() || _updateHistoryGeometryRequired) { | 	if (hasPendingResizedItems() || _updateHistoryGeometryRequired) { | ||||||
| 		if (_list) { | 		if (_list) { | ||||||
| 			updateHistoryGeometry(); | 			updateHistoryGeometry(); | ||||||
|  |  | ||||||
|  | @ -356,7 +356,6 @@ public: | ||||||
| 	bool notify_switchInlineBotButtonReceived(const QString &query, UserData *samePeerBot, MsgId samePeerReplyTo); | 	bool notify_switchInlineBotButtonReceived(const QString &query, UserData *samePeerBot, MsgId samePeerReplyTo); | ||||||
| 	void notify_userIsBotChanged(UserData *user); | 	void notify_userIsBotChanged(UserData *user); | ||||||
| 	void notify_migrateUpdated(PeerData *peer); | 	void notify_migrateUpdated(PeerData *peer); | ||||||
| 	void notify_handlePendingHistoryUpdate(); |  | ||||||
| 
 | 
 | ||||||
| 	bool cmd_search(); | 	bool cmd_search(); | ||||||
| 	bool cmd_next_chat(); | 	bool cmd_next_chat(); | ||||||
|  | @ -484,6 +483,7 @@ private: | ||||||
| 		bool allFilesForCompress = true; | 		bool allFilesForCompress = true; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | 	void handlePendingHistoryUpdate(); | ||||||
| 	void fullPeerUpdated(PeerData *peer); | 	void fullPeerUpdated(PeerData *peer); | ||||||
| 	void topBarClick(); | 	void topBarClick(); | ||||||
| 	void toggleTabbedSelectorMode(); | 	void toggleTabbedSelectorMode(); | ||||||
|  |  | ||||||
|  | @ -819,13 +819,6 @@ void MainWidget::onFilesOrForwardDrop(const PeerId &peerId, const QMimeData *dat | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool MainWidget::isItemVisible(HistoryItem *item) { |  | ||||||
| 	if (isHidden() || _a_show.animating()) { |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| 	return _history->isItemVisible(item); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void MainWidget::notify_botCommandsChanged(UserData *bot) { | void MainWidget::notify_botCommandsChanged(UserData *bot) { | ||||||
| 	_history->notify_botCommandsChanged(bot); | 	_history->notify_botCommandsChanged(bot); | ||||||
| } | } | ||||||
|  | @ -901,10 +894,6 @@ void MainWidget::notify_historyMuteUpdated(History *history) { | ||||||
| 	_dialogs->notify_historyMuteUpdated(history); | 	_dialogs->notify_historyMuteUpdated(history); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MainWidget::notify_handlePendingHistoryUpdate() { |  | ||||||
| 	_history->notify_handlePendingHistoryUpdate(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool MainWidget::cmd_search() { | bool MainWidget::cmd_search() { | ||||||
| 	if (Ui::isLayerShown() || Ui::isMediaViewShown()) return false; | 	if (Ui::isLayerShown() || Ui::isMediaViewShown()) return false; | ||||||
| 	return _history->cmd_search(); | 	return _history->cmd_search(); | ||||||
|  |  | ||||||
|  | @ -378,8 +378,6 @@ public: | ||||||
| 
 | 
 | ||||||
| 	bool contentOverlapped(const QRect &globalRect); | 	bool contentOverlapped(const QRect &globalRect); | ||||||
| 
 | 
 | ||||||
| 	bool isItemVisible(HistoryItem *item); |  | ||||||
| 
 |  | ||||||
| 	void documentLoadProgress(DocumentData *document); | 	void documentLoadProgress(DocumentData *document); | ||||||
| 
 | 
 | ||||||
| 	void app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col); | 	void app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col); | ||||||
|  | @ -398,7 +396,6 @@ public: | ||||||
| 	void notify_migrateUpdated(PeerData *peer); | 	void notify_migrateUpdated(PeerData *peer); | ||||||
| 	void notify_historyItemLayoutChanged(const HistoryItem *item); | 	void notify_historyItemLayoutChanged(const HistoryItem *item); | ||||||
| 	void notify_historyMuteUpdated(History *history); | 	void notify_historyMuteUpdated(History *history); | ||||||
| 	void notify_handlePendingHistoryUpdate(); |  | ||||||
| 
 | 
 | ||||||
| 	bool cmd_search(); | 	bool cmd_search(); | ||||||
| 	bool cmd_next_chat(); | 	bool cmd_next_chat(); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue