diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 31bd4f2f7e..e6bda28ce4 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -87,6 +87,43 @@ int BinarySearchBlocksOrItems(const T &list, int edge) { // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html +class HistoryInner::BotAbout : public ClickHandlerHost { +public: + BotAbout(not_null parent, not_null info); + + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; + + not_null info; + int width = 0; + int height = 0; + QRect rect; + +private: + not_null _parent; + +}; + +HistoryInner::BotAbout::BotAbout( + not_null parent, + not_null info) +: info(info) +, _parent(parent) { +} + +void HistoryInner::BotAbout::clickHandlerActiveChanged( + const ClickHandlerPtr &p, + bool active) { + _parent->update(rect); +} + +void HistoryInner::BotAbout::clickHandlerPressedChanged( + const ClickHandlerPtr &p, + bool pressed) { + _parent->update(rect); +} + HistoryInner::HistoryInner( not_null historyWidget, not_null controller, @@ -365,6 +402,44 @@ void HistoryInner::enumerateDates(Method method) { enumerateItems(dateCallback); } +TextSelection HistoryInner::computeRenderSelection( + not_null selected, + not_null item) const { + const auto itemSelection = [&](not_null item) { + auto i = selected->find(item); + if (i != selected->end()) { + return i->second; + } + return TextSelection(); + }; + const auto group = item->Get(); + 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); +} + TextSelection HistoryInner::itemRenderSelection( not_null item, int selfromy, @@ -377,39 +452,7 @@ TextSelection HistoryInner::itemRenderSelection( return FullSelection; } } else if (!_selected.empty()) { - const auto itemSelection = [&](not_null item) { - auto i = _selected.find(item); - if (i != _selected.end()) { - return i->second; - } - return TextSelection(); - }; - const auto group = item->Get(); - 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 computeRenderSelection(&_selected, item); } return TextSelection(); } @@ -1605,8 +1648,9 @@ void HistoryInner::copyContextText() { if (!item || (item->getMedia() && item->getMedia()->type() == MediaTypeSticker)) { return; } - - setToClipboard(item->selectedText(FullSelection)); + const auto group = item->getFullGroup(); + const auto leader = group ? group->leader : item; + setToClipboard(leader->selectedText(FullSelection)); } void HistoryInner::setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode) { @@ -1634,32 +1678,65 @@ TextWithEntities HistoryInner::getSelectedText() const { return item->selectedText(selection); } - int fullSize = 0; - QString timeFormat(qsl(", [dd.MM.yy hh:mm]\n")); - QMap texts; - for (const auto [item, selection] : selected) { - if (item->detached()) continue; + const auto timeFormat = qsl(", [dd.MM.yy hh:mm]\n"); + auto groupLeadersAdded = base::flat_set>(); + auto fullSize = 0; + auto texts = base::flat_map, TextWithEntities>(); + const auto addItem = [&]( + not_null item, + TextSelection selection) { auto time = item->date.toString(timeFormat); - TextWithEntities part, unwrapped = item->selectedText(FullSelection); - int size = item->author()->name.size() + time.size() + unwrapped.text.size(); + auto part = TextWithEntities(); + auto unwrapped = item->selectedText(selection); + auto size = item->author()->name.size() + + time.size() + + unwrapped.text.size(); part.text.reserve(size); - int y = itemTop(item); + auto y = itemTop(item); if (y >= 0) { part.text.append(item->author()->name).append(time); TextUtilities::Append(part, std::move(unwrapped)); - texts.insert(y, part); + texts.emplace(std::make_pair(y, item->id), part); fullSize += size; } + }; + + for (const auto [item, selection] : selected) { + if (item->detached()) { + continue; + } + + if (const auto group = item->Get()) { + if (groupLeadersAdded.contains(group->leader)) { + continue; + } + const auto leaderSelection = computeRenderSelection( + &selected, + group->leader); + if (leaderSelection == FullSelection) { + groupLeadersAdded.emplace(group->leader); + addItem(group->leader, FullSelection); + } else if (item == group->leader) { + const auto leaderFullSelection = AddGroupItemSelection( + TextSelection(), + int(group->others.size())); + addItem(item, leaderFullSelection); + } else { + addItem(item, FullSelection); + } + } else { + addItem(item, FullSelection); + } } - TextWithEntities result; + auto result = TextWithEntities(); auto sep = qsl("\n\n"); result.text.reserve(fullSize + (texts.size() - 1) * sep.size()); - for (auto i = texts.begin(), e = texts.end(); i != e; ++i) { - TextUtilities::Append(result, std::move(i.value())); - if (i + 1 != e) { + for (auto i = texts.begin(), e = texts.end(); i != e;) { + TextUtilities::Append(result, std::move(i->second)); + if (++i != e) { result.text.append(sep); } } @@ -2386,14 +2463,6 @@ void HistoryInner::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dr update(); } -void HistoryInner::BotAbout::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { - _parent->update(rect); -} - -void HistoryInner::BotAbout::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { - _parent->update(rect); -} - int HistoryInner::historyHeight() const { int result = 0; if (!_history || _history->isEmpty()) { @@ -2441,13 +2510,16 @@ int HistoryInner::itemTop(const HistoryItem *item) const { // -1 if should not b } void HistoryInner::notifyIsBotChanged() { - BotInfo *newinfo = (_history && _history->peer->isUser()) ? _history->peer->asUser()->botInfo.get() : nullptr; - if ((!newinfo && !_botAbout) || (newinfo && _botAbout && _botAbout->info == newinfo)) { + const auto newinfo = (_history && _history->peer->isUser()) + ? _history->peer->asUser()->botInfo.get() + : nullptr; + if ((!newinfo && !_botAbout) + || (newinfo && _botAbout && _botAbout->info == newinfo)) { return; } if (newinfo) { - _botAbout.reset(new BotAbout(this, newinfo)); + _botAbout = std::make_unique(this, newinfo); if (newinfo && !newinfo->inited) { Auth().api().requestFullPeer(_peer); } diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 0501c22e51..9040b6a088 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -133,6 +133,9 @@ private slots: void onScrollDateHideByTimer(); private: + class BotAbout; + using SelectedItems = std::map>; + enum class MouseAction { None, PrepareDrag, @@ -140,7 +143,6 @@ private: PrepareSelect, Selecting, }; - enum class SelectAction { Select, Deselect, @@ -175,6 +177,9 @@ private: not_null item, int selfromy, int seltoy) const; + TextSelection computeRenderSelection( + not_null selected, + not_null item) const; void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard); @@ -196,23 +201,6 @@ private: // or at least we don't need to display first _history date (just skip it by height) int _historySkipHeight = 0; - class BotAbout : public ClickHandlerHost { - public: - BotAbout(HistoryInner *parent, BotInfo *info) : info(info), _parent(parent) { - } - BotInfo *info = nullptr; - int width = 0; - int height = 0; - QRect rect; - - // ClickHandlerHost interface - void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; - void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; - - private: - HistoryInner *_parent; - - }; std::unique_ptr _botAbout; HistoryWidget *_widget = nullptr; @@ -224,7 +212,6 @@ private: bool _firstLoading = false; style::cursor _cursor = style::cur_default; - using SelectedItems = std::map>; SelectedItems _selected; void applyDragSelection(); diff --git a/Telegram/SourceFiles/history/history_media_grouped.cpp b/Telegram/SourceFiles/history/history_media_grouped.cpp index 0568faaf27..a98404e49c 100644 --- a/Telegram/SourceFiles/history/history_media_grouped.cpp +++ b/Telegram/SourceFiles/history/history_media_grouped.cpp @@ -288,10 +288,15 @@ QString HistoryGroupedMedia::inDialogsText() const { TextWithEntities HistoryGroupedMedia::selectedText( TextSelection selection) const { - return WithCaptionSelectedText( - lang(lng_in_dlg_album), - _caption, - selection); + if (!IsSubGroupSelection(selection)) { + return WithCaptionSelectedText( + lang(lng_in_dlg_album), + _caption, + selection); + } else if (IsGroupItemSelection(selection, int(_elements.size()) - 1)) { + return main()->selectedText(FullSelection); + } + return TextWithEntities(); } void HistoryGroupedMedia::clickHandlerActiveChanged( diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index f01cb738ac..25e58c5a54 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1539,7 +1539,9 @@ TextWithEntities HistoryMessage::selectedText(TextSelection selection) const { ExpandLinksAll); auto skipped = skipTextSelection(selection); auto mediaDisplayed = (_media && _media->isDisplayed()); - auto mediaResult = mediaDisplayed ? _media->selectedText(skipped) : TextWithEntities(); + auto mediaResult = (mediaDisplayed || isHiddenByGroup()) + ? _media->selectedText(skipped) + : TextWithEntities(); if (auto entry = Get()) { const auto originalSelection = mediaDisplayed ? _media->skipSelection(skipped)