From 600737c44fe143f984c51517bd8dfb26711f2598 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 27 Jan 2018 20:26:24 +0300 Subject: [PATCH] Fix copy selected items text in old and new lists. --- .../SourceFiles/data/data_media_types.cpp | 4 +- Telegram/SourceFiles/data/data_media_types.h | 4 + .../history/history_inner_widget.cpp | 80 ++++---- .../history/history_inner_widget.h | 7 + .../SourceFiles/history/history_item_text.cpp | 50 ++++- .../SourceFiles/history/history_item_text.h | 7 + .../history/history_media_grouped.cpp | 10 - .../history/view/history_view_list_widget.cpp | 173 +++++++----------- .../history/view/history_view_list_widget.h | 9 +- 9 files changed, 175 insertions(+), 169 deletions(-) diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index a3a54bee44..0eb84fc023 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -130,6 +130,8 @@ QString WithCaptionNotificationText( caption); } +} // namespace + TextWithEntities WithCaptionClipboardText( const QString &attachType, TextWithEntities &&caption) { @@ -143,8 +145,6 @@ TextWithEntities WithCaptionClipboardText( return result; } -} // namespace - Media::Media(not_null parent) : _parent(parent) { } diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 141cb974b6..dce8d0c417 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -364,4 +364,8 @@ private: }; +TextWithEntities WithCaptionClipboardText( + const QString &attachType, + TextWithEntities &&caption); + } // namespace Data diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 8c738c525f..3f7fc26c04 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1767,56 +1767,46 @@ TextWithEntities HistoryInner::getSelectedText() const { } const auto timeFormat = qsl(", [dd.MM.yy hh:mm]\n"); - auto groupLeadersAdded = base::flat_set>(); + auto groups = base::flat_set>(); auto fullSize = 0; - auto texts = base::flat_map, TextWithEntities>(); + auto texts = base::flat_map(); - const auto addItem = [&](not_null view) { - const auto item = view->data(); + const auto wrapItem = [&]( + not_null item, + TextWithEntities &&unwrapped) { auto time = item->date.toString(timeFormat); auto part = TextWithEntities(); - auto unwrapped = HistoryItemText(item); auto size = item->author()->name.size() + time.size() + unwrapped.text.size(); part.text.reserve(size); + part.text.append(item->author()->name).append(time); + TextUtilities::Append(part, std::move(unwrapped)); + texts.emplace(item->position(), part); + fullSize += size; + }; + const auto addItem = [&](not_null item) { + wrapItem(item, HistoryItemText(item)); + }; + const auto addGroup = [&](not_null group) { + Expects(!group->items.empty()); - auto y = itemTop(view); - if (y >= 0) { - part.text.append(item->author()->name).append(time); - TextUtilities::Append(part, std::move(unwrapped)); - texts.emplace(std::make_pair(y, item->id), part); - fullSize += size; - } + wrapItem(group->items.back(), HistoryGroupText(group)); }; for (const auto [item, selection] : selected) { - const auto view = item->mainView(); - if (!view) { - continue; - } - if (const auto group = Auth().data().groups().find(item)) { - // #TODO group copy - //if (groupLeadersAdded.contains(group->leader)) { - // continue; - //} - //const auto leaderSelection = computeRenderSelection( - // &selected, - // group->leader); - //if (leaderSelection == FullSelection) { - // groupLeadersAdded.emplace(group->leader); - // addItem(group->leader); - //} else if (view == group->leader) { - // const auto leaderFullSelection = AddGroupItemSelection( - // TextSelection(), - // int(group->others.size())); - // addItem(view, leaderFullSelection); - //} else { - // addItem(view); - //} + if (groups.contains(group)) { + continue; + } + if (isSelectedGroup(&selected, group)) { + groups.emplace(group); + addGroup(group); + } else { + addItem(item); + } } else { - addItem(view); + addItem(item); } } @@ -2686,16 +2676,22 @@ bool HistoryInner::isSelected( return (i != toItems->cend()) && (i->second == FullSelection); } +bool HistoryInner::isSelectedGroup( + not_null toItems, + not_null group) const { + for (const auto other : group->items) { + if (!isSelected(toItems, other)) { + return false; + } + } + return true; +} + bool HistoryInner::isSelectedAsGroup( not_null toItems, not_null item) const { if (const auto group = Auth().data().groups().find(item)) { - for (const auto other : group->items) { - if (!isSelected(toItems, other)) { - return false; - } - } - return true; + return isSelectedGroup(toItems, group); } return isSelected(toItems, item); } diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 77d97a9a09..3ab5db8af0 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/scroll_area.h" #include "history/view/history_view_top_bar_widget.h" +namespace Data { +class Group; +} // namespace Data + namespace HistoryView { class ElementDelegate; struct TextState; @@ -244,6 +248,9 @@ private: bool isSelected( not_null toItems, not_null item) const; + bool isSelectedGroup( + not_null toItems, + not_null group) const; bool isSelectedAsGroup( not_null toItems, not_null item) const; diff --git a/Telegram/SourceFiles/history/history_item_text.cpp b/Telegram/SourceFiles/history/history_item_text.cpp index 9ba0aa8bf1..549caf7650 100644 --- a/Telegram/SourceFiles/history/history_item_text.cpp +++ b/Telegram/SourceFiles/history/history_item_text.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_components.h" #include "data/data_media_types.h" #include "data/data_web_page.h" +#include "data/data_groups.h" #include "lang/lang_keys.h" #include "ui/text_options.h" @@ -55,11 +56,27 @@ TextWithEntities WrapAsForwarded( return result; } +TextWithEntities WrapAsItem( + not_null item, + TextWithEntities &&result) { + if (const auto reply = item->Get()) { + if (const auto message = reply->replyToMsg) { + result = WrapAsReply(std::move(result), message); + } + } + if (const auto forwarded = item->Get()) { + result = WrapAsForwarded(std::move(result), forwarded); + } + return result; +} + TextWithEntities HistoryItemText(not_null item) { const auto media = item->media(); - auto textResult = item->clipboardText(); auto mediaResult = media ? media->clipboardText() : TextWithEntities(); + auto textResult = mediaResult.text.isEmpty() + ? item->clipboardText() + : TextWithEntities(); auto logEntryOriginalResult = [&] { const auto entry = item->Get(); if (!entry) { @@ -94,13 +111,26 @@ TextWithEntities HistoryItemText(not_null item) { result.text += qstr("\n\n"); TextUtilities::Append(result, std::move(logEntryOriginalResult)); } - if (const auto reply = item->Get()) { - if (const auto message = reply->replyToMsg) { - result = WrapAsReply(std::move(result), message); - } - } - if (const auto forwarded = item->Get()) { - result = WrapAsForwarded(std::move(result), forwarded); - } - return result; + return WrapAsItem(item, std::move(result)); +} + +TextWithEntities HistoryGroupText(not_null group) { + Expects(!group->items.empty()); + + auto caption = [&] { + const auto first = begin(group->items); + const auto result = (*first)->clipboardText(); + if (result.text.isEmpty()) { + return result; + } + for (auto i = first + 1; i != end(group->items); ++i) { + if (!(*i)->clipboardText().text.isEmpty()) { + return TextWithEntities(); + } + } + return result; + }(); + return WrapAsItem(group->items.back(), Data::WithCaptionClipboardText( + lang(lng_in_dlg_album), + std::move(caption))); } diff --git a/Telegram/SourceFiles/history/history_item_text.h b/Telegram/SourceFiles/history/history_item_text.h index 3e61d308b9..50fbf93a9f 100644 --- a/Telegram/SourceFiles/history/history_item_text.h +++ b/Telegram/SourceFiles/history/history_item_text.h @@ -7,4 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +class HistoryItem; + +namespace Data { +class Group; +} // namespace Data + TextWithEntities HistoryItemText(not_null item); +TextWithEntities HistoryGroupText(not_null group); diff --git a/Telegram/SourceFiles/history/history_media_grouped.cpp b/Telegram/SourceFiles/history/history_media_grouped.cpp index 1b0dbb8004..d4836990f4 100644 --- a/Telegram/SourceFiles/history/history_media_grouped.cpp +++ b/Telegram/SourceFiles/history/history_media_grouped.cpp @@ -280,16 +280,6 @@ TextSelection HistoryGroupedMedia::adjustSelection( TextWithEntities HistoryGroupedMedia::selectedText( TextSelection selection) const { return _caption.originalTextWithEntities(selection, ExpandLinksAll); - // #TODO group select - //if (!IsSubGroupSelection(selection)) { - // return WithCaptionSelectedText( - // lang(lng_in_dlg_album), - // _caption, - // selection); - //} else if (IsGroupItemSelection(selection, int(_parts.size()) - 1)) { - // return main()->selectedText(FullSelection); - //} - //return TextWithEntities(); } void HistoryGroupedMedia::clickHandlerActiveChanged( diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index c3f35e0988..f34e0df567 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_media_types.h" #include "history/history_message.h" #include "history/history_item_components.h" +#include "history/history_item_text.h" #include "history/view/history_view_context_menu.h" #include "history/view/history_view_element.h" #include "history/view/history_view_message.h" @@ -527,53 +528,22 @@ bool ListWidget::overSelectedItems() const { return false; } -//bool ListWidget::applyItemSelection( -// SelectedMap &applyTo, -// FullMsgId itemId) const { -// if (applyTo.size() >= MaxSelectedItems) { -// return false; -// } -// auto [iterator, ok] = applyTo.try_emplace( -// itemId, -// SelectionData()); -// if (!ok) { -// return false; -// } -// const auto item = App::histItemById(itemId); -// if (!item) { -// applyTo.erase(iterator); -// return false; -// } -// iterator->second.canDelete = item->canDelete(); -// iterator->second.canForward = item->allowsForward(); -// return true; -//} -// -//void ListWidget::toggleItemSelection(FullMsgId itemId) { -// auto it = _selected.find(itemId); -// if (it == _selected.cend()) { -// if (_selectedTextItem) { -// clearTextSelection(); -// } -// if (applyItemSelection(_selected, itemId)) { -// repaintItem(itemId); -// pushSelectedItems(); -// } -// } else { -// removeItemSelection(it); -// } -//} +bool ListWidget::isSelectedGroup( + const SelectedMap &applyTo, + not_null group) const { + for (const auto other : group->items) { + if (!applyTo.contains(other->fullId())) { + return false; + } + } + return true; +} bool ListWidget::isSelectedAsGroup( const SelectedMap &applyTo, not_null item) const { if (const auto group = Auth().data().groups().find(item)) { - for (const auto other : group->items) { - if (!applyTo.contains(other->fullId())) { - return false; - } - } - return true; + return isSelectedGroup(applyTo, group); } return applyTo.contains(item->fullId()); } @@ -1135,72 +1105,69 @@ TextWithEntities ListWidget::getSelectedText() const { return _selectedText; } - // #TODO selection - //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 timeFormat = qsl(", [dd.MM.yy hh:mm]\n"); + auto groups = base::flat_set>(); + auto fullSize = 0; + auto texts = std::vector, + TextWithEntities>>(); + texts.reserve(selected.size()); - //const auto addItem = [&]( - // not_null view, - // TextSelection selection) { - // const auto item = view->data(); - // auto time = item->date.toString(timeFormat); - // auto part = TextWithEntities(); - // auto unwrapped = view->selectedText(selection); - // auto size = item->author()->name.size() - // + time.size() - // + unwrapped.text.size(); - // part.text.reserve(size); + const auto wrapItem = [&]( + not_null item, + TextWithEntities &&unwrapped) { + auto time = item->date.toString(timeFormat); + auto part = TextWithEntities(); + auto size = item->author()->name.size() + + time.size() + + unwrapped.text.size(); + part.text.reserve(size); + part.text.append(item->author()->name).append(time); + TextUtilities::Append(part, std::move(unwrapped)); + texts.push_back(std::make_pair(std::move(item), std::move(part))); + fullSize += size; + }; + const auto addItem = [&](not_null item) { + wrapItem(item, HistoryItemText(item)); + }; + const auto addGroup = [&](not_null group) { + Expects(!group->items.empty()); - // auto y = itemTop(view); - // if (y >= 0) { - // part.text.append(item->author()->name).append(time); - // TextUtilities::Append(part, std::move(unwrapped)); - // texts.emplace(std::make_pair(y, item->id), part); - // fullSize += size; - // } - //}; + wrapItem(group->items.back(), HistoryGroupText(group)); + }; - //for (const auto [item, selection] : selected) { - // const auto view = item->mainView(); - // if (!view) { - // continue; - // } - - // if (const auto group = Auth().data().groups().find(item)) { - // // #TODO group copy - // //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 (view == group->leader) { - // // const auto leaderFullSelection = AddGroupItemSelection( - // // TextSelection(), - // // int(group->others.size())); - // // addItem(view, leaderFullSelection); - // //} else { - // // addItem(view, FullSelection); - // //} - // } else { - // addItem(view, FullSelection); - // } - //} + for (const auto [itemId, data] : selected) { + if (const auto item = App::histItemById(itemId)) { + if (const auto group = Auth().data().groups().find(item)) { + if (groups.contains(group)) { + continue; + } + if (isSelectedGroup(selected, group)) { + groups.emplace(group); + addGroup(group); + } else { + addItem(item); + } + } else { + addItem(item); + } + } + } + ranges::sort(texts, [&]( + const std::pair, TextWithEntities> &a, + const std::pair, TextWithEntities> &b) { + return _delegate->listIsLessInOrder(a.first, b.first); + }); 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;) { - // TextUtilities::Append(result, std::move(i->second)); - // if (++i != e) { - // result.text.append(sep); - // } - //} + auto sep = qsl("\n\n"); + result.text.reserve(fullSize + (texts.size() - 1) * sep.size()); + for (auto i = begin(texts), e = end(texts); i != e;) { + TextUtilities::Append(result, std::move(i->second)); + if (++i != e) { + result.text.append(sep); + } + } return result; } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 525f290703..61563d0dcb 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -22,6 +22,10 @@ namespace Window { class Controller; } // namespace Window +namespace Data { +class Group; +} // namespace Data + namespace HistoryView { struct TextState; @@ -288,8 +292,6 @@ private: void setTextSelection( not_null view, TextSelection selection); - //bool applyItemSelection(SelectedMap &applyTo, FullMsgId itemId) const; - //void toggleItemSelection(FullMsgId itemId); bool isGoodForSelection(not_null item) const; bool isGoodForSelection( @@ -306,6 +308,9 @@ private: SelectedMap &applyTo, not_null item, SelectAction action) const; + bool isSelectedGroup( + const SelectedMap &applyTo, + not_null group) const; bool isSelectedAsGroup( const SelectedMap &applyTo, not_null item) const;