Enable distinct selecting of grouped media.

This commit is contained in:
John Preston 2017-12-15 19:25:47 +03:00
parent 4c9931ab02
commit 537400d8b2
22 changed files with 880 additions and 306 deletions

View File

@ -224,7 +224,10 @@ QString documentSaveFilename(const DocumentData *data, bool forceSavingAs = fals
return saveFileName(caption, filter, prefix, name, forceSavingAs, dir);
}
void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context, ActionOnLoad action) {
void DocumentOpenClickHandler::doOpen(
not_null<DocumentData*> data,
HistoryItem *context,
ActionOnLoad action) {
if (!data->date) return;
auto msgId = context ? context->fullId() : FullMsgId();
@ -329,9 +332,13 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context,
}
void DocumentOpenClickHandler::onClickImpl() const {
const auto item = App::hoveredLinkItem()
const auto item = context()
? App::histItemById(context())
: App::hoveredLinkItem()
? App::hoveredLinkItem()
: (App::contextItem() ? App::contextItem() : nullptr);
: App::contextItem()
? App::contextItem()
: nullptr;
const auto action = document()->isVoiceMessage()
? ActionOnLoadNone
: ActionOnLoadOpen;
@ -339,13 +346,19 @@ void DocumentOpenClickHandler::onClickImpl() const {
}
void GifOpenClickHandler::onClickImpl() const {
const auto item = App::hoveredLinkItem()
const auto item = context()
? App::histItemById(context())
: App::hoveredLinkItem()
? App::hoveredLinkItem()
: (App::contextItem() ? App::contextItem() : nullptr);
: App::contextItem()
? App::contextItem()
: nullptr;
doOpen(document(), item, ActionOnLoadPlayInline);
}
void DocumentSaveClickHandler::doSave(DocumentData *data, bool forceSavingAs) {
void DocumentSaveClickHandler::doSave(
not_null<DocumentData*> data,
bool forceSavingAs) {
if (!data->date) return;
auto filepath = data->filepath(DocumentData::FilePathResolveSaveFromDataSilent, forceSavingAs);

View File

@ -311,15 +311,22 @@ QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform);
class DocumentClickHandler : public LeftButtonClickHandler {
public:
DocumentClickHandler(DocumentData *document)
: _document(document) {
DocumentClickHandler(
not_null<DocumentData*> document,
FullMsgId context = FullMsgId())
: _document(document)
, _context(context) {
}
DocumentData *document() const {
not_null<DocumentData*> document() const {
return _document;
}
FullMsgId context() const {
return _context;
}
private:
DocumentData *_document;
not_null<DocumentData*> _document;
FullMsgId _context;
};
@ -327,7 +334,7 @@ class DocumentSaveClickHandler : public DocumentClickHandler {
public:
using DocumentClickHandler::DocumentClickHandler;
static void doSave(
DocumentData *document,
not_null<DocumentData*> document,
bool forceSavingAs = false);
protected:
@ -339,7 +346,7 @@ class DocumentOpenClickHandler : public DocumentClickHandler {
public:
using DocumentClickHandler::DocumentClickHandler;
static void doOpen(
DocumentData *document,
not_null<DocumentData*> document,
HistoryItem *context,
ActionOnLoad action = ActionOnLoadOpen);

View File

@ -121,7 +121,7 @@ ImagePtr PhotoData::makeReplyPreview() {
}
void PhotoOpenClickHandler::onClickImpl() const {
Messenger::Instance().showPhoto(this, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem());
Messenger::Instance().showPhoto(this);
}
void PhotoSaveClickHandler::onClickImpl() const {
@ -136,13 +136,9 @@ void PhotoCancelClickHandler::onClickImpl() const {
if (!data->date) return;
if (data->uploading()) {
if (auto item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : nullptr)) {
if (auto media = item->getMedia()) {
if (media->type() == MediaTypePhoto && static_cast<HistoryPhoto*>(media)->photo() == data) {
App::contextItem(item);
App::main()->cancelUploadLayer();
}
}
if (const auto item = App::histItemById(context())) {
App::contextItem(item);
App::main()->cancelUploadLayer();
}
} else {
data->cancel();

View File

@ -74,8 +74,11 @@ class PhotoClickHandler : public LeftButtonClickHandler {
public:
PhotoClickHandler(
not_null<PhotoData*> photo,
FullMsgId context = FullMsgId(),
PeerData *peer = nullptr)
: _photo(photo), _peer(peer) {
: _photo(photo)
, _context(context)
, _peer(peer) {
}
not_null<PhotoData*> photo() const {
return _photo;
@ -83,10 +86,14 @@ public:
PeerData *peer() const {
return _peer;
}
FullMsgId context() const {
return _context;
}
private:
not_null<PhotoData*> _photo;
PeerData *_peer;
FullMsgId _context;
PeerData *_peer = nullptr;
};

View File

@ -363,6 +363,55 @@ void HistoryInner::enumerateDates(Method method) {
enumerateItems<EnumItemsDirection::BottomToTop>(dateCallback);
}
TextSelection HistoryInner::itemRenderSelection(
not_null<HistoryItem*> item,
int selfromy,
int seltoy) const {
Expects(!item->detached());
const auto y = item->block()->y() + item->y();
if (y >= selfromy && y < seltoy) {
if (_dragSelecting && !item->serviceMsg() && item->id > 0) {
return FullSelection;
}
} else if (!_selected.empty()) {
const auto itemSelection = [&](not_null<HistoryItem*> item) {
auto i = _selected.find(item);
if (i != _selected.end()) {
return i->second;
}
return TextSelection();
};
const auto group = item->Get<HistoryMessageGroup>();
if (group) {
if (group->leader != item) {
return TextSelection();
}
auto result = TextSelection();
auto allFullSelected = true;
const auto count = int(group->others.size());
for (auto i = 0; i != count; ++i) {
if (itemSelection(group->others[i]) == FullSelection) {
result = AddGroupItemSelection(result, i);
} else {
allFullSelected = false;
}
}
const auto leaderSelection = itemSelection(item);
if (leaderSelection == FullSelection) {
return allFullSelected
? FullSelection
: AddGroupItemSelection(result, count);
} else if (leaderSelection != TextSelection()) {
return leaderSelection;
}
return result;
}
return itemSelection(item);
}
return TextSelection();
}
void HistoryInner::paintEvent(QPaintEvent *e) {
if (Ui::skipPaintEvent(this, e)) {
return;
@ -399,9 +448,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
adjustCurrent(clip.top());
auto selEnd = _selected.cend();
auto hasSel = !_selected.empty();
auto drawToY = clip.y() + clip.height();
auto selfromy = itemTop(_dragSelFrom);
@ -425,18 +471,11 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
p.save();
p.translate(0, y);
if (clip.y() < y + item->height()) while (y < drawToY) {
TextSelection sel;
if (y >= selfromy && y < seltoy) {
if (_dragSelecting && !item->serviceMsg() && item->id > 0) {
sel = FullSelection;
}
} else if (hasSel) {
auto i = _selected.find(item);
if (i != selEnd) {
sel = i->second;
}
}
item->draw(p, clip.translated(0, -y), sel, ms);
const auto selection = itemRenderSelection(
item,
selfromy - mtop,
seltoy - mtop);
item->draw(p, clip.translated(0, -y), selection, ms);
if (item->hasViews()) {
App::main()->scheduleViewIncrement(item);
@ -469,25 +508,18 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
auto iItem = (_curHistory == _history ? _curItem : 0);
auto item = block->items[iItem];
auto historyRect = clip.intersected(QRect(0, hdrawtop, width(), clip.top() + clip.height()));
auto hclip = clip.intersected(QRect(0, hdrawtop, width(), clip.top() + clip.height()));
auto y = htop + block->y() + item->y();
p.save();
p.translate(0, y);
while (y < drawToY) {
auto h = item->height();
if (historyRect.y() < y + h && hdrawtop < y + h) {
TextSelection sel;
if (y >= selfromy && y < seltoy) {
if (_dragSelecting && !item->serviceMsg() && item->id > 0) {
sel = FullSelection;
}
} else if (hasSel) {
auto i = _selected.find(item);
if (i != selEnd) {
sel = i->second;
}
}
item->draw(p, historyRect.translated(0, -y), sel, ms);
if (hclip.y() < y + h && hdrawtop < y + h) {
const auto selection = itemRenderSelection(
item,
selfromy - htop,
seltoy - htop);
item->draw(p, hclip.translated(0, -y), selection, ms);
if (item->hasViews()) {
App::main()->scheduleViewIncrement(item);
@ -830,7 +862,9 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
_mouseAction = MouseAction::PrepareDrag;
} else if (!_selected.empty()) {
if (_selected.cbegin()->second == FullSelection) {
if (_selected.find(_mouseActionItem) != _selected.cend() && App::hoveredItem()) {
if (_dragStateItem
&& _selected.find(_dragStateItem) != _selected.cend()
&& App::hoveredItem()) {
_mouseAction = MouseAction::PrepareDrag; // start items drag
} else if (!_pressWasInactive) {
_mouseAction = MouseAction::PrepareSelect; // start items select
@ -915,6 +949,7 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
void HistoryInner::mouseActionCancel() {
_mouseActionItem = nullptr;
_dragStateItem = nullptr;
_mouseAction = MouseAction::None;
_dragStartPosition = QPoint(0, 0);
_dragSelFrom = _dragSelTo = nullptr;
@ -928,7 +963,8 @@ void HistoryInner::performDrag() {
bool uponSelected = false;
if (_mouseActionItem) {
if (!_selected.empty() && _selected.cbegin()->second == FullSelection) {
uponSelected = (_selected.find(_mouseActionItem) != _selected.cend());
uponSelected = _dragStateItem
&& (_selected.find(_dragStateItem) != _selected.cend());
} else {
HistoryStateRequest request;
request.flags |= Text::StateRequest::Flag::LookupSymbol;
@ -986,7 +1022,7 @@ void HistoryInner::performDrag() {
forwardMimeType = qsl("application/x-td-forward-pressed");
}
}
if (auto pressedLnkItem = App::pressedLinkItem()) {
if (const auto pressedLnkItem = _dragStateItem) {
if ((pressedMedia = pressedLnkItem->getMedia())) {
if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) {
forwardMimeType = qsl("application/x-td-forward-pressed-link");
@ -1029,6 +1065,9 @@ void HistoryInner::itemRemoved(not_null<const HistoryItem*> item) {
if (_mouseActionItem == item) {
mouseActionCancel();
}
if (_dragStateItem == item) {
_dragStateItem = nullptr;
}
if (_dragSelFrom == item || _dragSelTo == item) {
_dragSelFrom = 0;
@ -1041,15 +1080,14 @@ void HistoryInner::itemRemoved(not_null<const HistoryItem*> item) {
void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button) {
mouseActionUpdate(screenPos);
auto pressedLinkItem = App::pressedLinkItem();
auto activated = ClickHandler::unpressed();
if (_mouseAction == MouseAction::Dragging) {
activated.clear();
} else if (auto pressed = pressedLinkItem) {
} else if (_mouseActionItem) {
// if we are in selecting items mode perhaps we want to
// toggle selection instead of activating the pressed link
if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) {
if (auto media = pressed->getMedia()) {
if (auto media = _mouseActionItem->getMedia()) {
if (media->toggleSelectionByHandlerClick(activated)) {
activated.clear();
}
@ -1068,29 +1106,30 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
App::activateClickHandler(activated, button);
return;
}
if (_mouseAction == MouseAction::PrepareSelect && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection) {
auto i = _selected.find(_mouseActionItem);
if (i == _selected.cend()) {
if (!_mouseActionItem->serviceMsg()
&& IsServerMsgId(_mouseActionItem->id)
&& _selected.size() < MaxSelectedItems) {
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
_selected.clear();
}
_selected.emplace(_mouseActionItem, FullSelection);
}
} else {
_selected.erase(i);
}
if ((_mouseAction == MouseAction::PrepareSelect)
&& !_pressWasInactive
&& !_selected.empty()
&& (_selected.cbegin()->second == FullSelection)) {
changeDragSelection(
&_selected,
_mouseActionItem,
SelectAction::Invert);
repaintItem(_mouseActionItem);
} else if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && button != Qt::RightButton) {
auto i = _selected.find(_mouseActionItem);
} else if ((_mouseAction == MouseAction::PrepareDrag)
&& !_pressWasInactive
&& _dragStateItem
&& (button != Qt::RightButton)) {
auto i = _selected.find(_dragStateItem);
if (i != _selected.cend() && i->second == FullSelection) {
_selected.erase(i);
repaintItem(_mouseActionItem);
} else if (i == _selected.cend() && !_mouseActionItem->serviceMsg() && _mouseActionItem->id > 0 && !_selected.empty() && _selected.cbegin()->second == FullSelection) {
} else if ((i == _selected.cend())
&& !_dragStateItem->serviceMsg()
&& (_dragStateItem->id > 0)
&& !_selected.empty()
&& _selected.cbegin()->second == FullSelection) {
if (_selected.size() < MaxSelectedItems) {
_selected.emplace(_mouseActionItem, FullSelection);
_selected.emplace(_dragStateItem, FullSelection);
repaintItem(_mouseActionItem);
}
} else {
@ -1117,7 +1156,8 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
setToClipboard(_selected.cbegin()->first->selectedText(_selected.cbegin()->second), QClipboard::Selection);
const auto [item, selection] = *_selected.cbegin();
setToClipboard(item->selectedText(selection), QClipboard::Selection);
}
#endif // Q_OS_LINUX32 || Q_OS_LINUX64
}
@ -1206,9 +1246,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
_menu = new Ui::PopupMenu(nullptr);
_contextMenuLink = ClickHandler::getActive();
HistoryItem *item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem();
PhotoClickHandler *lnkPhoto = dynamic_cast<PhotoClickHandler*>(_contextMenuLink.data());
DocumentClickHandler *lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLink.data());
auto item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem();
auto lnkPhoto = dynamic_cast<PhotoClickHandler*>(_contextMenuLink.data());
auto lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLink.data());
auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false;
auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false;
auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : false;
@ -1279,7 +1319,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
}
}
if (App::hoveredLinkItem()->id > 0 && !App::hoveredLinkItem()->serviceMsg()) {
_menu->addAction(lang(lng_context_select_msg), _widget, SLOT(selectMessage()))->setEnabled(true);
_menu->addAction(lang(lng_context_select_msg), base::lambda_guarded(this, [this] {
// TODO
}))->setEnabled(true);
}
App::contextItem(App::hoveredLinkItem());
}
@ -1513,7 +1555,7 @@ void HistoryInner::saveContextGif() {
}
void HistoryInner::copyContextText() {
auto item = App::contextItem();
const auto item = App::contextItem();
if (!item || (item->getMedia() && item->getMedia()->type() == MediaTypeSticker)) {
return;
}
@ -1532,24 +1574,24 @@ void HistoryInner::resizeEvent(QResizeEvent *e) {
}
TextWithEntities HistoryInner::getSelectedText() const {
SelectedItems sel = _selected;
auto selected = _selected;
if (_mouseAction == MouseAction::Selecting && _dragSelFrom && _dragSelTo) {
applyDragSelection(&sel);
applyDragSelection(&selected);
}
if (sel.empty()) {
if (selected.empty()) {
return TextWithEntities();
}
if (sel.cbegin()->second != FullSelection) {
return sel.cbegin()->first->selectedText(sel.cbegin()->second);
if (selected.cbegin()->second != FullSelection) {
const auto [item, selection] = *selected.cbegin();
return item->selectedText(selection);
}
int fullSize = 0;
QString timeFormat(qsl(", [dd.MM.yy hh:mm]\n"));
QMap<int, TextWithEntities> texts;
for (auto &selected : sel) {
auto item = selected.first;
for (const auto [item, selection] : selected) {
if (item->detached()) continue;
auto time = item->date.toString(timeFormat);
@ -2007,10 +2049,11 @@ MessageIdsList HistoryInner::getSelectedItems() const {
return result;
}
void HistoryInner::selectItem(HistoryItem *item) {
void HistoryInner::selectItem(not_null<HistoryItem*> item) {
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
_selected.clear();
} else if (_selected.size() == MaxSelectedItems && _selected.find(item) == _selected.cend()) {
} else if (_selected.size() == MaxSelectedItems
&& _selected.find(item) == _selected.cend()) {
return;
}
_selected.emplace(item, FullSelection);
@ -2059,10 +2102,16 @@ void HistoryInner::onUpdateSelected() {
HistoryTextState dragState;
ClickHandlerHost *lnkhost = nullptr;
bool selectingText = (item == _mouseActionItem && item == App::hoveredItem() && !_selected.empty() && _selected.cbegin()->second != FullSelection);
auto selectingText = (item == _mouseActionItem)
&& (item == App::hoveredItem())
&& !_selected.empty()
&& (_selected.cbegin()->second != FullSelection);
if (point.y() < _historyPaddingTop) {
if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
dragState = _botAbout->info->text.getState(point - _botAbout->rect.topLeft() - QPoint(st::msgPadding.left(), st::msgPadding.top() + st::botDescSkip + st::msgNameFont->height), _botAbout->width);
dragState = HistoryTextState(nullptr, _botAbout->info->text.getState(
point - _botAbout->rect.topLeft() - QPoint(st::msgPadding.left(), st::msgPadding.top() + st::botDescSkip + st::msgNameFont->height),
_botAbout->width));
_dragStateItem = App::histItemById(dragState.itemId);
lnkhost = _botAbout.get();
}
} else if (item) {
@ -2077,7 +2126,7 @@ void HistoryInner::onUpdateSelected() {
auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top();
auto scrollDateOpacity = _scrollDateOpacity.current(_scrollDateShown ? 1. : 0.);
enumerateDates([this, &dragState, &lnkhost, &point, scrollDateOpacity, dateHeight/*, lastDate, showFloatingBefore*/](not_null<HistoryItem*> item, int itemtop, int dateTop) {
enumerateDates([&](not_null<HistoryItem*> item, int itemtop, int dateTop) {
// stop enumeration if the date is above our point
if (dateTop + dateHeight <= point.y()) {
return false;
@ -2116,7 +2165,10 @@ void HistoryInner::onUpdateSelected() {
} else {
static_cast<DateClickHandler*>(_scrollDateLink.data())->setDate(item->date.date());
}
dragState.link = _scrollDateLink;
dragState = HistoryTextState(
nullptr,
_scrollDateLink);
_dragStateItem = App::histItemById(dragState.itemId);
lnkhost = item;
}
}
@ -2132,11 +2184,12 @@ void HistoryInner::onUpdateSelected() {
selectingText = false;
}
dragState = item->getState(m, request);
_dragStateItem = App::histItemById(dragState.itemId);
lnkhost = item;
if (!dragState.link && m.x() >= st::historyPhotoLeft && m.x() < st::historyPhotoLeft + st::msgPhotoSize) {
if (auto msg = item->toHistoryMessage()) {
if (msg->hasFromPhoto()) {
enumerateUserpics([&dragState, &lnkhost, &point](not_null<HistoryMessage*> message, int userpicTop) -> bool {
enumerateUserpics([&](not_null<HistoryMessage*> message, int userpicTop) -> bool {
// stop enumeration if the userpic is below our point
if (userpicTop > point.y()) {
return false;
@ -2144,7 +2197,10 @@ void HistoryInner::onUpdateSelected() {
// stop enumeration if we've found a userpic under the cursor
if (point.y() >= userpicTop && point.y() < userpicTop + st::msgPhotoSize) {
dragState.link = message->displayFrom()->openLink();
dragState = HistoryTextState(
nullptr,
message->displayFrom()->openLink());
_dragStateItem = App::histItemById(dragState.itemId);
lnkhost = message;
return false;
}
@ -2171,7 +2227,7 @@ void HistoryInner::onUpdateSelected() {
} else if (_mouseCursorState == HistoryInTextCursorState && (_selected.empty() || _selected.cbegin()->second != FullSelection)) {
cur = style::cur_text;
} else if (_mouseCursorState == HistoryInDateCursorState) {
// cur = style::cur_cross;
//cur = style::cur_cross;
}
} else if (item) {
if (_mouseAction == MouseAction::Selecting) {
@ -2235,7 +2291,9 @@ void HistoryInner::onUpdateSelected() {
if (ClickHandler::getPressed()) {
cur = style::cur_pointer;
} else if (_mouseAction == MouseAction::Selecting && !_selected.empty() && _selected.cbegin()->second != FullSelection) {
} else if ((_mouseAction == MouseAction::Selecting)
&& !_selected.empty()
&& (_selected.cbegin()->second != FullSelection)) {
if (!_dragSelFrom || !_dragSelTo) {
cur = style::cur_text;
}
@ -2243,7 +2301,7 @@ void HistoryInner::onUpdateSelected() {
}
// Voice message seek support.
if (auto pressedItem = App::pressedLinkItem()) {
if (const auto pressedItem = _dragStateItem) {
if (!pressedItem->detached()) {
if (pressedItem->history() == _history || pressedItem->history() == _migrated) {
auto adjustedPoint = mapPointToItem(point, pressedItem);
@ -2370,25 +2428,125 @@ void HistoryInner::applyDragSelection() {
applyDragSelection(&_selected);
}
void HistoryInner::addSelectionRange(SelectedItems *toItems, int32 fromblock, int32 fromitem, int32 toblock, int32 toitem, History *h) const {
bool HistoryInner::isFullSelected(
not_null<SelectedItems*> toItems,
not_null<HistoryItem*> item) const {
const auto group = [&] {
if (const auto group = item->Get<HistoryMessageGroup>()) {
if (group->leader == item) {
return group;
}
return group->leader->Get<HistoryMessageGroup>();
}
return (HistoryMessageGroup*)nullptr;
}();
const auto singleSelected = [&](not_null<HistoryItem*> item) {
const auto i = toItems->find(item);
return (i != toItems->cend()) && (i->second == FullSelection);
};
if (group) {
if (!singleSelected(group->leader)) {
return false;
}
for (const auto other : group->others) {
if (!singleSelected(other)) {
return false;
}
}
} else if (!singleSelected(item)) {
return false;
}
return true;
}
void HistoryInner::changeDragSelection(
not_null<SelectedItems*> toItems,
not_null<HistoryItem*> item,
SelectAction action) const {
if (action == SelectAction::Invert) {
action = isFullSelected(toItems, item)
? SelectAction::Deselect
: SelectAction::Select;
}
auto total = toItems->size();
const auto add = (action == SelectAction::Select);
const auto goodForAdding = [&](not_null<HistoryItem*> item) {
if (item->id <= 0 || item->serviceMsg()) {
return false;
}
if (toItems->find(item) == toItems->end()) {
++total;
}
return true;
};
const auto addSingle = [&](not_null<HistoryItem*> item) {
const auto i = toItems->find(item);
if (i == toItems->cend()) {
toItems->emplace(item, FullSelection);
} else if (i->second != FullSelection) {
i->second = FullSelection;
}
};
const auto removeSingle = [&](not_null<HistoryItem*> item) {
const auto i = toItems->find(item);
if (i != toItems->cend()) {
toItems->erase(i);
}
};
const auto group = [&] {
if (const auto group = item->Get<HistoryMessageGroup>()) {
if (group->leader == item) {
return group;
}
return group->leader->Get<HistoryMessageGroup>();
}
return (HistoryMessageGroup*)nullptr;
}();
if (group) {
const auto adding = [&] {
if (!add || !goodForAdding(group->leader)) {
return false;
}
for (const auto other : group->others) {
if (!goodForAdding(other)) {
return false;
}
}
return (total <= MaxSelectedItems);
}();
if (adding) {
addSingle(group->leader);
for (const auto other : group->others) {
addSingle(other);
}
} else {
removeSingle(group->leader);
for (const auto other : group->others) {
removeSingle(other);
}
}
} else {
if (add && goodForAdding(item) && total <= MaxSelectedItems) {
addSingle(item);
} else {
removeSingle(item);
}
}
}
void HistoryInner::addSelectionRange(
not_null<SelectedItems*> toItems,
not_null<History*> history,
int fromblock,
int fromitem,
int toblock,
int toitem) const {
if (fromblock >= 0 && fromitem >= 0 && toblock >= 0 && toitem >= 0) {
for (; fromblock <= toblock; ++fromblock) {
auto block = h->blocks[fromblock];
for (int32 cnt = (fromblock < toblock) ? block->items.size() : (toitem + 1); fromitem < cnt; ++fromitem) {
auto block = history->blocks[fromblock];
for (int cnt = (fromblock < toblock) ? block->items.size() : (toitem + 1); fromitem < cnt; ++fromitem) {
auto item = block->items[fromitem];
auto i = toItems->find(item);
if (item->id > 0 && !item->serviceMsg()) {
if (i == toItems->cend()) {
if (toItems->size() >= MaxSelectedItems) break;
toItems->emplace(item, FullSelection);
} else if (i->second != FullSelection) {
i->second = FullSelection;
}
} else {
if (i != toItems->cend()) {
toItems->erase(i);
}
}
changeDragSelection(toItems, item, SelectAction::Select);
}
if (toItems->size() >= MaxSelectedItems) break;
fromitem = 0;
@ -2396,27 +2554,33 @@ void HistoryInner::addSelectionRange(SelectedItems *toItems, int32 fromblock, in
}
}
void HistoryInner::applyDragSelection(SelectedItems *toItems) const {
int32 selfromy = itemTop(_dragSelFrom), seltoy = itemTop(_dragSelTo);
void HistoryInner::applyDragSelection(
not_null<SelectedItems*> toItems) const {
const auto selfromy = itemTop(_dragSelFrom);
const auto seltoy = [&] {
auto result = itemTop(_dragSelTo);
return (result < 0) ? result : (result + _dragSelTo->height());
}();
if (selfromy < 0 || seltoy < 0) {
return;
}
seltoy += _dragSelTo->height();
if (!toItems->empty() && toItems->cbegin()->second != FullSelection) {
toItems->clear();
}
if (_dragSelecting) {
int32 fromblock = _dragSelFrom->block()->indexInHistory(), fromitem = _dragSelFrom->indexInBlock();
int32 toblock = _dragSelTo->block()->indexInHistory(), toitem = _dragSelTo->indexInBlock();
auto fromblock = _dragSelFrom->block()->indexInHistory();
auto fromitem = _dragSelFrom->indexInBlock();
auto toblock = _dragSelTo->block()->indexInHistory();
auto toitem = _dragSelTo->indexInBlock();
if (_migrated) {
if (_dragSelFrom->history() == _migrated) {
if (_dragSelTo->history() == _migrated) {
addSelectionRange(toItems, fromblock, fromitem, toblock, toitem, _migrated);
addSelectionRange(toItems, _migrated, fromblock, fromitem, toblock, toitem);
toblock = -1;
toitem = -1;
} else {
addSelectionRange(toItems, fromblock, fromitem, _migrated->blocks.size() - 1, _migrated->blocks.back()->items.size() - 1, _migrated);
addSelectionRange(toItems, _migrated, fromblock, fromitem, _migrated->blocks.size() - 1, _migrated->blocks.back()->items.size() - 1);
}
fromblock = 0;
fromitem = 0;
@ -2425,20 +2589,20 @@ void HistoryInner::applyDragSelection(SelectedItems *toItems) const {
toitem = -1;
}
}
addSelectionRange(toItems, fromblock, fromitem, toblock, toitem, _history);
addSelectionRange(toItems, _history, fromblock, fromitem, toblock, toitem);
} else {
for (auto i = toItems->begin(); i != toItems->cend();) {
auto toRemove = std::vector<not_null<HistoryItem*>>();
for (auto i = toItems->begin(); i != toItems->cend(); ++i) {
auto iy = itemTop(i->first);
if (iy < 0) {
if (iy < -1) i = toItems->erase(i);
continue;
}
if (iy >= selfromy && iy < seltoy) {
i = toItems->erase(i);
} else {
++i;
if (iy < -1) {
toRemove.push_back(i->first);
} else if (iy >= 0 && iy >= selfromy && iy < seltoy) {
toRemove.push_back(i->first);
}
}
for (const auto item : toRemove) {
changeDragSelection(toItems, item, SelectAction::Deselect);
}
}
}

View File

@ -66,7 +66,7 @@ public:
HistoryTopBarWidget::SelectedState getSelectionState() const;
void clearSelectedItems(bool onlyTextSelection = false);
MessageIdsList getSelectedItems() const;
void selectItem(HistoryItem *item);
void selectItem(not_null<HistoryItem*> item);
void updateBotInfo(bool recount = true);
@ -165,6 +165,10 @@ private:
HistoryItem *prevItem(HistoryItem *item);
HistoryItem *nextItem(HistoryItem *item);
void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting);
TextSelection itemRenderSelection(
not_null<HistoryItem*> item,
int selfromy,
int seltoy) const;
void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard);
@ -217,8 +221,26 @@ private:
using SelectedItems = std::map<HistoryItem*, TextSelection, std::less<>>;
SelectedItems _selected;
void applyDragSelection();
void applyDragSelection(SelectedItems *toItems) const;
void addSelectionRange(SelectedItems *toItems, int32 fromblock, int32 fromitem, int32 toblock, int32 toitem, History *h) const;
void applyDragSelection(not_null<SelectedItems*> toItems) const;
void addSelectionRange(
not_null<SelectedItems*> toItems,
not_null<History*> history,
int fromblock,
int fromitem,
int toblock,
int toitem) const;
bool isFullSelected(
not_null<SelectedItems*> toItems,
not_null<HistoryItem*> item) const;
enum class SelectAction {
Select,
Deselect,
Invert,
};
void changeDragSelection(
not_null<SelectedItems*> toItems,
not_null<HistoryItem*> item,
SelectAction action) const;
// Does any of the shown histories has this flag set.
bool hasPendingResizedItems() const {
@ -230,6 +252,7 @@ private:
QPoint _dragStartPosition;
QPoint _mousePosition;
HistoryItem *_mouseActionItem = nullptr;
HistoryItem *_dragStateItem = nullptr;
HistoryCursorState _mouseCursorState = HistoryDefaultCursorState;
uint16 _mouseTextSymbol = 0;
bool _pressWasInactive = false;

View File

@ -46,6 +46,29 @@ constexpr int kAttachMessageToPreviousSecondsDelta = 900;
} // namespace
HistoryTextState::HistoryTextState(not_null<const HistoryItem*> item)
: itemId(item->fullId()) {
}
HistoryTextState::HistoryTextState(
not_null<const HistoryItem*> item,
const Text::StateResult &state)
: itemId(item->fullId())
, cursor(state.uponSymbol
? HistoryInTextCursorState
: HistoryDefaultCursorState)
, link(state.link)
, afterSymbol(state.afterSymbol)
, symbol(state.symbol) {
}
HistoryTextState::HistoryTextState(
not_null<const HistoryItem*> item,
ClickHandlerPtr link)
: itemId(item->fullId())
, link(link) {
}
ReplyMarkupClickHandler::ReplyMarkupClickHandler(const HistoryItem *item, int row, int col)
: _itemId(item->fullId())
, _row(row)

View File

@ -79,25 +79,33 @@ enum HistoryCursorState {
struct HistoryTextState {
HistoryTextState() = default;
HistoryTextState(const Text::StateResult &state)
: cursor(state.uponSymbol ? HistoryInTextCursorState : HistoryDefaultCursorState)
, link(state.link)
, afterSymbol(state.afterSymbol)
, symbol(state.symbol) {
HistoryTextState(not_null<const HistoryItem*> item);
HistoryTextState(
not_null<const HistoryItem*> item,
const Text::StateResult &state);
HistoryTextState(
not_null<const HistoryItem*> item,
ClickHandlerPtr link);
HistoryTextState(
std::nullptr_t,
const Text::StateResult &state)
: cursor(state.uponSymbol
? HistoryInTextCursorState
: HistoryDefaultCursorState)
, link(state.link)
, afterSymbol(state.afterSymbol)
, symbol(state.symbol) {
}
HistoryTextState &operator=(const Text::StateResult &state) {
cursor = state.uponSymbol ? HistoryInTextCursorState : HistoryDefaultCursorState;
link = state.link;
afterSymbol = state.afterSymbol;
symbol = state.symbol;
return *this;
}
HistoryTextState(ClickHandlerPtr link) : link(link) {
HistoryTextState(std::nullptr_t, ClickHandlerPtr link)
: link(link) {
}
FullMsgId itemId;
HistoryCursorState cursor = HistoryDefaultCursorState;
ClickHandlerPtr link;
bool afterSymbol = false;
uint16 symbol = 0;
};
struct HistoryStateRequest {

View File

@ -135,7 +135,8 @@ public:
return false;
}
virtual std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const = 0;
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const = 0;
virtual DocumentData *getDocument() {
return nullptr;

View File

@ -54,13 +54,20 @@ HistoryGroupedMedia::Element::Element(not_null<HistoryItem*> item)
HistoryGroupedMedia::HistoryGroupedMedia(
not_null<HistoryItem*> parent,
const std::vector<not_null<HistoryItem*>> &others)
: HistoryMedia(parent) {
: HistoryMedia(parent)
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
const auto result = applyGroup(others);
Ensures(result);
}
void HistoryGroupedMedia::initDimensions() {
if (_caption.hasSkipBlock()) {
_caption.setSkipBlock(
_parent->skipBlockWidth(),
_parent->skipBlockHeight());
}
std::vector<QSize> sizes;
sizes.reserve(_elements.size());
for (const auto &element : _elements) {
@ -84,6 +91,14 @@ void HistoryGroupedMedia::initDimensions() {
_elements[i].initialGeometry = item.geometry;
_elements[i].sides = item.sides;
}
if (!_caption.isEmpty()) {
auto captionw = _maxw - st::msgPadding.left() - st::msgPadding.right();
_minh += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
_minh += st::msgPadding.bottom();
}
}
}
int HistoryGroupedMedia::resizeGetHeight(int width) {
@ -94,7 +109,7 @@ int HistoryGroupedMedia::resizeGetHeight(int width) {
}
const auto initialSpacing = st::historyGroupSkip;
const auto factor = width / float64(st::historyGroupWidthMax);
const auto factor = width / float64(_maxw);
const auto scale = [&](int value) {
return int(std::round(value * factor));
};
@ -124,6 +139,15 @@ int HistoryGroupedMedia::resizeGetHeight(int width) {
accumulate_max(_height, top + height);
}
if (!_caption.isEmpty()) {
const auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
_height += st::mediaPadding.bottom() + st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
_height += st::msgPadding.bottom();
}
}
return _height;
}
@ -132,7 +156,13 @@ void HistoryGroupedMedia::draw(
const QRect &clip,
TextSelection selection,
TimeMs ms) const {
for (const auto &element : _elements) {
for (auto i = 0, count = int(_elements.size()); i != count; ++i) {
const auto &element = _elements[i];
const auto elementSelection = (selection == FullSelection)
? FullSelection
: IsGroupItemSelection(selection, i)
? FullSelection
: TextSelection();
auto corners = GetCornersFromSides(element.sides);
if (!isBubbleTop()) {
corners &= ~(RectPart::TopLeft | RectPart::TopRight);
@ -143,13 +173,36 @@ void HistoryGroupedMedia::draw(
element.content->drawGrouped(
p,
clip,
selection,
elementSelection,
ms,
element.geometry,
corners,
&element.cacheKey,
&element.cache);
}
// date
const auto selected = (selection == FullSelection);
if (!_caption.isEmpty()) {
const auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
const auto outbg = _parent->hasOutLayout();
const auto captiony = _height
- (isBubbleBottom() ? st::msgPadding.bottom() : 0)
- _caption.countHeight(captionw);
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
_caption.draw(p, st::msgPadding.left(), captiony, captionw, style::al_left, 0, -1, selection);
} else if (_parent->getMedia() == this) {
auto fullRight = _width;
auto fullBottom = _height;
if (_parent->id < 0 || App::hoveredItem() == _parent) {
_parent->drawInfo(p, fullRight, fullBottom, _width, selected, InfoDisplayOverImage);
}
if (!_parent->hasBubble() && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
_parent->drawRightAction(p, fastShareLeft, fastShareTop, _width);
}
}
}
HistoryTextState HistoryGroupedMedia::getState(
@ -157,10 +210,39 @@ HistoryTextState HistoryGroupedMedia::getState(
HistoryStateRequest request) const {
for (const auto &element : _elements) {
if (element.geometry.contains(point)) {
return element.content->getStateGrouped(
auto result = element.content->getStateGrouped(
element.geometry,
point,
request);
result.itemId = element.item->fullId();
return result;
}
}
if (!_caption.isEmpty()) {
const auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
const auto captiony = _height
- (isBubbleBottom() ? st::msgPadding.bottom() : 0)
- _caption.countHeight(captionw);
if (QRect(st::msgPadding.left(), captiony, captionw, _height - captiony).contains(point)) {
return HistoryTextState(_parent, _caption.getState(
point - QPoint(st::msgPadding.left(), captiony),
captionw,
request.forText()));
}
}
auto result = HistoryTextState(_parent);
if (_caption.isEmpty() && _parent->getMedia() == this) {
auto fullRight = _width;
auto fullBottom = _height;
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
result.cursor = HistoryInDateCursorState;
}
if (!_parent->hasBubble() && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
result.link = _parent->rightActionLink();
}
}
}
return HistoryTextState();
@ -191,6 +273,14 @@ TextSelection HistoryGroupedMedia::adjustSelection(
return _caption.adjustSelection(selection, type);
}
QString HistoryGroupedMedia::notificationText() const {
return WithCaptionNotificationText(lang(lng_in_dlg_album), _caption);
}
QString HistoryGroupedMedia::inDialogsText() const {
return WithCaptionDialogsText(lang(lng_in_dlg_album), _caption);
}
TextWithEntities HistoryGroupedMedia::selectedText(
TextSelection selection) const {
return WithCaptionSelectedText(
@ -212,6 +302,9 @@ void HistoryGroupedMedia::clickHandlerPressedChanged(
bool pressed) {
for (const auto &element : _elements) {
element.content->clickHandlerPressedChanged(p, pressed);
if (pressed && element.content->dragItemByHandler(p)) {
App::pressedLinkItem(element.item);
}
}
}
@ -243,7 +336,7 @@ bool HistoryGroupedMedia::applyGroup(
Assert(media != nullptr && media->canBeGrouped());
_elements.push_back(Element(item));
_elements.back().content = item->getMedia()->clone(_parent);
_elements.back().content = item->getMedia()->clone(_parent, item);
};
if (_elements.empty()) {
pushElement(_parent);

View File

@ -34,7 +34,8 @@ public:
return MediaTypeGrouped;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Unexpected("Clone HistoryGroupedMedia.");
}
@ -64,6 +65,8 @@ public:
return !_caption.isEmpty();
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
void clickHandlerActiveChanged(

View File

@ -272,12 +272,16 @@ HistoryPhoto::HistoryPhoto(
: HistoryFileMedia(parent)
, _data(photo)
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
const auto fullId = parent->fullId();
setLinks(
MakeShared<PhotoOpenClickHandler>(_data),
MakeShared<PhotoSaveClickHandler>(_data),
MakeShared<PhotoCancelClickHandler>(_data));
MakeShared<PhotoOpenClickHandler>(_data, fullId),
MakeShared<PhotoSaveClickHandler>(_data, fullId),
MakeShared<PhotoCancelClickHandler>(_data, fullId));
if (!caption.isEmpty()) {
_caption.setText(st::messageTextStyle, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent));
_caption.setText(
st::messageTextStyle,
caption + _parent->skipBlock(),
itemTextNoMonoOptions(_parent));
}
init();
}
@ -289,10 +293,11 @@ HistoryPhoto::HistoryPhoto(
int width)
: HistoryFileMedia(parent)
, _data(photo) {
const auto fullId = parent->fullId();
setLinks(
MakeShared<PhotoOpenClickHandler>(_data, chat),
MakeShared<PhotoSaveClickHandler>(_data, chat),
MakeShared<PhotoCancelClickHandler>(_data, chat));
MakeShared<PhotoOpenClickHandler>(_data, fullId, chat),
MakeShared<PhotoSaveClickHandler>(_data, fullId, chat),
MakeShared<PhotoCancelClickHandler>(_data, fullId, chat));
_width = width;
init();
@ -308,16 +313,18 @@ HistoryPhoto::HistoryPhoto(
HistoryPhoto::HistoryPhoto(
not_null<HistoryItem*> parent,
not_null<HistoryItem*> realParent,
const HistoryPhoto &other)
: HistoryFileMedia(parent)
, _data(other._data)
, _pixw(other._pixw)
, _pixh(other._pixh)
, _caption(other._caption) {
const auto fullId = realParent->fullId();
setLinks(
MakeShared<PhotoOpenClickHandler>(_data),
MakeShared<PhotoSaveClickHandler>(_data),
MakeShared<PhotoCancelClickHandler>(_data));
MakeShared<PhotoOpenClickHandler>(_data, fullId),
MakeShared<PhotoSaveClickHandler>(_data, fullId),
MakeShared<PhotoCancelClickHandler>(_data, fullId));
init();
}
@ -533,9 +540,11 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim
}
HistoryTextState HistoryPhoto::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(_parent);
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
}
int skipx = 0, skipy = 0, width = _width, height = _height;
auto bubble = _parent->hasBubble();
@ -549,7 +558,10 @@ HistoryTextState HistoryPhoto::getState(QPoint point, HistoryStateRequest reques
height -= st::msgPadding.bottom();
}
if (QRect(st::msgPadding.left(), height, captionw, _height - height).contains(point)) {
result = _caption.getState(point - QPoint(st::msgPadding.left(), height), captionw, request.forText());
result = HistoryTextState(_parent, _caption.getState(
point - QPoint(st::msgPadding.left(), height),
captionw,
request.forText()));
return result;
}
height -= st::mediaCaptionSkip;
@ -696,7 +708,7 @@ HistoryTextState HistoryPhoto::getStateGrouped(
return {};
}
const auto delayed = _data->full->toDelayedStorageImage();
return _data->uploading()
return HistoryTextState(_parent, _data->uploading()
? _cancell
: _data->loaded()
? _openl
@ -704,7 +716,7 @@ HistoryTextState HistoryPhoto::getStateGrouped(
? ((!delayed || !delayed->location().isNull())
? _cancell
: ClickHandlerPtr())
: _savel;
: _savel);
}
void HistoryPhoto::validateGroupedCache(
@ -872,10 +884,13 @@ HistoryVideo::HistoryVideo(
, _thumbw(1)
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
if (!caption.isEmpty()) {
_caption.setText(st::messageTextStyle, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent));
_caption.setText(
st::messageTextStyle,
caption + _parent->skipBlock(),
itemTextNoMonoOptions(_parent));
}
setDocumentLinks(_data);
setDocumentLinks(_data, parent);
setStatusSize(FileStatusSizeReady);
@ -884,12 +899,13 @@ HistoryVideo::HistoryVideo(
HistoryVideo::HistoryVideo(
not_null<HistoryItem*> parent,
not_null<HistoryItem*> realParent,
const HistoryVideo &other)
: HistoryFileMedia(parent)
, _data(other._data)
, _thumbw(other._thumbw)
, _caption(other._caption) {
setDocumentLinks(_data);
setDocumentLinks(_data, realParent);
setStatusSize(other._statusSize);
}
@ -1089,7 +1105,7 @@ HistoryTextState HistoryVideo::getState(QPoint point, HistoryStateRequest reques
return {};
}
HistoryTextState result;
auto result = HistoryTextState(_parent);
bool loaded = _data->loaded();
int32 skipx = 0, skipy = 0, width = _width, height = _height;
@ -1105,7 +1121,10 @@ HistoryTextState HistoryVideo::getState(QPoint point, HistoryStateRequest reques
height -= st::msgPadding.bottom();
}
if (QRect(st::msgPadding.left(), height, captionw, _height - height).contains(point)) {
result = _caption.getState(point - QPoint(st::msgPadding.left(), height), captionw, request.forText());
result = HistoryTextState(_parent, _caption.getState(
point - QPoint(st::msgPadding.left(), height),
captionw,
request.forText()));
}
height -= st::mediaCaptionSkip;
}
@ -1242,13 +1261,13 @@ HistoryTextState HistoryVideo::getStateGrouped(
if (!geometry.contains(point)) {
return {};
}
return _data->uploading()
return HistoryTextState(_parent, _data->uploading()
? _cancell
: _data->loaded()
? _openl
: _data->loading()
? _cancell
: _savel;
: _savel);
}
void HistoryVideo::validateGroupedCache(
@ -1425,7 +1444,7 @@ HistoryDocument::HistoryDocument(
fillNamedFromData(named);
}
setDocumentLinks(_data);
setDocumentLinks(_data, parent);
setStatusSize(FileStatusSizeReady);
@ -1450,7 +1469,7 @@ HistoryDocument::HistoryDocument(
}
}
setDocumentLinks(_data);
setDocumentLinks(_data, parent);
setStatusSize(other._statusSize);
@ -1821,9 +1840,11 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection,
}
HistoryTextState HistoryDocument::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(_parent);
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
}
bool loaded = _data->loaded();
@ -1882,7 +1903,10 @@ HistoryTextState HistoryDocument::getState(QPoint point, HistoryStateRequest req
auto height = _height;
if (auto captioned = Get<HistoryDocumentCaptioned>()) {
if (point.y() >= bottom) {
result = captioned->_caption.getState(point - QPoint(st::msgPadding.left(), bottom), _width - st::msgPadding.left() - st::msgPadding.right(), request.forText());
result = HistoryTextState(_parent, captioned->_caption.getState(
point - QPoint(st::msgPadding.left(), bottom),
_width - st::msgPadding.left() - st::msgPadding.right(),
request.forText()));
return result;
}
auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
@ -2163,7 +2187,7 @@ HistoryGif::HistoryGif(
: HistoryFileMedia(parent)
, _data(document)
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
setDocumentLinks(_data, true);
setDocumentLinks(_data, parent, true);
setStatusSize(FileStatusSizeReady);
@ -2182,7 +2206,7 @@ HistoryGif::HistoryGif(
, _thumbw(other._thumbw)
, _thumbh(other._thumbh)
, _caption(other._caption) {
setDocumentLinks(_data, true);
setDocumentLinks(_data, parent, true);
setStatusSize(other._statusSize);
}
@ -2614,9 +2638,11 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
}
HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(_parent);
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
}
int32 skipx = 0, skipy = 0, width = _width, height = _height;
bool bubble = _parent->hasBubble();
@ -2630,7 +2656,10 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request)
height -= st::msgPadding.bottom();
}
if (QRect(st::msgPadding.left(), height, captionw, _height - height).contains(point)) {
result = _caption.getState(point - QPoint(st::msgPadding.left(), height), captionw, request.forText());
result = HistoryTextState(_parent, _caption.getState(
point - QPoint(st::msgPadding.left(), height),
captionw,
request.forText()));
return result;
}
height -= st::mediaCaptionSkip;
@ -2679,7 +2708,10 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request)
if (breakEverywhere) {
textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
}
result = forwarded->_text.getState(point - QPoint(rectx + st::msgReplyPadding.left(), recty + st::msgReplyPadding.top()), innerw, textRequest);
result = HistoryTextState(_parent, forwarded->_text.getState(
point - QPoint(rectx + st::msgReplyPadding.left(), recty + st::msgReplyPadding.top()),
innerw,
textRequest));
result.symbol = 0;
result.afterSymbol = false;
if (breakEverywhere) {
@ -3138,8 +3170,10 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T
}
HistoryTextState HistorySticker::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
auto result = HistoryTextState(_parent);
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
}
auto outbg = _parent->hasOutLayout();
auto childmedia = (_parent->getMedia() != this);
@ -3408,7 +3442,7 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, T
}
HistoryTextState HistoryContact::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(_parent);
int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0;
auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus;
@ -3556,7 +3590,7 @@ void HistoryCall::draw(Painter &p, const QRect &r, TextSelection selection, Time
}
HistoryTextState HistoryCall::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(_parent);
if (QRect(0, 0, _width, _height).contains(point)) {
result.link = _link;
return result;
@ -3617,9 +3651,12 @@ HistoryWebPage::HistoryWebPage(not_null<HistoryItem*> parent, not_null<WebPageDa
, _description(st::msgMinWidth - st::webPageLeft) {
}
HistoryWebPage::HistoryWebPage(not_null<HistoryItem*> parent, const HistoryWebPage &other) : HistoryMedia(parent)
HistoryWebPage::HistoryWebPage(
not_null<HistoryItem*> parent,
const HistoryWebPage &other)
: HistoryMedia(parent)
, _data(other._data)
, _attach(other._attach ? other._attach->clone(parent) : nullptr)
, _attach(other._attach ? other._attach->clone(parent, parent) : nullptr)
, _asArticle(other._asArticle)
, _title(other._title)
, _description(other._description)
@ -3999,9 +4036,11 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, T
}
HistoryTextState HistoryWebPage::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(_parent);
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
}
int32 skipx = 0, skipy = 0, width = _width, height = _height;
QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
@ -4030,7 +4069,11 @@ HistoryTextState HistoryWebPage::getState(QPoint point, HistoryStateRequest requ
if (point.y() >= tshift && point.y() < tshift + _titleLines * lineHeight) {
Text::StateRequestElided titleRequest = request.forText();
titleRequest.lines = _titleLines;
result = _title.getStateElidedLeft(point - QPoint(padding.left(), tshift), width, _width, titleRequest);
result = HistoryTextState(_parent, _title.getStateElidedLeft(
point - QPoint(padding.left(), tshift),
width,
_width,
titleRequest));
} else if (point.y() >= tshift + _titleLines * lineHeight) {
symbolAdd += _title.length();
}
@ -4042,9 +4085,17 @@ HistoryTextState HistoryWebPage::getState(QPoint point, HistoryStateRequest requ
if (_descriptionLines > 0) {
Text::StateRequestElided descriptionRequest = request.forText();
descriptionRequest.lines = _descriptionLines;
result = _description.getStateElidedLeft(point - QPoint(padding.left(), tshift), width, _width, descriptionRequest);
result = HistoryTextState(_parent, _description.getStateElidedLeft(
point - QPoint(padding.left(), tshift),
width,
_width,
descriptionRequest));
} else {
result = _description.getStateLeft(point - QPoint(padding.left(), tshift), width, _width, request.forText());
result = HistoryTextState(_parent, _description.getStateLeft(
point - QPoint(padding.left(), tshift),
width,
_width,
request.forText()));
}
} else if (point.y() >= tshift + descriptionHeight) {
symbolAdd += _description.length();
@ -4178,7 +4229,7 @@ HistoryGame::HistoryGame(
const HistoryGame &other)
: HistoryMedia(parent)
, _data(other._data)
, _attach(other._attach ? other._attach->clone(parent) : nullptr)
, _attach(other._attach ? other._attach->clone(parent, parent) : nullptr)
, _title(other._title)
, _description(other._description) {
}
@ -4395,9 +4446,11 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, Time
}
HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(_parent);
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
}
int32 width = _width, height = _height;
QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
@ -4416,7 +4469,11 @@ HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request
if (point.y() >= tshift && point.y() < tshift + _titleLines * lineHeight) {
Text::StateRequestElided titleRequest = request.forText();
titleRequest.lines = _titleLines;
result = _title.getStateElidedLeft(point - QPoint(padding.left(), tshift), width, _width, titleRequest);
result = HistoryTextState(_parent, _title.getStateElidedLeft(
point - QPoint(padding.left(), tshift),
width,
_width,
titleRequest));
} else if (point.y() >= tshift + _titleLines * lineHeight) {
symbolAdd += _title.length();
}
@ -4426,7 +4483,11 @@ HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request
if (point.y() >= tshift && point.y() < tshift + _descriptionLines * lineHeight) {
Text::StateRequestElided descriptionRequest = request.forText();
descriptionRequest.lines = _descriptionLines;
result = _description.getStateElidedLeft(point - QPoint(padding.left(), tshift), width, _width, descriptionRequest);
result = HistoryTextState(_parent, _description.getStateElidedLeft(
point - QPoint(padding.left(), tshift),
width,
_width,
descriptionRequest));
} else if (point.y() >= tshift + _descriptionLines * lineHeight) {
symbolAdd += _description.length();
}
@ -4576,7 +4637,7 @@ HistoryInvoice::HistoryInvoice(
not_null<HistoryItem*> parent,
const HistoryInvoice &other)
: HistoryMedia(parent)
, _attach(other._attach ? other._attach->clone(parent) : nullptr)
, _attach(other._attach ? other._attach->clone(parent, parent) : nullptr)
, _titleHeight(other._titleHeight)
, _descriptionHeight(other._descriptionHeight)
, _title(other._title)
@ -4833,9 +4894,11 @@ void HistoryInvoice::draw(Painter &p, const QRect &r, TextSelection selection, T
}
HistoryTextState HistoryInvoice::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(_parent);
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
}
int32 width = _width, height = _height;
QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
@ -4853,7 +4916,11 @@ HistoryTextState HistoryInvoice::getState(QPoint point, HistoryStateRequest requ
if (point.y() >= tshift && point.y() < tshift + _titleHeight) {
Text::StateRequestElided titleRequest = request.forText();
titleRequest.lines = _titleHeight / lineHeight;
result = _title.getStateElidedLeft(point - QPoint(padding.left(), tshift), width, _width, titleRequest);
result = HistoryTextState(_parent, _title.getStateElidedLeft(
point - QPoint(padding.left(), tshift),
width,
_width,
titleRequest));
} else if (point.y() >= tshift + _titleHeight) {
symbolAdd += _title.length();
}
@ -4861,7 +4928,11 @@ HistoryTextState HistoryInvoice::getState(QPoint point, HistoryStateRequest requ
}
if (_descriptionHeight) {
if (point.y() >= tshift && point.y() < tshift + _descriptionHeight) {
result = _description.getStateLeft(point - QPoint(padding.left(), tshift), width, _width, request.forText());
result = HistoryTextState(_parent, _description.getStateLeft(
point - QPoint(padding.left(), tshift),
width,
_width,
request.forText()));
} else if (point.y() >= tshift + _descriptionHeight) {
symbolAdd += _description.length();
}
@ -5122,10 +5193,12 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection,
}
HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(_parent);
auto symbolAdd = 0;
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
}
int32 skipx = 0, skipy = 0, width = _width, height = _height;
bool bubble = _parent->hasBubble();
@ -5145,7 +5218,11 @@ HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest req
if (!_title.isEmpty()) {
auto titleh = qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
if (point.y() >= skipy && point.y() < skipy + titleh) {
result = _title.getStateLeft(point - QPoint(skipx + st::msgPadding.left(), skipy), textw, _width, request.forText());
result = HistoryTextState(_parent, _title.getStateLeft(
point - QPoint(skipx + st::msgPadding.left(), skipy),
textw,
_width,
request.forText()));
return result;
} else if (point.y() >= skipy + titleh) {
symbolAdd += _title.length();
@ -5155,7 +5232,11 @@ HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest req
if (!_description.isEmpty()) {
auto descriptionh = qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
if (point.y() >= skipy && point.y() < skipy + descriptionh) {
result = _description.getStateLeft(point - QPoint(skipx + st::msgPadding.left(), skipy), textw, _width, request.forText());
result = HistoryTextState(_parent, _description.getStateLeft(
point - QPoint(skipx + st::msgPadding.left(), skipy),
textw,
_width,
request.forText()));
} else if (point.y() >= skipy + descriptionh) {
symbolAdd += _description.length();
}

View File

@ -72,21 +72,28 @@ public:
protected:
ClickHandlerPtr _openl, _savel, _cancell;
void setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell);
void setDocumentLinks(DocumentData *document, bool inlinegif = false) {
void setDocumentLinks(
not_null<DocumentData*> document,
not_null<HistoryItem*> realParent,
bool inlinegif = false) {
ClickHandlerPtr open, save;
const auto context = realParent->fullId();
if (inlinegif) {
open = MakeShared<GifOpenClickHandler>(document);
open = MakeShared<GifOpenClickHandler>(document, context);
} else {
open = MakeShared<DocumentOpenClickHandler>(document);
open = MakeShared<DocumentOpenClickHandler>(document, context);
}
if (inlinegif) {
save = MakeShared<GifOpenClickHandler>(document);
save = MakeShared<GifOpenClickHandler>(document, context);
} else if (document->isVoiceMessage()) {
save = MakeShared<DocumentOpenClickHandler>(document);
save = MakeShared<DocumentOpenClickHandler>(document, context);
} else {
save = MakeShared<DocumentSaveClickHandler>(document);
save = MakeShared<DocumentSaveClickHandler>(document, context);
}
setLinks(std::move(open), std::move(save), MakeShared<DocumentCancelClickHandler>(document));
setLinks(
std::move(open),
std::move(save),
MakeShared<DocumentCancelClickHandler>(document, context));
}
// >= 0 will contain download / upload string, _statusSize = loaded bytes
@ -153,15 +160,19 @@ public:
not_null<PeerData*> chat,
const MTPDphoto &photo,
int width);
HistoryPhoto(not_null<HistoryItem*> parent, const HistoryPhoto &other);
HistoryPhoto(
not_null<HistoryItem*> parent,
not_null<HistoryItem*> realParent,
const HistoryPhoto &other);
void init();
HistoryMediaType type() const override {
return MediaTypePhoto;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
return std::make_unique<HistoryPhoto>(newParent, *this);
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
return std::make_unique<HistoryPhoto>(newParent, realParent, *this);
}
void initDimensions() override;
@ -269,14 +280,18 @@ public:
not_null<HistoryItem*> parent,
not_null<DocumentData*> document,
const QString &caption);
HistoryVideo(not_null<HistoryItem*> parent, const HistoryVideo &other);
HistoryVideo(
not_null<HistoryItem*> parent,
not_null<HistoryItem*> realParent,
const HistoryVideo &other);
HistoryMediaType type() const override {
return MediaTypeVideo;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
return std::make_unique<HistoryVideo>(newParent, *this);
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
return std::make_unique<HistoryVideo>(newParent, realParent, *this);
}
void initDimensions() override;
@ -460,7 +475,10 @@ public:
: MediaTypeFile);
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryDocument>(newParent, *this);
}
@ -579,7 +597,10 @@ public:
return MediaTypeGif;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryGif>(newParent, *this);
}
@ -696,7 +717,10 @@ public:
return MediaTypeSticker;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistorySticker>(newParent, _data);
}
@ -772,8 +796,16 @@ public:
return MediaTypeContact;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
return std::make_unique<HistoryContact>(newParent, _userId, _fname, _lname, _phone);
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryContact>(
newParent,
_userId,
_fname,
_lname,
_phone);
}
void initDimensions() override;
@ -840,7 +872,8 @@ public:
return MediaTypeCall;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Unexpected("Clone HistoryCall.");
}
@ -902,7 +935,10 @@ public:
return MediaTypeWebPage;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryWebPage>(newParent, *this);
}
@ -1012,7 +1048,10 @@ public:
return MediaTypeGame;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryGame>(newParent, *this);
}
@ -1127,7 +1166,10 @@ public:
return MediaTypeInvoice;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryInvoice>(newParent, *this);
}
@ -1231,7 +1273,10 @@ public:
return MediaTypeLocation;
}
std::unique_ptr<HistoryMedia> clone(
not_null<HistoryItem*> newParent) const override {
not_null<HistoryItem*> newParent,
not_null<HistoryItem*> realParent) const override {
Expects(newParent == realParent);
return std::make_unique<HistoryLocation>(newParent, *this);
}

View File

@ -757,7 +757,7 @@ HistoryMessage::HistoryMessage(
return (mediaType != MediaTypeCount);
};
if (cloneMedia()) {
_media = mediaOriginal->clone(this);
_media = mediaOriginal->clone(this, this);
}
setText(fwd->originalText());
}
@ -1512,12 +1512,22 @@ Storage::SharedMediaTypesMask HistoryMessage::sharedMediaTypes() const {
TextWithEntities HistoryMessage::selectedText(TextSelection selection) const {
TextWithEntities logEntryOriginalResult;
auto textResult = _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection, ExpandLinksAll);
const auto textSelection = (selection == FullSelection)
? AllTextSelection
: IsSubGroupSelection(selection)
? TextSelection(0, 0)
: selection;
auto textResult = _text.originalTextWithEntities(
textSelection,
ExpandLinksAll);
auto skipped = skipTextSelection(selection);
auto mediaDisplayed = (_media && _media->isDisplayed());
auto mediaResult = mediaDisplayed ? _media->selectedText(skipped) : TextWithEntities();
if (auto entry = Get<HistoryMessageLogEntryOriginal>()) {
logEntryOriginalResult = entry->_page->selectedText(mediaDisplayed ? _media->skipSelection(skipped) : skipped);
const auto originalSelection = mediaDisplayed
? _media->skipSelection(skipped)
: skipped;
logEntryOriginalResult = entry->_page->selectedText(originalSelection);
}
auto result = textResult;
if (result.text.isEmpty()) {
@ -2232,7 +2242,7 @@ bool HistoryMessage::pointInTime(int right, int bottom, QPoint point, InfoDispla
}
HistoryTextState HistoryMessage::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(this);
auto g = countGeometry();
if (g.width() < 1) {
@ -2272,7 +2282,9 @@ HistoryTextState HistoryMessage::getState(QPoint point, HistoryStateRequest requ
auto entryLeft = g.left();
auto entryTop = trect.y() + trect.height();
if (point.y() >= entryTop && point.y() < entryTop + entryHeight) {
result = entry->_page->getState(point - QPoint(entryLeft, entryTop), request);
result = entry->_page->getState(
point - QPoint(entryLeft, entryTop),
request);
result.symbol += _text.length() + (mediaDisplayed ? _media->fullSelectionLength() : 0);
}
}
@ -2408,7 +2420,10 @@ void HistoryMessage::updatePressed(QPoint point) {
}
}
bool HistoryMessage::getStateFromName(QPoint point, QRect &trect, HistoryTextState *outResult) const {
bool HistoryMessage::getStateFromName(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const {
if (displayFromName()) {
if (point.y() >= trect.top() && point.y() < trect.top() + st::msgNameFont->height) {
auto user = displayFrom();
@ -2428,7 +2443,11 @@ bool HistoryMessage::getStateFromName(QPoint point, QRect &trect, HistoryTextSta
return false;
}
bool HistoryMessage::getStateForwardedInfo(QPoint point, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const {
bool HistoryMessage::getStateForwardedInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult,
const HistoryStateRequest &request) const {
if (displayForwardedFrom()) {
auto forwarded = Get<HistoryMessageForwarded>();
auto fwdheight = ((forwarded->_text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height;
@ -2438,7 +2457,10 @@ bool HistoryMessage::getStateForwardedInfo(QPoint point, QRect &trect, HistoryTe
if (breakEverywhere) {
textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
}
*outResult = forwarded->_text.getState(point - trect.topLeft(), trect.width(), textRequest);
*outResult = HistoryTextState(this, forwarded->_text.getState(
point - trect.topLeft(),
trect.width(),
textRequest));
outResult->symbol = 0;
outResult->afterSymbol = false;
if (breakEverywhere) {
@ -2453,7 +2475,10 @@ bool HistoryMessage::getStateForwardedInfo(QPoint point, QRect &trect, HistoryTe
return false;
}
bool HistoryMessage::getStateReplyInfo(QPoint point, QRect &trect, HistoryTextState *outResult) const {
bool HistoryMessage::getStateReplyInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const {
if (auto reply = Get<HistoryMessageReply>()) {
int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
if (point.y() >= trect.top() && point.y() < trect.top() + h) {
@ -2467,7 +2492,10 @@ bool HistoryMessage::getStateReplyInfo(QPoint point, QRect &trect, HistoryTextSt
return false;
}
bool HistoryMessage::getStateViaBotIdInfo(QPoint point, QRect &trect, HistoryTextState *outResult) const {
bool HistoryMessage::getStateViaBotIdInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const {
if (!displayFromName() && !Has<HistoryMessageForwarded>()) {
if (auto via = Get<HistoryMessageVia>()) {
if (QRect(trect.x(), trect.y(), via->_width, st::msgNameFont->height).contains(point)) {
@ -2480,9 +2508,16 @@ bool HistoryMessage::getStateViaBotIdInfo(QPoint point, QRect &trect, HistoryTex
return false;
}
bool HistoryMessage::getStateText(QPoint point, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const {
bool HistoryMessage::getStateText(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult,
const HistoryStateRequest &request) const {
if (trect.contains(point)) {
*outResult = _text.getState(point - trect.topLeft(), trect.width(), request.forText());
*outResult = HistoryTextState(this, _text.getState(
point - trect.topLeft(),
trect.width(),
request.forText()));
return true;
}
return false;
@ -2524,13 +2559,18 @@ TextSelection HistoryMessage::adjustSelection(TextSelection selection, TextSelec
}
void HistoryMessage::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (_media) _media->clickHandlerActiveChanged(p, active);
HistoryItem::clickHandlerActiveChanged(p, active);
if (_media) {
_media->clickHandlerActiveChanged(p, active);
}
}
void HistoryMessage::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
if (_media) _media->clickHandlerPressedChanged(p, pressed);
HistoryItem::clickHandlerPressedChanged(p, pressed);
if (_media) {
// HistoryGroupedMedia overrides HistoryItem App::pressedLinkItem().
_media->clickHandlerPressedChanged(p, pressed);
}
}
QString HistoryMessage::notificationHeader() const {

View File

@ -329,11 +329,28 @@ private:
void paintViaBotIdInfo(Painter &p, QRect &trect, bool selected) const;
void paintText(Painter &p, QRect &trect, TextSelection selection) const;
bool getStateFromName(QPoint point, QRect &trect, HistoryTextState *outResult) const;
bool getStateForwardedInfo(QPoint point, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const;
bool getStateReplyInfo(QPoint point, QRect &trect, HistoryTextState *outResult) const;
bool getStateViaBotIdInfo(QPoint point, QRect &trect, HistoryTextState *outResult) const;
bool getStateText(QPoint point, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const;
bool getStateFromName(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const;
bool getStateForwardedInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult,
const HistoryStateRequest &request) const;
bool getStateReplyInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const;
bool getStateViaBotIdInfo(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult) const;
bool getStateText(
QPoint point,
QRect &trect,
not_null<HistoryTextState*> outResult,
const HistoryStateRequest &request) const;
void setMedia(const MTPMessageMedia *media);
void setReplyMarkup(const MTPReplyMarkup *markup);

View File

@ -594,7 +594,7 @@ bool HistoryService::hasPoint(QPoint point) const {
}
HistoryTextState HistoryService::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto result = HistoryTextState(this);
auto g = countGeometry();
if (g.width() < 1) {
@ -618,7 +618,10 @@ HistoryTextState HistoryService::getState(QPoint point, HistoryStateRequest requ
if (trect.contains(point)) {
auto textRequest = request.forText();
textRequest.align = style::al_center;
result = _text.getState(point - trect.topLeft(), trect.width(), textRequest);
result = HistoryTextState(this, _text.getState(
point - trect.topLeft(),
trect.width(),
textRequest));
if (auto gamescore = Get<HistoryServiceGameScore>()) {
if (!result.link && result.cursor == HistoryInTextCursorState && g.contains(point)) {
result.link = gamescore->lnk;

View File

@ -218,9 +218,9 @@ HistoryTextState Gif::getState(
HistoryStateRequest request) const {
if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) {
if (_delete && rtlpoint(point, _width).x() >= _width - st::stickerPanDeleteIconBg.width() && point.y() < st::stickerPanDeleteIconBg.height()) {
return _delete;
return { nullptr, _delete };
} else {
return _send;
return { nullptr, _send };
}
}
return {};
@ -411,7 +411,7 @@ HistoryTextState Sticker::getState(
QPoint point,
HistoryStateRequest request) const {
if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) {
return _send;
return { nullptr, _send };
}
return {};
}
@ -501,7 +501,7 @@ HistoryTextState Photo::getState(
QPoint point,
HistoryStateRequest request) const {
if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) {
return _send;
return { nullptr, _send };
}
return {};
}
@ -646,10 +646,10 @@ HistoryTextState Video::getState(
QPoint point,
HistoryStateRequest request) const {
if (QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) {
return _link;
return { nullptr, _link };
}
if (QRect(st::inlineThumbSize + st::inlineThumbSkip, 0, _width - st::inlineThumbSize - st::inlineThumbSkip, _height).contains(point)) {
return _send;
return { nullptr, _send };
}
return {};
}
@ -787,11 +787,11 @@ HistoryTextState File::getState(
QPoint point,
HistoryStateRequest request) const {
if (QRect(0, st::inlineRowMargin, st::msgFileSize, st::msgFileSize).contains(point)) {
return getShownDocument()->loading() ? _cancel : _open;
return { nullptr, getShownDocument()->loading() ? _cancel : _open };
} else {
auto left = st::msgFileSize + st::inlineThumbSkip;
if (QRect(left, 0, _width - left, _height).contains(point)) {
return _send;
return { nullptr, _send };
}
}
return {};
@ -955,7 +955,7 @@ HistoryTextState Contact::getState(
if (!QRect(0, st::inlineRowMargin, st::msgFileSize, st::inlineThumbSize).contains(point)) {
auto left = (st::msgFileSize + st::inlineThumbSkip);
if (QRect(left, 0, _width - left, _height).contains(point)) {
return _send;
return { nullptr, _send };
}
}
return {};
@ -1090,7 +1090,7 @@ HistoryTextState Article::getState(
QPoint point,
HistoryStateRequest request) const {
if (_withThumb && QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) {
return _link;
return { nullptr, _link };
}
auto left = _withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0;
if (QRect(left, 0, _width - left, _height).contains(point)) {
@ -1100,10 +1100,10 @@ HistoryTextState Article::getState(
auto descriptionLines = 2;
auto descriptionHeight = qMin(_description.countHeight(_width - left), st::normalFont->height * descriptionLines);
if (rtlrect(left, st::inlineRowMargin + titleHeight + descriptionHeight, _urlWidth, st::normalFont->height, _width).contains(point)) {
return _url;
return { nullptr, _url };
}
}
return _send;
return { nullptr, _send };
}
return {};
}
@ -1275,23 +1275,23 @@ HistoryTextState Game::getState(
HistoryStateRequest request) const {
int left = st::inlineThumbSize + st::inlineThumbSkip;
if (QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) {
return _send;
return { nullptr, _send };
}
if (QRect(left, 0, _width - left, _height).contains(point)) {
return _send;
return { nullptr, _send };
}
return {};
}
void Game::prepareThumb(int width, int height) const {
auto thumb = ([this]() {
auto thumb = [this] {
if (auto photo = getResultPhoto()) {
return photo->medium;
} else if (auto document = getResultDocument()) {
return document->thumb;
}
return ImagePtr();
})();
}();
if (thumb->isNull()) {
return;
}

View File

@ -24,6 +24,40 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
constexpr auto FullSelection = TextSelection { 0xFFFF, 0xFFFF };
inline bool IsSubGroupSelection(TextSelection selection) {
return (selection.from == 0xFFFF) && (selection.to != 0xFFFF);
}
inline bool IsGroupItemSelection(
TextSelection selection,
int index) {
Expects(index >= 0 && index < 0x0F);
return IsSubGroupSelection(selection) && (selection.to & (1 << index));
}
inline [[nodiscard]] TextSelection AddGroupItemSelection(
TextSelection selection,
int index) {
Expects(index >= 0 && index < 0x0F);
const auto bit = uint16(1U << index);
return TextSelection(
0xFFFF,
IsSubGroupSelection(selection) ? (selection.to | bit) : bit);
}
inline[[nodiscard]] TextSelection RemoveGroupItemSelection(
TextSelection selection,
int index) {
Expects(index >= 0 && index < 0x0F);
const auto bit = uint16(1U << index);
return IsSubGroupSelection(selection)
? TextSelection(0xFFFF, selection.to & ~bit)
: selection;
}
extern TextParseOptions _textNameOptions, _textDlgOptions;
extern TextParseOptions _historyTextOptions, _historyBotOptions, _historyTextNoMonoOptions, _historyBotNoMonoOptions;

View File

@ -201,9 +201,11 @@ bool Messenger::hideMediaView() {
return false;
}
void Messenger::showPhoto(not_null<const PhotoOpenClickHandler*> link, HistoryItem *item) {
return (!item && link->peer())
? showPhoto(link->photo(), link->peer())
void Messenger::showPhoto(not_null<const PhotoOpenClickHandler*> link) {
const auto item = App::histItemById(link->context());
const auto peer = link->peer();
return (!item && peer)
? showPhoto(link->photo(), peer)
: showPhoto(link->photo(), item);
}
@ -214,7 +216,9 @@ void Messenger::showPhoto(not_null<PhotoData*> photo, HistoryItem *item) {
_mediaView->setFocus();
}
void Messenger::showPhoto(not_null<PhotoData*> photo, PeerData *peer) {
void Messenger::showPhoto(
not_null<PhotoData*> photo,
not_null<PeerData*> peer) {
if (_mediaView->isHidden()) Ui::hideLayer(anim::type::instant);
_mediaView->showPhoto(photo, peer);
_mediaView->activateWindow();

View File

@ -86,9 +86,9 @@ public:
// MediaView interface.
void checkMediaViewActivation();
bool hideMediaView();
void showPhoto(not_null<const PhotoOpenClickHandler*> link, HistoryItem *item = nullptr);
void showPhoto(not_null<const PhotoOpenClickHandler*> link);
void showPhoto(not_null<PhotoData*> photo, HistoryItem *item);
void showPhoto(not_null<PhotoData*> photo, PeerData *item);
void showPhoto(not_null<PhotoData*> photo, not_null<PeerData*> item);
void showDocument(not_null<DocumentData*> document, HistoryItem *item);
PeerData *ui_getPeerForMouseAction();

View File

@ -283,7 +283,7 @@ Photo::Photo(
not_null<PhotoData*> photo)
: ItemBase(parent)
, _data(photo)
, _link(MakeShared<PhotoOpenClickHandler>(photo)) {
, _link(MakeShared<PhotoOpenClickHandler>(photo, parent->fullId())) {
}
void Photo::initDimensions() {
@ -352,7 +352,7 @@ HistoryTextState Photo::getState(
QPoint point,
HistoryStateRequest request) const {
if (hasPoint(point)) {
return _link;
return { parent(), _link };
}
return {};
}
@ -508,7 +508,12 @@ HistoryTextState Video::getState(
bool loaded = _data->loaded();
if (hasPoint(point)) {
return loaded ? _openl : (_data->loading() ? _cancell : _savel);
const auto link = loaded
? _openl
: _data->loading()
? _cancell
: _savel;
return { parent(), link };
}
return {};
}
@ -687,13 +692,14 @@ HistoryTextState Voice::getState(
_st.songThumbSize,
_width);
if (inner.contains(point)) {
return loaded
const auto link = loaded
? _openl
: ((_data->loading() || _data->status == FileUploading)
? _cancell
: _openl);
: (_data->loading() || _data->status == FileUploading)
? _cancell
: _openl;
return { parent(), link };
}
auto result = HistoryTextState();
auto result = HistoryTextState(parent());
const auto statusmaxwidth = _width - nameleft - nameright;
const auto statusrect = rtlrect(
nameleft,
@ -718,7 +724,7 @@ HistoryTextState Voice::getState(
st::normalFont->height,
_width);
if (namerect.contains(point) && !result.link && !_data->loading()) {
return _namel;
return { parent(), _namel };
}
return result;
}
@ -1014,11 +1020,12 @@ HistoryTextState Document::getState(
_st.songThumbSize,
_width);
if (inner.contains(point)) {
return loaded
const auto link = loaded
? _openl
: ((_data->loading() || _data->status == FileUploading)
? _cancell
: _openl);
: (_data->loading() || _data->status == FileUploading)
? _cancell
: _openl;
return { parent(), link };
}
const auto namerect = rtlrect(
nameleft,
@ -1027,7 +1034,7 @@ HistoryTextState Document::getState(
st::semiboldFont->height,
_width);
if (namerect.contains(point) && !_data->loading()) {
return _namel;
return { parent(), _namel };
}
} else {
const auto nameleft = _st.fileThumbSize + _st.filePadding.right();
@ -1047,11 +1054,12 @@ HistoryTextState Document::getState(
_width);
if (rthumb.contains(point)) {
return loaded
const auto link = loaded
? _openl
: ((_data->loading() || _data->status == FileUploading)
? _cancell
: _savel);
: (_data->loading() || _data->status == FileUploading)
? _cancell
: _savel;
return { parent(), link };
}
if (_data->status != FileUploadFailed) {
@ -1062,7 +1070,7 @@ HistoryTextState Document::getState(
st::normalFont->height,
_width);
if (daterect.contains(point)) {
return _msgl;
return { parent(), _msgl };
}
}
if (!_data->loading() && _data->isValid()) {
@ -1073,7 +1081,7 @@ HistoryTextState Document::getState(
_height - st::linksBorder,
_width);
if (loaded && leftofnamerect.contains(point)) {
return _namel;
return { parent(), _namel };
}
const auto namerect = rtlrect(
nameleft,
@ -1082,7 +1090,7 @@ HistoryTextState Document::getState(
st::semiboldFont->height,
_width);
if (namerect.contains(point)) {
return _namel;
return { parent(), _namel };
}
}
}
@ -1212,7 +1220,9 @@ Link::Link(
if (_page->type == WebPageProfile || _page->type == WebPageVideo) {
_photol = MakeShared<UrlClickHandler>(_page->url);
} else if (_page->type == WebPagePhoto || _page->siteName == qstr("Twitter") || _page->siteName == qstr("Facebook")) {
_photol = MakeShared<PhotoOpenClickHandler>(_page->photo);
_photol = MakeShared<PhotoOpenClickHandler>(
_page->photo,
parent->fullId());
} else {
_photol = MakeShared<UrlClickHandler>(_page->url);
}
@ -1415,7 +1425,7 @@ HistoryTextState Link::getState(
HistoryStateRequest request) const {
int32 left = st::linksPhotoSize + st::linksPhotoPadding, top = st::linksMargin.top() + st::linksBorder, w = _width - left;
if (rtlrect(0, top, st::linksPhotoSize, st::linksPhotoSize, _width).contains(point)) {
return _photol;
return { parent(), _photol };
}
if (!_title.isEmpty() && _text.isEmpty() && _links.size() == 1) {
@ -1423,7 +1433,7 @@ HistoryTextState Link::getState(
}
if (!_title.isEmpty()) {
if (rtlrect(left, top, qMin(w, _titlew), st::semiboldFont->height, _width).contains(point)) {
return _photol;
return { parent(), _photol };
}
top += st::webPageTitleFont->height;
}
@ -1432,7 +1442,7 @@ HistoryTextState Link::getState(
}
for (int32 i = 0, l = _links.size(); i < l; ++i) {
if (rtlrect(left, top, qMin(w, _links.at(i).width), st::normalFont->height, _width).contains(point)) {
return ClickHandlerPtr(_links[i].lnk);
return { parent(), ClickHandlerPtr(_links[i].lnk) };
}
top += st::normalFont->height;
}

View File

@ -94,6 +94,8 @@ CoverWidget::CoverWidget(QWidget *parent, UserData *self)
}
PhotoData *CoverWidget::validatePhoto() const {
Expects(_self != nullptr);
const auto photo = _self->userpicPhotoId()
? App::photo(_self->userpicPhotoId())
: nullptr;
@ -106,7 +108,7 @@ PhotoData *CoverWidget::validatePhoto() const {
}
void CoverWidget::showPhoto() {
if (auto photo = validatePhoto()) {
if (const auto photo = validatePhoto()) {
Messenger::Instance().showPhoto(photo, _self);
}
}